@@ -115,7 +115,7 @@ interface TestRunEventMap {
115115 * Called when the test run has stopped, i.e. after the
116116 * done, abort or error events have been emitted.
117117 */
118- stop : void
118+ stop : EmptyObject
119119
120120 /**
121121 * Fired when a log entry is emitted by the test run.
@@ -125,13 +125,20 @@ interface TestRunEventMap {
125125
126126export class TestRun extends EventEmitter < TestRunEventMap > {
127127 #process: ChildProcessWithoutNullStreams
128+ #disposer = new AsyncDisposableStack ( )
128129
129130 #checks: Check [ ] = [ ]
130- #disposables: Array < AsyncDisposable | Disposable > = [ ]
131131
132- constructor ( process : ChildProcessWithoutNullStreams ) {
132+ constructor (
133+ process : ChildProcessWithoutNullStreams ,
134+ disposables : Array < AsyncDisposable | Disposable > = [ ]
135+ ) {
133136 super ( )
134137
138+ for ( const disposable of disposables ) {
139+ this . #disposer. use ( disposable )
140+ }
141+
135142 process . on ( 'spawn' , this . #handleStart)
136143
137144 process . on ( 'error' , this . #handleError)
@@ -168,26 +175,22 @@ export class TestRun extends EventEmitter<TestRunEventMap> {
168175
169176 this . #process = process
170177
171- this . on ( 'done' , this . #emitStop )
172- this . on ( 'abort' , this . #emitStop )
173- this . on ( 'error' , this . #emitStop )
178+ this . on ( 'done' , this . #handleStop )
179+ this . on ( 'abort' , this . #handleStop )
180+ this . on ( 'error' , this . #handleStop )
174181 }
175182
176183 isRunning ( ) : boolean {
177184 return this . #process. pid != undefined && this . #process. exitCode === null
178185 }
179186
180187 async stop ( ) : Promise < void > {
181- await Promise . all ( [
182- this . #kill( ) ,
183- ...this . #disposables. map ( ( disposable ) => {
184- if ( Symbol . asyncDispose in disposable ) {
185- return disposable [ Symbol . asyncDispose ] ( )
186- }
187-
188- return disposable [ Symbol . dispose ] ( )
189- } ) ,
190- ] )
188+ // If we encounter an error while disposing we assume the resources have already
189+ // been disposed and ignore the error. `disposeAsync` is idempotent so calling it
190+ // multiple times is safe.
191+ await Promise . allSettled ( [ this . #kill( ) , this . #disposer. disposeAsync ( ) ] )
192+
193+ this . emit ( 'stop' , { } )
191194 }
192195
193196 #kill( ) {
@@ -252,11 +255,13 @@ export class TestRun extends EventEmitter<TestRunEventMap> {
252255 }
253256 }
254257
255- #emitStop = ( ) => {
256- this . emit ( 'stop' , undefined )
257- }
258-
259- addDisposable ( disposable : AsyncDisposable | Disposable ) {
260- this . #disposables. push ( disposable )
258+ #handleStop = async ( ) => {
259+ try {
260+ await this . #disposer. disposeAsync ( )
261+ } catch ( error ) {
262+ console . error ( 'Error disposing children of test run' , error )
263+ } finally {
264+ this . emit ( 'stop' , { } )
265+ }
261266 }
262267}
0 commit comments