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