Skip to content
Merged
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
112 changes: 60 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,23 @@ The python scrip `vcast_exec.py` is the main driver for build/execute VectorCAST
The api for vcast_exec.py follows:

```
usage: vcast_exec.py [VectorCAST Project]
[-h/--help]
[--build-execute] [--setup SETUP]
[--use_imported_result IMPORTEDRESULTS]
[--build | --incremental]
[--output_dir OUTPUT_DIR] [--source_root SOURCE_ROOT]
[--html_base_dir HTML_BASE_DIR]
[--cobertura] [--cobertura_extended] [--lcov] [--junit]
[--export_rgw] [--sonarqube] [--pclp_input PCLP_INPUT]
[--pclp_output_html PCLP_OUTPUT_HTML]
[--exit_with_failed_count] [EXIT_WITH_FAILED_COUNT]]
[--exit_with_failed_comp [EXIT_WITH_FAILED_COMP]
[--check_build_log] [--aggregate] [--metrics]
[--fullstatus] [--utfull] [--tcmr] [--noindex]
[--jobs JOBS] [--ci] [-l LEVEL] [-e ENVIRONMENT]
[--gitlab | --azure] [--print_exc] [--timing]
[-v/--verbose] [--version]
usage: vcast_exec.py [-h] [--build-execute] [--setup SETUP]
[--use_imported_result IMPORTEDRESULTS]
[--build | --incremental] [--output_dir OUTPUT_DIR]
[--source_root SOURCE_ROOT]
[--html_base_dir HTML_BASE_DIR] [--cobertura]
[--cobertura_extended] [--lcov] [--junit] [--export_rgw]
[--sonarqube] [--pclp_input PCLP_INPUT]
[--pclp_output_html PCLP_OUTPUT_HTML]
[--exit_with_failed_count [EXIT_WITH_FAILED_COUNT]]
[--exit_with_failed_comp [EXIT_WITH_FAILED_COMP]]
[--check_build_log]
[--covToDisplay {statement,branch,mcdc,function,functioncall}]
[--aggregate] [--metrics] [--fullstatus] [--utfull]
[--tcmr] [--noindex] [--jobs JOBS] [--ci] [-l LEVEL]
[-e ENVIRONMENT] [--gitlab | --azure] [--print_exc]
[--timing] [-v] [--version]
[ManageProject]

positional arguments:
ManageProject VectorCAST Project Name
Expand All @@ -62,70 +62,70 @@ The api for vcast_exec.py follows:
--build-execute Builds and exeuctes the VectorCAST Project
--setup SETUP Path to setup_env.bat/.sh (optional)
--use_imported_result IMPORTEDRESULTS
Use existing VCR file from repository for CBT via
Imported Results
Use existing VCR file from repository for CBT via
Imported Results
--build Only builds the VectorCAST Project
--incremental Use Change Based Testing
(Cannot be used with --build)
--incremental Use Change Based Testing (Cannot be used with --build)

Metrics Options:
Options generating metrics

--output_dir OUTPUT_DIR
Set the base directory of the xml_data directory.
Default is the workspace directory
Set the base directory of the xml_data directory.
Default is the workspace directory
--source_root SOURCE_ROOT
Set the absolute path for the source file
in coverage reporting
Set the absolute path for the source file in coverage
reporting
--html_base_dir HTML_BASE_DIR
Set the base directory of the html_reports directory.
The default is the workspace directory
Set the base directory of the html_reports directory.
The default is the workspace directory
--cobertura Generate coverage results in Cobertura xml format
--cobertura_extended Generate coverage results in extended
Cobertura xml format
--cobertura_extended Generate coverage results in extended Cobertura xml
format
--lcov Generate coverage results in an LCOV format
--junit Generate test results in Junit xml format
--export_rgw Export RGW data
--sonarqube Generate test results in SonarQube Generic
test execution report format (CppUnit)
--sonarqube Generate test results in SonarQube Generic test
execution report format (CppUnit)
--pclp_input PCLP_INPUT
Generate static analysis results from
PC-lint Plus XML file to generic
static analysis format (codequality)
Generate static analysis results from PC-lint Plus XML
file to generic static analysis format (codequality)
--pclp_output_html PCLP_OUTPUT_HTML
Generate static analysis results from
PC-lint Plus XML file to an HTML output
Generate static analysis results from PC-lint Plus XML
file to an HTML output
--exit_with_failed_count [EXIT_WITH_FAILED_COUNT]
Returns failed test case count as script exit.
Set a value to indicate a percentage above which
the job will be marked as failed
Returns failed test case count as script exit. Set a
value to indicate a percentage above which the job
will be marked as failed
--exit_with_failed_comp [EXIT_WITH_FAILED_COMP]
Returns failed if any of the functions have a
Complexity (Vg) > value.
--check_build_log Checks build log for a list of error phrases.
Returns failure if any are found.
Returns failed if any of the functions have a
Complexity (Vg) > value.
--check_build_log Checks build log for a list of error phrases. Returns
failure if any are found.
--covToDisplay {statement,branch,mcdc,function,functioncall}
Selects which coverage to display for coverage print.
Default is "statement".

Report Selection:
VectorCAST Manage reports that can be generated

--aggregate Generate aggregate coverage report for VectorCAST Project
--aggregate Generate aggregate coverage report VectorCAST Project
--metrics Generate metrics reports for VectorCAST Project
--fullstatus Generate full status reports for VectorCAST Project
--utfull Generate Full Reports for each VectorCAST
environment in the VectorCAST Project
--tcmr Generate Test Cases Management Reports for each
VectorCAST environment in the VectorCAST Project
--noindex Stops index.html report that ties all the other
HTML reports together from being created
for VectorCAST Project
--utfull Generate Full Reports for each VectorCAST environment
in project
--tcmr Generate Test Cases Management Reports for each
VectorCAST environment in project
--noindex Stops index.html report that ties all the other HTML
reports together from being created

Build/Execution Options:
Options that effect build/execute operation

--jobs JOBS Number of concurrent jobs (default = 1)
--ci Use Continuous Integration Licenses
-l LEVEL, --level LEVEL
Environment Name if only doing single environment.
Environment Name if only doing single environment.
Should be in the form of compiler/testsuite
-e ENVIRONMENT, --environment ENVIRONMENT
Environment Name if only doing single environment.
Expand All @@ -139,10 +139,18 @@ The api for vcast_exec.py follows:
--timing Prints timing information for metrics generation
-v, --verbose Enable verbose output
--version Displays the version information

```

# Change log
07/2026

06/2026
* Added --covToDisplay
* Added additional failure phrases
* Harness Link Failure
* Environment built but not Linked
* Modified failure return codes
* Fixed Full Reports always getting generated
* Fixed --output_dir and --html_base_dir so that
* xml_data gets copied to --output_dir
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2026.06.16-1329-0400-gdb82f6b
v2026.07.09-1544-0400-gb6e4569
4 changes: 3 additions & 1 deletion check_build_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
"not permitted in continuous integration mode",
"has been opened by a newer version of VectorCAST",
"Environment built but not Compiled",
"ERROR: Compile failed for unit"
"Harness Link Failure",
"ERROR: Compile failed for unit",
"Environment built but not Linked"
]

VC_unstablePhrases = [
Expand Down
36 changes: 34 additions & 2 deletions cobertura.py
Original file line number Diff line number Diff line change
Expand Up @@ -757,7 +757,8 @@ def runCoberturaResults(packages, api, verbose = False, extended = False, source
return total_st, cov_st, total_lines, cov_lines, total_br, cov_br, total_func, cov_func, total_fc, cov_fc, total_mcdc, cov_mcdc, branch_rate, statement_rate, line_rate, func_rate, FC_rate, MCDC_rate, vg


def generateCoverageResults(inFile, azure = False, xml_data_dir = "xml_data", verbose = False, extended = False, source_root = "" ):
def generateCoverageResults(inFile, azure = False, xml_data_dir = "xml_data",
verbose = False, extended = False, source_root = "", covToDisplay="statement"):

# cwd = os.getcwd()
# xml_data_dir = os.path.join(cwd,xml_data_dir)
Expand Down Expand Up @@ -816,7 +817,38 @@ def generateCoverageResults(inFile, azure = False, xml_data_dir = "xml_data", ve
if FC_rate != -1.0: print ("function calls: {:.2f}% ({:d} out of {:d})".format(FC_rate*100.0, cov_fc, total_fc))
if MCDC_rate != -1.0: print ("mcdc pairs: {:.2f}% ({:d} out of {:d})".format(MCDC_rate*100.0, cov_mcdc, total_mcdc))

if statement_rate != -1.0: print ("coverage: {:.2f}% of statements".format(statement_rate*100.0))
# use selected coverage from --covToDisplay option
match covToDisplay:
case "statement":
if statement_rate != -1.0:
print ("coverage: {:.2f}% of statements".format(statement_rate*100.0))
else:
print (f"[ERROR] selected coverage {covToDisplay} has no coverage metrics")

case "branch":
if branch_rate != -1.0:
print ("coverage: {:.2f}% of branch".format(branch_rate*100.0))
else:
print (f"[ERROR] selected coverage {covToDisplay} has no coverage metrics")

case "mcdc":
if MCDC_rate != -1.0:
print ("coverage: {:.2f}% of mcdc pairs".format(MCDC_rate*100.0))
else:
print (f"[ERROR] selected coverage {covToDisplay} has no coverage metrics")

case "function":
if func_rate != -1.0:
print ("coverage: {:.2f}% of functions".format(func_rate*100.0))
else:
print (f"[ERROR] selected coverage {covToDisplay} has no coverage metrics")

case "functioncall":
if FC_rate != -1.0:
print ("coverage: {:.2f}% of function calls".format(FC_rate*100.0))
else:
print (f"[ERROR] selected coverage {covToDisplay} has no coverage metrics")

if complexity != -1.0: print ("complexity: {:d}".format(complexity))
source = etree.SubElement(sources, "source")
source.text = "./"
Expand Down
35 changes: 32 additions & 3 deletions generate_xml.py
Original file line number Diff line number Diff line change
Expand Up @@ -929,6 +929,8 @@ def generate_local_results(self, results, key):
localXML.topLevelAPI = self.api
localXML.noResults = self.noResults
localXML.generate_unit()
self.failed_count += localXML.failed_count
self.passed_count += localXML.passed_count

##need_fixup
if not self.no_full_reports:
Expand Down Expand Up @@ -1062,6 +1064,13 @@ def generate_testresults(self):
self.fh_data = ""
self.localDataOnly = True
self.noResults = False
# CI exit code uses self.failed_count / self.passed_count (see vcast_exec --exit_with_failed_count).
# get_full_status ALL aggregate can disagree with per-environment JUnit (e.g. EXEC_NO_EXPECTED_VALUES
# / "No expected values" in testcase XML). Merge counts from GenerateXml.generate_unit() per env.
self.failed_count = 0
self.passed_count = 0
all_errors = 0
all_success = 0
if results['ALL']['testcase_results'] == {}:
print("** No results in project")
self.noResults = True
Expand All @@ -1070,14 +1079,13 @@ def generate_testresults(self):
success = results['ALL']['testcase_results']['success_count']
errors = total - success
failed = errors
all_errors = errors
all_success = success
self.fh_data += ("<?xml version=\"1.0\" encoding=\"" + self.encFmt.upper() + "\"?>\n")
self.fh_data += ("<testsuites>\n")
self.fh_data += (" <testsuite errors=\"%d\" tests=\"%d\" failures=\"%d\" name=\"%s\" id=\"1\">\n" %
(errors,total,failed,escape(self.manageProjectName, quote=False)))

self.failed_count = errors
self.passed_count = success

for result in results:
if result in all_envs:
if len(result.split("/")) != 3:
Expand Down Expand Up @@ -1110,6 +1118,27 @@ def generate_testresults(self):
self.fh_data += (testcaseString % (tc_name_full, classname, extraStatus))
self.failed_count += 1

if self.failed_count == 0 and self.passed_count == 0 and not self.noResults:
self.failed_count = all_errors
self.passed_count = all_success
elif self.noResults and self.failed_count == 0 and self.passed_count == 0:
# The environment(s) were processed during build-execute but produced
# ZERO testcase results (results['ALL']['testcase_results'] == {} and no
# imported results contributed counts above).
# This is what happens when the VectorCAST report engine drops results
# - e.g. a non-UTF-8 byte in the execution data ("Replacing unknown
# characters in TC-000023.XML" / "Error generating report for
# VCAST_AUTOMATIC_COMPOUND"), or a report-generation error.
# Without this branch failed_count stays 0, so vcast_exec
# --exit_with_failed_count exits 0 and the CI job passes *vacuously*
# while reporting no tests at all - masking the real state (and any
# genuine regression). Record a synthetic failure so the job fails.
print("** ERROR: No testcase results reported for %s - report "
"generation produced no results; counting as a failure so the "
"CI job does not pass vacuously." % self.manageProjectName)
self.failed_count = 1
self.passed_count = 0

self.fh_data += (" </testsuite>\n")
self.fh_data += ("</testsuites>\n")
if not self.localDataOnly:
Expand Down
8 changes: 7 additions & 1 deletion merge_vcr.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ def run(origVcrFile, newVcrFile, verbose):
except:
pass

tempNewVcrFile = os.path.join("newVcr",newVcrFile)
tempNewVcrFile = os.path.join("newVcr",os.path.basename(newVcrFile))
tempOrigVcrFile = os.path.join("origVcr",os.path.basename(origVcrFile))

shutil.copyfile(newVcrFile, tempNewVcrFile)
Expand All @@ -76,12 +76,16 @@ def run(origVcrFile, newVcrFile, verbose):
for file in glob.glob("newVcr/*.*"):
if file.endswith(".db") and "_cover" not in file:
newDbName = file
elif file.endswith(".db") and "_cover" in file:
newCoverDbName = file

with zipfile.ZipFile(tempOrigVcrFile, 'r') as zip_ref:
zip_ref.extractall("origVcr")
for file in glob.glob("origVcr/*.*"):
if file.endswith(".db") and "_cover" not in file:
origDbName = file
elif file.endswith(".db") and "_cover" in file:
origCoverDbName = file

os.remove(tempNewVcrFile)
os.remove(tempOrigVcrFile)
Expand All @@ -102,6 +106,8 @@ def run(origVcrFile, newVcrFile, verbose):

new_db.close()
orig_db.close()

shutil.copyfile(newCoverDbName, origCoverDbName)

os.remove(newVcrFile)
os.remove(origVcrFile)
Expand Down
Loading
Loading