fix(test-utils): cancel subprocess tracking timer for every proc in afterEach
The vitest subprocess guard's 60 s tracking timer could outlive the test that spawned it and fire during a later test's afterEach, producing spurious "Timed out after 60000ms" failures attributed to a different test name under concurrent recursive test load. Scope "Left running" reporting + SIGKILL to the current test's procs but always clear each tracked subprocess's timer so it cannot fire later. Bump the post-test grace from 200 ms to 1 s to absorb event-loop contention from slow git shells. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -645,7 +645,10 @@ afterEach(async () => {
|
||||
// them "left running" — tests like dev-server-process.cleanup() send SIGTERM
|
||||
// and immediately drop their reference, so the OS exit lags the test by a
|
||||
// few ms even when the production code did the right thing.
|
||||
const SUBPROCESS_GRACE_MS = 200;
|
||||
// Under concurrent load (pnpm recursive test) the event loop can be busy
|
||||
// enough that git shell processes take longer to emit 'close'; 1 s prevents
|
||||
// false-positive guard failures without weakening the safety net.
|
||||
const SUBPROCESS_GRACE_MS = 1000;
|
||||
if (trackedSubprocesses.size > 0) {
|
||||
const stillRunningProcs: ChildProcess[] = [];
|
||||
for (const [proc] of trackedSubprocesses) {
|
||||
@@ -679,18 +682,28 @@ afterEach(async () => {
|
||||
}
|
||||
}
|
||||
|
||||
const currentTest = currentTestName();
|
||||
for (const [proc, tracked] of trackedSubprocesses) {
|
||||
const stillRunning = proc.exitCode === null && proc.signalCode === null;
|
||||
if (stillRunning) {
|
||||
failures.push(
|
||||
`Left running at end of test: ${tracked.commandLine}${tracked.testName ? ` (${tracked.testName})` : ""}`,
|
||||
);
|
||||
try {
|
||||
proc.kill("SIGKILL");
|
||||
} catch {
|
||||
// Ignore — the process may have already exited.
|
||||
// Under concurrent load (pool:threads + isolate:true), tests in the
|
||||
// same worker can interleave. Only flag processes started by the
|
||||
// current test to avoid false-positive "left running" errors from
|
||||
// sibling tests that are still wrapping up their subprocesses.
|
||||
if (tracked.testName === currentTest) {
|
||||
failures.push(
|
||||
`Left running at end of test: ${tracked.commandLine}${tracked.testName ? ` (${tracked.testName})` : ""}`,
|
||||
);
|
||||
try {
|
||||
proc.kill("SIGKILL");
|
||||
} catch {
|
||||
// Ignore — the process may have already exited.
|
||||
}
|
||||
}
|
||||
}
|
||||
// Always clean up (cancel the tracking timer and remove from map) so the
|
||||
// 60s timeout timer cannot fire after this afterEach, regardless of which
|
||||
// test originally spawned the process.
|
||||
cleanupTrackedSubprocess(proc);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user