Skip to content

Fix error when running tests multiple times from the editor gutter #1742

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,16 @@
}
],
"commandPalette": [
{
"command": "swift.runTestsMultipleTimes",
"group": "testExtras",
"when": "false"
},
{
"command": "swift.runTestsUntilFailure",
"group": "testExtras",
"when": "false"
},
{
"command": "swift.generateLaunchConfigurations",
"when": "swift.hasExecutableProduct"
Expand Down
39 changes: 1 addition & 38 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import { resolveDependencies } from "./commands/dependencies/resolve";
import { resetPackage } from "./commands/resetPackage";
import { updateDependencies } from "./commands/dependencies/update";
import { runPluginTask } from "./commands/runPluginTask";
import { runTestMultipleTimes } from "./commands/testMultipleTimes";
import { extractTestItemsAndCount, runTestMultipleTimes } from "./commands/testMultipleTimes";
import { newSwiftFile } from "./commands/newFile";
import { runAllTests } from "./commands/runAllTests";
import { updateDependenciesViewList } from "./commands/dependencies/updateDepViewList";
Expand Down Expand Up @@ -282,43 +282,6 @@ export function register(ctx: WorkspaceContext): vscode.Disposable[] {
];
}

/**
* Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this
* converts a varargs function from accepting both numbers and test items to:
*
* function (...testItems: vscode.TestItem[], count?: number): void;
*
* The VS Code testing view sends test items via varargs, but we have a couple testing commands that
* also accept a final count parameter. We have to find the count parameter ourselves since JavaScript
* only supports varargs at the end of an argument list.
*/
function extractTestItemsAndCount(...args: (vscode.TestItem | number)[]): {
testItems: vscode.TestItem[];
count?: number;
} {
const result = args.reduceRight<{
testItems: vscode.TestItem[];
count?: number;
}>(
(result, arg, index) => {
if (typeof arg === "number" && index === args.length - 1) {
result.count = arg;
return result;
} else if (typeof arg === "object") {
result.testItems.push(arg);
return result;
} else {
throw new Error(`Unexpected argument ${arg} at index ${index}`);
}
},
{ testItems: [] }
);
if (result.testItems.length === 0) {
throw new Error("At least one TestItem must be provided");
}
return result;
}

/**
* Certain commands can be called via a vscode TreeView, which will pass a {@link CommandNode} object.
* If the command is called via a command palette or other means, the target will be a string.
Expand Down
39 changes: 38 additions & 1 deletion src/commands/testMultipleTimes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ export async function runTestMultipleTimes(
let numExecutions = count;
if (numExecutions === undefined) {
const str = await vscode.window.showInputBox({
prompt: "Label: ",
placeHolder: `${untilFailure ? "Maximum " : ""}# of times to run`,
validateInput: value =>
/^[1-9]\d*$/.test(value) ? undefined : "Enter an integer value",
Expand Down Expand Up @@ -86,3 +85,41 @@ export async function runTestMultipleTimes(

return runStates;
}

/**
* Extracts an array of vscode.TestItem and count from the provided varargs. Effectively, this
* converts a varargs function from accepting both numbers and test items to:
*
* function (...testItems: vscode.TestItem[], count?: number): void;
*
* The VS Code testing view sends test items via varargs, but we have a couple testing commands that
* also accept a final count parameter. We have to find the count parameter ourselves since JavaScript
* only supports varargs at the end of an argument list.
*/
export function extractTestItemsAndCount(
...args: (vscode.TestItem | number | undefined | null)[]
): {
testItems: vscode.TestItem[];
count?: number;
} {
const result = args.reduce<{
testItems: vscode.TestItem[];
count?: number;
}>(
(result, arg, index) => {
if (arg === undefined || arg === null) {
return result;
} else if (typeof arg === "number" && index === args.length - 1) {
result.count = arg ?? undefined;
return result;
} else if (typeof arg === "object") {
result.testItems.push(arg);
return result;
} else {
throw new Error(`Unexpected argument ${arg} at index ${index}`);
}
},
{ testItems: [] }
);
return result;
}
81 changes: 81 additions & 0 deletions test/unit-tests/commands/runTestMultipleTimes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the VS Code Swift open source project
//
// Copyright (c) 2025 the VS Code Swift project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of VS Code Swift project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import * as vscode from "vscode";
import { expect } from "chai";
import { extractTestItemsAndCount } from "../../../src/commands/testMultipleTimes";

suite("Run Tests Multiple Times", () => {
suite("extractTestItemsAndCount()", () => {
function createDummyTestItem(label: string): vscode.TestItem {
return { label } as vscode.TestItem;
}

test("handles empty arguments", () => {
const { testItems, count } = extractTestItemsAndCount();
expect(testItems).to.deep.equal([]);
expect(count).to.be.undefined;
});

test("handles test items with no count", () => {
const testItem1 = createDummyTestItem("Test Item 1");
const testItem2 = createDummyTestItem("Test Item 2");
const testItem3 = createDummyTestItem("Test Item 3");

const { testItems, count } = extractTestItemsAndCount(testItem1, testItem2, testItem3);
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
expect(count).to.be.undefined;
});

test("handles test items with count", () => {
const testItem1 = createDummyTestItem("Test Item 1");
const testItem2 = createDummyTestItem("Test Item 2");
const testItem3 = createDummyTestItem("Test Item 3");

const { testItems, count } = extractTestItemsAndCount(
testItem1,
testItem2,
testItem3,
17
);
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
expect(count).to.equal(17);
});

test("ignores undefined or null arguments", () => {
const testItem1 = createDummyTestItem("Test Item 1");
const testItem2 = createDummyTestItem("Test Item 2");
const testItem3 = createDummyTestItem("Test Item 3");

const { testItems, count } = extractTestItemsAndCount(
testItem1,
null,
testItem2,
testItem3,
undefined
);
expect(testItems).to.deep.equal([testItem1, testItem2, testItem3]);
expect(count).to.be.undefined;
});

test("throws an error if the count is not the last argument", () => {
const testItem1 = createDummyTestItem("Test Item 1");
const testItem2 = createDummyTestItem("Test Item 2");

expect(() => extractTestItemsAndCount(testItem1, 17, testItem2)).to.throw(
"Unexpected argument 17 at index 1"
);
});
});
});