Commit 8a6abc0
committed
Replace wall-clock margins in stdio cleanup tests with kernel-level signals
The stdio cleanup tests asserted upper bounds on elapsed wall-clock time
(cleanup took < 6.0s, < 5.0s, < 3.0s, between 1.5s and 4.5s) and paced
themselves with fixed sleeps ("let the process start"). Upper bounds on
elapsed time fail on slow runners — spawning an interpreter on a loaded
CI box can eat the whole margin — and the sleeps raced process startup.
Every cleanup test now uses the socket-based liveness probe the
process-tree tests already had: the child connects back to a listener
owned by the test (deterministic readiness, replacing the sleeps; setup
lines such as installing a signal handler are guaranteed to have run
before the connect), and the kernel closing that socket when the
process dies is the proof of termination (replacing the elapsed-time
upper bounds). Time bounds that remain are generous fail_after hang
guards, not performance claims, plus one deliberate lower bound:
test_stdio_client_stdin_close_ignored still asserts cleanup takes at
least PROCESS_TERMINATION_TIMEOUT, which pins the genuine time-based
contract that escalation must wait out the stdin-close grace period.
Lower bounds cannot fail from runner slowness.
Details worth knowing:
- test_stdio_client_sigint_only_process is renamed to
test_stdio_client_sigterm_ignoring_process: the cleanup path never
sends SIGINT (it escalates stdin-close -> SIGTERM -> SIGKILL), so the
old child's SIGINT handler never fired and SIGKILL was what actually
killed it. The test now states what it proves. Its whole-function
"pragma: lax no cover" stays, with the real reason documented:
coverage is enforced per CI job at 100% including on Windows runners,
where the test is skipped and its body would count as uncovered.
- test_stdio_client_graceful_stdin_exit now proves "no signal was
needed" directly: the child sends a marker over the socket only on
the stdin-EOF exit path before exiting on its own. The old upper
bound (< 3.0s) could not distinguish a stdin-driven exit from a
SIGTERM-driven one, which also completes in about 2.2s.
- TestChildProcessCleanup's three tests become top-level functions per
the no-test-classes rule; bodies unchanged.
- The defensive except (TimeoutError, Exception) block and the
pragma'd cancelled_caught/pytest.fail arms are gone: hang guards are
plain anyio.fail_after, whose TimeoutError fails the test with no
dead lines to exclude from coverage.
- PROCESS_TERMINATION_TIMEOUT is intentionally not monkeypatched
anywhere: the 2s grace period is the contract under test in the
lower-bound test, and everywhere else the tests no longer measure
time at all, so shrinking it would only save ~6s of real escalation
waiting at the cost of testing a constant production never uses.1 parent 19fe9fa commit 8a6abc0
1 file changed
Lines changed: 216 additions & 324 deletions
0 commit comments