Skip to content

Commit abc76d5

Browse files
authored
Fail tests if they crash (#1505)
If a test crashes during a test run the extension never marks the test as failed, since it doesn't see the output that marks the test as completed. If we finish a test run and we still have a test in the 'pending' state where no pass/fail/skip has been seen, mark the test as failed with a message stating the test never finished. Issue: #1504
1 parent 96a8904 commit abc76d5

File tree

3 files changed

+67
-2
lines changed

3 files changed

+67
-2
lines changed

assets/test/defaultPackage/Tests/PackageTests/PackageTests.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,16 +106,25 @@ struct MixedSwiftTestingSuite {
106106
}
107107
print(string)
108108
}
109-
#endif
109+
110+
@Test func testCrashing() throws {
111+
print([1,2][3])
112+
}
110113

111114
// Disabled until Attachments are formalized and released.
112115
// #if swift(>=6.1)
113116
// @Test func testAttachment() throws {
114117
// Attachment("Hello, world!", named: "hello.txt").attach()
115118
// }
116-
// #endif
119+
#endif
117120

118121
final class DuplicateSuffixTests: XCTestCase {
119122
func testPassing() throws {}
120123
func testPassingSuffix() throws {}
121124
}
125+
126+
final class CrashingXCTests: XCTestCase {
127+
func testCrashing() throws {
128+
print([1,2][3])
129+
}
130+
}

src/TestExplorer/TestRunner.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export enum TestLibrary {
5858
}
5959

6060
export interface TestRunState {
61+
pending: vscode.TestItem[];
6162
failed: {
6263
test: vscode.TestItem;
6364
message: vscode.TestMessage | readonly vscode.TestMessage[];
@@ -88,6 +89,7 @@ export class TestRunProxy {
8889

8990
public static initialTestRunState(): TestRunState {
9091
return {
92+
pending: [],
9193
failed: [],
9294
passed: [],
9395
skipped: [],
@@ -220,18 +222,25 @@ export class TestRunProxy {
220222
}
221223

222224
public started(test: vscode.TestItem) {
225+
this.runState.pending.push(test);
223226
this.testRun?.started(test);
224227
}
225228

229+
private clearPendingTest(test: vscode.TestItem) {
230+
this.runState.pending = this.runState.pending.filter(t => t !== test);
231+
}
232+
226233
public skipped(test: vscode.TestItem) {
227234
test.tags = [...test.tags, new vscode.TestTag(TestRunProxy.Tags.SKIPPED)];
228235

229236
this.runState.skipped.push(test);
237+
this.clearPendingTest(test);
230238
this.testRun?.skipped(test);
231239
}
232240

233241
public passed(test: vscode.TestItem, duration?: number) {
234242
this.runState.passed.push(test);
243+
this.clearPendingTest(test);
235244
this.testRun?.passed(test, duration);
236245
}
237246

@@ -241,6 +250,7 @@ export class TestRunProxy {
241250
duration?: number
242251
) {
243252
this.runState.failed.push({ test, message });
253+
this.clearPendingTest(test);
244254
this.testRun?.failed(test, message, duration);
245255
}
246256

@@ -250,6 +260,7 @@ export class TestRunProxy {
250260
duration?: number
251261
) {
252262
this.runState.errored.push(test);
263+
this.clearPendingTest(test);
253264
this.testRun?.errored(test, message, duration);
254265
}
255266

@@ -260,6 +271,12 @@ export class TestRunProxy {
260271
this.testRunStarted();
261272
}
262273

274+
// Any tests still in the pending state are considered failed. This
275+
// can happen if a test causes a crash and aborts the test run.
276+
this.runState.pending.forEach(test => {
277+
this.failed(test, new vscode.TestMessage("Test did not complete."));
278+
});
279+
263280
this.reportAttachments();
264281
this.testRun?.end();
265282
this.testRunCompleteEmitter.fire();

test/integration-tests/testexplorer/TestExplorerIntegration.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,11 @@ suite("Test Explorer Suite", function () {
198198
"testWithKnownIssue()",
199199
"testWithKnownIssueAndUnknownIssue()",
200200
"testLotsOfOutput()",
201+
"testCrashing()",
201202
"DuplicateSuffixTests",
202203
["testPassing()", "testPassingSuffix()"],
204+
"CrashingXCTests",
205+
["testCrashing()"],
203206
],
204207
]);
205208
} else if (workspaceContext.swiftVersion.isLessThan(new Version(6, 0, 0))) {
@@ -208,6 +211,8 @@ suite("Test Explorer Suite", function () {
208211
assertTestControllerHierarchy(testExplorer.controller, [
209212
"PackageTests",
210213
[
214+
"CrashingXCTests",
215+
["testCrashing"],
211216
"DebugReleaseTestSuite",
212217
["testDebug", "testRelease"],
213218
"DuplicateSuffixTests",
@@ -342,6 +347,23 @@ suite("Test Explorer Suite", function () {
342347
});
343348
});
344349

350+
test("crashing", async () => {
351+
const testRun = await runTest(
352+
testExplorer,
353+
TestKind.standard,
354+
"PackageTests.testCrashing()"
355+
);
356+
357+
assertTestResults(testRun, {
358+
failed: [
359+
{
360+
test: "PackageTests.testCrashing()",
361+
issues: ["Test did not complete."],
362+
},
363+
],
364+
});
365+
});
366+
345367
test("tests run in debug mode @slow", async function () {
346368
const testRun = await runTest(
347369
testExplorer,
@@ -441,6 +463,23 @@ suite("Test Explorer Suite", function () {
441463
});
442464
});
443465

466+
test("Crashing XCTest", async function () {
467+
const crashingRun = await runTest(
468+
testExplorer,
469+
TestKind.standard,
470+
"PackageTests.CrashingXCTests/testCrashing"
471+
);
472+
473+
assertTestResults(crashingRun, {
474+
failed: [
475+
{
476+
test: "PackageTests.CrashingXCTests/testCrashing",
477+
issues: ["Test did not complete."],
478+
},
479+
],
480+
});
481+
});
482+
444483
test("Cancellation", async function () {
445484
const targetProfile = testExplorer.testRunProfiles.find(
446485
profile => profile.label === TestKind.standard

0 commit comments

Comments
 (0)