From cc7d73d23af50ffa4dff3f51035070f48b8b9447 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 14 Nov 2026 04:17:24 +0800 Subject: [PATCH] test(ci): double test deadlines on hosted runners (#10417) * test(javascript): give the wide fingerprint regression a capacity deadline Each case runs about 22 s of CPU-bound projection on a typical hosted runner. Its explicit 30 s deadline failed on slower runners at both two and four Vitest workers, and it also overrode the suite's 60 s coverage-mode allowance. * test(ci): double test deadlines on hosted runners Hosted CI jobs run four Vitest workers on a four-core runner. CPU-bound capacity cases then share every core with other workers and their subprocesses; three such cases exceeded the 30 s deadline at four workers (16.8-22.7 s at two). The deadline only needs to bound a hang, so hosted CI doubles it: 60 s, or 120 s with coverage. Local runs keep 30 s and 60 s. * test(ci): check worker and deadline policy for local and hosted runners The policy test loaded vitest.config.ts in a child that inherited the parent's GITHUB_ACTIONS, so on CI it observed the hosted deadlines and failed. Select the runner explicitly and check both: two workers and the base deadline locally, four workers and twice the deadline on hosted CI. --- docs/development/testing.md | 4 +- .../filesystem/ciTestProjects.test.ts | 71 +++++++++++-------- .../semanticFingerprintAccumulation.test.ts | 5 +- vitest.config.ts | 7 +- 4 files changed, 54 insertions(+), 33 deletions(-) diff --git a/docs/development/testing.md b/docs/development/testing.md index ecebe4a920..9a2a23844b 100644 --- a/docs/development/testing.md +++ b/docs/development/testing.md @@ -239,7 +239,9 @@ changes additionally need package verification; provider changes need actual provider evidence. Vitest projects use up to two workers, bounded by host parallelism. GitHub -Actions jobs own their runner and use up to four. +Actions jobs own their runner and use up to four. Test deadlines are 30 seconds, +or 60 with coverage; hosted CI doubles both because CPU-bound cases there share +every core with other workers and their subprocesses. `process-boundary` runs later with serial files because process-tree sampling shares host resources. Acceptance and process-global cases use isolated forks without serial scheduling. Pure domain/service, composition, conformance, diff --git a/tests/boundary/filesystem/ciTestProjects.test.ts b/tests/boundary/filesystem/ciTestProjects.test.ts index 0e6c0baec2..ee76728f6f 100644 --- a/tests/boundary/filesystem/ciTestProjects.test.ts +++ b/tests/boundary/filesystem/ciTestProjects.test.ts @@ -1,5 +1,6 @@ import { execFileSync } from "node:child_process"; import { mkdir, writeFile } from "node:fs/promises"; +import { availableParallelism } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; @@ -161,34 +162,42 @@ describe("Vitest project configuration", () => { ); }); - it.each([ - { flags: [], timeout: 30_000, lines: 68 }, - { flags: ["--coverage"], timeout: 60_000, lines: 68 }, - { flags: ["--coverage.enabled"], timeout: 60_000, lines: 68 }, - { flags: ["--coverage=false"], timeout: 30_000, lines: 68 }, - { - flags: ["--coverage.enabled=false"], - timeout: 30_000, - lines: 68, - }, - { - flags: ["--coverage.provider=v8"], - timeout: 30_000, - lines: 68, - }, - { - flags: ["--coverage.enabled", "--shard", "1/6"], - timeout: 60_000, - lines: 0, - }, - { - flags: ["--coverage.enabled=false", "--shard=1/6"], - timeout: 30_000, - lines: 0, - }, - ])( - "resolves worker resources from actual CLI options: $flags", - async ({ flags, timeout, lines }) => { + it.each( + [ + { flags: [], timeout: 30_000, lines: 68 }, + { flags: ["--coverage"], timeout: 60_000, lines: 68 }, + { flags: ["--coverage.enabled"], timeout: 60_000, lines: 68 }, + { flags: ["--coverage=false"], timeout: 30_000, lines: 68 }, + { + flags: ["--coverage.enabled=false"], + timeout: 30_000, + lines: 68, + }, + { + flags: ["--coverage.provider=v8"], + timeout: 30_000, + lines: 68, + }, + { + flags: ["--coverage.enabled", "--shard", "1/6"], + timeout: 60_000, + lines: 0, + }, + { + flags: ["--coverage.enabled=false", "--shard=1/6"], + timeout: 30_000, + lines: 0, + }, + ].flatMap((policy) => + // Hosted CI owns a four-core runner: up to four workers, doubled deadlines. + [ + { ...policy, hosted: false, workers: 2, scale: 1 }, + { ...policy, hosted: true, workers: 4, scale: 2 }, + ], + ), + )( + "resolves worker resources from actual CLI options: $flags (hosted: $hosted)", + async ({ flags, timeout, lines, hosted, workers, scale }) => { const root = await createTestTempDirectory("rea-vitest-worker-policy-"); const script = join(root, "worker-policy.mjs"); await writeFile( @@ -200,6 +209,7 @@ describe("Vitest project configuration", () => { execArgv: project.test.execArgv, timeout: config.test.testTimeout, lines: config.test.coverage.thresholds.lines, + workers: config.test.maxWorkers, })); `, ); @@ -208,6 +218,8 @@ describe("Vitest project configuration", () => { ["--import", "tsx", script, ...flags], { cwd: repository, + // Select the runner explicitly; CI itself sets GITHUB_ACTIONS. + env: { ...process.env, GITHUB_ACTIONS: hosted ? "true" : "" }, timeout: 10_000, maxBuffer: 64 * 1024, stopSignal: "SIGTERM", @@ -215,8 +227,9 @@ describe("Vitest project configuration", () => { ); expect(JSON.parse(result.stdout)).toEqual({ execArgv: ["--max-old-space-size=512"], - timeout, + timeout: timeout * scale, lines, + workers: Math.min(workers, availableParallelism()), }); }, ); diff --git a/tests/boundary/javascript/semanticFingerprintAccumulation.test.ts b/tests/boundary/javascript/semanticFingerprintAccumulation.test.ts index 7c642f10ef..c8603ca5d0 100644 --- a/tests/boundary/javascript/semanticFingerprintAccumulation.test.ts +++ b/tests/boundary/javascript/semanticFingerprintAccumulation.test.ts @@ -94,5 +94,8 @@ it.each(["direct", "transferred"] as const)( }); } }, - 30_000, + // About 22 s of CPU-bound projection on a typical hosted runner. A suite + // deadline of 30 s fails it on slower runners, and an explicit 30 s also + // removed the suite's longer coverage-mode allowance. + 60_000, ); diff --git a/vitest.config.ts b/vitest.config.ts index da60fe65ed..64a5ce846a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -150,8 +150,11 @@ export default defineConfig({ runner: "./tests/processOwnershipRunner.ts", // Boundary projects may compete with TypeScript, docs, and package checks // under Turbo. Keep the deadline bounded while avoiding false failures from - // host-level CPU and filesystem contention. - testTimeout: COVERAGE_ENABLED ? 60_000 : 30_000, + // host-level CPU and filesystem contention. Hosted CI runs a worker per + // core, so CPU-bound cases share the runner with each other's subprocesses; + // the deadline still bounds a hang there, at twice the local allowance. + testTimeout: + (COVERAGE_ENABLED ? 60_000 : 30_000) * (DEDICATED_CI_RUNNER ? 2 : 1), coverage: { enabled: false, provider: "v8",