Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/vitest/src/node/pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ export function createPool(ctx: Vitest): ProcessPool {
invalidates,
providedContext: project.getProvidedContext(),
workerId: workerId++,
environment,
},
environment,
project,
env,
execArgv,
Expand Down
55 changes: 47 additions & 8 deletions packages/vitest/src/node/pools/pool.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ContextTestEnvironment } from '../../types/worker'
import type { Logger } from '../logger'
import type { StateManager } from '../state'
import type { PoolOptions, PoolTask, WorkerResponse } from './types'
Expand Down Expand Up @@ -213,15 +214,17 @@ export class Pool {
const index = this.sharedRunners.findIndex(runner => isEqualRunner(runner, task))

if (index !== -1) {
return this.sharedRunners.splice(index, 1)[0]
const runner = this.sharedRunners.splice(index, 1)[0]
runner.reconfigure(task)
return runner
}
}

const options: PoolOptions = {
distPath: this.options.distPath,
project: task.project,
method,
environment: task.environment,
environment: task.context.environment,
env: task.env,
execArgv: task.execArgv,
}
Expand Down Expand Up @@ -289,11 +292,47 @@ function isEqualRunner(runner: PoolRunner, task: PoolTask) {
if (task.isolate) {
throw new Error('Isolated tasks should not share runners')
}
if (runner.worker.name !== task.worker || runner.project !== task.project) {
return false
}
// by default, check that the environments are the same
// some workers (like vmThreads/vmForks) do not need this check
if (!runner.worker.canReuse) {
return isEnvironmentEqual(task.context.environment, runner.environment)
}
return runner.worker.canReuse(task)
}

function isEnvironmentEqual(env1: ContextTestEnvironment, env2: ContextTestEnvironment): boolean {
if (env1.name !== env2.name) {
return false
}
return deepEqual(env1.options, env2.options)
}
Comment on lines +306 to +311
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks the same as

function isEqualEnvironments(a: TestSpecification, b: TestSpecification) {
const aEnv = environments.get(a)
const bEnv = environments.get(b)
if (!aEnv && !bEnv) {
return true
}
if (!aEnv || !bEnv || aEnv.name !== bEnv.name) {
return false
}
if (!aEnv.options && !bEnv.options) {
return true
}
if (!aEnv.options || !bEnv.options) {
return false
}
return getSerializedOptions(aEnv) === getSerializedOptions(bEnv)
}


function deepEqual(obj1: any, obj2: any): boolean {
if (obj1 === obj2) {
return true
}
if (obj1 == null || obj2 == null) {
return obj1 === obj2
}
if (typeof obj1 !== 'object' || typeof obj2 !== 'object') {
return false
}

const keys1 = Object.keys(obj1)
const keys2 = Object.keys(obj2)

if (keys1.length !== keys2.length) {
return false
}

for (const key of keys1) {
if (!keys2.includes(key) || !deepEqual(obj1[key], obj2[key])) {
return false
}
}

return (
runner.worker.name === task.worker
&& runner.project === task.project
&& runner.environment.name === task.environment.name
&& (!runner.worker.canReuse || runner.worker.canReuse(task))
)
return true
}
13 changes: 11 additions & 2 deletions packages/vitest/src/node/pools/poolRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type { RunnerRPC, RuntimeRPC } from '../../types/rpc'
import type { ContextTestEnvironment, WorkerExecuteContext } from '../../types/worker'
import type { Traces } from '../../utils/traces'
import type { TestProject } from '../project'
import type { PoolOptions, PoolRunnerOTEL, PoolWorker, WorkerRequest, WorkerResponse } from './types'
import type { PoolOptions, PoolRunnerOTEL, PoolTask, PoolWorker, WorkerRequest, WorkerResponse } from './types'
import { EventEmitter } from 'node:events'
import { createDefer } from '@vitest/utils/helpers'
import { createBirpc } from 'birpc'
Expand Down Expand Up @@ -43,7 +43,7 @@ export class PoolRunner {
public poolId: number | undefined = undefined

public readonly project: TestProject
public readonly environment: ContextTestEnvironment
public environment: ContextTestEnvironment

private _state: RunnerState = RunnerState.IDLE
private _operationLock: DeferPromise<void> | null = null
Expand Down Expand Up @@ -113,6 +113,15 @@ export class PoolRunner {
this._offCancel = vitest.onCancel(reason => this._rpc.onCancel(reason))
}

/**
* "reconfigure" can only be called if `environment` is different, since different project always
* requires a new PoolRunner instance.
*/
public reconfigure(task: PoolTask): void {
this.environment = task.context.environment
this._otel?.span.setAttribute('vitest.environment', this.environment.name)
}

postMessage(message: WorkerRequest): void {
// Only send messages when runner is active (not fully stopped)
// Allow sending during STOPPING state for the 'stop' message itself
Expand Down
3 changes: 1 addition & 2 deletions packages/vitest/src/node/pools/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export interface PoolWorker {

/**
* This is called on workers that already satisfy certain constraints:
* - The task has the same worker name
* - The task has the same project
* - The task has the same environment
*/
canReuse?: (task: PoolTask) => boolean
}
Expand All @@ -55,7 +55,6 @@ export interface PoolTask {
*/
execArgv: string[]
context: WorkerExecuteContext
environment: ContextTestEnvironment
memoryLimit: number | null
}

Expand Down
4 changes: 4 additions & 0 deletions packages/vitest/src/node/pools/workers/vmForksWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ export class VmForksPoolWorker extends ForksPoolWorker {
/** Loads {@link file://./../../../runtime/workers/vmForks.ts} */
this.entrypoint = resolve(options.distPath, 'workers/vmForks.js')
}

canReuse(): boolean {
return true
}
}
4 changes: 4 additions & 0 deletions packages/vitest/src/node/pools/workers/vmThreadsWorker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ export class VmThreadsPoolWorker extends ThreadsPoolWorker {
/** Loads {@link file://./../../../runtime/workers/vmThreads.ts} */
this.entrypoint = resolve(options.distPath, 'workers/vmThreads.js')
}

canReuse(): boolean {
return true
}
}
1 change: 1 addition & 0 deletions packages/vitest/src/types/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface WorkerExecuteContext {
files: FileSpecification[]
providedContext: Record<string, any>
invalidates?: string[]
environment: ContextTestEnvironment

/** Exposed to test runner as `VITEST_WORKER_ID`. Value is unique per each isolated worker. */
workerId: number
Expand Down
Loading