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