• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/ruby
2
3# basic_test_pb.rb is in the same directory as this test.
4$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
5
6require 'basic_test_features_pb'
7require 'basic_test_pb'
8require 'common_tests'
9require 'google/protobuf'
10require 'json'
11require 'test/unit'
12
13module BasicTest
14  TestMessage = BasicTest::TestMessage
15  Outer = BasicTest::Outer
16
17  class MessageContainerTest < Test::Unit::TestCase
18    # Required by CommonTests module to resolve proto3 proto classes used in tests.
19    def proto_module
20      ::BasicTest
21    end
22    include CommonTests
23
24    def test_issue_8311_crash
25      BasicTest::Outer8311.new(
26          inners: []
27      )['inners'].to_s
28
29      assert_raises Google::Protobuf::TypeError do
30        BasicTest::Outer8311.new(
31            inners: [nil]
32        ).to_s
33      end
34    end
35
36    def test_issue_8559_crash
37      msg = TestMessage.new
38      msg.repeated_int32 = ::Google::Protobuf::RepeatedField.new(:int32, [1, 2, 3])
39
40      # https://github.com/jruby/jruby/issues/6818 was fixed in JRuby 9.3.0.0
41      if cruby_or_jruby_9_3_or_higher?
42        GC.start(full_mark: true, immediate_sweep: true)
43      end
44      TestMessage.encode(msg)
45    end
46
47    def test_issue_9440
48      msg = HelloRequest.new
49      msg.id = 8
50      assert_equal 8, msg.id
51      msg.version = '1'
52      assert_equal 8, msg.id
53    end
54
55    def test_issue_9507
56      m = BasicTest::NpeMessage.new(
57        other: "foo"      # must be set, but can be blank
58      )
59
60      begin
61        encoded = BasicTest::NpeMessage.encode(m)
62      rescue java.lang.NullPointerException
63        flunk "NPE rescued"
64      end
65      decoded = BasicTest::NpeMessage.decode(encoded)
66      decoded.inspect
67      decoded.to_proto
68    end
69
70    def test_has_field
71      m = TestSingularFields.new
72      refute m.has_singular_msg?
73      m.singular_msg = TestMessage2.new
74      assert m.has_singular_msg?
75      assert TestSingularFields.descriptor.lookup('singular_msg').has?(m)
76
77      m = OneofMessage.new
78      refute m.has_my_oneof?
79      refute m.has_a?
80      m.a = "foo"
81      assert m.has_my_oneof?
82      assert m.has_a?
83      assert_true OneofMessage.descriptor.lookup('a').has?(m)
84
85      m = TestSingularFields.new
86      assert_raises NoMethodError do
87        m.has_singular_int32?
88      end
89      assert_raises ArgumentError do
90        TestSingularFields.descriptor.lookup('singular_int32').has?(m)
91      end
92
93      assert_raises NoMethodError do
94        m.has_singular_string?
95      end
96      assert_raises ArgumentError do
97        TestSingularFields.descriptor.lookup('singular_string').has?(m)
98      end
99
100      assert_raises NoMethodError do
101        m.has_singular_bool?
102      end
103      assert_raises ArgumentError do
104        TestSingularFields.descriptor.lookup('singular_bool').has?(m)
105      end
106
107      m = TestMessage.new
108      assert_raises NoMethodError do
109        m.has_repeated_msg?
110      end
111      assert_raises ArgumentError do
112        TestMessage.descriptor.lookup('repeated_msg').has?(m)
113      end
114    end
115
116    def test_no_presence
117      m = TestSingularFields.new
118
119      # Explicitly setting to zero does not cause anything to be serialized.
120      m.singular_int32 = 0
121      assert_empty TestSingularFields.encode(m)
122      # Explicitly setting to a non-zero value *does* cause serialization.
123      m.singular_int32 = 1
124      refute_empty TestSingularFields.encode(m)
125      m.singular_int32 = 0
126      assert_empty TestSingularFields.encode(m)
127    end
128
129    def test_set_clear_defaults
130      m = TestSingularFields.new
131
132      m.singular_int32 = -42
133      assert_equal( -42, m.singular_int32 )
134      m.clear_singular_int32
135      assert_equal 0, m.singular_int32
136
137      m.singular_int32 = 50
138      assert_equal 50, m.singular_int32
139      TestSingularFields.descriptor.lookup('singular_int32').clear(m)
140      assert_equal 0, m.singular_int32
141
142      m.singular_string = "foo bar"
143      assert_equal "foo bar", m.singular_string
144      m.clear_singular_string
145      assert_empty m.singular_string
146      m.singular_string = "foo"
147      assert_equal "foo", m.singular_string
148      TestSingularFields.descriptor.lookup('singular_string').clear(m)
149      assert_empty m.singular_string
150      m.singular_msg = TestMessage2.new(:foo => 42)
151      assert_equal TestMessage2.new(:foo => 42), m.singular_msg
152      assert m.has_singular_msg?
153      m.clear_singular_msg
154      assert_nil m.singular_msg
155      refute m.has_singular_msg?
156
157      m.singular_msg = TestMessage2.new(:foo => 42)
158      assert_equal TestMessage2.new(:foo => 42), m.singular_msg
159      TestSingularFields.descriptor.lookup('singular_msg').clear(m)
160      assert_nil m.singular_msg
161    end
162
163    def test_import_proto2
164      m = TestMessage.new
165      refute m.has_optional_proto2_submessage?
166      m.optional_proto2_submessage = ::FooBar::Proto2::TestImportedMessage.new
167      assert m.has_optional_proto2_submessage?
168      assert TestMessage.descriptor.lookup('optional_proto2_submessage').has?(m)
169
170      m.clear_optional_proto2_submessage
171      refute m.has_optional_proto2_submessage?
172    end
173
174    def test_clear_repeated_fields
175      m = TestMessage.new
176
177      m.repeated_int32.push(1)
178      assert_equal [1], m.repeated_int32
179      m.clear_repeated_int32
180      assert_empty m.repeated_int32
181      m.repeated_int32.push(1)
182      assert_equal [1], m.repeated_int32
183      TestMessage.descriptor.lookup('repeated_int32').clear(m)
184      assert_empty m.repeated_int32
185      m = OneofMessage.new
186      m.a = "foo"
187      assert_equal "foo", m.a
188      assert m.has_my_oneof?
189      assert_equal :a, m.my_oneof
190      m.clear_a
191      refute m.has_my_oneof?
192
193      m.a = "foobar"
194      assert m.has_my_oneof?
195      m.clear_my_oneof
196      refute m.has_my_oneof?
197
198      m.a = "bar"
199      assert_equal "bar", m.a
200      assert m.has_my_oneof?
201      OneofMessage.descriptor.lookup('a').clear(m)
202      refute m.has_my_oneof?
203    end
204
205    def test_initialization_map_errors
206      e = assert_raises ArgumentError do
207        TestMessage.new(:hello => "world")
208      end
209      assert_match(/hello/, e.message)
210
211      e = assert_raises ArgumentError do
212        MapMessage.new(:map_string_int32 => "hello")
213      end
214      assert_equal "Expected Hash object as initializer value for map field 'map_string_int32' (given String).", e.message
215      e = assert_raises ArgumentError do
216        TestMessage.new(:repeated_uint32 => "hello")
217      end
218      assert_equal "Expected array as initializer value for repeated field 'repeated_uint32' (given String).", e.message
219    end
220
221    def test_map_field
222      m = MapMessage.new
223      assert_empty m.map_string_int32.to_h
224      assert_empty m.map_string_msg.to_h
225
226      m = MapMessage.new(
227        :map_string_int32 => {"a" => 1, "b" => 2},
228        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
229                            "b" => TestMessage2.new(:foo => 2)},
230        :map_string_enum => {"a" => :A, "b" => :B})
231      assert_equal ["a", "b"], m.map_string_int32.keys.sort
232      assert_equal 1, m.map_string_int32["a"]
233      assert_equal 2, m.map_string_msg["b"].foo
234      assert_equal :A, m.map_string_enum["a"]
235      m.map_string_int32["c"] = 3
236      assert_equal 3, m.map_string_int32["c"]
237      m.map_string_msg["c"] = TestMessage2.new(:foo => 3)
238      assert_equal TestMessage2.new(:foo => 3), m.map_string_msg["c"]
239      m.map_string_msg.delete("b")
240      m.map_string_msg.delete("c")
241      assert_equal({ "a" => TestMessage2.new(:foo => 1).to_h }, m.map_string_msg.to_h)
242      assert_raises Google::Protobuf::TypeError do
243        m.map_string_msg["e"] = TestMessage.new # wrong value type
244      end
245      # ensure nothing was added by the above
246      assert_equal({ "a" => TestMessage2.new(:foo => 1).to_h }, m.map_string_msg.to_h)
247      m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
248      assert_raises Google::Protobuf::TypeError do
249        m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64)
250      end
251      assert_raises Google::Protobuf::TypeError do
252        m.map_string_int32 = {}
253      end
254
255      assert_raises Google::Protobuf::TypeError do
256        m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" })
257      end
258    end
259
260    def test_map_field_with_symbol
261      m = MapMessage.new
262      assert_empty m.map_string_int32.to_h
263      assert_empty m.map_string_msg.to_h
264
265      m = MapMessage.new(
266        :map_string_int32 => {a: 1, "b" => 2},
267        :map_string_msg => {a: TestMessage2.new(:foo => 1),
268                            b: TestMessage2.new(:foo => 10)})
269      assert_equal 1, m.map_string_int32[:a]
270      assert_equal 2, m.map_string_int32[:b]
271      assert_equal 10, m.map_string_msg[:b].foo
272    end
273
274    def test_map_inspect
275      m = MapMessage.new(
276        :map_string_int32 => {"a" => 1, "b" => 2},
277        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
278                            "b" => TestMessage2.new(:foo => 2)},
279        :map_string_enum => {"a" => :A, "b" => :B})
280
281      # JRuby doesn't keep consistent ordering so check for either version
282      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}>"
283      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}>"
284      inspect_result = m.inspect
285      assert_includes [expected_a, expected_b], inspect_result
286    end
287
288    def test_map_corruption
289      # This pattern led to a crash in a previous version of upb/protobuf.
290      m = MapMessage.new(map_string_int32: { "aaa" => 1 })
291      m.map_string_int32['podid'] = 2
292      m.map_string_int32['aaa'] = 3
293    end
294
295    def test_map_wrappers
296      run_asserts = ->(m) {
297        assert_equal 2.0, m.map_double[0].value
298        assert_equal 4.0, m.map_float[0].value
299        assert_equal 3, m.map_int32[0].value
300        assert_equal 4, m.map_int64[0].value
301        assert_equal 5, m.map_uint32[0].value
302        assert_equal 6, m.map_uint64[0].value
303        assert m.map_bool[0].value
304        assert_equal 'str', m.map_string[0].value
305        assert_equal 'fun', m.map_bytes[0].value
306      }
307
308      m = proto_module::Wrapper.new(
309        map_double: {0 => Google::Protobuf::DoubleValue.new(value: 2.0)},
310        map_float: {0 => Google::Protobuf::FloatValue.new(value: 4.0)},
311        map_int32: {0 => Google::Protobuf::Int32Value.new(value: 3)},
312        map_int64: {0 => Google::Protobuf::Int64Value.new(value: 4)},
313        map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 5)},
314        map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 6)},
315        map_bool: {0 => Google::Protobuf::BoolValue.new(value: true)},
316        map_string: {0 => Google::Protobuf::StringValue.new(value: 'str')},
317        map_bytes: {0 => Google::Protobuf::BytesValue.new(value: 'fun')},
318      )
319
320      run_asserts.call(m)
321      serialized = proto_module::Wrapper::encode(m)
322      m2 = proto_module::Wrapper::decode(serialized)
323      run_asserts.call(m2)
324
325      # Test the case where we are serializing directly from the parsed form
326      # (before anything lazy is materialized).
327      m3 = proto_module::Wrapper::decode(serialized)
328      serialized2 = proto_module::Wrapper::encode(m3)
329      m4 = proto_module::Wrapper::decode(serialized2)
330      run_asserts.call(m4)
331
332      # Test that the lazy form compares equal to the expanded form.
333      m5 = proto_module::Wrapper::decode(serialized2)
334      assert_equal m5, m
335    end
336
337    def test_map_wrappers_with_default_values
338      run_asserts = ->(m) {
339        assert_equal 0.0, m.map_double[0].value
340        assert_equal 0.0, m.map_float[0].value
341        assert_equal 0, m.map_int32[0].value
342        assert_equal 0, m.map_int64[0].value
343        assert_equal 0, m.map_uint32[0].value
344        assert_equal 0, m.map_uint64[0].value
345        refute m.map_bool[0].value
346        assert_empty m.map_string[0].value
347        assert_empty m.map_bytes[0].value
348      }
349
350      m = proto_module::Wrapper.new(
351        map_double: {0 => Google::Protobuf::DoubleValue.new(value: 0.0)},
352        map_float: {0 => Google::Protobuf::FloatValue.new(value: 0.0)},
353        map_int32: {0 => Google::Protobuf::Int32Value.new(value: 0)},
354        map_int64: {0 => Google::Protobuf::Int64Value.new(value: 0)},
355        map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 0)},
356        map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 0)},
357        map_bool: {0 => Google::Protobuf::BoolValue.new(value: false)},
358        map_string: {0 => Google::Protobuf::StringValue.new(value: '')},
359        map_bytes: {0 => Google::Protobuf::BytesValue.new(value: '')},
360      )
361
362      run_asserts.call(m)
363      serialized = proto_module::Wrapper::encode(m)
364      m2 = proto_module::Wrapper::decode(serialized)
365      run_asserts.call(m2)
366
367      # Test the case where we are serializing directly from the parsed form
368      # (before anything lazy is materialized).
369      m3 = proto_module::Wrapper::decode(serialized)
370      serialized2 = proto_module::Wrapper::encode(m3)
371      m4 = proto_module::Wrapper::decode(serialized2)
372      run_asserts.call(m4)
373
374      # Test that the lazy form compares equal to the expanded form.
375      m5 = proto_module::Wrapper::decode(serialized2)
376      assert_equal m5, m
377    end
378
379    def test_map_wrappers_with_no_value
380      run_asserts = ->(m) {
381        assert_equal 0.0, m.map_double[0].value
382        assert_equal 0.0, m.map_float[0].value
383        assert_equal 0, m.map_int32[0].value
384        assert_equal 0, m.map_int64[0].value
385        assert_equal 0, m.map_uint32[0].value
386        assert_equal 0, m.map_uint64[0].value
387        refute m.map_bool[0].value
388        assert_empty m.map_string[0].value
389        assert_empty m.map_bytes[0].value
390      }
391
392      m = proto_module::Wrapper.new(
393        map_double: {0 => Google::Protobuf::DoubleValue.new()},
394        map_float: {0 => Google::Protobuf::FloatValue.new()},
395        map_int32: {0 => Google::Protobuf::Int32Value.new()},
396        map_int64: {0 => Google::Protobuf::Int64Value.new()},
397        map_uint32: {0 => Google::Protobuf::UInt32Value.new()},
398        map_uint64: {0 => Google::Protobuf::UInt64Value.new()},
399        map_bool: {0 => Google::Protobuf::BoolValue.new()},
400        map_string: {0 => Google::Protobuf::StringValue.new()},
401        map_bytes: {0 => Google::Protobuf::BytesValue.new()},
402      )
403      run_asserts.call(m)
404
405      serialized = proto_module::Wrapper::encode(m)
406      m2 = proto_module::Wrapper::decode(serialized)
407      run_asserts.call(m2)
408
409      # Test the case where we are serializing directly from the parsed form
410      # (before anything lazy is materialized).
411      m3 = proto_module::Wrapper::decode(serialized)
412      serialized2 = proto_module::Wrapper::encode(m3)
413      m4 = proto_module::Wrapper::decode(serialized2)
414      run_asserts.call(m4)
415    end
416
417    def test_concurrent_decoding
418      o = Outer.new
419      o.items[0] = Inner.new
420      raw = Outer.encode(o)
421
422      thds = 2.times.map do
423        Thread.new do
424          100000.times do
425            assert_equal o, Outer.decode(raw)
426          end
427        end
428      end
429      thds.map(&:join)
430    end
431
432    def test_map_encode_decode
433      m = MapMessage.new(
434        :map_string_int32 => {"a" => 1, "b" => 2},
435        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
436                            "b" => TestMessage2.new(:foo => 2)},
437        :map_string_enum => {"a" => :A, "b" => :B})
438      m2 = MapMessage.decode(MapMessage.encode(m))
439      assert_equal m, m2
440      m3 = MapMessageWireEquiv.decode(MapMessage.encode(m))
441      assert_equal 2, m3.map_string_int32.length
442      kv = {}
443      m3.map_string_int32.map { |msg| kv[msg.key] = msg.value }
444      assert_equal({"a" => 1, "b" => 2}, kv)
445      kv = {}
446      m3.map_string_msg.map { |msg| kv[msg.key] = msg.value }
447      assert_equal({"a" => TestMessage2.new(:foo => 1),
448                    "b" => TestMessage2.new(:foo => 2)}, kv)
449    end
450
451    def test_protobuf_decode_json_ignore_unknown_fields
452      m = TestMessage.decode_json({
453        optional_string: "foo",
454        not_in_message: "some_value"
455      }.to_json, { ignore_unknown_fields: true })
456
457      assert_equal "foo", m.optional_string
458      e = assert_raises Google::Protobuf::ParseError do
459        TestMessage.decode_json({ not_in_message: "some_value" }.to_json)
460      end
461      assert_match(/No such field: not_in_message/, e.message)
462    end
463
464    #def test_json_quoted_string
465    #  m = TestMessage.decode_json(%q(
466    #    "optionalInt64": "1",,
467    #  }))
468    #  puts(m)
469    #  assert_equal 1, m.optional_int32
470    #end
471
472    def test_to_h
473      m = TestMessage.new(
474        :optional_bool => true,
475        :optional_double => -10.100001,
476        :optional_string => 'foo',
477        :repeated_string => ['bar1', 'bar2'],
478        :repeated_msg => [TestMessage2.new(:foo => 100)]
479      )
480      expected_result = {
481        :optional_bool=>true,
482        :optional_double=>-10.100001,
483        :optional_string=>"foo",
484        :repeated_string=>["bar1", "bar2"],
485        :repeated_msg=>[{:foo => 100}],
486      }
487      assert_equal expected_result, m.to_h
488
489      m = MapMessage.new(
490        :map_string_int32 => {"a" => 1, "b" => 2},
491        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
492                            "b" => TestMessage2.new(:foo => 2)},
493        :map_string_enum => {"a" => :A, "b" => :B})
494      expected_result = {
495        :map_string_int32 => {"a" => 1, "b" => 2},
496        :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}},
497        :map_string_enum => {"a" => :A, "b" => :B}
498      }
499      assert_equal expected_result, m.to_h
500    end
501
502
503    def test_json_maps
504      m = MapMessage.new(:map_string_int32 => {"a" => 1})
505      expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}}
506      expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}}
507      assert_equal expected, JSON.parse(MapMessage.encode_json(m, :emit_defaults=>true), :symbolize_names => true)
508
509      json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true, :emit_defaults=>true)
510      assert_equal expected_preserve, JSON.parse(json, :symbolize_names => true)
511
512      m2 = MapMessage.decode_json(MapMessage.encode_json(m))
513      assert_equal m, m2
514    end
515
516    def test_json_maps_emit_defaults_submsg
517      m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new(foo: 0)})
518      expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}}
519
520      actual = MapMessage.encode_json(m, :emit_defaults => true)
521
522      assert_equal expected, JSON.parse(actual, :symbolize_names => true)
523    end
524
525    def test_json_emit_defaults_submsg
526      m = TestSingularFields.new(singular_msg: proto_module::TestMessage2.new)
527
528      expected = {
529        singularInt32: 0,
530        singularInt64: "0",
531        singularUint32: 0,
532        singularUint64: "0",
533        singularBool: false,
534        singularFloat: 0,
535        singularDouble: 0,
536        singularString: "",
537        singularBytes: "",
538        singularMsg: {},
539        singularEnum: "Default",
540      }
541
542      actual = proto_module::TestMessage.encode_json(m, :emit_defaults => true)
543
544      assert_equal expected, JSON.parse(actual, :symbolize_names => true)
545    end
546
547    def test_respond_to
548      msg = MapMessage.new
549      assert_respond_to msg, :map_string_int32
550      refute_respond_to msg, :bacon
551    end
552
553    def test_file_descriptor
554      file_descriptor = TestMessage.descriptor.file_descriptor
555      refute_nil file_descriptor
556      assert_equal "basic_test.proto", file_descriptor.name
557
558      file_descriptor = TestEnum.descriptor.file_descriptor
559      refute_nil file_descriptor
560      assert_equal "basic_test.proto", file_descriptor.name
561    end
562
563    def test_map_freeze
564      m = proto_module::MapMessage.new
565      m.map_string_int32['a'] = 5
566      m.map_string_msg['b'] = proto_module::TestMessage2.new
567
568      m.map_string_int32.freeze
569      m.map_string_msg.freeze
570
571      assert m.map_string_int32.frozen?
572      assert m.map_string_msg.frozen?
573
574      assert_raises(FrozenError) { m.map_string_int32['foo'] = 1 }
575      assert_raises(FrozenError) { m.map_string_msg['bar'] = proto_module::TestMessage2.new }
576      assert_raises(FrozenError) { m.map_string_int32.delete('a') }
577      assert_raises(FrozenError) { m.map_string_int32.clear }
578    end
579
580    def test_map_length
581      m = proto_module::MapMessage.new
582      assert_equal 0, m.map_string_int32.length
583      assert_equal 0, m.map_string_msg.length
584      assert_equal 0, m.map_string_int32.size
585      assert_equal 0, m.map_string_msg.size
586
587      m.map_string_int32['a'] = 1
588      m.map_string_int32['b'] = 2
589      m.map_string_msg['a'] = proto_module::TestMessage2.new
590      assert_equal 2, m.map_string_int32.length
591      assert_equal 1, m.map_string_msg.length
592      assert_equal 2, m.map_string_int32.size
593      assert_equal 1, m.map_string_msg.size
594    end
595
596    def test_string_with_singleton_class_enabled
597      str = 'foobar'
598      # NOTE: Accessing a singleton class of an object changes its low level class representation
599      #       as far as the C API's CLASS_OF() method concerned, exposing the issue
600      str.singleton_class
601      m = proto_module::TestMessage.new(
602        optional_string: str,
603        optional_bytes: str
604      )
605
606      assert_equal str, m.optional_string
607      assert_equal str, m.optional_bytes
608    end
609
610    def test_utf8
611      m = proto_module::TestMessage.new(
612        optional_string: "µpb",
613      )
614      m2 = proto_module::TestMessage.decode(proto_module::TestMessage.encode(m))
615      assert_equal m2, m
616    end
617
618    def test_map_fields_respond_to? # regression test for issue 9202
619      msg = proto_module::MapMessage.new
620      assert_respond_to msg, :map_string_int32=
621      msg.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
622      assert_respond_to msg, :map_string_int32
623      assert_equal( Google::Protobuf::Map.new(:string, :int32), msg.map_string_int32 )
624      assert_respond_to msg, :clear_map_string_int32
625      msg.clear_map_string_int32
626
627      refute_respond_to msg, :has_map_string_int32?
628      assert_raises NoMethodError do
629        msg.has_map_string_int32?
630      end
631      refute_respond_to msg, :map_string_int32_as_value
632      assert_raises NoMethodError do
633        msg.map_string_int32_as_value
634      end
635      refute_respond_to msg, :map_string_int32_as_value=
636      assert_raises NoMethodError do
637        msg.map_string_int32_as_value = :boom
638      end
639    end
640
641    def test_has_presence
642      assert_true TestMessage.descriptor.lookup("optional_int32").has_presence?
643      assert_false TestMessage.descriptor.lookup("repeated_int32").has_presence?
644      assert_false TestSingularFields.descriptor.lookup("singular_int32").has_presence?
645    end
646
647    def test_is_packed
648      assert_false TestMessage.descriptor.lookup("optional_int32").is_packed?
649      assert_true TestMessage.descriptor.lookup("repeated_int32").is_packed?
650    end
651
652    def test_file_descriptor_options
653      file_descriptor = TestMessage.descriptor.file_descriptor
654
655      assert_instance_of Google::Protobuf::FileOptions, file_descriptor.options
656      assert file_descriptor.options.deprecated
657    end
658
659    def test_field_descriptor_options
660      field_descriptor = TestDeprecatedMessage.descriptor.lookup("foo")
661
662      assert_instance_of Google::Protobuf::FieldOptions, field_descriptor.options
663      assert field_descriptor.options.deprecated
664    end
665
666    def test_descriptor_options
667      descriptor = TestDeprecatedMessage.descriptor
668
669      assert_instance_of Google::Protobuf::MessageOptions, descriptor.options
670      assert descriptor.options.deprecated
671    end
672
673    def test_enum_descriptor_options
674      enum_descriptor = TestDeprecatedEnum.descriptor
675
676      assert_instance_of Google::Protobuf::EnumOptions, enum_descriptor.options
677      assert enum_descriptor.options.deprecated
678    end
679
680    def test_oneof_descriptor_options
681      descriptor = TestDeprecatedMessage.descriptor
682      oneof_descriptor = descriptor.lookup_oneof("test_deprecated_message_oneof")
683
684      assert_instance_of Google::Protobuf::OneofOptions, oneof_descriptor.options
685      test_top_level_option = Google::Protobuf::DescriptorPool.generated_pool.lookup 'basic_test.test_top_level_option'
686      assert_instance_of Google::Protobuf::FieldDescriptor, test_top_level_option
687      assert_equal "Custom option value", test_top_level_option.get(oneof_descriptor.options)
688    end
689
690    def test_nested_extension
691      descriptor = TestDeprecatedMessage.descriptor
692      oneof_descriptor = descriptor.lookup_oneof("test_deprecated_message_oneof")
693
694      assert_instance_of Google::Protobuf::OneofOptions, oneof_descriptor.options
695      test_nested_option = Google::Protobuf::DescriptorPool.generated_pool.lookup 'basic_test.TestDeprecatedMessage.test_nested_option'
696      assert_instance_of Google::Protobuf::FieldDescriptor, test_nested_option
697      assert_equal "Another custom option value", test_nested_option.get(oneof_descriptor.options)
698    end
699
700    def test_options_deep_freeze
701      descriptor = TestDeprecatedMessage.descriptor
702
703      assert_raise FrozenError do
704        descriptor.options.uninterpreted_option.push \
705          Google::Protobuf::UninterpretedOption.new
706      end
707    end
708
709    def test_message_freeze
710      message = TestDeprecatedMessage.new
711      nested_message_2 = TestMessage2.new
712
713      message.map_string_msg["message"] = TestMessage2.new
714      message.repeated_msg.push(TestMessage2.new)
715
716      message.freeze
717
718      assert_raise FrozenError do
719        message.map_string_msg["message"].foo = nested_message_2
720      end
721
722      assert_raise FrozenError do
723        message.repeated_msg[0].foo = nested_message_2
724      end
725    end
726
727    def test_oneof_fields_respond_to? # regression test for issue 9202
728      msg = proto_module::OneofMessage.new
729      # `has_` prefix + "?" suffix actions should work for oneofs fields and members.
730      assert_false msg.has_my_oneof?
731      assert msg.respond_to? :has_my_oneof?
732      assert_respond_to msg, :has_a?
733      refute msg.has_a?
734      assert_respond_to msg, :has_b?
735      refute msg.has_b?
736      assert_respond_to msg, :has_c?
737      refute msg.has_c?
738      assert_respond_to msg, :has_d?
739      refute msg.has_d?
740    end
741
742    def test_string_subclass
743      str = "hello"
744      myString = Class.new(String)
745
746      m = proto_module::TestMessage.new(
747        optional_string: myString.new(str),
748      )
749
750      assert_equal str, m.optional_string
751    end
752
753    def test_proto3_explicit_presence
754      descriptor = TestMessage.descriptor.lookup("optional_int32")
755      assert_true descriptor.has_presence?
756      assert_false descriptor.options.has_features?
757    end
758
759    def test_proto3_implicit_presence
760      descriptor = TestSingularFields.descriptor.lookup("singular_int32")
761      assert_false descriptor.has_presence?
762      assert_false descriptor.options.has_features?
763    end
764
765    def test_proto3_packed_encoding
766      descriptor = TestMessage.descriptor.lookup("repeated_int32")
767      assert_true descriptor.is_packed?
768      assert_false descriptor.options.has_features?
769    end
770
771    def test_proto3_expanded_encoding
772      descriptor = TestUnpackedMessage.descriptor.lookup("repeated_int32")
773      assert_false descriptor.is_packed?
774      assert_false descriptor.options.has_features?
775    end
776
777    def test_proto3_expanded_encoding_unpackable
778      descriptor = TestMessage.descriptor.lookup("optional_msg")
779      assert_false descriptor.is_packed?
780      assert_false descriptor.options.has_features?
781    end
782
783    def test_editions_explicit_presence
784      descriptor = TestFeaturesMessage.descriptor.lookup("explicit")
785      assert_true descriptor.has_presence?
786      assert_false descriptor.options.has_features?
787    end
788
789    def test_editions_implicit_presence
790      descriptor = TestFeaturesMessage.descriptor.lookup("implicit")
791      assert_false descriptor.has_presence?
792      assert_false descriptor.options.has_features?
793    end
794
795    def test_editions_required_presence
796      descriptor = TestFeaturesMessage.descriptor.lookup("legacy_required")
797      assert_equal :required, descriptor.label
798      assert_false descriptor.options.has_features?
799    end
800
801    def test_editions_packed_encoding
802      descriptor = TestFeaturesMessage.descriptor.lookup("packed")
803      assert_true descriptor.is_packed?
804      assert_false descriptor.options.has_features?
805    end
806
807    def test_editions_expanded_encoding
808      descriptor = TestFeaturesMessage.descriptor.lookup("expanded")
809      assert_false descriptor.is_packed?
810      assert_false descriptor.options.has_features?
811    end
812
813    def test_editions_expanded_encoding_unpackable
814      descriptor = TestFeaturesMessage.descriptor.lookup("unpackable")
815      assert_false descriptor.is_packed?
816      assert_false descriptor.options.has_features?
817    end
818
819    def test_field_delimited_encoding
820      descriptor = TestFeaturesMessage.descriptor.lookup("delimited")
821      assert_equal :group, descriptor.type
822      assert_false descriptor.options.has_features?
823    end
824
825    def test_field_length_prefixed_encoding
826      descriptor = TestFeaturesMessage.descriptor.lookup("length_prefixed")
827      assert_equal :message, descriptor.type
828      assert_false descriptor.options.has_features?
829    end
830
831  end
832end
833