Skip to content

Commit cc2bb81

Browse files
committed
Merge branch 'main' into reqs_uutstub_flag
2 parents 5bbca81 + 567a10d commit cc2bb81

10 files changed

Lines changed: 244 additions & 27 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
All notable changes to the "vectorcastTestExplorer" extension will be documented in this file.
44

5-
## [1.0.27] - 2025-12-17
5+
## [1.0.28] - 2026-01-27
6+
7+
### Added
8+
- Added possibility to open up multiple workdpace folders
9+
- Added Button to open up a Source File under Test from the Testing Pane
10+
11+
## [1.0.27] - 2025-12-15
612

713
### Bug fixes
814
- The LLM compatibility mode is now correctly applied to pre-run model usability checks too
915

10-
## [1.0.26] - 2025-12-15
16+
## [1.0.26] - 2025-12-17
1117

1218
### Added
1319
- Improved Reqs2x progress tracking to be more precise

package.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "vectorcasttestexplorer",
33
"displayName": "VectorCAST Test Explorer",
44
"description": "VectorCAST Test Explorer for VS Code",
5-
"version": "1.0.27",
5+
"version": "1.0.28",
66
"license": "MIT",
77
"repository": {
88
"type": "git",
@@ -224,6 +224,11 @@
224224
"category": "VectorCAST Test Explorer",
225225
"title": "Remove Coded Tests"
226226
},
227+
{
228+
"command": "vectorcastTestExplorer.openSourceFileFromTestpaneCommand",
229+
"category": "VectorCAST Test Explorer",
230+
"title": "Open Source File under Test"
231+
},
227232
{
228233
"command": "vectorcastTestExplorer.insertBasisPathTests",
229234
"category": "VectorCAST Test Explorer",
@@ -769,6 +774,10 @@
769774
"command": "vectorcastTestExplorer.removeCodedTests",
770775
"when": "never"
771776
},
777+
{
778+
"command": "vectorcastTestExplorer.openSourceFileFromTestpaneCommand",
779+
"when": "never"
780+
},
772781
{
773782
"command": "vectorcastTestExplorer.insertBasisPathTests",
774783
"when": "never"
@@ -1072,6 +1081,11 @@
10721081
"group": "vcast.testScript",
10731082
"when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId in vectorcastTestExplorer.vcastHasCodedTestsList"
10741083
},
1084+
{
1085+
"command": "vectorcastTestExplorer.openSourceFileFromTestpaneCommand",
1086+
"group": "vcast.enviroManagement",
1087+
"when": "testId =~ /^vcast:.*$/ && !(testId =~ /^.*<<COMPOUND>>.*$/) && !(testId =~ /^.*<<INIT>>.*$/) && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && testId not in vectorcastTestExplorer.vcastEnviroList"
1088+
},
10751089
{
10761090
"command": "vectorcastTestExplorer.insertBasisPathTests",
10771091
"group": "vcast.testGeneration",

python/vTestInterface.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,6 @@ def processCommandLogic(mode, clicast, pathToUse, testString="", options=""):
804804
except Exception as err:
805805
errors.append(f"{vce_path}: {str(err)}")
806806

807-
topLevel["testData"] = enviro_list[0]["testData"] if enviro_list else []
808-
topLevel["unitData"] = enviro_list[0]["unitData"] if enviro_list else []
809807
topLevel["enviro"] = enviro_list
810808
if errors:
811809
topLevel["errors"] = errors

src/extension.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ import {
155155
addIncludePath,
156156
envIsEmbeddedInProject,
157157
getEnviroNameFromFile,
158+
getEnvironmentData,
158159
getLevelFromNodeId,
159160
getVcmRoot,
160161
openProjectFromEnviroPath,
@@ -1235,6 +1236,78 @@ function configureExtension(context: vscode.ExtensionContext) {
12351236
);
12361237
context.subscriptions.push(getMCDCReportCommand);
12371238

1239+
// Command: vectorcastTestExplorer.openSourceFileFromTestpaneCommand
1240+
let openSourceFileFromTestpaneCommand = vscode.commands.registerCommand(
1241+
"vectorcastTestExplorer.openSourceFileFromTestpaneCommand",
1242+
async (args: any) => {
1243+
if (args) {
1244+
const testNode: testNodeType = getTestNode(args.id);
1245+
if (testNode) {
1246+
const enviroPath = testNode.enviroPath;
1247+
const unitName = testNode.unitName;
1248+
const functionName = testNode.functionName;
1249+
const envData = await getEnvironmentData(enviroPath);
1250+
1251+
if (envData.unitData) {
1252+
for (const unitInfo of envData.unitData) {
1253+
// Extract unit name from path to match against unitName
1254+
const pathBasename = path.basename(
1255+
unitInfo.path,
1256+
path.extname(unitInfo.path)
1257+
);
1258+
1259+
if (pathBasename === unitName) {
1260+
const sourcePath = unitInfo.path;
1261+
const uri = vscode.Uri.file(sourcePath);
1262+
1263+
// Determine the line number to open at (0 = top default)
1264+
let lineNumber = 0;
1265+
1266+
// If functionName is defined, try to find it in the function list
1267+
if (functionName && unitInfo.functionList) {
1268+
for (const func of unitInfo.functionList) {
1269+
if (
1270+
func.name === functionName &&
1271+
func.startLine !== undefined
1272+
) {
1273+
lineNumber = func.startLine;
1274+
break;
1275+
}
1276+
}
1277+
}
1278+
1279+
// Open the document at the specified line
1280+
const document = await vscode.workspace.openTextDocument(uri);
1281+
const position = new vscode.Position(
1282+
Math.max(0, lineNumber - 1),
1283+
0
1284+
);
1285+
const selection = new vscode.Range(position, position);
1286+
1287+
await vscode.window.showTextDocument(document, {
1288+
preview: false, // open as a real tab
1289+
preserveFocus: false,
1290+
selection: selection,
1291+
});
1292+
1293+
break;
1294+
}
1295+
}
1296+
} else {
1297+
vscode.window.showErrorMessage(
1298+
`Could not find environment data for: ${enviroPath}`
1299+
);
1300+
}
1301+
} else {
1302+
vscode.window.showErrorMessage(
1303+
`Unable to open Source File for Node: ${args.id}`
1304+
);
1305+
}
1306+
}
1307+
}
1308+
);
1309+
context.subscriptions.push(openSourceFileFromTestpaneCommand);
1310+
12381311
let showRequirementsCommand = vscode.commands.registerCommand(
12391312
"vectorcastTestExplorer.showRequirements",
12401313
async (args: any) => {

src/testPane.ts

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -47,16 +47,17 @@ import {
4747
addLaunchConfiguration,
4848
cleanTestResultsPaneMessage,
4949
forceLowerCaseDriveLetter,
50-
getWorkspaceRootPath,
50+
getWorkspaceRootPaths,
5151
loadLaunchFile,
52+
mergeWorkspaceEnvResponses,
5253
normalizePath,
5354
openFileWithLineSelected,
5455
} from "./utilities";
5556

5657
import {
5758
deleteSingleTest,
5859
getCBTNamesFromFile,
59-
getDataForEnvironment,
60+
getDataForEnvironmentFromAPI,
6061
getDataForProject,
6162
getWorkspaceEnvDataVPython,
6263
loadTestScriptIntoEnvironment,
@@ -133,16 +134,14 @@ type UnitData = {
133134
functionList: FunctionUnitData[];
134135
};
135136

136-
type EnviroData = {
137+
export type EnviroData = {
137138
vcePath: string;
138139
testData: FileTestData[];
139140
unitData: UnitData[];
140141
mockingSupport: boolean;
141142
};
142143

143-
type CachedWorkspaceData = {
144-
testData: FileTestData[];
145-
unitData: UnitData[];
144+
export type CachedWorkspaceData = {
146145
enviro: EnviroData[];
147146
errors?: string[];
148147
};
@@ -704,7 +703,7 @@ let vcastHasCodedTestsList: string[] = [];
704703

705704
// Global cache for workspace-wide env data
706705
// Used to avoid redundant API calls during refresh
707-
let cachedWorkspaceEnvData: CachedWorkspaceData | null = null;
706+
export let cachedWorkspaceEnvData: CachedWorkspaceData | null = null;
708707

709708
export function clearCachedWorkspaceEnvData(): void {
710709
cachedWorkspaceEnvData = null;
@@ -754,11 +753,11 @@ async function loadEnviroData(
754753
}
755754
} else {
756755
// Fallback in case the cache is not build, but it should be
757-
return await getDataForEnvironment(buildPathDir);
756+
return await getDataForEnvironmentFromAPI(buildPathDir);
758757
}
759758
} else {
760759
// Individual environment fetch (e.g. adding new test scripts, coded tests, ...)
761-
return await getDataForEnvironment(buildPathDir);
760+
return await getDataForEnvironmentFromAPI(buildPathDir);
762761
}
763762
// We have a valid build directory, but we couldn't find a matching VCE file in the workspace data.
764763
vectorMessage(
@@ -768,13 +767,19 @@ async function loadEnviroData(
768767
}
769768

770769
async function buildEnvDataCacheForCurrentDir() {
771-
const workspaceDir = getWorkspaceRootPath();
772-
if (workspaceDir) {
773-
cachedWorkspaceEnvData = await getWorkspaceEnvDataVPython(workspaceDir);
774-
} else {
775-
// This should not be possible as we have env data, but just in case
770+
const folderPaths = getWorkspaceRootPaths();
771+
if (!folderPaths.length) {
776772
vectorMessage("No workspace root found, cannot refresh environment data.");
773+
return;
777774
}
775+
776+
// Query each root in parallel
777+
const responses = await Promise.all(
778+
folderPaths.map((p) => getWorkspaceEnvDataVPython(p))
779+
);
780+
781+
// Merge them
782+
cachedWorkspaceEnvData = await mergeWorkspaceEnvResponses(responses);
778783
}
779784

780785
/**
@@ -945,6 +950,7 @@ async function loadAllVCTests(
945950
vcastUnbuiltEnviroList = [];
946951
clearEnviroDataCache();
947952
clearTestNodeCache();
953+
clearCachedWorkspaceEnvData();
948954

949955
// Resets the "used" and empty/unused compilers / testsuites
950956
clearGlobalCompilersAndTestsuites();
@@ -1010,7 +1016,6 @@ async function loadAllVCTests(
10101016
// end if workspace folders
10111017

10121018
checkWorkspaceEnvDataForErrors();
1013-
clearCachedWorkspaceEnvData();
10141019

10151020
// In case we have empty testsuites or compilers in the project,
10161021
// we won't find them in the Env data so we have to add them manually here

src/utilities.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { errorLevel, vectorMessage } from "./messagePane";
99
import { getGlobalCoverageData } from "./vcastTestInterface";
1010
import { rebuildEnvironment } from "./vcastAdapter";
1111
import { rebuildEnvironmentCallback } from "./callbacks";
12+
import { CachedWorkspaceData, EnviroData } from "./testPane";
1213
import { executeWithRealTimeEchoWithProgress } from "./vcastCommandRunner";
1314
import { getVectorCastInstallationLocation } from "./vcastInstallation";
1415

@@ -413,12 +414,34 @@ export async function updateCoverageAndRebuildEnv() {
413414
/**
414415
* Returns the root of the opened workspace.
415416
*/
416-
export function getWorkspaceRootPath(): string | undefined {
417+
export function getWorkspaceRootPaths(): string[] {
417418
const folders = vscode.workspace.workspaceFolders;
418-
if (folders && folders.length > 0) {
419-
return folders[0].uri.fsPath;
419+
if (!folders || folders.length === 0) return [];
420+
return folders.map((f) => f.uri.fsPath);
421+
}
422+
423+
/**
424+
* Merges multiple workspace responses into a single CachedWorkspaceData object.
425+
*/
426+
export async function mergeWorkspaceEnvResponses(
427+
responses: CachedWorkspaceData[]
428+
): Promise<CachedWorkspaceData> {
429+
const allErrors: string[] = [];
430+
const allEnvs: EnviroData[] = [];
431+
432+
for (const resp of responses) {
433+
if (resp.errors) {
434+
allErrors.push(...resp.errors);
435+
}
436+
if (resp.enviro) {
437+
allEnvs.push(...resp.enviro);
438+
}
420439
}
421-
return undefined;
440+
441+
return {
442+
enviro: allEnvs,
443+
errors: allErrors.length ? allErrors : undefined,
444+
};
422445
}
423446

424447
export async function getFullEnvReport(

src/vcastAdapter.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,9 @@ function getProjectDataFromPython(projectDirectoryPath: string): any {
636636

637637
// Get Environment Data ---------------------------------------------------------------
638638
// Server logic is in a separate function below
639-
export async function getDataForEnvironment(enviroPath: string): Promise<any> {
639+
export async function getDataForEnvironmentFromAPI(
640+
enviroPath: string
641+
): Promise<any> {
640642
// what we get back is a JSON formatted string (if the command works)
641643
// that has two sub-fields: testData, and unitData
642644
vectorMessage("Processing environment data for: " + enviroPath);
@@ -708,7 +710,7 @@ export async function getWorkspaceEnvDataVPython(
708710
);
709711
let jsonData = getJsonDataFromTestInterface(commandToRun, workspaceDir);
710712

711-
vectorMessage("Building environments data for workspace...");
713+
vectorMessage(`Building environments data for workspace: ${workspaceDir}`);
712714

713715
return jsonData;
714716
}

src/vcastUtilities.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424

2525
import {
2626
dumpTestScriptFile,
27+
getDataForEnvironmentFromAPI,
2728
openProjectInVcast,
2829
runATGCommands,
2930
runBasisPathCommands,
@@ -43,6 +44,7 @@ import {
4344

4445
import { clientRequestType, vcastCommandType } from "../src-common/vcastServer";
4546
import {
47+
cachedWorkspaceEnvData,
4648
globalController,
4749
globalProjectDataCache,
4850
globalProjectMap,
@@ -838,3 +840,29 @@ export function getLevelFromNodeId(path: string) {
838840

839841
return { projectName, level };
840842
}
843+
844+
/**
845+
* Retrieves Environment Data from the Cache or by asking the API if the cache
846+
* is empty
847+
* @param enviroPath Path of Environment
848+
*/
849+
export async function getEnvironmentData(enviroPath: string) {
850+
// Add .vce extension to enviroPath for cache key
851+
const cacheKey = enviroPath.endsWith(".vce")
852+
? enviroPath
853+
: `${enviroPath}.vce`;
854+
855+
// Try to get data from cache first
856+
let envData = null;
857+
if (cachedWorkspaceEnvData) {
858+
envData =
859+
cachedWorkspaceEnvData[cacheKey as keyof typeof cachedWorkspaceEnvData];
860+
}
861+
862+
// If not in cache, fetch it
863+
if (!envData) {
864+
envData = await getDataForEnvironmentFromAPI(enviroPath);
865+
}
866+
867+
return envData;
868+
}

0 commit comments

Comments
 (0)