fix(ci): budget native Windows coverage timing

This commit is contained in:
Tianyi Cui
2026-08-12 15:39:17 +08:00
parent de17206051
commit f3bfcf33bb
7 changed files with 52 additions and 4 deletions

View File

@@ -64,6 +64,9 @@ describe('CI workflow', () => {
expect(windowsNative['runs-on']).toContain('dsh-windows-2025-16core')
expect(windowsNative.name).toBe('windows node 24 / native complete')
expect(windowsNative.if).toBe("github.event_name == 'pull_request'")
expect(windowsNative.env).toMatchObject({
DSH_COVERAGE_TEST_TIMEOUT_MS: '15000',
})
const nativeCommandSteps = (windowsNative.steps as unknown[]).filter((step): step is Record<string, unknown> & { run: string } => (
isRecord(step) && typeof step.run === 'string'
))

View File

@@ -92,6 +92,35 @@ describe('gate graph validation', () => {
expect(byId.get('duplication')?.allowFailure).toBe(true)
})
it('applies one configured test and polling 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',
]))
}
})
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)=/),
]))
}
})
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.each([
['empty', [], /gate graph has no gates/],
['duplicate ids', [gate('same'), gate('same')], /duplicate gate id "same"/],

View File

@@ -481,6 +481,9 @@ 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 and expect.poll
// defaults together for instrumented lanes whose scheduling overhead exceeds
// those defaults. Explicit fixture timeouts remain authoritative.
function coverageWorkerArgs(): { instrumented: string[]; exempt: string[] } {
const [flag] = positiveIntArg('DSH_COVERAGE_MAX_WORKERS', '--maxWorkers')
if (flag === undefined) return { instrumented: [], exempt: [] }
@@ -493,14 +496,23 @@ function coverageWorkerArgs(): { instrumented: string[]; exempt: string[] } {
}
}
function coverageTimeoutArgs(): string[] {
return [
...positiveIntArg('DSH_COVERAGE_TEST_TIMEOUT_MS', '--testTimeout'),
...positiveIntArg('DSH_COVERAGE_TEST_TIMEOUT_MS', '--expect.poll.timeout'),
]
}
function coverageGates(): Gate[] {
const workers = coverageWorkerArgs()
const timeouts = coverageTimeoutArgs()
return [
pnpmExec('coverage', [
'vitest',
'run',
'--coverage',
...workers.instrumented,
...timeouts,
], {
label: 'test:coverage',
env: { [COVERAGE_EXEMPT_ENV]: '1' },
@@ -510,6 +522,7 @@ function coverageGates(): Gate[] {
'run',
...coverageExemptHeavySuites.map(suite => suite.filter),
...workers.exempt,
...timeouts,
], {
label: 'test:coverage-exempt-heavy',
}),