feat: add --no-file-parallelism, --maxWorkers, --minWorkers flags (#4705)

Co-authored-by: Ari Perkkiö <ari.perkkio@gmail.com>
This commit is contained in:
Vladimir 2023-12-19 11:35:22 +01:00 committed by GitHub
parent 71911039c3
commit fd5d7e66e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 102 additions and 31 deletions

View File

@ -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`

View File

@ -73,6 +73,9 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
| `--poolOptions <options>` | 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 <name>` | Select reporter: `default`, `verbose`, `dot`, `junit`, `json`, or a path to a custom reporter |
| `--outputFile <filename/-s>` | Write test results to a file when the `--reporter=json` or `--reporter=junit` option is also specified <br /> Via [cac's dot notation] you can specify individual outputs for multiple reporters |

View File

@ -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.

View File

@ -37,6 +37,9 @@ cli
.option('--poolOptions <options>', '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 <env>', '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')

View File

@ -120,13 +120,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"`)
}
}

View File

@ -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<void>[] = []
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))))
}

View File

@ -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<void>[] = []
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))))
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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.
*

View File

@ -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 () => {