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