diff --git a/docs/config/index.md b/docs/config/index.md index 597bc3e4003c..dbe4a84dbbc4 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -879,6 +879,33 @@ Pass additional arguments to `node` process in the VM context. See [Command-line Be careful when using, it as some options may crash worker, e.g. --prof, --title. See https://github.com/nodejs/node/issues/41103. ::: +### fileParallelism + +- **Type:** `boolean` +- **Default:** `true` +- **CLI:** `--no-file-parallelism`, `--fileParallelism=false` +- **Version:** Since Vitest 1.1 + +Should all test files run in parallel. Setting this to `false` will override `maxWorkers` and `minWorkers` options to `1`. + +::: tip +This option doesn't affect tests running in the same file. If you want to run those in parallel, use `concurrent` option on [describe](/api/#describe-concurrent) or via [a config](#sequence-concurrent). +::: + +### maxWorkers + +- **Type:** `number` +- **Version:** Since Vitest 1.1 + +Maximum number of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority. + +### minWorkers + +- **Type:** `number` +- **Version:** Since Vitest 1.1 + +Minimum number of workers to run tests in. `poolOptions.{threads,vmThreads}.minThreads`/`poolOptions.forks.minForks` has higher priority. + ### testTimeout - **Type:** `number` diff --git a/docs/guide/cli.md b/docs/guide/cli.md index 66a0661c4d3a..f476c567ab63 100644 --- a/docs/guide/cli.md +++ b/docs/guide/cli.md @@ -73,6 +73,9 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim | `--poolOptions ` | Specify pool options | | `--poolOptions.threads.isolate` | Isolate tests in threads pool (default: `true`) | | `--poolOptions.forks.isolate` | Isolate tests in forks pool (default: `true`) | +| `--fileParallelism` | Should all test files run in parallel. Use --no-parallelism to disable (default: true) | +| `--maxWorkers` | Maximum number of workers to run tests in | +| `--minWorkers` | Minimum number of workers to run tests in | | `--silent` | Silent console output from tests | | `--reporter ` | Select reporter: `default`, `verbose`, `dot`, `junit`, `json`, or a path to a custom reporter | | `--outputFile ` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified
Via [cac's dot notation] you can specify individual outputs for multiple reporters | diff --git a/docs/guide/debugging.md b/docs/guide/debugging.md index a4dcdf4169e9..97ecaebe3341 100644 --- a/docs/guide/debugging.md +++ b/docs/guide/debugging.md @@ -62,6 +62,13 @@ vitest --inspect-brk --pool threads --poolOptions.threads.singleThread vitest --inspect-brk --pool forks --poolOptions.forks.singleFork ``` +If you are using Vitest 1.1 or higher, you can also just provide `--no-parallelism` flag: + +```sh +# If pool is unknown +vitest --inspect-brk --no-file-parallelism +``` + Once Vitest starts it will stop execution and wait for you to open developer tools that can connect to [Node.js inspector](https://nodejs.org/en/docs/guides/debugging-getting-started/). You can use Chrome DevTools for this by opening `chrome://inspect` on browser. In watch mode you can keep the debugger open during test re-runs by using the `--poolOptions.threads.isolate false` options. diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index aa8d728108a4..7da8e4bed450 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -35,6 +35,9 @@ cli .option('--poolOptions ', 'Specify pool options') .option('--poolOptions.threads.isolate', 'Isolate tests in threads pool (default: true)') .option('--poolOptions.forks.isolate', 'Isolate tests in forks pool (default: true)') + .option('--fileParallelism', 'Should all test files run in parallel. Use --no-file-parallelism to disable (default: true)') + .option('--maxWorkers', 'Maximum number of workers to run tests in') + .option('--minWorkers', 'Minimum number of workers to run tests in') .option('--environment ', 'Specify runner environment, if not running in the browser (default: node)') .option('--passWithNoTests', 'Pass when no tests found') .option('--logHeapUsage', 'Show the size of heap for each test') diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 674b80dc1d0e..c8847a07efa2 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -113,13 +113,21 @@ export function resolveConfig( resolved.shard = { index, count } } + resolved.fileParallelism ??= true + + if (!resolved.fileParallelism) { + // ignore user config, parallelism cannot be implemented without limiting workers + resolved.maxWorkers = 1 + resolved.minWorkers = 1 + } + if (resolved.inspect || resolved.inspectBrk) { const isSingleThread = resolved.pool === 'threads' && resolved.poolOptions?.threads?.singleThread const isSingleFork = resolved.pool === 'forks' && resolved.poolOptions?.forks?.singleFork - if (!isSingleThread && !isSingleFork) { + if (resolved.fileParallelism && !isSingleThread && !isSingleFork) { const inspectOption = `--inspect${resolved.inspectBrk ? '-brk' : ''}` - throw new Error(`You cannot use ${inspectOption} without "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"`) + throw new Error(`You cannot use ${inspectOption} without "--no-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"`) } } diff --git a/packages/vitest/src/node/pools/child.ts b/packages/vitest/src/node/pools/child.ts index 4891c0447550..513d3ad7a309 100644 --- a/packages/vitest/src/node/pools/child.ts +++ b/packages/vitest/src/node/pools/child.ts @@ -58,8 +58,10 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath } ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1) - const maxThreads = ctx.config.poolOptions?.forks?.maxForks ?? threadsCount - const minThreads = ctx.config.poolOptions?.forks?.minForks ?? threadsCount + const poolOptions = ctx.config.poolOptions?.forks ?? {} + + const maxThreads = poolOptions.maxForks ?? ctx.config.maxWorkers ?? threadsCount + const minThreads = poolOptions.minForks ?? ctx.config.minWorkers ?? threadsCount const options: TinypoolOptions = { runtime: 'child_process', @@ -70,20 +72,20 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath } env, execArgv: [ - ...ctx.config.poolOptions?.forks?.execArgv ?? [], + ...poolOptions.execArgv ?? [], ...execArgv, ], terminateTimeout: ctx.config.teardownTimeout, + concurrentTasksPerWorker: 1, } - if (ctx.config.poolOptions?.forks?.isolate ?? true) { + const isolated = poolOptions.isolate ?? true + + if (isolated) options.isolateWorkers = true - options.concurrentTasksPerWorker = 1 - } - if (ctx.config.poolOptions?.forks?.singleFork) { - options.concurrentTasksPerWorker = 1 + if (poolOptions.singleFork || !ctx.config.fileParallelism) { options.maxThreads = 1 options.minThreads = 1 } @@ -164,7 +166,7 @@ export function createChildProcessPool(ctx: Vitest, { execArgv, env, forksPath } const files = Object.values(filesByEnv).flat() const results: PromiseSettledResult[] = [] - if (ctx.config.poolOptions?.forks?.isolate ?? true) { + if (isolated) { results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))) } diff --git a/packages/vitest/src/node/pools/threads.ts b/packages/vitest/src/node/pools/threads.ts index 4d78c458a546..c426cc6074c1 100644 --- a/packages/vitest/src/node/pools/threads.ts +++ b/packages/vitest/src/node/pools/threads.ts @@ -43,34 +43,36 @@ export function createThreadsPool(ctx: Vitest, { execArgv, env, workerPath }: Po ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1) - const maxThreads = ctx.config.poolOptions?.threads?.maxThreads ?? threadsCount - const minThreads = ctx.config.poolOptions?.threads?.minThreads ?? threadsCount + const poolOptions = ctx.config.poolOptions?.threads ?? {} + + const maxThreads = poolOptions.maxThreads ?? ctx.config.maxWorkers ?? threadsCount + const minThreads = poolOptions.minThreads ?? ctx.config.minWorkers ?? threadsCount const options: TinypoolOptions = { filename: workerPath, // TODO: investigate further // It seems atomics introduced V8 Fatal Error https://github.com/vitest-dev/vitest/issues/1191 - useAtomics: ctx.config.poolOptions?.threads?.useAtomics ?? false, + useAtomics: poolOptions.useAtomics ?? false, maxThreads, minThreads, env, execArgv: [ - ...ctx.config.poolOptions?.threads?.execArgv ?? [], + ...poolOptions.execArgv ?? [], ...execArgv, ], terminateTimeout: ctx.config.teardownTimeout, + concurrentTasksPerWorker: 1, } - if (ctx.config.poolOptions?.threads?.isolate ?? true) { + const isolated = poolOptions.isolate ?? true + + if (isolated) options.isolateWorkers = true - options.concurrentTasksPerWorker = 1 - } - if (ctx.config.poolOptions?.threads?.singleThread) { - options.concurrentTasksPerWorker = 1 + if (poolOptions.singleThread || !ctx.config.fileParallelism) { options.maxThreads = 1 options.minThreads = 1 } @@ -144,7 +146,7 @@ export function createThreadsPool(ctx: Vitest, { execArgv, env, workerPath }: Po const files = Object.values(filesByEnv).flat() const results: PromiseSettledResult[] = [] - if (ctx.config.poolOptions?.threads?.isolate ?? true) { + if (isolated) { results.push(...await Promise.allSettled(files.map(({ file, environment, project }) => runFiles(project, getConfig(project), [file], environment, invalidates)))) } diff --git a/packages/vitest/src/node/pools/typecheck.ts b/packages/vitest/src/node/pools/typecheck.ts index 281f2a71d5f6..19f811c1f596 100644 --- a/packages/vitest/src/node/pools/typecheck.ts +++ b/packages/vitest/src/node/pools/typecheck.ts @@ -96,7 +96,7 @@ export function createTypecheckPool(ctx: Vitest): ProcessPool { setTimeout(() => { resolve(false) clearInterval(_i) - }, 500) + }, 500).unref() }) const triggered = await _p if (project.typechecker && !triggered) { diff --git a/packages/vitest/src/node/pools/vm-threads.ts b/packages/vitest/src/node/pools/vm-threads.ts index 143d506bb425..9b1c4f2f22c9 100644 --- a/packages/vitest/src/node/pools/vm-threads.ts +++ b/packages/vitest/src/node/pools/vm-threads.ts @@ -48,14 +48,16 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: Pool ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1) - const maxThreads = ctx.config.poolOptions?.vmThreads?.maxThreads ?? threadsCount - const minThreads = ctx.config.poolOptions?.vmThreads?.minThreads ?? threadsCount + const poolOptions = ctx.config.poolOptions?.vmThreads ?? {} + + const maxThreads = poolOptions.maxThreads ?? ctx.config.maxWorkers ?? threadsCount + const minThreads = poolOptions.minThreads ?? ctx.config.minWorkers ?? threadsCount const options: TinypoolOptions = { filename: vmPath, // TODO: investigate further // It seems atomics introduced V8 Fatal Error https://github.com/vitest-dev/vitest/issues/1191 - useAtomics: ctx.config.poolOptions?.vmThreads?.useAtomics ?? false, + useAtomics: poolOptions.useAtomics ?? false, maxThreads, minThreads, @@ -66,16 +68,16 @@ export function createVmThreadsPool(ctx: Vitest, { execArgv, env, vmPath }: Pool '--experimental-vm-modules', '--require', suppressWarningsPath, - ...ctx.config.poolOptions?.vmThreads?.execArgv ?? [], + ...poolOptions.execArgv ?? [], ...execArgv, ], terminateTimeout: ctx.config.teardownTimeout, + concurrentTasksPerWorker: 1, maxMemoryLimitBeforeRecycle: getMemoryLimit(ctx.config) || undefined, } - if (ctx.config.poolOptions?.vmThreads?.singleThread) { - options.concurrentTasksPerWorker = 1 + if (poolOptions.singleThread || !ctx.config.fileParallelism) { options.maxThreads = 1 options.minThreads = 1 } diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index df434c519bb1..4bcb680e9e0b 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -294,6 +294,23 @@ export interface InlineConfig { */ poolOptions?: PoolOptions + /** + * Maximum number of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority. + */ + maxWorkers?: number + /** + * Minimum number of workers to run tests in. `poolOptions.{threads,vmThreads}.minThreads`/`poolOptions.forks.minForks` has higher priority. + */ + minWorkers?: number + + /** + * Should all test files run in parallel. Doesn't affect tests running in the same file. + * Setting this to `false` will override `maxWorkers` and `minWorkers` options to `1`. + * + * @default true + */ + fileParallelism?: boolean + /** * Automatically assign pool based on globs. The first match will be used. * diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index 31cb4194ba8b..dad307c82aba 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -33,19 +33,19 @@ test('shard index must be smaller than count', async () => { test('inspect requires changing pool and singleThread/singleFork', async () => { const { stderr } = await runVitest({ inspect: true }) - expect(stderr).toMatch('Error: You cannot use --inspect without "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') + expect(stderr).toMatch('Error: You cannot use --inspect without "--no-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) test('inspect cannot be used with multi-threading', async () => { const { stderr } = await runVitest({ inspect: true, pool: 'threads', poolOptions: { threads: { singleThread: false } } }) - expect(stderr).toMatch('Error: You cannot use --inspect without "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') + expect(stderr).toMatch('Error: You cannot use --inspect without "--no-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) test('inspect-brk cannot be used with multi processing', async () => { const { stderr } = await runVitest({ inspect: true, pool: 'forks', poolOptions: { forks: { singleFork: false } } }) - expect(stderr).toMatch('Error: You cannot use --inspect without "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') + expect(stderr).toMatch('Error: You cannot use --inspect without "--no-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) test('c8 coverage provider is not supported', async () => {