diff --git a/.travis.yml b/.travis.yml index cd5c95c12..8c1a7f9d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,8 @@ env: before_install: # install needed deps - travis_retry sudo apt-get update -qq - - travis_retry sudo apt-get install -qq python-pygments python-pytest qt5-default qt5-qmake qtbase5-dev qtcreator libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet + - travis_retry sudo apt-get install -qq python-pygments qt5-default qt5-qmake qtbase5-dev qtcreator libxml2-utils libpcre3 gdb unzip wx-common xmlstarlet + - travis_retry sudo python2 -m pip install --upgrade pytest==4.6.4 matrix: # do notify immediately about it when a job of a build fails. diff --git a/addons/misra.py b/addons/misra.py index bb6b45f6e..6c096f9d4 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -543,30 +543,6 @@ def generateTable(): print(num[:8] + s) -def remove_file_prefix(file_path, prefix): - """ - Remove a file path prefix from a give path. leftover - directory separators at the beginning of a file - after the removal are also stripped. - - Example: - '/remove/this/path/file.c' - with a prefix of: - '/remove/this/path' - becomes: - file.c - """ - result = None - if file_path.startswith(prefix): - result = file_path[len(prefix):] - # Remove any leftover directory separators at the - # beginning - result = result.lstrip('\\/') - else: - result = file_path - return result - - class Rule(object): """Class to keep rule text and metadata""" @@ -660,9 +636,6 @@ class MisraChecker: # List of suppression extracted from the dumpfile self.dumpfileSuppressions = None - # Prefix to ignore when matching suppression files. - self.filePrefix = None - # Statistics of all violations suppressed per rule self.suppressionStats = dict() @@ -2037,10 +2010,7 @@ class MisraChecker: # Remove any prefix listed in command arguments from the filename. filename = None if location.file is not None: - if self.filePrefix is not None: - filename = remove_file_prefix(location.file, self.filePrefix) - else: - filename = location.file + filename = os.path.basename(location.file) if ruleNum in self.suppressedRules: fileDict = self.suppressedRules[ruleNum] @@ -2090,8 +2060,10 @@ class MisraChecker: if res: num1 = int(res.group(2)) * 100 ruleNum = num1 + int(res.group(3)) - self.addSuppressedRule(ruleNum, each.fileName, - each.lineNumber, each.symbolName) + linenr = None + if each.lineNumber: + linenr = int(each.lineNumber) + self.addSuppressedRule(ruleNum, each.fileName, linenr, each.symbolName) def showSuppressedRules(self): @@ -2119,14 +2091,6 @@ class MisraChecker: print(" %s" % line) - def setFilePrefix(self, prefix): - """ - Set the file prefix to ignnore from files when matching - suppression files - """ - self.filePrefix = prefix - - def setSuppressionList(self, suppressionlist): num1 = 0 num2 = 0 @@ -2440,7 +2404,6 @@ def get_args(): parser.add_argument("-generate-table", help=argparse.SUPPRESS, action="store_true") parser.add_argument("dumpfile", nargs='*', help="Path of dump file from cppcheck") parser.add_argument("--show-suppressed-rules", help="Print rule suppression list", action="store_true") - parser.add_argument("-P", "--file-prefix", type=str, help="Prefix to strip when matching suppression file rules") parser.add_argument("--cli", help="Addon is executed from Cppcheck", action="store_true") return parser.parse_args() @@ -2472,9 +2435,6 @@ def main(): if args.suppress_rules: checker.setSuppressionList(args.suppress_rules) - if args.file_prefix: - checker.setFilePrefix(args.file_prefix) - if args.dumpfile: exitCode = 0 for item in args.dumpfile: diff --git a/addons/test/test-misra.py b/addons/test/test-misra.py index e686000c6..43a549f36 100644 --- a/addons/test/test-misra.py +++ b/addons/test/test-misra.py @@ -1,6 +1,7 @@ # python -m pytest addons/test/test-misra.py import json import pytest +import re import sys try: from cStringIO import StringIO @@ -9,52 +10,33 @@ except ImportError: import subprocess -class CapturingStdout(object): - - def __enter__(self): - self._stdout = sys.stdout - sys.stdout = self._stringio = StringIO() - self.captured = [] - return self - - def __exit__(self, *args): - self.captured.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory - sys.stdout = self._stdout - - -class CapturingStderr(object): - - def __enter__(self): - self._stderr = sys.stderr - sys.stderr = self._stringio = StringIO() - self.captured = [] - return self - - def __exit__(self, *args): - self.captured.extend(self._stringio.getvalue().splitlines()) - del self._stringio # free up some memory - sys.stderr = self._stderr - - TEST_SOURCE_FILES = ['./addons/test/misra-test.c'] +def dump_create(fpath, *argv): + cmd = ["./cppcheck", "--dump", "--quiet", fpath] + list(argv) + p = subprocess.Popen(cmd) + p.communicate() + if p.returncode != 0: + raise OSError("cppcheck returns error code: %d" % p.returncode) + subprocess.Popen(["sync"]) + + +def dump_remove(fpath): + subprocess.Popen(["rm", "-f", fpath + ".dump"]) + + def setup_module(module): for f in TEST_SOURCE_FILES: - p = subprocess.Popen(["./cppcheck", "--dump", "--quiet", f]) - p.communicate() - if p.returncode != 0: - raise OSError("cppcheck returns error code: %d" % p.returncode) - subprocess.Popen(["sync"]) + dump_create(f) def teardown_module(module): for f in TEST_SOURCE_FILES: - subprocess.Popen(["rm", "-f", f + ".dump"]) + dump_remove(f) -@pytest.fixture +@pytest.fixture(scope="function") def checker(): from addons.misra import MisraChecker, MisraSettings, get_args args = get_args() @@ -85,11 +67,10 @@ def test_loadRuleTexts_mutiple_lines(checker): assert(checker.ruleTexts[106].text == "Should") -def test_verifyRuleTexts(checker): +def test_verifyRuleTexts(checker, capsys): checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt") - with CapturingStdout() as output: - checker.verifyRuleTexts() - captured = ''.join(output.captured) + checker.verifyRuleTexts() + captured = capsys.readouterr().out assert("21.3" not in captured) assert("1.3" in captured) @@ -102,23 +83,15 @@ def test_rules_misra_severity(checker): assert(checker.ruleTexts[2104].misra_severity == '') -def test_extra_output_from_misra_py(checker): - # Extra data generated by misra.py addon are available only through --cli option. - checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt") - with CapturingStderr() as output: - checker.parseDump("./addons/test/misra-test.c.dump") - captured = ''.join(output.captured) - assert("Mandatory" in captured) - assert("Required" in captured) - assert("Advisory" in captured) - +def test_json_out(checker, capsys): sys.argv.append("--cli") checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt") - with CapturingStdout() as output: - checker.parseDump("./addons/test/misra-test.c.dump") + checker.parseDump("./addons/test/misra-test.c.dump") + captured = capsys.readouterr() + captured = captured.out.splitlines() sys.argv.remove("--cli") json_output = {} - for line in output.captured: + for line in captured: try: json_line = json.loads(line) json_output[json_line['errorId']] = json_line @@ -129,11 +102,25 @@ def test_extra_output_from_misra_py(checker): assert("Advisory" in json_output['c2012-20.1']['extra']) -def test_rules_cppcheck_severity(checker): +def test_rules_cppcheck_severity(checker, capsys): checker.loadRuleTexts("./addons/test/assets/misra_rules_dummy.txt") - with CapturingStderr() as output: - checker.parseDump("./addons/test/misra-test.c.dump") - captured = ''.join(output.captured) + checker.parseDump("./addons/test/misra-test.c.dump") + captured = capsys.readouterr().err assert("(error)" not in captured) assert("(warning)" not in captured) assert("(style)" in captured) + + +def test_rules_suppression(checker, capsys): + test_sources = ["addons/test/misra-suppressions1-test.c", + "addons/test/misra-suppressions2-test.c"] + + for src in test_sources: + re_suppressed= r"\[%s\:[0-9]+\]" % src + dump_remove(src) + dump_create(src, "--suppressions-list=addons/test/suppressions.txt") + checker.parseDump(src + ".dump") + captured = capsys.readouterr().err + found = re.search(re_suppressed, captured) + assert(found is None) + dump_remove(src)