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