Skip to content

Commit

Permalink
feat!: support multiple parallel child_process (#3925)
Browse files Browse the repository at this point in the history
  • Loading branch information
AriPerkkio committed Sep 27, 2023
1 parent a5979ea commit 8b4a44a
Show file tree
Hide file tree
Showing 28 changed files with 332 additions and 168 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ jobs:
- name: Test
run: pnpm run test:ci

- name: Test Single Thread
run: pnpm run test:ci:single-thread
- name: Test No Threads
run: pnpm run test:ci:no-threads

- name: Test Vm Threads
run: pnpm run test:ci:vm-threads
Expand Down
7 changes: 7 additions & 0 deletions docs/api/vi.md
Original file line number Diff line number Diff line change
Expand Up @@ -698,8 +698,15 @@ unmockedIncrement(30) === 31

To enable mocking timers, you need to call this method. It will wrap all further calls to timers (such as `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval`, `nextTick`, `setImmediate`, `clearImmediate`, and `Date`), until [`vi.useRealTimers()`](#vi-userealtimers) is called.

Mocking `nextTick` is not supported when running Vitest inside `node:child_process` by using `--no-threads`. NodeJS uses `process.nextTick` internally in `node:child_process` and hangs when it is mocked. Mocking `nextTick` is supported when running Vitest with `--threads`.

The implementation is based internally on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers).

::: tip
Since version `0.35.0` `vi.useFakeTimers()` no longer automatically mocks `process.nextTick`.
It can still be mocked by specyfing the option in `toFake` argument: `vi.useFakeTimers({ toFake: ['nextTick'] })`.
:::

## vi.isFakeTimers

- **Type:** `() => boolean`
Expand Down
2 changes: 1 addition & 1 deletion docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ Percentage based memory limit [does not work on Linux CircleCI](https://github.c

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.

Disabling this option also disables module isolation, meaning all tests with the same environment are running inside a single child process.
Disabling this option makes all tests run inside multiple child processes.

### singleThread

Expand Down
2 changes: 1 addition & 1 deletion docs/guide/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Vitest also supports debugging tests without IDEs. However this requires that te
vitest --inspect-brk --single-thread

# To run in a child process
vitest --inspect-brk --no-threads
vitest --inspect-brk --single-thread --no-threads
```

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.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"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:no-threads": "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",
"typecheck": "tsc --noEmit",
"typecheck:why": "tsc --noEmit --explainFiles > explainTypes.txt",
"ui:build": "vite build packages/ui",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@vitest/ws-client": "workspace:*",
"@vueuse/core": "^10.2.1",
"ansi-to-html": "^0.7.2",
"birpc": "0.2.12",
"birpc": "0.2.14",
"codemirror": "^5.65.13",
"codemirror-theme-vars": "^0.1.2",
"cypress": "^12.16.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
"std-env": "^3.3.3",
"strip-literal": "^1.0.1",
"tinybench": "^2.5.0",
"tinypool": "^0.7.0",
"tinypool": "^0.8.1",
"vite": "^3.1.0 || ^4.0.0 || ^5.0.0-0",
"vite-node": "workspace:*",
"why-is-node-running": "^2.2.2"
Expand All @@ -180,7 +180,7 @@
"@types/micromatch": "^4.0.2",
"@types/prompts": "^2.4.4",
"@types/sinonjs__fake-timers": "^8.1.2",
"birpc": "0.2.12",
"birpc": "0.2.14",
"chai-subset": "^1.6.0",
"cli-truncate": "^3.1.0",
"event-target-polyfill": "^0.0.3",
Expand Down
4 changes: 2 additions & 2 deletions packages/vitest/src/api/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function setup(vitestOrWorkspace: Vitest | WorkspaceProject, server?: Vit

const wss = new WebSocketServer({ noServer: true })

const clients = new Map<WebSocket, BirpcReturn<WebSocketEvents>>()
const clients = new Map<WebSocket, BirpcReturn<WebSocketEvents, WebSocketHandlers>>()

;(server || ctx.server).httpServer?.on('upgrade', (request, socket, head) => {
if (!request.url)
Expand Down Expand Up @@ -154,7 +154,7 @@ class WebSocketReporter implements Reporter {
constructor(
public ctx: Vitest,
public wss: WebSocketServer,
public clients: Map<WebSocket, BirpcReturn<WebSocketEvents>>,
public clients: Map<WebSocket, BirpcReturn<WebSocketEvents, WebSocketHandlers>>,
) {}

onCollected(files?: File[]) {
Expand Down
8 changes: 7 additions & 1 deletion packages/vitest/src/integrations/mock/timers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,13 @@ export class FakeTimers {
}

if (!this._fakingTime) {
const toFake = Object.keys(this._fakeTimers.timers) as Array<keyof FakeTimerWithContext['timers']>
const toFake = Object.keys(this._fakeTimers.timers)
// Do not mock nextTick by default. It can still be mocked through userConfig.
.filter(timer => timer !== 'nextTick') as (keyof FakeTimerWithContext['timers'])[]

// @ts-expect-error -- untyped internal
if (this._userConfig?.toFake?.includes('nextTick') && globalThis.__vitest_worker__.isChildProcess)
throw new Error('process.nextTick cannot be mocked inside child_process')

this._clock = this._fakeTimers.install({
now: Date.now(),
Expand Down
18 changes: 13 additions & 5 deletions packages/vitest/src/integrations/vi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,21 @@ function createVitest(): VitestUtils {

const utils: VitestUtils = {
useFakeTimers(config?: FakeTimerInstallOpts) {
if (config) {
_timers.configure(config)
const workerState = getWorkerState()

if (workerState.isChildProcess) {
if (config?.toFake?.includes('nextTick') || workerState.config?.fakeTimers?.toFake?.includes('nextTick')) {
throw new Error(
'vi.useFakeTimers({ toFake: ["nextTick"] }) is not supported in node:child_process. Use --threads if mocking nextTick is required.',
)
}
}
else {
const workerState = getWorkerState()

if (config)
_timers.configure(config)
else
_timers.configure(workerState.config.fakeTimers)
}

_timers.useFakeTimers()
return utils
},
Expand Down
Loading

0 comments on commit 8b4a44a

Please sign in to comment.