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_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 113 def test_has_field 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) 119 120 m = OneofMessage.new 121 assert !m.has_my_oneof? 122 m.a = "foo" 123 assert m.has_my_oneof? 124 assert_raise NoMethodError do 125 m.has_a? 126 end 127 assert_true OneofMessage.descriptor.lookup('a').has?(m) 128 129 m = TestSingularFields.new 130 assert_raise NoMethodError do 131 m.has_singular_int32? 132 end 133 assert_raise ArgumentError do 134 TestSingularFields.descriptor.lookup('singular_int32').has?(m) 135 end 136 137 assert_raise NoMethodError do 138 m.has_singular_string? 139 end 140 assert_raise ArgumentError do 141 TestSingularFields.descriptor.lookup('singular_string').has?(m) 142 end 143 144 assert_raise NoMethodError do 145 m.has_singular_bool? 146 end 147 assert_raise ArgumentError do 148 TestSingularFields.descriptor.lookup('singular_bool').has?(m) 149 end 150 151 m = TestMessage.new 152 assert_raise NoMethodError do 153 m.has_repeated_msg? 154 end 155 assert_raise ArgumentError do 156 TestMessage.descriptor.lookup('repeated_msg').has?(m) 157 end 158 end 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 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 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 221 222 def test_clear_repeated_fields 223 m = TestMessage.new 224 225 m.repeated_int32.push(1) 226 assert_equal [1], m.repeated_int32 227 m.clear_repeated_int32 228 assert_equal [], m.repeated_int32 229 230 m.repeated_int32.push(1) 231 assert_equal [1], m.repeated_int32 232 TestMessage.descriptor.lookup('repeated_int32').clear(m) 233 assert_equal [], m.repeated_int32 234 235 m = OneofMessage.new 236 m.a = "foo" 237 assert_equal "foo", m.a 238 assert m.has_my_oneof? 239 assert_equal :a, m.my_oneof 240 m.clear_a 241 assert !m.has_my_oneof? 242 243 m.a = "foobar" 244 assert m.has_my_oneof? 245 m.clear_my_oneof 246 assert !m.has_my_oneof? 247 248 m.a = "bar" 249 assert_equal "bar", m.a 250 assert m.has_my_oneof? 251 OneofMessage.descriptor.lookup('a').clear(m) 252 assert !m.has_my_oneof? 253 end 254 255 def test_initialization_map_errors 256 e = assert_raise ArgumentError do 257 TestMessage.new(:hello => "world") 258 end 259 assert_match(/hello/, e.message) 260 261 e = assert_raise ArgumentError do 262 MapMessage.new(:map_string_int32 => "hello") 263 end 264 assert_equal e.message, "Expected Hash object as initializer value for map field 'map_string_int32' (given String)." 265 266 e = assert_raise ArgumentError do 267 TestMessage.new(:repeated_uint32 => "hello") 268 end 269 assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32' (given String)." 270 end 271 272 def test_map_field 273 m = MapMessage.new 274 assert m.map_string_int32 == {} 275 assert m.map_string_msg == {} 276 277 m = MapMessage.new( 278 :map_string_int32 => {"a" => 1, "b" => 2}, 279 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 280 "b" => TestMessage2.new(:foo => 2)}, 281 :map_string_enum => {"a" => :A, "b" => :B}) 282 assert m.map_string_int32.keys.sort == ["a", "b"] 283 assert m.map_string_int32["a"] == 1 284 assert m.map_string_msg["b"].foo == 2 285 assert m.map_string_enum["a"] == :A 286 287 m.map_string_int32["c"] = 3 288 assert m.map_string_int32["c"] == 3 289 m.map_string_msg["c"] = TestMessage2.new(:foo => 3) 290 assert m.map_string_msg["c"] == TestMessage2.new(:foo => 3) 291 m.map_string_msg.delete("b") 292 m.map_string_msg.delete("c") 293 assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } 294 295 assert_raise Google::Protobuf::TypeError do 296 m.map_string_msg["e"] = TestMessage.new # wrong value type 297 end 298 # ensure nothing was added by the above 299 assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } 300 301 m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32) 302 assert_raise Google::Protobuf::TypeError do 303 m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64) 304 end 305 assert_raise Google::Protobuf::TypeError do 306 m.map_string_int32 = {} 307 end 308 309 assert_raise Google::Protobuf::TypeError do 310 m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" }) 311 end 312 end 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 328 def test_map_inspect 329 m = MapMessage.new( 330 :map_string_int32 => {"a" => 1, "b" => 2}, 331 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 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}" 340 end 341 342 def test_map_corruption 343 # This pattern led to a crash in a previous version of upb/protobuf. 344 m = MapMessage.new(map_string_int32: { "aaa" => 1 }) 345 m.map_string_int32['podid'] = 2 346 m.map_string_int32['aaa'] = 3 347 end 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 471 def test_concurrent_decoding 472 o = Outer.new 473 o.items[0] = Inner.new 474 raw = Outer.encode(o) 475 476 thds = 2.times.map do 477 Thread.new do 478 100000.times do 479 assert_equal o, Outer.decode(raw) 480 end 481 end 482 end 483 thds.map(&:join) 484 end 485 486 def test_map_encode_decode 487 m = MapMessage.new( 488 :map_string_int32 => {"a" => 1, "b" => 2}, 489 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 490 "b" => TestMessage2.new(:foo => 2)}, 491 :map_string_enum => {"a" => :A, "b" => :B}) 492 m2 = MapMessage.decode(MapMessage.encode(m)) 493 assert m == m2 494 495 m3 = MapMessageWireEquiv.decode(MapMessage.encode(m)) 496 assert m3.map_string_int32.length == 2 497 498 kv = {} 499 m3.map_string_int32.map { |msg| kv[msg.key] = msg.value } 500 assert kv == {"a" => 1, "b" => 2} 501 502 kv = {} 503 m3.map_string_msg.map { |msg| kv[msg.key] = msg.value } 504 assert kv == {"a" => TestMessage2.new(:foo => 1), 505 "b" => TestMessage2.new(:foo => 2)} 506 end 507 508 def test_protobuf_decode_json_ignore_unknown_fields 509 m = TestMessage.decode_json({ 510 optional_string: "foo", 511 not_in_message: "some_value" 512 }.to_json, { ignore_unknown_fields: true }) 513 514 assert_equal m.optional_string, "foo" 515 e = assert_raise Google::Protobuf::ParseError do 516 TestMessage.decode_json({ not_in_message: "some_value" }.to_json) 517 end 518 assert_match(/No such field: not_in_message/, e.message) 519 end 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 529 def test_to_h 530 m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'], :repeated_msg => [TestMessage2.new(:foo => 100)]) 531 expected_result = { 532 :optional_bool=>true, 533 :optional_bytes=>"", 534 :optional_double=>-10.100001, 535 :optional_enum=>:Default, 536 :optional_float=>0.0, 537 :optional_int32=>0, 538 :optional_int64=>0, 539 :optional_msg=>nil, 540 :optional_msg2=>nil, 541 :optional_proto2_submessage=>nil, 542 :optional_string=>"foo", 543 :optional_uint32=>0, 544 :optional_uint64=>0, 545 :repeated_bool=>[], 546 :repeated_bytes=>[], 547 :repeated_double=>[], 548 :repeated_enum=>[], 549 :repeated_float=>[], 550 :repeated_int32=>[], 551 :repeated_int64=>[], 552 :repeated_msg=>[{:foo => 100}], 553 :repeated_string=>["bar1", "bar2"], 554 :repeated_uint32=>[], 555 :repeated_uint64=>[] 556 } 557 assert_equal expected_result, m.to_h 558 559 m = MapMessage.new( 560 :map_string_int32 => {"a" => 1, "b" => 2}, 561 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 562 "b" => TestMessage2.new(:foo => 2)}, 563 :map_string_enum => {"a" => :A, "b" => :B}) 564 expected_result = { 565 :map_string_int32 => {"a" => 1, "b" => 2}, 566 :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}}, 567 :map_string_enum => {"a" => :A, "b" => :B} 568 } 569 assert_equal expected_result, m.to_h 570 end 571 572 573 def test_json_maps 574 m = MapMessage.new(:map_string_int32 => {"a" => 1}) 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 578 579 json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true, :emit_defaults=>true) 580 assert_equal JSON.parse(json, :symbolize_names => true), expected_preserve 581 582 m2 = MapMessage.decode_json(MapMessage.encode_json(m)) 583 assert_equal m, m2 584 end 585 586 def test_json_maps_emit_defaults_submsg 587 m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new(foo: 0)}) 588 expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}} 589 590 actual = MapMessage.encode_json(m, :emit_defaults => true) 591 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) 615 end 616 617 def test_respond_to 618 msg = MapMessage.new 619 assert msg.respond_to?(:map_string_int32) 620 assert !msg.respond_to?(:bacon) 621 end 622 623 def test_file_descriptor 624 file_descriptor = TestMessage.descriptor.file_descriptor 625 assert nil != file_descriptor 626 assert_equal "tests/basic_test.proto", file_descriptor.name 627 assert_equal :proto3, file_descriptor.syntax 628 629 file_descriptor = TestEnum.descriptor.file_descriptor 630 assert nil != file_descriptor 631 assert_equal "tests/basic_test.proto", file_descriptor.name 632 assert_equal :proto3, file_descriptor.syntax 633 end 634 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? 737 end 738 end 739end 740