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