google-protobuf 3.7.0 → 3.21.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of google-protobuf might be problematic. Click here for more details.

Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/ext/google/protobuf_c/convert.c +361 -0
  3. data/ext/google/protobuf_c/convert.h +75 -0
  4. data/ext/google/protobuf_c/defs.c +669 -1646
  5. data/ext/google/protobuf_c/defs.h +107 -0
  6. data/ext/google/protobuf_c/extconf.rb +13 -8
  7. data/ext/google/protobuf_c/map.c +330 -477
  8. data/ext/google/protobuf_c/map.h +66 -0
  9. data/ext/google/protobuf_c/message.c +1048 -379
  10. data/ext/google/protobuf_c/message.h +104 -0
  11. data/ext/google/protobuf_c/protobuf.c +413 -54
  12. data/ext/google/protobuf_c/protobuf.h +53 -546
  13. data/ext/google/protobuf_c/repeated_field.c +318 -315
  14. data/ext/google/protobuf_c/repeated_field.h +63 -0
  15. data/ext/google/protobuf_c/ruby-upb.c +11115 -0
  16. data/ext/google/protobuf_c/ruby-upb.h +5612 -0
  17. data/ext/google/protobuf_c/third_party/utf8_range/LICENSE +21 -0
  18. data/ext/google/protobuf_c/third_party/utf8_range/naive.c +92 -0
  19. data/ext/google/protobuf_c/third_party/utf8_range/range2-neon.c +157 -0
  20. data/ext/google/protobuf_c/third_party/utf8_range/range2-sse.c +170 -0
  21. data/ext/google/protobuf_c/third_party/utf8_range/utf8_range.h +9 -0
  22. data/ext/google/protobuf_c/wrap_memcpy.c +4 -3
  23. data/lib/google/protobuf/any_pb.rb +1 -1
  24. data/lib/google/protobuf/api_pb.rb +4 -3
  25. data/lib/google/protobuf/descriptor_dsl.rb +465 -0
  26. data/lib/google/protobuf/descriptor_pb.rb +269 -0
  27. data/lib/google/protobuf/duration_pb.rb +1 -1
  28. data/lib/google/protobuf/empty_pb.rb +1 -1
  29. data/lib/google/protobuf/field_mask_pb.rb +1 -1
  30. data/lib/google/protobuf/message_exts.rb +2 -2
  31. data/lib/google/protobuf/repeated_field.rb +15 -2
  32. data/lib/google/protobuf/source_context_pb.rb +1 -1
  33. data/lib/google/protobuf/struct_pb.rb +4 -4
  34. data/lib/google/protobuf/timestamp_pb.rb +1 -1
  35. data/lib/google/protobuf/type_pb.rb +9 -8
  36. data/lib/google/protobuf/well_known_types.rb +20 -4
  37. data/lib/google/protobuf/wrappers_pb.rb +9 -9
  38. data/lib/google/protobuf.rb +6 -4
  39. data/tests/basic.rb +455 -77
  40. data/tests/generated_code_test.rb +0 -0
  41. data/tests/stress.rb +1 -1
  42. metadata +27 -30
  43. data/ext/google/protobuf_c/encode_decode.c +0 -1574
  44. data/ext/google/protobuf_c/storage.c +0 -1019
  45. data/ext/google/protobuf_c/upb.c +0 -17318
  46. data/ext/google/protobuf_c/upb.h +0 -9755
data/tests/basic.rb CHANGED
@@ -17,7 +17,6 @@ module BasicTest
17
17
  add_message "BadFieldNames" do
18
18
  optional :dup, :int32, 1
19
19
  optional :class, :int32, 2
20
- optional :"a.b", :int32, 3
21
20
  end
22
21
  end
23
22
 
@@ -32,12 +31,91 @@ module BasicTest
32
31
  end
33
32
  include CommonTests
34
33
 
34
+ def test_issue_8311_crash
35
+ Google::Protobuf::DescriptorPool.generated_pool.build do
36
+ add_file("inner.proto", :syntax => :proto3) do
37
+ add_message "Inner" do
38
+ # Removing either of these fixes the segfault.
39
+ optional :foo, :string, 1
40
+ optional :bar, :string, 2
41
+ end
42
+ end
43
+ end
44
+
45
+ Google::Protobuf::DescriptorPool.generated_pool.build do
46
+ add_file("outer.proto", :syntax => :proto3) do
47
+ add_message "Outer" do
48
+ repeated :inners, :message, 1, "Inner"
49
+ end
50
+ end
51
+ end
52
+
53
+ outer = ::Google::Protobuf::DescriptorPool.generated_pool.lookup("Outer").msgclass
54
+
55
+ outer.new(
56
+ inners: []
57
+ )['inners'].to_s
58
+
59
+ assert_raise Google::Protobuf::TypeError do
60
+ outer.new(
61
+ inners: [nil]
62
+ ).to_s
63
+ end
64
+ end
65
+
66
+ def test_issue_8559_crash
67
+ msg = TestMessage.new
68
+ msg.repeated_int32 = ::Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])
69
+
70
+ # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
71
+ if cruby_or_jruby_9_3_or_higher?
72
+ GC.start(full_mark: true, immediate_sweep: true)
73
+ end
74
+ TestMessage.encode(msg)
75
+ end
76
+
77
+ def test_issue_9440
78
+ msg = HelloRequest.new
79
+ msg.id = 8
80
+ assert_equal 8, msg.id
81
+ msg.version = '1'
82
+ assert_equal 8, msg.id
83
+ end
84
+
85
+ def test_issue_9507
86
+ pool = Google::Protobuf::DescriptorPool.new
87
+ pool.build do
88
+ add_message "NpeMessage" do
89
+ optional :type, :enum, 1, "TestEnum"
90
+ optional :other, :string, 2
91
+ end
92
+ add_enum "TestEnum" do
93
+ value :Something, 0
94
+ end
95
+ end
96
+
97
+ msgclass = pool.lookup("NpeMessage").msgclass
98
+
99
+ m = msgclass.new(
100
+ other: "foo" # must be set, but can be blank
101
+ )
102
+
103
+ begin
104
+ encoded = msgclass.encode(m)
105
+ rescue java.lang.NullPointerException
106
+ flunk "NPE rescued"
107
+ end
108
+ decoded = msgclass.decode(encoded)
109
+ decoded.inspect
110
+ decoded.to_proto
111
+ end
112
+
35
113
  def test_has_field
36
- m = TestMessage.new
37
- assert !m.has_optional_msg?
38
- m.optional_msg = TestMessage2.new
39
- assert m.has_optional_msg?
40
- assert TestMessage.descriptor.lookup('optional_msg').has?(m)
114
+ m = TestSingularFields.new
115
+ assert !m.has_singular_msg?
116
+ m.singular_msg = TestMessage2.new
117
+ assert m.has_singular_msg?
118
+ assert TestSingularFields.descriptor.lookup('singular_msg').has?(m)
41
119
 
42
120
  m = OneofMessage.new
43
121
  assert !m.has_my_oneof?
@@ -46,32 +124,31 @@ module BasicTest
46
124
  assert_raise NoMethodError do
47
125
  m.has_a?
48
126
  end
49
- assert_raise ArgumentError do
50
- OneofMessage.descriptor.lookup('a').has?(m)
51
- end
127
+ assert_true OneofMessage.descriptor.lookup('a').has?(m)
52
128
 
53
- m = TestMessage.new
129
+ m = TestSingularFields.new
54
130
  assert_raise NoMethodError do
55
- m.has_optional_int32?
131
+ m.has_singular_int32?
56
132
  end
57
133
  assert_raise ArgumentError do
58
- TestMessage.descriptor.lookup('optional_int32').has?(m)
134
+ TestSingularFields.descriptor.lookup('singular_int32').has?(m)
59
135
  end
60
136
 
61
137
  assert_raise NoMethodError do
62
- m.has_optional_string?
138
+ m.has_singular_string?
63
139
  end
64
140
  assert_raise ArgumentError do
65
- TestMessage.descriptor.lookup('optional_string').has?(m)
141
+ TestSingularFields.descriptor.lookup('singular_string').has?(m)
66
142
  end
67
143
 
68
144
  assert_raise NoMethodError do
69
- m.has_optional_bool?
145
+ m.has_singular_bool?
70
146
  end
71
147
  assert_raise ArgumentError do
72
- TestMessage.descriptor.lookup('optional_bool').has?(m)
148
+ TestSingularFields.descriptor.lookup('singular_bool').has?(m)
73
149
  end
74
150
 
151
+ m = TestMessage.new
75
152
  assert_raise NoMethodError do
76
153
  m.has_repeated_msg?
77
154
  end
@@ -80,40 +157,70 @@ module BasicTest
80
157
  end
81
158
  end
82
159
 
160
+ def test_no_presence
161
+ m = TestSingularFields.new
162
+
163
+ # Explicitly setting to zero does not cause anything to be serialized.
164
+ m.singular_int32 = 0
165
+ assert_equal "", TestSingularFields.encode(m)
166
+
167
+ # Explicitly setting to a non-zero value *does* cause serialization.
168
+ m.singular_int32 = 1
169
+ assert_not_equal "", TestSingularFields.encode(m)
170
+
171
+ m.singular_int32 = 0
172
+ assert_equal "", TestSingularFields.encode(m)
173
+ end
174
+
83
175
  def test_set_clear_defaults
176
+ m = TestSingularFields.new
177
+
178
+ m.singular_int32 = -42
179
+ assert_equal( -42, m.singular_int32 )
180
+ m.clear_singular_int32
181
+ assert_equal 0, m.singular_int32
182
+
183
+ m.singular_int32 = 50
184
+ assert_equal 50, m.singular_int32
185
+ TestSingularFields.descriptor.lookup('singular_int32').clear(m)
186
+ assert_equal 0, m.singular_int32
187
+
188
+ m.singular_string = "foo bar"
189
+ assert_equal "foo bar", m.singular_string
190
+ m.clear_singular_string
191
+ assert_equal "", m.singular_string
192
+
193
+ m.singular_string = "foo"
194
+ assert_equal "foo", m.singular_string
195
+ TestSingularFields.descriptor.lookup('singular_string').clear(m)
196
+ assert_equal "", m.singular_string
197
+
198
+ m.singular_msg = TestMessage2.new(:foo => 42)
199
+ assert_equal TestMessage2.new(:foo => 42), m.singular_msg
200
+ assert m.has_singular_msg?
201
+ m.clear_singular_msg
202
+ assert_equal nil, m.singular_msg
203
+ assert !m.has_singular_msg?
204
+
205
+ m.singular_msg = TestMessage2.new(:foo => 42)
206
+ assert_equal TestMessage2.new(:foo => 42), m.singular_msg
207
+ TestSingularFields.descriptor.lookup('singular_msg').clear(m)
208
+ assert_equal nil, m.singular_msg
209
+ end
210
+
211
+ def test_import_proto2
84
212
  m = TestMessage.new
213
+ assert !m.has_optional_proto2_submessage?
214
+ m.optional_proto2_submessage = ::FooBar::Proto2::TestImportedMessage.new
215
+ assert m.has_optional_proto2_submessage?
216
+ assert TestMessage.descriptor.lookup('optional_proto2_submessage').has?(m)
217
+
218
+ m.clear_optional_proto2_submessage
219
+ assert !m.has_optional_proto2_submessage?
220
+ end
85
221
 
86
- m.optional_int32 = -42
87
- assert_equal -42, m.optional_int32
88
- m.clear_optional_int32
89
- assert_equal 0, m.optional_int32
90
-
91
- m.optional_int32 = 50
92
- assert_equal 50, m.optional_int32
93
- TestMessage.descriptor.lookup('optional_int32').clear(m)
94
- assert_equal 0, m.optional_int32
95
-
96
- m.optional_string = "foo bar"
97
- assert_equal "foo bar", m.optional_string
98
- m.clear_optional_string
99
- assert_equal "", m.optional_string
100
-
101
- m.optional_string = "foo"
102
- assert_equal "foo", m.optional_string
103
- TestMessage.descriptor.lookup('optional_string').clear(m)
104
- assert_equal "", m.optional_string
105
-
106
- m.optional_msg = TestMessage2.new(:foo => 42)
107
- assert_equal TestMessage2.new(:foo => 42), m.optional_msg
108
- assert m.has_optional_msg?
109
- m.clear_optional_msg
110
- assert_equal nil, m.optional_msg
111
- assert !m.has_optional_msg?
112
-
113
- m.optional_msg = TestMessage2.new(:foo => 42)
114
- assert_equal TestMessage2.new(:foo => 42), m.optional_msg
115
- TestMessage.descriptor.lookup('optional_msg').clear(m)
116
- assert_equal nil, m.optional_msg
222
+ def test_clear_repeated_fields
223
+ m = TestMessage.new
117
224
 
118
225
  m.repeated_int32.push(1)
119
226
  assert_equal [1], m.repeated_int32
@@ -129,6 +236,7 @@ module BasicTest
129
236
  m.a = "foo"
130
237
  assert_equal "foo", m.a
131
238
  assert m.has_my_oneof?
239
+ assert_equal :a, m.my_oneof
132
240
  m.clear_a
133
241
  assert !m.has_my_oneof?
134
242
 
@@ -144,7 +252,6 @@ module BasicTest
144
252
  assert !m.has_my_oneof?
145
253
  end
146
254
 
147
-
148
255
  def test_initialization_map_errors
149
256
  e = assert_raise ArgumentError do
150
257
  TestMessage.new(:hello => "world")
@@ -154,12 +261,12 @@ module BasicTest
154
261
  e = assert_raise ArgumentError do
155
262
  MapMessage.new(:map_string_int32 => "hello")
156
263
  end
157
- assert_equal e.message, "Expected Hash object as initializer value for map field 'map_string_int32'."
264
+ assert_equal e.message, "Expected Hash object as initializer value for map field 'map_string_int32' (given String)."
158
265
 
159
266
  e = assert_raise ArgumentError do
160
267
  TestMessage.new(:repeated_uint32 => "hello")
161
268
  end
162
- assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32'."
269
+ assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32' (given String)."
163
270
  end
164
271
 
165
272
  def test_map_field
@@ -170,10 +277,12 @@ module BasicTest
170
277
  m = MapMessage.new(
171
278
  :map_string_int32 => {"a" => 1, "b" => 2},
172
279
  :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
173
- "b" => TestMessage2.new(:foo => 2)})
280
+ "b" => TestMessage2.new(:foo => 2)},
281
+ :map_string_enum => {"a" => :A, "b" => :B})
174
282
  assert m.map_string_int32.keys.sort == ["a", "b"]
175
283
  assert m.map_string_int32["a"] == 1
176
284
  assert m.map_string_msg["b"].foo == 2
285
+ assert m.map_string_enum["a"] == :A
177
286
 
178
287
  m.map_string_int32["c"] = 3
179
288
  assert m.map_string_int32["c"] == 3
@@ -197,18 +306,37 @@ module BasicTest
197
306
  m.map_string_int32 = {}
198
307
  end
199
308
 
200
- assert_raise TypeError do
309
+ assert_raise Google::Protobuf::TypeError do
201
310
  m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" })
202
311
  end
203
312
  end
204
313
 
314
+ def test_map_field_with_symbol
315
+ m = MapMessage.new
316
+ assert m.map_string_int32 == {}
317
+ assert m.map_string_msg == {}
318
+
319
+ m = MapMessage.new(
320
+ :map_string_int32 => {a: 1, "b" => 2},
321
+ :map_string_msg => {a: TestMessage2.new(:foo => 1),
322
+ b: TestMessage2.new(:foo => 10)})
323
+ assert_equal 1, m.map_string_int32[:a]
324
+ assert_equal 2, m.map_string_int32[:b]
325
+ assert_equal 10, m.map_string_msg[:b].foo
326
+ end
327
+
205
328
  def test_map_inspect
206
329
  m = MapMessage.new(
207
330
  :map_string_int32 => {"a" => 1, "b" => 2},
208
331
  :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
209
- "b" => TestMessage2.new(:foo => 2)})
210
- expected = "<BasicTest::MapMessage: map_string_int32: {\"b\"=>2, \"a\"=>1}, map_string_msg: {\"b\"=><BasicTest::TestMessage2: foo: 2>, \"a\"=><BasicTest::TestMessage2: foo: 1>}>"
211
- assert_equal expected, m.inspect
332
+ "b" => TestMessage2.new(:foo => 2)},
333
+ :map_string_enum => {"a" => :A, "b" => :B})
334
+
335
+ # JRuby doesn't keep consistent ordering so check for either version
336
+ expected_a = "<BasicTest::MapMessage: map_string_int32: {\"b\"=>2, \"a\"=>1}, map_string_msg: {\"b\"=><BasicTest::TestMessage2: foo: 2>, \"a\"=><BasicTest::TestMessage2: foo: 1>}, map_string_enum: {\"b\"=>:B, \"a\"=>:A}>"
337
+ expected_b = "<BasicTest::MapMessage: map_string_int32: {\"a\"=>1, \"b\"=>2}, map_string_msg: {\"a\"=><BasicTest::TestMessage2: foo: 1>, \"b\"=><BasicTest::TestMessage2: foo: 2>}, map_string_enum: {\"a\"=>:A, \"b\"=>:B}>"
338
+ inspect_result = m.inspect
339
+ assert expected_a == inspect_result || expected_b == inspect_result, "Incorrect inspect result: #{inspect_result}"
212
340
  end
213
341
 
214
342
  def test_map_corruption
@@ -218,6 +346,128 @@ module BasicTest
218
346
  m.map_string_int32['aaa'] = 3
219
347
  end
220
348
 
349
+ def test_map_wrappers
350
+ run_asserts = ->(m) {
351
+ assert_equal 2.0, m.map_double[0].value
352
+ assert_equal 4.0, m.map_float[0].value
353
+ assert_equal 3, m.map_int32[0].value
354
+ assert_equal 4, m.map_int64[0].value
355
+ assert_equal 5, m.map_uint32[0].value
356
+ assert_equal 6, m.map_uint64[0].value
357
+ assert_equal true, m.map_bool[0].value
358
+ assert_equal 'str', m.map_string[0].value
359
+ assert_equal 'fun', m.map_bytes[0].value
360
+ }
361
+
362
+ m = proto_module::Wrapper.new(
363
+ map_double: {0 => Google::Protobuf::DoubleValue.new(value: 2.0)},
364
+ map_float: {0 => Google::Protobuf::FloatValue.new(value: 4.0)},
365
+ map_int32: {0 => Google::Protobuf::Int32Value.new(value: 3)},
366
+ map_int64: {0 => Google::Protobuf::Int64Value.new(value: 4)},
367
+ map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 5)},
368
+ map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 6)},
369
+ map_bool: {0 => Google::Protobuf::BoolValue.new(value: true)},
370
+ map_string: {0 => Google::Protobuf::StringValue.new(value: 'str')},
371
+ map_bytes: {0 => Google::Protobuf::BytesValue.new(value: 'fun')},
372
+ )
373
+
374
+ run_asserts.call(m)
375
+ serialized = proto_module::Wrapper::encode(m)
376
+ m2 = proto_module::Wrapper::decode(serialized)
377
+ run_asserts.call(m2)
378
+
379
+ # Test the case where we are serializing directly from the parsed form
380
+ # (before anything lazy is materialized).
381
+ m3 = proto_module::Wrapper::decode(serialized)
382
+ serialized2 = proto_module::Wrapper::encode(m3)
383
+ m4 = proto_module::Wrapper::decode(serialized2)
384
+ run_asserts.call(m4)
385
+
386
+ # Test that the lazy form compares equal to the expanded form.
387
+ m5 = proto_module::Wrapper::decode(serialized2)
388
+ assert_equal m5, m
389
+ end
390
+
391
+ def test_map_wrappers_with_default_values
392
+ run_asserts = ->(m) {
393
+ assert_equal 0.0, m.map_double[0].value
394
+ assert_equal 0.0, m.map_float[0].value
395
+ assert_equal 0, m.map_int32[0].value
396
+ assert_equal 0, m.map_int64[0].value
397
+ assert_equal 0, m.map_uint32[0].value
398
+ assert_equal 0, m.map_uint64[0].value
399
+ assert_equal false, m.map_bool[0].value
400
+ assert_equal '', m.map_string[0].value
401
+ assert_equal '', m.map_bytes[0].value
402
+ }
403
+
404
+ m = proto_module::Wrapper.new(
405
+ map_double: {0 => Google::Protobuf::DoubleValue.new(value: 0.0)},
406
+ map_float: {0 => Google::Protobuf::FloatValue.new(value: 0.0)},
407
+ map_int32: {0 => Google::Protobuf::Int32Value.new(value: 0)},
408
+ map_int64: {0 => Google::Protobuf::Int64Value.new(value: 0)},
409
+ map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 0)},
410
+ map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 0)},
411
+ map_bool: {0 => Google::Protobuf::BoolValue.new(value: false)},
412
+ map_string: {0 => Google::Protobuf::StringValue.new(value: '')},
413
+ map_bytes: {0 => Google::Protobuf::BytesValue.new(value: '')},
414
+ )
415
+
416
+ run_asserts.call(m)
417
+ serialized = proto_module::Wrapper::encode(m)
418
+ m2 = proto_module::Wrapper::decode(serialized)
419
+ run_asserts.call(m2)
420
+
421
+ # Test the case where we are serializing directly from the parsed form
422
+ # (before anything lazy is materialized).
423
+ m3 = proto_module::Wrapper::decode(serialized)
424
+ serialized2 = proto_module::Wrapper::encode(m3)
425
+ m4 = proto_module::Wrapper::decode(serialized2)
426
+ run_asserts.call(m4)
427
+
428
+ # Test that the lazy form compares equal to the expanded form.
429
+ m5 = proto_module::Wrapper::decode(serialized2)
430
+ assert_equal m5, m
431
+ end
432
+
433
+ def test_map_wrappers_with_no_value
434
+ run_asserts = ->(m) {
435
+ assert_equal 0.0, m.map_double[0].value
436
+ assert_equal 0.0, m.map_float[0].value
437
+ assert_equal 0, m.map_int32[0].value
438
+ assert_equal 0, m.map_int64[0].value
439
+ assert_equal 0, m.map_uint32[0].value
440
+ assert_equal 0, m.map_uint64[0].value
441
+ assert_equal false, m.map_bool[0].value
442
+ assert_equal '', m.map_string[0].value
443
+ assert_equal '', m.map_bytes[0].value
444
+ }
445
+
446
+ m = proto_module::Wrapper.new(
447
+ map_double: {0 => Google::Protobuf::DoubleValue.new()},
448
+ map_float: {0 => Google::Protobuf::FloatValue.new()},
449
+ map_int32: {0 => Google::Protobuf::Int32Value.new()},
450
+ map_int64: {0 => Google::Protobuf::Int64Value.new()},
451
+ map_uint32: {0 => Google::Protobuf::UInt32Value.new()},
452
+ map_uint64: {0 => Google::Protobuf::UInt64Value.new()},
453
+ map_bool: {0 => Google::Protobuf::BoolValue.new()},
454
+ map_string: {0 => Google::Protobuf::StringValue.new()},
455
+ map_bytes: {0 => Google::Protobuf::BytesValue.new()},
456
+ )
457
+ run_asserts.call(m)
458
+
459
+ serialized = proto_module::Wrapper::encode(m)
460
+ m2 = proto_module::Wrapper::decode(serialized)
461
+ run_asserts.call(m2)
462
+
463
+ # Test the case where we are serializing directly from the parsed form
464
+ # (before anything lazy is materialized).
465
+ m3 = proto_module::Wrapper::decode(serialized)
466
+ serialized2 = proto_module::Wrapper::encode(m3)
467
+ m4 = proto_module::Wrapper::decode(serialized2)
468
+ run_asserts.call(m4)
469
+ end
470
+
221
471
  def test_concurrent_decoding
222
472
  o = Outer.new
223
473
  o.items[0] = Inner.new
@@ -237,7 +487,8 @@ module BasicTest
237
487
  m = MapMessage.new(
238
488
  :map_string_int32 => {"a" => 1, "b" => 2},
239
489
  :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
240
- "b" => TestMessage2.new(:foo => 2)})
490
+ "b" => TestMessage2.new(:foo => 2)},
491
+ :map_string_enum => {"a" => :A, "b" => :B})
241
492
  m2 = MapMessage.decode(MapMessage.encode(m))
242
493
  assert m == m2
243
494
 
@@ -267,6 +518,14 @@ module BasicTest
267
518
  assert_match(/No such field: not_in_message/, e.message)
268
519
  end
269
520
 
521
+ #def test_json_quoted_string
522
+ # m = TestMessage.decode_json(%q(
523
+ # "optionalInt64": "1",,
524
+ # }))
525
+ # puts(m)
526
+ # assert_equal 1, m.optional_int32
527
+ #end
528
+
270
529
  def test_to_h
271
530
  m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'], :repeated_msg => [TestMessage2.new(:foo => 100)])
272
531
  expected_result = {
@@ -278,6 +537,8 @@ module BasicTest
278
537
  :optional_int32=>0,
279
538
  :optional_int64=>0,
280
539
  :optional_msg=>nil,
540
+ :optional_msg2=>nil,
541
+ :optional_proto2_submessage=>nil,
281
542
  :optional_string=>"foo",
282
543
  :optional_uint32=>0,
283
544
  :optional_uint64=>0,
@@ -298,44 +559,62 @@ module BasicTest
298
559
  m = MapMessage.new(
299
560
  :map_string_int32 => {"a" => 1, "b" => 2},
300
561
  :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
301
- "b" => TestMessage2.new(:foo => 2)})
562
+ "b" => TestMessage2.new(:foo => 2)},
563
+ :map_string_enum => {"a" => :A, "b" => :B})
302
564
  expected_result = {
303
565
  :map_string_int32 => {"a" => 1, "b" => 2},
304
- :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}}
566
+ :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}},
567
+ :map_string_enum => {"a" => :A, "b" => :B}
305
568
  }
306
569
  assert_equal expected_result, m.to_h
307
570
  end
308
571
 
309
572
 
310
573
  def test_json_maps
311
- # TODO: Fix JSON in JRuby version.
312
- return if RUBY_PLATFORM == "java"
313
574
  m = MapMessage.new(:map_string_int32 => {"a" => 1})
314
- expected = {mapStringInt32: {a: 1}, mapStringMsg: {}}
315
- expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}}
316
- assert JSON.parse(MapMessage.encode_json(m), :symbolize_names => true) == expected
575
+ expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}}
576
+ expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}}
577
+ assert_equal JSON.parse(MapMessage.encode_json(m, :emit_defaults=>true), :symbolize_names => true), expected
317
578
 
318
- json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true)
319
- assert JSON.parse(json, :symbolize_names => true) == expected_preserve
579
+ json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true, :emit_defaults=>true)
580
+ assert_equal JSON.parse(json, :symbolize_names => true), expected_preserve
320
581
 
321
582
  m2 = MapMessage.decode_json(MapMessage.encode_json(m))
322
- assert m == m2
583
+ assert_equal m, m2
323
584
  end
324
585
 
325
586
  def test_json_maps_emit_defaults_submsg
326
- # TODO: Fix JSON in JRuby version.
327
- return if RUBY_PLATFORM == "java"
328
- m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new})
329
- expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}}
587
+ m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new(foo: 0)})
588
+ expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}}
330
589
 
331
590
  actual = MapMessage.encode_json(m, :emit_defaults => true)
332
591
 
333
- assert JSON.parse(actual, :symbolize_names => true) == expected
592
+ assert_equal JSON.parse(actual, :symbolize_names => true), expected
593
+ end
594
+
595
+ def test_json_emit_defaults_submsg
596
+ m = TestSingularFields.new(singular_msg: proto_module::TestMessage2.new)
597
+
598
+ expected = {
599
+ singularInt32: 0,
600
+ singularInt64: "0",
601
+ singularUint32: 0,
602
+ singularUint64: "0",
603
+ singularBool: false,
604
+ singularFloat: 0,
605
+ singularDouble: 0,
606
+ singularString: "",
607
+ singularBytes: "",
608
+ singularMsg: {},
609
+ singularEnum: "Default",
610
+ }
611
+
612
+ actual = proto_module::TestMessage.encode_json(m, :emit_defaults => true)
613
+
614
+ assert_equal expected, JSON.parse(actual, :symbolize_names => true)
334
615
  end
335
616
 
336
617
  def test_respond_to
337
- # This test fails with JRuby 1.7.23, likely because of an old JRuby bug.
338
- return if RUBY_PLATFORM == "java"
339
618
  msg = MapMessage.new
340
619
  assert msg.respond_to?(:map_string_int32)
341
620
  assert !msg.respond_to?(:bacon)
@@ -351,11 +630,110 @@ module BasicTest
351
630
  assert nil != file_descriptor
352
631
  assert_equal "tests/basic_test.proto", file_descriptor.name
353
632
  assert_equal :proto3, file_descriptor.syntax
633
+ end
354
634
 
355
- file_descriptor = BadFieldNames.descriptor.file_descriptor
356
- assert nil != file_descriptor
357
- assert_equal nil, file_descriptor.name
358
- assert_equal :proto3, file_descriptor.syntax
635
+ # Ruby 2.5 changed to raise FrozenError instead of RuntimeError
636
+ FrozenErrorType = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5') ? RuntimeError : FrozenError
637
+
638
+ def test_map_freeze
639
+ m = proto_module::MapMessage.new
640
+ m.map_string_int32['a'] = 5
641
+ m.map_string_msg['b'] = proto_module::TestMessage2.new
642
+
643
+ m.map_string_int32.freeze
644
+ m.map_string_msg.freeze
645
+
646
+ assert m.map_string_int32.frozen?
647
+ assert m.map_string_msg.frozen?
648
+
649
+ assert_raise(FrozenErrorType) { m.map_string_int32['foo'] = 1 }
650
+ assert_raise(FrozenErrorType) { m.map_string_msg['bar'] = proto_module::TestMessage2.new }
651
+ assert_raise(FrozenErrorType) { m.map_string_int32.delete('a') }
652
+ assert_raise(FrozenErrorType) { m.map_string_int32.clear }
653
+ end
654
+
655
+ def test_map_length
656
+ m = proto_module::MapMessage.new
657
+ assert_equal 0, m.map_string_int32.length
658
+ assert_equal 0, m.map_string_msg.length
659
+ assert_equal 0, m.map_string_int32.size
660
+ assert_equal 0, m.map_string_msg.size
661
+
662
+ m.map_string_int32['a'] = 1
663
+ m.map_string_int32['b'] = 2
664
+ m.map_string_msg['a'] = proto_module::TestMessage2.new
665
+ assert_equal 2, m.map_string_int32.length
666
+ assert_equal 1, m.map_string_msg.length
667
+ assert_equal 2, m.map_string_int32.size
668
+ assert_equal 1, m.map_string_msg.size
669
+ end
670
+
671
+ def test_string_with_singleton_class_enabled
672
+ str = 'foobar'
673
+ # NOTE: Accessing a singleton class of an object changes its low level class representation
674
+ # as far as the C API's CLASS_OF() method concerned, exposing the issue
675
+ str.singleton_class
676
+ m = proto_module::TestMessage.new(
677
+ optional_string: str,
678
+ optional_bytes: str
679
+ )
680
+
681
+ assert_equal str, m.optional_string
682
+ assert_equal str, m.optional_bytes
683
+ end
684
+
685
+ def test_utf8
686
+ m = proto_module::TestMessage.new(
687
+ optional_string: "µpb",
688
+ )
689
+ m2 = proto_module::TestMessage.decode(proto_module::TestMessage.encode(m))
690
+ assert_equal m2, m
691
+ end
692
+
693
+ def test_map_fields_respond_to? # regression test for issue 9202
694
+ msg = proto_module::MapMessage.new
695
+ assert msg.respond_to?(:map_string_int32=)
696
+ msg.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
697
+ assert msg.respond_to?(:map_string_int32)
698
+ assert_equal( Google::Protobuf::Map.new(:string, :int32), msg.map_string_int32 )
699
+ assert msg.respond_to?(:clear_map_string_int32)
700
+ msg.clear_map_string_int32
701
+
702
+ assert !msg.respond_to?(:has_map_string_int32?)
703
+ assert_raise NoMethodError do
704
+ msg.has_map_string_int32?
705
+ end
706
+ assert !msg.respond_to?(:map_string_int32_as_value)
707
+ assert_raise NoMethodError do
708
+ msg.map_string_int32_as_value
709
+ end
710
+ assert !msg.respond_to?(:map_string_int32_as_value=)
711
+ assert_raise NoMethodError do
712
+ msg.map_string_int32_as_value = :boom
713
+ end
714
+ end
715
+ end
716
+
717
+ def test_oneof_fields_respond_to? # regression test for issue 9202
718
+ msg = proto_module::OneofMessage.new
719
+ # `has_` prefix + "?" suffix actions should only work for oneofs fields.
720
+ assert msg.has_my_oneof?
721
+ assert msg.respond_to? :has_my_oneof?
722
+ assert !msg.respond_to?( :has_a? )
723
+ assert_raise NoMethodError do
724
+ msg.has_a?
725
+ end
726
+ assert !msg.respond_to?( :has_b? )
727
+ assert_raise NoMethodError do
728
+ msg.has_b?
729
+ end
730
+ assert !msg.respond_to?( :has_c? )
731
+ assert_raise NoMethodError do
732
+ msg.has_c?
733
+ end
734
+ assert !msg.respond_to?( :has_d? )
735
+ assert_raise NoMethodError do
736
+ msg.has_d?
359
737
  end
360
738
  end
361
739
  end
File without changes