diff options
Diffstat (limited to 'ruby/tests/basic.rb')
-rw-r--r-- | ruby/tests/basic.rb | 324 |
1 files changed, 279 insertions, 45 deletions
diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index da85520f..ad34d53d 100644 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -1,6 +1,7 @@ #!/usr/bin/ruby require 'google/protobuf' +require 'json' require 'test/unit' # ------------- generated code -------------- @@ -50,6 +51,17 @@ module BasicTest optional :foo, :int32, 1 end + add_message "TestEmbeddedMessageParent" do + optional :child_msg, :message, 1, "TestEmbeddedMessageChild" + optional :number, :int32, 2 + + repeated :repeated_msg, :message, 3, "TestEmbeddedMessageChild" + repeated :repeated_number, :int32, 4 + end + add_message "TestEmbeddedMessageChild" do + optional :sub_child, :message, 1, "TestMessage" + end + add_message "Recursive1" do optional :foo, :message, 1, "Recursive2" end @@ -95,13 +107,25 @@ module BasicTest optional :d, :enum, 4, "TestEnum" end end + + add_message "repro.Outer" do + map :items, :int32, :message, 1, "repro.Inner" + end + + add_message "repro.Inner" do + end end + + Outer = pool.lookup("repro.Outer").msgclass + Inner = pool.lookup("repro.Inner").msgclass Foo = pool.lookup("Foo").msgclass Bar = pool.lookup("Bar").msgclass Baz = pool.lookup("Baz").msgclass TestMessage = pool.lookup("TestMessage").msgclass TestMessage2 = pool.lookup("TestMessage2").msgclass + TestEmbeddedMessageParent = pool.lookup("TestEmbeddedMessageParent").msgclass + TestEmbeddedMessageChild = pool.lookup("TestEmbeddedMessageChild").msgclass Recursive1 = pool.lookup("Recursive1").msgclass Recursive2 = pool.lookup("Recursive2").msgclass TestEnum = pool.lookup("TestEnum").enummodule @@ -150,12 +174,18 @@ module BasicTest m.optional_double = 0.5 m.optional_string = "hello" assert m.optional_string == "hello" + m.optional_string = :hello + assert m.optional_string == "hello" m.optional_bytes = "world".encode!('ASCII-8BIT') assert m.optional_bytes == "world" m.optional_msg = TestMessage2.new(:foo => 42) assert m.optional_msg == TestMessage2.new(:foo => 42) m.optional_msg = nil assert m.optional_msg == nil + m.optional_enum = :C + assert m.optional_enum == :C + m.optional_enum = 'C' + assert m.optional_enum == :C end def test_ctor_args @@ -172,6 +202,34 @@ module BasicTest assert m.repeated_string[2] == "world" end + def test_ctor_string_symbol_args + m = TestMessage.new(:optional_enum => 'C', :repeated_enum => ['A', 'B']) + assert_equal :C, m.optional_enum + assert_equal [:A, :B], m.repeated_enum + + m = TestMessage.new(:optional_string => :foo, :repeated_string => [:foo, :bar]) + assert_equal 'foo', m.optional_string + assert_equal ['foo', 'bar'], m.repeated_string + end + + def test_embeddedmsg_hash_init + m = TestEmbeddedMessageParent.new(:child_msg => {sub_child: {optional_int32: 1}}, + :number => 2, + :repeated_msg => [{sub_child: {optional_int32: 3}}], + :repeated_number => [10, 20, 30]) + + assert_equal 2, m.number + assert_equal [10, 20, 30], m.repeated_number + + assert_not_nil m.child_msg + assert_not_nil m.child_msg.sub_child + assert_equal m.child_msg.sub_child.optional_int32, 1 + + assert_not_nil m.repeated_msg + assert_equal 1, m.repeated_msg.length + assert_equal 3, m.repeated_msg.first.sub_child.optional_int32 + end + def test_inspect m = TestMessage.new(:optional_int32 => -42, :optional_enum => :A, @@ -183,12 +241,15 @@ module BasicTest def test_hash m1 = TestMessage.new(:optional_int32 => 42) - m2 = TestMessage.new(:optional_int32 => 102) + m2 = TestMessage.new(:optional_int32 => 102, repeated_string: ['please', 'work', 'ok?']) + m3 = TestMessage.new(:optional_int32 => 102, repeated_string: ['please', 'work', 'ok?']) assert m1.hash != 0 assert m2.hash != 0 + assert m3.hash != 0 # relying on the randomness here -- if hash function changes and we are # unlucky enough to get a collision, then change the values above. assert m1.hash != m2.hash + assert_equal m2.hash, m3.hash end def test_unknown_field_errors @@ -255,14 +316,17 @@ module BasicTest m = TestMessage.new # Assigning a normal (ASCII or UTF8) string to a bytes field, or - # ASCII-8BIT to a string field, raises an error. - assert_raise TypeError do - m.optional_bytes = "Test string ASCII".encode!('ASCII') - end - assert_raise TypeError do + # ASCII-8BIT to a string field will convert to the proper encoding. + m.optional_bytes = "Test string ASCII".encode!('ASCII') + assert m.optional_bytes.frozen? + assert_equal Encoding::ASCII_8BIT, m.optional_bytes.encoding + assert_equal "Test string ASCII", m.optional_bytes + + assert_raise Encoding::UndefinedConversionError do m.optional_bytes = "Test string UTF-8 \u0100".encode!('UTF-8') end - assert_raise TypeError do + + assert_raise Encoding::UndefinedConversionError do m.optional_string = ["FFFF"].pack('H*') end @@ -270,11 +334,10 @@ module BasicTest m.optional_bytes = ["FFFF"].pack('H*') m.optional_string = "\u0100" - # strings are mutable so we can do this, but serialize should catch it. + # strings are immutable so we can't do this, but serialize should catch it. m.optional_string = "asdf".encode!('UTF-8') - m.optional_string.encode!('ASCII-8BIT') - assert_raise TypeError do - data = TestMessage.encode(m) + assert_raise RuntimeError do + m.optional_string.encode!('ASCII-8BIT') end end @@ -466,9 +529,9 @@ module BasicTest assert m.length == 2 m2 = m.dup - assert m == m2 + assert_equal m, m2 assert m.hash != 0 - assert m.hash == m2.hash + assert_equal m.hash, m2.hash collected = {} m.each { |k,v| collected[v] = k } @@ -558,7 +621,7 @@ module BasicTest assert_raise TypeError do m[1] = 1 end - assert_raise TypeError do + assert_raise Encoding::UndefinedConversionError do bytestring = ["FFFF"].pack("H*") m[bytestring] = 1 end @@ -566,9 +629,8 @@ module BasicTest m = Google::Protobuf::Map.new(:bytes, :int32) bytestring = ["FFFF"].pack("H*") m[bytestring] = 1 - assert_raise TypeError do - m["asdf"] = 1 - end + # Allowed -- we will automatically convert to ASCII-8BIT. + m["asdf"] = 1 assert_raise TypeError do m[1] = 1 end @@ -599,7 +661,7 @@ module BasicTest assert_raise RangeError do m["z"] = :Z end - assert_raise TypeError do + assert_raise RangeError do m["z"] = "z" end end @@ -663,6 +725,28 @@ module BasicTest end end + def test_map_corruption + # This pattern led to a crash in a previous version of upb/protobuf. + m = MapMessage.new(map_string_int32: { "aaa" => 1 }) + m.map_string_int32['podid'] = 2 + m.map_string_int32['aaa'] = 3 + end + + def test_concurrent_decoding + o = Outer.new + o.items[0] = Inner.new + raw = Outer.encode(o) + + thds = 2.times.map do + Thread.new do + 100000.times do + assert_equal o, Outer.decode(raw) + end + end + end + thds.map(&:join) + end + def test_map_encode_decode m = MapMessage.new( :map_string_int32 => {"a" => 1, "b" => 2}, @@ -703,36 +787,36 @@ module BasicTest def test_oneof d = OneofMessage.new - assert d.a == nil - assert d.b == nil + assert d.a == "" + assert d.b == 0 assert d.c == nil - assert d.d == nil + assert d.d == :Default assert d.my_oneof == nil d.a = "hi" assert d.a == "hi" - assert d.b == nil + assert d.b == 0 assert d.c == nil - assert d.d == nil + assert d.d == :Default assert d.my_oneof == :a d.b = 42 - assert d.a == nil + assert d.a == "" assert d.b == 42 assert d.c == nil - assert d.d == nil + assert d.d == :Default assert d.my_oneof == :b d.c = TestMessage2.new(:foo => 100) - assert d.a == nil - assert d.b == nil + assert d.a == "" + assert d.b == 0 assert d.c.foo == 100 - assert d.d == nil + assert d.d == :Default assert d.my_oneof == :c d.d = :C - assert d.a == nil - assert d.b == nil + assert d.a == "" + assert d.b == 0 assert d.c == nil assert d.d == :C assert d.my_oneof == :d @@ -748,23 +832,23 @@ module BasicTest d3 = OneofMessage.decode( encoded_field_c + encoded_field_a + encoded_field_d) - assert d3.a == nil - assert d3.b == nil + assert d3.a == "" + assert d3.b == 0 assert d3.c == nil assert d3.d == :B d4 = OneofMessage.decode( encoded_field_c + encoded_field_a + encoded_field_d + encoded_field_c) - assert d4.a == nil - assert d4.b == nil + assert d4.a == "" + assert d4.b == 0 assert d4.c.foo == 1 - assert d4.d == nil + assert d4.d == :Default d5 = OneofMessage.new(:a => "hello") - assert d5.a != nil + assert d5.a == "hello" d5.a = nil - assert d5.a == nil + assert d5.a == "" assert OneofMessage.encode(d5) == '' assert d5.my_oneof == nil end @@ -853,15 +937,22 @@ module BasicTest def test_encode_decode_helpers m = TestMessage.new(:optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + assert_equal 'foo', m.optional_string + assert_equal ['bar1', 'bar2'], m.repeated_string + json = m.to_json m2 = TestMessage.decode_json(json) - assert m2.optional_string == 'foo' - assert m2.repeated_string == ['bar1', 'bar2'] + assert_equal 'foo', m2.optional_string + assert_equal ['bar1', 'bar2'], m2.repeated_string + if RUBY_PLATFORM != "java" + assert m2.optional_string.frozen? + assert m2.repeated_string[0].frozen? + end proto = m.to_proto m2 = TestMessage.decode(proto) - assert m2.optional_string == 'foo' - assert m2.repeated_string == ['bar1', 'bar2'] + assert_equal 'foo', m2.optional_string + assert_equal ['bar1', 'bar2'], m2.repeated_string end def test_protobuf_encode_decode_helpers @@ -883,7 +974,7 @@ module BasicTest end def test_to_h - m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2']) + m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'], :repeated_msg => [TestMessage2.new(:foo => 100)]) expected_result = { :optional_bool=>true, :optional_bytes=>"", @@ -903,12 +994,22 @@ module BasicTest :repeated_float=>[], :repeated_int32=>[], :repeated_int64=>[], - :repeated_msg=>[], + :repeated_msg=>[{:foo => 100}], :repeated_string=>["bar1", "bar2"], :repeated_uint32=>[], :repeated_uint64=>[] } assert_equal expected_result, m.to_h + + m = MapMessage.new( + :map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => TestMessage2.new(:foo => 1), + "b" => TestMessage2.new(:foo => 2)}) + expected_result = { + :map_string_int32 => {"a" => 1, "b" => 2}, + :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}} + } + assert_equal expected_result, m.to_h end @@ -1145,6 +1246,8 @@ module BasicTest json_text = TestMessage.encode_json(m) m2 = TestMessage.decode_json(json_text) + puts m.inspect + puts m2.inspect assert m == m2 # Crash case from GitHub issue 283. @@ -1156,14 +1259,145 @@ module BasicTest Foo.encode_json(Foo.new(bar: bar, baz: [baz1, baz2])) end + def test_json_emit_defaults + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" + m = TestMessage.new + + expected = { + optionalInt32: 0, + optionalInt64: 0, + optionalUint32: 0, + optionalUint64: 0, + optionalBool: false, + optionalFloat: 0, + optionalDouble: 0, + optionalString: "", + optionalBytes: "", + optionalEnum: "Default", + repeatedInt32: [], + repeatedInt64: [], + repeatedUint32: [], + repeatedUint64: [], + repeatedBool: [], + repeatedFloat: [], + repeatedDouble: [], + repeatedString: [], + repeatedBytes: [], + repeatedMsg: [], + repeatedEnum: [] + } + + actual = TestMessage.encode_json(m, :emit_defaults => true) + + assert JSON.parse(actual, :symbolize_names => true) == expected + end + + def test_json_emit_defaults_submsg + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" + m = TestMessage.new(optional_msg: TestMessage2.new) + + expected = { + optionalInt32: 0, + optionalInt64: 0, + optionalUint32: 0, + optionalUint64: 0, + optionalBool: false, + optionalFloat: 0, + optionalDouble: 0, + optionalString: "", + optionalBytes: "", + optionalMsg: {foo: 0}, + optionalEnum: "Default", + repeatedInt32: [], + repeatedInt64: [], + repeatedUint32: [], + repeatedUint64: [], + repeatedBool: [], + repeatedFloat: [], + repeatedDouble: [], + repeatedString: [], + repeatedBytes: [], + repeatedMsg: [], + repeatedEnum: [] + } + + actual = TestMessage.encode_json(m, :emit_defaults => true) + + assert JSON.parse(actual, :symbolize_names => true) == expected + end + + def test_json_emit_defaults_repeated_submsg + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" + m = TestMessage.new(repeated_msg: [TestMessage2.new]) + + expected = { + optionalInt32: 0, + optionalInt64: 0, + optionalUint32: 0, + optionalUint64: 0, + optionalBool: false, + optionalFloat: 0, + optionalDouble: 0, + optionalString: "", + optionalBytes: "", + optionalEnum: "Default", + repeatedInt32: [], + repeatedInt64: [], + repeatedUint32: [], + repeatedUint64: [], + repeatedBool: [], + repeatedFloat: [], + repeatedDouble: [], + repeatedString: [], + repeatedBytes: [], + repeatedMsg: [{foo: 0}], + repeatedEnum: [] + } + + actual = TestMessage.encode_json(m, :emit_defaults => true) + + assert JSON.parse(actual, :symbolize_names => true) == expected + end + def test_json_maps # TODO: Fix JSON in JRuby version. return if RUBY_PLATFORM == "java" m = MapMessage.new(:map_string_int32 => {"a" => 1}) - expected = '{"map_string_int32":{"a":1},"map_string_msg":{}}' - assert MapMessage.encode_json(m) == expected + expected = {mapStringInt32: {a: 1}, mapStringMsg: {}} + expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}} + assert JSON.parse(MapMessage.encode_json(m), :symbolize_names => true) == expected + + json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true) + assert JSON.parse(json, :symbolize_names => true) == expected_preserve + m2 = MapMessage.decode_json(MapMessage.encode_json(m)) assert m == m2 end + + def test_json_maps_emit_defaults_submsg + # TODO: Fix JSON in JRuby version. + return if RUBY_PLATFORM == "java" + m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new}) + expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}} + + actual = MapMessage.encode_json(m, :emit_defaults => true) + + assert JSON.parse(actual, :symbolize_names => true) == expected + end + + def test_comparison_with_arbitrary_object + assert MapMessage.new != nil + end + + def test_respond_to + # This test fails with JRuby 1.7.23, likely because of an old JRuby bug. + return if RUBY_PLATFORM == "java" + msg = MapMessage.new + assert msg.respond_to?(:map_string_int32) + assert !msg.respond_to?(:bacon) + end end end |