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