From f340ed172eb5088a6e23a37437c175651fdfdda0 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 26 Jun 2025 15:53:21 -0400 Subject: [PATCH 1/5] Mark tests as skipped when they fail to compile If there is a compilation error when compiling the tests for a test run show the tests that were expected to be part of the run as "skipped" and associated the build output (with the build failure) with the test items. The goal is to make it clear that these tests were not run due to a build error. --- src/TestExplorer/TestRunner.ts | 28 +++++++++++++++++-- .../testexplorer/utilities.ts | 2 ++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index a1c1c8963..a3758289b 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -67,6 +67,7 @@ export interface TestRunState { passed: vscode.TestItem[]; skipped: vscode.TestItem[]; errored: vscode.TestItem[]; + enqueued: vscode.TestItem[]; unknown: number; output: string[]; } @@ -95,6 +96,7 @@ export class TestRunProxy { passed: [], skipped: [], errored: [], + enqueued: [], unknown: 0, output: [], }; @@ -181,10 +183,9 @@ export class TestRunProxy { for (const outputLine of this.queuedOutput) { this.performAppendOutput(this.testRun, outputLine); } - this.queuedOutput = []; for (const test of this.testItems) { - this.testRun.enqueued(test); + this.enqueued(test); } }; @@ -218,11 +219,17 @@ export class TestRunProxy { } } + private enqueued(test: vscode.TestItem) { + this.testRun?.enqueued(test); + this.runState.enqueued.push(test); + } + public unknownTestRan() { this.runState.unknown++; } public started(test: vscode.TestItem) { + this.clearEnqueuedTest(test); this.runState.pending.push(test); this.testRun?.started(test); } @@ -231,6 +238,10 @@ export class TestRunProxy { this.runState.pending = this.runState.pending.filter(t => t !== test); } + private clearEnqueuedTest(test: vscode.TestItem) { + this.runState.enqueued = this.runState.enqueued.filter(t => t.id !== test.id); + } + public skipped(test: vscode.TestItem) { test.tags = [...test.tags, new vscode.TestTag(TestRunProxy.Tags.SKIPPED)]; @@ -278,6 +289,19 @@ export class TestRunProxy { this.failed(test, new vscode.TestMessage("Test did not complete.")); }); + // If there are tests that never started, mark them as skipped. + // This can happen if there is a build error preventing tests from running. + this.runState.enqueued.forEach(test => { + if (test.children.size === 0) { + for (const output of this.queuedOutput) { + this.appendOutputToTest(output, test); + } + this.skipped(test); + } + }); + + this.queuedOutput = []; + this.reportAttachments(); this.testRun?.end(); this.testRunCompleteEmitter.fire(); diff --git a/test/integration-tests/testexplorer/utilities.ts b/test/integration-tests/testexplorer/utilities.ts index ab6774efd..74aa665fb 100644 --- a/test/integration-tests/testexplorer/utilities.ts +++ b/test/integration-tests/testexplorer/utilities.ts @@ -145,6 +145,7 @@ export function assertTestResults( passed?: string[]; skipped?: string[]; errored?: string[]; + enqueued?: string[]; unknown?: number; } ) { @@ -173,6 +174,7 @@ export function assertTestResults( .sort(), skipped: (state.skipped ?? []).sort(), errored: (state.errored ?? []).sort(), + enqueued: (state.enqueued ?? []).sort(), unknown: 0, }, ` From d1628aaeec10f9541a876e9096aef181104f5150 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 26 Jun 2025 21:26:18 -0400 Subject: [PATCH 2/5] Fix tests --- src/TestExplorer/TestRunner.ts | 18 ++++++++++++++++-- .../testexplorer/utilities.ts | 1 + 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index a3758289b..512c1c44e 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -239,7 +239,19 @@ export class TestRunProxy { } private clearEnqueuedTest(test: vscode.TestItem) { - this.runState.enqueued = this.runState.enqueued.filter(t => t.id !== test.id); + this.runState.enqueued = this.runState.enqueued.filter(t => t !== test); + + if (!test.parent) { + return; + } + + const parentHasEnqueuedChildren = Array.from(test.parent.children).some(([_, child]) => + this.runState.enqueued.includes(child) + ); + + if (!parentHasEnqueuedChildren) { + this.clearEnqueuedTest(test.parent); + } } public skipped(test: vscode.TestItem) { @@ -292,7 +304,9 @@ export class TestRunProxy { // If there are tests that never started, mark them as skipped. // This can happen if there is a build error preventing tests from running. this.runState.enqueued.forEach(test => { - if (test.children.size === 0) { + // Omit adding the root test item as a skipped test to keep just the suites/tests + // in the test run output, just like a regular pass/fail test run. + if (test.parent) { for (const output of this.queuedOutput) { this.appendOutputToTest(output, test); } diff --git a/test/integration-tests/testexplorer/utilities.ts b/test/integration-tests/testexplorer/utilities.ts index 74aa665fb..803deaeda 100644 --- a/test/integration-tests/testexplorer/utilities.ts +++ b/test/integration-tests/testexplorer/utilities.ts @@ -162,6 +162,7 @@ export function assertTestResults( .sort(), skipped: testRun.runState.skipped.map(({ id }) => id).sort(), errored: testRun.runState.errored.map(({ id }) => id).sort(), + enqueued: testRun.runState.enqueued.map(({ id }) => id).sort(), unknown: testRun.runState.unknown, }, { From b382f2b6e093aa87810b7a87129191c73c46a507 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 27 Jun 2025 08:37:43 -0400 Subject: [PATCH 3/5] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4741a24a5..88b2f55a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - Make sure newline starts with /// when splitting doc comment ([#1651](https://github.com/swiftlang/vscode-swift/pull/1651)) - Prevent continuous "package resolve" cycles ([#1654](https://github.com/swiftlang/vscode-swift/pull/1654)) - Fix error when running `Reset Package Dependencies` command from the Project view ([#1661](https://github.com/swiftlang/vscode-swift/pull/1661)) +- Mark tests as skipped when a compilation error preempts a test run ([#1659](https://github.com/swiftlang/vscode-swift/pull/1659)) ## 2.6.0 - 2025-06-26 From 22147e9834e584126c7ad18a51ef827c66793721 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 27 Jun 2025 09:41:58 -0400 Subject: [PATCH 4/5] Clean enqueued on test skip --- src/TestExplorer/TestRunner.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index 512c1c44e..b80645d0d 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -255,6 +255,7 @@ export class TestRunProxy { } public skipped(test: vscode.TestItem) { + this.clearEnqueuedTest(test); test.tags = [...test.tags, new vscode.TestTag(TestRunProxy.Tags.SKIPPED)]; this.runState.skipped.push(test); From 2c1907950253a2d37f76ef9648d0e4fc3e6e3707 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 27 Jun 2025 10:17:34 -0400 Subject: [PATCH 5/5] Always clear enqueued state, only in tests --- src/TestExplorer/TestRunner.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index b80645d0d..c41267321 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -18,7 +18,12 @@ import * as stream from "stream"; import * as os from "os"; import * as asyncfs from "fs/promises"; import { FolderContext } from "../FolderContext"; -import { compactMap, execFile, getErrorDescription } from "../utilities/utilities"; +import { + compactMap, + execFile, + getErrorDescription, + IS_PRODUCTION_BUILD, +} from "../utilities/utilities"; import { createSwiftTask } from "../tasks/SwiftTaskProvider"; import configuration from "../configuration"; import { WorkspaceContext } from "../WorkspaceContext"; @@ -239,6 +244,11 @@ export class TestRunProxy { } private clearEnqueuedTest(test: vscode.TestItem) { + if (IS_PRODUCTION_BUILD) { + // `runState.enqueued` exists only for test validation purposes. + return; + } + this.runState.enqueued = this.runState.enqueued.filter(t => t !== test); if (!test.parent) { @@ -264,6 +274,7 @@ export class TestRunProxy { } public passed(test: vscode.TestItem, duration?: number) { + this.clearEnqueuedTest(test); this.runState.passed.push(test); this.clearPendingTest(test); this.testRun?.passed(test, duration); @@ -274,6 +285,7 @@ export class TestRunProxy { message: vscode.TestMessage | readonly vscode.TestMessage[], duration?: number ) { + this.clearEnqueuedTest(test); this.runState.failed.push({ test, message }); this.clearPendingTest(test); this.testRun?.failed(test, message, duration); @@ -284,6 +296,7 @@ export class TestRunProxy { message: vscode.TestMessage | readonly vscode.TestMessage[], duration?: number ) { + this.clearEnqueuedTest(test); this.runState.errored.push(test); this.clearPendingTest(test); this.testRun?.errored(test, message, duration);