Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 5 additions & 13 deletions src/TestExplorer/TestParsers/SwiftTestingOutputParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand All @@ -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) {
Expand Down
62 changes: 26 additions & 36 deletions src/TestExplorer/TestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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([]);
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -858,7 +849,6 @@ export class TestRunner {
return this.testRun.runState;
}

// XCTestRuns are started immediately
this.testRun.testRunStarted();

await this.launchTests(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ suite("SwiftTestingOutputParser Suite", () => {

beforeEach(() => {
outputParser = new SwiftTestingOutputParser(
() => {},
() => {},
() => {}
);
Expand Down Expand Up @@ -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: [],
})
);
},
() => {}
);
Expand Down