"""Cppcheck report/capture fixtures without native analysis or compilation."""
import json
from pathlib import Path
import sys
import tempfile
import unittest
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from analyzer_policy import read_policy
from cppcheck.capture import source_files
from cppcheck.cppcheck import native_result, project_entry
class CppcheckTests(unittest.TestCase):
def setUp(self):
self.source = Path("/tmp/scst/scst/src/file.c")
self.progress = f"Checking {self.source} ...\n"
self.checkers = "No critical errors encountered.\nYes CheckOther::checkSomething\n"
def report(self, diagnostics="", status=0, progress=None, checkers=None):
xml = (''
f'{diagnostics}')
return native_result(xml, self.progress if progress is None else progress,
self.checkers if checkers is None else checkers,
status, [self.source], Path("/tmp/scst"))
def test_valid_empty_report_requires_native_coverage(self):
self.assertTrue(self.report()["complete"])
for kwargs in ({"progress": ""}, {"checkers": ""}, {"status": 1}, {"status": 3},
{"checkers": "There were critical errors (syntaxError).\nYes Check\n"}):
with self.subTest(kwargs=kwargs):
self.assertFalse(self.report(**kwargs)["complete"])
def test_relative_progress_and_configuration_suffix(self):
self.assertTrue(self.report(progress="Checking scst/src/file.c: CONFIG_X=1...\n")["complete"])
def test_findings_are_preserved(self):
diagnostic = ''
result = self.report(diagnostic, 3)
self.assertTrue(result["complete"])
self.assertEqual(result["remaining"], 1)
self.assertEqual(result["diagnostics"][0]["id"], "nullPointer")
def test_native_zero_with_parser_failure_is_incomplete(self):
for identifier in ("syntaxError", "unknownMacro", "missingIncludeSystem", "internalError"):
with self.subTest(identifier=identifier):
diagnostic = f''
self.assertFalse(self.report(diagnostic)["complete"])
def test_malformed_and_partial_xml_are_incomplete(self):
for xml in ("", "", '',
''):
with self.subTest(xml=xml):
self.assertFalse(native_result(xml, self.progress, self.checkers, 0,
[self.source], Path("/tmp/scst"))["complete"])
def test_capture_requires_compile_and_shared_scope(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "usr/fileio/file.c"
source.parent.mkdir(parents=True)
source.touch()
self.assertEqual(source_files(["-c", str(source)], "userspace", root), [source])
self.assertEqual(source_files(["-M", str(source)], "userspace", root), [])
self.assertEqual(source_files(["-Iinclude", str(source)], "kernel", root), [source])
outside = root / "qla2x00t/legacy.c"
outside.parent.mkdir()
outside.touch()
self.assertEqual(source_files([str(outside)], "kernel", root), [])
def test_basic_policy_rejects_models_and_suppressions(self):
with tempfile.TemporaryDirectory() as temporary:
path = Path(temporary) / "policy.json"
policy = {"version": 1, "notes": ["Native commands."]}
path.write_text(json.dumps(policy))
self.assertEqual(read_policy(path, "cppcheck"), policy)
for field in ("models", "suppressions", "source_exceptions"):
path.write_text(json.dumps({**policy, field: []}))
with self.subTest(field=field), self.assertRaises(ValueError):
read_policy(path, "cppcheck")
def test_project_retains_real_headers_relative_to_compiler_directory(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
(root / "include").mkdir()
(root / "include/config.h").touch()
arguments = ["cc", "-Iinclude", "-isystem", "system", "-include", "include/config.h",
"-D__KERNEL__", "-c", "file.c"]
record = {"directory": str(root), "file": str(root / "file.c"), "arguments": arguments}
entry = project_entry(record)
self.assertEqual(entry["arguments"], ["cc", "-I", str(root / "include"),
"-include", str(root / "include/config.h"),
"-D__KERNEL__", "-c", "file.c", "-I", str(root / "system")])
self.assertEqual(record["arguments"], arguments)
if __name__ == "__main__":
unittest.main()