Skip to content

Commit 1e4385a

Browse files
Handle multiple test items passed to runTestsMultipleTimes and runTestsUntilFailure (#1724)
1 parent 540e8e5 commit 1e4385a

File tree

4 files changed

+63
-13
lines changed

4 files changed

+63
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- Added code lenses to run suites/tests, configurable with the `swift.showTestCodeLenses` setting ([#1698](https://github.com/swiftlang/vscode-swift/pull/1698))
88
- New `swift.excludePathsFromActivation` setting to ignore specified sub-folders from being activated as projects ([#1693](https://github.com/swiftlang/vscode-swift/pull/1693))
99

10+
### Fixed
11+
12+
- `Run multiple times...` and `Run until failure...` will now work when multiple tests are selected ([#1724](https://github.com/swiftlang/vscode-swift/pull/1724))
13+
1014
## 2.8.0 - 2025-07-14
1115

1216
### Added

src/commands.ts

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ export enum Commands {
8686
SHOW_NESTED_DEPENDENCIES_LIST = "swift.nestedDependenciesList",
8787
UPDATE_DEPENDENCIES = "swift.updateDependencies",
8888
RUN_TESTS_MULTIPLE_TIMES = "swift.runTestsMultipleTimes",
89+
RUN_TESTS_UNTIL_FAILURE = "swift.runTestsUntilFailure",
8990
RESET_PACKAGE = "swift.resetPackage",
9091
USE_LOCAL_DEPENDENCY = "swift.useLocalDependency",
9192
UNEDIT_DEPENDENCY = "swift.uneditDependency",
@@ -133,16 +134,24 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
133134
async target => await debugBuild(ctx, ...unwrapTreeItem(target))
134135
),
135136
vscode.commands.registerCommand(Commands.CLEAN_BUILD, async () => await cleanBuild(ctx)),
136-
vscode.commands.registerCommand(Commands.RUN_TESTS_MULTIPLE_TIMES, async (item, count) => {
137-
if (ctx.currentFolder) {
138-
return await runTestMultipleTimes(ctx.currentFolder, item, false, count);
137+
vscode.commands.registerCommand(
138+
Commands.RUN_TESTS_MULTIPLE_TIMES,
139+
async (...args: (vscode.TestItem | number)[]) => {
140+
const { testItems, count } = extractTestItemsAndCount(...args);
141+
if (ctx.currentFolder) {
142+
return await runTestMultipleTimes(ctx.currentFolder, testItems, false, count);
143+
}
139144
}
140-
}),
141-
vscode.commands.registerCommand("swift.runTestsUntilFailure", async (item, count) => {
142-
if (ctx.currentFolder) {
143-
return await runTestMultipleTimes(ctx.currentFolder, item, true, count);
145+
),
146+
vscode.commands.registerCommand(
147+
Commands.RUN_TESTS_UNTIL_FAILURE,
148+
async (...args: (vscode.TestItem | number)[]) => {
149+
const { testItems, count } = extractTestItemsAndCount(...args);
150+
if (ctx.currentFolder) {
151+
return await runTestMultipleTimes(ctx.currentFolder, testItems, true, count);
152+
}
144153
}
145-
}),
154+
),
146155
// Note: switchPlatform is only available on macOS and Swift 6.1 or later
147156
// (gated in `package.json`) because it's the only OS and toolchain combination that
148157
// has Darwin SDKs available and supports code editing with SourceKit-LSP
@@ -267,6 +276,43 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
267276
];
268277
}
269278

279+
/**
280+
* Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this
281+
* converts a varargs function from accepting both numbers and test items to:
282+
*
283+
* function (...testItems: vscode.TestItem[], count?: number): void;
284+
*
285+
* The VS Code testing view sends test items via varargs, but we have a couple testing commands that
286+
* also accept a final count parameter. We have to find the count parameter ourselves since JavaScript
287+
* only supports varargs at the end of an argument list.
288+
*/
289+
function extractTestItemsAndCount(...args: (vscode.TestItem | number)[]): {
290+
testItems: vscode.TestItem[];
291+
count?: number;
292+
} {
293+
const result = args.reduceRight<{
294+
testItems: vscode.TestItem[];
295+
count?: number;
296+
}>(
297+
(result, arg, index) => {
298+
if (typeof arg === "number" && index === args.length - 1) {
299+
result.count = arg;
300+
return result;
301+
} else if (typeof arg === "object") {
302+
result.testItems.push(arg);
303+
return result;
304+
} else {
305+
throw new Error(`Unexpected argument ${arg} at index ${index}`);
306+
}
307+
},
308+
{ testItems: [] }
309+
);
310+
if (result.testItems.length === 0) {
311+
throw new Error("At least one TestItem must be provided");
312+
}
313+
return result;
314+
}
315+
270316
/**
271317
* Certain commands can be called via a vscode TreeView, which will pass a {@link CommandNode} object.
272318
* If the command is called via a command palette or other means, the target will be a string.

src/commands/testMultipleTimes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import { FolderContext } from "../FolderContext";
2626
*/
2727
export async function runTestMultipleTimes(
2828
currentFolder: FolderContext,
29-
test: vscode.TestItem,
29+
tests: vscode.TestItem[],
3030
untilFailure: boolean,
3131
count: number | undefined = undefined,
3232
testRunner?: () => Promise<TestRunState>
@@ -52,7 +52,7 @@ export async function runTestMultipleTimes(
5252
const testExplorer = currentFolder.testExplorer;
5353
const runner = new TestRunner(
5454
TestKind.standard,
55-
new vscode.TestRunRequest([test]),
55+
new vscode.TestRunRequest(tests),
5656
currentFolder,
5757
testExplorer.controller,
5858
token.token

test/integration-tests/commands/runTestMultipleTimes.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,12 @@ suite("Test Multiple Times Command Test Suite", () => {
3939
});
4040

4141
test("Runs successfully after testing 0 times", async () => {
42-
const runState = await runTestMultipleTimes(folderContext, testItem, false, 0);
42+
const runState = await runTestMultipleTimes(folderContext, [testItem], false, 0);
4343
expect(runState).to.be.an("array").that.is.empty;
4444
});
4545

4646
test("Runs successfully after testing 3 times", async () => {
47-
const runState = await runTestMultipleTimes(folderContext, testItem, false, 3, () =>
47+
const runState = await runTestMultipleTimes(folderContext, [testItem], false, 3, () =>
4848
Promise.resolve(TestRunProxy.initialTestRunState())
4949
);
5050

@@ -61,7 +61,7 @@ suite("Test Multiple Times Command Test Suite", () => {
6161
failed: [{ test: testItem, message: new vscode.TestMessage("oh no") }],
6262
};
6363
let ctr = 0;
64-
const runState = await runTestMultipleTimes(folderContext, testItem, true, 3, () => {
64+
const runState = await runTestMultipleTimes(folderContext, [testItem], true, 3, () => {
6565
ctr += 1;
6666
if (ctr === 2) {
6767
return Promise.resolve(failure);

0 commit comments

Comments
 (0)