Skip to content

Commit e9940f3

Browse files
committed
added unit tests for dynamic backoff
1 parent eb74572 commit e9940f3

File tree

4 files changed

+115
-4
lines changed

4 files changed

+115
-4
lines changed

tests/asyncio/retry/test_retry_streaming_async.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,35 @@ async def test_retry_streaming_target_bad_sleep_generator():
3939
await retry_target_stream(None, lambda x: True, [], None).__anext__()
4040

4141

42+
@mock.patch("asyncio.sleep", autospec=True)
43+
@pytest.mark.asyncio
44+
async def test_retry_streaming_target_dynamic_backoff(sleep):
45+
"""
46+
sleep_generator should be iterated after on_error, to support dynamic backoff
47+
"""
48+
from functools import partial
49+
from google.api_core.retry.retry_streaming_async import retry_target_stream
50+
51+
sleep.side_effect = RuntimeError("stop after sleep")
52+
# start with empty sleep generator; values are added after exception in push_sleep_value
53+
sleep_values = []
54+
error_target = partial(TestAsyncStreamingRetry._generator_mock, error_on=0)
55+
inserted_sleep = 99
56+
57+
def push_sleep_value(err):
58+
sleep_values.append(inserted_sleep)
59+
60+
with pytest.raises(RuntimeError):
61+
await retry_target_stream(
62+
error_target,
63+
predicate=lambda x: True,
64+
sleep_generator=sleep_values,
65+
on_error=push_sleep_value,
66+
).__anext__()
67+
assert sleep.call_count == 1
68+
sleep.assert_called_once_with(inserted_sleep)
69+
70+
4271
class TestAsyncStreamingRetry(Test_BaseRetry):
4372
def _make_one(self, *args, **kwargs):
4473
return retry_streaming_async.AsyncStreamingRetry(*args, **kwargs)
@@ -66,8 +95,8 @@ def if_exception_type(exc):
6695
str(retry_),
6796
)
6897

98+
@staticmethod
6999
async def _generator_mock(
70-
self,
71100
num=5,
72101
error_on=None,
73102
exceptions_seen=None,
@@ -87,7 +116,7 @@ async def _generator_mock(
87116
for i in range(num):
88117
if sleep_time:
89118
await asyncio.sleep(sleep_time)
90-
if error_on and i == error_on:
119+
if error_on is not None and i == error_on:
91120
raise ValueError("generator mock error")
92121
yield i
93122
except (Exception, BaseException, GeneratorExit) as e:

tests/asyncio/retry/test_retry_unary_async.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,33 @@ async def test_retry_target_bad_sleep_generator():
139139
await retry_async.retry_target(mock.sentinel.target, lambda x: True, [], None)
140140

141141

142+
@mock.patch("asyncio.sleep", autospec=True)
143+
@pytest.mark.asyncio
144+
async def test_retry_target_dynamic_backoff(sleep):
145+
"""
146+
sleep_generator should be iterated after on_error, to support dynamic backoff
147+
"""
148+
sleep.side_effect = RuntimeError("stop after sleep")
149+
# start with empty sleep generator; values are added after exception in push_sleep_value
150+
sleep_values = []
151+
exception = ValueError("trigger retry")
152+
error_target = mock.Mock(side_effect=exception)
153+
inserted_sleep = 99
154+
155+
def push_sleep_value(err):
156+
sleep_values.append(inserted_sleep)
157+
158+
with pytest.raises(RuntimeError):
159+
await retry_async.retry_target(
160+
error_target,
161+
predicate=lambda x: True,
162+
sleep_generator=sleep_values,
163+
on_error=push_sleep_value,
164+
)
165+
assert sleep.call_count == 1
166+
sleep.assert_called_once_with(inserted_sleep)
167+
168+
142169
class TestAsyncRetry(Test_BaseRetry):
143170
def _make_one(self, *args, **kwargs):
144171
return retry_async.AsyncRetry(*args, **kwargs)

tests/unit/retry/test_retry_streaming.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,35 @@ def test_retry_streaming_target_bad_sleep_generator():
3636
next(retry_streaming.retry_target_stream(None, lambda x: True, [], None))
3737

3838

39+
@mock.patch("time.sleep", autospec=True)
40+
def test_retry_streaming_target_dynamic_backoff(sleep):
41+
"""
42+
sleep_generator should be iterated after on_error, to support dynamic backoff
43+
"""
44+
from functools import partial
45+
46+
sleep.side_effect = RuntimeError("stop after sleep")
47+
# start with empty sleep generator; values are added after exception in push_sleep_value
48+
sleep_values = []
49+
error_target = partial(TestStreamingRetry._generator_mock, error_on=0)
50+
inserted_sleep = 99
51+
52+
def push_sleep_value(err):
53+
sleep_values.append(inserted_sleep)
54+
55+
with pytest.raises(RuntimeError):
56+
next(
57+
retry_streaming.retry_target_stream(
58+
error_target,
59+
predicate=lambda x: True,
60+
sleep_generator=sleep_values,
61+
on_error=push_sleep_value,
62+
)
63+
)
64+
assert sleep.call_count == 1
65+
sleep.assert_called_once_with(inserted_sleep)
66+
67+
3968
class TestStreamingRetry(Test_BaseRetry):
4069
def _make_one(self, *args, **kwargs):
4170
return retry_streaming.StreamingRetry(*args, **kwargs)
@@ -63,8 +92,8 @@ def if_exception_type(exc):
6392
str(retry_),
6493
)
6594

95+
@staticmethod
6696
def _generator_mock(
67-
self,
6897
num=5,
6998
error_on=None,
7099
return_val=None,
@@ -82,7 +111,7 @@ def _generator_mock(
82111
"""
83112
try:
84113
for i in range(num):
85-
if error_on and i == error_on:
114+
if error_on is not None and i == error_on:
86115
raise ValueError("generator mock error")
87116
yield i
88117
return return_val

tests/unit/retry/test_retry_unary.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,32 @@ def test_retry_target_bad_sleep_generator():
149149
retry.retry_target(mock.sentinel.target, lambda x: True, [], None)
150150

151151

152+
@mock.patch("time.sleep", autospec=True)
153+
def test_retry_target_dynamic_backoff(sleep):
154+
"""
155+
sleep_generator should be iterated after on_error, to support dynamic backoff
156+
"""
157+
sleep.side_effect = RuntimeError("stop after sleep")
158+
# start with empty sleep generator; values are added after exception in push_sleep_value
159+
sleep_values = []
160+
exception = ValueError("trigger retry")
161+
error_target = mock.Mock(side_effect=exception)
162+
inserted_sleep = 99
163+
164+
def push_sleep_value(err):
165+
sleep_values.append(inserted_sleep)
166+
167+
with pytest.raises(RuntimeError):
168+
retry.retry_target(
169+
error_target,
170+
predicate=lambda x: True,
171+
sleep_generator=sleep_values,
172+
on_error=push_sleep_value,
173+
)
174+
assert sleep.call_count == 1
175+
sleep.assert_called_once_with(inserted_sleep)
176+
177+
152178
class TestRetry(Test_BaseRetry):
153179
def _make_one(self, *args, **kwargs):
154180
return retry.Retry(*args, **kwargs)

0 commit comments

Comments
 (0)