(options?: StopOptions)
| 244 | } |
| 245 | |
| 246 | async stop(options?: StopOptions): Promise<void> { |
| 247 | // Wait for any ongoing operation to complete |
| 248 | if (this._operationLock) { |
| 249 | await this._operationLock |
| 250 | } |
| 251 | |
| 252 | if (this._state === RunnerState.STOPPED || this._state === RunnerState.STOPPING) { |
| 253 | return |
| 254 | } |
| 255 | |
| 256 | this._otel?.span.setAttribute('vitest.worker.files', this._otel.files) |
| 257 | |
| 258 | if (this._state === RunnerState.IDLE) { |
| 259 | this._otel?.span.end() |
| 260 | this._state = RunnerState.STOPPED |
| 261 | return |
| 262 | } |
| 263 | |
| 264 | // Create operation lock to prevent concurrent start/stop |
| 265 | this._operationLock = createDefer() |
| 266 | |
| 267 | try { |
| 268 | this._state = RunnerState.STOPPING |
| 269 | |
| 270 | // Remove exit listener early to avoid "unexpected exit" errors during shutdown |
| 271 | this.worker.off('exit', this.emitUnexpectedExit) |
| 272 | |
| 273 | const stopSpan = this.startTracesSpan('vitest.worker.stop') |
| 274 | await this.withTimeout( |
| 275 | new Promise<void>((resolve) => { |
| 276 | const onStop = (response: WorkerResponse) => { |
| 277 | if (response.type === 'stopped') { |
| 278 | if (response.error) { |
| 279 | stopSpan.recordException(response.error as Error) |
| 280 | this.project.vitest.state.catchError( |
| 281 | response.error, |
| 282 | 'Teardown Error', |
| 283 | ) |
| 284 | } |
| 285 | |
| 286 | resolve() |
| 287 | this.off('message', onStop) |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | // Don't wait for graceful exit's response when force exiting |
| 292 | if (options?.force) { |
| 293 | return onStop({ type: 'stopped', __vitest_worker_response__: true }) |
| 294 | } |
| 295 | |
| 296 | this.on('message', onStop) |
| 297 | this.postMessage({ |
| 298 | type: 'stop', |
| 299 | __vitest_worker_request__: true, |
| 300 | otelCarrier: this.getOTELCarrier(), |
| 301 | }) |
| 302 | }), |
| 303 | STOP_TIMEOUT, |
no test coverage detected