diff --git a/CHANGELOG.md b/CHANGELOG.md index cfb0f29126..21949b996b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Features: Bug Fixes: +- Register `cmake.testProgram`, `cmake.testWorkingDirectory`, and `cmake.testArgs` as VS Code commands so they can be used as `${command:cmake.testProgram}` in launch.json from the Run and Debug panel. [#4574](https://github.com/microsoft/vscode-cmake-tools/issues/4574) - Fix "CMake: Run Without Debugging" not changing the working directory when the build directory changes. [#4549](https://github.com/microsoft/vscode-cmake-tools/issues/4549) - Fix CMake script path links not working in CHS/CSY/FRA/PLK locales due to localized quotes. [#4383](https://github.com/microsoft/vscode-cmake-tools/issues/4383) - Fix user-level tasks defined in `~/.config/Code/User/tasks.json` causing infinite spinner. [#4659](https://github.com/microsoft/vscode-cmake-tools/pull/4659) diff --git a/docs/debug-launch.md b/docs/debug-launch.md index 7d68c0792b..4430cd4776 100644 --- a/docs/debug-launch.md +++ b/docs/debug-launch.md @@ -213,8 +213,9 @@ You can substitute the value of any variable in the CMake cache by adding a `com You can also construct launch.json configurations that allow you to debug tests in the Test Explorer. -> **Note:** -> These launch.json configurations are to be used specifically from the UI of the Test Explorer. +There are two forms of these variables: +- `${command:cmake.testProgram}`, `${command:cmake.testArgs}`, `${command:cmake.testWorkingDirectory}` — These are resolved as VS Code command substitutions and work both from the **Test Explorer** and the **Run and Debug** panel. When launched from Run and Debug, you will be prompted to select a test. +- `${cmake.testProgram}`, `${cmake.testArgs}`, `${cmake.testWorkingDirectory}` — These are only resolved when launching from the **Test Explorer** UI. If used from the Run and Debug panel, they will not be substituted. The easiest way to do this is to construct the debug configuration using `cmake.testProgram` for the `program` field, `cmake.testArgs` for the `args` field, and `cmake.testWorkingDirectory` for the `cwd` field. @@ -227,11 +228,10 @@ A couple of examples: "name": "(ctest) Launch", "type": "cppdbg", "request": "launch", - "presentation": { "hidden": true }, // Resolved by CMake Tools: - "cwd": "${cmake.testWorkingDirectory}", - "program": "${cmake.testProgram}", - "args": [ "${cmake.testArgs}"], + "cwd": "${command:cmake.testWorkingDirectory}", + "program": "${command:cmake.testProgram}", + "args": [ "${command:cmake.testArgs}"], } ``` ### msvc @@ -240,10 +240,9 @@ A couple of examples: "name": "(ctest) Launch", "type": "cppvsdbg", "request": "launch", - "presentation": { "hidden": true }, // Resolved by CMake Tools: - "program": "${cmake.testProgram}", - "args": [ "${cmake.testArgs}"], + "program": "${command:cmake.testProgram}", + "args": [ "${command:cmake.testArgs}"], } ``` diff --git a/package.json b/package.json index 24b0dc71ea..2948358cb7 100644 --- a/package.json +++ b/package.json @@ -48,6 +48,9 @@ "onCommand:cmake.launchTargetDirectory", "onCommand:cmake.launchTargetFilename", "onCommand:cmake.launchTargetName", + "onCommand:cmake.testProgram", + "onCommand:cmake.testWorkingDirectory", + "onCommand:cmake.testArgs", "onCommand:cmake.getLaunchTargetPath", "onCommand:cmake.getLaunchTargetDirectory", "onCommand:cmake.getLaunchTargetFilename", diff --git a/src/ctest.ts b/src/ctest.ts index cb764d626e..27930f2bcf 100644 --- a/src/ctest.ts +++ b/src/ctest.ts @@ -1259,11 +1259,14 @@ export class CTestDriver implements vscode.Disposable { // Commands can't be used to replace array (i.e., args); and both test program and test args requires folder and // test name as parameters, which means one lauch config for each test. So replacing them here is a better way. chosenConfig.config = this.replaceAllInObject(chosenConfig.config, '${cmake.testProgram}', this.testProgram(testName)); + chosenConfig.config = this.replaceAllInObject(chosenConfig.config, '${command:cmake.testProgram}', this.testProgram(testName)); chosenConfig.config = this.replaceAllInObject(chosenConfig.config, '${cmake.testWorkingDirectory}', this.testWorkingDirectory(testName)); + chosenConfig.config = this.replaceAllInObject(chosenConfig.config, '${command:cmake.testWorkingDirectory}', this.testWorkingDirectory(testName)); // Replace cmake.testArgs wrapped in quotes, like `"${command:cmake.testArgs}"`, without any spaces in between, - // since we need to repalce the quotes as well. + // since we need to replace the quotes as well. chosenConfig.config = this.replaceArrayItems(chosenConfig.config, '${cmake.testArgs}', this.testArgs(testName)) as vscode.DebugConfiguration; + chosenConfig.config = this.replaceArrayItems(chosenConfig.config, '${command:cmake.testArgs}', this.testArgs(testName)) as vscode.DebugConfiguration; // Identify the session we started chosenConfig.config[magicKey] = magicValue; @@ -1307,7 +1310,7 @@ export class CTestDriver implements vscode.Disposable { } } - private testProgram(testName: string): string { + public testProgram(testName: string): string { if (this.tests) { for (const test of this.tests.tests) { if (test.name === testName) { @@ -1324,7 +1327,7 @@ export class CTestDriver implements vscode.Disposable { return ''; } - private testWorkingDirectory(testName: string): string { + public testWorkingDirectory(testName: string): string { const property = this.tests?.tests .find(test => test.name === testName)?.properties .find(prop => prop.name === 'WORKING_DIRECTORY'); @@ -1335,7 +1338,7 @@ export class CTestDriver implements vscode.Disposable { return ''; } - private testArgs(testName: string): string[] { + public testArgs(testName: string): string[] { if (this.tests) { for (const test of this.tests.tests) { if (test.name === testName) { diff --git a/src/extension.ts b/src/extension.ts index 81dca6ba4b..1b6a1c39f5 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1796,6 +1796,54 @@ export class ExtensionManager implements vscode.Disposable { }, folder); } + private async pickTestName(cmakeProject: CMakeProject): Promise { + const testNames = cmakeProject.cTestController.getTestNames(); + if (!testNames || testNames.length === 0) { + void vscode.window.showWarningMessage(localize('no.ctest.tests.found', 'No CTest tests found. Make sure to configure and build your project first.')); + return undefined; + } + if (testNames.length === 1) { + return testNames[0]; + } + const chosen = await vscode.window.showQuickPick(testNames, { + placeHolder: localize('choose.test', 'Select a test') + }); + return chosen; + } + + testProgram(folder?: vscode.WorkspaceFolder | string) { + telemetry.logEvent("substitution", { command: "testProgram" }); + return this.queryCMakeProject(async cmakeProject => { + const testName = await this.pickTestName(cmakeProject); + if (!testName) { + return ''; + } + return cmakeProject.cTestController.testProgram(testName); + }, folder); + } + + testWorkingDirectory(folder?: vscode.WorkspaceFolder | string) { + telemetry.logEvent("substitution", { command: "testWorkingDirectory" }); + return this.queryCMakeProject(async cmakeProject => { + const testName = await this.pickTestName(cmakeProject); + if (!testName) { + return ''; + } + return cmakeProject.cTestController.testWorkingDirectory(testName); + }, folder); + } + + testArgs(folder?: vscode.WorkspaceFolder | string) { + telemetry.logEvent("substitution", { command: "testArgs" }); + return this.queryCMakeProject(async cmakeProject => { + const testName = await this.pickTestName(cmakeProject); + if (!testName) { + return []; + } + return cmakeProject.cTestController.testArgs(testName); + }, folder); + } + buildTargetName(folder?: vscode.WorkspaceFolder | string) { telemetry.logEvent("substitution", { command: "buildTargetName" }); return this.queryCMakeProject(cmakeProject => cmakeProject.buildTargetName(), folder); @@ -2401,6 +2449,9 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle 'getLaunchTargetDirectory', 'getLaunchTargetFilename', 'getLaunchTargetName', + 'testProgram', + 'testWorkingDirectory', + 'testArgs', 'buildTargetName', 'buildKit', 'buildType',