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