def test_constant_backoff_validation(): with pytest.raises(AssertionError): ConstantBackoff(retries='foo') with pytest.raises(AssertionError): ConstantBackoff(retries=-1) with pytest.raises(AssertionError): ConstantBackoff(interval='foo') with pytest.raises(AssertionError): ConstantBackoff(interval=-1)
def test_constant_backoff_max_retries(): backoff = ConstantBackoff(retries=5) assert backoff.retries == 5 assert backoff.interval == .1 assert backoff.pending_retries == backoff.retries for i in range(5): backoff.next() assert backoff.pending_retries == backoff.retries - (i + 1) assert backoff.next() == backoff.STOP
def test_retry_async(MagicMock): import asyncio loop = asyncio.get_event_loop() # Track coro calls count = {'calls': 0} @asyncio.coroutine def coro(times, x): count['calls'] += 1 if count['calls'] < times: raise RuntimeError('foo') return x * x on_retry = MagicMock() retrier = retry(on_retry=on_retry, backoff=ConstantBackoff(interval=.2, retries=10)) decorator = retrier(asyncio.coroutine(functools.partial(coro, 4))) result = loop.run_until_complete(decorator(2)) assert result == 4 assert on_retry.called assert on_retry.call_count == 3
def test_async_retrier_custom(): @asyncio.coroutine def sleep(): pass @asyncio.coroutine def on_retry(): pass @asyncio.coroutine def evaluator(): pass backoff = ConstantBackoff() retrier = AsyncRetrier(timeout=1, on_retry=on_retry, sleep_coro=sleep, evaluator=evaluator, backoff=backoff) assert retrier.error is None assert retrier.attempts == 0 assert retrier.timeout == 1 assert retrier.on_retry is on_retry assert retrier.evaluator is evaluator assert retrier.sleep == sleep assert retrier.backoff == backoff
def test_async_retrier_context_manager(MagicMock, coro, run_coro): on_retry = MagicMock() retrier = AsyncRetrier(timeout=.25, on_retry=on_retry, backoff=ConstantBackoff(interval=.1, retries=5)) @asyncio.coroutine def run_context(): assert (yield from retrier.__aenter__()) is retrier # noqa try: yield from retrier.run(coro(10), 2, 4, foo='bar') # noqa except: yield from retrier.__aexit__(None, None, None) # noqa else: raise AssertionError('must raise exception') run_coro(run_context()) assert on_retry.called assert on_retry.call_count == 3 assert retrier.attempts >= 1 assert retrier.attempts < 4 assert isinstance(retrier.error, RuntimeError)
def test_retry(MagicMock): on_retry = MagicMock() retrier = retry(on_retry=on_retry)(task(4)) assert retrier(2) == 4 assert on_retry.called assert on_retry.call_count == 3 on_retry = MagicMock() retrier = retry(on_retry=on_retry, backoff=ConstantBackoff())(task(4)) assert retrier(3) == 9 assert on_retry.called assert on_retry.call_count == 3 on_retry = MagicMock() retrier = retry(on_retry=on_retry, backoff=ConstantBackoff())(task(4)) assert retrier(4) == 16 assert on_retry.called assert on_retry.call_count == 3
def test_async_retrier_run_max_retries_error(MagicMock, coro): on_retry = MagicMock() task = coro(10) retrier = AsyncRetrier(on_retry=on_retry, backoff=ConstantBackoff(interval=0, retries=2)) with pytest.raises(MaxRetriesExceeded): run_coro(retrier.run(task, 2, 4, foo=6)) assert on_retry.called assert on_retry.call_count == 2 assert retrier.attempts == 2 assert isinstance(retrier.error, RuntimeError)
def test_async_retrier_run_max_timeout(MagicMock, coro): on_retry = MagicMock() task = coro(10) retrier = AsyncRetrier(timeout=.25, on_retry=on_retry, backoff=ConstantBackoff(interval=.1)) with pytest.raises(asyncio.TimeoutError): run_coro(retrier.run(task, 2, 4, foo=6)) assert on_retry.called assert on_retry.call_count == 3 assert retrier.attempts >= 1 assert retrier.attempts < 4 assert isinstance(retrier.error, RuntimeError)
def test_async_retrier_cancelled_error(MagicMock, coro): on_retry = MagicMock() @asyncio.coroutine def coro(x): if on_retry.call_count < x: raise ValueError('small number') raise asyncio.CancelledError('oops') retrier = AsyncRetrier(on_retry=on_retry, backoff=ConstantBackoff(interval=0, retries=10)) with pytest.raises(asyncio.CancelledError): run_coro(retrier.run(coro, 4)) assert on_retry.called assert on_retry.call_count == 4 assert retrier.attempts == 5 assert retrier.error is not None
def test_async_retrier_evaluator_error_default(MagicMock, coro): on_retry = MagicMock() task = coro(4) def evaluator(x): if x < 4: raise ValueError('small number') raise ImportError('pass error') retrier = AsyncRetrier(evaluator=evaluator, on_retry=on_retry, backoff=ConstantBackoff(interval=0, retries=10)) with pytest.raises(ImportError): run_coro(retrier.run(task, 2, 4, foo=6)) assert on_retry.called assert on_retry.call_count == 3 assert retrier.attempts == 3 assert isinstance(retrier.error, ImportError)
def test_async_retrier_evaluator(MagicMock, coro): on_retry = MagicMock() task = coro(4) def evaluator(x): if x < 4: raise ValueError('small number') return False retrier = AsyncRetrier(evaluator=evaluator, on_retry=on_retry, backoff=ConstantBackoff(interval=0, retries=10)) res = run_coro(retrier.run(task, 2, 4, foo=6)) assert res == 12 assert on_retry.called assert on_retry.call_count == 3 assert retrier.attempts == 3 assert retrier.error is None
def test_constant_backoff(): backoff = ConstantBackoff() assert backoff.retries == 10 assert backoff.interval == .1 delay = backoff.next() assert delay == backoff.interval assert backoff.pending_retries == backoff.retries - 1 delay = backoff.next() assert delay == backoff.interval assert backoff.pending_retries == backoff.retries - 2 backoff.reset() assert backoff.retries == 10 assert backoff.interval == .1 assert backoff.pending_retries == backoff.retries
def test_constant_backoff_defaults(): backoff = ConstantBackoff() assert backoff.retries == 10 assert backoff.interval == .1 assert backoff.pending_retries == backoff.retries
def test_constant_backoff_params(): backoff = ConstantBackoff(interval=.5, retries=5) assert backoff.retries == 5 assert backoff.interval == .5 assert backoff.pending_retries == backoff.retries