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