Skip to content

Commit 5a34d40

Browse files
authored
Optimize clearing enqueued tests (#1734)
* Optimize clearing enqueued tests Refactor the enqueued tests list to be a Set so we can clear enqueued tests without the need to potentially traverse the full remaining list of enqueued tests. Also avoid recreating the test item finder on every `get testItemFinder`, and store test items in the darwin test item finder in a Set as well for faster lookup. * Update changelog
1 parent 9f874ec commit 5a34d40

File tree

3 files changed

+31
-26
lines changed

3 files changed

+31
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
- `Run multiple times...` and `Run until failure...` will now work when multiple tests are selected ([#1724](https://github.com/swiftlang/vscode-swift/pull/1724))
1515
- Provide the Swift environment variables when resolving a `swift-plugin` task ([#1727](https://github.com/swiftlang/vscode-swift/pull/1727))
16+
- Optimized test explorer for test runs with many test cases ([#1734](https://github.com/swiftlang/vscode-swift/pull/1734))
1617

1718
## 2.8.0 - 2025-07-14
1819

src/TestExplorer/TestRunner.ts

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface TestRunState {
6767
passed: vscode.TestItem[];
6868
skipped: vscode.TestItem[];
6969
errored: vscode.TestItem[];
70-
enqueued: vscode.TestItem[];
70+
enqueued: Set<vscode.TestItem>;
7171
unknown: number;
7272
output: string[];
7373
}
@@ -80,6 +80,7 @@ export class TestRunProxy {
8080
private _testItems: vscode.TestItem[];
8181
private iteration: number | undefined;
8282
private attachments: { [key: string]: string[] } = {};
83+
private testItemFinder: TestItemFinder;
8384
public coverage: TestCoverage;
8485
public token: CompositeCancellationToken;
8586

@@ -96,7 +97,7 @@ export class TestRunProxy {
9697
passed: [],
9798
skipped: [],
9899
errored: [],
99-
enqueued: [],
100+
enqueued: new Set<vscode.TestItem>(),
100101
unknown: 0,
101102
output: [],
102103
};
@@ -120,6 +121,10 @@ export class TestRunProxy {
120121
this._testItems = args.testItems;
121122
this.coverage = new TestCoverage(folderContext);
122123
this.token = new CompositeCancellationToken(testProfileCancellationToken);
124+
this.testItemFinder =
125+
process.platform === "darwin"
126+
? new DarwinTestItemFinder(args.testItems)
127+
: new NonDarwinTestItemFinder(args.testItems, this.folderContext);
123128
this.onTestRunComplete = this.testRunCompleteEmitter.event;
124129
}
125130

@@ -177,12 +182,23 @@ export class TestRunProxy {
177182

178183
this.testRun = this.controller.createTestRun(this.testRunRequest);
179184
this.token.add(this.testRun.token);
185+
186+
const existingTestItemCount = this.testItems.length;
180187
this._testItems = [...this.testItems, ...addedTestItems];
181188

189+
if (this._testItems.length !== existingTestItemCount) {
190+
// Recreate a test item finder with the added test items
191+
this.testItemFinder =
192+
process.platform === "darwin"
193+
? new DarwinTestItemFinder(this.testItems)
194+
: new NonDarwinTestItemFinder(this.testItems, this.folderContext);
195+
}
196+
182197
// Forward any output captured before the testRun was created.
183198
for (const outputLine of this.queuedOutput) {
184199
this.performAppendOutput(this.testRun, outputLine);
185200
}
201+
this.queuedOutput = [];
186202

187203
for (const test of this.testItems) {
188204
this.enqueued(test);
@@ -211,17 +227,9 @@ export class TestRunProxy {
211227
return this.testItemFinder.getIndex(id, filename);
212228
}
213229

214-
private get testItemFinder(): TestItemFinder {
215-
if (process.platform === "darwin") {
216-
return new DarwinTestItemFinder(this.testItems);
217-
} else {
218-
return new NonDarwinTestItemFinder(this.testItems, this.folderContext);
219-
}
220-
}
221-
222230
private enqueued(test: vscode.TestItem) {
223231
this.testRun?.enqueued(test);
224-
this.runState.enqueued.push(test);
232+
this.runState.enqueued.add(test);
225233
}
226234

227235
public unknownTestRan() {
@@ -239,14 +247,14 @@ export class TestRunProxy {
239247
}
240248

241249
private clearEnqueuedTest(test: vscode.TestItem) {
242-
this.runState.enqueued = this.runState.enqueued.filter(t => t !== test);
250+
this.runState.enqueued.delete(test);
243251

244252
if (!test.parent) {
245253
return;
246254
}
247255

248256
const parentHasEnqueuedChildren = Array.from(test.parent.children).some(([_, child]) =>
249-
this.runState.enqueued.includes(child)
257+
this.runState.enqueued.has(child)
250258
);
251259

252260
if (!parentHasEnqueuedChildren) {
@@ -304,7 +312,6 @@ export class TestRunProxy {
304312
this.runState.pending.forEach(test => {
305313
this.failed(test, new vscode.TestMessage("Test did not complete."));
306314
});
307-
308315
// If there are tests that never started, mark them as skipped.
309316
// This can happen if there is a build error preventing tests from running.
310317
this.runState.enqueued.forEach(test => {
@@ -1169,15 +1176,6 @@ export class TestRunner {
11691176
});
11701177
}
11711178

1172-
/** Get TestItem finder for current platform */
1173-
get testItemFinder(): TestItemFinder {
1174-
if (process.platform === "darwin") {
1175-
return new DarwinTestItemFinder(this.testArgs.testItems);
1176-
} else {
1177-
return new NonDarwinTestItemFinder(this.testArgs.testItems, this.folderContext);
1178-
}
1179-
}
1180-
11811179
private generateFifoPipePath(testRunDateNow: number): string {
11821180
return process.platform === "win32"
11831181
? `\\\\.\\pipe\\vscodemkfifo-${testRunDateNow}`
@@ -1193,10 +1191,14 @@ interface TestItemFinder {
11931191

11941192
/** Defines how to find test items given a test id from XCTest output on Darwin platforms */
11951193
class DarwinTestItemFinder implements TestItemFinder {
1196-
constructor(public testItems: vscode.TestItem[]) {}
1194+
private readonly testItemMap: Map<string, number>;
1195+
1196+
constructor(public testItems: vscode.TestItem[]) {
1197+
this.testItemMap = new Map(testItems.map((item, index) => [item.id, index]));
1198+
}
11971199

11981200
getIndex(id: string): number {
1199-
return this.testItems.findIndex(item => item.id === id);
1201+
return this.testItemMap.get(id) ?? -1;
12001202
}
12011203
}
12021204

test/integration-tests/testexplorer/utilities.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,9 @@ export function assertTestResults(
165165
.sort(),
166166
skipped: testRun.runState.skipped.map(({ id }) => id).sort(),
167167
errored: testRun.runState.errored.map(({ id }) => id).sort(),
168-
enqueued: testRun.runState.enqueued.map(({ id }) => id).sort(),
168+
enqueued: Array.from(testRun.runState.enqueued)
169+
.map(({ id }) => id)
170+
.sort(),
169171
unknown: testRun.runState.unknown,
170172
},
171173
{

0 commit comments

Comments
 (0)