diff --git a/tests/conftest.py b/tests/conftest.py index 8a5efe8d..71ea2e62 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -77,7 +77,7 @@ if not _warmup_app_controller.wait_for_warmup(timeout=60.0): def _watchdog_exit() -> None: import time time.sleep(30.0) - os._exit(0) + os._exit(2) import threading threading.Thread(target=_watchdog_exit, daemon=True, name="conftest-hang-watchdog").start() diff --git a/tests/test_conftest_watchdog.py b/tests/test_conftest_watchdog.py index c26dda4c..548b55be 100644 --- a/tests/test_conftest_watchdog.py +++ b/tests/test_conftest_watchdog.py @@ -9,12 +9,19 @@ observed: hanging on HTTP call to the hook server or on process.wait() for the sloppy.py subprocess. -The conftest installs a daemon-thread watchdog (os._exit(0) after a -timeout) to bound the hang. This test verifies the watchdog is -actually registered after the conftest loads. It does NOT spawn a -subprocess (which would itself be bound by the watchdog and create a -recursive timeout), it just inspects threading.enumerate() at the -time the test runs. +The conftest installs a daemon-thread watchdog (os._exit(2) after a +30s timeout) to bound the hang. The non-zero exit code is critical: +run_tests_batched.py uses subprocess.run(check=True) and only +prints "Batch N failed." if pytest exits non-zero. Exit code 0 would +silently report a successful batch even when the watchdog killed +pytest mid-test (the FAILURES section never gets printed). Exit +code 2 is the standard "interrupted by signal/timeout" code that +preserves the failure signal to the runner. + +This test verifies the watchdog is actually registered after the +conftest loads. It does NOT spawn a subprocess (which would itself +be bound by the watchdog and create a recursive timeout), it just +inspects threading.enumerate() at the time the test runs. If the watchdog is removed or the timeout grows, this test fails and the run_tests_batched.py hang returns.