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