Skip to content

Commit

Permalink
feat!: add support for pool and poolOptions
Browse files Browse the repository at this point in the history
BREAKING CHANGES

- `--threads` is now `--pool=threads`
- `--no-threads` is now `--pool=forks`
- `--single-thread` is now `--poolOptions.threads.singleThread`
- `--experimental-vm-threads` is now `--pool=vmThreads`
- `--experimental-vm-worker-memory-limit` is now `--poolOptions.vmThreads.memoryLimit`
- `--isolate` is now `--poolOptions.<pool-name>.isolate`
- `test.maxThreads` is now `test.poolOptions.<pool-name>.maxThreads`
- `test.minThreads` is now `test.poolOptions.<pool-name>.minThreads`
- `test.useAtomics` is now `test.poolOptions.<pool-name>.useAtomics`
- `test.poolMatchGlobs.child_process` is now `test.poolMatchGlobs.forks`
- `test.poolMatchGlobs.experimentalVmThreads` is now `test.poolMatchGlobs.vmThreads`
  • Loading branch information
AriPerkkio committed Sep 24, 2023
1 parent 4e94120 commit f32cd4b
Show file tree
Hide file tree
Showing 40 changed files with 541 additions and 264 deletions.
234 changes: 177 additions & 57 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ Should Vitest process assets (.png, .svg, .jpg, etc) files and resolve them like
This module will have a default export equal to the path to the asset, if no query is specified.

::: warning
At the moment, this option only works with [`experimentalVmThreads`](#experimentalvmthreads) pool.
At the moment, this option only works with [`vmThreads`](#vmthreads) pool.
:::

#### deps.web.transformCss
Expand All @@ -262,7 +262,7 @@ Should Vitest process CSS (.css, .scss, .sass, etc) files and resolve them like
If CSS files are disabled with [`css`](#css) options, this option will just silence `ERR_UNKNOWN_FILE_EXTENSION` errors.

::: warning
At the moment, this option only works with [`experimentalVmThreads`](#experimentalvmthreads) pool.
At the moment, this option only works with [`vmThreads`](#vmthreads) pool.
:::

#### deps.web.transformGlobPattern
Expand All @@ -275,7 +275,7 @@ Regexp pattern to match external files that should be transformed.
By default, files inside `node_modules` are externalized and not transformed, unless it's CSS or an asset, and corresponding option is not disabled.

::: warning
At the moment, this option only works with [`experimentalVmThreads`](#experimentalvmthreads) pool.
At the moment, this option only works with [`vmThreads`](#vmthreads) pool.
:::

#### deps.registerNodeLoader<NonProjectOption />
Expand Down Expand Up @@ -547,7 +547,7 @@ export default defineConfig({

### poolMatchGlobs

- **Type:** `[string, 'threads' | 'child_process' | 'experimentalVmThreads'][]`
- **Type:** `[string, 'threads' | 'forks' | 'VmThreads'][]`
- **Default:** `[]`
- **Version:** Since Vitest 0.29.4

Expand Down Expand Up @@ -621,15 +621,28 @@ Custom reporters for output. Reporters can be [a Reporter instance](https://gith
Write test results to a file when the `--reporter=json`, `--reporter=html` or `--reporter=junit` option is also specified.
By providing an object instead of a string you can define individual outputs when using multiple reporters.

### experimentalVmThreads
### pool<NonProjectOption />

- **Type:** `boolean`
- **CLI:** `--experimentalVmThreads`, `--experimental-vm-threads`
- **Version:** Since Vitest 0.34.0
- **Type:** `'threads' | 'forks' | 'vmThreads'`
- **Default:** `'threads'`
- **CLI:** `--pool=threads`
- **Version:** Since Vitest 1.0.0-beta

Pool used to run tests in.

#### threads<NonProjectOption />

Enable multi-threading using [tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina)). When using threads you are unable to use process related APIs such as `process.chdir()`. Some libraries written in native languages, such as Prisma, `bcrypt` and `canvas`, have problems when running in multiple threads and run into segfaults. In these cases it is adviced to use `forks` pool instead.

Run tests using [VM context](https://nodejs.org/api/vm.html) (inside a sandboxed environment) in a worker pool.
#### forks<NonProjectOption />

This makes tests run faster, but the VM module is unstable when running [ESM code](https://github.com/nodejs/node/issues/37648). Your tests will [leak memory](https://github.com/nodejs/node/issues/33439) - to battle that, consider manually editing [`experimentalVmWorkerMemoryLimit`](#experimentalvmworkermemorylimit) value.
Similar as `threads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and main process is not as fast as with `threads` pool. Process related APIs such as `process.chdir()` are available in `forks` pool.

#### vmThreads<NonProjectOption />

Run tests using [VM context](https://nodejs.org/api/vm.html) (inside a sandboxed environment) in a `threads` pool.

This makes tests run faster, but the VM module is unstable when running [ESM code](https://github.com/nodejs/node/issues/37648). Your tests will [leak memory](https://github.com/nodejs/node/issues/33439) - to battle that, consider manually editing [`poolOptions.vmThreads.memoryLimit`](#pooloptions-vmthreads-memorylimit) value.

::: warning
Running code in a sandbox has some advantages (faster tests), but also comes with a number of disadvantages.
Expand All @@ -651,83 +664,192 @@ catch (err) {
Please, be aware of these issues when using this option. Vitest team cannot fix any of the issues on our side.
:::

### experimentalVmWorkerMemoryLimit
### poolOptions<NonProjectOption />

- **Type:** `string | number`
- **CLI:** `--experimentalVmWorkerMemoryLimit`, `--experimental-vm-worker-memory-limit`
- **Default:** `1 / CPU Cores`
- **Version:** Since Vitest 0.34.0
- **Type:** `Record<'threads' | 'forks' | 'vmThreads', {}>`
- **Default:** `{}`
- **Version:** Since Vitest 1.0.0-beta

Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default.
#### poolOptions.threads<NonProjectOption />

This option only affects workers that run tests in [VM context](#experimentalvmthreads).
Options for `threads` pool.

::: tip
The implementation is based on Jest's [`workerIdleMemoryLimit`](https://jestjs.io/docs/configuration#workeridlememorylimit-numberstring).
```ts
import { defineConfig } from 'vitest/config'

The limit can be specified in a number of different ways and whatever the result is `Math.floor` is used to turn it into an integer value:
export default defineConfig({
test: {
poolOptions: {
threads: {
// Threads related options here
}
}
}
})
```

- `<= 1` - The value is assumed to be a percentage of system memory. So 0.5 sets the memory limit of the worker to half of the total system memory
- `\> 1` - Assumed to be a fixed byte value. Because of the previous rule if you wanted a value of 1 byte (I don't know why) you could use 1.1.
- With units
- `50%` - As above, a percentage of total system memory
- `100KB`, `65MB`, etc - With units to denote a fixed memory limit.
- `K` / `KB` - Kilobytes (x1000)
- `KiB` - Kibibytes (x1024)
- `M` / `MB` - Megabytes
- `MiB` - Mebibytes
- `G` / `GB` - Gigabytes
- `GiB` - Gibibytes
:::
##### poolOptions.threads.maxThreads<NonProjectOption />

::: warning
Percentage based memory limit [does not work on Linux CircleCI](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677) workers due to incorrect system memory being reported.
- **Type:** `number`
- **Default:** _available CPUs_

Maximum number of threads. You can also use `VITEST_MAX_THREADS` environment variable.

##### poolOptions.threads.minThreads<NonProjectOption />

- **Type:** `number`
- **Default:** _available CPUs_

Minimum number of threads. You can also use `VITEST_MIN_THREADS` environment variable.

##### poolOptions.threads.singleThread<NonProjectOption />

- **Type:** `boolean`
- **Default:** `false`

Run all tests with the same environment inside a single worker thread. This will disable built-in module isolation (your source code or [inlined](#deps-inline) code will still be reevaluated for each test), but can improve test performance.


:::warning
Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses workers not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself.

This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that.
:::

### threads
##### poolOptions.threads.useAtomics<NonProjectOption />

- **Type:** `boolean`
- **Default:** `false`

Use Atomics to synchronize threads.

This can improve performance in some cases, but might cause segfault in older Node versions.

##### poolOptions.threads.isolate<NonProjectOption />

- **Type:** `boolean`
- **Default:** `true`
- **CLI:** `--threads`, `--threads=false`

Enable multi-threading using [tinypool](https://github.com/tinylibs/tinypool) (a lightweight fork of [Piscina](https://github.com/piscinajs/piscina)). Prior to Vitest 0.29.0, Vitest was still running tests inside worker thread, even if this option was disabled. Since 0.29.0, if this option is disabled, Vitest uses `child_process` to spawn a process to run tests inside, meaning you can use `process.chdir` and other API that was not available inside workers. If you want to revert to the previous behaviour, use `--single-thread` option instead.
Isolate environment for each test file.

#### poolOptions.forks<NonProjectOption />

Options for `forks` pool.

```ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
poolOptions: {
forks: {
// Forks related options here
}
}
}
})
```

##### poolOptions.forks.maxForks<NonProjectOption />

Disabling this option also disables module isolation, meaning all tests with the same environment are running inside a single child process.
- **Type:** `number`
- **Default:** _available CPUs_

### singleThread
Maximum number of forks.

##### poolOptions.forks.minForks<NonProjectOption />

- **Type:** `number`
- **Default:** _available CPUs_

Minimum number of forks.

##### poolOptions.forks.isolate<NonProjectOption />

- **Type:** `boolean`
- **Default:** `true`

Isolate environment for each test file.

##### poolOptions.forks.singleFork<NonProjectOption />

- **Type:** `boolean`
- **Default:** `false`
- **Version:** Since Vitest 0.29.0

Run all tests with the same environment inside a single worker thread. This will disable built-in module isolation (your source code or [inlined](#deps-inline) code will still be reevaluated for each test), but can improve test performance. Before Vitest 0.29.0 this was equivalent to using `--no-threads`.
Run all tests with the same environment inside a single child process. This will disable built-in module isolation (your source code or [inlined](#deps-inline) code will still be reevaluated for each test), but can improve test performance.


:::warning
Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses workers not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself.
Even though this option will force tests to run one after another, this option is different from Jest's `--runInBand`. Vitest uses child processes not only for running tests in parallel, but also to provide isolation. By disabling this option, your tests will run sequentially, but in the same global context, so you must provide isolation yourself.

This might cause all sorts of issues, if you are relying on global state (frontend frameworks usually do) or your code relies on environment to be defined separately for each test. But can be a speed boost for your tests (up to 3 times faster), that don't necessarily rely on global state or can easily bypass that.
:::

### maxThreads<NonProjectOption />
#### poolOptions.vmThreads<NonProjectOption />

Options for `vmThreads` pool.

```ts
import { defineConfig } from 'vitest/config'

export default defineConfig({
test: {
poolOptions: {
vmThreads: {
// VM threads related options here
}
}
}
})
```

##### poolOptions.vmThreads.maxThreads<NonProjectOption />

- **Type:** `number`
- **Default:** _available CPUs_

Maximum number of threads. You can also use `VITEST_MAX_THREADS` environment variable.

### minThreads<NonProjectOption />
##### poolOptions.vmThreads.minThreads<NonProjectOption />

- **Type:** `number`
- **Default:** _available CPUs_

Minimum number of threads. You can also use `VITEST_MIN_THREADS` environment variable.

### useAtomics<NonProjectOption />
##### poolOptions.vmThreads.memoryLimit<NonProjectOption />

- **Type:** `string | number`
- **Default:** `1 / CPU Cores`

Specifies the memory limit for workers before they are recycled. This value heavily depends on your environment, so it's better to specify it manually instead of relying on the default.

::: tip
The implementation is based on Jest's [`workerIdleMemoryLimit`](https://jestjs.io/docs/configuration#workeridlememorylimit-numberstring).

The limit can be specified in a number of different ways and whatever the result is `Math.floor` is used to turn it into an integer value:

- `<= 1` - The value is assumed to be a percentage of system memory. So 0.5 sets the memory limit of the worker to half of the total system memory
- `\> 1` - Assumed to be a fixed byte value. Because of the previous rule if you wanted a value of 1 byte (I don't know why) you could use 1.1.
- With units
- `50%` - As above, a percentage of total system memory
- `100KB`, `65MB`, etc - With units to denote a fixed memory limit.
- `K` / `KB` - Kilobytes (x1000)
- `KiB` - Kibibytes (x1024)
- `M` / `MB` - Megabytes
- `MiB` - Mebibytes
- `G` / `GB` - Gigabytes
- `GiB` - Gibibytes
:::

::: warning
Percentage based memory limit [does not work on Linux CircleCI](https://github.com/jestjs/jest/issues/11956#issuecomment-1212925677) workers due to incorrect system memory being reported.
:::

##### poolOptions.vmThreads.useAtomics<NonProjectOption />

- **Type:** `boolean`
- **Default:** `false`
- **Version:** Since Vitest 0.28.3

Use Atomics to synchronize threads.

Expand Down Expand Up @@ -773,7 +895,7 @@ Path to setup files. They will be run before each test file.
Changing setup files will trigger rerun of all tests.
:::

You can use `process.env.VITEST_POOL_ID` (integer-like string) inside to distinguish between threads (will always be `'1'`, if run with `threads: false`).
You can use `process.env.VITEST_POOL_ID` (integer-like string) inside to distinguish between threads.

:::tip
Note, that if you are running [`--threads=false`](#threads), this setup file will be run in the same global scope multiple times. Meaning, that you are accessing the same global object before each test, so make sure you are not doing the same thing more than you need.
Expand Down Expand Up @@ -842,16 +964,6 @@ test('execute a script', async () => {
Make sure that your files are not excluded by `watchExclude`.
:::

### isolate

- **Type:** `boolean`
- **Default:** `true`
- **CLI:** `--isolate`, `--isolate=false`

Isolate environment for each test file. Does not work if you disable [`--threads`](#threads).

This options has no effect on [`experimentalVmThreads`](#experimentalvmthreads).

### coverage<NonProjectOption />

You can use [`v8`](https://v8.dev/blog/javascript-code-coverage), [`istanbul`](https://istanbul.js.org/) or [a custom coverage solution](/guide/coverage#custom-coverage-provider) for coverage collection.
Expand Down Expand Up @@ -1211,6 +1323,14 @@ Run all tests in a specific browser. Possible options in different providers:

Run the browser in a `headless` mode. If you are running Vitest in CI, it will be enabled by default.

#### browser.isolate

- **Type:** `boolean`
- **Default:** `true`
- **CLI:** `--browser`, `--browser.isolate=false`

Isolate test environment after each test.

#### browser.api

- **Type:** `number | { port?, strictPort?, host? }`
Expand Down
8 changes: 4 additions & 4 deletions docs/guide/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Setting | Value
--- | ---
Working directory | /path/to/your-project-root
JavaScript file | ./node_modules/vitest/vitest.mjs
Application parameters | run --threads false
Application parameters | run --pool forks

Then run this configuration in debug mode. The IDE will stop at JS/TS breakpoints set in the editor.

Expand All @@ -56,12 +56,12 @@ Vitest also supports debugging tests without IDEs. However this requires that te

```sh
# To run in a single worker
vitest --inspect-brk --single-thread
vitest --inspect-brk --pool threads --poolOptions.threads.singleThread

# To run in a child process
vitest --inspect-brk --no-threads
vitest --inspect-brk --pool forks
```

Once Vitest starts it will stop execution and waits for you to open developer tools that can connect to [NodeJS 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 `--single-thread --isolate false` options.
In watch mode you can keep the debugger open during test re-runs by using the `--poolOptions.threads.isolate false` options.
8 changes: 6 additions & 2 deletions examples/solid/vite.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import solid from 'vite-plugin-solid'
export default defineConfig({
test: {
environment: 'jsdom',
threads: false,
isolate: false,
pool: 'forks',
poolOptions: {
forks: {
isolate: false,
},
},
},
plugins: [solid()],
resolve: {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
"test:run": "vitest run -r test/core",
"test:all": "CI=true pnpm -r --stream run test --allowOnly",
"test:ci": "CI=true pnpm -r --stream --filter !test-fails --filter !test-browser --filter !test-esm --filter !test-browser run test --allowOnly",
"test:ci:vm-threads": "CI=true pnpm -r --stream --filter !test-fails --filter !test-single-thread --filter !test-browser --filter !test-esm --filter !test-browser run test --allowOnly --experimental-vm-threads",
"test:ci:single-thread": "CI=true pnpm -r --stream --filter !test-fails --filter !test-coverage --filter !test-watch --filter !test-bail --filter !test-esm --filter !test-browser run test --allowOnly --no-threads",
"test:ci:vm-threads": "CI=true pnpm -r --stream --filter !test-fails --filter !test-coverage --filter !test-single-thread --filter !test-browser --filter !test-esm --filter !test-browser run test --allowOnly --pool vmThreads",
"test:ci:single-thread": "CI=true pnpm -r --stream --filter !test-fails --filter !test-vm-threads --filter !test-coverage --filter !test-watch --filter !test-bail --filter !test-esm --filter !test-browser run test --allowOnly --pool forks",
"typecheck": "tsc --noEmit",
"typecheck:why": "tsc --noEmit --explainFiles > explainTypes.txt",
"ui:build": "vite build packages/ui",
Expand Down

0 comments on commit f32cd4b

Please sign in to comment.