Skip to content

Commit e32a469

Browse files
Copilotsnehara99
andauthored
Navigate to test source file when clicking CTest in Test Explorer (#4761)
* Initial plan * feat: navigate to test source file when clicking test in Test Explorer Match test executables to CMake target source files via the code model, so clicking a CTest unit test in the Test Explorer navigates to the actual test source file instead of CMakeLists.txt. Priority order for test location resolution: 1. DEF_SOURCE_LINE property (user-specified) 2. Code model source file matching (new - matches executable to target) 3. Backtrace fallback (CMakeLists.txt location) Fixes #4449 Co-authored-by: snehara99 <[email protected]> * Remove PR citation from changelog entry Co-authored-by: snehara99 <[email protected]> * Fix null safety: guard test.command access in refreshTestsInTestExplorer Co-authored-by: snehara99 <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: snehara99 <[email protected]>
1 parent c7ce061 commit e32a469

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Features:
1414
- Support `targetName` argument for launch-target command substitutions (`cmake.launchTargetPath`, etc.) via `${input:...}` variables, enabling build-before-run for non-active executable targets without changing the active launch target. [#4656](https://github.com/microsoft/vscode-cmake-tools/issues/4656)
1515

1616
Improvements:
17+
- Clicking on a CTest unit test in the Test Explorer now navigates to the test source file by matching the test executable to its CMake target's source files. [#4449](https://github.com/microsoft/vscode-cmake-tools/issues/4449)
1718
- Make "CMake: Add ... Preset" commands available in the command palette when `cmake.useCMakePresets` is set to `auto`, even before a CMakePresets.json file exists. [#4401](https://github.com/microsoft/vscode-cmake-tools/issues/4401)
1819
- Improve CMake syntax highlighting with command-scoped keyword rules, expanded variable recognition, and better generator-expression support. Remove obsolete deprecated-keyword entries that caused false positives on user-defined names. [#4709](https://github.com/microsoft/vscode-cmake-tools/issues/4709) [#4508](https://github.com/microsoft/vscode-cmake-tools/issues/4508) [#4613](https://github.com/microsoft/vscode-cmake-tools/issues/4613)
1920
- Add MSVC linker error problem matching to the Problems pane. [#4675](https://github.com/microsoft/vscode-cmake-tools/pull/4675) [@bradphelan](https://github.com/bradphelan)

src/ctest.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,40 @@ export class CTestDriver implements vscode.Disposable {
778778
return undefined;
779779
}
780780

781+
/**
782+
* Builds a map from normalized executable paths to source file information
783+
* by looking at the code model content from the CMake driver.
784+
*/
785+
private buildExecutableToSourcesMap(driver: CMakeDriver): Map<string, { sourceDir: string; sources: string[] }> {
786+
const map = new Map<string, { sourceDir: string; sources: string[] }>();
787+
const codeModelContent = driver.codeModelContent;
788+
if (!codeModelContent) {
789+
return map;
790+
}
791+
for (const config of codeModelContent.configurations) {
792+
for (const project of config.projects) {
793+
for (const target of project.targets) {
794+
if (target.artifacts && target.fileGroups && target.sourceDirectory) {
795+
// Collect compilable (non-generated) source files from file groups
796+
const sources: string[] = [];
797+
for (const fg of target.fileGroups) {
798+
if (fg.language && !fg.isGenerated && fg.sources) {
799+
sources.push(...fg.sources);
800+
}
801+
}
802+
if (sources.length > 0) {
803+
for (const artifact of target.artifacts) {
804+
const normalizedArtifact = util.platformNormalizePath(artifact);
805+
map.set(normalizedArtifact, { sourceDir: target.sourceDirectory, sources });
806+
}
807+
}
808+
}
809+
}
810+
}
811+
}
812+
return map;
813+
}
814+
781815
private createTestItemAndSuiteTree(testName: string, testExplorerRoot: vscode.TestItem, initializedTestExplorer: vscode.TestController, uri?: vscode.Uri): TestAndParentSuite {
782816
let parentSuiteItem = testExplorerRoot;
783817
let testLabel = testName;
@@ -848,6 +882,9 @@ export class CTestDriver implements vscode.Disposable {
848882
}
849883
} else if (testType === "CTestInfo" && this.tests !== undefined) {
850884
if (this.tests && this.tests.kind === 'ctestInfo') {
885+
// Build a map from executable paths to source files using the code model
886+
const executableToSources = this.buildExecutableToSourcesMap(driver);
887+
851888
this.tests.tests.forEach(test => {
852889
let testDefFile: string | undefined;
853890
let testDefLine: number | undefined;
@@ -868,6 +905,16 @@ export class CTestDriver implements vscode.Disposable {
868905
}
869906
}
870907

908+
// Try to find the test source file by matching the test executable to a CMake target
909+
if (!testDefFile && test.command && test.command.length > 0) {
910+
const testExe = util.platformNormalizePath(test.command[0]);
911+
const targetInfo = executableToSources.get(testExe);
912+
if (targetInfo && targetInfo.sources.length > 0) {
913+
testDefFile = path.resolve(targetInfo.sourceDir, targetInfo.sources[0]);
914+
testDefLine = 1;
915+
}
916+
}
917+
871918
const nodes = this.tests!.backtraceGraph.nodes;
872919
if (!testDefFile && test.backtrace !== undefined && nodes[test.backtrace] !== undefined) {
873920
// Use the backtrace graph to find the file and line number

0 commit comments

Comments
 (0)