google-gax 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,7 +45,7 @@ module Google
45
45
  bundle_config.each_pair do |key, value|
46
46
  options[key.intern] = value
47
47
  end
48
- # TODO: comment-out when bundling is supported.
48
+ # Bundling is currently not supported.
49
49
  # Executor.new(options)
50
50
  nil
51
51
  end
@@ -29,6 +29,6 @@
29
29
 
30
30
  module Google
31
31
  module Gax
32
- VERSION = '0.4.0'.freeze
32
+ VERSION = '0.4.2'.freeze
33
33
  end
34
34
  end
@@ -48,19 +48,19 @@ describe Google::Gax do
48
48
  describe 'create_api_call' do
49
49
  it 'calls api call' do
50
50
  settings = CallSettings.new
51
- timeout_arg = nil
52
- func = proc do |timeout: nil|
53
- timeout_arg = timeout
51
+ deadline_arg = nil
52
+ func = proc do |deadline: nil, **_kwargs|
53
+ deadline_arg = deadline
54
54
  42
55
55
  end
56
56
  my_callable = Google::Gax.create_api_call(func, settings)
57
57
  expect(my_callable.call(nil)).to eq(42)
58
- expect(timeout_arg).to_not be_nil
58
+ expect(deadline_arg).to be_a(Time)
59
59
 
60
- new_timeout = timeout_arg + 20
61
- options = Google::Gax::CallOptions.new(timeout: new_timeout)
60
+ new_deadline = Time.now + 20
61
+ options = Google::Gax::CallOptions.new(timeout: 20)
62
62
  expect(my_callable.call(nil, options)).to eq(42)
63
- expect(timeout_arg).to eq(new_timeout)
63
+ expect(deadline_arg).to be_within(0.9).of(new_deadline)
64
64
  end
65
65
  end
66
66
 
@@ -71,9 +71,9 @@ describe Google::Gax do
71
71
  page_descriptor = Google::Gax::PageDescriptor.new('page_token',
72
72
  'next_page_token', 'nums')
73
73
  settings = CallSettings.new(page_descriptor: page_descriptor)
74
- timeout_arg = nil
75
- func = proc do |request, timeout: nil|
76
- timeout_arg = timeout
74
+ deadline_arg = nil
75
+ func = proc do |request, deadline: nil, **_kwargs|
76
+ deadline_arg = deadline
77
77
  page_token = request['page_token']
78
78
  if page_token > 0 && page_token < page_size * pages_to_stream
79
79
  { 'nums' => (page_token...(page_token + page_size)),
@@ -90,7 +90,7 @@ describe Google::Gax do
90
90
  expect(my_callable.call('page_token' => 0).to_a).to match_array(
91
91
  (0...(page_size * pages_to_stream))
92
92
  )
93
- expect(timeout_arg).to_not be_nil
93
+ expect(deadline_arg).to be_a(Time)
94
94
  end
95
95
 
96
96
  it 'offers interface for pages' do
@@ -118,10 +118,10 @@ describe Google::Gax do
118
118
  describe 'failures without retry' do
119
119
  it 'simply fails' do
120
120
  settings = CallSettings.new(errors: [GRPC::Cancelled])
121
- timeout_arg = nil
121
+ deadline_arg = nil
122
122
  call_count = 0
123
- func = proc do |timeout: nil|
124
- timeout_arg = timeout
123
+ func = proc do |deadline: nil, **_kwargs|
124
+ deadline_arg = deadline
125
125
  call_count += 1
126
126
  raise GRPC::Cancelled, ''
127
127
  end
@@ -132,26 +132,66 @@ describe Google::Gax do
132
132
  rescue Google::Gax::GaxError => exc
133
133
  expect(exc.cause).to be_a(GRPC::Cancelled)
134
134
  end
135
- expect(timeout_arg).to_not be_nil
135
+ expect(deadline_arg).to be_a(Time)
136
136
  expect(call_count).to eq(1)
137
137
  end
138
138
 
139
139
  it 'does not wrap unknown errors' do
140
140
  settings = CallSettings.new
141
- timeout_arg = nil
141
+ deadline_arg = nil
142
142
  call_count = 0
143
- func = proc do |timeout: nil|
144
- timeout_arg = timeout
143
+ func = proc do |deadline: nil, **_kwargs|
144
+ deadline_arg = deadline
145
145
  call_count += 1
146
146
  raise CustomException.new('', FAKE_STATUS_CODE_1)
147
147
  end
148
148
  my_callable = Google::Gax.create_api_call(func, settings)
149
149
  expect { my_callable.call }.to raise_error(CustomException)
150
- expect(timeout_arg).to_not be_nil
150
+ expect(deadline_arg).to be_a(Time)
151
151
  expect(call_count).to eq(1)
152
152
  end
153
153
  end
154
154
 
155
+ describe 'bundleable' do
156
+ it 'bundling page streaming error' do
157
+ settings = CallSettings.new(
158
+ page_descriptor: Object.new,
159
+ bundle_descriptor: Object.new,
160
+ bundler: Object.new
161
+ )
162
+ expect do
163
+ Google::Gax.create_api_call(proc {}, settings)
164
+ end.to raise_error(RuntimeError)
165
+ end
166
+
167
+ it 'bundles the API call' do
168
+ BundleOptions = Google::Gax::BundleOptions
169
+ BundleDescriptor = Google::Gax::BundleDescriptor
170
+
171
+ bundler = Google::Gax::Executor.new(
172
+ BundleOptions.new(element_count_threshold: 8)
173
+ )
174
+ fake_descriptor = BundleDescriptor.new('elements', [])
175
+ settings = CallSettings.new(
176
+ bundler: bundler,
177
+ bundle_descriptor: fake_descriptor,
178
+ timeout: 0
179
+ )
180
+
181
+ func = proc do |request, _|
182
+ request['elements'].count
183
+ end
184
+
185
+ callable = Google::Gax.create_api_call(func, settings)
186
+
187
+ first = callable.call('elements' => [0] * 5)
188
+ expect(first).to be_an_instance_of Google::Gax::Event
189
+ expect(first.result).to be_nil
190
+ second = callable.call('elements' => [0] * 3)
191
+ expect(second.result).to be 8
192
+ end
193
+ end
194
+
155
195
  describe 'retryable' do
156
196
  RetryOptions = Google::Gax::RetryOptions
157
197
  BackoffSettings = Google::Gax::BackoffSettings
@@ -166,9 +206,9 @@ describe Google::Gax do
166
206
 
167
207
  to_attempt = 3
168
208
 
169
- timeout_arg = nil
170
- func = proc do |timeout: nil|
171
- timeout_arg = timeout
209
+ deadline_arg = nil
210
+ func = proc do |deadline: nil, **_kwargs|
211
+ deadline_arg = deadline
172
212
  to_attempt -= 1
173
213
  raise CustomException.new('', FAKE_STATUS_CODE_1) if to_attempt > 0
174
214
  1729
@@ -176,7 +216,7 @@ describe Google::Gax do
176
216
  my_callable = Google::Gax.create_api_call(func, settings)
177
217
  expect(my_callable.call).to eq(1729)
178
218
  expect(to_attempt).to eq(0)
179
- expect(timeout_arg).to_not be_nil
219
+ expect(deadline_arg).to be_a(Time)
180
220
  end
181
221
 
182
222
  it 'doesn\'t retry if no codes' do
@@ -211,11 +251,17 @@ describe Google::Gax do
211
251
  call_count = 0
212
252
 
213
253
  time_now = Time.now
214
- allow(Time).to receive(:now).exactly(4).times.and_return(
215
- *([time_now] * to_attempt + [time_now + 2])
254
+ # Time.now will be called twice for each API call (one in set_timeout_arg
255
+ # and the other in retryable). It returns time_now for to_attempt * 2
256
+ # times (which allows retrying), and then finally returns time_now + 2
257
+ # to exceed the deadline.
258
+ allow(Time).to receive(:now).exactly(to_attempt * 2 + 1).times.and_return(
259
+ *([time_now] * to_attempt * 2 + [time_now + 2])
216
260
  )
217
261
 
218
- func = proc do
262
+ deadline_arg = nil
263
+ func = proc do |deadline: nil, **_kwargs|
264
+ deadline_arg = deadline
219
265
  call_count += 1
220
266
  raise CustomException.new('', FAKE_STATUS_CODE_1)
221
267
  end
@@ -227,6 +273,7 @@ describe Google::Gax do
227
273
  rescue Google::Gax::RetryError => exc
228
274
  expect(exc.cause).to be_a(CustomException)
229
275
  end
276
+ expect(deadline_arg).to eq(time_now)
230
277
  expect(call_count).to eq(to_attempt)
231
278
  end
232
279
 
@@ -252,10 +299,10 @@ describe Google::Gax do
252
299
  start_time = time_now
253
300
  incr_time = proc { |secs| time_now += secs }
254
301
  call_count = 0
255
- func = proc do |_, timeout: nil|
302
+ func = proc do |_, deadline: nil, **_kwargs|
256
303
  call_count += 1
257
- incr_time.call(timeout)
258
- raise CustomException.new(timeout.to_s, FAKE_STATUS_CODE_1)
304
+ incr_time.call(deadline - time_now)
305
+ raise CustomException.new(deadline.to_s, FAKE_STATUS_CODE_1)
259
306
  end
260
307
 
261
308
  allow(Time).to receive(:now) { time_now }
@@ -0,0 +1,627 @@
1
+ # Copyright 2016, Google Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions are
6
+ # met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright
9
+ # notice, this list of conditions and the following disclaimer.
10
+ # * Redistributions in binary form must reproduce the above
11
+ # copyright notice, this list of conditions and the following disclaimer
12
+ # in the documentation and/or other materials provided with the
13
+ # distribution.
14
+ # * Neither the name of Google Inc. nor the names of its
15
+ # contributors may be used to endorse or promote products derived from
16
+ # this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'google/gax/bundling'
31
+ require 'google/gax'
32
+ require 'google/protobuf'
33
+
34
+ describe Google::Gax do
35
+ Pool = Google::Protobuf::DescriptorPool.new
36
+ Pool.build do
37
+ add_message 'google.protobuf.Simple' do
38
+ optional :field1, :string, 1
39
+ optional :field2, :string, 2
40
+ end
41
+ add_message 'google.protobuf.Outer' do
42
+ optional :inner, :message, 1, 'google.protobuf.Simple'
43
+ optional :field1, :string, 2
44
+ end
45
+ add_message 'google.protobuf.Bundled' do
46
+ repeated :field1, :string, 1
47
+ end
48
+ end
49
+
50
+ Simple = Pool.lookup('google.protobuf.Simple').msgclass
51
+ Outer = Pool.lookup('google.protobuf.Outer').msgclass
52
+ Bundled = Pool.lookup('google.protobuf.Bundled').msgclass
53
+
54
+ def simple_builder(value, other_value: nil)
55
+ return Simple.new(field1: value) if other_value.nil?
56
+ Simple.new(field1: value, field2: other_value)
57
+ end
58
+
59
+ def outer_builder(value)
60
+ Outer.new(inner: simple_builder(value, other_value: value), field1: value)
61
+ end
62
+
63
+ def bundled_builder(value)
64
+ Bundled.new(field1: value)
65
+ end
66
+
67
+ describe 'method `compute_bundle_id`' do
68
+ context 'should work for' do
69
+ it 'single field value' do
70
+ actual =
71
+ Google::Gax.compute_bundle_id(
72
+ simple_builder('dummy_value'), ['field1']
73
+ )
74
+ expect(actual).to eq(['dummy_value'])
75
+ end
76
+
77
+ it 'composite value with nil' do
78
+ actual =
79
+ Google::Gax.compute_bundle_id(
80
+ simple_builder('dummy_value'), %w(field1 field2)
81
+ )
82
+ expect(actual).to eq(['dummy_value', ''])
83
+ end
84
+
85
+ it 'composite value' do
86
+ actual =
87
+ Google::Gax.compute_bundle_id(
88
+ simple_builder('dummy_value', other_value: 'other_value'),
89
+ %w(field1 field2)
90
+ )
91
+ expect(actual).to eq(%w(dummy_value other_value))
92
+ end
93
+
94
+ it 'simple dotted value' do
95
+ actual =
96
+ Google::Gax.compute_bundle_id(
97
+ outer_builder('dummy_value'),
98
+ %w(inner.field1)
99
+ )
100
+ expect(actual).to eq(['dummy_value'])
101
+ end
102
+
103
+ it 'complex case' do
104
+ actual =
105
+ Google::Gax.compute_bundle_id(
106
+ outer_builder('dummy_value'),
107
+ %w(inner.field1 inner.field2 field1)
108
+ )
109
+ expect(actual).to eq(%w(dummy_value dummy_value dummy_value))
110
+ end
111
+
112
+ it 'should return false for a single missing field value' do
113
+ expect(
114
+ Google::Gax.compute_bundle_id(
115
+ simple_builder('dummy_value'),
116
+ ['field3']
117
+ )
118
+ ).to eq([nil])
119
+ end
120
+
121
+ it 'should return false for a composite value' do
122
+ expect(
123
+ Google::Gax.compute_bundle_id(
124
+ simple_builder('dummy_value'),
125
+ %w(field1 field3)
126
+ )
127
+ ).to eq(['dummy_value', nil])
128
+ end
129
+
130
+ it 'should return false for a simple dotted value' do
131
+ expect(
132
+ Google::Gax.compute_bundle_id(
133
+ outer_builder('dotted this'),
134
+ ['inner.field3']
135
+ )
136
+ ).to eq([nil])
137
+ end
138
+ end
139
+ end
140
+
141
+ # A dummy api call that simply returns the request.
142
+ def return_request
143
+ proc do |req|
144
+ req
145
+ end
146
+ end
147
+
148
+ def create_a_test_task(api_call: return_request)
149
+ Google::Gax::Task.new(
150
+ api_call,
151
+ 'an_id',
152
+ 'field1',
153
+ bundled_builder([]),
154
+ {}
155
+ )
156
+ end
157
+
158
+ # A dummy api call that raises an exception
159
+ def raise_exc
160
+ proc do |_|
161
+ raise Google::Gax::GaxError, 'Raised in a test'
162
+ end
163
+ end
164
+
165
+ describe Google::Gax::Task do
166
+ test_message = 'a simple msg'.freeze
167
+ context 'increase in element count' do
168
+ it 'no messages added' do
169
+ test_task = create_a_test_task
170
+ actual = test_task.element_count
171
+ expect(actual).to eq(0)
172
+ end
173
+
174
+ it '1 message added' do
175
+ test_task = create_a_test_task
176
+ test_task.extend([test_message])
177
+ actual = test_task.element_count
178
+ expect(actual).to eq(1)
179
+ end
180
+
181
+ it '5 messages added' do
182
+ test_task = create_a_test_task
183
+ test_task.extend([test_message] * 5)
184
+ actual = test_task.element_count
185
+ expect(actual).to eq(5)
186
+ end
187
+ end
188
+
189
+ context 'increase in request byte count' do
190
+ it 'no messages added' do
191
+ test_task = create_a_test_task
192
+ actual = test_task.request_bytesize
193
+ expect(actual).to eq(0)
194
+ end
195
+
196
+ it '1 message added' do
197
+ test_task = create_a_test_task
198
+ test_task.extend([test_message])
199
+ actual = test_task.request_bytesize
200
+ expect(actual).to eq(test_message.bytesize)
201
+ end
202
+
203
+ it '5 messages added' do
204
+ test_task = create_a_test_task
205
+ test_task.extend([test_message] * 5)
206
+ actual = test_task.request_bytesize
207
+ expect(actual).to eq(5 * test_message.bytesize)
208
+ end
209
+ end
210
+
211
+ context 'sends bundle elements' do
212
+ it 'no messages added' do
213
+ test_task = create_a_test_task
214
+ expect(test_task.element_count).to eq(0)
215
+ test_task.run
216
+ expect(test_task.element_count).to eq(0)
217
+ expect(test_task.request_bytesize).to eq(0)
218
+ end
219
+
220
+ it '1 message added' do
221
+ test_task = create_a_test_task
222
+ event = test_task.extend([test_message])
223
+ expect(test_task.element_count).to eq(1)
224
+ test_task.run
225
+ expect(test_task.element_count).to eq(0)
226
+ expect(test_task.request_bytesize).to eq(0)
227
+ expect(event).to_not be_nil
228
+ expect(event.result).to eq(bundled_builder([test_message]))
229
+ end
230
+
231
+ it '5 messages added' do
232
+ test_task = create_a_test_task
233
+ event = test_task.extend([test_message] * 5)
234
+ test_task.run
235
+ expect(test_task.element_count).to eq(0)
236
+ expect(test_task.request_bytesize).to eq(0)
237
+ expect(event).to_not be_nil
238
+ expect(event.result).to eq(bundled_builder([test_message] * 5))
239
+ end
240
+ end
241
+
242
+ context 'api call execution fails' do
243
+ it 'adds an error if execution fails' do
244
+ test_task = create_a_test_task(api_call: raise_exc)
245
+ event = test_task.extend([test_message])
246
+ expect(test_task.element_count).to eq(1)
247
+ test_task.run
248
+ expect(test_task.element_count).to eq(0)
249
+ expect(test_task.request_bytesize).to eq(0)
250
+ expect(event.result).to be_a(Google::Gax::GaxError)
251
+ end
252
+ end
253
+
254
+ context 'calling the canceller' do
255
+ it 'stops the element from getting sent' do
256
+ another_message = 'another msg'
257
+ test_task = create_a_test_task
258
+ an_event = test_task.extend([test_message])
259
+ another_event = test_task.extend([another_message])
260
+ expect(test_task.element_count).to eq(2)
261
+ expect(an_event.cancel).to eq(true)
262
+ expect(test_task.element_count).to eq(1)
263
+ expect(an_event.cancel).to eq(false)
264
+ expect(test_task.element_count).to eq(1)
265
+ test_task.run
266
+ expect(test_task.element_count).to eq(0)
267
+ expect(another_event.result).to eq(bundled_builder([another_message]))
268
+ expect(an_event.set?).to eq(false)
269
+ expect(an_event.result).to be_nil
270
+ end
271
+ end
272
+ end
273
+
274
+ describe Google::Gax::Executor do
275
+ SIMPLE_DESCRIPTOR = Google::Gax::BundleDescriptor.new('field1', [])
276
+ DEMUX_DESCRIPTOR =
277
+ Google::Gax::BundleDescriptor.new(
278
+ 'field1', [], subresponse_field: 'field1'
279
+ )
280
+ bundler = nil
281
+
282
+ after(:each) do
283
+ bundler.close if bundler.instance_of? Google::Gax::Executor
284
+ end
285
+
286
+ context 'grouped by bundle id' do
287
+ it 'should group the api_calls by bundle_id' do
288
+ an_elt = 'dummy_message'
289
+ api_call = return_request
290
+ bundle_ids = %w(id1 id2)
291
+ threshold = 5
292
+ options =
293
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
294
+ bundler = Google::Gax::Executor.new(options)
295
+ bundle_ids.each do |id|
296
+ (threshold - 1).times do
297
+ event = bundler.schedule(
298
+ api_call,
299
+ id,
300
+ SIMPLE_DESCRIPTOR,
301
+ bundled_builder([an_elt])
302
+ )
303
+ expect(event.canceller).to_not be_nil
304
+ expect(event.set?).to eq(false)
305
+ expect(event.result).to be_nil
306
+ end
307
+ end
308
+
309
+ bundle_ids.each do |id|
310
+ event = bundler.schedule(
311
+ api_call,
312
+ id,
313
+ SIMPLE_DESCRIPTOR,
314
+ bundled_builder([an_elt])
315
+ )
316
+ expect(event.canceller).to_not be_nil
317
+ expect(event.set?).to eq(true)
318
+ expect(event.result).to eq(
319
+ bundled_builder([an_elt] * threshold)
320
+ )
321
+ end
322
+ end
323
+ end
324
+
325
+ context 'bundling with subresponses' do
326
+ it 'are demuxed correctly' do
327
+ an_elt = 'dummy_message'
328
+ api_call = return_request
329
+ bundle_id = 'an_id'
330
+ threshold = 5
331
+ options =
332
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
333
+ bundler = Google::Gax::Executor.new(options)
334
+ events = []
335
+
336
+ # send 3 groups of elements of different sizes in the bundle
337
+ 1.upto(3) do |i|
338
+ event = bundler.schedule(
339
+ api_call,
340
+ bundle_id,
341
+ DEMUX_DESCRIPTOR,
342
+ bundled_builder(["#{an_elt}#{i}"] * i)
343
+ )
344
+ events.push(event)
345
+ end
346
+ previous_event = nil
347
+ events.each_with_index do |current_event, i|
348
+ index = i + 1
349
+ expect(current_event).to_not eq(previous_event)
350
+ expect(current_event.set?).to eq(true)
351
+ expect(current_event.result).to eq(
352
+ bundled_builder(["#{an_elt}#{index}"] * index)
353
+ )
354
+ previous_event = current_event
355
+ end
356
+ end
357
+
358
+ it 'each have an exception when demuxed call fails' do
359
+ an_elt = 'dummy_message'
360
+ api_call = raise_exc
361
+ bundle_id = 'an_id'
362
+ threshold = 5
363
+ options =
364
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
365
+ bundler = Google::Gax::Executor.new(options)
366
+ events = []
367
+
368
+ 0.upto(threshold - 2) do |i|
369
+ event = bundler.schedule(
370
+ api_call,
371
+ bundle_id,
372
+ DEMUX_DESCRIPTOR,
373
+ bundled_builder(["#{an_elt}#{i}"])
374
+ )
375
+ expect(event.set?).to be(false)
376
+ expect(event.result).to be_nil
377
+ events.push(event)
378
+ end
379
+ last_event = bundler.schedule(
380
+ api_call,
381
+ bundle_id,
382
+ DEMUX_DESCRIPTOR,
383
+ bundled_builder(["#{an_elt}#{threshold - 1}"])
384
+ )
385
+ events.push(last_event)
386
+
387
+ previous_event = nil
388
+ events.each do |event|
389
+ expect(event).to_not eq(previous_event)
390
+ expect(event.set?).to eq(true)
391
+ expect(event.result).to be_a(Google::Gax::GaxError)
392
+ previous_event = event
393
+ end
394
+ end
395
+
396
+ it 'each event has same result from mismatched demuxed api call' do
397
+ an_elt = 'dummy_message'
398
+ mismatched_result = bundled_builder([an_elt, an_elt])
399
+ bundle_id = 'an_id'
400
+ threshold = 5
401
+ options =
402
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
403
+ bundler = Google::Gax::Executor.new(options)
404
+ events = []
405
+ # send 3 groups of elements of different sizes in the bundle
406
+ 1.upto(3) do |i|
407
+ event = bundler.schedule(
408
+ proc { |_| mismatched_result },
409
+ bundle_id,
410
+ DEMUX_DESCRIPTOR,
411
+ bundled_builder(["#{an_elt}#{i}"] * i)
412
+ )
413
+ events.push(event)
414
+ end
415
+ previous_event = nil
416
+ events.each do |event|
417
+ expect(event).to_not eq(previous_event)
418
+ expect(event.set?).to eq(true)
419
+ expect(event.result).to eq(
420
+ mismatched_result
421
+ )
422
+ previous_event = event
423
+ end
424
+ end
425
+ end
426
+
427
+ context 'bundles are triggered to run correctly' do
428
+ it 'api call not invoked until element threshold' do
429
+ an_elt = 'dummy_msg'
430
+ an_id = 'bundle_id'
431
+ api_call = return_request
432
+ threshold = 3
433
+ options =
434
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
435
+ bundler = Google::Gax::Executor.new(options)
436
+ 1.upto(3) do |i|
437
+ event = bundler.schedule(
438
+ api_call,
439
+ an_id,
440
+ SIMPLE_DESCRIPTOR,
441
+ bundled_builder([an_elt])
442
+ )
443
+
444
+ expect(event.canceller).to_not be_nil
445
+ if i < threshold
446
+ expect(event.set?).to eq(false)
447
+ expect(event.result).to be_nil
448
+ else
449
+ expect(event.set?).to eq(true)
450
+ expect(event.result).to eq(
451
+ bundled_builder([an_elt] * threshold)
452
+ )
453
+ end
454
+ end
455
+ end
456
+
457
+ it 'api call not invoked until byte threshold' do
458
+ an_elt = 'dummy_msg'
459
+ an_id = 'bundle_id'
460
+ api_call = return_request
461
+ elts_for_threshold = 3
462
+ threshold = elts_for_threshold * an_elt.bytesize
463
+ options =
464
+ Google::Gax::BundleOptions.new(request_byte_threshold: threshold)
465
+ bundler = Google::Gax::Executor.new(options)
466
+ 1.upto(elts_for_threshold) do |i|
467
+ event = bundler.schedule(
468
+ api_call,
469
+ an_id,
470
+ SIMPLE_DESCRIPTOR,
471
+ bundled_builder([an_elt])
472
+ )
473
+
474
+ expect(event.canceller).to_not be_nil
475
+ if i < elts_for_threshold
476
+ expect(event.set?).to eq(false)
477
+ expect(event.result).to be_nil
478
+ else
479
+ expect(event.set?).to eq(true)
480
+ expect(event.result).to eq(
481
+ bundled_builder([an_elt] * elts_for_threshold)
482
+ )
483
+ end
484
+ end
485
+ end
486
+
487
+ class MockTimer
488
+ attr_accessor :asleep
489
+
490
+ def initialize
491
+ @asleep = true
492
+ end
493
+
494
+ def run_after(_)
495
+ loop do
496
+ break unless @asleep
497
+ sleep(1 / Google::Gax::MILLIS_PER_SECOND)
498
+ end
499
+ yield
500
+ end
501
+ end
502
+
503
+ it 'api call not invoked until time threshold' do
504
+ test_timer = MockTimer.new
505
+ an_elt = 'dummy_msg'
506
+ an_id = 'bundle_id'
507
+ api_call = return_request
508
+ delay = 10_000
509
+ options =
510
+ Google::Gax::BundleOptions.new(delay_threshold_millis: delay)
511
+ bundler = Google::Gax::Executor.new(options, timer: test_timer)
512
+ event = bundler.schedule(
513
+ api_call,
514
+ an_id,
515
+ SIMPLE_DESCRIPTOR,
516
+ bundled_builder([an_elt])
517
+ )
518
+ expect(event.canceller).to_not be_nil
519
+ expect(event.set?).to eq(false)
520
+
521
+ test_timer.asleep = false
522
+
523
+ # Wait until the event is set because the timer flag will need time
524
+ # to propogate through to the thread.
525
+ event.wait
526
+ expect(event.result).to eq(bundled_builder([an_elt]))
527
+ end
528
+ end
529
+
530
+ context 'method `close` works' do
531
+ it 'non timed bundles are sent when the executor is closed' do
532
+ an_elt = 'dummy_message'
533
+ api_call = return_request
534
+ bundle_id = 'an_id'
535
+ threshold = 10 # arbitrary, greater than the number of elts sent
536
+ options =
537
+ Google::Gax::BundleOptions.new(element_count_threshold: threshold)
538
+ bundler = Google::Gax::Executor.new(options)
539
+ events = []
540
+
541
+ # send 3 groups of elements of different sizes in the bundle
542
+ 1.upto(3) do |i|
543
+ event = bundler.schedule(
544
+ api_call,
545
+ bundle_id,
546
+ DEMUX_DESCRIPTOR,
547
+ bundled_builder(["#{an_elt}#{i}"] * i)
548
+ )
549
+ events.push(event)
550
+ end
551
+
552
+ bundler.close
553
+
554
+ previous_event = nil
555
+ events.each_with_index do |current_event, i|
556
+ index = i + 1
557
+ expect(current_event).to_not eq(previous_event)
558
+ expect(current_event.set?).to eq(true)
559
+ expect(current_event.result).to eq(
560
+ bundled_builder(["#{an_elt}#{index}"] * index)
561
+ )
562
+ previous_event = current_event
563
+ end
564
+ end
565
+
566
+ it 'timed bundles are sent when the executor is closed' do
567
+ an_elt = 'dummy_msg'
568
+ an_id = 'bundle_id'
569
+ api_call = return_request
570
+ delay_threshold_millis = 100_000 # arbitrary, very high.
571
+ options =
572
+ Google::Gax::BundleOptions.new(
573
+ delay_threshold_millis: delay_threshold_millis
574
+ )
575
+ bundler = Google::Gax::Executor.new(options)
576
+ event = bundler.schedule(
577
+ api_call,
578
+ an_id,
579
+ SIMPLE_DESCRIPTOR,
580
+ bundled_builder([an_elt])
581
+ )
582
+ expect(event.canceller).to_not be_nil
583
+ expect(event.set?).to eq(false)
584
+
585
+ bundler.close
586
+ expect(event.canceller).to_not be_nil
587
+ expect(event.set?).to eq(true)
588
+ expect(event.result).to eq(bundled_builder([an_elt]))
589
+ end
590
+ end
591
+ end
592
+ describe Google::Gax::Event do
593
+ context 'event is working correctly' do
594
+ it 'can be set' do
595
+ event = Google::Gax::Event.new
596
+ expect(event.set?).to eq(false)
597
+ event.result = Object.new
598
+ expect(event.set?).to eq(true)
599
+ end
600
+
601
+ it 'returns false when #cancel is called with no canceller' do
602
+ event = Google::Gax::Event.new
603
+ expect(event.canceller).to be_nil
604
+ expect(event.cancel).to eq(false)
605
+ end
606
+
607
+ it 'returns cancellers result when # cancel is called' do
608
+ event = Google::Gax::Event.new
609
+ event.canceller = proc { true }
610
+ expect(event.cancel).to eq(true)
611
+ event.canceller = proc { false }
612
+ expect(event.cancel).to eq(false)
613
+ end
614
+
615
+ it 'wait does not block if event is set' do
616
+ event = Google::Gax::Event.new
617
+ event.result = Object.new
618
+ expect(event.wait).to eq(true)
619
+ end
620
+
621
+ it 'waits returns false after the timeout period' do
622
+ event = Google::Gax::Event.new
623
+ expect(event.wait(timeout_millis: 1)).to eq(false)
624
+ end
625
+ end
626
+ end
627
+ end