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