@@ -39,6 +39,7 @@ import { TestCoverage } from "../coverage/LcovResults";
39
39
import { BuildConfigurationFactory , TestingConfigurationFactory } from "../debugger/buildConfig" ;
40
40
import { TestKind , isDebugging , isRelease } from "./TestKind" ;
41
41
import { reduceTestItemChildren } from "./TestUtils" ;
42
+ import { CompositeCancellationToken } from "../utilities/cancellation" ;
42
43
43
44
export enum TestLibrary {
44
45
xctest = "XCTest" ,
@@ -65,6 +66,7 @@ export class TestRunProxy {
65
66
private _testItems : vscode . TestItem [ ] ;
66
67
private iteration : number | undefined ;
67
68
public coverage : TestCoverage ;
69
+ public token : CompositeCancellationToken ;
68
70
69
71
public testRunCompleteEmitter = new vscode . EventEmitter < void > ( ) ;
70
72
public onTestRunComplete : vscode . Event < void > ;
@@ -87,14 +89,20 @@ export class TestRunProxy {
87
89
return this . _testItems ;
88
90
}
89
91
92
+ public get isCancellationRequested ( ) : boolean {
93
+ return this . token . isCancellationRequested ;
94
+ }
95
+
90
96
constructor (
91
97
private testRunRequest : vscode . TestRunRequest ,
92
98
private controller : vscode . TestController ,
93
99
private args : TestRunArguments ,
94
- private folderContext : FolderContext
100
+ private folderContext : FolderContext ,
101
+ testProfileCancellationToken : vscode . CancellationToken
95
102
) {
96
103
this . _testItems = args . testItems ;
97
104
this . coverage = new TestCoverage ( folderContext ) ;
105
+ this . token = new CompositeCancellationToken ( testProfileCancellationToken ) ;
98
106
this . onTestRunComplete = this . testRunCompleteEmitter . event ;
99
107
}
100
108
@@ -144,6 +152,7 @@ export class TestRunProxy {
144
152
} ) ;
145
153
146
154
this . testRun = this . controller . createTestRun ( this . testRunRequest ) ;
155
+ this . token . add ( this . testRun . token ) ;
147
156
this . _testItems = [ ...this . testItems , ...addedTestItems ] ;
148
157
149
158
// Forward any output captured before the testRun was created.
@@ -217,6 +226,7 @@ export class TestRunProxy {
217
226
}
218
227
this . testRun ?. end ( ) ;
219
228
this . testRunCompleteEmitter . fire ( ) ;
229
+ this . token . dispose ( ) ;
220
230
}
221
231
222
232
public setIteration ( iteration : number ) {
@@ -288,13 +298,14 @@ export class TestRunner {
288
298
private testKind : TestKind ,
289
299
private request : vscode . TestRunRequest ,
290
300
private folderContext : FolderContext ,
291
- private controller : vscode . TestController
301
+ private controller : vscode . TestController ,
302
+ token : vscode . CancellationToken
292
303
) {
293
304
this . testArgs = new TestRunArguments (
294
305
this . ensureRequestIncludesTests ( this . request ) ,
295
306
isDebugging ( testKind )
296
307
) ;
297
- this . testRun = new TestRunProxy ( request , controller , this . testArgs , folderContext ) ;
308
+ this . testRun = new TestRunProxy ( request , controller , this . testArgs , folderContext , token ) ;
298
309
this . xcTestOutputParser =
299
310
testKind === TestKind . parallel
300
311
? new ParallelXCTestOutputParser (
@@ -358,10 +369,11 @@ export class TestRunner {
358
369
TestKind . standard ,
359
370
request ,
360
371
folderContext ,
361
- controller
372
+ controller ,
373
+ token
362
374
) ;
363
375
onCreateTestRun . fire ( runner . testRun ) ;
364
- await runner . runHandler ( token ) ;
376
+ await runner . runHandler ( ) ;
365
377
} ,
366
378
true ,
367
379
runnableTag
@@ -374,10 +386,11 @@ export class TestRunner {
374
386
TestKind . parallel ,
375
387
request ,
376
388
folderContext ,
377
- controller
389
+ controller ,
390
+ token
378
391
) ;
379
392
onCreateTestRun . fire ( runner . testRun ) ;
380
- await runner . runHandler ( token ) ;
393
+ await runner . runHandler ( ) ;
381
394
} ,
382
395
false ,
383
396
runnableTag
@@ -390,10 +403,11 @@ export class TestRunner {
390
403
TestKind . release ,
391
404
request ,
392
405
folderContext ,
393
- controller
406
+ controller ,
407
+ token
394
408
) ;
395
409
onCreateTestRun . fire ( runner . testRun ) ;
396
- await runner . runHandler ( token ) ;
410
+ await runner . runHandler ( ) ;
397
411
} ,
398
412
false ,
399
413
runnableTag
@@ -407,15 +421,16 @@ export class TestRunner {
407
421
TestKind . coverage ,
408
422
request ,
409
423
folderContext ,
410
- controller
424
+ controller ,
425
+ token
411
426
) ;
412
427
onCreateTestRun . fire ( runner . testRun ) ;
413
428
if ( request . profile ) {
414
429
request . profile . loadDetailedCoverage = async ( testRun , fileCoverage ) => {
415
430
return runner . testRun . coverage . loadDetailedCoverage ( fileCoverage . uri ) ;
416
431
} ;
417
432
}
418
- await runner . runHandler ( token ) ;
433
+ await runner . runHandler ( ) ;
419
434
await vscode . commands . executeCommand ( "testing.openCoverage" ) ;
420
435
} ,
421
436
false ,
@@ -430,10 +445,11 @@ export class TestRunner {
430
445
TestKind . debug ,
431
446
request ,
432
447
folderContext ,
433
- controller
448
+ controller ,
449
+ token
434
450
) ;
435
451
onCreateTestRun . fire ( runner . testRun ) ;
436
- await runner . runHandler ( token ) ;
452
+ await runner . runHandler ( ) ;
437
453
} ,
438
454
false ,
439
455
runnableTag
@@ -446,10 +462,11 @@ export class TestRunner {
446
462
TestKind . debugRelease ,
447
463
request ,
448
464
folderContext ,
449
- controller
465
+ controller ,
466
+ token
450
467
) ;
451
468
onCreateTestRun . fire ( runner . testRun ) ;
452
- await runner . runHandler ( token ) ;
469
+ await runner . runHandler ( ) ;
453
470
} ,
454
471
false ,
455
472
runnableTag
@@ -463,13 +480,18 @@ export class TestRunner {
463
480
* @param token Cancellation token
464
481
* @returns When complete
465
482
*/
466
- async runHandler ( token : vscode . CancellationToken ) {
483
+ async runHandler ( ) {
467
484
const runState = new TestRunnerTestRunState ( this . testRun ) ;
485
+
486
+ const cancellationDisposable = this . testRun . token . onCancellationRequested ( ( ) => {
487
+ this . testRun . appendOutput ( "\r\nTest run cancelled." ) ;
488
+ } ) ;
489
+
468
490
try {
469
491
if ( isDebugging ( this . testKind ) ) {
470
- await this . debugSession ( token , runState ) ;
492
+ await this . debugSession ( runState ) ;
471
493
} else {
472
- await this . runSession ( token , runState ) ;
494
+ await this . runSession ( runState ) ;
473
495
}
474
496
} catch ( error ) {
475
497
this . workspaceContext . outputChannel . log ( `Error: ${ getErrorDescription ( error ) } ` ) ;
@@ -481,14 +503,12 @@ export class TestRunner {
481
503
await this . testRun . computeCoverage ( ) ;
482
504
}
483
505
506
+ cancellationDisposable . dispose ( ) ;
484
507
await this . testRun . end ( ) ;
485
508
}
486
509
487
510
/** Run test session without attaching to a debugger */
488
- async runSession (
489
- token : vscode . CancellationToken ,
490
- runState : TestRunnerTestRunState
491
- ) : Promise < TestRunState > {
511
+ async runSession ( runState : TestRunnerTestRunState ) : Promise < TestRunState > {
492
512
// Run swift-testing first, then XCTest.
493
513
// swift-testing being parallel by default should help these run faster.
494
514
if ( this . testArgs . hasSwiftTestingTests ) {
@@ -509,13 +529,7 @@ export class TestRunner {
509
529
true
510
530
) ;
511
531
512
- if ( testBuildConfig === null ) {
513
- return this . testRun . runState ;
514
- }
515
-
516
- const outputStream = this . testOutputWritable ( TestLibrary . swiftTesting , runState ) ;
517
- if ( token . isCancellationRequested ) {
518
- outputStream . end ( ) ;
532
+ if ( testBuildConfig === null || this . testRun . isCancellationRequested ) {
519
533
return this . testRun . runState ;
520
534
}
521
535
@@ -526,8 +540,7 @@ export class TestRunner {
526
540
await this . launchTests (
527
541
runState ,
528
542
this . testKind === TestKind . parallel ? TestKind . standard : this . testKind ,
529
- token ,
530
- outputStream ,
543
+ this . testOutputWritable ( TestLibrary . swiftTesting , runState ) ,
531
544
testBuildConfig ,
532
545
TestLibrary . swiftTesting
533
546
) ;
@@ -541,13 +554,8 @@ export class TestRunner {
541
554
this . testArgs . xcTestArgs ,
542
555
true
543
556
) ;
544
- if ( testBuildConfig === null ) {
545
- return this . testRun . runState ;
546
- }
547
557
548
- const parsedOutputStream = this . testOutputWritable ( TestLibrary . xctest , runState ) ;
549
- if ( token . isCancellationRequested ) {
550
- parsedOutputStream . end ( ) ;
558
+ if ( testBuildConfig === null || this . testRun . isCancellationRequested ) {
551
559
return this . testRun . runState ;
552
560
}
553
561
@@ -557,8 +565,7 @@ export class TestRunner {
557
565
await this . launchTests (
558
566
runState ,
559
567
this . testKind ,
560
- token ,
561
- parsedOutputStream ,
568
+ this . testOutputWritable ( TestLibrary . xctest , runState ) ,
562
569
testBuildConfig ,
563
570
TestLibrary . xctest
564
571
) ;
@@ -570,26 +577,20 @@ export class TestRunner {
570
577
private async launchTests (
571
578
runState : TestRunnerTestRunState ,
572
579
testKind : TestKind ,
573
- token : vscode . CancellationToken ,
574
580
outputStream : stream . Writable ,
575
581
testBuildConfig : vscode . DebugConfiguration ,
576
582
testLibrary : TestLibrary
577
583
) {
578
584
try {
579
585
switch ( testKind ) {
580
586
case TestKind . coverage :
581
- await this . runCoverageSession (
582
- token ,
583
- outputStream ,
584
- testBuildConfig ,
585
- testLibrary
586
- ) ;
587
+ await this . runCoverageSession ( outputStream , testBuildConfig , testLibrary ) ;
587
588
break ;
588
589
case TestKind . parallel :
589
- await this . runParallelSession ( token , outputStream , testBuildConfig , runState ) ;
590
+ await this . runParallelSession ( outputStream , testBuildConfig , runState ) ;
590
591
break ;
591
592
default :
592
- await this . runStandardSession ( token , outputStream , testBuildConfig , testKind ) ;
593
+ await this . runStandardSession ( outputStream , testBuildConfig , testKind ) ;
593
594
break ;
594
595
}
595
596
} catch ( error ) {
@@ -613,7 +614,6 @@ export class TestRunner {
613
614
614
615
/** Run tests outside of debugger */
615
616
async runStandardSession (
616
- token : vscode . CancellationToken ,
617
617
outputStream : stream . Writable ,
618
618
testBuildConfig : vscode . DebugConfiguration ,
619
619
testKind : TestKind
@@ -679,19 +679,21 @@ export class TestRunner {
679
679
}
680
680
} ) ;
681
681
682
- this . folderContext . taskQueue . queueOperation ( new TaskOperation ( task ) , token ) ;
682
+ this . folderContext . taskQueue . queueOperation (
683
+ new TaskOperation ( task ) ,
684
+ this . testRun . token
685
+ ) ;
683
686
} ) ;
684
687
}
685
688
686
689
/** Run tests with code coverage, and parse coverage results */
687
690
async runCoverageSession (
688
- token : vscode . CancellationToken ,
689
691
outputStream : stream . Writable ,
690
692
testBuildConfig : vscode . DebugConfiguration ,
691
693
testLibrary : TestLibrary
692
694
) {
693
695
try {
694
- await this . runStandardSession ( token , outputStream , testBuildConfig , TestKind . coverage ) ;
696
+ await this . runStandardSession ( outputStream , testBuildConfig , TestKind . coverage ) ;
695
697
} catch ( error ) {
696
698
// If this isn't a standard test failure, forward the error and skip generating coverage.
697
699
if ( error !== 1 ) {
@@ -704,7 +706,6 @@ export class TestRunner {
704
706
705
707
/** Run tests in parallel outside of debugger */
706
708
async runParallelSession (
707
- token : vscode . CancellationToken ,
708
709
outputStream : stream . Writable ,
709
710
testBuildConfig : vscode . DebugConfiguration ,
710
711
runState : TestRunnerTestRunState
@@ -714,7 +715,6 @@ export class TestRunner {
714
715
715
716
try {
716
717
testBuildConfig . args = await this . runStandardSession (
717
- token ,
718
718
outputStream ,
719
719
{
720
720
...testBuildConfig ,
@@ -743,13 +743,13 @@ export class TestRunner {
743
743
}
744
744
745
745
/** Run test session inside debugger */
746
- async debugSession ( token : vscode . CancellationToken , runState : TestRunnerTestRunState ) {
746
+ async debugSession ( runState : TestRunnerTestRunState ) {
747
747
// Perform a build all first to produce the binaries we'll run later.
748
748
let buildOutput = "" ;
749
749
try {
750
750
await this . runStandardSession (
751
- token ,
752
- // discard the output as we dont want to associate it with the test run.
751
+ // Capture the output to print it in case of a build error.
752
+ // We dont want to associate it with the test run.
753
753
new stream . Writable ( {
754
754
write : ( chunk , encoding , next ) => {
755
755
buildOutput += chunk . toString ( ) ;
@@ -846,6 +846,11 @@ export class TestRunner {
846
846
const debugRuns = validBuildConfigs . map ( config => {
847
847
return ( ) =>
848
848
new Promise < void > ( ( resolve , reject ) => {
849
+ if ( this . testRun . isCancellationRequested ) {
850
+ resolve ( ) ;
851
+ return ;
852
+ }
853
+
849
854
// add cancelation
850
855
const startSession = vscode . debug . onDidStartDebugSession ( session => {
851
856
if ( config . testType === TestLibrary . xctest ) {
@@ -862,12 +867,13 @@ export class TestRunner {
862
867
outputHandler ( output ) ;
863
868
} ) ;
864
869
865
- const cancellation = token . onCancellationRequested ( ( ) => {
870
+ const cancellation = this . testRun . token . onCancellationRequested ( ( ) => {
866
871
this . workspaceContext . outputChannel . logDiagnostic (
867
872
"Test Debugging Cancelled" ,
868
873
this . folderContext . name
869
874
) ;
870
875
vscode . debug . stopDebugging ( session ) ;
876
+ resolve ( ) ;
871
877
} ) ;
872
878
subscriptions . push ( cancellation ) ;
873
879
} ) ;
0 commit comments