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",