test(vitest): apply the lane test budget inside every inline project

DSH_COVERAGE_TEST_TIMEOUT_MS expanded to --testTimeout, --hookTimeout, and
--expect.poll.timeout on the coverage and serial lanes, but Vitest forwards
only a fixed list of CLI overrides into inline projects and neither
hookTimeout nor expect is on it, so under the root config's two projects the
hook and expect.poll budgets stayed at Vitest's defaults on every lane.

vitest.config.ts now reads the variable through coverageTestTimeoutOptions and
spreads the three budgets into both projects; run-gates and the partition
coordinator pass no timeout flags, leaving one owner for the rule. A new spec
spawns vitest on the root config with a 200 ms budget and proves all three
waits end inside both projects, that unset keeps the defaults, and that a
malformed value fails at config load.

Fixes #5335
Related to #5325
This commit is contained in:
Tianyi Cui committed 2026-09-28 13:37:20 +08:00
1 parent 8be17176fe
commit 333d7e75d2
12 files changed
+220 -73

No files matched your search

@@ -9,8 +9,8 @@
en: 5853599207f6b3bb
zh: d58b5e8e19d93a4f
/agent-note-subagent-teardown-tests-inherit-their-execution-lane-budgets/decision:
en: 721ea003ebaef8c7
zh: e17be423bc4c7568
en: 5c60e4926ac090af
zh: e34b73ab1f6358e0
/agent-note-subagent-teardown-tests-inherit-their-execution-lane-budgets/alternatives-considered:
en: 3ad30b2574818a61
zh: d323adddd5065ae4
@@ -12,7 +12,7 @@ The Windows coverage run (run 34085536250, job 101628739668) reports two teardow
The [ACP test](../../../../packages/subagent/subagent-acp/tests/subagent-acp.spec.ts) awaits disposal under the execution lane’s test budget, then checks the actual child outcome. Failure cleanup awaits disposal and child completion before removing the private directory. A deferred exit observation proves that disposal cannot finish merely because termination was requested. The production EOF and termination grace periods remain unchanged.
The [Codex test](../../../../packages/subagent/subagent-codex/tests/real-product.spec.ts) inherits the execution lane’s hook budget. Cleanup captures its contexts, HTTP fixtures, and temporary roots before its first asynchronous wait, so an overdue hook cannot drain resources registered by another test. It preserves context-disposal, server-closure, and directory-removal ordering, waits for every captured disposer, and attempts the remaining cleanup stages after a rejection. Collected errors identify each failing stage or path and retain their causes; cleanup reports them only after all captured resources have been attempted.
The [Codex test](../../../../packages/subagent/subagent-codex/tests/real-product.spec.ts) inherits the execution lane’s hook budget, which reaches the inline Vitest projects only through `vitest.config.ts` reading `DSH_COVERAGE_TEST_TIMEOUT_MS`: Vitest forwards `--testTimeout` but neither `--hookTimeout` nor `--expect.poll.timeout` into inline projects, so a flag-carried lane budget raises the per-test default alone. Cleanup captures its contexts, HTTP fixtures, and temporary roots before its first asynchronous wait, so an overdue hook cannot drain resources registered by another test. It preserves context-disposal, server-closure, and directory-removal ordering, waits for every captured disposer, and attempts the remaining cleanup stages after a rejection. Collected errors identify each failing stage or path and retain their causes; cleanup reports them only after all captured resources have been attempted.
The [native Windows CI decision](../process/2026-08-08-native-windows-pull-request-ci.md) continues to own lane scheduling and budgets. This change only removes conflicting local deadlines and strengthens resource-lifetime assertions; it does not establish a Windows process-kill or filesystem defect.
@@ -12,7 +12,7 @@ Windows 覆盖率运行 (run 34085536250, job 101628739668) 为测试和钩子
[ACP 测试](../../../../packages/subagent/subagent-acp/tests/subagent-acp.spec.ts) 在执行通道的测试预算内等待清理完成,然后检查真实子进程的结果。失败清理先等待释放和子进程完成,再删除私有目录。延迟的退出观察证明,清理不能仅因已请求终止而完成。生产环境的 EOF 与终止宽限期保持不变。
[Codex 测试](../../../../packages/subagent/subagent-codex/tests/real-product.spec.ts) 继承执行通道的钩子预算。清理在第一次异步等待前取得其上下文、HTTP 夹具和临时根目录,因此超时钩子不能取走其他测试注册的资源。清理保留上下文释放、服务器关闭、目录删除的顺序,等待所有已取得的释放操作,并在拒绝后继续尝试其余清理阶段。收集的错误指出各自失败的阶段或路径并保留原始原因;只有全部已取得资源都尝试清理后才报告错误。
[Codex 测试](../../../../packages/subagent/subagent-codex/tests/real-product.spec.ts) 继承执行通道的钩子预算;该预算只通过 `vitest.config.ts` 读取 `DSH_COVERAGE_TEST_TIMEOUT_MS` 才到达内联 Vitest projects:Vitest 只把 `--testTimeout` 转发给内联 project,不转发 `--hookTimeout` 与 `--expect.poll.timeout`,因此以标志传递的通道预算只会提高单测试默认值。清理在第一次异步等待前取得其上下文、HTTP 夹具和临时根目录,因此超时钩子不能取走其他测试注册的资源。清理保留上下文释放、服务器关闭、目录删除的顺序,等待所有已取得的释放操作,并在拒绝后继续尝试其余清理阶段。收集的错误指出各自失败的阶段或路径并保留原始原因;只有全部已取得资源都尝试清理后才报告错误。
[原生 Windows CI 决策](../process/2026-08-08-native-windows-pull-request-ci.zh.md) 继续负责通道调度和预算。本次改动仅移除冲突的局部期限并加强资源生命周期断言;它并不证明 Windows 进程终止或文件系统存在缺陷。
+11
View File
@@ -613,6 +613,17 @@ describe('CI workflow', () => {
expect(config).not.toContain("pool: process.platform === 'win32' ? 'threads' : 'forks'")
expect(config.match(/pool: 'forks'/g)).toHaveLength(2)
})
it('applies the lane test budget inside every Vitest project', () => {
// The coverage and serial lanes grant DSH_COVERAGE_TEST_TIMEOUT_MS; Vitest
// forwards only --testTimeout of the three budgets into inline projects,
// so each project reads the budget from the config (behavior pinned by
// scripts/lane-test-budget.spec.ts).
const config = readFileSync(resolve(root, 'vitest.config.ts'), 'utf8')
expect(config).toContain('const laneTestBudget = coverageTestTimeoutOptions(process.env[COVERAGE_TEST_TIMEOUT_ENV])')
expect(config.match(/^ {10}\.\.\.laneTestBudget,$/gm)).toHaveLength(2)
})
})
describe('Runtime and LLM e2e Blacksmith routing', () => {
+16 -12
View File
@@ -11,9 +11,10 @@ import {
CoveragePartitionCoordinator,
assignWeightedPartitions,
collectPartitionDurations,
coverageTestTimeoutArgs,
coverageTestTimeoutOptions,
forwardedCoverageArgs,
parseCoveragePartitionCount,
parseCoverageTestTimeout,
parseListOutput,
readFileDurations,
writeFileDurations,
@@ -102,21 +103,24 @@ describe('coverage partition count', () => {
})
})
describe('coverage partition timeout', () => {
it('applies one configured timeout to tests, polling, and hooks', () => {
expect(coverageTestTimeoutArgs('30000')).toEqual([
'--testTimeout=30000',
'--expect.poll.timeout=30000',
'--hookTimeout=30000',
])
describe('lane test budget', () => {
it('applies one configured budget to tests, hooks, and expect.poll', () => {
expect(coverageTestTimeoutOptions('30000')).toEqual({
testTimeout: 30000,
hookTimeout: 30000,
expect: { poll: { timeout: 30000 } },
})
})
it('keeps Vitest defaults when the timeout is absent', () => {
expect(coverageTestTimeoutArgs(undefined)).toEqual([])
it.each([undefined, ''])('keeps Vitest defaults when the budget is %j', (raw) => {
expect(parseCoverageTestTimeout(raw)).toBeUndefined()
expect(coverageTestTimeoutOptions(raw)).toEqual({})
})
it('rejects invalid timeout input', () => {
expect(() => coverageTestTimeoutArgs('0'))
it.each(['0', '-1', '2.5', '090', '90000ms', 'many'])('rejects %j', (raw) => {
expect(() => parseCoverageTestTimeout(raw))
.toThrow(`${COVERAGE_TEST_TIMEOUT_ENV} must be a positive integer, got ${JSON.stringify(raw)}.`)
expect(() => coverageTestTimeoutOptions(raw))
.toThrow(`${COVERAGE_TEST_TIMEOUT_ENV} must be a positive integer`)
})
})
+35 -11
View File
@@ -12,7 +12,7 @@ export const COVERAGE_PARTITIONS_ENV = 'DSH_COVERAGE_PARTITIONS'
/** Internal marker that suppresses reports and thresholds inside a partition process. */
export const COVERAGE_PARTITION_MODE_ENV = 'DSH_COVERAGE_PARTITION_MODE'
/** Environment variable overriding instrumented test, polling, and hook timeouts. */
/** Environment variable carrying a lane's per-test, hook, and `expect.poll` budget in milliseconds. */
export const COVERAGE_TEST_TIMEOUT_ENV = 'DSH_COVERAGE_TEST_TIMEOUT_MS'
/**
@@ -85,22 +85,46 @@ export function parseCoveragePartitionCount(raw: string | undefined): number | u
}
/**
* Resolve the paired Vitest timeout arguments used by coverage partitions.
* `--hookTimeout` travels with the test budget because setup and teardown pay
* the same host contention the raised test budget accounts for: fixtures that
* await child exit or retry Windows handle release spend that cost in
* `afterEach`, where Vitest's separate 10 s default would otherwise fail a
* suite whose cases all passed.
* Parse the lane test budget carried by `DSH_COVERAGE_TEST_TIMEOUT_MS`.
* @param raw - the configured millisecond budget, or undefined to keep Vitest's defaults.
* @returns the Vitest arguments applying that budget, empty when unset.
* @returns the budget in milliseconds, or undefined when unset.
* @throws when the value is set and is not a positive integer.
*/
export function coverageTestTimeoutArgs(raw: string | undefined): string[] {
if (raw === undefined || raw === '') return []
export function parseCoverageTestTimeout(raw: string | undefined): number | undefined {
if (raw === undefined || raw === '') return undefined
const parsed = Number.parseInt(raw, 10)
if (!Number.isSafeInteger(parsed) || parsed < 1 || String(parsed) !== raw) {
throw new Error(`${COVERAGE_TEST_TIMEOUT_ENV} must be a positive integer, got ${JSON.stringify(raw)}.`)
}
return [`--testTimeout=${raw}`, `--expect.poll.timeout=${raw}`, `--hookTimeout=${raw}`]
return parsed
}
/** The Vitest `test` options one lane budget sets in each inline project. */
export interface CoverageTestTimeoutOptions {
testTimeout?: number
hookTimeout?: number
expect?: { poll: { timeout: number } }
}
/**
* Resolve the Vitest test options that apply one lane budget to the per-test,
* hook, and `expect.poll` defaults together. The hook budget travels with the
* test budget because setup and teardown pay the same host contention the
* raised test budget accounts for: fixtures that await child exit or retry
* Windows handle release spend that cost in `afterEach`, where Vitest's
* separate 10 s default would otherwise fail a suite whose cases all passed.
* The root config spreads these into each inline project: Vitest forwards
* only a fixed list of CLI overrides into projects, and `--hookTimeout` and
* `--expect.poll.timeout` are not on it, so a flag-based budget reaches only
* the per-test default. Explicit `describe`, case, and fixture timeouts remain
* authoritative.
* @param raw - the configured millisecond budget, or undefined to keep Vitest's defaults.
* @returns the options to spread into a project's `test` block, empty when unset.
*/
export function coverageTestTimeoutOptions(raw: string | undefined): CoverageTestTimeoutOptions {
const timeout = parseCoverageTestTimeout(raw)
if (timeout === undefined) return {}
return { testTimeout: timeout, hookTimeout: timeout, expect: { poll: { timeout } } }
}
/** Remove pnpm's package-script separator before forwarding Vitest arguments. */
@@ -0,0 +1,28 @@
/**
* Waits that end only when a lane budget below them is in force. Runs only
* inside the Vitest child that scripts/lane-test-budget.spec.ts spawns.
*/
import { beforeEach, describe, expect, it } from 'vitest'
const sleep = (ms: number): Promise<void> => new Promise(resolve => setTimeout(resolve, ms))
describe('lane test budget fixture', () => {
it('per-test budget', async () => {
await sleep(600)
})
// Its own case budget stays above the wait so the poll budget, not the
// per-test budget, is what ends the assertion.
it('expect.poll budget', { timeout: 5_000 }, async () => {
const start = Date.now()
await expect.poll(() => Date.now() - start).toBeGreaterThan(500)
})
describe('hook budget', () => {
beforeEach(async () => {
await sleep(600)
})
it('runs after the hook', () => {})
})
})
+97
View File
@@ -0,0 +1,97 @@
/**
* DSH_COVERAGE_TEST_TIMEOUT_MS reaches every inline Vitest project. The root
* config spreads the budget into each project because Vitest forwards only a
* fixed list of CLI overrides into projects: --testTimeout is on it,
* --hookTimeout and --expect.poll.timeout are not, so a flag-based budget
* raises only the per-test default. A budget below the fixture's waits must
* end all three inside both projects; unset must keep Vitest's defaults, under
* which the same fixture passes.
*/
import { spawnSync } from 'node:child_process'
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, describe, expect, it } from 'vitest'
import { COVERAGE_TEST_TIMEOUT_ENV } from './coverage-partitions.ts'
const root = resolve(import.meta.dirname, '..')
const vitestCli = fileURLToPath(new URL('../node_modules/vitest/vitest.mjs', import.meta.url))
const fixture = 'scripts/fixtures/lane-test-budget.fixture.ts'
// The root config with every project's inventory narrowed to the fixture, so
// the fixture never joins the ordinary inventory and still runs once per
// project. Absolute import in POSIX spelling because the temporary directory
// is outside the repository; the sibling package.json keeps Vite bundling the
// config as ESM, which the repository root's "type" otherwise supplies.
const temporaryRoot = mkdtempSync(join(tmpdir(), 'dsh-lane-test-budget-'))
const configPath = join(temporaryRoot, 'vitest.config.ts')
writeFileSync(join(temporaryRoot, 'package.json'), '{ "type": "module" }\n', 'utf8')
writeFileSync(configPath, [
`import base from ${JSON.stringify(resolve(root, 'vitest.config.ts').split('\\').join('/'))}`,
'export default {',
' ...base,',
' test: {',
' ...base.test,',
' projects: (base.test.projects ?? []).map(project => ({',
' ...project,',
` test: { ...project.test, include: [${JSON.stringify(fixture)}] },`,
' })),',
' },',
'}',
'',
].join('\n'), 'utf8')
afterAll(() => { rmSync(temporaryRoot, { recursive: true, force: true }) })
function runFixture(budget: string | undefined): { status: number | null; output: string } {
const env: NodeJS.ProcessEnv = { ...process.env, NO_COLOR: '1' }
for (const name of Object.keys(env)) {
// The parent worker's Vitest state and the coverage coordinator's own
// variables describe this process, not the child; the Actions reporter
// would otherwise annotate the parent job with the fixture's failures.
if (name.startsWith('VITEST') || name.startsWith('DSH_COVERAGE_') || name === 'GITHUB_ACTIONS') Reflect.deleteProperty(env, name)
}
if (budget !== undefined) env[COVERAGE_TEST_TIMEOUT_ENV] = budget
const child = spawnSync(process.execPath, [vitestCli, 'run', '--config', configPath], {
cwd: root,
env,
encoding: 'utf8',
maxBuffer: 16 * 1024 * 1024,
})
if (child.error !== undefined) throw child.error
return { status: child.status, output: `${child.stdout}\n${child.stderr}` }
}
describe('lane test budget', () => {
it('ends per-test, hook, and expect.poll waits in both inline projects', { timeout: 90_000 }, () => {
const { status, output } = runFixture('200')
expect(output).toMatch(/\|thread-safe\| scripts\/fixtures\/lane-test-budget\.fixture\.ts/)
expect(output).toMatch(/\|process-bound\| scripts\/fixtures\/lane-test-budget\.fixture\.ts/)
expect(output).toMatch(/Test Files\s+2 failed \(2\)/)
expect(output).toMatch(/Tests\s+6 failed \(6\)/)
expect(output.match(/Test timed out in 200ms\./g)).toHaveLength(2)
expect(output.match(/Hook timed out in 200ms\./g)).toHaveLength(2)
expect(output.match(/Matcher did not succeed in time\./g)).toHaveLength(2)
for (const [, polled] of output.matchAll(/expected (\d+) to be greater than 500/g)) {
expect(Number(polled)).toBeLessThan(500)
}
expect(status).toBe(1)
})
it('keeps Vitest defaults when the budget is unset', { timeout: 90_000 }, () => {
const { status, output } = runFixture(undefined)
expect(output).toMatch(/Test Files\s+2 passed \(2\)/)
expect(output).toMatch(/Tests\s+6 passed \(6\)/)
expect(status).toBe(0)
})
it('refuses a malformed budget at config load before any test runs', { timeout: 90_000 }, () => {
const { status, output } = runFixture('90000ms')
expect(output).toContain(`${COVERAGE_TEST_TIMEOUT_ENV} must be a positive integer, got "90000ms".`)
expect(output).not.toMatch(/lane-test-budget\.fixture\.ts \(/)
expect(status).toBe(1)
})
})
+1 -6
View File
@@ -2,9 +2,7 @@
import { resolve } from 'node:path'
import {
COVERAGE_PARTITIONS_ENV,
COVERAGE_TEST_TIMEOUT_ENV,
CoveragePartitionCoordinator,
coverageTestTimeoutArgs,
forwardedCoverageArgs,
parseCoveragePartitionCount,
} from './coverage-partitions.ts'
@@ -22,9 +20,6 @@ const coordinator = new CoveragePartitionCoordinator({
root: resolve(import.meta.dirname, '..'),
partitions,
pnpmEntrypoint,
vitestArgs: [
...coverageTestTimeoutArgs(process.env[COVERAGE_TEST_TIMEOUT_ENV]),
...forwardedCoverageArgs(process.argv.slice(2)),
],
vitestArgs: forwardedCoverageArgs(process.argv.slice(2)),
})
process.exitCode = await coordinator.run()
+14 -27
View File
@@ -518,36 +518,23 @@ describe('gate graph validation', () => {
expect(results.some(result => result.status === 'skipped')).toBe(false)
})
it('applies one configured test, polling, and hook timeout to both coverage gates', () => {
const gates = withEnv('DSH_COVERAGE_TEST_TIMEOUT_MS', '15000', () =>
withPnpmEntrypoint(() => gatesForMode('ci-windows-complete')))
for (const id of ['coverage', 'coverage-exempt-heavy']) {
expect(gates.find(subject => subject.id === id)?.args).toEqual(expect.arrayContaining([
'--testTimeout=15000',
'--expect.poll.timeout=15000',
'--hookTimeout=15000',
]))
it('leaves the lane test budget to the inherited environment on both coverage gates', () => {
// vitest.config.ts reads DSH_COVERAGE_TEST_TIMEOUT_MS per inline project;
// a CLI flag here would reach only the per-test default and split the
// budget between two owners.
for (const budget of ['15000', undefined]) {
const gates = withEnv('DSH_COVERAGE_TEST_TIMEOUT_MS', budget, () =>
withPnpmEntrypoint(() => gatesForMode('ci-windows-complete')))
for (const id of ['coverage', 'coverage-exempt-heavy']) {
const gate = gates.find(subject => subject.id === id)
expect(gate?.args).not.toEqual(expect.arrayContaining([
expect.stringMatching(/^--(?:testTimeout|expect\.poll\.timeout|hookTimeout)=/),
]))
expect(gate?.env ?? {}).not.toHaveProperty('DSH_COVERAGE_TEST_TIMEOUT_MS')
}
}
})
it('keeps Vitest timeout defaults when the coverage override is absent', () => {
const gates = withEnv('DSH_COVERAGE_TEST_TIMEOUT_MS', undefined, () =>
withPnpmEntrypoint(() => gatesForMode('ci-windows-complete')))
for (const id of ['coverage', 'coverage-exempt-heavy']) {
expect(gates.find(subject => subject.id === id)?.args).not.toEqual(expect.arrayContaining([
expect.stringMatching(/^--(?:testTimeout|expect\.poll\.timeout|hookTimeout)=/),
]))
}
})
it('rejects an invalid coverage timeout before starting a gate', () => {
expect(() => withEnv('DSH_COVERAGE_TEST_TIMEOUT_MS', '0', () =>
withPnpmEntrypoint(() => gatesForMode('ci-windows-complete'))))
.toThrow('DSH_COVERAGE_TEST_TIMEOUT_MS must be a positive integer')
})
it('selects partitioned coverage only when explicitly configured', () => {
const coverage = withEnv('DSH_COVERAGE_PARTITIONS', '3', () =>
withPnpmEntrypoint(() => gatesForMode('ci-windows-complete').find(subject => subject.id === 'coverage')))
+3 -12
View File
@@ -12,12 +12,7 @@ import { resolve } from 'node:path'
import { performance } from 'node:perf_hooks'
import { CLIENT_BUILD_PROFILE_SELECTOR } from './client-build-environment.ts'
import { COVERAGE_EXEMPT_ENV, coverageExemptHeavySuites } from './coverage-exempt.ts'
import {
COVERAGE_PARTITIONS_ENV,
COVERAGE_TEST_TIMEOUT_ENV,
coverageTestTimeoutArgs,
parseCoveragePartitionCount,
} from './coverage-partitions.ts'
import { COVERAGE_PARTITIONS_ENV, parseCoveragePartitionCount } from './coverage-partitions.ts'
import { pnpmInvocation } from './pnpm-invocation.ts'
/** A named aggregate exposed by the gate runner. */
@@ -653,9 +648,8 @@ function lintGate(options: { needs?: string[] } = {}): Gate {
// small share. A budget of 1 gives each gate 1 worker; lanes that need a strict
// total of one (the serial reference jobs) also set DSH_GATE_CONCURRENCY=1,
// which keeps the gates from overlapping at all.
// DSH_COVERAGE_TEST_TIMEOUT_MS raises Vitest's per-test, expect.poll, and hook
// defaults together for instrumented lanes whose scheduling overhead exceeds
// those defaults. Explicit fixture timeouts remain authoritative.
// DSH_COVERAGE_TEST_TIMEOUT_MS reaches both gates through the inherited
// environment: vitest.config.ts applies it to every inline project.
function coverageWorkerArgs(): { instrumented: string[]; exempt: string[] } {
const [flag] = positiveIntArg('DSH_COVERAGE_MAX_WORKERS', '--maxWorkers')
if (flag === undefined) return { instrumented: [], exempt: [] }
@@ -670,7 +664,6 @@ function coverageWorkerArgs(): { instrumented: string[]; exempt: string[] } {
function coverageGates(): Gate[] {
const workers = coverageWorkerArgs()
const timeouts = coverageTestTimeoutArgs(process.env[COVERAGE_TEST_TIMEOUT_ENV])
const partitions = parseCoveragePartitionCount(process.env[COVERAGE_PARTITIONS_ENV])
const instrumented = partitions === undefined
? pnpmExec('coverage', [
@@ -678,7 +671,6 @@ function coverageGates(): Gate[] {
'run',
'--coverage',
...workers.instrumented,
...timeouts,
], {
label: 'test:coverage',
env: { [COVERAGE_EXEMPT_ENV]: '1' },
@@ -697,7 +689,6 @@ function coverageGates(): Gate[] {
'run',
...coverageExemptHeavySuites.map(suite => suite.filter),
...workers.exempt,
...timeouts,
], {
label: 'test:coverage-exempt-heavy',
needs: ['native-system'],
+11 -1
View File
@@ -5,7 +5,7 @@ import { resolvePwshPath } from './packages/shell/pwsh-local/src/resolve.ts'
import { defineConfig } from 'vitest/config'
import { standardDecoratorPlugin, vitestExecArgv } from './vitest.shared.ts'
import { COVERAGE_EXEMPT_ENV, coverageExemptHeavySuites } from './scripts/coverage-exempt.ts'
import { COVERAGE_PARTITION_MODE_ENV } from './scripts/coverage-partitions.ts'
import { COVERAGE_PARTITION_MODE_ENV, COVERAGE_TEST_TIMEOUT_ENV, coverageTestTimeoutOptions } from './scripts/coverage-partitions.ts'
// Prints exact `path:line:col` records for every uncovered statement, branch
// path, and function when a file misses the per-file 100% gate — the built-in
@@ -144,6 +144,14 @@ if (coveragePartitionRaw !== undefined && coveragePartitionRaw !== '' && coverag
}
const coveragePartitionMode = coveragePartitionRaw === '1'
// Lanes on shared hosts raise the per-test, hook, and expect.poll defaults
// together through DSH_COVERAGE_TEST_TIMEOUT_MS. The budget lands in each
// inline project below because Vitest forwards only a fixed list of CLI
// overrides into projects: --testTimeout is on it, --hookTimeout and
// --expect.poll.timeout are not. Unset keeps Vitest's defaults; explicit
// describe, case, and fixture timeouts remain authoritative.
const laneTestBudget = coverageTestTimeoutOptions(process.env[COVERAGE_TEST_TIMEOUT_ENV])
// These suites exercise process-global state, process APIs, or timing-sensitive process I/O
// that worker threads cannot isolate reliably under aggregate gate contention.
// Keep the narrow exception in forks while the rest of the inventory avoids per-file processes.
@@ -177,6 +185,7 @@ export default defineConfig({
// MaybeLocal in cjs_lexer::Parse) from worker threads on macOS,
// Linux, and Windows. Forked workers avoid that shared thread path.
pool: 'forks',
...laneTestBudget,
setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-invariants.ts', './scripts/test-dom-environment.ts'],
include: testIncludes,
exclude: [
@@ -192,6 +201,7 @@ export default defineConfig({
name: 'process-bound',
execArgv: vitestExecArgv,
pool: 'forks',
...laneTestBudget,
setupFiles: ['./scripts/test-proxy-environment.ts', './scripts/test-invariants.ts', './scripts/test-dom-environment.ts'],
include: processBoundTests,
exclude: [