@@ -182,35 +182,94 @@ def test_constructor_options(self):
182182 assert retry_ ._on_error is _some_function
183183
184184 def test_with_deadline (self ):
185- retry_ = retry .Retry ()
185+ retry_ = retry .Retry (
186+ predicate = mock .sentinel .predicate ,
187+ initial = 1 ,
188+ maximum = 2 ,
189+ multiplier = 3 ,
190+ deadline = 4 ,
191+ on_error = mock .sentinel .on_error ,
192+ )
186193 new_retry = retry_ .with_deadline (42 )
187194 assert retry_ is not new_retry
188195 assert new_retry ._deadline == 42
189196
197+ # the rest of the attributes should remain the same
198+ assert new_retry ._predicate is retry_ ._predicate
199+ assert new_retry ._initial == retry_ ._initial
200+ assert new_retry ._maximum == retry_ ._maximum
201+ assert new_retry ._multiplier == retry_ ._multiplier
202+ assert new_retry ._on_error is retry_ ._on_error
203+
190204 def test_with_predicate (self ):
191- retry_ = retry .Retry ()
205+ retry_ = retry .Retry (
206+ predicate = mock .sentinel .predicate ,
207+ initial = 1 ,
208+ maximum = 2 ,
209+ multiplier = 3 ,
210+ deadline = 4 ,
211+ on_error = mock .sentinel .on_error ,
212+ )
192213 new_retry = retry_ .with_predicate (mock .sentinel .predicate )
193214 assert retry_ is not new_retry
194215 assert new_retry ._predicate == mock .sentinel .predicate
195216
217+ # the rest of the attributes should remain the same
218+ assert new_retry ._deadline == retry_ ._deadline
219+ assert new_retry ._initial == retry_ ._initial
220+ assert new_retry ._maximum == retry_ ._maximum
221+ assert new_retry ._multiplier == retry_ ._multiplier
222+ assert new_retry ._on_error is retry_ ._on_error
223+
196224 def test_with_delay_noop (self ):
197- retry_ = retry .Retry ()
225+ retry_ = retry .Retry (
226+ predicate = mock .sentinel .predicate ,
227+ initial = 1 ,
228+ maximum = 2 ,
229+ multiplier = 3 ,
230+ deadline = 4 ,
231+ on_error = mock .sentinel .on_error ,
232+ )
198233 new_retry = retry_ .with_delay ()
199234 assert retry_ is not new_retry
200235 assert new_retry ._initial == retry_ ._initial
201236 assert new_retry ._maximum == retry_ ._maximum
202237 assert new_retry ._multiplier == retry_ ._multiplier
203238
204239 def test_with_delay (self ):
205- retry_ = retry .Retry ()
240+ retry_ = retry .Retry (
241+ predicate = mock .sentinel .predicate ,
242+ initial = 1 ,
243+ maximum = 2 ,
244+ multiplier = 3 ,
245+ deadline = 4 ,
246+ on_error = mock .sentinel .on_error ,
247+ )
206248 new_retry = retry_ .with_delay (initial = 1 , maximum = 2 , multiplier = 3 )
207249 assert retry_ is not new_retry
208250 assert new_retry ._initial == 1
209251 assert new_retry ._maximum == 2
210252 assert new_retry ._multiplier == 3
211253
254+ # the rest of the attributes should remain the same
255+ assert new_retry ._deadline == retry_ ._deadline
256+ assert new_retry ._predicate is retry_ ._predicate
257+ assert new_retry ._on_error is retry_ ._on_error
258+
212259 def test___str__ (self ):
213- retry_ = retry .Retry ()
260+ def if_exception_type (exc ):
261+ return bool (exc ) # pragma: NO COVER
262+
263+ # Explicitly set all attributes as changed Retry defaults should not
264+ # cause this test to start failing.
265+ retry_ = retry .Retry (
266+ predicate = if_exception_type ,
267+ initial = 1.0 ,
268+ maximum = 60.0 ,
269+ multiplier = 2.0 ,
270+ deadline = 120.0 ,
271+ on_error = None ,
272+ )
214273 assert re .match (
215274 (
216275 r"<Retry predicate=<function.*?if_exception_type.*?>, "
@@ -259,6 +318,54 @@ def test___call___and_execute_retry(self, sleep, uniform):
259318 sleep .assert_called_once_with (retry_ ._initial )
260319 assert on_error .call_count == 1
261320
321+ # Make uniform return half of its maximum, which is the calculated sleep time.
322+ @mock .patch ("random.uniform" , autospec = True , side_effect = lambda m , n : n / 2.0 )
323+ @mock .patch ("time.sleep" , autospec = True )
324+ def test___call___and_execute_retry_hitting_deadline (self , sleep , uniform ):
325+
326+ on_error = mock .Mock (spec = ["__call__" ], side_effect = [None ] * 10 )
327+ retry_ = retry .Retry (
328+ predicate = retry .if_exception_type (ValueError ),
329+ initial = 1.0 ,
330+ maximum = 1024.0 ,
331+ multiplier = 2.0 ,
332+ deadline = 9.9 ,
333+ )
334+
335+ utcnow = datetime .datetime .utcnow ()
336+ utcnow_patcher = mock .patch (
337+ "google.api_core.datetime_helpers.utcnow" , return_value = utcnow
338+ )
339+
340+ target = mock .Mock (spec = ["__call__" ], side_effect = [ValueError ()] * 10 )
341+ # __name__ is needed by functools.partial.
342+ target .__name__ = "target"
343+
344+ decorated = retry_ (target , on_error = on_error )
345+ target .assert_not_called ()
346+
347+ with utcnow_patcher as patched_utcnow :
348+ # Make sure that calls to fake time.sleep() also advance the mocked
349+ # time clock.
350+ def increase_time (sleep_delay ):
351+ patched_utcnow .return_value += datetime .timedelta (seconds = sleep_delay )
352+ sleep .side_effect = increase_time
353+
354+ with pytest .raises (exceptions .RetryError ):
355+ decorated ("meep" )
356+
357+ assert target .call_count == 5
358+ target .assert_has_calls ([mock .call ("meep" )] * 5 )
359+ assert on_error .call_count == 5
360+
361+ # check the delays
362+ assert sleep .call_count == 4 # once between each successive target calls
363+ last_wait = sleep .call_args .args [0 ]
364+ total_wait = sum (call_args .args [0 ] for call_args in sleep .call_args_list )
365+
366+ assert last_wait == 2.9 # and not 8.0, because the last delay was shortened
367+ assert total_wait == 9.9 # the same as the deadline
368+
262369 @mock .patch ("time.sleep" , autospec = True )
263370 def test___init___without_retry_executed (self , sleep ):
264371 _some_function = mock .Mock ()
0 commit comments