Skip to content

Commit dacd73a

Browse files
committed
feat: add poolOrder option
1 parent 6555d61 commit dacd73a

File tree

5 files changed

+134
-39
lines changed

5 files changed

+134
-39
lines changed

docs/config/index.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -772,6 +772,58 @@ Please, be aware of these issues when using this option. Vitest team cannot fix
772772

773773
Similar as `vmThreads` pool but uses `child_process` instead of `worker_threads` via [tinypool](https://github.com/tinylibs/tinypool). Communication between tests and the main process is not as fast as with `vmThreads` pool. Process related APIs such as `process.chdir()` are available in `vmForks` pool. Please be aware that this pool has the same pitfalls listed in `vmThreads`.
774774

775+
### poolOrder <Version>3.2.0</Version> {#poolorder}
776+
777+
- **Type:** `number`
778+
- **Default:** `0`
779+
780+
The pool group order in which this project runs its tests. If not specified, all projects run in parallel.
781+
782+
This option only works if you have more than one project in your [workspace](/guide/workspace). If the same value is used in different projects, all those project will run together.
783+
784+
::: details Example
785+
Consider this example:
786+
787+
```ts
788+
import { defineConfig } from 'vitest/config'
789+
790+
export default defineConfig({
791+
test: {
792+
workspace: [
793+
{
794+
test: {
795+
name: 'slow',
796+
poolOrder: 0,
797+
},
798+
},
799+
{
800+
test: {
801+
name: 'fast',
802+
poolOrder: 0,
803+
},
804+
},
805+
{
806+
test: {
807+
name: 'flaky',
808+
poolOrder: 1,
809+
},
810+
},
811+
],
812+
},
813+
})
814+
```
815+
816+
Tests in these projects will run in this order:
817+
818+
```
819+
0. slow |
820+
|> running together
821+
0. fast |
822+
823+
1. flaky |> runs after slow and fast alone
824+
```
825+
:::
826+
775827
### poolOptions<NonProjectOption /> {#pooloptions}
776828

777829
- **Type:** `Record<'threads' | 'forks' | 'vmThreads' | 'vmForks', {}>`

packages/vitest/src/node/cli/cli-config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,7 @@ export const cliOptionsConfig: VitestCLIOptions = {
829829

830830
// disable CLI options
831831
cliExclude: null,
832+
poolOrder: null,
832833
server: null,
833834
setupFiles: null,
834835
globalSetup: null,

packages/vitest/src/node/config/resolveConfig.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,8 @@ export function resolveConfig(
482482

483483
resolved.pool ??= 'threads'
484484

485+
resolved.poolOrder ??= 0
486+
485487
if (process.env.VITEST_MAX_THREADS) {
486488
resolved.poolOptions = {
487489
...resolved.poolOptions,

packages/vitest/src/node/pool.ts

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export function createPool(ctx: Vitest): ProcessPool {
148148
}
149149
}
150150

151+
const poolConcurrentPromises = new Map<string, Promise<ProcessPool>>()
151152
const customPools = new Map<string, ProcessPool>()
152153
async function resolveCustomPool(filepath: string) {
153154
if (customPools.has(filepath)) {
@@ -178,26 +179,42 @@ export function createPool(ctx: Vitest): ProcessPool {
178179
return poolInstance as ProcessPool
179180
}
180181

181-
const filesByPool: Record<LocalPool, TestSpecification[]> = {
182-
forks: [],
183-
threads: [],
184-
vmThreads: [],
185-
vmForks: [],
186-
typescript: [],
182+
function getConcurrentPool(pool: string, fn: () => Promise<ProcessPool>) {
183+
if (poolConcurrentPromises.has(pool)) {
184+
return poolConcurrentPromises.get(pool)!
185+
}
186+
const promise = fn().finally(() => {
187+
poolConcurrentPromises.delete(pool)
188+
})
189+
poolConcurrentPromises.set(pool, promise)
190+
return promise
191+
}
192+
193+
function getCustomPool(pool: string) {
194+
return getConcurrentPool(pool, () => resolveCustomPool(pool))
195+
}
196+
197+
function getBrowserPool() {
198+
return getConcurrentPool('browser', async () => {
199+
const { createBrowserPool } = await import('@vitest/browser')
200+
return createBrowserPool(ctx)
201+
})
187202
}
188203

204+
const groupedSpecifications: TestSpecification[][] = []
205+
189206
const factories: Record<LocalPool, () => ProcessPool> = {
190207
vmThreads: () => createVmThreadsPool(ctx, options),
208+
vmForks: () => createVmForksPool(ctx, options),
191209
threads: () => createThreadsPool(ctx, options),
192210
forks: () => createForksPool(ctx, options),
193-
vmForks: () => createVmForksPool(ctx, options),
194211
typescript: () => createTypecheckPool(ctx),
195212
}
196213

197214
for (const spec of files) {
198-
const { pool } = spec[2]
199-
filesByPool[pool] ??= []
200-
filesByPool[pool].push(spec)
215+
const group = spec[0].config.poolOrder ?? 0
216+
groupedSpecifications[group] ??= []
217+
groupedSpecifications[group].push(spec)
201218
}
202219

203220
const Sequencer = ctx.config.sequence.sequencer
@@ -210,35 +227,48 @@ export function createPool(ctx: Vitest): ProcessPool {
210227
return sequencer.sort(specs)
211228
}
212229

213-
await Promise.all(
214-
Object.entries(filesByPool).map(async (entry) => {
215-
const [pool, files] = entry as [Pool, TestSpecification[]]
216-
217-
if (!files.length) {
218-
return null
219-
}
220-
221-
const specs = await sortSpecs(files)
222-
223-
if (pool in factories) {
224-
const factory = factories[pool]
225-
pools[pool] ??= factory()
226-
return pools[pool]![method](specs, invalidate)
227-
}
228-
229-
if (pool === 'browser') {
230-
pools[pool] ??= await (async () => {
231-
const { createBrowserPool } = await import('@vitest/browser')
232-
return createBrowserPool(ctx)
233-
})()
234-
return pools[pool]![method](specs, invalidate)
235-
}
236-
237-
const poolHandler = await resolveCustomPool(pool)
238-
pools[poolHandler.name] ??= poolHandler
239-
return poolHandler[method](specs, invalidate)
240-
}),
241-
)
230+
for (const specifications of groupedSpecifications) {
231+
const filesByPool: Record<LocalPool, TestSpecification[]> = {
232+
forks: [],
233+
threads: [],
234+
vmThreads: [],
235+
vmForks: [],
236+
typescript: [],
237+
}
238+
239+
specifications.forEach((specification) => {
240+
const pool = specification[2].pool
241+
filesByPool[pool] ??= []
242+
filesByPool[pool].push(specification)
243+
})
244+
245+
await Promise.all(
246+
Object.entries(filesByPool).map(async (entry) => {
247+
const [pool, files] = entry as [Pool, TestSpecification[]]
248+
249+
if (!files.length) {
250+
return null
251+
}
252+
253+
const specs = await sortSpecs(files)
254+
255+
if (pool in factories) {
256+
const factory = factories[pool]
257+
pools[pool] ??= factory()
258+
return pools[pool]![method](specs, invalidate)
259+
}
260+
261+
if (pool === 'browser') {
262+
pools.browser ??= await getBrowserPool()
263+
return pools.browser[method](specs, invalidate)
264+
}
265+
266+
const poolHandler = await getCustomPool(pool)
267+
pools[poolHandler.name] ??= poolHandler
268+
return poolHandler[method](specs, invalidate)
269+
}),
270+
)
271+
}
242272
}
243273

244274
return {

packages/vitest/src/node/types/config.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,16 @@ export interface InlineConfig {
349349
*/
350350
poolOptions?: PoolOptions
351351

352+
/**
353+
* The pool group order in which this project runs its tests.
354+
* If not specified, all projects run in parallel.
355+
*
356+
* You can group tests in certain projects to run together and delay others.
357+
*
358+
* @default 0
359+
*/
360+
poolOrder?: number
361+
352362
/**
353363
* Maximum number or percentage of workers to run tests in. `poolOptions.{threads,vmThreads}.maxThreads`/`poolOptions.forks.maxForks` has higher priority.
354364
*/

0 commit comments

Comments
 (0)