Skip to content

Commit ed0a9d8

Browse files
committed
Use swift-testing stdout as test results log verbatim
Instead of reconstructing the swift-testing output from a combination of stdout and the JSON event stream, simply pass stdout through directly and use that as the test results log. Use the JSON event stream to drive test pass/fail/skip/issue/etc behaviour. This resolves a race condition between the JSON event stream and stdout interleaving output in an incorrect order. This approach has the drawback of no longer associating individual lines of output derived from the JSON event stream with a specific test, however this was offering limited benefit since user output wasn't captured in this way. Once every line of output can be accounted for in the JSON event stream we can add this functionality back in with user output included. Issue: #1054
1 parent 303d4d2 commit ed0a9d8

File tree

2 files changed

+11
-66
lines changed

2 files changed

+11
-66
lines changed

src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts

+8-57
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
} from "./TestEventStreamReader";
2323
import { ITestRunState } from "./TestRunState";
2424
import { TestClass } from "../TestDiscovery";
25-
import { regexEscapedString, sourceLocationToVSCodeLocation } from "../../utilities/utilities";
25+
import { sourceLocationToVSCodeLocation } from "../../utilities/utilities";
2626
import { exec } from "child_process";
2727

2828
// All events produced by a swift-testing run will be one of these three types.
@@ -222,44 +222,13 @@ export class SwiftTestingOutputParser {
222222
* a JSON event and injecting them in to the test run output.
223223
* @param chunk A chunk of stdout emitted during a test run.
224224
*/
225-
public parseStdout = (() => {
226-
const values = [
227-
...Object.values(TestSymbol)
228-
.filter(symbol => symbol !== TestSymbol.none)
229-
.map(symbol =>
230-
regexEscapedString(
231-
// Trim the ANSI reset code from the search since some lines
232-
// are fully colorized from the symbol to the end of line.
233-
SymbolRenderer.eventMessageSymbol(symbol).replace(
234-
SymbolRenderer.resetANSIEscapeCode,
235-
""
236-
)
237-
)
238-
),
239-
// It is possible there is no symbol for a line produced by swift-testing,
240-
// for instance if the user has a multi line comment before a failing expectation
241-
// only the first line of the printed comment will have a symbol, but to make the
242-
// indentation consistent the subsequent lines will have three spaces. We don't want
243-
// to treat this as output produced by the user during the test run, so omit these.
244-
// This isn't ideal since this will swallow lines the user prints if they start with
245-
// three spaces, but until we have user output as part of the JSON event stream we have
246-
// this workaround.
247-
" ",
248-
];
249-
250-
// Build a regex of all the line beginnings that come out of swift-testing events.
251-
const isSwiftTestingLineBeginning = new RegExp(`^${values.join("|")}`);
252-
253-
return (chunk: string, runState: ITestRunState) => {
254-
for (const line of chunk.split("\n")) {
255-
// Any line in stdout that fails to match as a swift-testing line is treated
256-
// as a user printed value and recorded to the test run output with no associated test.
257-
if (line.trim().length > 0 && isSwiftTestingLineBeginning.test(line) === false) {
258-
runState.recordOutput(undefined, `${line}\r\n`);
259-
}
225+
public parseStdout(chunk: string, runState: ITestRunState) {
226+
for (const line of chunk.split("\n")) {
227+
if (line.trim().length > 0) {
228+
runState.recordOutput(undefined, `${line}\r\n`);
260229
}
261-
};
262-
})();
230+
}
231+
}
263232

264233
private createReader(path: string): INamedPipeReader {
265234
return process.platform === "win32"
@@ -328,16 +297,6 @@ export class SwiftTestingOutputParser {
328297
return fullNameIndex;
329298
}
330299

331-
private recordOutput(
332-
runState: ITestRunState,
333-
messages: EventMessage[],
334-
testIndex: number | undefined
335-
) {
336-
messages.forEach(message => {
337-
runState.recordOutput(testIndex, `${MessageRenderer.render(message)}\r\n`);
338-
});
339-
}
340-
341300
/**
342301
* Partitions a collection of messages in to issues and details about the issues.
343302
* This is used to print the issues first, followed by the details.
@@ -411,11 +370,11 @@ export class SwiftTestingOutputParser {
411370
// Notify the runner that we've recieved all the test cases and
412371
// are going to start running tests now.
413372
this.testRunStarted();
373+
return;
414374
} else if (item.payload.kind === "testStarted") {
415375
const testName = this.testName(item.payload.testID);
416376
const testIndex = runState.getTestItemIndex(testName, undefined);
417377
runState.started(testIndex, item.payload.instant.absolute);
418-
this.recordOutput(runState, item.payload.messages, testIndex);
419378
return;
420379
} else if (item.payload.kind === "testCaseStarted") {
421380
const testID = this.idFromOptionalTestCase(
@@ -424,13 +383,11 @@ export class SwiftTestingOutputParser {
424383
);
425384
const testIndex = this.getTestCaseIndex(runState, testID);
426385
runState.started(testIndex, item.payload.instant.absolute);
427-
this.recordOutput(runState, item.payload.messages, testIndex);
428386
return;
429387
} else if (item.payload.kind === "testSkipped") {
430388
const testName = this.testName(item.payload.testID);
431389
const testIndex = runState.getTestItemIndex(testName, undefined);
432390
runState.skipped(testIndex);
433-
this.recordOutput(runState, item.payload.messages, testIndex);
434391
return;
435392
} else if (item.payload.kind === "issueRecorded") {
436393
const testID = this.idFromOptionalTestCase(
@@ -466,8 +423,6 @@ export class SwiftTestingOutputParser {
466423
);
467424
});
468425

469-
this.recordOutput(runState, messages, testIndex);
470-
471426
if (item.payload._testCase && testID !== item.payload.testID) {
472427
const testIndex = this.getTestCaseIndex(runState, item.payload.testID);
473428
messages.forEach(message => {
@@ -478,7 +433,6 @@ export class SwiftTestingOutputParser {
478433
} else if (item.payload.kind === "testEnded") {
479434
const testName = this.testName(item.payload.testID);
480435
const testIndex = runState.getTestItemIndex(testName, undefined);
481-
this.recordOutput(runState, item.payload.messages, testIndex);
482436

483437
// When running a single test the testEnded and testCaseEnded events
484438
// have the same ID, and so we'd end the same test twice.
@@ -494,7 +448,6 @@ export class SwiftTestingOutputParser {
494448
item.payload._testCase
495449
);
496450
const testIndex = this.getTestCaseIndex(runState, testID);
497-
this.recordOutput(runState, item.payload.messages, testIndex);
498451

499452
// When running a single test the testEnded and testCaseEnded events
500453
// have the same ID, and so we'd end the same test twice.
@@ -505,8 +458,6 @@ export class SwiftTestingOutputParser {
505458
runState.completed(testIndex, { timestamp: item.payload.instant.absolute });
506459
return;
507460
}
508-
509-
this.recordOutput(runState, item.payload.messages, undefined);
510461
}
511462
}
512463
}

test/suite/testexplorer/SwiftTestingOutputParser.test.ts

+3-9
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ suite("SwiftTestingOutputParser Suite", () => {
153153
timing: {
154154
timestamp: 0,
155155
},
156-
output: renderedMessages.map(message => `${message}\r\n`),
156+
output: [],
157157
},
158158
]);
159159
}
@@ -292,21 +292,15 @@ suite("SwiftTestingOutputParser Suite", () => {
292292
assert.deepEqual(testRunState.tests, [
293293
{
294294
name: "MyTests.MyTests/testOutput()",
295-
output: [
296-
`\u001b[92m${renderedSymbol}\u001b[0m testCaseStarted\r\n`,
297-
`\u001b[92m${renderedSymbol}\u001b[0m testCaseEnded\r\n`,
298-
],
295+
output: [],
299296
status: TestStatus.passed,
300297
timing: {
301298
timestamp: 0,
302299
},
303300
},
304301
{
305302
name: "MyTests.MyTests/testOutput2()",
306-
output: [
307-
`\u001b[92m${renderedSymbol}\u001b[0m testCaseStarted\r\n`,
308-
`\u001b[92m${renderedSymbol}\u001b[0m testCaseEnded\r\n`,
309-
],
303+
output: [],
310304
status: TestStatus.passed,
311305
timing: {
312306
timestamp: 0,

0 commit comments

Comments
 (0)