From 16a05503c032431c16d6fa9332a3ee12fddd6820 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 4 Sep 2025 14:37:30 -0400 Subject: [PATCH 1/4] Fix "Run Tests Multiple Times" on Linux When we process the `...args` passed to the 'run multiple' commands we're only checking if the argument is an object before deeming it a TestItem. On linux VS Code is passing an event object like `{ preserveFocus: true }`, which is being treated as a TestItem and causing the command to error out. --- src/commands/testMultipleTimes.ts | 7 +++++-- .../testexplorer/TestExplorerIntegration.test.ts | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/commands/testMultipleTimes.ts b/src/commands/testMultipleTimes.ts index 9986e0638..38d380352 100644 --- a/src/commands/testMultipleTimes.ts +++ b/src/commands/testMultipleTimes.ts @@ -51,9 +51,10 @@ export async function runTestMultipleTimes( } const token = new vscode.CancellationTokenSource(); const testExplorer = currentFolder.testExplorer; + const request = new vscode.TestRunRequest(tests); const runner = new TestRunner( kind, - new vscode.TestRunRequest(tests), + request, currentFolder, testExplorer.controller, token.token @@ -127,7 +128,9 @@ export function extractTestItemsAndCount( result.count = arg ?? undefined; return result; } else if (typeof arg === "object") { - result.testItems.push(arg); + if (arg.hasOwnProperty("id") && arg.hasOwnProperty("uri")) { + result.testItems.push(arg); + } return result; } else { throw new Error(`Unexpected argument ${arg} at index ${index}`); diff --git a/test/integration-tests/testexplorer/TestExplorerIntegration.test.ts b/test/integration-tests/testexplorer/TestExplorerIntegration.test.ts index 7de6cd84b..9c666511a 100644 --- a/test/integration-tests/testexplorer/TestExplorerIntegration.test.ts +++ b/test/integration-tests/testexplorer/TestExplorerIntegration.test.ts @@ -614,6 +614,7 @@ suite("Test Explorer Suite", function () { await vscode.commands.executeCommand( Commands.RUN_TESTS_MULTIPLE_TIMES, testItems[0], + { preserveFocus: true }, // a trailing argument included on Linux numIterations ); From d30e04b183657b4b3fdbd49e815b621059147631 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 4 Sep 2025 14:41:28 -0400 Subject: [PATCH 2/4] Update changelog --- CHANGELOG.md | 1 + src/commands/testMultipleTimes.ts | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38e9727c6..d1af56e6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - Don't start debugging XCTest cases if the swift-testing debug session was stopped ([#1797](https://github.com/swiftlang/vscode-swift/pull/1797)) - Improve error handling when the swift path is misconfigured ([#1801](https://github.com/swiftlang/vscode-swift/pull/1801)) +- Fix an error when performing "Run/Debug Tests Multiple Times" on Linux ([#1824](https://github.com/swiftlang/vscode-swift/pull/1824)) ## 2.11.20250806 - 2025-08-06 diff --git a/src/commands/testMultipleTimes.ts b/src/commands/testMultipleTimes.ts index 38d380352..197fe9447 100644 --- a/src/commands/testMultipleTimes.ts +++ b/src/commands/testMultipleTimes.ts @@ -128,7 +128,10 @@ export function extractTestItemsAndCount( result.count = arg ?? undefined; return result; } else if (typeof arg === "object") { - if (arg.hasOwnProperty("id") && arg.hasOwnProperty("uri")) { + if ( + Object.prototype.hasOwnProperty.call(arg, "id") && + Object.prototype.hasOwnProperty.call(arg, "uri") + ) { result.testItems.push(arg); } return result; From d53e89cd4146e22748081b7fb22b18780d340176 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Thu, 4 Sep 2025 16:09:47 -0400 Subject: [PATCH 3/4] Fixup unit tests --- test/unit-tests/commands/runTestMultipleTimes.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit-tests/commands/runTestMultipleTimes.test.ts b/test/unit-tests/commands/runTestMultipleTimes.test.ts index 217e96d9b..b45f8e039 100644 --- a/test/unit-tests/commands/runTestMultipleTimes.test.ts +++ b/test/unit-tests/commands/runTestMultipleTimes.test.ts @@ -19,7 +19,7 @@ import { extractTestItemsAndCount } from "../../../src/commands/testMultipleTime suite("Run Tests Multiple Times", () => { suite("extractTestItemsAndCount()", () => { function createDummyTestItem(label: string): vscode.TestItem { - return { label } as vscode.TestItem; + return { id: label, uri: vscode.Uri.file(`/dummy/path/${label}`) } as vscode.TestItem; } test("handles empty arguments", () => { From 2fdcfed24f73d4b93ab6523a35d6de28ed0dddf9 Mon Sep 17 00:00:00 2001 From: Paul LeMarquand Date: Fri, 5 Sep 2025 09:07:04 -0400 Subject: [PATCH 4/4] Update based on feedback --- src/commands/testMultipleTimes.ts | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/commands/testMultipleTimes.ts b/src/commands/testMultipleTimes.ts index 197fe9447..ed286ef74 100644 --- a/src/commands/testMultipleTimes.ts +++ b/src/commands/testMultipleTimes.ts @@ -111,9 +111,7 @@ export async function runTestMultipleTimes( * 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)[] -): { +export function extractTestItemsAndCount(...args: unknown[]): { testItems: vscode.TestItem[]; count?: number; } { @@ -127,13 +125,11 @@ export function extractTestItemsAndCount( } else if (typeof arg === "number" && index === args.length - 1) { result.count = arg ?? undefined; return result; + } else if (isVSCodeTestItem(arg)) { + result.testItems.push(arg); + return result; } else if (typeof arg === "object") { - if ( - Object.prototype.hasOwnProperty.call(arg, "id") && - Object.prototype.hasOwnProperty.call(arg, "uri") - ) { - result.testItems.push(arg); - } + // Object but not a TestItem, just skip it return result; } else { throw new Error(`Unexpected argument ${arg} at index ${index}`); @@ -143,3 +139,15 @@ export function extractTestItemsAndCount( ); return result; } + +/** + * Checks if an object is a vscode.TestItem via duck typing. + */ +function isVSCodeTestItem(obj: unknown): obj is vscode.TestItem { + return ( + typeof obj === "object" && + obj !== null && + Object.prototype.hasOwnProperty.call(obj, "id") && + Object.prototype.hasOwnProperty.call(obj, "uri") + ); +}