Skip to content
Open
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 8 additions & 9 deletions docs/debug-launch.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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}"],
}
```

Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
11 changes: 7 additions & 4 deletions src/ctest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<vscode.DebugConfiguration>(chosenConfig.config, '${cmake.testProgram}', this.testProgram(testName));
chosenConfig.config = this.replaceAllInObject<vscode.DebugConfiguration>(chosenConfig.config, '${command:cmake.testProgram}', this.testProgram(testName));
chosenConfig.config = this.replaceAllInObject<vscode.DebugConfiguration>(chosenConfig.config, '${cmake.testWorkingDirectory}', this.testWorkingDirectory(testName));
chosenConfig.config = this.replaceAllInObject<vscode.DebugConfiguration>(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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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');
Expand All @@ -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) {
Expand Down
51 changes: 51 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1796,6 +1796,54 @@ export class ExtensionManager implements vscode.Disposable {
}, folder);
}

private async pickTestName(cmakeProject: CMakeProject): Promise<string | undefined> {
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);
Expand Down Expand Up @@ -2401,6 +2449,9 @@ async function setup(context: vscode.ExtensionContext, progress?: ProgressHandle
'getLaunchTargetDirectory',
'getLaunchTargetFilename',
'getLaunchTargetName',
'testProgram',
'testWorkingDirectory',
'testArgs',
'buildTargetName',
'buildKit',
'buildType',
Expand Down