diff --git a/package.json b/package.json index 8cb08977..869f2ddb 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,8 @@ "main": "out/extension.js", "activationEvents": [ "workspaceContains:**/UNITDATA.VCD", - "workspaceContains:**/*.vcm" + "workspaceContains:**/*.vcm", + "workspaceContains:**/*.vcp" ], "contributes": { "viewsWelcome": [ @@ -989,97 +990,97 @@ { "command": "vectorcastTestExplorer.updateProjectEnvironment", "group": "vcast.project", - "when": "(testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker) && !config.vectorcastTestExplorer.automaticallyUpdateManageProject" + "when": "(testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker) && !config.vectorcastTestExplorer.automaticallyUpdateManageProject && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.buildProjectEnviro", "group": "vcast.build", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.getEnvFullReport", "group": "vcast.build", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.buildExecuteIncremental", "group": "vcast.build", - "when": "vectorcastTestExplorer.globalProjectIsOpenedChecker && ( testId =~ /\\.vcm$/ || testId in vectorcastTestExplorer.globalProjectCompilers || testId in vectorcastTestExplorer.globalProjectTestsuites || (testId =~ /^vcast:.*$/ && ( testId in vectorcastTestExplorer.vcastEnviroList || testId in vectorcastTestExplorer.vcastUnbuiltEnviroList)))" + "when": "vectorcastTestExplorer.globalProjectIsOpenedChecker && ( testId =~ /\\.vcm$/ || testId in vectorcastTestExplorer.globalProjectCompilers || testId in vectorcastTestExplorer.globalProjectTestsuites || (testId =~ /^vcast:.*$/ && ( testId in vectorcastTestExplorer.vcastEnviroList || testId in vectorcastTestExplorer.vcastUnbuiltEnviroList))) && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.rebuildEnviro", "group": "vcast.build", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.cleanEnviro", "group": "vcast.build", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.globalProjectIsOpenedChecker && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.globalProjectIsOpenedChecker && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.openVCAST", "group": "vcast.enviroManagement", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.deleteEnviro", "group": "vcast.enviroManagement", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && !vectorcastTestExplorer.globalProjectIsOpenedChecker" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && !vectorcastTestExplorer.globalProjectIsOpenedChecker && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.deleteEnviroFromProject", "group": "vcast.enviroManagement", - "when": "testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker" + "when": "testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.removeTestsuite", "group": "vcast.enviroManagement", - "when": "testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker" + "when": "testId =~ /^vcast:.*$/ && (testId in vectorcastTestExplorer.vcastUnbuiltEnviroList || testId in vectorcastTestExplorer.vcastEnviroList) && vectorcastTestExplorer.globalProjectIsOpenedChecker && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.editTestScript", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.createTestScript", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /.*coded_tests_driver.*/ )" + "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /.*coded_tests_driver.*/ ) && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.editCodedTest", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && testId =~ /.*coded_tests_driver.+/" + "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && testId =~ /.*coded_tests_driver.+/ && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.addCodedTests", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId not in vectorcastTestExplorer.vcastHasCodedTestsList" + "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId not in vectorcastTestExplorer.vcastHasCodedTestsList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.generateCodedTests", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId not in vectorcastTestExplorer.vcastHasCodedTestsList" + "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId not in vectorcastTestExplorer.vcastHasCodedTestsList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.removeCodedTests", "group": "vcast.testScript", - "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId in vectorcastTestExplorer.vcastHasCodedTestsList" + "when": "testId =~ /^vcast:.*$/ && testId =~ /.*coded_tests_driver$/ && testId in vectorcastTestExplorer.vcastHasCodedTestsList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.insertBasisPathTests", "group": "vcast.testGeneration", - "when": "testId =~ /^vcast:.*$/ && !(testId =~ /^.*<>.*$/) && !(testId =~ /^.*<>.*$/) && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && !(testId =~ /^.*<>.*$/) && !(testId =~ /^.*<>.*$/) && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.insertATGTests", "group": "vcast.testGeneration", - "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.atgAvailable && !(testId =~ /^.*<>.*$/) && !(testId =~ /^.*<>.*$/) && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.atgAvailable && !(testId =~ /^.*<>.*$/) && !(testId =~ /^.*<>.*$/) && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.deleteTest", "group": "vcast.delete", - "when": "testId =~ /^vcast:.*$/ && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && !(testId =~ /.*coded_tests_driver.*/) && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.deleteTestsuite", @@ -1094,37 +1095,37 @@ { "command": "vectorcastTestExplorer.importRequirementsFromGateway", "group": "vcast@8", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId not in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId not in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.generateRequirements", "group": "vcast@8", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId not in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId not in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.showRequirements", "group": "vcast@9", - "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled" + "when": "testId =~ /^vcast:.*$/ && testId in vectorcastTestExplorer.vcastEnviroList && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.generateTestsFromRequirements", "group": "vcast@10", - "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled" + "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.removeRequirements", "group": "vcast@9", - "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled" + "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.populateRequirementsGateway", "group": "vcast@9", - "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled" + "when": "testId =~ /^vcast:.*$/ && vectorcastTestExplorer.reqs2xFeatureEnabled && testId in vectorcastTestExplorer.vcastRequirementsAvailable && vectorcastTestExplorer.generateRequirementsEnabled && !(testId =~ /\\.vcp$/)" }, { "command": "vectorcastTestExplorer.viewResults", "group": "vcast.results", - "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList" + "when": "testId =~ /^vcast:.*$/ && testId not in vectorcastTestExplorer.vcastEnviroList && testId not in vectorcastTestExplorer.vcastUnbuiltEnviroList && !(testId =~ /\\.vcp$/)" } ] }, diff --git a/python/mcdcReport.py b/python/mcdcReport.py index 3c632695..1bf364e7 100644 --- a/python/mcdcReport.py +++ b/python/mcdcReport.py @@ -1,10 +1,12 @@ import argparse import pathlib import sys +import os from vector.apps.DataAPI.unit_test_api import UnitTestApi +from vector.apps.DataAPI.cover_api import CoverApi -from pythonUtilities import monkeypatch_custom_css +from pythonUtilities import monkeypatch_custom_css, get_api_context def parse_args(): @@ -32,8 +34,13 @@ def parse_args(): def get_mcdc_lines(env): all_lines_with_data = {} - with UnitTestApi(env) as api: - for unit in api.Unit.filter(): + ApiClass, entity_attr = get_api_context(env) + + with ApiClass(env) as api: + # Access the API property (api.Unit or api.File) dynamically + source_modules = getattr(api, entity_attr) + + for unit in source_modules.filter(): for mcdc_dec in unit.cover_data.mcdc_decisions: if not mcdc_dec.num_conditions: continue @@ -63,11 +70,15 @@ def generate_mcdc_report(env, unit_filter, line_filter, output): # Patch get_option to use our CSS without setting the CFG option monkeypatch_custom_css(custom_css) - # Open-up the unit test API - with UnitTestApi(env) as api: + # We have to check whether env is the path to a "Normal" env or to a "Cover Project" + # and therefore use a different API + ApiClass, entity_attr = get_api_context(env) + + with ApiClass(env) as api: + source_modules = getattr(api, entity_attr) # Find and check for our unit unit_found = False - for unit in api.Unit.filter(name=unit_filter): + for unit in source_modules.filter(name=unit_filter): unit_found = True # Spin through all MCDC decisions looking for the one on our line diff --git a/python/pythonUtilities.py b/python/pythonUtilities.py index cc70ce89..7999ea7e 100644 --- a/python/pythonUtilities.py +++ b/python/pythonUtilities.py @@ -6,6 +6,10 @@ import re from vector.apps.DataAPI.configuration import EnvironmentMixin +from vector.apps.DataAPI.unit_test_api import UnitTestApi +from vector.apps.DataAPI.cover_api import CoverApi + + # This contains the clicast command that was used to start the data server globalClicastCommand = "" @@ -233,3 +237,20 @@ def repl(match): return match.group(0) return env_var_pattern.sub(repl, path) + + +def get_api_context(env_path): + """ + Determines if the environment is a Cover Project or a standard Unit Test env. + And returns what API we need to use. + """ + clean_path = os.path.normpath(env_path) + + # Check if it's a Cover project by checking if there is a vcp file + # with the same name on the same level like the build dir (env_path) + vcp_file = clean_path + ".vcp" + if os.path.isfile(vcp_file): + return CoverApi, "File" + + # If there is no vcp file --> normal env + return UnitTestApi, "Unit" diff --git a/python/vTestInterface.py b/python/vTestInterface.py index c9b54c0c..3943a0e9 100644 --- a/python/vTestInterface.py +++ b/python/vTestInterface.py @@ -36,6 +36,7 @@ from vector.apps.DataAPI.manage_api import VCProjectApi from vector.apps.DataAPI.vcproject_models import EnvironmentType from vector.apps.DataAPI.unit_test_api import UnitTestApi +from vector.apps.DataAPI.cover_api import CoverApi from vector.lib.core.system import cd from vector.enums import COVERAGE_TYPE_TYPE_T @@ -720,19 +721,28 @@ def getProjectCompilerData(api): return compilerList -def find_vce_files(root_dir): +def find_environment_files(root_dir): + """ + Scans the directory tree once and returns two lists: + 1. vce_files: Paths to vce files for Unit Test environments + 2. vcp_files: Paths to vcp files for Cover Projects + """ vce_files = [] + vcp_files = [] def scan_dir(path): with os.scandir(path) as entries: for entry in entries: - if entry.is_file() and entry.name.endswith(".vce"): - vce_files.append(entry.path) + if entry.is_file(): + if entry.name.endswith(".vce"): + vce_files.append(entry.path) + elif entry.name.endswith(".vcp"): + vcp_files.append(entry.path) elif entry.is_dir(follow_symlinks=False): scan_dir(entry.path) scan_dir(root_dir) - return vce_files + return vce_files, vcp_files def processCommandLogic(mode, clicast, pathToUse, testString="", options=""): @@ -780,10 +790,14 @@ def processCommandLogic(mode, clicast, pathToUse, testString="", options=""): elif mode == "getWorkspaceEnviroData": enviro_list = [] + vcp_list = [] errors = [] topLevel = {} - vce_files = find_vce_files(pathToUse) + # Scan directory for both file types + vce_files, vcp_files = find_environment_files(pathToUse) + + # Process VCE Files for vce_path in vce_files: try: api = UnitTestApi(vce_path) @@ -800,13 +814,40 @@ def processCommandLogic(mode, clicast, pathToUse, testString="", options=""): "mockingSupport": mocking_support, } ) - except Exception as err: errors.append(f"{vce_path}: {str(err)}") + # Process VCP Files + for vcp_path in vcp_files: + try: + api = CoverApi(vcp_path) + test_data = [] # Cover projects do not have test data + unit_data = getUnitData(api) + mocking_support = False # Cover projects do not support mocking + api.close() + + vcp_list.append( + { + "vcpPath": normalize_path(vcp_path), + "testData": test_data, + "unitData": unit_data, + "mockingSupport": mocking_support, + } + ) + except Exception as err: + errors.append(f"{vcp_path}: {str(err)}") + + # Construct Top Level Object + # Defaulting top-level data to the first VCE environment found (if any) topLevel["testData"] = enviro_list[0]["testData"] if enviro_list else [] topLevel["unitData"] = enviro_list[0]["unitData"] if enviro_list else [] + + if not topLevel["unitData"] and vcp_list: + topLevel["unitData"] = vcp_list[0]["unitData"] + topLevel["enviro"] = enviro_list + topLevel["vcp"] = vcp_list + if errors: topLevel["errors"] = errors diff --git a/src/editorDecorator.ts b/src/editorDecorator.ts index 9719e6a0..20ede5d5 100644 --- a/src/editorDecorator.ts +++ b/src/editorDecorator.ts @@ -3,7 +3,12 @@ import { DecorationRenderOptions, TextEditorDecorationType } from "vscode"; import { testNodeType } from "./testData"; -import { getEnvPathForFilePath, getRangeOption } from "./utilities"; +import { + getEnvPathForFilePath, + getRangeOption, + normalizePath, + resolveVcpPaths, +} from "./utilities"; import { checksumMatchesEnvironment } from "./vcastTestInterface"; import { getMCDCCoverageLines } from "./vcastAdapter"; @@ -32,12 +37,18 @@ export async function updateCurrentActiveUnitMCDCLines() { let activeEditor = vscode.window.activeTextEditor; if (activeEditor) { // First we need to get the env name from the active file - const filePath = activeEditor.document.uri.fsPath; - const enviroPath = getEnvPathForFilePath(filePath); - + const filePath = normalizePath(activeEditor.document.uri.fsPath); + let enviroPath = getEnvPathForFilePath(filePath); // Get the unit name based on the file name without extension const fullPath = activeEditor.document.fileName; - const unitName = path.basename(fullPath, path.extname(fullPath)); + let unitName = path.basename(fullPath, path.extname(fullPath)); + + // If the file is in a cover project, we need adapt the paths + ({ enviroPath, unitName } = resolveVcpPaths( + enviroPath, + unitName, + fullPath + )); // Get all mcdc lines for every unit and parse it into JSON if (enviroPath) { diff --git a/src/extension.ts b/src/extension.ts index 1caa308f..99a2ba60 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -79,6 +79,7 @@ import { forceLowerCaseDriveLetter, decodeVar, getFullEnvReport, + resolveVcpPaths, } from "./utilities"; import { @@ -1221,10 +1222,18 @@ function configureExtension(context: vscode.ExtensionContext) { const filePath = activeEditor ? activeEditor.document.uri.fsPath : fileFromUri; - const enviroPath = getEnvPathForFilePath(filePath); - const fileName = path.parse(filePath).name; + let enviroPath = getEnvPathForFilePath(filePath); + let unitName = path.parse(filePath).name; + + // If the file is in a cover project, we need adapt the paths + ({ enviroPath, unitName } = resolveVcpPaths( + enviroPath, + unitName, + filePath + )); + if (enviroPath) { - viewMCDCReport(enviroPath, fileName, args.lineNumber); + viewMCDCReport(enviroPath, unitName, args.lineNumber); } else { vscode.window.showErrorMessage( `Did not find environment name ${enviroPath} or path for file: ${filePath}` diff --git a/src/testData.ts b/src/testData.ts index d1d01b1d..178e93a0 100644 --- a/src/testData.ts +++ b/src/testData.ts @@ -1,6 +1,7 @@ import { normalizePath, quote } from "./utilities"; export interface environmentNodeDataType { + isVcp: boolean; projectPath: string; buildDirectory: string; isBuilt: boolean; diff --git a/src/testPane.ts b/src/testPane.ts index 2fe7dfaf..df2cbc6a 100644 --- a/src/testPane.ts +++ b/src/testPane.ts @@ -135,6 +135,7 @@ type UnitData = { type EnviroData = { vcePath: string; + vcpPath: string; testData: FileTestData[]; unitData: UnitData[]; mockingSupport: boolean; @@ -144,6 +145,7 @@ type CachedWorkspaceData = { testData: FileTestData[]; unitData: UnitData[]; enviro: EnviroData[]; + vcp: EnviroData[]; errors?: string[]; }; @@ -630,6 +632,35 @@ export function addFreeEnvironments( } } +/** + * Adds VCP (VectorCAST Cover Project) files found in the workspace cache to the environment list. + * These are treated as environments but will have no test children. + * @param environmentList A list to push environment data. + * @param workspaceRoot The workspace root directory path. + */ +export function addVcpEnvironments( + environmentList: any[], + workspaceRoot: string +): void { + // Check if the cached data exists and has the 'vcp' key we added in Python + if (cachedWorkspaceEnvData?.vcp) { + for (const vcpData of cachedWorkspaceEnvData.vcp) { + // vcpData contains: { vcpPath, testData (empty), unitData, mockingSupport } + const normalizedPath = normalizePath(vcpData.vcpPath); + const displayName = path.basename(normalizedPath); + + environmentList.push({ + projectPath: "", // VCPs are usually standalone or treated differently than managed envs + buildDirectory: normalizedPath, // We use the VCP file path as the identifier + isBuilt: true, + displayName: displayName, + workspaceRoot: workspaceRoot, + isVcp: true, + }); + } + } +} + /** * Checks if the given path is an environment of interest. * @param candidatePath - The path to check @@ -739,17 +770,29 @@ async function loadEnviroData( comingFromRefresh: boolean ): Promise { let buildDirDerivedFromVCEPath: string = ""; + let buildDirDerivedFromVCPPath: string = ""; let buildPathDir: string = enviroData.buildDirectory; if (comingFromRefresh) { // If we've already fetched the full workspace data, reuse it if (cachedWorkspaceEnvData) { - const enviroList = cachedWorkspaceEnvData["enviro"]; - vectorMessage(`Processing environment data for: ${buildPathDir}`); - for (const envAPIData of enviroList) { - buildDirDerivedFromVCEPath = envAPIData.vcePath.split(".vce")[0]; - if (buildDirDerivedFromVCEPath === buildPathDir) { - return envAPIData; + if (enviroData.isVcp) { + const vcpList = cachedWorkspaceEnvData["vcp"]; + vectorMessage(`Processing coverage project data for: ${buildPathDir}`); + for (const vcpAPIData of vcpList) { + buildDirDerivedFromVCPPath = vcpAPIData.vcpPath; + if (buildDirDerivedFromVCPPath === buildPathDir) { + return vcpAPIData; + } + } + } else { + const enviroList = cachedWorkspaceEnvData["enviro"]; + vectorMessage(`Processing environment data for: ${buildPathDir}`); + for (const envAPIData of enviroList) { + buildDirDerivedFromVCEPath = envAPIData.vcePath.split(".vce")[0]; + if (buildDirDerivedFromVCEPath === buildPathDir) { + return envAPIData; + } } } } else { @@ -980,6 +1023,9 @@ async function loadAllVCTests( ignoreEnvsInProject, environmentList ); + + // Add VCP (Cover Project) files + addVcpEnvironments(environmentList, workspaceRoot); } if (environmentList.length > 0) { diff --git a/src/utilities.ts b/src/utilities.ts index 16d2ab66..aa825fbc 100644 --- a/src/utilities.ts +++ b/src/utilities.ts @@ -406,7 +406,9 @@ export async function updateCoverageAndRebuildEnv() { } // Now rebuild every env so that the coverage is updated for (let enviroPath of envArray) { - await rebuildEnvironment(enviroPath, rebuildEnvironmentCallback); + if (!enviroPath.endsWith(".vcp")) { + await rebuildEnvironment(enviroPath, rebuildEnvironmentCallback); + } } } @@ -457,3 +459,25 @@ export async function getFullEnvReport( // Return the generated HTML file path return htmlReportPath; } + +/** + * Checks if the environment is a Cover Project (.vcp). + * If so, it adjusts the environment path (removing .vcp) and appends the + * file extension to the unit name, as required by the Cover API. + */ +export function resolveVcpPaths( + enviroPath: string | null, + unitName: string, + fullFilePath: string +): { enviroPath: string | null; unitName: string } { + if (enviroPath?.endsWith(".vcp")) { + // Cover projects require the full filename (e.g. "manager.c" instead of "manager") + unitName = unitName + path.extname(fullFilePath); + + // The build directory for VCP is the path without the .vcp extension + const parsed = path.parse(enviroPath); + enviroPath = path.join(parsed.dir, parsed.name); + } + + return { enviroPath, unitName }; +} diff --git a/src/vcastTestInterface.ts b/src/vcastTestInterface.ts index bf107ad5..1a977553 100644 --- a/src/vcastTestInterface.ts +++ b/src/vcastTestInterface.ts @@ -189,6 +189,7 @@ export function clearTestDataFromStatusArray(): void { // List of source file from all local environments interface coverageDataType { crc32Checksum: number; + isVCP: boolean; covered: number[]; uncovered: number[]; partiallyCovered: number[]; @@ -248,7 +249,7 @@ export function getCoverageDataForFile(filePath: string): coverageSummaryType { let uncoveredList: number[] = []; let partiallyCoveredList: number[] = []; for (const enviroData of dataForThisFile.enviroList.values()) { - if (enviroData.crc32Checksum == checksum) { + if (enviroData.crc32Checksum == checksum || enviroData.isVCP) { coveredList = coveredList.concat(enviroData.covered); uncoveredList = uncoveredList.concat(enviroData.uncovered); partiallyCoveredList = partiallyCoveredList.concat( @@ -338,7 +339,9 @@ export function updateGlobalDataForFile(enviroPath: string, fileList: any[]) { .map(Number); const checksum = fileList[fileIndex].cmcChecksum; + let coverageData: coverageDataType = { + isVCP: enviroPath.endsWith(".vcp"), crc32Checksum: checksum, covered: coveredList, uncovered: uncoveredList, @@ -753,7 +756,6 @@ async function configureWorkspaceAndBuildEnviro( if (projectEnvParameters) { // Create the environment using the provided file list await commonEnvironmentSetup(fileList, envLocation, false); - const envName = createEnvNameFromFiles(fileList); const envFilePath = path.join(envLocation, `${envName}.env`); const testSuites = projectEnvParameters.testsuiteArgs; @@ -882,7 +884,6 @@ async function commonEnvironmentSetup( return; } } - // Build the environment with the valid name await buildEnvironmentVCAST( fileList, @@ -904,7 +905,6 @@ export async function newEnvironment( // file in the list will be a C/C++ file but we need to filter // for the multi-select case. // - let fileList: string[] = []; for (let index = 0; index < URIlist.length; index++) { const filePath = URIlist[index].fsPath; diff --git a/src/vcastUtilities.ts b/src/vcastUtilities.ts index d19f83b0..43fc4a6a 100644 --- a/src/vcastUtilities.ts +++ b/src/vcastUtilities.ts @@ -444,11 +444,10 @@ export function getVcastInterfaceCommandForMCDC( let optionsDict: { [command: string]: string | number } = {}; optionsDict["unitName"] = unitName; optionsDict["lineNumber"] = lineNumber; - const jsonOptions: string = JSON.stringify(optionsDict).replaceAll( - '"', - '\\"' - ); - const testArgument = `--options="${jsonOptions}"`; + + const jsonOptions: string = JSON.stringify(optionsDict); + const testArgument = `--options='${jsonOptions}'`; + return `${commandToRun} ${testArgument}`; } diff --git a/tests/internal/e2e/test/cCoverage/c_cov_example.sh b/tests/internal/e2e/test/cCoverage/c_cov_example.sh new file mode 100644 index 00000000..b51cd3d0 --- /dev/null +++ b/tests/internal/e2e/test/cCoverage/c_cov_example.sh @@ -0,0 +1,55 @@ +#!/bin/sh -ex + +# Always start with a clean working directory +rm -fr work +mkdir work +cd work +export WORK_DIRECTORY=$PWD +# export VECTORCAST_DIR=/home/JOBDATA/VectorCAST/vc20__86806_vcwrap__87490_inst_mod/deliver/linux64/debug + +# Download lua 5.3.5 for instrumentation +time wget https://www.lua.org/ftp/lua-5.4.0.tar.gz +time tar -zxvf lua-5.4.0.tar.gz + + +# Configuration VectorCAST, including the compiler +$VECTORCAST_DIR/clicast template GNU_CPP11_X +$VECTORCAST_DIR/clicast option VCAST_COVERAGE_FOR_HEADERS TRUE +$VECTORCAST_DIR/clicast option VCAST_COVERAGE_FOR_AGGREGATE_INIT TRUE + + +# Create the cover environment +time $VECTORCAST_DIR/clicast cover environment script_run ../env.enc + + +$VECTORCAST_DIR/clicast -e env cover env disable_instrumentation + + +# These commands will be put into a single clicast build command + + # Single step instrument and build + cd lua-5.4.0/src + + # The command that instruments and builds + time make generic -j16 -B + + # Add the instrumentation data into the cover environment + cd $WORK_DIRECTORY + +# Run the tests +mkdir lua_tests +cd lua_tests +time wget https://www.lua.org/tests/lua-5.4.0-tests.tar.gz +time tar -zxvf lua-5.4.0-tests.tar.gz +cd lua-5.4.0-tests +set +e +../../lua-5.4.0/src/lua all.lua +mv TESTINSS.DAT TESTINSS.DAT.all.lua +../../lua-5.4.0/src/lua api.lua +mv TESTINSS.DAT TESTINSS.DAT.api.lua +set -e + +# Add the test suite result to the cover environment +cd $WORK_DIRECTORY +time $VECTORCAST_DIR/clicast -e env cover result add lua_tests/lua-5.4.0-tests/TESTINSS.DAT.all.lua all.lua +time $VECTORCAST_DIR/clicast -e env cover result add lua_tests/lua-5.4.0-tests/TESTINSS.DAT.api.lua api.lua \ No newline at end of file diff --git a/tests/internal/e2e/test/cCoverage/env.enc b/tests/internal/e2e/test/cCoverage/env.enc new file mode 100644 index 00000000..818093db --- /dev/null +++ b/tests/internal/e2e/test/cCoverage/env.enc @@ -0,0 +1,41 @@ + + + 2 + + env + STATEMENT_MCDC + + + SourceRoot + ./lua-5.4.0 + src + + + PROBE_ID: 2 + PROBE_UNIT: luac.c + PROBE_FUNCTION: main + PROBE_LINE: return 0; + PROBE_CONTEXT_BEFORE: + if (lua_pcallk(L, (2), (0), (0), 0, ((void *)0))!=0) fatal(lua_tolstring(L, (-1), ((void *)0))); + lua_close(L); + END_PROBE_CONTEXT_BEFORE: + PROBE_CODE: + VCAST_DUMP_COVERAGE_DATA(); + END_PROBE_CODE: + + + + SourceRoot + src/lua.c + + true + + + + SourceRoot + src/luac.c + + true + + + diff --git a/tests/internal/e2e/test/specs/vcast_c_tests.test.ts b/tests/internal/e2e/test/specs/vcast_c_tests.test.ts new file mode 100644 index 00000000..ac901074 --- /dev/null +++ b/tests/internal/e2e/test/specs/vcast_c_tests.test.ts @@ -0,0 +1,223 @@ +// Test/specs/vcast.test.ts +import { + TextEditor, + type BottomBarPanel, + type Workbench, +} from "wdio-vscode-service"; +import { Key } from "webdriverio"; +import { + expandWorkspaceFolderSectionInExplorer, + updateTestID, + checkIfRequestInLogs, + checkElementExistsInHTML, + findTreeNodeAtLevel, + TIMEOUT, +} from "../test_utils/vcast_utils"; +import { checkForServerRunnability } from "../../../../unit/getToolversion"; + +describe("vTypeCheck VS Code Extension", () => { + let bottomBar: BottomBarPanel; + let workbench: Workbench; + let useDataServer: boolean = true; + before(async () => { + workbench = await browser.getWorkbench(); + // Opening bottom bar and problems view before running any tests + bottomBar = workbench.getBottomBar(); + await bottomBar.toggle(true); + process.env.E2E_TEST_ID = "0"; + let releaseIsSuitableForServer = await checkForServerRunnability(); + if (process.env.VCAST_USE_PYTHON || !releaseIsSuitableForServer) { + useDataServer = false; + } + }); + + it("test 1: should be able to load VS Code", async () => { + await updateTestID(); + expect(await workbench.getTitleBar().getTitle()).toBe( + "[Extension Development Host] vcastTutorial - Visual Studio Code" + ); + }); + + it("should activate vcastAdapter", async () => { + await updateTestID(); + + await browser.keys([Key.Control, Key.Shift, "p"]); + // Typing Vector in the quick input box + // This brings up VectorCAST Test Explorer: Configure + // so just need to hit Enter to activate + for (const character of "vector") { + await browser.keys(character); + } + + await browser.keys(Key.Enter); + + const activityBar = workbench.getActivityBar(); + const viewControls = await activityBar.getViewControls(); + for (const viewControl of viewControls) { + console.log(await viewControl.getTitle()); + } + + await bottomBar.toggle(true); + const outputView = await bottomBar.openOutputView(); + + console.log("Waiting for VectorCAST activation"); + await $("aria/VectorCAST Test Pane Initialization"); + console.log("WAITING FOR TESTING"); + await browser.waitUntil( + async () => (await activityBar.getViewControl("Testing")) !== undefined, + { timeout: TIMEOUT } + ); + console.log("WAITING FOR TEST EXPLORER"); + browser.pause(10000); + await browser.waitUntil(async () => + (await outputView.getChannelNames()) + .toString() + .includes("VectorCAST Test Explorer") + ); + await outputView.selectChannel("VectorCAST Test Explorer"); + console.log("Channel selected"); + console.log("WAITING FOR LANGUAGE SERVER"); + await browser.waitUntil( + async () => + (await outputView.getText()) + .toString() + .includes("Starting the language server"), + { timeout: TIMEOUT } + ); + + const testingView = await activityBar.getViewControl("Testing"); + await testingView?.openView(); + }); + + it("should check for server starting logs if in server mode", async () => { + const outputView = await bottomBar.openOutputView(); + + // Check if server started + if (useDataServer) { + // Check message pane for expected message + await browser.waitUntil( + async () => + (await outputView.getText()) + .toString() + .includes("Started VectorCAST Data Server"), + { timeout: TIMEOUT } + ); + + // Check server logs + const logs = await checkIfRequestInLogs(3, ["port:", "clicast"]); + expect(logs).toBe(true); + } + }); + + it("should check for vcp node", async () => { + const activityBar = workbench.getActivityBar(); + const testingView = await activityBar.getViewControl("Testing"); + await testingView?.openView(); + const vcpNode = await findTreeNodeAtLevel(0, "env.vcp"); + expect(vcpNode).toBeDefined(); + }); + + it("should check for c coverage", async () => { + workbench = await browser.getWorkbench(); + bottomBar = workbench.getBottomBar(); + const activityBar = workbench.getActivityBar(); + const testingView = await activityBar.getViewControl("Explorer"); + await testingView?.openView(); + + await bottomBar.toggle(false); + + const workspaceFolderSection = + await expandWorkspaceFolderSectionInExplorer("vcastTutorial"); + + const workFolder = workspaceFolderSection.findItem("work"); + await (await workFolder).select(); + + const luaFolder = workspaceFolderSection.findItem("lua-5.4.0"); + await (await luaFolder).select(); + + const srcFolder = workspaceFolderSection.findItem("src"); + await (await srcFolder).select(); + + const file = workspaceFolderSection.findItem("lua.c"); + await (await file).select(); + + // Check if the file is already open in the editor + const editorView = workbench.getEditorView(); + const openEditors = await editorView.getOpenEditorTitles(); + const isFileOpen = openEditors.includes("lua.c"); + + if (!isFileOpen) { + await (await file).select(); + } + + // Give VS Code some time to settle (language server, decorations, etc.) + await browser.pause(30_000); + + const icon = "no-cover-icon-with-mcdc"; + const lineNumber = 65; + + const tab = (await editorView.openEditor("lua.c")) as TextEditor; + await tab.moveCursor(lineNumber, 1); + + // Use the EXACT same pattern as the working test + const lineNumberElement = await $(`.line-numbers=${lineNumber}`); + const flaskElement = await ( + await lineNumberElement.parentElement() + ).$(".cgmr.codicon"); + + // Verify the icon + const backgroundImageCSS = + await flaskElement.getCSSProperty("background-image"); + expect(backgroundImageCSS.value.includes(`/${icon}`)).toBe(true); + + // Close bottom bar before context menu + await bottomBar.toggle(false); + await browser.pause(1000); + + // Open context menu + await flaskElement.click({ button: 2 }); + await browser.pause(2000); + await (await $("aria/VectorCAST MC/DC Report")).click(); + + const outputView = await bottomBar.openOutputView(); + + // Wait for report generation + await browser.waitUntil( + async () => + (await outputView.getText()) + .toString() + .includes("Report file path is:"), + { timeout: TIMEOUT } + ); + + // Wait for webview to open + await browser.waitUntil( + async () => (await workbench.getAllWebviews()).length > 0, + { timeout: TIMEOUT } + ); + + const webviews = await workbench.getAllWebviews(); + expect(webviews).toHaveLength(1); + + const webview = webviews[0]; + await webview.open(); + + // Count report blocks in the HTML + const reportBlockCount = await browser.execute(() => { + return document.querySelectorAll("div.report-block").length; + }); + + expect(reportBlockCount).toEqual(1); + + // Validate report content + await expect(await checkElementExistsInHTML("lua.c")).toBe(true); + await expect(await checkElementExistsInHTML("65")).toBe(true); + await expect( + await checkElementExistsInHTML("Pairs satisfied: 0 of 2 ( 0% )") + ).toBe(true); + + // Cleanup + await webview.close(); + await editorView.closeEditor("VectorCAST Report", 1); + }); +}); diff --git a/tests/internal/e2e/test/specs/vcast_manage.test.ts b/tests/internal/e2e/test/specs/vcast_manage.test.ts index 0b462eec..0260a42f 100644 --- a/tests/internal/e2e/test/specs/vcast_manage.test.ts +++ b/tests/internal/e2e/test/specs/vcast_manage.test.ts @@ -814,6 +814,7 @@ describe("vTypeCheck VS Code Extension", () => { const button = await $(`aria/Import OK`); await button.click(); console.log("Checking for Output logs if Environment creation is finished"); + await bottomBar.maximize(); await browser.waitUntil( async () => (await outputView.getText()) @@ -846,6 +847,8 @@ describe("vTypeCheck VS Code Extension", () => { const testsuiteNode = await findTreeNodeAtLevel(3, "DATABASE-MANAGER"); expect(testsuiteNode).toBeDefined(); + await bottomBar.restore(); + // Closing all current notifications for the next test const notificationsCenter = await workbench.openNotificationsCenter(); await notificationsCenter.clearAllNotifications(); diff --git a/tests/internal/e2e/test/specs_config.ts b/tests/internal/e2e/test/specs_config.ts index 251b82e4..f5f95449 100755 --- a/tests/internal/e2e/test/specs_config.ts +++ b/tests/internal/e2e/test/specs_config.ts @@ -170,6 +170,14 @@ export function getSpecGroups(useVcast24: boolean) { }, params: {}, }; + specGroups["c_coverage"] = { + specs: ["./**/**/vcast_c_tests.test.ts"], + env: { + VCAST_USE_PYTHON: "True", + C_COVERAGE: "True", + }, + params: {}, + }; specGroups["basic_user_interactions_server"] = { specs: [ diff --git a/tests/internal/e2e/test/wdio.conf.ts b/tests/internal/e2e/test/wdio.conf.ts index 18af6977..8799dc3e 100644 --- a/tests/internal/e2e/test/wdio.conf.ts +++ b/tests/internal/e2e/test/wdio.conf.ts @@ -335,6 +335,7 @@ export const config: Options.Testrunner = { async () => await buildEnvsWithSpecificReleases(initialWorkdir), ], ["MANAGE_TEST", async () => await testManage(initialWorkdir)], + ["C_COVERAGE", async () => await testCCoverage(initialWorkdir)], ]); // Determine the environment key @@ -392,6 +393,40 @@ export const config: Options.Testrunner = { * ================================================================================================ */ + async function testCCoverage(initialWorkdir: string) { + const workFolder = path.join(initialWorkdir, "test", "vcastTutorial"); + const testInputCCoverage = path.join(initialWorkdir, "test", "cCoverage"); + + await checkVPython(); + clicastExecutablePath = await checkClicast(); + process.env.CLICAST_PATH = clicastExecutablePath; + + await prepareConfig(initialWorkdir, clicastExecutablePath); + + // Create vcastTutorial directory + await mkdir(workFolder, { recursive: true }); + + // Copy the build script to vcastTutorial + const scriptSource = path.join(testInputCCoverage, "c_cov_example.sh"); + const scriptDest = path.join(workFolder, "c_cov_example.sh"); + const envSource = path.join(testInputCCoverage, "env.enc"); + const envDest = path.join(workFolder, "env.enc"); + + if (process.platform === "win32") { + await executeCommand(`copy /y ${scriptSource} ${scriptDest}`); + await executeCommand(`copy /y ${envSource} ${envDest}`); + } else { + await executeCommand(`cp ${scriptSource} ${scriptDest}`); + await executeCommand(`cp ${envSource} ${envDest}`); + } + + // Execute the coverage build script in vcastTutorial + const build_coverage = `cd ${workFolder} && bash ./c_cov_example.sh`; + await executeCommand(build_coverage); + + // No copy needed - everything is put in place by the sh script + } + async function testManage(initialWorkdir: string) { const testInputManage = path.join(initialWorkdir, "test", "manage"); const build_demo = `cd ${testInputManage} && ./demo_build.sh`;