protobuf-cucumber 3.10.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.rubocop.yml +70 -0
  4. data/.rubocop_todo.yml +145 -0
  5. data/.travis.yml +40 -0
  6. data/.yardopts +5 -0
  7. data/CHANGES.md +344 -0
  8. data/CONTRIBUTING.md +16 -0
  9. data/Gemfile +3 -0
  10. data/LICENSE.txt +22 -0
  11. data/README.md +33 -0
  12. data/Rakefile +64 -0
  13. data/bin/protoc-gen-ruby +22 -0
  14. data/bin/rpc_server +5 -0
  15. data/install-protobuf.sh +28 -0
  16. data/lib/protobuf.rb +129 -0
  17. data/lib/protobuf/cli.rb +257 -0
  18. data/lib/protobuf/code_generator.rb +120 -0
  19. data/lib/protobuf/decoder.rb +28 -0
  20. data/lib/protobuf/deprecation.rb +117 -0
  21. data/lib/protobuf/descriptors.rb +3 -0
  22. data/lib/protobuf/descriptors/google/protobuf/compiler/plugin.pb.rb +62 -0
  23. data/lib/protobuf/descriptors/google/protobuf/descriptor.pb.rb +301 -0
  24. data/lib/protobuf/encoder.rb +11 -0
  25. data/lib/protobuf/enum.rb +365 -0
  26. data/lib/protobuf/exceptions.rb +9 -0
  27. data/lib/protobuf/field.rb +74 -0
  28. data/lib/protobuf/field/base_field.rb +380 -0
  29. data/lib/protobuf/field/base_field_object_definitions.rb +504 -0
  30. data/lib/protobuf/field/bool_field.rb +64 -0
  31. data/lib/protobuf/field/bytes_field.rb +78 -0
  32. data/lib/protobuf/field/double_field.rb +25 -0
  33. data/lib/protobuf/field/enum_field.rb +61 -0
  34. data/lib/protobuf/field/field_array.rb +104 -0
  35. data/lib/protobuf/field/field_hash.rb +122 -0
  36. data/lib/protobuf/field/fixed32_field.rb +25 -0
  37. data/lib/protobuf/field/fixed64_field.rb +28 -0
  38. data/lib/protobuf/field/float_field.rb +43 -0
  39. data/lib/protobuf/field/int32_field.rb +21 -0
  40. data/lib/protobuf/field/int64_field.rb +34 -0
  41. data/lib/protobuf/field/integer_field.rb +23 -0
  42. data/lib/protobuf/field/message_field.rb +51 -0
  43. data/lib/protobuf/field/sfixed32_field.rb +27 -0
  44. data/lib/protobuf/field/sfixed64_field.rb +28 -0
  45. data/lib/protobuf/field/signed_integer_field.rb +29 -0
  46. data/lib/protobuf/field/sint32_field.rb +21 -0
  47. data/lib/protobuf/field/sint64_field.rb +21 -0
  48. data/lib/protobuf/field/string_field.rb +51 -0
  49. data/lib/protobuf/field/uint32_field.rb +21 -0
  50. data/lib/protobuf/field/uint64_field.rb +21 -0
  51. data/lib/protobuf/field/varint_field.rb +77 -0
  52. data/lib/protobuf/generators/base.rb +85 -0
  53. data/lib/protobuf/generators/enum_generator.rb +39 -0
  54. data/lib/protobuf/generators/extension_generator.rb +27 -0
  55. data/lib/protobuf/generators/field_generator.rb +193 -0
  56. data/lib/protobuf/generators/file_generator.rb +262 -0
  57. data/lib/protobuf/generators/group_generator.rb +122 -0
  58. data/lib/protobuf/generators/message_generator.rb +104 -0
  59. data/lib/protobuf/generators/option_generator.rb +17 -0
  60. data/lib/protobuf/generators/printable.rb +160 -0
  61. data/lib/protobuf/generators/service_generator.rb +50 -0
  62. data/lib/protobuf/lifecycle.rb +33 -0
  63. data/lib/protobuf/logging.rb +39 -0
  64. data/lib/protobuf/message.rb +260 -0
  65. data/lib/protobuf/message/fields.rb +233 -0
  66. data/lib/protobuf/message/serialization.rb +85 -0
  67. data/lib/protobuf/optionable.rb +70 -0
  68. data/lib/protobuf/rpc/buffer.rb +78 -0
  69. data/lib/protobuf/rpc/client.rb +140 -0
  70. data/lib/protobuf/rpc/connectors/base.rb +221 -0
  71. data/lib/protobuf/rpc/connectors/ping.rb +89 -0
  72. data/lib/protobuf/rpc/connectors/socket.rb +78 -0
  73. data/lib/protobuf/rpc/connectors/zmq.rb +319 -0
  74. data/lib/protobuf/rpc/dynamic_discovery.pb.rb +50 -0
  75. data/lib/protobuf/rpc/env.rb +60 -0
  76. data/lib/protobuf/rpc/error.rb +28 -0
  77. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  78. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  79. data/lib/protobuf/rpc/middleware.rb +25 -0
  80. data/lib/protobuf/rpc/middleware/exception_handler.rb +40 -0
  81. data/lib/protobuf/rpc/middleware/logger.rb +95 -0
  82. data/lib/protobuf/rpc/middleware/request_decoder.rb +79 -0
  83. data/lib/protobuf/rpc/middleware/response_encoder.rb +83 -0
  84. data/lib/protobuf/rpc/middleware/runner.rb +18 -0
  85. data/lib/protobuf/rpc/rpc.pb.rb +64 -0
  86. data/lib/protobuf/rpc/rpc_method.rb +16 -0
  87. data/lib/protobuf/rpc/server.rb +39 -0
  88. data/lib/protobuf/rpc/servers/socket/server.rb +121 -0
  89. data/lib/protobuf/rpc/servers/socket/worker.rb +56 -0
  90. data/lib/protobuf/rpc/servers/socket_runner.rb +46 -0
  91. data/lib/protobuf/rpc/servers/zmq/broker.rb +194 -0
  92. data/lib/protobuf/rpc/servers/zmq/server.rb +321 -0
  93. data/lib/protobuf/rpc/servers/zmq/util.rb +48 -0
  94. data/lib/protobuf/rpc/servers/zmq/worker.rb +105 -0
  95. data/lib/protobuf/rpc/servers/zmq_runner.rb +70 -0
  96. data/lib/protobuf/rpc/service.rb +172 -0
  97. data/lib/protobuf/rpc/service_directory.rb +261 -0
  98. data/lib/protobuf/rpc/service_dispatcher.rb +45 -0
  99. data/lib/protobuf/rpc/service_filters.rb +250 -0
  100. data/lib/protobuf/rpc/stat.rb +119 -0
  101. data/lib/protobuf/socket.rb +21 -0
  102. data/lib/protobuf/tasks.rb +1 -0
  103. data/lib/protobuf/tasks/compile.rake +80 -0
  104. data/lib/protobuf/varint.rb +20 -0
  105. data/lib/protobuf/varint_pure.rb +31 -0
  106. data/lib/protobuf/version.rb +3 -0
  107. data/lib/protobuf/wire_type.rb +10 -0
  108. data/lib/protobuf/zmq.rb +21 -0
  109. data/profile.html +5103 -0
  110. data/proto/dynamic_discovery.proto +44 -0
  111. data/proto/google/protobuf/compiler/plugin.proto +147 -0
  112. data/proto/google/protobuf/descriptor.proto +779 -0
  113. data/proto/rpc.proto +69 -0
  114. data/protobuf-cucumber.gemspec +57 -0
  115. data/spec/benchmark/tasks.rb +143 -0
  116. data/spec/bin/protoc-gen-ruby_spec.rb +23 -0
  117. data/spec/encoding/all_types_spec.rb +103 -0
  118. data/spec/encoding/extreme_values_spec.rb +0 -0
  119. data/spec/functional/class_inheritance_spec.rb +52 -0
  120. data/spec/functional/code_generator_spec.rb +58 -0
  121. data/spec/functional/socket_server_spec.rb +59 -0
  122. data/spec/functional/zmq_server_spec.rb +105 -0
  123. data/spec/lib/protobuf/cli_spec.rb +317 -0
  124. data/spec/lib/protobuf/code_generator_spec.rb +87 -0
  125. data/spec/lib/protobuf/enum_spec.rb +307 -0
  126. data/spec/lib/protobuf/field/bool_field_spec.rb +91 -0
  127. data/spec/lib/protobuf/field/double_field_spec.rb +9 -0
  128. data/spec/lib/protobuf/field/enum_field_spec.rb +44 -0
  129. data/spec/lib/protobuf/field/field_array_spec.rb +105 -0
  130. data/spec/lib/protobuf/field/field_hash_spec.rb +168 -0
  131. data/spec/lib/protobuf/field/fixed32_field_spec.rb +7 -0
  132. data/spec/lib/protobuf/field/fixed64_field_spec.rb +7 -0
  133. data/spec/lib/protobuf/field/float_field_spec.rb +90 -0
  134. data/spec/lib/protobuf/field/int32_field_spec.rb +120 -0
  135. data/spec/lib/protobuf/field/int64_field_spec.rb +7 -0
  136. data/spec/lib/protobuf/field/message_field_spec.rb +132 -0
  137. data/spec/lib/protobuf/field/sfixed32_field_spec.rb +9 -0
  138. data/spec/lib/protobuf/field/sfixed64_field_spec.rb +9 -0
  139. data/spec/lib/protobuf/field/sint32_field_spec.rb +9 -0
  140. data/spec/lib/protobuf/field/sint64_field_spec.rb +9 -0
  141. data/spec/lib/protobuf/field/string_field_spec.rb +79 -0
  142. data/spec/lib/protobuf/field/uint32_field_spec.rb +7 -0
  143. data/spec/lib/protobuf/field/uint64_field_spec.rb +7 -0
  144. data/spec/lib/protobuf/field_spec.rb +192 -0
  145. data/spec/lib/protobuf/generators/base_spec.rb +154 -0
  146. data/spec/lib/protobuf/generators/enum_generator_spec.rb +82 -0
  147. data/spec/lib/protobuf/generators/extension_generator_spec.rb +42 -0
  148. data/spec/lib/protobuf/generators/field_generator_spec.rb +197 -0
  149. data/spec/lib/protobuf/generators/file_generator_spec.rb +119 -0
  150. data/spec/lib/protobuf/generators/message_generator_spec.rb +0 -0
  151. data/spec/lib/protobuf/generators/service_generator_spec.rb +99 -0
  152. data/spec/lib/protobuf/lifecycle_spec.rb +94 -0
  153. data/spec/lib/protobuf/message_spec.rb +944 -0
  154. data/spec/lib/protobuf/optionable_spec.rb +265 -0
  155. data/spec/lib/protobuf/rpc/client_spec.rb +66 -0
  156. data/spec/lib/protobuf/rpc/connectors/base_spec.rb +226 -0
  157. data/spec/lib/protobuf/rpc/connectors/ping_spec.rb +69 -0
  158. data/spec/lib/protobuf/rpc/connectors/socket_spec.rb +34 -0
  159. data/spec/lib/protobuf/rpc/connectors/zmq_spec.rb +110 -0
  160. data/spec/lib/protobuf/rpc/middleware/exception_handler_spec.rb +62 -0
  161. data/spec/lib/protobuf/rpc/middleware/logger_spec.rb +49 -0
  162. data/spec/lib/protobuf/rpc/middleware/request_decoder_spec.rb +115 -0
  163. data/spec/lib/protobuf/rpc/middleware/response_encoder_spec.rb +91 -0
  164. data/spec/lib/protobuf/rpc/servers/socket_server_spec.rb +38 -0
  165. data/spec/lib/protobuf/rpc/servers/zmq/server_spec.rb +43 -0
  166. data/spec/lib/protobuf/rpc/servers/zmq/util_spec.rb +55 -0
  167. data/spec/lib/protobuf/rpc/servers/zmq/worker_spec.rb +35 -0
  168. data/spec/lib/protobuf/rpc/service_directory_spec.rb +293 -0
  169. data/spec/lib/protobuf/rpc/service_dispatcher_spec.rb +35 -0
  170. data/spec/lib/protobuf/rpc/service_filters_spec.rb +517 -0
  171. data/spec/lib/protobuf/rpc/service_spec.rb +162 -0
  172. data/spec/lib/protobuf/rpc/stat_spec.rb +101 -0
  173. data/spec/lib/protobuf/varint_spec.rb +29 -0
  174. data/spec/lib/protobuf_spec.rb +105 -0
  175. data/spec/spec_helper.rb +42 -0
  176. data/spec/support/all.rb +6 -0
  177. data/spec/support/packed_field.rb +23 -0
  178. data/spec/support/protos/all_types.data.bin +0 -0
  179. data/spec/support/protos/all_types.data.txt +119 -0
  180. data/spec/support/protos/enum.pb.rb +63 -0
  181. data/spec/support/protos/enum.proto +37 -0
  182. data/spec/support/protos/extreme_values.data.bin +0 -0
  183. data/spec/support/protos/google_unittest.bin +0 -0
  184. data/spec/support/protos/google_unittest.pb.rb +798 -0
  185. data/spec/support/protos/google_unittest.proto +884 -0
  186. data/spec/support/protos/google_unittest_custom_options.bin +0 -0
  187. data/spec/support/protos/google_unittest_custom_options.pb.rb +361 -0
  188. data/spec/support/protos/google_unittest_custom_options.proto +424 -0
  189. data/spec/support/protos/google_unittest_import.pb.rb +55 -0
  190. data/spec/support/protos/google_unittest_import.proto +73 -0
  191. data/spec/support/protos/google_unittest_import_public.pb.rb +31 -0
  192. data/spec/support/protos/google_unittest_import_public.proto +41 -0
  193. data/spec/support/protos/map-test.bin +157 -0
  194. data/spec/support/protos/map-test.pb.rb +85 -0
  195. data/spec/support/protos/map-test.proto +68 -0
  196. data/spec/support/protos/multi_field_extensions.pb.rb +59 -0
  197. data/spec/support/protos/multi_field_extensions.proto +35 -0
  198. data/spec/support/protos/resource.pb.rb +172 -0
  199. data/spec/support/protos/resource.proto +137 -0
  200. data/spec/support/resource_service.rb +23 -0
  201. data/spec/support/server.rb +65 -0
  202. data/spec/support/test_app_file.rb +2 -0
  203. data/varint_prof.rb +82 -0
  204. metadata +579 -0
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ require 'protobuf/generators/file_generator'
4
+
5
+ RSpec.describe ::Protobuf::Generators::FileGenerator do
6
+
7
+ let(:base_descriptor_fields) { { :name => 'test/foo.proto' } }
8
+ let(:descriptor_fields) { base_descriptor_fields }
9
+ let(:file_descriptor) { ::Google::Protobuf::FileDescriptorProto.new(descriptor_fields) }
10
+
11
+ subject { described_class.new(file_descriptor) }
12
+ specify { expect(subject.file_name).to eq('test/foo.pb.rb') }
13
+
14
+ describe '#print_import_requires' do
15
+ let(:descriptor_fields) do
16
+ base_descriptor_fields.merge(
17
+ :dependency => [
18
+ 'test/bar.proto',
19
+ 'test/baz.proto',
20
+ ],
21
+ )
22
+ end
23
+
24
+ it 'prints a ruby require for each dependency' do
25
+ expect(subject).to receive(:print_require).with('test/bar.pb', false)
26
+ expect(subject).to receive(:print_require).with('test/baz.pb', false)
27
+ subject.print_import_requires
28
+ end
29
+
30
+ it 'prints a ruby require_relative for each dependency if environment variable was set' do
31
+ expect(subject).to receive(:print_require).with('test/bar.pb', true)
32
+ expect(subject).to receive(:print_require).with('test/baz.pb', true)
33
+ ENV['PB_REQUIRE_RELATIVE'] = 'true'
34
+ subject.print_import_requires
35
+ ENV.delete('PB_REQUIRE_RELATIVE')
36
+ end
37
+ end
38
+
39
+ describe '#compile' do
40
+ it 'generates the file contents' do
41
+ subject.compile
42
+ expect(subject.to_s).to eq <<EOF
43
+ # encoding: utf-8
44
+
45
+ ##
46
+ # This file is auto-generated. DO NOT EDIT!
47
+ #
48
+ require 'protobuf'
49
+
50
+ EOF
51
+ end
52
+
53
+ it 'generates the file contents using default package name' do
54
+ allow(ENV).to receive(:key?).with('PB_ALLOW_DEFAULT_PACKAGE_NAME')
55
+ .and_return(true)
56
+ subject.compile
57
+ expect(subject.to_s).to eq <<EOF
58
+ # encoding: utf-8
59
+
60
+ ##
61
+ # This file is auto-generated. DO NOT EDIT!
62
+ #
63
+ require 'protobuf'
64
+
65
+ module Foo
66
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
67
+ end
68
+
69
+ EOF
70
+ end
71
+
72
+ context 'with extended messages' do
73
+ let(:descriptor_fields) do
74
+ base_descriptor_fields.merge(
75
+ :package => 'test.pkg.file_generator_spec',
76
+ :extension => [{
77
+ :name => 'boom',
78
+ :number => 20_000,
79
+ :label => Google::Protobuf::FieldDescriptorProto::Label::LABEL_OPTIONAL,
80
+ :type => Google::Protobuf::FieldDescriptorProto::Type::TYPE_STRING,
81
+ :extendee => '.google.protobuf.FieldOptions',
82
+ }],
83
+ )
84
+ end
85
+
86
+ it 'generates the file contents that include the namespaced extension name' do
87
+ subject.compile
88
+ expect(subject.to_s).to eq <<EOF
89
+ # encoding: utf-8
90
+
91
+ ##
92
+ # This file is auto-generated. DO NOT EDIT!
93
+ #
94
+ require 'protobuf'
95
+
96
+ module Test
97
+ module Pkg
98
+ module File_generator_spec
99
+ ::Protobuf::Optionable.inject(self) { ::Google::Protobuf::FileOptions }
100
+
101
+ ##
102
+ # Extended Message Fields
103
+ #
104
+ class ::Google::Protobuf::FieldOptions < ::Protobuf::Message
105
+ optional :string, :".test.pkg.file_generator_spec.boom", 20000, :extension => true
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ end
113
+
114
+ EOF
115
+ end
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ require 'protobuf/generators/service_generator'
4
+
5
+ RSpec.describe ::Protobuf::Generators::ServiceGenerator do
6
+
7
+ class ::CustomMethodEnum < ::Protobuf::Enum
8
+ define :BOOM, 1
9
+ define :BAM, 2
10
+ end
11
+
12
+ class ::CustomMethodMessage < ::Protobuf::Message
13
+ optional :string, :foo, 1
14
+ end
15
+
16
+ class ::Google::Protobuf::MethodOptions < ::Protobuf::Message
17
+ optional :string, :custom_string_option, 22000, :extension => true
18
+ optional :bool, :custom_bool_option, 22001, :extension => true
19
+ optional :int32, :custom_int32_option, 22002, :extension => true
20
+ optional ::CustomMethodEnum, :custom_enum_option, 22003, :extension => true
21
+ optional ::CustomMethodMessage, :custom_message_option, 22004, :extension => true
22
+ end
23
+
24
+ let(:methods) do
25
+ [
26
+ { :name => 'Search', :input_type => 'FooRequest', :output_type => 'FooResponse' },
27
+ {
28
+ :name => 'FooBar',
29
+ :input_type => '.foo.Request',
30
+ :output_type => '.bar.Response',
31
+ :options => {
32
+ :custom_string_option => 'boom',
33
+ :custom_bool_option => true,
34
+ :custom_int32_option => 123,
35
+ :custom_message_option => CustomMethodMessage.new(:foo => 'bam'),
36
+ :custom_enum_option => CustomMethodEnum::BAM,
37
+ },
38
+ },
39
+ ]
40
+ end
41
+ let(:service_fields) do
42
+ {
43
+ :name => 'TestService',
44
+ :method => methods,
45
+ }
46
+ end
47
+
48
+ let(:service) { ::Google::Protobuf::ServiceDescriptorProto.new(service_fields) }
49
+
50
+ subject(:service_generator) { described_class.new(service) }
51
+
52
+ describe '#compile' do
53
+ let(:compiled) do
54
+ <<EOF
55
+ class TestService < ::Protobuf::Rpc::Service
56
+ rpc :search, FooRequest, FooResponse
57
+ rpc :foo_bar, ::Foo::Request, ::Bar::Response do
58
+ set_option :custom_string_option, "boom"
59
+ set_option :custom_bool_option, true
60
+ set_option :custom_int32_option, 123
61
+ set_option :custom_enum_option, ::CustomMethodEnum::BAM
62
+ set_option :custom_message_option, { :foo => "bam" }
63
+ end
64
+ end
65
+
66
+ EOF
67
+ end
68
+
69
+ it "compiles the service and it's rpc methods" do
70
+ subject.compile
71
+ expect(subject.to_s).to eq(compiled)
72
+ end
73
+
74
+ context 'with PB_USE_RAW_RPC_NAMES in the environemnt' do
75
+ let(:compiled) do
76
+ <<EOF
77
+ class TestService < ::Protobuf::Rpc::Service
78
+ rpc :Search, FooRequest, FooResponse
79
+ rpc :FooBar, ::Foo::Request, ::Bar::Response do
80
+ set_option :custom_string_option, "boom"
81
+ set_option :custom_bool_option, true
82
+ set_option :custom_int32_option, 123
83
+ set_option :custom_enum_option, ::CustomMethodEnum::BAM
84
+ set_option :custom_message_option, { :foo => "bam" }
85
+ end
86
+ end
87
+
88
+ EOF
89
+ end
90
+
91
+ before { allow(ENV).to receive(:key?).with('PB_USE_RAW_RPC_NAMES').and_return(true) }
92
+
93
+ it 'uses the raw RPC name and does not underscore it' do
94
+ subject.compile
95
+ expect(subject.to_s).to eq(compiled)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+ require 'protobuf/lifecycle'
3
+
4
+ RSpec.describe ::Protobuf::Lifecycle do
5
+ subject { described_class }
6
+
7
+ around do |example|
8
+ # this entire class is deprecated
9
+ ::Protobuf.deprecator.silence(&example)
10
+ end
11
+
12
+ before do
13
+ ::ActiveSupport::Notifications.notifier = ::ActiveSupport::Notifications::Fanout.new
14
+ end
15
+
16
+ it "registers a string as the event_name" do
17
+ expect(::ActiveSupport::Notifications).to receive(:subscribe).with("something")
18
+ subject.register("something") { true }
19
+ end
20
+
21
+ it "only registers blocks for event callbacks" do
22
+ expect do
23
+ subject.register("something")
24
+ end.to raise_error(/block/)
25
+ end
26
+
27
+ it "calls the registered block when triggered" do
28
+ this = nil
29
+ subject.register("this") do
30
+ this = "not nil"
31
+ end
32
+
33
+ subject.trigger("this")
34
+ expect(this).to_not be_nil
35
+ expect(this).to eq("not nil")
36
+ end
37
+
38
+ it "calls multiple registered blocks when triggered" do
39
+ this = nil
40
+ that = nil
41
+
42
+ subject.register("this") do
43
+ this = "not nil"
44
+ end
45
+
46
+ subject.register("this") do
47
+ that = "not nil"
48
+ end
49
+
50
+ subject.trigger("this")
51
+ expect(this).to_not be_nil
52
+ expect(this).to eq("not nil")
53
+ expect(that).to_not be_nil
54
+ expect(that).to eq("not nil")
55
+ end
56
+
57
+ context 'when the registered block has arity' do
58
+ context 'and the triggered event does not have args' do
59
+ it 'does not pass the args' do
60
+ outer_bar = nil
61
+
62
+ subject.register('foo') do |bar|
63
+ expect(bar).to be_nil
64
+ outer_bar = 'triggered'
65
+ end
66
+
67
+ subject.trigger('foo')
68
+ expect(outer_bar).to eq 'triggered'
69
+ end
70
+ end
71
+
72
+ context 'and the triggered event has arguments' do
73
+ it 'does not pass the args' do
74
+ outer_bar = nil
75
+
76
+ subject.register('foo') do |bar|
77
+ expect(bar).to_not be_nil
78
+ outer_bar = bar
79
+ end
80
+
81
+ subject.trigger('foo', 'baz')
82
+ expect(outer_bar).to eq 'baz'
83
+ end
84
+ end
85
+ end
86
+
87
+ context "normalized event names" do
88
+ specify { expect(subject.normalized_event_name(:derp)).to eq("derp") }
89
+ specify { expect(subject.normalized_event_name(:Derp)).to eq("derp") }
90
+ specify { expect(subject.normalized_event_name("DERP")).to eq("derp") }
91
+ specify { expect(subject.normalized_event_name("derp")).to eq("derp") }
92
+ end
93
+
94
+ end
@@ -0,0 +1,944 @@
1
+ # encoding: utf-8
2
+
3
+ require 'stringio'
4
+ require 'spec_helper'
5
+ require PROTOS_PATH.join('resource.pb')
6
+ require PROTOS_PATH.join('enum.pb')
7
+
8
+ RSpec.describe Protobuf::Message do
9
+
10
+ describe '.decode' do
11
+ let(:message) { ::Test::Resource.new(:name => "Jim") }
12
+
13
+ it 'creates a new message object decoded from the given bytes' do
14
+ expect(::Test::Resource.decode(message.encode)).to eq message
15
+ end
16
+
17
+ context 'with a new enum value' do
18
+ let(:older_message) do
19
+ Class.new(Protobuf::Message) do
20
+ enum_class = Class.new(::Protobuf::Enum) do
21
+ define :YAY, 1
22
+ end
23
+
24
+ optional enum_class, :enum_field, 1
25
+ repeated enum_class, :enum_list, 2
26
+ end
27
+ end
28
+
29
+ let(:newer_message) do
30
+ Class.new(Protobuf::Message) do
31
+ enum_class = Class.new(::Protobuf::Enum) do
32
+ define :YAY, 1
33
+ define :HOORAY, 2
34
+ end
35
+
36
+ optional enum_class, :enum_field, 1
37
+ repeated enum_class, :enum_list, 2
38
+ end
39
+ end
40
+
41
+ context 'with a singular field' do
42
+ it 'treats the field as if it was unset when decoding' do
43
+ newer = newer_message.new(:enum_field => :HOORAY).serialize
44
+
45
+ expect(older_message.decode(newer).enum_field!).to be_nil
46
+ end
47
+
48
+ it 'rejects an unknown value when using the constructor' do
49
+ expect { older_message.new(:enum_field => :HOORAY) }.to raise_error(TypeError)
50
+ end
51
+
52
+ it 'rejects an unknown value when the setter' do
53
+ older = older_message.new
54
+ expect { older.enum_field = :HOORAY }.to raise_error(TypeError)
55
+ end
56
+ end
57
+
58
+ context 'with a repeated field' do
59
+ it 'treats the field as if it was unset when decoding' do
60
+ newer = newer_message.new(:enum_list => [:HOORAY]).serialize
61
+
62
+ expect(older_message.decode(newer).enum_list).to eq([])
63
+ end
64
+
65
+ it 'rejects an unknown value when using the constructor' do
66
+ expect { older_message.new(:enum_list => [:HOORAY]) }.to raise_error(TypeError)
67
+ end
68
+
69
+ it 'rejects an unknown value when the setter' do
70
+ older = older_message.new
71
+ expect { older.enum_field = [:HOORAY] }.to raise_error(TypeError)
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '.decode_from' do
78
+ let(:message) { ::Test::Resource.new(:name => "Jim") }
79
+
80
+ it 'creates a new message object decoded from the given byte stream' do
81
+ stream = ::StringIO.new(message.encode)
82
+ expect(::Test::Resource.decode_from(stream)).to eq message
83
+ end
84
+ end
85
+
86
+ describe 'defining a new field' do
87
+ context 'when defining a field with a tag that has already been used' do
88
+ it 'raises a TagCollisionError' do
89
+ expect do
90
+ Class.new(Protobuf::Message) do
91
+ optional ::Protobuf::Field::Int32Field, :foo, 1
92
+ optional ::Protobuf::Field::Int32Field, :bar, 1
93
+ end
94
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 1 has already been used/)
95
+ end
96
+ end
97
+
98
+ context 'when defining an extension field with a tag that has already been used' do
99
+ it 'raises a TagCollisionError' do
100
+ expect do
101
+ Class.new(Protobuf::Message) do
102
+ extensions 100...110
103
+ optional ::Protobuf::Field::Int32Field, :foo, 100
104
+ optional ::Protobuf::Field::Int32Field, :bar, 100, :extension => true
105
+ end
106
+ end.to raise_error(Protobuf::TagCollisionError, /Field number 100 has already been used/)
107
+ end
108
+ end
109
+
110
+ context 'when defining a field with a name that has already been used' do
111
+ it 'raises a DuplicateFieldNameError' do
112
+ expect do
113
+ Class.new(Protobuf::Message) do
114
+ optional ::Protobuf::Field::Int32Field, :foo, 1
115
+ optional ::Protobuf::Field::Int32Field, :foo, 2
116
+ end
117
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
118
+ end
119
+ end
120
+
121
+ context 'when defining an extension field with a name that has already been used' do
122
+ it 'raises a DuplicateFieldNameError' do
123
+ expect do
124
+ Class.new(Protobuf::Message) do
125
+ extensions 100...110
126
+ optional ::Protobuf::Field::Int32Field, :foo, 1
127
+ optional ::Protobuf::Field::Int32Field, :foo, 100, :extension => true
128
+ end
129
+ end.to raise_error(Protobuf::DuplicateFieldNameError, /Field name foo has already been used/)
130
+ end
131
+ end
132
+ end
133
+
134
+ describe '.encode' do
135
+ let(:values) { { :name => "Jim" } }
136
+
137
+ it 'creates a new message object with the given values and returns the encoded bytes' do
138
+ expect(::Test::Resource.encode(values)).to eq ::Test::Resource.new(values).encode
139
+ end
140
+ end
141
+
142
+ describe '#initialize' do
143
+ it "defaults to the first value listed in the enum's type definition" do
144
+ test_enum = Test::EnumTestMessage.new
145
+ expect(test_enum.non_default_enum).to eq(Test::EnumTestType.enums.first)
146
+ end
147
+
148
+ it "defaults to a a value with a name" do
149
+ test_enum = Test::EnumTestMessage.new
150
+ expect(test_enum.non_default_enum.name).to eq(Test::EnumTestType.enums.first.name)
151
+ end
152
+
153
+ it "exposes the enum getter raw value through ! method" do
154
+ test_enum = Test::EnumTestMessage.new
155
+ expect(test_enum.non_default_enum!).to be_nil
156
+ end
157
+
158
+ it "exposes the enum getter raw value through ! method (when set)" do
159
+ test_enum = Test::EnumTestMessage.new
160
+ test_enum.non_default_enum = 1
161
+ expect(test_enum.non_default_enum!).to eq(1)
162
+ end
163
+
164
+ it "does not try to set attributes which have nil values" do
165
+ expect_any_instance_of(Test::EnumTestMessage).not_to receive("non_default_enum=")
166
+ Test::EnumTestMessage.new(:non_default_enum => nil)
167
+ end
168
+
169
+ it "takes a hash as an initialization argument" do
170
+ test_enum = Test::EnumTestMessage.new(:non_default_enum => 2)
171
+ expect(test_enum.non_default_enum).to eq(2)
172
+ end
173
+
174
+ it "initializes with an object that responds to #to_hash" do
175
+ hashie_object = OpenStruct.new(:to_hash => { :non_default_enum => 2 })
176
+ test_enum = Test::EnumTestMessage.new(hashie_object)
177
+ expect(test_enum.non_default_enum).to eq(2)
178
+ end
179
+
180
+ it "initializes with an object with a block" do
181
+ test_enum = Test::EnumTestMessage.new { |p| p.non_default_enum = 2 }
182
+ expect(test_enum.non_default_enum).to eq(2)
183
+ end
184
+
185
+ # to be deprecated
186
+ it "allows you to pass nil to repeated fields" do
187
+ test = Test::Resource.new(:repeated_enum => nil)
188
+ expect(test.repeated_enum).to eq([])
189
+ end
190
+ end
191
+
192
+ describe '#encode' do
193
+ context "encoding" do
194
+ it "accepts UTF-8 strings into string fields" do
195
+ message = ::Test::Resource.new(:name => "Kyle Redfearn\u0060s iPad")
196
+
197
+ expect { message.encode }.to_not raise_error
198
+ end
199
+
200
+ it "keeps utf-8 when utf-8 is input for string fields" do
201
+ name = 'my name💩'
202
+ name.force_encoding(Encoding::UTF_8)
203
+
204
+ message = ::Test::Resource.new(:name => name)
205
+ new_message = ::Test::Resource.decode(message.encode)
206
+ expect(new_message.name == name).to be true
207
+ end
208
+
209
+ it "trims binary when binary is input for string fields" do
210
+ name = "my name\xC3"
211
+ name.force_encoding(Encoding::BINARY)
212
+
213
+ message = ::Test::Resource.new(:name => name)
214
+ new_message = ::Test::Resource.decode(message.encode)
215
+ expect(new_message.name == "my name").to be true
216
+ end
217
+ end
218
+
219
+ context "when there's no value for a required field" do
220
+ let(:message) { ::Test::ResourceWithRequiredField.new }
221
+
222
+ it "raises a 'message not initialized' error" do
223
+ expect do
224
+ message.encode
225
+ end.to raise_error(Protobuf::SerializationError, /required/i)
226
+ end
227
+ end
228
+
229
+ context "repeated fields" do
230
+ let(:message) { ::Test::Resource.new(:name => "something") }
231
+
232
+ it "does not raise an error when repeated fields are []" do
233
+ expect do
234
+ message.repeated_enum = []
235
+ message.encode
236
+ end.to_not raise_error
237
+ end
238
+
239
+ it "sets the value to nil when empty array is passed" do
240
+ message.repeated_enum = []
241
+ expect(message.instance_variable_get("@values")[:repeated_enum]).to be_nil
242
+ end
243
+
244
+ it "does not compact the edit original array" do
245
+ a = [nil].freeze
246
+ message.repeated_enum = a
247
+ expect(message.repeated_enum).to eq([])
248
+ expect(a).to eq([nil].freeze)
249
+ end
250
+
251
+ it "compacts the set array" do
252
+ message.repeated_enum = [nil]
253
+ expect(message.repeated_enum).to eq([])
254
+ end
255
+
256
+ it "raises TypeError when a non-array replaces it" do
257
+ expect do
258
+ message.repeated_enum = 2
259
+ end.to raise_error(/value of type/)
260
+ end
261
+ end
262
+ end
263
+
264
+ describe "boolean predicate methods" do
265
+ subject { Test::ResourceFindRequest.new(:name => "resource") }
266
+
267
+ it { is_expected.to respond_to(:active?) }
268
+
269
+ it "sets the predicate to true when the boolean value is true" do
270
+ subject.active = true
271
+ expect(subject.active?).to be true
272
+ end
273
+
274
+ it "sets the predicate to false when the boolean value is false" do
275
+ subject.active = false
276
+ expect(subject.active?).to be false
277
+ end
278
+
279
+ it "does not put predicate methods on non-boolean fields" do
280
+ expect(Test::ResourceFindRequest.new(:name => "resource")).to_not respond_to(:name?)
281
+ end
282
+ end
283
+
284
+ describe "#respond_to_and_has?" do
285
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
286
+
287
+ it "is false when the message does not have the field" do
288
+ expect(subject.respond_to_and_has?(:other_field)).to be false
289
+ end
290
+
291
+ it "is true when the message has the field" do
292
+ expect(subject.respond_to_and_has?(:non_default_enum)).to be true
293
+ end
294
+ end
295
+
296
+ describe "#respond_to_has_and_present?" do
297
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
298
+
299
+ it "is false when the message does not have the field" do
300
+ expect(subject.respond_to_and_has_and_present?(:other_field)).to be false
301
+ end
302
+
303
+ it "is false when the field is repeated and a value is not present" do
304
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
305
+ end
306
+
307
+ it "is false when the field is repeated and the value is empty array" do
308
+ subject.repeated_enums = []
309
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be false
310
+ end
311
+
312
+ it "is true when the field is repeated and a value is present" do
313
+ subject.repeated_enums = [2]
314
+ expect(subject.respond_to_and_has_and_present?(:repeated_enums)).to be true
315
+ end
316
+
317
+ it "is true when the message has the field" do
318
+ expect(subject.respond_to_and_has_and_present?(:non_default_enum)).to be true
319
+ end
320
+
321
+ context "#API" do
322
+ subject { Test::EnumTestMessage.new(:non_default_enum => 2) }
323
+
324
+ specify { expect(subject).to respond_to(:respond_to_and_has_and_present?) }
325
+ specify { expect(subject).to respond_to(:responds_to_and_has_and_present?) }
326
+ specify { expect(subject).to respond_to(:responds_to_has?) }
327
+ specify { expect(subject).to respond_to(:respond_to_has?) }
328
+ specify { expect(subject).to respond_to(:respond_to_has_present?) }
329
+ specify { expect(subject).to respond_to(:responds_to_has_present?) }
330
+ specify { expect(subject).to respond_to(:respond_to_and_has_present?) }
331
+ specify { expect(subject).to respond_to(:responds_to_and_has_present?) }
332
+ end
333
+
334
+ end
335
+
336
+ describe '#inspect' do
337
+ let(:klass) do
338
+ Class.new(Protobuf::Message) do |klass|
339
+ enum_class = Class.new(Protobuf::Enum) do
340
+ define :YAY, 1
341
+ end
342
+
343
+ klass.const_set(:EnumKlass, enum_class)
344
+
345
+ optional :string, :name, 1
346
+ repeated :int32, :counts, 2
347
+ optional enum_class, :enum, 3
348
+ end
349
+ end
350
+
351
+ before { stub_const('MyMessage', klass) }
352
+
353
+ it 'lists the fields' do
354
+ proto = klass.new(:name => 'wooo', :counts => [1, 2, 3], :enum => klass::EnumKlass::YAY)
355
+ expect(proto.inspect).to eq('#<MyMessage name="wooo" counts=[1, 2, 3] enum=#<Protobuf::Enum(MyMessage::EnumKlass)::YAY=1>>')
356
+ end
357
+ end
358
+
359
+ describe '#to_hash' do
360
+ context 'generating values for an ENUM field' do
361
+ it 'converts the enum to its tag representation' do
362
+ hash = Test::EnumTestMessage.new(:non_default_enum => :TWO).to_hash
363
+ expect(hash).to eq(:non_default_enum => 2)
364
+ end
365
+
366
+ it 'does not populate default values' do
367
+ hash = Test::EnumTestMessage.new.to_hash
368
+ expect(hash).to eq({})
369
+ end
370
+
371
+ it 'converts repeated enum fields to an array of the tags' do
372
+ hash = Test::EnumTestMessage.new(:repeated_enums => [:ONE, :TWO, :TWO, :ONE]).to_hash
373
+ expect(hash).to eq(:repeated_enums => [1, 2, 2, 1])
374
+ end
375
+ end
376
+
377
+ context 'generating values for a Message field' do
378
+ it 'recursively hashes field messages' do
379
+ hash = Test::Nested.new(:resource => { :name => 'Nested' }).to_hash
380
+ expect(hash).to eq(:resource => { :name => 'Nested' })
381
+ end
382
+
383
+ it 'recursively hashes a repeated set of messages' do
384
+ proto = Test::Nested.new(
385
+ :multiple_resources => [
386
+ Test::Resource.new(:name => 'Resource 1'),
387
+ Test::Resource.new(:name => 'Resource 2'),
388
+ ],
389
+ )
390
+
391
+ expect(proto.to_hash).to eq(
392
+ :multiple_resources => [
393
+ { :name => 'Resource 1' },
394
+ { :name => 'Resource 2' },
395
+ ],
396
+ )
397
+ end
398
+ end
399
+
400
+ it 'uses simple field names as keys when possible and fully qualified names otherwise' do
401
+ message = Class.new(::Protobuf::Message) do
402
+ optional :int32, :field, 1
403
+ optional :int32, :colliding_field, 2
404
+ extensions 100...200
405
+ optional :int32, :".ext.normal_ext_field", 100, :extension => true
406
+ optional :int32, :".ext.colliding_field", 101, :extension => true
407
+ optional :int32, :".ext.colliding_field2", 102, :extension => true
408
+ optional :int32, :".ext2.colliding_field2", 103, :extension => true
409
+ end
410
+
411
+ hash = {
412
+ :field => 1,
413
+ :colliding_field => 2,
414
+ :normal_ext_field => 3,
415
+ :".ext.colliding_field" => 4,
416
+ :".ext.colliding_field2" => 5,
417
+ :".ext2.colliding_field2" => 6,
418
+ }
419
+ instance = message.new(hash)
420
+ expect(instance.to_hash).to eq(hash)
421
+ end
422
+ end
423
+
424
+ describe '#to_json' do
425
+ subject do
426
+ ::Test::ResourceFindRequest.new(:name => 'Test Name', :active => false)
427
+ end
428
+
429
+ specify { expect(subject.to_json).to eq '{"name":"Test Name","active":false}' }
430
+
431
+ context 'for byte fields' do
432
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
433
+
434
+ subject do
435
+ ::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
436
+ end
437
+
438
+ specify { expect(subject.to_json).to eq '{"widget_bytes":["Bo0xSFAXOmI="]}' }
439
+ end
440
+
441
+ context 'using lower camel case field names' do
442
+ let(:bytes) { "\x06\x8D1HP\x17:b".force_encoding(Encoding::ASCII_8BIT) }
443
+
444
+ subject do
445
+ ::Test::ResourceFindRequest.new(:widget_bytes => [bytes])
446
+ end
447
+
448
+ specify { expect(subject.to_json(:lower_camel_case => true)).to eq '{"widgetBytes":["Bo0xSFAXOmI="]}' }
449
+ end
450
+ end
451
+
452
+ describe '.from_json' do
453
+ it 'decodes optional bytes field with base64' do
454
+ expected_single_bytes = "\x06\x8D1HP\x17:b".unpack('C*')
455
+ single_bytes = ::Test::ResourceFindRequest
456
+ .from_json('{"singleBytes":"Bo0xSFAXOmI="}')
457
+ .single_bytes.unpack('C*')
458
+
459
+ expect(single_bytes).to(eq(expected_single_bytes))
460
+ end
461
+
462
+ it 'decodes repeated bytes field with base64' do
463
+ expected_widget_bytes = ["\x06\x8D1HP\x17:b"].map { |s| s.unpack('C*') }
464
+ widget_bytes = ::Test::ResourceFindRequest
465
+ .from_json('{"widgetBytes":["Bo0xSFAXOmI="]}')
466
+ .widget_bytes.map { |s| s.unpack('C*') }
467
+
468
+ expect(widget_bytes).to(eq(expected_widget_bytes))
469
+ end
470
+ end
471
+
472
+ describe '.to_json' do
473
+ it 'returns the class name of the message for use in json encoding' do
474
+ expect do
475
+ ::Timeout.timeout(0.1) do
476
+ expect(::Test::Resource.to_json).to eq("Test::Resource")
477
+ end
478
+ end.not_to raise_error
479
+ end
480
+ end
481
+
482
+ describe "#define_accessor" do
483
+ subject { ::Test::Resource.new }
484
+
485
+ it 'allows string fields to be set to nil' do
486
+ expect { subject.name = nil }.to_not raise_error
487
+ end
488
+
489
+ it 'does not allow string fields to be set to Numeric' do
490
+ expect { subject.name = 1 }.to raise_error(/name/)
491
+ end
492
+
493
+ it 'does not allow a repeated field is set to nil' do
494
+ expect { subject.repeated_enum = nil }.to raise_error(TypeError)
495
+ end
496
+
497
+ context '#{simple_field_name}!' do
498
+ it 'returns value of set field' do
499
+ expect(::Test::Resource.new(:name => "Joe").name!).to eq("Joe")
500
+ end
501
+
502
+ it 'returns value of set field with default' do
503
+ expect(::Test::Resource.new(:name => "").name!).to eq("")
504
+ end
505
+
506
+ it 'returns nil if extension field is unset' do
507
+ expect(subject.ext_is_searchable!).to be_nil
508
+ end
509
+
510
+ it 'returns value of set extension field' do
511
+ message = ::Test::Resource.new(:ext_is_searchable => true)
512
+ expect(message.ext_is_searchable!).to be(true)
513
+ end
514
+
515
+ it 'returns value of set extension field with default' do
516
+ message = ::Test::Resource.new(:ext_is_searchable => false)
517
+ expect(message.ext_is_searchable!).to be(false)
518
+ end
519
+
520
+ it 'returns nil for an unset repeated field that has only be read' do
521
+ message = ::Test::Resource.new
522
+ expect(message.repeated_enum!).to be_nil
523
+ message.repeated_enum
524
+ expect(message.repeated_enum!).to be_nil
525
+ end
526
+
527
+ it 'returns value for an unset repeated field has been read and appended to' do
528
+ message = ::Test::Resource.new
529
+ message.repeated_enum << 1
530
+ expect(message.repeated_enum!).to eq([1])
531
+ end
532
+
533
+ it 'returns value for an unset repeated field has been explicitly set' do
534
+ message = ::Test::Resource.new
535
+ message.repeated_enum = [1]
536
+ expect(message.repeated_enum!).to eq([1])
537
+ end
538
+ end
539
+ end
540
+
541
+ describe '.get_extension_field' do
542
+ it 'fetches an extension field by its tag' do
543
+ field = ::Test::Resource.get_extension_field(100)
544
+ expect(field).to be_a(::Protobuf::Field::BoolField)
545
+ expect(field.tag).to eq(100)
546
+ expect(field.name).to eq(:ext_is_searchable)
547
+ expect(field.fully_qualified_name).to eq(:'.test.Searchable.ext_is_searchable')
548
+ expect(field).to be_extension
549
+ end
550
+
551
+ it 'fetches an extension field by its symbolized name' do
552
+ expect(::Test::Resource.get_extension_field(:ext_is_searchable)).to be_a(::Protobuf::Field::BoolField)
553
+ expect(::Test::Resource.get_extension_field('ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
554
+ expect(::Test::Resource.get_extension_field(:'.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
555
+ expect(::Test::Resource.get_extension_field('.test.Searchable.ext_is_searchable')).to be_a(::Protobuf::Field::BoolField)
556
+ end
557
+
558
+ it 'returns nil when attempting to get a non-extension field' do
559
+ expect(::Test::Resource.get_extension_field(1)).to be_nil
560
+ end
561
+
562
+ it 'returns nil when field is not found' do
563
+ expect(::Test::Resource.get_extension_field(-1)).to be_nil
564
+ expect(::Test::Resource.get_extension_field(nil)).to be_nil
565
+ end
566
+ end
567
+
568
+ describe '#field?' do
569
+ it 'returns false for non-existent field' do
570
+ expect(::Test::Resource.get_field('doesnotexist')).to be_nil
571
+ expect(::Test::Resource.new.field?('doesnotexist')).to be(false)
572
+ end
573
+
574
+ it 'returns false for unset field' do
575
+ expect(::Test::Resource.get_field('name')).to be
576
+ expect(::Test::Resource.new.field?('name')).to be(false)
577
+ end
578
+
579
+ it 'returns false for unset field from tag' do
580
+ expect(::Test::Resource.get_field(1)).to be
581
+ expect(::Test::Resource.new.field?(1)).to be(false)
582
+ end
583
+
584
+ it 'returns true for set field' do
585
+ expect(::Test::Resource.new(:name => "Joe").field?('name')).to be(true)
586
+ end
587
+
588
+ it 'returns true for set field with default' do
589
+ expect(::Test::Resource.new(:name => "").field?('name')).to be(true)
590
+ end
591
+
592
+ it 'returns true from field tag value' do
593
+ expect(::Test::Resource.new(:name => "Joe").field?(1)).to be(true)
594
+ end
595
+
596
+ it 'returns false for unset extension field' do
597
+ ext_field = :".test.Searchable.ext_is_searchable"
598
+ expect(::Test::Resource.get_extension_field(ext_field)).to be
599
+ expect(::Test::Resource.new.field?(ext_field)).to be(false)
600
+ end
601
+
602
+ it 'returns false for unset extension field from tag' do
603
+ expect(::Test::Resource.get_extension_field(100)).to be
604
+ expect(::Test::Resource.new.field?(100)).to be(false)
605
+ end
606
+
607
+ it 'returns true for set extension field' do
608
+ ext_field = :".test.Searchable.ext_is_searchable"
609
+ message = ::Test::Resource.new(ext_field => true)
610
+ expect(message.field?(ext_field)).to be(true)
611
+ end
612
+
613
+ it 'returns true for set extension field with default' do
614
+ ext_field = :".test.Searchable.ext_is_searchable"
615
+ message = ::Test::Resource.new(ext_field => false)
616
+ expect(message.field?(ext_field)).to be(true)
617
+ end
618
+
619
+ it 'returns true for set extension field from tag' do
620
+ ext_field = :".test.Searchable.ext_is_searchable"
621
+ message = ::Test::Resource.new(ext_field => false)
622
+ expect(message.field?(100)).to be(true)
623
+ end
624
+
625
+ it 'returns false for repeated field that has been read from' do
626
+ message = ::Test::Resource.new
627
+ expect(message.field?(:repeated_enum)).to be(false)
628
+ message.repeated_enum
629
+ expect(message.field?(:repeated_enum)).to be(false)
630
+ end
631
+
632
+ it 'returns true for a repeated field that has been read from and appended to' do
633
+ message = ::Test::Resource.new
634
+ message.repeated_enum << 1
635
+ expect(message.field?(:repeated_enum)).to be(true)
636
+ end
637
+
638
+ it 'returns true for a repeated field that has been set with the setter' do
639
+ message = ::Test::Resource.new
640
+ message.repeated_enum = [1]
641
+ expect(message.field?(:repeated_enum)).to be(true)
642
+ end
643
+
644
+ it 'returns false for a repeated field that has been replaced with []' do
645
+ message = ::Test::Resource.new
646
+ message.repeated_enum.replace([])
647
+ expect(message.field?(:repeated_enum)).to be(false)
648
+ end
649
+ end
650
+
651
+ describe '.get_field' do
652
+ it 'fetches a non-extension field by its tag' do
653
+ field = ::Test::Resource.get_field(1)
654
+ expect(field).to be_a(::Protobuf::Field::StringField)
655
+ expect(field.tag).to eq(1)
656
+ expect(field.name).to eq(:name)
657
+ expect(field.fully_qualified_name).to eq(:name)
658
+ expect(field).not_to be_extension
659
+ end
660
+
661
+ it 'fetches a non-extension field by its symbolized name' do
662
+ expect(::Test::Resource.get_field(:name)).to be_a(::Protobuf::Field::StringField)
663
+ expect(::Test::Resource.get_field('name')).to be_a(::Protobuf::Field::StringField)
664
+ end
665
+
666
+ it 'fetches an extension field when forced' do
667
+ expect(::Test::Resource.get_field(100, true)).to be_a(::Protobuf::Field::BoolField)
668
+ expect(::Test::Resource.get_field(:'.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
669
+ expect(::Test::Resource.get_field('.test.Searchable.ext_is_searchable', true)).to be_a(::Protobuf::Field::BoolField)
670
+ end
671
+
672
+ it 'returns nil when attempting to get an extension field' do
673
+ expect(::Test::Resource.get_field(100)).to be_nil
674
+ end
675
+
676
+ it 'returns nil when field is not defined' do
677
+ expect(::Test::Resource.get_field(-1)).to be_nil
678
+ expect(::Test::Resource.get_field(nil)).to be_nil
679
+ end
680
+ end
681
+
682
+ describe 'defining a field' do
683
+ # Case 1
684
+ context 'single base field' do
685
+ let(:klass) do
686
+ Class.new(Protobuf::Message) do
687
+ optional :string, :foo, 1
688
+ end
689
+ end
690
+
691
+ it 'has an accessor for foo' do
692
+ message = klass.new(:foo => 'bar')
693
+ expect(message.foo).to eq('bar')
694
+ expect(message[:foo]).to eq('bar')
695
+ expect(message['foo']).to eq('bar')
696
+ end
697
+ end
698
+
699
+ # Case 2
700
+ context 'base field and extension field name collision' do
701
+ let(:klass) do
702
+ Class.new(Protobuf::Message) do
703
+ optional :string, :foo, 1
704
+ optional :string, :".boom.foo", 2, :extension => true
705
+ end
706
+ end
707
+
708
+ it 'has an accessor for foo that refers to the base field' do
709
+ message = klass.new(:foo => 'bar', '.boom.foo' => 'bam')
710
+ expect(message.foo).to eq('bar')
711
+ expect(message[:foo]).to eq('bar')
712
+ expect(message['foo']).to eq('bar')
713
+ expect(message[:'.boom.foo']).to eq('bam')
714
+ expect(message['.boom.foo']).to eq('bam')
715
+ end
716
+ end
717
+
718
+ # Case 3
719
+ context 'no base field with extension fields with name collision' do
720
+ let(:klass) do
721
+ Class.new(Protobuf::Message) do
722
+ optional :string, :".boom.foo", 2, :extension => true
723
+ optional :string, :".goat.foo", 3, :extension => true
724
+ end
725
+ end
726
+
727
+ it 'has an accessor for foo that refers to the extension field' do
728
+ message = klass.new('.boom.foo' => 'bam', '.goat.foo' => 'red')
729
+ expect { message.foo }.to raise_error(NoMethodError)
730
+ expect { message[:foo] }.to raise_error(ArgumentError)
731
+ expect { message['foo'] }.to raise_error(ArgumentError)
732
+ expect(message[:'.boom.foo']).to eq('bam')
733
+ expect(message['.boom.foo']).to eq('bam')
734
+ expect(message[:'.goat.foo']).to eq('red')
735
+ expect(message['.goat.foo']).to eq('red')
736
+ end
737
+ end
738
+
739
+ # Case 4
740
+ context 'no base field with an extension field' do
741
+ let(:klass) do
742
+ Class.new(Protobuf::Message) do
743
+ optional :string, :".boom.foo", 2, :extension => true
744
+ end
745
+ end
746
+
747
+ it 'has an accessor for foo that refers to the extension field' do
748
+ message = klass.new('.boom.foo' => 'bam')
749
+ expect(message.foo).to eq('bam')
750
+ expect(message[:foo]).to eq('bam')
751
+ expect(message['foo']).to eq('bam')
752
+ expect(message[:'.boom.foo']).to eq('bam')
753
+ expect(message['.boom.foo']).to eq('bam')
754
+ end
755
+ end
756
+ end
757
+
758
+ describe 'map fields' do
759
+ it 'serializes the same as equivalent non-map-field' do
760
+ class MessageWithMapField < ::Protobuf::Message
761
+ map :int32, :string, :map, 1
762
+ end
763
+
764
+ class MessageWithoutMapField < ::Protobuf::Message
765
+ class MapEntry < ::Protobuf::Message
766
+ optional :int32, :key, 1
767
+ optional :string, :value, 2
768
+ end
769
+ repeated MapEntry, :map, 1
770
+ end
771
+
772
+ map_msg = MessageWithMapField.new(:map =>
773
+ {
774
+ 1 => 'one',
775
+ 2 => 'two',
776
+ 3 => 'three',
777
+ 4 => 'four',
778
+ })
779
+ mapless_msg = MessageWithoutMapField.new(:map =>
780
+ [{ :key => 1, :value => 'one' },
781
+ { :key => 2, :value => 'two' },
782
+ { :key => 3, :value => 'three' },
783
+ { :key => 4, :value => 'four' },
784
+ ])
785
+
786
+ map_bytes = map_msg.encode
787
+ mapless_bytes = mapless_msg.encode
788
+ expect(map_bytes).to eq(mapless_bytes)
789
+
790
+ expect(MessageWithMapField.decode(mapless_bytes)).to eq(map_msg)
791
+ expect(MessageWithoutMapField.decode(map_bytes)).to eq(mapless_msg)
792
+ end
793
+ end
794
+
795
+ describe '.[]=' do
796
+ context 'clearing fields' do
797
+ it 'clears repeated fields with an empty array' do
798
+ instance = ::Test::Resource.new(:repeated_enum => [::Test::StatusType::ENABLED])
799
+ expect(instance.field?(:repeated_enum)).to be(true)
800
+ instance[:repeated_enum] = []
801
+ expect(instance.field?(:repeated_enum)).to be(false)
802
+ end
803
+
804
+ it 'clears optional fields with nil' do
805
+ instance = ::Test::Resource.new(:name => "Joe")
806
+ expect(instance.field?(:name)).to be(true)
807
+ instance[:name] = nil
808
+ expect(instance.field?(:name)).to be(false)
809
+ end
810
+
811
+ it 'clears optional extenstion fields with nil' do
812
+ instance = ::Test::Resource.new(:ext_is_searchable => true)
813
+ expect(instance.field?(:ext_is_searchable)).to be(true)
814
+ instance[:ext_is_searchable] = nil
815
+ expect(instance.field?(:ext_is_searchable)).to be(false)
816
+ end
817
+ end
818
+
819
+ context 'setting fields' do
820
+ let(:instance) { ::Test::Resource.new }
821
+
822
+ it 'sets and replaces repeated fields' do
823
+ initial = [::Test::StatusType::ENABLED, ::Test::StatusType::DISABLED]
824
+ instance[:repeated_enum] = initial
825
+ expect(instance[:repeated_enum]).to eq(initial)
826
+ replacement = [::Test::StatusType::DELETED]
827
+ instance[:repeated_enum] = replacement
828
+ expect(instance[:repeated_enum]).to eq(replacement)
829
+ end
830
+
831
+ it 'sets acceptable optional field values' do
832
+ instance[:name] = "Joe"
833
+ expect(instance[:name]).to eq("Joe")
834
+ instance[1] = "Tom"
835
+ expect(instance[:name]).to eq("Tom")
836
+ end
837
+
838
+ it 'sets acceptable empty string field values' do
839
+ instance[:name] = ""
840
+ expect(instance.name!).to eq("")
841
+ end
842
+
843
+ it 'sets acceptable empty message field values' do
844
+ instance = ::Test::Nested.new
845
+ instance[:resource] = {}
846
+ expect(instance.resource!).to eq(::Test::Resource.new)
847
+ end
848
+
849
+ it 'sets acceptable extension field values' do
850
+ instance[:ext_is_searchable] = true
851
+ expect(instance[:ext_is_searchable]).to eq(true)
852
+ instance[:".test.Searchable.ext_is_searchable"] = false
853
+ expect(instance[:ext_is_searchable]).to eq(false)
854
+ instance[100] = true
855
+ expect(instance[:ext_is_searchable]).to eq(true)
856
+ end
857
+
858
+ # to be deprecated
859
+ it 'does nothing when sent an empty array' do
860
+ instance[:repeated_enum] = nil
861
+ expect(instance[:repeated_enum]).to eq([])
862
+ instance[:repeated_enum] = [1, 2]
863
+ expect(instance[:repeated_enum]).to eq([1, 2])
864
+ instance[:repeated_enum] = nil
865
+ # Yes this is very silly, but backwards compatible
866
+ expect(instance[:repeated_enum]).to eq([1, 2])
867
+ end
868
+ end
869
+
870
+ context 'throwing TypeError' do
871
+ let(:instance) { ::Test::Resource.new }
872
+
873
+ it 'throws when a repeated value is set with a non array' do
874
+ expect { instance[:repeated_enum] = "string" }.to raise_error(TypeError)
875
+ end
876
+
877
+ it 'throws when a repeated value is set with an array of the wrong type' do
878
+ expect { instance[:repeated_enum] = [true, false] }.to raise_error(TypeError)
879
+ end
880
+
881
+ it 'throws when an optional value is not #acceptable?' do
882
+ expect { instance[:name] = 1 }.to raise_error(TypeError)
883
+ end
884
+ end
885
+
886
+ context 'ignoring unknown fields' do
887
+ around do |example|
888
+ orig = ::Protobuf.ignore_unknown_fields?
889
+ ::Protobuf.ignore_unknown_fields = true
890
+ example.call
891
+ ::Protobuf.ignore_unknown_fields = orig
892
+ end
893
+
894
+ context 'with valid fields' do
895
+ let(:values) { { :name => "Jim" } }
896
+
897
+ it "does not raise an error" do
898
+ expect { ::Test::Resource.new(values) }.to_not raise_error
899
+ end
900
+ end
901
+
902
+ context 'with non-existent field' do
903
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
904
+
905
+ it "does not raise an error" do
906
+ expect { ::Test::Resource.new(values) }.to_not raise_error
907
+ end
908
+ end
909
+ end
910
+
911
+ context 'not ignoring unknown fields' do
912
+ around do |example|
913
+ orig = ::Protobuf.ignore_unknown_fields?
914
+ ::Protobuf.ignore_unknown_fields = false
915
+ example.call
916
+ ::Protobuf.ignore_unknown_fields = orig
917
+ end
918
+
919
+ context 'with valid fields' do
920
+ let(:values) { { :name => "Jim" } }
921
+
922
+ it "does not raise an error" do
923
+ expect { ::Test::Resource.new(values) }.to_not raise_error
924
+ end
925
+ end
926
+
927
+ context 'with non-existent field' do
928
+ let(:values) { { :name => "Jim", :othername => "invalid" } }
929
+
930
+ it "raises an error and mentions the erroneous field" do
931
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
932
+ end
933
+
934
+ context 'with a nil value' do
935
+ let(:values) { { :name => "Jim", :othername => nil } }
936
+
937
+ it "raises an error and mentions the erroneous field" do
938
+ expect { ::Test::Resource.new(values) }.to raise_error(::Protobuf::FieldNotDefinedError, /othername/)
939
+ end
940
+ end
941
+ end
942
+ end
943
+ end
944
+ end