From 954369add94c663430294ffbae01ec0ac6b7fddf Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Fri, 23 Oct 2026 01:20:10 -0700 Subject: [PATCH] test(browser): keep the slow-reset summary on a coarse clock On Windows the report timer can fire a few milliseconds short of the threshold, and the summary was then dropped. A reset that was reported now always gets its summary. The server test uses the plain reset, which the Windows jobs can seed, and puts the server stderr in its failure messages. --- tests/browser/slow_reset_watch.py | 6 +++++- tests/test_browser_slow_reset_watch.py | 11 +++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/tests/browser/slow_reset_watch.py b/tests/browser/slow_reset_watch.py index a3f6a6c8..e3b453b6 100644 --- a/tests/browser/slow_reset_watch.py +++ b/tests/browser/slow_reset_watch.py @@ -61,6 +61,7 @@ class ResetWatch: self._start = time.monotonic() self._calls: list[str] = [] self._calls_lock = threading.Lock() + self._stalled = threading.Event() self._timer = threading.Timer(threshold, self._report_stall) self._timer.daemon = True self._timer.name = "pm-slow-reset-watch" @@ -81,13 +82,16 @@ class ResetWatch: def finish(self, outcome: str) -> None: self._timer.cancel() elapsed = self.elapsed() - if elapsed < self.threshold: + # A coarse clock (Windows) can fire the timer just short of the + # threshold; a reset that was reported always gets its summary. + if elapsed < self.threshold and not self._stalled.is_set(): return with self._calls_lock: calls = list(self._calls) self._write([f"{self.label} {outcome} after {elapsed:.3f}s", *calls]) def _report_stall(self) -> None: + self._stalled.set() lines = [f"{self.label} still running after {self.elapsed():.3f}s"] try: lines.extend(self._describe()) diff --git a/tests/test_browser_slow_reset_watch.py b/tests/test_browser_slow_reset_watch.py index bb24c53b..631e7ca4 100644 --- a/tests/test_browser_slow_reset_watch.py +++ b/tests/test_browser_slow_reset_watch.py @@ -153,7 +153,7 @@ class TestSlowResetOnTheServer(unittest.TestCase): def reset(self) -> tuple[int, float]: request = urllib.request.Request( self.base + "/__test__/reset", - data=b'{"chronological_history": true}', + data=b"{}", headers={"Content-Type": "application/json"}, method="POST", ) @@ -168,7 +168,7 @@ class TestSlowResetOnTheServer(unittest.TestCase): return self.stderr.read().decode("utf-8", "replace") def test_a_reset_held_behind_a_write_lock_names_the_wait_and_ends_with_it(self): - self.assertEqual(self.reset()[0], 200) + self.assertEqual(self.reset()[0], 200, self.server_stderr()) self.assertNotIn(REPORT_PREFIX, self.server_stderr()) held = threading.Event() @@ -201,6 +201,7 @@ class TestSlowResetOnTheServer(unittest.TestCase): time.sleep(PROBE_INTERVAL_SECONDS) resetting.join() lock.join() + self.assertEqual(len(outcome), 1, self.server_stderr()) [(status, elapsed)] = outcome self.assertLess(slowest, RESPONSIVE_SECONDS) @@ -220,10 +221,12 @@ class TestSlowResetOnTheServer(unittest.TestCase): self.assertRegex( text, r"kernel: asyncio_\d+ \(0x[0-9a-f]{16}\) state \w, waiting in " ) - self.assertIn("in seed_workspace_visual", text) + # The plain reset's seed() waits on its first DELETE. + self.assertRegex(text, r"line \d+ in seed\n") self.assertIn("POST /__test__/reset answered 200 after", text) self.assertRegex( - text, r"executor call .*reset.*: queued \d+\.\d+s, ran \d+\.\d+s" + text, + r"executor call create_app..seed: queued \d+\.\d+s, ran \d+\.\d+s", )