1#!/usr/bin/ruby 2 3require 'google/protobuf' 4require 'test/unit' 5 6class RepeatedFieldTest < Test::Unit::TestCase 7 8 def test_acts_like_enumerator 9 m = TestMessage.new 10 (Enumerable.instance_methods - TestMessage.new.repeated_string.methods).each do |method_name| 11 assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" 12 end 13 end 14 15 def test_acts_like_an_array 16 m = TestMessage.new 17 arr_methods = ([].methods - TestMessage.new.repeated_string.methods) 18 # jRuby additions to the Array class that we can ignore 19 arr_methods -= [ :indices, :iter_for_each, :iter_for_each_index, 20 :iter_for_each_with_index, :dimensions, :copy_data, :copy_data_simple, 21 :nitems, :iter_for_reverse_each, :indexes, :append, :prepend] 22 arr_methods -= [:union, :difference, :filter!] 23 arr_methods -= [:intersection, :deconstruct] # ruby 2.7 methods we can ignore 24 arr_methods -= [:intersect?] # ruby 3.1 methods we can ignore 25 arr_methods.each do |method_name| 26 assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" 27 end 28 end 29 30 def test_first 31 m = TestMessage.new 32 repeated_field_names(TestMessage).each do |field_name| 33 assert_nil m.send(field_name).first 34 end 35 fill_test_msg(m) 36 assert_equal -10, m.repeated_int32.first 37 assert_equal -1_000_000, m.repeated_int64.first 38 assert_equal 10, m.repeated_uint32.first 39 assert_equal 1_000_000, m.repeated_uint64.first 40 assert_equal true, m.repeated_bool.first 41 assert_equal -1.01, m.repeated_float.first.round(2) 42 assert_equal -1.0000000000001, m.repeated_double.first 43 assert_equal 'foo', m.repeated_string.first 44 assert_equal "bar".encode!('ASCII-8BIT'), m.repeated_bytes.first 45 assert_equal TestMessage2.new(:foo => 1), m.repeated_msg.first 46 assert_equal :A, m.repeated_enum.first 47 end 48 49 50 def test_last 51 m = TestMessage.new 52 repeated_field_names(TestMessage).each do |field_name| 53 assert_nil m.send(field_name).first 54 end 55 fill_test_msg(m) 56 assert_equal -11, m.repeated_int32.last 57 assert_equal -1_000_001, m.repeated_int64.last 58 assert_equal 11, m.repeated_uint32.last 59 assert_equal 1_000_001, m.repeated_uint64.last 60 assert_equal false, m.repeated_bool.last 61 assert_equal -1.02, m.repeated_float.last.round(2) 62 assert_equal -1.0000000000002, m.repeated_double.last 63 assert_equal 'bar', m.repeated_string.last 64 assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.last 65 assert_equal TestMessage2.new(:foo => 2), m.repeated_msg.last 66 assert_equal :B, m.repeated_enum.last 67 end 68 69 70 def test_pop 71 m = TestMessage.new 72 repeated_field_names(TestMessage).each do |field_name| 73 assert_nil m.send(field_name).pop 74 end 75 fill_test_msg(m) 76 77 assert_equal -11, m.repeated_int32.pop 78 assert_equal -10, m.repeated_int32.pop 79 assert_equal -1_000_001, m.repeated_int64.pop 80 assert_equal -1_000_000, m.repeated_int64.pop 81 assert_equal 11, m.repeated_uint32.pop 82 assert_equal 10, m.repeated_uint32.pop 83 assert_equal 1_000_001, m.repeated_uint64.pop 84 assert_equal 1_000_000, m.repeated_uint64.pop 85 assert_equal false, m.repeated_bool.pop 86 assert_equal true, m.repeated_bool.pop 87 assert_equal -1.02, m.repeated_float.pop.round(2) 88 assert_equal -1.01, m.repeated_float.pop.round(2) 89 assert_equal -1.0000000000002, m.repeated_double.pop 90 assert_equal -1.0000000000001, m.repeated_double.pop 91 assert_equal 'bar', m.repeated_string.pop 92 assert_equal 'foo', m.repeated_string.pop 93 assert_equal "foo".encode!('ASCII-8BIT'), m.repeated_bytes.pop 94 assert_equal "bar".encode!('ASCII-8BIT'), m.repeated_bytes.pop 95 assert_equal TestMessage2.new(:foo => 2), m.repeated_msg.pop 96 assert_equal TestMessage2.new(:foo => 1), m.repeated_msg.pop 97 assert_equal :B, m.repeated_enum.pop 98 assert_equal :A, m.repeated_enum.pop 99 repeated_field_names(TestMessage).each do |field_name| 100 assert_nil m.send(field_name).pop 101 end 102 103 fill_test_msg(m) 104 assert_equal ['bar', 'foo'], m.repeated_string.pop(2) 105 assert_nil m.repeated_string.pop 106 end 107 108 109 def test_each 110 m = TestMessage.new 111 5.times{|i| m.repeated_string << 'string' } 112 count = 0 113 m.repeated_string.each do |val| 114 assert_equal 'string', val 115 count += 1 116 end 117 assert_equal 5, count 118 result = m.repeated_string.each{|val| val + '_junk'} 119 assert_equal ['string'] * 5, result 120 end 121 122 123 def test_empty? 124 m = TestMessage.new 125 assert_equal true, m.repeated_string.empty? 126 m.repeated_string << 'foo' 127 assert_equal false, m.repeated_string.empty? 128 m.repeated_string << 'bar' 129 assert_equal false, m.repeated_string.empty? 130 end 131 132 def test_array_accessor 133 m = TestMessage.new 134 reference_arr = %w(foo bar baz) 135 m.repeated_string += reference_arr.clone 136 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 137 arr[1] 138 end 139 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 140 arr[-2] 141 end 142 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 143 arr[20] 144 end 145 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 146 arr[1, 2] 147 end 148 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 149 arr[0..2] 150 end 151 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 152 arr[-1, 1] 153 end 154 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 155 arr[10, 12] 156 end 157 end 158 159 def test_array_settor 160 m = TestMessage.new 161 reference_arr = %w(foo bar baz) 162 m.repeated_string += reference_arr.clone 163 164 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 165 arr[1] = 'junk' 166 end 167 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 168 arr[-2] = 'snappy' 169 end 170 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 171 arr[3] = '' 172 end 173 # slight deviation; we are strongly typed, and nil is not allowed 174 # for string types; 175 m.repeated_string[5] = 'spacious' 176 assert_equal ["foo", "snappy", "baz", "", "", "spacious"], m.repeated_string 177 178 #make sure it sests the default types for other fields besides strings 179 %w(repeated_int32 repeated_int64 repeated_uint32 repeated_uint64).each do |field_name| 180 m.send(field_name)[3] = 10 181 assert_equal [0,0,0,10], m.send(field_name) 182 end 183 m.repeated_float[3] = 10.1 184 #wonky mri float handling 185 assert_equal [0,0,0], m.repeated_float.to_a[0..2] 186 assert_equal 10.1, m.repeated_float[3].round(1) 187 m.repeated_double[3] = 10.1 188 assert_equal [0,0,0,10.1], m.repeated_double 189 m.repeated_bool[3] = true 190 assert_equal [false, false, false, true], m.repeated_bool 191 m.repeated_bytes[3] = "bar".encode!('ASCII-8BIT') 192 assert_equal ['', '', '', "bar".encode!('ASCII-8BIT')], m.repeated_bytes 193 m.repeated_msg[3] = TestMessage2.new(:foo => 1) 194 assert_equal [nil, nil, nil, TestMessage2.new(:foo => 1)], m.repeated_msg 195 m.repeated_enum[3] = :A 196 assert_equal [:Default, :Default, :Default, :A], m.repeated_enum 197 198 # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 199 # arr[20] = 'spacious' 200 # end 201 # TODO: accessor doesn't allow other ruby-like methods 202 # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 203 # arr[1, 2] = 'fizz' 204 # end 205 # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 206 # arr[0..2] = 'buzz' 207 # end 208 end 209 210 def test_push 211 m = TestMessage.new 212 reference_arr = %w(foo bar baz) 213 m.repeated_string += reference_arr.clone 214 215 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 216 arr.push('fizz') 217 end 218 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 219 arr << 'fizz' 220 end 221 #TODO: push should support multiple 222 # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 223 # arr.push('fizz', 'buzz') 224 # end 225 end 226 227 def test_clear 228 m = TestMessage.new 229 reference_arr = %w(foo bar baz) 230 m.repeated_string += reference_arr.clone 231 232 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 233 arr.clear 234 end 235 end 236 237 def test_concat 238 m = TestMessage.new 239 reference_arr = %w(foo bar baz) 240 m.repeated_string += reference_arr.clone 241 m.repeated_string.concat(['fizz', 'buzz']) 242 assert_equal %w(foo bar baz fizz buzz), m.repeated_string 243 #TODO: concat should return the orig array 244 # check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 245 # arr.concat(['fizz', 'buzz']) 246 # end 247 end 248 249 def test_equal 250 m = TestMessage.new 251 reference_arr = %w(foo bar baz) 252 m.repeated_string += reference_arr.clone 253 assert_equal reference_arr, m.repeated_string 254 reference_arr << 'fizz' 255 assert_not_equal reference_arr, m.repeated_string 256 m.repeated_string << 'fizz' 257 assert_equal reference_arr, m.repeated_string 258 end 259 260 def test_hash 261 # just a sanity check 262 m = TestMessage.new 263 reference_arr = %w(foo bar baz) 264 m.repeated_string += reference_arr.clone 265 assert m.repeated_string.hash.is_a?(Integer) 266 hash = m.repeated_string.hash 267 assert_equal hash, m.repeated_string.hash 268 m.repeated_string << 'j' 269 assert_not_equal hash, m.repeated_string.hash 270 end 271 272 def test_plus 273 m = TestMessage.new 274 reference_arr = %w(foo bar baz) 275 m.repeated_string += reference_arr.clone 276 277 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 278 arr + ['fizz', 'buzz'] 279 end 280 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 281 arr += ['fizz', 'buzz'] 282 end 283 end 284 285 def test_replace 286 m = TestMessage.new 287 reference_arr = %w(foo bar baz) 288 m.repeated_string += reference_arr.clone 289 290 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 291 arr.replace(['fizz', 'buzz']) 292 end 293 end 294 295 def test_to_a 296 m = TestMessage.new 297 reference_arr = %w(foo bar baz) 298 m.repeated_string += reference_arr.clone 299 300 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 301 arr.to_a 302 end 303 end 304 305 def test_to_ary 306 m = TestMessage.new 307 reference_arr = %w(foo bar baz) 308 m.repeated_string += reference_arr.clone 309 310 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 311 arr.to_ary 312 end 313 end 314 315 # emulate Array behavior 316 ########################## 317 318 def test_collect! 319 m = TestMessage.new 320 reference_arr = %w(foo bar baz) 321 m.repeated_string += reference_arr.clone 322 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 323 arr.collect!{|x| x + "!" } 324 end 325 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 326 arr.collect!.with_index{|x, i| x[0...i] } 327 end 328 end 329 330 def test_delete 331 m = TestMessage.new 332 reference_arr = %w(foo bar baz) 333 m.repeated_string += reference_arr.clone 334 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 335 arr.delete('bar') 336 end 337 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 338 arr.delete('nope') 339 end 340 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 341 arr.delete('nope'){'within'} 342 end 343 end 344 345 def test_delete_at 346 m = TestMessage.new 347 reference_arr = %w(foo bar baz) 348 m.repeated_string += reference_arr.clone 349 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 350 arr.delete_at(2) 351 end 352 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 353 arr.delete_at(10) 354 end 355 end 356 357 def test_fill 358 m = TestMessage.new 359 reference_arr = %w(foo bar baz) 360 m.repeated_string += reference_arr.clone 361 362 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 363 arr.fill("x") 364 end 365 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 366 arr.fill("z", 2, 2) 367 end 368 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 369 arr.fill("y", 0..1) 370 end 371 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 372 arr.fill { |i| (i*i).to_s } 373 end 374 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 375 arr.fill(-2) { |i| (i*i*i).to_s } 376 end 377 end 378 379 def test_flatten! 380 m = TestMessage.new 381 reference_arr = %w(foo bar baz) 382 m.repeated_string += reference_arr.clone 383 384 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 385 arr.flatten! 386 end 387 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 388 arr.flatten!(1) 389 end 390 end 391 392 def test_insert 393 m = TestMessage.new 394 reference_arr = %w(foo bar baz) 395 m.repeated_string += reference_arr.clone 396 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 397 arr.insert(2, 'fizz') 398 end 399 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 400 arr.insert(3, 'fizz', 'buzz', 'bazz') 401 end 402 end 403 404 def test_inspect 405 m = TestMessage.new 406 assert_equal '[]', m.repeated_string.inspect 407 m.repeated_string << 'foo' 408 assert_equal m.repeated_string.to_a.inspect, m.repeated_string.inspect 409 m.repeated_string << 'bar' 410 assert_equal m.repeated_string.to_a.inspect, m.repeated_string.inspect 411 end 412 413 def test_reverse! 414 m = TestMessage.new 415 reference_arr = %w(foo bar baz) 416 m.repeated_string += reference_arr.clone 417 418 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 419 arr.reverse! 420 end 421 end 422 423 def test_rotate! 424 m = TestMessage.new 425 reference_arr = %w(foo bar baz) 426 m.repeated_string += reference_arr.clone 427 428 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 429 arr.rotate! 430 end 431 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 432 arr.rotate!(2) 433 end 434 end 435 436 def test_select! 437 m = TestMessage.new 438 reference_arr = %w(foo bar baz) 439 m.repeated_string += reference_arr.clone 440 441 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 442 arr.select! { |v| v =~ /[aeiou]/ } 443 end 444 end 445 446 def test_shift 447 m = TestMessage.new 448 reference_arr = %w(foo bar baz) 449 m.repeated_string += reference_arr.clone 450 451 # should return an element 452 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 453 arr.shift 454 end 455 # should return an array 456 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 457 arr.shift(2) 458 end 459 # should return nil 460 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 461 arr.shift 462 end 463 end 464 465 def test_shuffle! 466 m = TestMessage.new 467 m.repeated_string += %w(foo bar baz) 468 orig_repeated_string = m.repeated_string.clone 469 result = m.repeated_string.shuffle! 470 assert_equal m.repeated_string, result 471 # NOTE: sometimes it doesn't change the order... 472 # assert_not_equal m.repeated_string.to_a, orig_repeated_string.to_a 473 end 474 475 def test_slice! 476 m = TestMessage.new 477 reference_arr = %w(foo bar baz bar fizz buzz) 478 m.repeated_string += reference_arr.clone 479 480 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 481 arr.slice!(2) 482 end 483 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 484 arr.slice!(1,2) 485 end 486 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 487 arr.slice!(0..1) 488 end 489 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 490 arr.slice!(10) 491 end 492 end 493 494 def test_sort! 495 m = TestMessage.new 496 reference_arr = %w(foo bar baz) 497 m.repeated_string += reference_arr.clone 498 499 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 500 arr.sort! 501 end 502 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 503 arr.sort! { |x,y| y <=> x } 504 end 505 end 506 507 def test_sort_by! 508 m = TestMessage.new 509 reference_arr = %w(foo bar baz) 510 m.repeated_string += reference_arr.clone 511 512 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 513 arr.sort_by! 514 end 515 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 516 arr.sort_by!(&:hash) 517 end 518 end 519 520 def test_uniq! 521 m = TestMessage.new 522 reference_arr = %w(foo bar baz) 523 m.repeated_string += reference_arr.clone 524 525 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 526 arr.uniq! 527 end 528 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 529 arr.uniq!{|s| s[0] } 530 end 531 end 532 533 def test_unshift 534 m = TestMessage.new 535 reference_arr = %w(foo bar baz) 536 m.repeated_string += reference_arr.clone 537 538 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 539 arr.unshift('1') 540 end 541 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 542 arr.unshift('a', 'b') 543 end 544 check_self_modifying_method(m.repeated_string, reference_arr) do |arr| 545 arr.unshift('') 546 end 547 end 548 549 550 ##### HELPER METHODS 551 552 def check_self_modifying_method(repeated_field, ref_array) 553 expected_result = yield(ref_array) 554 actual_result = yield(repeated_field) 555 if expected_result.is_a?(Enumerator) 556 assert_equal expected_result.to_a, actual_result.to_a 557 else 558 assert_equal expected_result, actual_result 559 end 560 assert_equal ref_array, repeated_field 561 end 562 563 564 def repeated_field_names(klass) 565 klass.descriptor.find_all{|f| f.label == :repeated}.map(&:name) 566 end 567 568 569 def fill_test_msg(test_msg) 570 test_msg.repeated_int32 += [-10, -11] 571 test_msg.repeated_int64 += [-1_000_000, -1_000_001] 572 test_msg.repeated_uint32 += [10, 11] 573 test_msg.repeated_uint64 += [1_000_000, 1_000_001] 574 test_msg.repeated_bool += [true, false] 575 test_msg.repeated_float += [-1.01, -1.02] 576 test_msg.repeated_double += [-1.0000000000001, -1.0000000000002] 577 test_msg.repeated_string += %w(foo bar) 578 test_msg.repeated_bytes += ["bar".encode!('ASCII-8BIT'), "foo".encode!('ASCII-8BIT')] 579 test_msg.repeated_msg << TestMessage2.new(:foo => 1) 580 test_msg.repeated_msg << TestMessage2.new(:foo => 2) 581 test_msg.repeated_enum << :A 582 test_msg.repeated_enum << :B 583 end 584 585 586 pool = Google::Protobuf::DescriptorPool.new 587 pool.build do 588 589 add_message "TestMessage" do 590 optional :optional_int32, :int32, 1 591 optional :optional_int64, :int64, 2 592 optional :optional_uint32, :uint32, 3 593 optional :optional_uint64, :uint64, 4 594 optional :optional_bool, :bool, 5 595 optional :optional_float, :float, 6 596 optional :optional_double, :double, 7 597 optional :optional_string, :string, 8 598 optional :optional_bytes, :bytes, 9 599 optional :optional_msg, :message, 10, "TestMessage2" 600 optional :optional_enum, :enum, 11, "TestEnum" 601 602 repeated :repeated_int32, :int32, 12 603 repeated :repeated_int64, :int64, 13 604 repeated :repeated_uint32, :uint32, 14 605 repeated :repeated_uint64, :uint64, 15 606 repeated :repeated_bool, :bool, 16 607 repeated :repeated_float, :float, 17 608 repeated :repeated_double, :double, 18 609 repeated :repeated_string, :string, 19 610 repeated :repeated_bytes, :bytes, 20 611 repeated :repeated_msg, :message, 21, "TestMessage2" 612 repeated :repeated_enum, :enum, 22, "TestEnum" 613 end 614 add_message "TestMessage2" do 615 optional :foo, :int32, 1 616 end 617 618 add_enum "TestEnum" do 619 value :Default, 0 620 value :A, 1 621 value :B, 2 622 value :C, 3 623 end 624 end 625 626 TestMessage = pool.lookup("TestMessage").msgclass 627 TestMessage2 = pool.lookup("TestMessage2").msgclass 628 TestEnum = pool.lookup("TestEnum").enummodule 629 630 631end 632