diff --git a/tests/conftest.py b/tests/conftest.py index 0141d434..3ceb878e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -124,19 +124,24 @@ def _smart_watchdog_exit() -> None: threading.Thread(target=_smart_watchdog_exit, daemon=True, name="conftest-smart-watchdog").start() def _unconditional_watchdog_exit() -> None: - """Hard fail-safe: fires regardless of pytest state after 90s total. + """Hard fail-safe: also signal-based, but with a much longer + timeout than the smart watchdog. - The smart watchdog (above) is gated on pytest_unconfigure setting - _pytest_finished_event. If something is hung BEFORE pytest - unconfigure runs (e.g., the conftest's own _warmup_app_controller - hangs in wait_for_warmup during startup, or pytest never reaches - its unconfigure phase), the smart watchdog's first wait - blocks. This unconditional watchdog is the sledgehammer: 90s - from conftest load, fire os._exit(2) regardless. + The smart watchdog (above) uses 300s. This sledgehammer waits + 900s (15 minutes) for the same signal, so a long-running test + can take up to 15 minutes before we declare it a hang. The + only case this catches that the smart doesn't: pytest finishes + but the test session is so long the smart's 300s expires first. + In that case we still want the runner to move on. + + If the signal never fires (true hang), os._exit(2) so the runner + catches it as CalledProcessError. """ + if not _pytest_finished_event.wait(timeout=900.0): + os._exit(2) import time - time.sleep(90.0) - os._exit(2) + time.sleep(5.0) + os._exit(0) threading.Thread(target=_unconditional_watchdog_exit, daemon=True, name="conftest-unconditional-watchdog").start()