From b1e6c6883b7d31c93575a5a5cc30cbedc0ff00ec Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 16 Jul 2026 22:07:08 -0700 Subject: [PATCH] test: wait for parseable pid in tsdown-build pid-file polls waitForFile existence polling can catch writeFileSync's open-truncate 0-byte window, yielding NaN pids and false isProcessAlive failures (the same class as #109140). Poll until the file parses to a positive pid; covers all three sibling pid-read sites in the suite. --- test/scripts/tsdown-build.test.ts | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/test/scripts/tsdown-build.test.ts b/test/scripts/tsdown-build.test.ts index 6b993ab2afdb..37c50603d061 100644 --- a/test/scripts/tsdown-build.test.ts +++ b/test/scripts/tsdown-build.test.ts @@ -71,6 +71,23 @@ async function waitForFile(filePath: string, timeoutMs: number): Promise { throw new Error(`timed out waiting for ${filePath}`); } +// Pid files are written with plain writeFileSync, so an existence poll can +// observe the open-truncate 0-byte window and parse NaN (the #109140 flake +// class). Wait until the content parses to a real pid, not just for the file. +async function waitForPidFile(filePath: string, timeoutMs: number): Promise { + const deadlineAt = Date.now() + timeoutMs; + while (Date.now() < deadlineAt) { + if (fs.existsSync(filePath)) { + const pid = Number.parseInt(fs.readFileSync(filePath, "utf8"), 10); + if (Number.isInteger(pid) && pid > 0) { + return pid; + } + } + await sleep(5); + } + throw new Error(`timed out waiting for pid in ${filePath}`); +} + async function waitForDead(pid: number, timeoutMs: number): Promise { const deadlineAt = Date.now() + timeoutMs; while (Date.now() < deadlineAt) { @@ -909,8 +926,7 @@ describe("runTsdownBuildInvocation", () => { }, ); - await waitForFile(childPidPath, timeoutMs); - childPid = Number.parseInt(fs.readFileSync(childPidPath, "utf8"), 10); + childPid = await waitForPidFile(childPidPath, timeoutMs); expect(isProcessAlive(childPid)).toBe(true); const result = await runPromise; @@ -976,7 +992,7 @@ describe("runTsdownBuildInvocation", () => { ); await waitForFile(readyPath, 2_000); - childPid = Number.parseInt(fs.readFileSync(childPidPath, "utf8"), 10); + childPid = await waitForPidFile(childPidPath, 2_000); const result = await runPromise; expect(result.timedOut).toBe(true); @@ -1029,8 +1045,7 @@ describe("runTsdownBuildInvocation", () => { }); await waitForFile(readyPath, 2_000); - await waitForFile(childPidPath, 2_000); - childPid = Number.parseInt(fs.readFileSync(childPidPath, "utf8"), 10); + childPid = await waitForPidFile(childPidPath, 2_000); expect(isProcessAlive(childPid)).toBe(true); runner.kill("SIGTERM");