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