diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 6e69a8931b3a..38b01bfb36c9 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -16,7 +16,7 @@ import { } from '../constants' import { benchmarkConfigDefaults, configDefaults } from '../defaults' import { isCI, stdProvider, toArray } from '../utils' -import type { BuiltinPool } from '../types/pool-options' +import type { BuiltinPool, ForksOptions, PoolOptions, ThreadsOptions } from '../types/pool-options' import { getWorkersCountByPercentage } from '../utils/workers' import { VitestCache } from './cache' import { BaseSequencer } from './sequencers/BaseSequencer' @@ -98,6 +98,15 @@ export function resolveApiServerConfig( return api } +function resolveInlineWorkerOption(value: string | number): number { + if (typeof value === 'string' && value.trim().endsWith('%')) { + return getWorkersCountByPercentage(value) + } + else { + return Number(value) + } +} + export function resolveConfig( mode: VitestRunMode, options: UserConfig, @@ -177,21 +186,11 @@ export function resolveConfig( } if (resolved.maxWorkers) { - if (typeof options.maxWorkers === 'string' && options.maxWorkers.trim().endsWith('%')) { - resolved.maxWorkers = getWorkersCountByPercentage(options.maxWorkers) - } - else { - resolved.maxWorkers = Number(resolved.maxWorkers) - } + resolved.maxWorkers = resolveInlineWorkerOption(resolved.maxWorkers) } if (resolved.minWorkers) { - if (typeof options.minWorkers === 'string' && options.minWorkers.trim().endsWith('%')) { - resolved.minWorkers = getWorkersCountByPercentage(options.minWorkers) - } - else { - resolved.minWorkers = Number(resolved.minWorkers) - } + resolved.minWorkers = resolveInlineWorkerOption(resolved.minWorkers) } resolved.browser ??= {} as any @@ -447,6 +446,48 @@ export function resolveConfig( } } + const poolThreadsOptions = [ + ['threads', 'minThreads'], + ['threads', 'maxThreads'], + ['vmThreads', 'minThreads'], + ['vmThreads', 'maxThreads'], + ] as const satisfies [keyof PoolOptions, keyof ThreadsOptions][] + + for (const [poolOptionKey, workerOptionKey] of poolThreadsOptions) { + const workerInlineOption = resolved.poolOptions?.[poolOptionKey]?.[workerOptionKey] + + if (workerInlineOption) { + resolved.poolOptions = { + ...resolved.poolOptions, + [poolOptionKey]: { + ...resolved.poolOptions?.[poolOptionKey], + [workerOptionKey]: resolveInlineWorkerOption(workerInlineOption), + }, + } + } + } + + const poolForksOptions = [ + ['forks', 'minForks'], + ['forks', 'maxForks'], + ['vmForks', 'minForks'], + ['vmForks', 'maxForks'], + ] as const satisfies [keyof PoolOptions, keyof ForksOptions][] + + for (const [poolOptionKey, workerOptionKey] of poolForksOptions) { + const workerInlineOption = resolved.poolOptions?.[poolOptionKey]?.[workerOptionKey] + + if (workerInlineOption) { + resolved.poolOptions = { + ...resolved.poolOptions, + [poolOptionKey]: { + ...resolved.poolOptions?.[poolOptionKey], + [workerOptionKey]: resolveInlineWorkerOption(workerInlineOption), + }, + } + } + } + if (resolved.workspace) { // if passed down from the CLI and it's relative, resolve relative to CWD resolved.workspace diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 6c0ead31f2f7..6b230a80c18e 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -17,7 +17,7 @@ import type { SnapshotStateOptions } from './snapshot' import type { Arrayable, ParsedStack } from './general' import type { BenchmarkUserOptions } from './benchmark' import type { BrowserConfigOptions, ResolvedBrowserOptions } from './browser' -import type { Pool, PoolOptions } from './pool-options' +import type { Pool, PoolOptions, ResolvedPoolOptions } from './pool-options' export type { BrowserScript, BrowserConfigOptions } from './browser' export type { SequenceHooks, SequenceSetupFiles } from '@vitest/runner' @@ -969,7 +969,7 @@ export interface ResolvedConfig browser: ResolvedBrowserOptions pool: Pool - poolOptions?: PoolOptions + poolOptions?: ResolvedPoolOptions reporters: (InlineReporter | ReporterWithOptions)[] diff --git a/packages/vitest/src/types/pool-options.ts b/packages/vitest/src/types/pool-options.ts index 25e26c1d8cea..a3fbf8a20d0a 100644 --- a/packages/vitest/src/types/pool-options.ts +++ b/packages/vitest/src/types/pool-options.ts @@ -42,12 +42,19 @@ export interface PoolOptions extends Record { vmForks?: ForksOptions & VmOptions } +export interface ResolvedPoolOptions extends PoolOptions { + threads?: ResolvedThreadsOptions & WorkerContextOptions + forks?: ResolvedForksOptions & WorkerContextOptions + vmThreads?: ResolvedThreadsOptions & VmOptions + vmForks?: ResolvedForksOptions & VmOptions +} + export interface ThreadsOptions { /** Minimum amount of threads to use */ - minThreads?: number + minThreads?: number | string /** Maximum amount of threads to use */ - maxThreads?: number + maxThreads?: number | string /** * Run tests inside a single thread. @@ -66,12 +73,17 @@ export interface ThreadsOptions { useAtomics?: boolean } +export interface ResolvedThreadsOptions extends ThreadsOptions { + minThreads?: number + maxThreads?: number +} + export interface ForksOptions { /** Minimum amount of child processes to use */ - minForks?: number + minForks?: number | string /** Maximum amount of child processes to use */ - maxForks?: number + maxForks?: number | string /** * Run tests inside a single fork. @@ -81,6 +93,11 @@ export interface ForksOptions { singleFork?: boolean } +export interface ResolvedForksOptions extends ForksOptions { + minForks?: number + maxForks?: number +} + export interface WorkerContextOptions { /** * Isolate test environment by recycling `worker_threads` or `child_process` after each test diff --git a/test/config/test/workers-option.test.ts b/test/config/test/workers-option.test.ts index d3d952f82404..2b3a3168506c 100644 --- a/test/config/test/workers-option.test.ts +++ b/test/config/test/workers-option.test.ts @@ -31,3 +31,24 @@ test('workers percent argument should not throw error', async () => { expect(stderr).toBe('') }) + +test.each([ + { poolOption: 'threads' }, + { poolOption: 'vmThreads' }, + { poolOption: 'forks' }, + { poolOption: 'vmForks' }, +] as const)('workers percent argument in $poolOption should not throw error', async ({ poolOption }) => { + let workerOptions = {} + + if (poolOption.toLowerCase().includes('threads')) { + workerOptions = { maxThreads: '100%', minThreads: '10%' } + } + + if (poolOption.toLowerCase().includes('forks')) { + workerOptions = { maxForks: '100%', minForks: '10%' } + } + + const { stderr } = await runVitest({ poolOptions: { [poolOption]: workerOptions } }) + + expect(stderr).toBe('') +})