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