Skip to content

Commit fb593b1

Browse files
committed
CI: make mac_sampler timing tests poll instead of fixed-sleep
test_mac_sampler.py started sampler threads, slept a fixed time, then asserted the handler/queue-poll count >= 2. On a loaded CI runner the sleep can elapse before the background thread fires enough times, producing flaky failures (seen: 'assert 1 >= 2' for test_sampler_polls_memory_queues_when_provided on macos-latest/3.10). Same class of wall-clock flake as the parity and HyperLogLog ones addressed in #1069, but not previously covered. Add a _wait_until(predicate, timeout=5s) poll helper and use it in the three timing-sensitive tests (invokes_handler_repeatedly, handler_exception_does_ not_kill_thread, polls_memory_queues_when_provided) so a slow runner just takes slightly longer instead of failing. Assertions are unchanged and still meaningful. No production code changes.
1 parent 80aafb7 commit fb593b1

1 file changed

Lines changed: 32 additions & 5 deletions

File tree

tests/test_mac_sampler.py

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,24 @@
1414
from scalene.scalene_mac_sampler import MacThreadSampler
1515

1616

17+
def _wait_until(predicate, timeout=5.0, interval=0.01):
18+
"""Poll ``predicate`` until true or ``timeout`` elapses.
19+
20+
The sampler is a background thread driven by wall-clock sleeps; on a loaded
21+
CI runner a fixed `time.sleep` can elapse before the thread has fired the
22+
expected number of times, producing flaky `assert count >= N` failures
23+
(e.g. `assert 1 >= 2` on macOS CI). Polling until the threshold is reached,
24+
with a generous timeout, keeps the assertion meaningful without being
25+
timing-fragile.
26+
"""
27+
deadline = time.time() + timeout
28+
while time.time() < deadline:
29+
if predicate():
30+
return True
31+
time.sleep(interval)
32+
return predicate()
33+
34+
1735
def test_sampler_invokes_handler_repeatedly():
1836
"""The sampler thread should call the CPU handler more than once."""
1937
calls = []
@@ -26,13 +44,14 @@ def handler(signum, frame):
2644
sampler = MacThreadSampler()
2745
sampler.start(handler, cpu_signal=42, cpu_sampling_rate=0.005)
2846
assert sampler.is_running
29-
time.sleep(0.2)
47+
_wait_until(lambda: len(calls) >= 2)
3048
sampler.stop()
3149
assert not sampler.is_running
3250

3351
with lock:
34-
# At ~200Hz over 200ms we expect many; assert a conservative lower
35-
# bound to avoid flakiness on a loaded CI machine.
52+
# At ~200Hz we expect many; assert a conservative lower bound. We poll
53+
# (above) rather than sleep a fixed time to avoid flakiness on a loaded
54+
# CI machine.
3655
assert len(calls) >= 2
3756
# Handler is always called with frame=None (Windows-style) and the
3857
# signal number we passed.
@@ -49,7 +68,7 @@ def bad_handler(signum, frame):
4968

5069
sampler = MacThreadSampler()
5170
sampler.start(bad_handler, cpu_signal=1, cpu_sampling_rate=0.005)
52-
time.sleep(0.15)
71+
_wait_until(lambda: count["n"] >= 2)
5372
still_running = sampler.is_running
5473
sampler.stop()
5574

@@ -96,7 +115,15 @@ def test_sampler_polls_memory_queues_when_provided():
96115
alloc_sigq=alloc,
97116
memcpy_sigq=memcpy,
98117
)
99-
time.sleep(0.15)
118+
119+
def _both_polled():
120+
with alloc.lock:
121+
a = len(alloc.items)
122+
with memcpy.lock:
123+
m = len(memcpy.items)
124+
return a >= 2 and m >= 2
125+
126+
_wait_until(_both_polled)
100127
sampler.stop()
101128

102129
with alloc.lock:

0 commit comments

Comments
 (0)