diff --git a/packages/vitest/src/node/cli-api.ts b/packages/vitest/src/node/cli-api.ts index b22932804ed9..993083fd297b 100644 --- a/packages/vitest/src/node/cli-api.ts +++ b/packages/vitest/src/node/cli-api.ts @@ -56,6 +56,9 @@ export async function startVitest( if (typeof options.browser === 'object' && !('enabled' in options.browser)) options.browser.enabled = true + if ('threads' in options && options.experimentalVmThreads) + throw new Error('Cannot use both "threads" (or "no-threads") and "experimentalVmThreads" at the same time.') + const ctx = await createVitest(mode, options, viteOverrides) if (mode === 'test' && ctx.config.coverage.enabled) { diff --git a/packages/vitest/src/node/cli.ts b/packages/vitest/src/node/cli.ts index 260fb0d8abd0..4b0d54923056 100644 --- a/packages/vitest/src/node/cli.ts +++ b/packages/vitest/src/node/cli.ts @@ -22,6 +22,7 @@ cli .option('--api [api]', 'Serve API, available options: --api.port , --api.host [host] and --api.strictPort') .option('--threads', 'Enabled threads (default: true)') .option('--single-thread', 'Run tests inside a single thread, requires --threads (default: false)') + .option('--experimental-vm-threads', 'Run tests in a worker pool using VM isolation (default: false)') .option('--silent', 'Silent console output from tests') .option('--hideSkippedTests', 'Hide logs for skipped tests') .option('--isolate', 'Isolate environment for each test file (default: true)') diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index 1f9810e762e2..3b6ecc0cfaf6 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -7,7 +7,7 @@ import type { Vitest } from './core' import { createChildProcessPool } from './pools/child' import { createThreadsPool } from './pools/threads' import { createBrowserPool } from './pools/browser' -import { createVmPool } from './pools/vm' +import { createVmThreadsPool } from './pools/vm-threads' import type { WorkspaceProject } from './workspace' export type WorkspaceSpec = [project: WorkspaceProject, testFile: string] @@ -31,12 +31,14 @@ export function createPool(ctx: Vitest): ProcessPool { child_process: null, threads: null, browser: null, - vm: null, + experimentalVmThreads: null, } - function getDefaultPoolName(project: WorkspaceProject) { + function getDefaultPoolName(project: WorkspaceProject): VitestPool { if (project.config.browser.enabled) return 'browser' + if (project.config.experimentalVmThreads) + return 'experimentalVmThreads' if (project.config.threads) return 'threads' return 'child_process' @@ -89,7 +91,7 @@ export function createPool(ctx: Vitest): ProcessPool { child_process: [] as WorkspaceSpec[], threads: [] as WorkspaceSpec[], browser: [] as WorkspaceSpec[], - vm: [] as WorkspaceSpec[], + experimentalVmThreads: [] as WorkspaceSpec[], } for (const spec of files) { @@ -106,9 +108,9 @@ export function createPool(ctx: Vitest): ProcessPool { return pools.browser.runTests(files, invalidate) } - if (pool === 'vm') { - pools.vm ??= createVmPool(ctx, options) - return pools.vm.runTests(files, invalidate) + if (pool === 'experimentalVmThreads') { + pools.experimentalVmThreads ??= createVmThreadsPool(ctx, options) + return pools.experimentalVmThreads.runTests(files, invalidate) } if (pool === 'threads') { diff --git a/packages/vitest/src/node/pools/vm.ts b/packages/vitest/src/node/pools/vm-threads.ts similarity index 91% rename from packages/vitest/src/node/pools/vm.ts rename to packages/vitest/src/node/pools/vm-threads.ts index a797c210f8ea..ee4a21a95d42 100644 --- a/packages/vitest/src/node/pools/vm.ts +++ b/packages/vitest/src/node/pools/vm-threads.ts @@ -36,7 +36,7 @@ function createWorkerChannel(project: WorkspaceProject) { return { workerPort, port } } -export function createVmPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool { +export function createVmThreadsPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions): ProcessPool { const threadsCount = ctx.config.watch ? Math.max(Math.floor(cpus().length / 2), 1) : Math.max(cpus().length - 1, 1) @@ -117,14 +117,6 @@ export function createVmPool(ctx: Vitest, { execArgv, env }: PoolProcessOptions) return config } - const workspaceMap = new Map() - for (const [project, file] of specs) { - const workspaceFiles = workspaceMap.get(file) ?? [] - workspaceFiles.push(project) - workspaceMap.set(file, workspaceFiles) - } - - // it's possible that project defines a file that is also defined by another project const { shard } = ctx.config if (shard) diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index c727eb1fd15c..5d6ca2f77732 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -18,7 +18,7 @@ export type { SequenceHooks, SequenceSetupFiles } from '@vitest/runner' export type BuiltinEnvironment = 'node' | 'jsdom' | 'happy-dom' | 'edge-runtime' // Record is used, so user can get intellisense for builtin environments, but still allow custom environments export type VitestEnvironment = BuiltinEnvironment | (string & Record) -export type VitestPool = 'browser' | 'threads' | 'child_process' | 'vm' +export type VitestPool = 'browser' | 'threads' | 'child_process' | 'experimentalVmThreads' export type CSSModuleScopeStrategy = 'stable' | 'scoped' | 'non-scoped' export type ApiConfig = Pick @@ -251,6 +251,16 @@ export interface InlineConfig { */ outputFile?: string | (Partial> & Record) + /** + * Run tests using VM context in a worker pool. + * + * This makes tests run faster, but VM module is unstable. Your tests might leak memory. + */ + experimentalVmThreads?: boolean + + // TODO: document that "--no-isolate" has no effect on experimentalVmThreads + // TODO: workerIdleMemoryLimit for experimentalVmThreads, so they don't leak + /** * Enable multi-threading * diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index 9a59f755e1e5..4e7c250903db 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -45,7 +45,7 @@ export default defineConfig({ './test/setup.ts', ], poolMatchGlobs: [ - ['**/*', 'vm'], + ['**/*', 'experimentalVmThreads'], ], testNamePattern: '^((?!does not include test that).)*$', coverage: {