diff --git a/CHANGELOG.md b/CHANGELOG.md index 69b03d9ab..73107cfc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Fix extension displaying SwiftPM's project view and automatic build tasks even when `disableSwiftPMIntegration` was true ([#2011](https://github.com/swiftlang/vscode-swift/pull/2011)) - Validate extension settings and warn if they are invalid ([#2016](https://github.com/swiftlang/vscode-swift/pull/2016)) - Show the Test Results panel when tests fail to compile and the user has `testing.automaticallyOpenTestResults` set to `openOnTestFailure` ([#2035](https://github.com/swiftlang/vscode-swift/pull/2035)) +- Swift-testing test runs are marked as 'started' in the UI immediately, not after compilation finishes ([#2039](https://github.com/swiftlang/vscode-swift/pull/2039)) - Added missing icon for `macro` targets in the Project Panel ([#2043](https://github.com/swiftlang/vscode-swift/pull/2043)) ## 2.14.3 - 2025-12-15 diff --git a/src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts b/src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts index 381bd3ad0..ef075c8df 100644 --- a/src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts +++ b/src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts @@ -191,8 +191,7 @@ export class SwiftTestingOutputParser { private path?: string; constructor( - public testRunStarted: () => void, - public addParameterizedTestCase: (testClass: TestClass, parentIndex: number) => void, + public addParameterizedTestCases: (testClasses: TestClass[], parentIndex: number) => void, public onAttachment: (testIndex: number, path: string) => void ) {} @@ -280,7 +279,6 @@ export class SwiftTestingOutputParser { private handleEventRecord(payload: EventRecordPayload, runState: ITestRunState) { switch (payload.kind) { case "runStarted": - this.handleRunStarted(); break; case "testStarted": this.handleTestStarted(payload, runState); @@ -322,9 +320,8 @@ export class SwiftTestingOutputParser { const testIndex = this.testItemIndexFromTestID(item.payload.id, runState); // If a test has test cases it is paramterized and we need to notify - // the caller that the TestClass should be added to the vscode.TestRun - // before it starts. - item.payload._testCases + // the caller that the TestClass should be added to the vscode.TestRun. + const parameterizedTestCases = item.payload._testCases .map((testCase, index) => this.parameterizedFunctionTestCaseToTestClass( item.payload.id, @@ -337,14 +334,9 @@ export class SwiftTestingOutputParser { index ) ) - .flatMap(testClass => (testClass ? [testClass] : [])) - .forEach(testClass => this.addParameterizedTestCase(testClass, testIndex)); - } + .flatMap(testClass => (testClass ? [testClass] : [])); - private handleRunStarted() { - // Notify the runner that we've received all the test cases and - // are going to start running tests now. - this.testRunStarted(); + this.addParameterizedTestCases(parameterizedTestCases, testIndex); } private handleTestStarted(payload: TestStarted, runState: ITestRunState) { diff --git a/src/TestExplorer/TestRunner.ts b/src/TestExplorer/TestRunner.ts index 700dc74af..4734fe3c0 100644 --- a/src/TestExplorer/TestRunner.ts +++ b/src/TestExplorer/TestRunner.ts @@ -83,7 +83,6 @@ export interface TestRunState { export class TestRunProxy { private testRun?: vscode.TestRun; - private addedTestItems: { testClass: TestClass; parentIndex: number }[] = []; private runStarted: boolean = false; private queuedOutput: string[] = []; private _testItems: vscode.TestItem[]; @@ -146,14 +145,23 @@ export class TestRunProxy { this.resetTags(this.controller); this.runStarted = true; - // When a test run starts we need to do several things: - // - Create new TestItems for each paramterized test that was added - // and attach them to their parent TestItem. - // - Create a new test run from the TestRunArguments + newly created TestItems. - // - Mark all of these test items as enqueued on the test run. + this.testRun = this.controller.createTestRun(this.testRunRequest); + this.token.add(this.testRun.token); + + // Forward any output captured before the testRun was created. + for (const outputLine of this.queuedOutput) { + this.performAppendOutput(this.testRun, outputLine); + } + this.queuedOutput = []; - const addedTestItems = this.addedTestItems - .map(({ testClass, parentIndex }) => { + for (const test of this.testItems) { + this.enqueued(test); + } + }; + + public addParameterizedTestCases = (testClasses: TestClass[], parentIndex: number) => { + const addedTestItems = testClasses + .map(testClass => { const parent = this.args.testItems[parentIndex]; // clear out the children before we add the new ones. parent.children.replace([]); @@ -189,34 +197,17 @@ export class TestRunProxy { return added; }); - - this.testRun = this.controller.createTestRun(this.testRunRequest); - this.token.add(this.testRun.token); - - const existingTestItemCount = this.testItems.length; this._testItems = [...this.testItems, ...addedTestItems]; - if (this._testItems.length !== existingTestItemCount) { - // Recreate a test item finder with the added test items - this.testItemFinder = - process.platform === "darwin" - ? new DarwinTestItemFinder(this.testItems) - : new NonDarwinTestItemFinder(this.testItems, this.folderContext); - } - - // Forward any output captured before the testRun was created. - for (const outputLine of this.queuedOutput) { - this.performAppendOutput(this.testRun, outputLine); - } - this.queuedOutput = []; - - for (const test of this.testItems) { + for (const test of addedTestItems) { this.enqueued(test); } - }; - public addParameterizedTestCase = (testClass: TestClass, parentIndex: number) => { - this.addedTestItems.push({ testClass, parentIndex }); + // Recreate a test item finder with the added test items + this.testItemFinder = + process.platform === "darwin" + ? new DarwinTestItemFinder(this.testItems) + : new NonDarwinTestItemFinder(this.testItems, this.folderContext); }; public addAttachment = (testIndex: number, attachment: string) => { @@ -486,8 +477,7 @@ export class TestRunner { ) : new XCTestOutputParser(); this.swiftTestOutputParser = new SwiftTestingOutputParser( - this.testRun.testRunStarted, - this.testRun.addParameterizedTestCase, + this.testRun.addParameterizedTestCases, this.testRun.addAttachment ); this.onDebugSessionTerminated = this.debugSessionTerminatedEmitter.event; @@ -501,8 +491,7 @@ export class TestRunner { public setIteration(iteration: number) { // The SwiftTestingOutputParser holds state and needs to be reset between iterations. this.swiftTestOutputParser = new SwiftTestingOutputParser( - this.testRun.testRunStarted, - this.testRun.addParameterizedTestCase, + this.testRun.addParameterizedTestCases, this.testRun.addAttachment ); this.testRun.setIteration(iteration); @@ -830,6 +819,8 @@ export class TestRunner { // The await simply waits for the watching to be configured. await this.swiftTestOutputParser.watch(fifoPipePath, runState); + this.testRun.testRunStarted(); + await this.launchTests( runState, this.testKind === TestKind.parallel ? TestKind.standard : this.testKind, @@ -858,7 +849,6 @@ export class TestRunner { return this.testRun.runState; } - // XCTestRuns are started immediately this.testRun.testRunStarted(); await this.launchTests( diff --git a/test/integration-tests/testexplorer/SwiftTestingOutputParser.test.ts b/test/integration-tests/testexplorer/SwiftTestingOutputParser.test.ts index 23751e04c..7b904a5c9 100644 --- a/test/integration-tests/testexplorer/SwiftTestingOutputParser.test.ts +++ b/test/integration-tests/testexplorer/SwiftTestingOutputParser.test.ts @@ -46,7 +46,6 @@ suite("SwiftTestingOutputParser Suite", () => { beforeEach(() => { outputParser = new SwiftTestingOutputParser( - () => {}, () => {}, () => {} ); @@ -234,13 +233,14 @@ suite("SwiftTestingOutputParser Suite", () => { ]); const outputParser = new SwiftTestingOutputParser( - () => {}, - testClass => { - testRunState.testItemFinder.tests.push({ - name: testClass.id, - status: TestStatus.enqueued, - output: [], - }); + testClasses => { + testClasses.forEach(testClass => + testRunState.testItemFinder.tests.push({ + name: testClass.id, + status: TestStatus.enqueued, + output: [], + }) + ); }, () => {} );