• 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_pb'
7require 'common_tests'
8require 'google/protobuf'
9require 'json'
10require 'test/unit'
11
12# ------------- generated code --------------
13
14module BasicTest
15  pool = Google::Protobuf::DescriptorPool.new
16  pool.build do
17    add_message "BadFieldNames" do
18      optional :dup, :int32, 1
19      optional :class, :int32, 2
20    end
21  end
22
23  BadFieldNames = pool.lookup("BadFieldNames").msgclass
24
25# ------------ test cases ---------------
26
27  class MessageContainerTest < Test::Unit::TestCase
28    # Required by CommonTests module to resolve proto3 proto classes used in tests.
29    def proto_module
30      ::BasicTest
31    end
32    include CommonTests
33
34    def test_has_field
35      m = TestSingularFields.new
36      assert !m.has_singular_msg?
37      m.singular_msg = TestMessage2.new
38      assert m.has_singular_msg?
39      assert TestSingularFields.descriptor.lookup('singular_msg').has?(m)
40
41      m = OneofMessage.new
42      assert !m.has_my_oneof?
43      m.a = "foo"
44      assert m.has_my_oneof?
45      assert_raise NoMethodError do
46        m.has_a?
47      end
48      assert_true OneofMessage.descriptor.lookup('a').has?(m)
49
50      m = TestSingularFields.new
51      assert_raise NoMethodError do
52        m.has_singular_int32?
53      end
54      assert_raise ArgumentError do
55        TestSingularFields.descriptor.lookup('singular_int32').has?(m)
56      end
57
58      assert_raise NoMethodError do
59        m.has_singular_string?
60      end
61      assert_raise ArgumentError do
62        TestSingularFields.descriptor.lookup('singular_string').has?(m)
63      end
64
65      assert_raise NoMethodError do
66        m.has_singular_bool?
67      end
68      assert_raise ArgumentError do
69        TestSingularFields.descriptor.lookup('singular_bool').has?(m)
70      end
71
72      m = TestMessage.new
73      assert_raise NoMethodError do
74        m.has_repeated_msg?
75      end
76      assert_raise ArgumentError do
77        TestMessage.descriptor.lookup('repeated_msg').has?(m)
78      end
79    end
80
81    def test_no_presence
82      m = TestSingularFields.new
83
84      # Explicitly setting to zero does not cause anything to be serialized.
85      m.singular_int32 = 0
86      assert_equal "", TestSingularFields.encode(m)
87
88      # Explicitly setting to a non-zero value *does* cause serialization.
89      m.singular_int32 = 1
90      assert_not_equal "", TestSingularFields.encode(m)
91
92      m.singular_int32 = 0
93      assert_equal "", TestSingularFields.encode(m)
94    end
95
96    def test_set_clear_defaults
97      m = TestSingularFields.new
98
99      m.singular_int32 = -42
100      assert_equal -42, m.singular_int32
101      m.clear_singular_int32
102      assert_equal 0, m.singular_int32
103
104      m.singular_int32 = 50
105      assert_equal 50, m.singular_int32
106      TestSingularFields.descriptor.lookup('singular_int32').clear(m)
107      assert_equal 0, m.singular_int32
108
109      m.singular_string = "foo bar"
110      assert_equal "foo bar", m.singular_string
111      m.clear_singular_string
112      assert_equal "", m.singular_string
113
114      m.singular_string = "foo"
115      assert_equal "foo", m.singular_string
116      TestSingularFields.descriptor.lookup('singular_string').clear(m)
117      assert_equal "", m.singular_string
118
119      m.singular_msg = TestMessage2.new(:foo => 42)
120      assert_equal TestMessage2.new(:foo => 42), m.singular_msg
121      assert m.has_singular_msg?
122      m.clear_singular_msg
123      assert_equal nil, m.singular_msg
124      assert !m.has_singular_msg?
125
126      m.singular_msg = TestMessage2.new(:foo => 42)
127      assert_equal TestMessage2.new(:foo => 42), m.singular_msg
128      TestSingularFields.descriptor.lookup('singular_msg').clear(m)
129      assert_equal nil, m.singular_msg
130    end
131
132    def test_clear_repeated_fields
133      m = TestMessage.new
134
135      m.repeated_int32.push(1)
136      assert_equal [1], m.repeated_int32
137      m.clear_repeated_int32
138      assert_equal [], m.repeated_int32
139
140      m.repeated_int32.push(1)
141      assert_equal [1], m.repeated_int32
142      TestMessage.descriptor.lookup('repeated_int32').clear(m)
143      assert_equal [], m.repeated_int32
144
145      m = OneofMessage.new
146      m.a = "foo"
147      assert_equal "foo", m.a
148      assert m.has_my_oneof?
149      assert_equal :a, m.my_oneof
150      m.clear_a
151      assert !m.has_my_oneof?
152
153      m.a = "foobar"
154      assert m.has_my_oneof?
155      m.clear_my_oneof
156      assert !m.has_my_oneof?
157
158      m.a = "bar"
159      assert_equal "bar", m.a
160      assert m.has_my_oneof?
161      OneofMessage.descriptor.lookup('a').clear(m)
162      assert !m.has_my_oneof?
163    end
164
165    def test_initialization_map_errors
166      e = assert_raise ArgumentError do
167        TestMessage.new(:hello => "world")
168      end
169      assert_match(/hello/, e.message)
170
171      e = assert_raise ArgumentError do
172        MapMessage.new(:map_string_int32 => "hello")
173      end
174      assert_equal e.message, "Expected Hash object as initializer value for map field 'map_string_int32' (given String)."
175
176      e = assert_raise ArgumentError do
177        TestMessage.new(:repeated_uint32 => "hello")
178      end
179      assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32' (given String)."
180    end
181
182    def test_map_field
183      m = MapMessage.new
184      assert m.map_string_int32 == {}
185      assert m.map_string_msg == {}
186
187      m = MapMessage.new(
188        :map_string_int32 => {"a" => 1, "b" => 2},
189        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
190                            "b" => TestMessage2.new(:foo => 2)},
191        :map_string_enum => {"a" => :A, "b" => :B})
192      assert m.map_string_int32.keys.sort == ["a", "b"]
193      assert m.map_string_int32["a"] == 1
194      assert m.map_string_msg["b"].foo == 2
195      assert m.map_string_enum["a"] == :A
196
197      m.map_string_int32["c"] = 3
198      assert m.map_string_int32["c"] == 3
199      m.map_string_msg["c"] = TestMessage2.new(:foo => 3)
200      assert m.map_string_msg["c"] == TestMessage2.new(:foo => 3)
201      m.map_string_msg.delete("b")
202      m.map_string_msg.delete("c")
203      assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) }
204
205      assert_raise Google::Protobuf::TypeError do
206        m.map_string_msg["e"] = TestMessage.new # wrong value type
207      end
208      # ensure nothing was added by the above
209      assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) }
210
211      m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32)
212      assert_raise Google::Protobuf::TypeError do
213        m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64)
214      end
215      assert_raise Google::Protobuf::TypeError do
216        m.map_string_int32 = {}
217      end
218
219      assert_raise TypeError do
220        m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" })
221      end
222    end
223
224    def test_map_field_with_symbol
225      m = MapMessage.new
226      assert m.map_string_int32 == {}
227      assert m.map_string_msg == {}
228
229      m = MapMessage.new(
230        :map_string_int32 => {a: 1, "b" => 2},
231        :map_string_msg => {a: TestMessage2.new(:foo => 1),
232                            b: TestMessage2.new(:foo => 10)})
233      assert_equal 1, m.map_string_int32[:a]
234      assert_equal 2, m.map_string_int32[:b]
235      assert_equal 10, m.map_string_msg[:b].foo
236    end
237
238    def test_map_inspect
239      m = MapMessage.new(
240        :map_string_int32 => {"a" => 1, "b" => 2},
241        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
242                            "b" => TestMessage2.new(:foo => 2)},
243        :map_string_enum => {"a" => :A, "b" => :B})
244      expected = "<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}>"
245      assert_equal expected, m.inspect
246    end
247
248    def test_map_corruption
249      # This pattern led to a crash in a previous version of upb/protobuf.
250      m = MapMessage.new(map_string_int32: { "aaa" => 1 })
251      m.map_string_int32['podid'] = 2
252      m.map_string_int32['aaa'] = 3
253    end
254
255    def test_map_wrappers
256      run_asserts = ->(m) {
257        assert_equal 2.0, m.map_double[0].value
258        assert_equal 4.0, m.map_float[0].value
259        assert_equal 3, m.map_int32[0].value
260        assert_equal 4, m.map_int64[0].value
261        assert_equal 5, m.map_uint32[0].value
262        assert_equal 6, m.map_uint64[0].value
263        assert_equal true, m.map_bool[0].value
264        assert_equal 'str', m.map_string[0].value
265        assert_equal 'fun', m.map_bytes[0].value
266      }
267
268      m = proto_module::Wrapper.new(
269        map_double: {0 => Google::Protobuf::DoubleValue.new(value: 2.0)},
270        map_float: {0 => Google::Protobuf::FloatValue.new(value: 4.0)},
271        map_int32: {0 => Google::Protobuf::Int32Value.new(value: 3)},
272        map_int64: {0 => Google::Protobuf::Int64Value.new(value: 4)},
273        map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 5)},
274        map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 6)},
275        map_bool: {0 => Google::Protobuf::BoolValue.new(value: true)},
276        map_string: {0 => Google::Protobuf::StringValue.new(value: 'str')},
277        map_bytes: {0 => Google::Protobuf::BytesValue.new(value: 'fun')},
278      )
279
280      run_asserts.call(m)
281      serialized = proto_module::Wrapper::encode(m)
282      m2 = proto_module::Wrapper::decode(serialized)
283      run_asserts.call(m2)
284
285      # Test the case where we are serializing directly from the parsed form
286      # (before anything lazy is materialized).
287      m3 = proto_module::Wrapper::decode(serialized)
288      serialized2 = proto_module::Wrapper::encode(m3)
289      m4 = proto_module::Wrapper::decode(serialized2)
290      run_asserts.call(m4)
291
292      # Test that the lazy form compares equal to the expanded form.
293      m5 = proto_module::Wrapper::decode(serialized2)
294      assert_equal m5, m
295    end
296
297    def test_map_wrappers_with_default_values
298      run_asserts = ->(m) {
299        assert_equal 0.0, m.map_double[0].value
300        assert_equal 0.0, m.map_float[0].value
301        assert_equal 0, m.map_int32[0].value
302        assert_equal 0, m.map_int64[0].value
303        assert_equal 0, m.map_uint32[0].value
304        assert_equal 0, m.map_uint64[0].value
305        assert_equal false, m.map_bool[0].value
306        assert_equal '', m.map_string[0].value
307        assert_equal '', m.map_bytes[0].value
308      }
309
310      m = proto_module::Wrapper.new(
311        map_double: {0 => Google::Protobuf::DoubleValue.new(value: 0.0)},
312        map_float: {0 => Google::Protobuf::FloatValue.new(value: 0.0)},
313        map_int32: {0 => Google::Protobuf::Int32Value.new(value: 0)},
314        map_int64: {0 => Google::Protobuf::Int64Value.new(value: 0)},
315        map_uint32: {0 => Google::Protobuf::UInt32Value.new(value: 0)},
316        map_uint64: {0 => Google::Protobuf::UInt64Value.new(value: 0)},
317        map_bool: {0 => Google::Protobuf::BoolValue.new(value: false)},
318        map_string: {0 => Google::Protobuf::StringValue.new(value: '')},
319        map_bytes: {0 => Google::Protobuf::BytesValue.new(value: '')},
320      )
321
322      run_asserts.call(m)
323      serialized = proto_module::Wrapper::encode(m)
324      m2 = proto_module::Wrapper::decode(serialized)
325      run_asserts.call(m2)
326
327      # Test the case where we are serializing directly from the parsed form
328      # (before anything lazy is materialized).
329      m3 = proto_module::Wrapper::decode(serialized)
330      serialized2 = proto_module::Wrapper::encode(m3)
331      m4 = proto_module::Wrapper::decode(serialized2)
332      run_asserts.call(m4)
333
334      # Test that the lazy form compares equal to the expanded form.
335      m5 = proto_module::Wrapper::decode(serialized2)
336      assert_equal m5, m
337    end
338
339    def test_map_wrappers_with_no_value
340      run_asserts = ->(m) {
341        assert_equal 0.0, m.map_double[0].value
342        assert_equal 0.0, m.map_float[0].value
343        assert_equal 0, m.map_int32[0].value
344        assert_equal 0, m.map_int64[0].value
345        assert_equal 0, m.map_uint32[0].value
346        assert_equal 0, m.map_uint64[0].value
347        assert_equal false, m.map_bool[0].value
348        assert_equal '', m.map_string[0].value
349        assert_equal '', m.map_bytes[0].value
350      }
351
352      m = proto_module::Wrapper.new(
353        map_double: {0 => Google::Protobuf::DoubleValue.new()},
354        map_float: {0 => Google::Protobuf::FloatValue.new()},
355        map_int32: {0 => Google::Protobuf::Int32Value.new()},
356        map_int64: {0 => Google::Protobuf::Int64Value.new()},
357        map_uint32: {0 => Google::Protobuf::UInt32Value.new()},
358        map_uint64: {0 => Google::Protobuf::UInt64Value.new()},
359        map_bool: {0 => Google::Protobuf::BoolValue.new()},
360        map_string: {0 => Google::Protobuf::StringValue.new()},
361        map_bytes: {0 => Google::Protobuf::BytesValue.new()},
362      )
363      run_asserts.call(m)
364
365      serialized = proto_module::Wrapper::encode(m)
366      m2 = proto_module::Wrapper::decode(serialized)
367      run_asserts.call(m2)
368
369      # Test the case where we are serializing directly from the parsed form
370      # (before anything lazy is materialized).
371      m3 = proto_module::Wrapper::decode(serialized)
372      serialized2 = proto_module::Wrapper::encode(m3)
373      m4 = proto_module::Wrapper::decode(serialized2)
374      run_asserts.call(m4)
375    end
376
377    def test_concurrent_decoding
378      o = Outer.new
379      o.items[0] = Inner.new
380      raw = Outer.encode(o)
381
382      thds = 2.times.map do
383        Thread.new do
384          100000.times do
385            assert_equal o, Outer.decode(raw)
386          end
387        end
388      end
389      thds.map(&:join)
390    end
391
392    def test_map_encode_decode
393      m = MapMessage.new(
394        :map_string_int32 => {"a" => 1, "b" => 2},
395        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
396                            "b" => TestMessage2.new(:foo => 2)},
397        :map_string_enum => {"a" => :A, "b" => :B})
398      m2 = MapMessage.decode(MapMessage.encode(m))
399      assert m == m2
400
401      m3 = MapMessageWireEquiv.decode(MapMessage.encode(m))
402      assert m3.map_string_int32.length == 2
403
404      kv = {}
405      m3.map_string_int32.map { |msg| kv[msg.key] = msg.value }
406      assert kv == {"a" => 1, "b" => 2}
407
408      kv = {}
409      m3.map_string_msg.map { |msg| kv[msg.key] = msg.value }
410      assert kv == {"a" => TestMessage2.new(:foo => 1),
411                    "b" => TestMessage2.new(:foo => 2)}
412    end
413
414    def test_protobuf_decode_json_ignore_unknown_fields
415      m = TestMessage.decode_json({
416        optional_string: "foo",
417        not_in_message: "some_value"
418      }.to_json, { ignore_unknown_fields: true })
419
420      assert_equal m.optional_string, "foo"
421      e = assert_raise Google::Protobuf::ParseError do
422        TestMessage.decode_json({ not_in_message: "some_value" }.to_json)
423      end
424      assert_match(/No such field: not_in_message/, e.message)
425    end
426
427    #def test_json_quoted_string
428    #  m = TestMessage.decode_json(%q(
429    #    "optionalInt64": "1",,
430    #  }))
431    #  puts(m)
432    #  assert_equal 1, m.optional_int32
433    #end
434
435    def test_to_h
436      m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'], :repeated_msg => [TestMessage2.new(:foo => 100)])
437      expected_result = {
438        :optional_bool=>true,
439        :optional_bytes=>"",
440        :optional_double=>-10.100001,
441        :optional_enum=>:Default,
442        :optional_float=>0.0,
443        :optional_int32=>0,
444        :optional_int64=>0,
445        :optional_msg=>nil,
446        :optional_string=>"foo",
447        :optional_uint32=>0,
448        :optional_uint64=>0,
449        :repeated_bool=>[],
450        :repeated_bytes=>[],
451        :repeated_double=>[],
452        :repeated_enum=>[],
453        :repeated_float=>[],
454        :repeated_int32=>[],
455        :repeated_int64=>[],
456        :repeated_msg=>[{:foo => 100}],
457        :repeated_string=>["bar1", "bar2"],
458        :repeated_uint32=>[],
459        :repeated_uint64=>[]
460      }
461      assert_equal expected_result, m.to_h
462
463      m = MapMessage.new(
464        :map_string_int32 => {"a" => 1, "b" => 2},
465        :map_string_msg => {"a" => TestMessage2.new(:foo => 1),
466                            "b" => TestMessage2.new(:foo => 2)},
467        :map_string_enum => {"a" => :A, "b" => :B})
468      expected_result = {
469        :map_string_int32 => {"a" => 1, "b" => 2},
470        :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}},
471        :map_string_enum => {"a" => :A, "b" => :B}
472      }
473      assert_equal expected_result, m.to_h
474    end
475
476
477    def test_json_maps
478      # TODO: Fix JSON in JRuby version.
479      return if RUBY_PLATFORM == "java"
480      m = MapMessage.new(:map_string_int32 => {"a" => 1})
481      expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}}
482      expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}}
483      assert_equal JSON.parse(MapMessage.encode_json(m), :symbolize_names => true), expected
484
485      json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true)
486      assert_equal JSON.parse(json, :symbolize_names => true), expected_preserve
487
488      m2 = MapMessage.decode_json(MapMessage.encode_json(m))
489      assert_equal m, m2
490    end
491
492    def test_json_maps_emit_defaults_submsg
493      # TODO: Fix JSON in JRuby version.
494      return if RUBY_PLATFORM == "java"
495      m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new})
496      expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}}
497
498      actual = MapMessage.encode_json(m, :emit_defaults => true)
499
500      assert_equal JSON.parse(actual, :symbolize_names => true), expected
501    end
502
503    def test_respond_to
504      # This test fails with JRuby 1.7.23, likely because of an old JRuby bug.
505      return if RUBY_PLATFORM == "java"
506      msg = MapMessage.new
507      assert msg.respond_to?(:map_string_int32)
508      assert !msg.respond_to?(:bacon)
509    end
510
511    def test_file_descriptor
512      file_descriptor = TestMessage.descriptor.file_descriptor
513      assert nil != file_descriptor
514      assert_equal "tests/basic_test.proto", file_descriptor.name
515      assert_equal :proto3, file_descriptor.syntax
516
517      file_descriptor = TestEnum.descriptor.file_descriptor
518      assert nil != file_descriptor
519      assert_equal "tests/basic_test.proto", file_descriptor.name
520      assert_equal :proto3, file_descriptor.syntax
521    end
522
523    # Ruby 2.5 changed to raise FrozenError instead of RuntimeError
524    FrozenErrorType = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5') ? RuntimeError : FrozenError
525
526    def test_map_freeze
527      m = proto_module::MapMessage.new
528      m.map_string_int32['a'] = 5
529      m.map_string_msg['b'] = proto_module::TestMessage2.new
530
531      m.map_string_int32.freeze
532      m.map_string_msg.freeze
533
534      assert m.map_string_int32.frozen?
535      assert m.map_string_msg.frozen?
536
537      assert_raise(FrozenErrorType) { m.map_string_int32['foo'] = 1 }
538      assert_raise(FrozenErrorType) { m.map_string_msg['bar'] = proto_module::TestMessage2.new }
539      assert_raise(FrozenErrorType) { m.map_string_int32.delete('a') }
540      assert_raise(FrozenErrorType) { m.map_string_int32.clear }
541    end
542  end
543end
544