Skip to content

Commit 39ddb8e

Browse files
committed
test(retry): make timing tests deterministic under load
why: The five wall-clock assertions (`0.9 <= elapsed <= 1.1`) flaked under load when run with --reruns 0 — nominal elapsed (~1.0s) sat ~100ms under the ceiling. The two "succeeds after 3 calls" tests also raced their 1s budget: under load only two of three calls fit, spuriously timing out. what: - Success cases: assert on behavior (call count + result) with a generous budget so all calls fit regardless of load; drop the wall-clock assertion - Timeout cases: keep the raises/returns assertion and the deterministic lower bound (retry_until only times out once elapsed >= budget); drop the fragile upper bound
1 parent 8b662aa commit 39ddb8e

1 file changed

Lines changed: 41 additions & 38 deletions

File tree

tests/test/test_retry.py

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -11,95 +11,98 @@
1111

1212

1313
def test_retry_three_times() -> None:
14-
"""Test retry_until()."""
15-
ini = time()
14+
"""retry_until retries until the callable succeeds."""
15+
calls = 0
1616
value = 0
1717

1818
def call_me_three_times() -> bool:
19-
nonlocal value
20-
sleep(0.3) # Sleep for 0.3 seconds to simulate work
19+
nonlocal value, calls
20+
calls += 1
21+
sleep(0.3) # simulate work
2122

2223
if value == 2:
2324
return True
2425

2526
value += 1
2627
return False
2728

28-
retry_until(call_me_three_times, 1)
29-
30-
end = time()
31-
32-
assert 0.9 <= (end - ini) <= 1.1 # Allow for small timing variations
29+
# Generous budget so all three calls fit even under load; assert on behavior
30+
# (call count + success), not wall-clock, to stay deterministic.
31+
assert retry_until(call_me_three_times, 5) is True
32+
assert calls == 3
3333

3434

3535
def test_function_times_out() -> None:
36-
"""Test time outs with retry_until()."""
36+
"""retry_until raises WaitTimeout after exhausting its budget."""
3737
ini = time()
38+
calls = 0
3839

3940
def never_true() -> bool:
40-
sleep(
41-
0.1,
42-
) # Sleep for 0.1 seconds to simulate work (called ~10 times in 1 second)
41+
nonlocal calls
42+
calls += 1
43+
sleep(0.1) # simulate work
4344
return False
4445

4546
with pytest.raises(exc.WaitTimeout):
4647
retry_until(never_true, 1)
4748

48-
end = time()
49-
50-
assert 0.9 <= (end - ini) <= 1.1 # Allow for small timing variations
49+
# It retried for the full budget before timing out. The lower bound is
50+
# deterministic (retry_until only times out once elapsed >= the budget);
51+
# no fragile upper bound that load can blow past.
52+
assert (time() - ini) >= 0.9
53+
assert calls > 1
5154

5255

5356
def test_function_times_out_no_raise() -> None:
54-
"""Tests retry_until() with exception raising disabled."""
57+
"""retry_until returns instead of raising when raises=False."""
5558
ini = time()
59+
calls = 0
5660

5761
def never_true() -> bool:
58-
sleep(
59-
0.1,
60-
) # Sleep for 0.1 seconds to simulate work (called ~10 times in 1 second)
62+
nonlocal calls
63+
calls += 1
64+
sleep(0.1) # simulate work
6165
return False
6266

6367
retry_until(never_true, 1, raises=False)
6468

65-
end = time()
66-
assert 0.9 <= (end - ini) <= 1.1 # Allow for small timing variations
69+
assert (time() - ini) >= 0.9
70+
assert calls > 1
6771

6872

6973
def test_function_times_out_no_raise_assert() -> None:
70-
"""Tests retry_until() with exception raising disabled, returning False."""
74+
"""retry_until returns False on timeout when raises=False."""
7175
ini = time()
76+
calls = 0
7277

7378
def never_true() -> bool:
74-
sleep(
75-
0.1,
76-
) # Sleep for 0.1 seconds to simulate work (called ~10 times in 1 second)
79+
nonlocal calls
80+
calls += 1
81+
sleep(0.1) # simulate work
7782
return False
7883

7984
assert not retry_until(never_true, 1, raises=False)
8085

81-
end = time()
82-
assert 0.9 <= (end - ini) <= 1.1 # Allow for small timing variations
86+
assert (time() - ini) >= 0.9
87+
assert calls > 1
8388

8489

8590
def test_retry_three_times_no_raise_assert() -> None:
86-
"""Tests retry_until() with exception raising disabled, with closure variable."""
87-
ini = time()
91+
"""retry_until returns True on success even with raises=False."""
92+
calls = 0
8893
value = 0
8994

9095
def call_me_three_times() -> bool:
91-
nonlocal value
92-
sleep(
93-
0.3,
94-
) # Sleep for 0.3 seconds to simulate work (called 3 times in ~0.9 seconds)
96+
nonlocal value, calls
97+
calls += 1
98+
sleep(0.3) # simulate work
9599

96100
if value == 2:
97101
return True
98102

99103
value += 1
100104
return False
101105

102-
assert retry_until(call_me_three_times, 1, raises=False)
103-
104-
end = time()
105-
assert 0.9 <= (end - ini) <= 1.1 # Allow for small timing variations
106+
# Behavior-based, generous budget: deterministic even under load.
107+
assert retry_until(call_me_three_times, 5, raises=False) is True
108+
assert calls == 3

0 commit comments

Comments
 (0)