@@ -81,6 +81,14 @@ interface TestFunction extends TestBase {
81
81
isParameterized : boolean ;
82
82
}
83
83
84
+ type ParameterizedTestRecord = TestRecord & {
85
+ payload : {
86
+ kind : "function" ;
87
+ isParameterized : true ;
88
+ _testCases : TestCase [ ] ;
89
+ } ;
90
+ } ;
91
+
84
92
export interface TestCase {
85
93
id : string ;
86
94
displayName : string ;
@@ -249,6 +257,188 @@ export class SwiftTestingOutputParser {
249
257
: new UnixNamedPipeReader ( path ) ;
250
258
}
251
259
260
+ private parse ( item : SwiftTestEvent , runState : ITestRunState ) {
261
+ switch ( item . kind ) {
262
+ case "test" :
263
+ this . handleTestEvent ( item , runState ) ;
264
+ break ;
265
+ case "event" :
266
+ this . handleEventRecord ( item . payload , runState ) ;
267
+ break ;
268
+ }
269
+ }
270
+
271
+ private handleTestEvent ( item : TestRecord , runState : ITestRunState ) {
272
+ if ( this . isParameterizedFunction ( item ) ) {
273
+ this . handleParameterizedFunction ( item , runState ) ;
274
+ }
275
+ }
276
+
277
+ private handleEventRecord ( payload : EventRecordPayload , runState : ITestRunState ) {
278
+ switch ( payload . kind ) {
279
+ case "runStarted" :
280
+ this . handleRunStarted ( ) ;
281
+ break ;
282
+ case "testStarted" :
283
+ this . handleTestStarted ( payload , runState ) ;
284
+ break ;
285
+ case "testCaseStarted" :
286
+ this . handleTestCaseStarted ( payload , runState ) ;
287
+ break ;
288
+ case "testSkipped" :
289
+ this . handleTestSkipped ( payload , runState ) ;
290
+ break ;
291
+ case "issueRecorded" :
292
+ this . handleIssueRecorded ( payload , runState ) ;
293
+ break ;
294
+ case "testEnded" :
295
+ this . handleTestEnded ( payload , runState ) ;
296
+ break ;
297
+ case "testCaseEnded" :
298
+ this . handleTestCaseEnded ( payload , runState ) ;
299
+ break ;
300
+ case "_valueAttached" :
301
+ this . handleValueAttached ( payload , runState ) ;
302
+ break ;
303
+ }
304
+ }
305
+
306
+ private isParameterizedFunction ( item : TestRecord ) : item is ParameterizedTestRecord {
307
+ return (
308
+ item . kind === "test" &&
309
+ item . payload . kind === "function" &&
310
+ item . payload . isParameterized &&
311
+ ! ! item . payload . _testCases
312
+ ) ;
313
+ }
314
+
315
+ private handleParameterizedFunction ( item : ParameterizedTestRecord , runState : ITestRunState ) {
316
+ // Store a map of [Test ID, [Test Case ID, TestCase]] so we can quickly
317
+ // map an event.payload.testID back to a test case.
318
+ this . buildTestCaseMapForParameterizedTest ( item ) ;
319
+
320
+ const testIndex = this . testItemIndexFromTestID ( item . payload . id , runState ) ;
321
+ // If a test has test cases it is paramterized and we need to notify
322
+ // the caller that the TestClass should be added to the vscode.TestRun
323
+ // before it starts.
324
+ item . payload . _testCases
325
+ . map ( ( testCase , index ) =>
326
+ this . parameterizedFunctionTestCaseToTestClass (
327
+ item . payload . id ,
328
+ testCase ,
329
+ sourceLocationToVSCodeLocation (
330
+ item . payload . sourceLocation . _filePath ,
331
+ item . payload . sourceLocation . line ,
332
+ item . payload . sourceLocation . column
333
+ ) ,
334
+ index
335
+ )
336
+ )
337
+ . flatMap ( testClass => ( testClass ? [ testClass ] : [ ] ) )
338
+ . forEach ( testClass => this . addParameterizedTestCase ( testClass , testIndex ) ) ;
339
+ }
340
+
341
+ private handleRunStarted ( ) {
342
+ // Notify the runner that we've received all the test cases and
343
+ // are going to start running tests now.
344
+ this . testRunStarted ( ) ;
345
+ }
346
+
347
+ private handleTestStarted ( payload : TestStarted , runState : ITestRunState ) {
348
+ const testIndex = this . testItemIndexFromTestID ( payload . testID , runState ) ;
349
+ runState . started ( testIndex , payload . instant . absolute ) ;
350
+ }
351
+
352
+ private handleTestCaseStarted ( payload : TestCaseStarted , runState : ITestRunState ) {
353
+ const testID = this . idFromOptionalTestCase ( payload . testID , payload . _testCase ) ;
354
+ const testIndex = this . getTestCaseIndex ( runState , testID ) ;
355
+ runState . started ( testIndex , payload . instant . absolute ) ;
356
+ }
357
+
358
+ private handleTestSkipped ( payload : TestSkipped , runState : ITestRunState ) {
359
+ const testIndex = this . testItemIndexFromTestID ( payload . testID , runState ) ;
360
+ runState . skipped ( testIndex ) ;
361
+ }
362
+
363
+ private handleIssueRecorded ( payload : IssueRecorded , runState : ITestRunState ) {
364
+ const testID = this . idFromOptionalTestCase ( payload . testID , payload . _testCase ) ;
365
+ const testIndex = this . getTestCaseIndex ( runState , testID ) ;
366
+ const { isKnown, sourceLocation } = payload . issue ;
367
+ const location = sourceLocationToVSCodeLocation (
368
+ sourceLocation . _filePath ,
369
+ sourceLocation . line ,
370
+ sourceLocation . column
371
+ ) ;
372
+
373
+ const messages = this . transformIssueMessageSymbols ( payload . messages ) ;
374
+ const { issues, details } = this . partitionIssueMessages ( messages ) ;
375
+
376
+ // Order the details after the issue text.
377
+ const additionalDetails = details
378
+ . map ( message => MessageRenderer . render ( message ) )
379
+ . join ( "\n" ) ;
380
+
381
+ issues . forEach ( message => {
382
+ runState . recordIssue (
383
+ testIndex ,
384
+ additionalDetails . length > 0
385
+ ? `${ MessageRenderer . render ( message ) } \n${ additionalDetails } `
386
+ : MessageRenderer . render ( message ) ,
387
+ isKnown ,
388
+ location
389
+ ) ;
390
+ } ) ;
391
+
392
+ if ( payload . _testCase && testID !== payload . testID ) {
393
+ const testIndex = this . getTestCaseIndex ( runState , payload . testID ) ;
394
+ messages . forEach ( message => {
395
+ runState . recordIssue ( testIndex , message . text , isKnown , location ) ;
396
+ } ) ;
397
+ }
398
+ }
399
+
400
+ private handleTestEnded ( payload : TestEnded , runState : ITestRunState ) {
401
+ const testIndex = this . testItemIndexFromTestID ( payload . testID , runState ) ;
402
+
403
+ // When running a single test the testEnded and testCaseEnded events
404
+ // have the same ID, and so we'd end the same test twice.
405
+ if ( this . checkTestCompleted ( testIndex ) ) {
406
+ return ;
407
+ }
408
+ runState . completed ( testIndex , { timestamp : payload . instant . absolute } ) ;
409
+ }
410
+
411
+ private handleTestCaseEnded ( payload : TestCaseEnded , runState : ITestRunState ) {
412
+ const testID = this . idFromOptionalTestCase ( payload . testID , payload . _testCase ) ;
413
+ const testIndex = this . getTestCaseIndex ( runState , testID ) ;
414
+
415
+ // When running a single test the testEnded and testCaseEnded events
416
+ // have the same ID, and so we'd end the same test twice.
417
+ if ( this . checkTestCompleted ( testIndex ) ) {
418
+ return ;
419
+ }
420
+ runState . completed ( testIndex , { timestamp : payload . instant . absolute } ) ;
421
+ }
422
+
423
+ private handleValueAttached ( payload : ValueAttached , runState : ITestRunState ) {
424
+ if ( ! payload . _attachment . path ) {
425
+ return ;
426
+ }
427
+ const testID = this . idFromOptionalTestCase ( payload . testID ) ;
428
+ const testIndex = this . getTestCaseIndex ( runState , testID ) ;
429
+
430
+ this . onAttachment ( testIndex , payload . _attachment . path ) ;
431
+ }
432
+
433
+ private checkTestCompleted ( testIndex : number ) : boolean {
434
+ // If the test has already been completed, we don't need to do anything.
435
+ if ( this . completionMap . get ( testIndex ) ) {
436
+ return true ;
437
+ }
438
+ this . completionMap . set ( testIndex , true ) ;
439
+ return false ;
440
+ }
441
+
252
442
private testName ( id : string ) : string {
253
443
const nameMatcher = / ^ ( .* \( .* \) ) \/ ( .* ) \. s w i f t : \d + : \d + $ / ;
254
444
const matches = id . match ( nameMatcher ) ;
@@ -355,135 +545,6 @@ export class SwiftTestingOutputParser {
355
545
}
356
546
return id ;
357
547
}
358
-
359
- private parse ( item : SwiftTestEvent , runState : ITestRunState ) {
360
- if (
361
- item . kind === "test" &&
362
- item . payload . kind === "function" &&
363
- item . payload . isParameterized &&
364
- item . payload . _testCases
365
- ) {
366
- // Store a map of [Test ID, [Test Case ID, TestCase]] so we can quickly
367
- // map an event.payload.testID back to a test case.
368
- this . buildTestCaseMapForParameterizedTest ( item ) ;
369
-
370
- const testIndex = this . testItemIndexFromTestID ( item . payload . id , runState ) ;
371
- // If a test has test cases it is paramterized and we need to notify
372
- // the caller that the TestClass should be added to the vscode.TestRun
373
- // before it starts.
374
- item . payload . _testCases
375
- . map ( ( testCase , index ) =>
376
- this . parameterizedFunctionTestCaseToTestClass (
377
- item . payload . id ,
378
- testCase ,
379
- sourceLocationToVSCodeLocation (
380
- item . payload . sourceLocation . _filePath ,
381
- item . payload . sourceLocation . line ,
382
- item . payload . sourceLocation . column
383
- ) ,
384
- index
385
- )
386
- )
387
- . flatMap ( testClass => ( testClass ? [ testClass ] : [ ] ) )
388
- . forEach ( testClass => this . addParameterizedTestCase ( testClass , testIndex ) ) ;
389
- } else if ( item . kind === "event" ) {
390
- if ( item . payload . kind === "runStarted" ) {
391
- // Notify the runner that we've recieved all the test cases and
392
- // are going to start running tests now.
393
- this . testRunStarted ( ) ;
394
- return ;
395
- } else if ( item . payload . kind === "testStarted" ) {
396
- const testIndex = this . testItemIndexFromTestID ( item . payload . testID , runState ) ;
397
- runState . started ( testIndex , item . payload . instant . absolute ) ;
398
- return ;
399
- } else if ( item . payload . kind === "testCaseStarted" ) {
400
- const testID = this . idFromOptionalTestCase (
401
- item . payload . testID ,
402
- item . payload . _testCase
403
- ) ;
404
- const testIndex = this . getTestCaseIndex ( runState , testID ) ;
405
- runState . started ( testIndex , item . payload . instant . absolute ) ;
406
- return ;
407
- } else if ( item . payload . kind === "testSkipped" ) {
408
- const testIndex = this . testItemIndexFromTestID ( item . payload . testID , runState ) ;
409
- runState . skipped ( testIndex ) ;
410
- return ;
411
- } else if ( item . payload . kind === "issueRecorded" ) {
412
- const testID = this . idFromOptionalTestCase (
413
- item . payload . testID ,
414
- item . payload . _testCase
415
- ) ;
416
- const testIndex = this . getTestCaseIndex ( runState , testID ) ;
417
-
418
- const isKnown = item . payload . issue . isKnown ;
419
- const sourceLocation = item . payload . issue . sourceLocation ;
420
- const location = sourceLocationToVSCodeLocation (
421
- sourceLocation . _filePath ,
422
- sourceLocation . line ,
423
- sourceLocation . column
424
- ) ;
425
-
426
- const messages = this . transformIssueMessageSymbols ( item . payload . messages ) ;
427
- const { issues, details } = this . partitionIssueMessages ( messages ) ;
428
-
429
- // Order the details after the issue text.
430
- const additionalDetails = details
431
- . map ( message => MessageRenderer . render ( message ) )
432
- . join ( "\n" ) ;
433
-
434
- issues . forEach ( message => {
435
- runState . recordIssue (
436
- testIndex ,
437
- additionalDetails . length > 0
438
- ? `${ MessageRenderer . render ( message ) } \n${ additionalDetails } `
439
- : MessageRenderer . render ( message ) ,
440
- isKnown ,
441
- location
442
- ) ;
443
- } ) ;
444
-
445
- if ( item . payload . _testCase && testID !== item . payload . testID ) {
446
- const testIndex = this . getTestCaseIndex ( runState , item . payload . testID ) ;
447
- messages . forEach ( message => {
448
- runState . recordIssue ( testIndex , message . text , isKnown , location ) ;
449
- } ) ;
450
- }
451
- return ;
452
- } else if ( item . payload . kind === "testEnded" ) {
453
- const testIndex = this . testItemIndexFromTestID ( item . payload . testID , runState ) ;
454
-
455
- // When running a single test the testEnded and testCaseEnded events
456
- // have the same ID, and so we'd end the same test twice.
457
- if ( this . completionMap . get ( testIndex ) ) {
458
- return ;
459
- }
460
- this . completionMap . set ( testIndex , true ) ;
461
- runState . completed ( testIndex , { timestamp : item . payload . instant . absolute } ) ;
462
- return ;
463
- } else if ( item . payload . kind === "testCaseEnded" ) {
464
- const testID = this . idFromOptionalTestCase (
465
- item . payload . testID ,
466
- item . payload . _testCase
467
- ) ;
468
- const testIndex = this . getTestCaseIndex ( runState , testID ) ;
469
-
470
- // When running a single test the testEnded and testCaseEnded events
471
- // have the same ID, and so we'd end the same test twice.
472
- if ( this . completionMap . get ( testIndex ) ) {
473
- return ;
474
- }
475
- this . completionMap . set ( testIndex , true ) ;
476
- runState . completed ( testIndex , { timestamp : item . payload . instant . absolute } ) ;
477
- return ;
478
- } else if ( item . payload . kind === "_valueAttached" && item . payload . _attachment . path ) {
479
- const testID = this . idFromOptionalTestCase ( item . payload . testID ) ;
480
- const testIndex = this . getTestCaseIndex ( runState , testID ) ;
481
-
482
- this . onAttachment ( testIndex , item . payload . _attachment . path ) ;
483
- return ;
484
- }
485
- }
486
- }
487
548
}
488
549
489
550
export class MessageRenderer {
0 commit comments