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
2 changes: 1 addition & 1 deletion src/codechecker.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ _codechecker_test = rule(
),
"severities": attr.string_list(
default = ["HIGH"],
doc = "List of defect severities: HIGH, MEDIUM, LOW, STYLE etc",
doc = "List of defect severities or checker names: HIGH, MEDIUM, LOW, STYLE etc",
),
"skip": attr.string_list(
default = [],
Expand Down
50 changes: 28 additions & 22 deletions src/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import subprocess
import sys
import os
import re
import json


def setup_logging(verbosity, codechecker_log):
"""Setup logging parameters for execution session"""
Expand Down Expand Up @@ -53,9 +54,7 @@ def build_env(env, log, clang, clang_tidy):
elif new_env.get("CC_ANALYZER_BIN"):
logging.debug("CC_ANALYZER_BIN is set by the configuration")
else:
new_env["CC_ANALYZER_BIN"] = (
f"clangsa:{clang};clang-tidy:{clang_tidy}"
)
new_env["CC_ANALYZER_BIN"] = f"clangsa:{clang};clang-tidy:{clang_tidy}"
logging.debug("env: %s", str(new_env))
return new_env

Expand Down Expand Up @@ -153,10 +152,7 @@ def parse(
stage("CodeChecker parse:")
env = build_env(env, log, clang, clang_tidy)
logging.info("CodeChecker parse -e json")
codechecker_parse = (
f"{codechecker} parse --config "
f"{config} {input_dir}"
)
codechecker_parse = f"{codechecker} parse --config " f"{config} {input_dir}"
# Save results to JSON file
command = (
f"{codechecker_parse} --export=json > " f"{input_dir}/result.json"
Expand All @@ -183,25 +179,27 @@ def check_results(output_dir, log, severities):
Args:
output_dir (str): Path to the output directory
log (str): Path to the log file
severities (str): Comma separated list of defect severities to fail on
severities (str): Comma separated list of defect severities
or checker names to fail on.
"""
stage("Checking result:")
# Get results file and read it
result_file = output_dir + "/result.txt"
logging.info("Find CodeChecker results in bazel-bin")
logging.info(" all artifacts: %s/", output_dir)
logging.info(" HTML report: %s/report/index.html", output_dir)
logging.info(" result file: %s", result_file)
results = read_file(log, result_file)
logging.info("Results: \n\n%s\n", results)
logging.info(" result file: %s/result.txt", output_dir)
report_data = json.loads(read_file(log, output_dir + "/result.json"))
logging.info(
"Results: \n\n%s\n", read_file(log, output_dir + "/result.txt")
)
reports = report_data.get("reports", [])
Comment thread
Szelethus marked this conversation as resolved.
# Collect defect severities to detect
if severities is None:
fail(
log,
"CodeChecker defect severities are invalid: "
f"{str(severities)}",
"CodeChecker defect severities are invalid: " f"{str(severities)}",
)
severities = shlex.split(severities) # pyright: ignore[reportArgumentType]
severities = shlex.split(severities) # pyright: ignore[reportArgumentType]
# Add HIGH severity by default
if not severities:
severities.append("HIGH")
Expand All @@ -211,12 +209,20 @@ def check_results(output_dir, log, severities):
logging.debug("Severities: %s", str(severities))
issues = dict.fromkeys(severities, 0)
logging.debug("Issues: %s", str(issues))
# Grep results for defects according to severities
for issue in issues:
found = re.findall(rf"^{issue} .* (\d+)", results, re.M)
defects = sum(int(number) for number in found)
logging.debug(" %s : %s = %d", issue, str(found), defects)
issues[issue] = defects
for report in reports:
severity = report.get("severity")
checker_name = report.get("checker_name", "unknown")
if severity in issues or checker_name in issues:
# We need to increment the key that was actually found.
matched_key = severity if severity in issues else checker_name
issues[matched_key] += 1
logging.debug(
" %s : %s = %d:%d",
severity,
checker_name,
report.get("file", {}).get("path", "unknown"),
report.get("line", "?"),
)
Comment thread
Szelethus marked this conversation as resolved.
logging.info("Defects: %s", str(issues))
# Check collected defects
passed = True
Expand Down
17 changes: 12 additions & 5 deletions test/unit/severities/severities_check.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
# limitations under the License.

"""
Drives common.check_results() against a real CodeChecker result.txt fixture.
Drives common.check_results() against a real CodeChecker result.json fixture.

Intended to be used as the main of a py_test Bazel target, one target per
case, generated by the severities_test() macro. check_results() reads
"<output_dir>/result.txt" and calls sys.exit(1) when it finds defects of the
"<output_dir>/result.json" and calls sys.exit(1) when it finds defects of the
requested severities or checker/bug names, so this driver copies the fixture
into a temp dir, runs check_results(), and compares the outcome against the
expected pass/fail.
Expand All @@ -36,14 +36,14 @@ def parse_args() -> argparse.Namespace:
"""Parse command-line arguments"""
parser = argparse.ArgumentParser(
description=(
"Run common.check_results() on a result.txt fixture and assert "
"Run common.check_results() on a result.json fixture and assert "
"whether it passes or fails for the given severities."
)
)
parser.add_argument(
"--file",
required=True,
help="Path to the result.txt fixture to check.",
help="Path to the result.json fixture to check.",
)
parser.add_argument(
"--severities",
Expand All @@ -70,7 +70,14 @@ def run(fixture: str, severities) -> bool:
"""
output_dir = tempfile.mkdtemp()
try:
shutil.copyfile(fixture, os.path.join(output_dir, "result.txt"))
shutil.copyfile(fixture, os.path.join(output_dir, "result.json"))
# The function logs the content of result.txt
# Since it is not used in any meaningful capacity,
# an empty file is sufficient
with open(
os.path.join(output_dir, "result.txt"), "w", encoding="utf-8"
) as handle:
handle.write("")
try:
check_results(
output_dir=output_dir, log=None, severities=severities
Expand Down
8 changes: 4 additions & 4 deletions test/unit/severities/severities_test.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ def severities_test(
# The producer label may be written relative to this package,
# e.g. "producers:high", but we need an absolute label.
if producer.startswith("//") or producer.startswith("@"):
result_txt = "%s/codechecker-files/result.txt" % producer
result_json = "%s/codechecker-files/result.json" % producer
else:
result_txt = "//%s/%s/codechecker-files/result.txt" % (native.package_name(), producer)
result_json = "//%s/%s/codechecker-files/result.json" % (native.package_name(), producer)

args = [
"--file=$(location {})".format(result_txt),
"--file=$(location {})".format(result_json),
"--expect={}".format(expect),
]

Expand All @@ -85,7 +85,7 @@ def severities_test(
srcs = ["severities_check.py"],
main = "severities_check.py",
args = args,
data = [result_txt],
data = [result_json],
local = True,
tags = ["unit"] + tags,
size = size,
Expand Down
Loading