Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions scripts/gate/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const model = loadModel(repoRoot, tracked);

const scriptModel = (scripts: Record<string, string>) => ({
scripts,
vitestProjects: ['unit-core', 'subprocess-stub'],
vitestProjects: ['unit-core', 'fuzz-worker'],
opaque: {},
});

Expand Down Expand Up @@ -61,31 +61,29 @@ test('a filtered Vitest run does not credit the whole project', () => {
test('a bare Vitest run spans every configured project', () => {
assert.deepEqual(scriptUnits('all', scriptModel({ all: 'vitest run --coverage' })), [
'vitest:unit-core',
'vitest:subprocess-stub',
'vitest:fuzz-worker',
]);
});

test('a negated --project subtracts from the configured set, so the skipped one is not credited', () => {
assert.deepEqual(
scriptUnits('cov', scriptModel({ cov: 'vitest run --coverage --project=!subprocess-stub' })),
scriptUnits('cov', scriptModel({ cov: 'vitest run --coverage --project=!fuzz-worker' })),
['vitest:unit-core'],
);
});

// The real `test:coverage:ci` shape: a negated `--project` leg, then a second leg that is a
// nested script. Both indirections have to survive, or the lane stops owning the project it
// hands to that leg.
test('the two halves of test:coverage:ci together still own every project', () => {
// A negated `--project` leg followed by a nested script must preserve both
// indirections, or the lane stops owning the project handed to that leg.
test('split coverage commands together still own every project', () => {
assert.deepEqual(
scriptUnits(
'test:coverage:ci',
scriptModel({
'test:coverage:ci':
'vitest run --coverage --project=!subprocess-stub && pnpm test:subprocess-stub',
'test:subprocess-stub': 'vitest run --project subprocess-stub',
'test:coverage:ci': 'vitest run --coverage --project=!fuzz-worker && pnpm test:fuzz-worker',
'test:fuzz-worker': 'vitest run --project fuzz-worker',
}),
),
['vitest:unit-core', 'vitest:subprocess-stub'],
['vitest:unit-core', 'vitest:fuzz-worker'],
);
});

Expand Down
2 changes: 1 addition & 1 deletion scripts/mutation/test-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
// own static module graph.
//
// Two files are removed from whatever Vitest returns:
// - the real-subprocess-spawn tests (SUBPROCESS_STUB_TESTS in vitest.config.ts —
// - the real-subprocess-spawn tests (MUTATION_EXCLUDED_TESTS in vitest.config.ts —
// spawns stubbed binaries and waits real subprocess/retry/poll time, out of scope
// by the issue's constraint, and thousands of mutant runs would turn it into
// timeout noise regardless of whether Vitest itself still serializes it, #1823);
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/test-utils/fake-adb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ export type FakeAdbProviderExtras = AndroidAdbProvider extends infer P
* production {@link withAndroidAdbProvider} scope — the same seam the daemon
* installs per request and the provider-scenario lane exercises. Prefer this
* over PATH-stub subprocess helpers (`withMockedAdb`): no PATH
* mutation, no spawns, no real subprocess waits, so converted files can leave
* SUBPROCESS_STUB_TESTS in vitest.config.ts (#1823).
* mutation, no spawns, no real subprocess waits, so converted files do not need a
* mutation exclusion in vitest.config.ts.
*
* The fake `exec` receives device-scoped args without a leading
* `-s <serial>`: scoped providers are per-device, and raw `runCmd('adb', …)`
Expand Down
34 changes: 13 additions & 21 deletions vitest.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,9 @@ import { defineConfig } from 'vitest/config';
import { resolveVitestMaxWorkers } from './scripts/lib/vitest-concurrency.ts';
import slowTestGateReporter from './scripts/vitest-slow-test-reporter.ts';

// Files that spawn a real subprocess per case. They used to run one at a time in
// their own serialized `subprocess-stub` project so broad file parallelism couldn't
// starve a spawn past its internal budget and turn it into a generic timeout.
// #1823 is now running that project's own kill criterion: un-serialized here in
// `unit-core`'s default forks pool, watched for 20 consecutive CI runs with no
// timeout-shaped failure. Revert (restore the project, restore this list to
// unit-core's exclude) the moment one appears. Still excluded from the mutation
// lane via SERIALIZED_TESTS below regardless of this experiment's outcome —
// thousands of mutant reruns times a real spawn per case is timeout noise either way.
const SUBPROCESS_STUB_TESTS: readonly string[] = [
// A real per-case spawn is timeout noise under thousands of mutant reruns, so the
// mutation lane excludes these tests even though the unit lane runs them normally.
const MUTATION_EXCLUDED_SUBPROCESS_TESTS: readonly string[] = [
// Stubs npx plus the package managers and spawns a real Metro dev server per case.
'src/__tests__/client-metro.test.ts',
// The SUT is the subprocess watchdog: a node subprocess per case, one hangs on purpose (#1414).
Expand Down Expand Up @@ -56,13 +49,14 @@ const FUZZ_WORKER_TESTS: readonly string[] = [
'scripts/fuzz/corpus-replay.test.ts',
];
/**
* Every test the mutation lane must not collect: a real per-case subprocess spawn is
* timeout noise under thousands of mutant reruns, independent of whether Vitest also
* serializes it — `fuzz-worker` still does; `subprocess-stub`'s former members no
* longer do (#1823). The two lists above stay module-local: this union is the whole
* cross-file surface, and the mutation lane wants exactly it.
* Every test the mutation lane must not collect. The two lists above stay
* module-local: this union is the whole cross-file surface, and the mutation lane
* wants exactly it.
*/
export const SERIALIZED_TESTS: readonly string[] = [...SUBPROCESS_STUB_TESTS, ...FUZZ_WORKER_TESTS];
export const MUTATION_EXCLUDED_TESTS: readonly string[] = [
...MUTATION_EXCLUDED_SUBPROCESS_TESTS,
...FUZZ_WORKER_TESTS,
];

// Imported by vitest.mutation.config.ts so the two lanes cannot drift: a guard
// added here must reach the Stryker sandbox too.
Expand Down Expand Up @@ -105,9 +99,8 @@ export default defineConfig({
include: [
'src/**/*.test.ts',
'packages/*/src/**/*.test.ts',
// The subprocess watchdog self-check (#1823): spawns a real node subprocess per
// case, one hangs on purpose (#1414). Formerly a `subprocess-stub` member; see
// SUBPROCESS_STUB_TESTS above for the kill-criterion experiment this rides.
// The subprocess watchdog self-check: spawns a real node subprocess per case,
// and one hangs on purpose (#1414).
'scripts/fuzz/harness.test.ts',
// The validation fuzz generators' expectation gates (#1781 B2): in-process, no
// subprocess or worker, so they ride the fast lane unlike their serialized siblings.
Expand Down Expand Up @@ -198,8 +191,7 @@ export default defineConfig({
},
{
test: {
// Serialized for the same contention reason `subprocess-stub` used to be (#1823):
// the per-case watchdog budget is real wall clock. The project exists so the
// Serialized because the per-case watchdog budget is real wall clock. The project exists so the
// coverage run can leave it out (see the comment above), not to run it differently.
name: 'fuzz-worker',
include: [...FUZZ_WORKER_TESTS],
Expand Down
8 changes: 6 additions & 2 deletions vitest.mutation.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vitest/config';
import { readTestScope, threadHostileTestFiles } from './scripts/mutation/test-scope.ts';
import { workspaceSourceAliases } from './scripts/mutation/workspace-aliases.ts';
import { SERIALIZED_TESTS, SETUP_FILES } from './vitest.config.ts';
import { MUTATION_EXCLUDED_TESTS, SETUP_FILES } from './vitest.config.ts';

const repoRoot = path.dirname(fileURLToPath(import.meta.url));

Expand All @@ -21,7 +21,11 @@ export default defineConfig({
},
test: {
include: scope ?? ['src/**/*.test.ts', 'packages/*/src/**/*.test.ts'],
exclude: [...SERIALIZED_TESTS, ...threadHostileTestFiles(repoRoot), '**/node_modules/**'],
exclude: [
...MUTATION_EXCLUDED_TESTS,
...threadHostileTestFiles(repoRoot),
'**/node_modules/**',
],
setupFiles: [...SETUP_FILES],
},
});
Loading