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 optional :"a.b", :int32, 3 21 end 22 end 23 24 BadFieldNames = pool.lookup("BadFieldNames").msgclass 25 26# ------------ test cases --------------- 27 28 class MessageContainerTest < Test::Unit::TestCase 29 # Required by CommonTests module to resolve proto3 proto classes used in tests. 30 def proto_module 31 ::BasicTest 32 end 33 include CommonTests 34 35 def test_has_field 36 m = TestMessage.new 37 assert !m.has_optional_msg? 38 m.optional_msg = TestMessage2.new 39 assert m.has_optional_msg? 40 assert TestMessage.descriptor.lookup('optional_msg').has?(m) 41 42 m = OneofMessage.new 43 assert !m.has_my_oneof? 44 m.a = "foo" 45 assert m.has_my_oneof? 46 assert_raise NoMethodError do 47 m.has_a? 48 end 49 assert_raise ArgumentError do 50 OneofMessage.descriptor.lookup('a').has?(m) 51 end 52 53 m = TestMessage.new 54 assert_raise NoMethodError do 55 m.has_optional_int32? 56 end 57 assert_raise ArgumentError do 58 TestMessage.descriptor.lookup('optional_int32').has?(m) 59 end 60 61 assert_raise NoMethodError do 62 m.has_optional_string? 63 end 64 assert_raise ArgumentError do 65 TestMessage.descriptor.lookup('optional_string').has?(m) 66 end 67 68 assert_raise NoMethodError do 69 m.has_optional_bool? 70 end 71 assert_raise ArgumentError do 72 TestMessage.descriptor.lookup('optional_bool').has?(m) 73 end 74 75 assert_raise NoMethodError do 76 m.has_repeated_msg? 77 end 78 assert_raise ArgumentError do 79 TestMessage.descriptor.lookup('repeated_msg').has?(m) 80 end 81 end 82 83 def test_set_clear_defaults 84 m = TestMessage.new 85 86 m.optional_int32 = -42 87 assert_equal -42, m.optional_int32 88 m.clear_optional_int32 89 assert_equal 0, m.optional_int32 90 91 m.optional_int32 = 50 92 assert_equal 50, m.optional_int32 93 TestMessage.descriptor.lookup('optional_int32').clear(m) 94 assert_equal 0, m.optional_int32 95 96 m.optional_string = "foo bar" 97 assert_equal "foo bar", m.optional_string 98 m.clear_optional_string 99 assert_equal "", m.optional_string 100 101 m.optional_string = "foo" 102 assert_equal "foo", m.optional_string 103 TestMessage.descriptor.lookup('optional_string').clear(m) 104 assert_equal "", m.optional_string 105 106 m.optional_msg = TestMessage2.new(:foo => 42) 107 assert_equal TestMessage2.new(:foo => 42), m.optional_msg 108 assert m.has_optional_msg? 109 m.clear_optional_msg 110 assert_equal nil, m.optional_msg 111 assert !m.has_optional_msg? 112 113 m.optional_msg = TestMessage2.new(:foo => 42) 114 assert_equal TestMessage2.new(:foo => 42), m.optional_msg 115 TestMessage.descriptor.lookup('optional_msg').clear(m) 116 assert_equal nil, m.optional_msg 117 118 m.repeated_int32.push(1) 119 assert_equal [1], m.repeated_int32 120 m.clear_repeated_int32 121 assert_equal [], m.repeated_int32 122 123 m.repeated_int32.push(1) 124 assert_equal [1], m.repeated_int32 125 TestMessage.descriptor.lookup('repeated_int32').clear(m) 126 assert_equal [], m.repeated_int32 127 128 m = OneofMessage.new 129 m.a = "foo" 130 assert_equal "foo", m.a 131 assert m.has_my_oneof? 132 m.clear_a 133 assert !m.has_my_oneof? 134 135 m.a = "foobar" 136 assert m.has_my_oneof? 137 m.clear_my_oneof 138 assert !m.has_my_oneof? 139 140 m.a = "bar" 141 assert_equal "bar", m.a 142 assert m.has_my_oneof? 143 OneofMessage.descriptor.lookup('a').clear(m) 144 assert !m.has_my_oneof? 145 end 146 147 148 def test_initialization_map_errors 149 e = assert_raise ArgumentError do 150 TestMessage.new(:hello => "world") 151 end 152 assert_match(/hello/, e.message) 153 154 e = assert_raise ArgumentError do 155 MapMessage.new(:map_string_int32 => "hello") 156 end 157 assert_equal e.message, "Expected Hash object as initializer value for map field 'map_string_int32' (given String)." 158 159 e = assert_raise ArgumentError do 160 TestMessage.new(:repeated_uint32 => "hello") 161 end 162 assert_equal e.message, "Expected array as initializer value for repeated field 'repeated_uint32' (given String)." 163 end 164 165 def test_map_field 166 m = MapMessage.new 167 assert m.map_string_int32 == {} 168 assert m.map_string_msg == {} 169 170 m = MapMessage.new( 171 :map_string_int32 => {"a" => 1, "b" => 2}, 172 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 173 "b" => TestMessage2.new(:foo => 2)}, 174 :map_string_enum => {"a" => :A, "b" => :B}) 175 assert m.map_string_int32.keys.sort == ["a", "b"] 176 assert m.map_string_int32["a"] == 1 177 assert m.map_string_msg["b"].foo == 2 178 assert m.map_string_enum["a"] == :A 179 180 m.map_string_int32["c"] = 3 181 assert m.map_string_int32["c"] == 3 182 m.map_string_msg["c"] = TestMessage2.new(:foo => 3) 183 assert m.map_string_msg["c"] == TestMessage2.new(:foo => 3) 184 m.map_string_msg.delete("b") 185 m.map_string_msg.delete("c") 186 assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } 187 188 assert_raise Google::Protobuf::TypeError do 189 m.map_string_msg["e"] = TestMessage.new # wrong value type 190 end 191 # ensure nothing was added by the above 192 assert m.map_string_msg == { "a" => TestMessage2.new(:foo => 1) } 193 194 m.map_string_int32 = Google::Protobuf::Map.new(:string, :int32) 195 assert_raise Google::Protobuf::TypeError do 196 m.map_string_int32 = Google::Protobuf::Map.new(:string, :int64) 197 end 198 assert_raise Google::Protobuf::TypeError do 199 m.map_string_int32 = {} 200 end 201 202 assert_raise TypeError do 203 m = MapMessage.new(:map_string_int32 => { 1 => "I am not a number" }) 204 end 205 end 206 207 def test_map_inspect 208 m = MapMessage.new( 209 :map_string_int32 => {"a" => 1, "b" => 2}, 210 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 211 "b" => TestMessage2.new(:foo => 2)}, 212 :map_string_enum => {"a" => :A, "b" => :B}) 213 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}>" 214 assert_equal expected, m.inspect 215 end 216 217 def test_map_corruption 218 # This pattern led to a crash in a previous version of upb/protobuf. 219 m = MapMessage.new(map_string_int32: { "aaa" => 1 }) 220 m.map_string_int32['podid'] = 2 221 m.map_string_int32['aaa'] = 3 222 end 223 224 def test_concurrent_decoding 225 o = Outer.new 226 o.items[0] = Inner.new 227 raw = Outer.encode(o) 228 229 thds = 2.times.map do 230 Thread.new do 231 100000.times do 232 assert_equal o, Outer.decode(raw) 233 end 234 end 235 end 236 thds.map(&:join) 237 end 238 239 def test_map_encode_decode 240 m = MapMessage.new( 241 :map_string_int32 => {"a" => 1, "b" => 2}, 242 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 243 "b" => TestMessage2.new(:foo => 2)}, 244 :map_string_enum => {"a" => :A, "b" => :B}) 245 m2 = MapMessage.decode(MapMessage.encode(m)) 246 assert m == m2 247 248 m3 = MapMessageWireEquiv.decode(MapMessage.encode(m)) 249 assert m3.map_string_int32.length == 2 250 251 kv = {} 252 m3.map_string_int32.map { |msg| kv[msg.key] = msg.value } 253 assert kv == {"a" => 1, "b" => 2} 254 255 kv = {} 256 m3.map_string_msg.map { |msg| kv[msg.key] = msg.value } 257 assert kv == {"a" => TestMessage2.new(:foo => 1), 258 "b" => TestMessage2.new(:foo => 2)} 259 end 260 261 def test_protobuf_decode_json_ignore_unknown_fields 262 m = TestMessage.decode_json({ 263 optional_string: "foo", 264 not_in_message: "some_value" 265 }.to_json, { ignore_unknown_fields: true }) 266 267 assert_equal m.optional_string, "foo" 268 e = assert_raise Google::Protobuf::ParseError do 269 TestMessage.decode_json({ not_in_message: "some_value" }.to_json) 270 end 271 assert_match(/No such field: not_in_message/, e.message) 272 end 273 274 def test_to_h 275 m = TestMessage.new(:optional_bool => true, :optional_double => -10.100001, :optional_string => 'foo', :repeated_string => ['bar1', 'bar2'], :repeated_msg => [TestMessage2.new(:foo => 100)]) 276 expected_result = { 277 :optional_bool=>true, 278 :optional_bytes=>"", 279 :optional_double=>-10.100001, 280 :optional_enum=>:Default, 281 :optional_float=>0.0, 282 :optional_int32=>0, 283 :optional_int64=>0, 284 :optional_msg=>nil, 285 :optional_string=>"foo", 286 :optional_uint32=>0, 287 :optional_uint64=>0, 288 :repeated_bool=>[], 289 :repeated_bytes=>[], 290 :repeated_double=>[], 291 :repeated_enum=>[], 292 :repeated_float=>[], 293 :repeated_int32=>[], 294 :repeated_int64=>[], 295 :repeated_msg=>[{:foo => 100}], 296 :repeated_string=>["bar1", "bar2"], 297 :repeated_uint32=>[], 298 :repeated_uint64=>[] 299 } 300 assert_equal expected_result, m.to_h 301 302 m = MapMessage.new( 303 :map_string_int32 => {"a" => 1, "b" => 2}, 304 :map_string_msg => {"a" => TestMessage2.new(:foo => 1), 305 "b" => TestMessage2.new(:foo => 2)}, 306 :map_string_enum => {"a" => :A, "b" => :B}) 307 expected_result = { 308 :map_string_int32 => {"a" => 1, "b" => 2}, 309 :map_string_msg => {"a" => {:foo => 1}, "b" => {:foo => 2}}, 310 :map_string_enum => {"a" => :A, "b" => :B} 311 } 312 assert_equal expected_result, m.to_h 313 end 314 315 316 def test_json_maps 317 # TODO: Fix JSON in JRuby version. 318 return if RUBY_PLATFORM == "java" 319 m = MapMessage.new(:map_string_int32 => {"a" => 1}) 320 expected = {mapStringInt32: {a: 1}, mapStringMsg: {}, mapStringEnum: {}} 321 expected_preserve = {map_string_int32: {a: 1}, map_string_msg: {}, map_string_enum: {}} 322 assert_equal JSON.parse(MapMessage.encode_json(m), :symbolize_names => true), expected 323 324 json = MapMessage.encode_json(m, :preserve_proto_fieldnames => true) 325 assert_equal JSON.parse(json, :symbolize_names => true), expected_preserve 326 327 m2 = MapMessage.decode_json(MapMessage.encode_json(m)) 328 assert_equal m, m2 329 end 330 331 def test_json_maps_emit_defaults_submsg 332 # TODO: Fix JSON in JRuby version. 333 return if RUBY_PLATFORM == "java" 334 m = MapMessage.new(:map_string_msg => {"a" => TestMessage2.new}) 335 expected = {mapStringInt32: {}, mapStringMsg: {a: {foo: 0}}, mapStringEnum: {}} 336 337 actual = MapMessage.encode_json(m, :emit_defaults => true) 338 339 assert_equal JSON.parse(actual, :symbolize_names => true), expected 340 end 341 342 def test_respond_to 343 # This test fails with JRuby 1.7.23, likely because of an old JRuby bug. 344 return if RUBY_PLATFORM == "java" 345 msg = MapMessage.new 346 assert msg.respond_to?(:map_string_int32) 347 assert !msg.respond_to?(:bacon) 348 end 349 350 def test_file_descriptor 351 file_descriptor = TestMessage.descriptor.file_descriptor 352 assert nil != file_descriptor 353 assert_equal "tests/basic_test.proto", file_descriptor.name 354 assert_equal :proto3, file_descriptor.syntax 355 356 file_descriptor = TestEnum.descriptor.file_descriptor 357 assert nil != file_descriptor 358 assert_equal "tests/basic_test.proto", file_descriptor.name 359 assert_equal :proto3, file_descriptor.syntax 360 361 file_descriptor = BadFieldNames.descriptor.file_descriptor 362 assert nil != file_descriptor 363 assert_equal nil, file_descriptor.name 364 assert_equal :proto3, file_descriptor.syntax 365 end 366 367 # Ruby 2.5 changed to raise FrozenError instead of RuntimeError 368 FrozenErrorType = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.5') ? RuntimeError : FrozenError 369 370 def test_map_freeze 371 m = proto_module::MapMessage.new 372 m.map_string_int32['a'] = 5 373 m.map_string_msg['b'] = proto_module::TestMessage2.new 374 375 m.map_string_int32.freeze 376 m.map_string_msg.freeze 377 378 assert m.map_string_int32.frozen? 379 assert m.map_string_msg.frozen? 380 381 assert_raise(FrozenErrorType) { m.map_string_int32['foo'] = 1 } 382 assert_raise(FrozenErrorType) { m.map_string_msg['bar'] = proto_module::TestMessage2.new } 383 assert_raise(FrozenErrorType) { m.map_string_int32.delete('a') } 384 assert_raise(FrozenErrorType) { m.map_string_int32.clear } 385 end 386 end 387end 388