Skip to content

Commit

Permalink
Merge pull request #75 from melexis/coverity_desktop_parser
Browse files Browse the repository at this point in the history
Initial solution for Coverity regex checker
  • Loading branch information
bavovanachte authored Nov 30, 2018
2 parents 8bcf78b + 8d1d6cf commit 68ada70
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 3 deletions.
31 changes: 31 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,37 @@ command:
python -m mlx.warnings --doxygen --command <commandfordoxygen>
Parse for Coverity Defects
--------------------------

Coverity is a static analysis tool which has option to run desktop analysis
on your local changes and report the results back directly in the console.
You only need to list affected files and below example lists changed files
between your branch and master, which it then forwards to `cov-run-desktop`:

.. code-block:: bash
cov-run-desktop --text-output-style=oneline `git diff --name-only --ignore-submodules master`
You can pipe the results to logfile, which you pass to warnings-plugin, or you use
the `--command` argument and execute the `cov-run-desktop` through

.. code-block:: bash
# command line log file
mlx-warnings cov-run-desktop-output.txt --coverity
# command line command execution
mlx-warnings --coverity --command <commandforcoverity>
# explicitly as python module for log file
python3 -m mlx.warnings --coverity cov-run-desktop-output.txt
python -m mlx.warnings --coverity cov-run-desktop-output.txt
# explicitly as python module
python3 -m mlx.warnings --coverity --command <commandforcoverity>
python -m mlx.warnings --coverity --command <commandforcoverity>
Parse for JUnit failures
------------------------

Expand Down
10 changes: 7 additions & 3 deletions src/mlx/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import subprocess
import sys
import glob
from mlx.warnings_checker import SphinxChecker, DoxyChecker, JUnitChecker, XMLRunnerChecker
from mlx.warnings_checker import SphinxChecker, DoxyChecker, JUnitChecker, XMLRunnerChecker, CoverityChecker
from .__warnings_version__ import version as warnings_version

__version__ = warnings_version
Expand All @@ -28,7 +28,7 @@ def __init__(self, verbose = False, configfile= None):
self.checkerList = {}
self.verbose = verbose
self.publicCheckers = [SphinxChecker(self.verbose), DoxyChecker(self.verbose), JUnitChecker(self.verbose),
XMLRunnerChecker(self.verbose)]
XMLRunnerChecker(self.verbose), CoverityChecker(self.verbose)]

if configfile is not None:
with open(configfile, 'r') as f:
Expand Down Expand Up @@ -183,6 +183,7 @@ def config_parser_json(self, config):
def warnings_wrapper(args):
parser = argparse.ArgumentParser(prog='mlx-warnings')
group1 = parser.add_argument_group('Configuration command line options')
group1.add_argument('--coverity', dest='coverity', action='store_true')
group1.add_argument('-d', '--doxygen', dest='doxygen', action='store_true')
group1.add_argument('-s', '--sphinx', dest='sphinx', action='store_true')
group1.add_argument('-j', '--junit', dest='junit', action='store_true')
Expand All @@ -206,7 +207,8 @@ def warnings_wrapper(args):

# Read config file
if args.configfile is not None:
if args.sphinx or args.doxygen or args.junit or (args.maxwarnings != 0) or (args.minwarnings != 0):
checkersflag = args.sphinx or args.doxygen or args.junit or args.coverity or args.xmlrunner
if checkersflag or (args.maxwarnings != 0) or (args.minwarnings != 0):
print("Configfile cannot be provided with other arguments")
sys.exit(2)
warnings = WarningsPlugin(verbose=args.verbose, configfile=args.configfile)
Expand All @@ -220,6 +222,8 @@ def warnings_wrapper(args):
warnings.activate_checker_name('junit')
if args.xmlrunner:
warnings.activate_checker_name('xmlrunner')
if args.coverity:
warnings.activate_checker_name('coverity')
warnings.set_maximum(args.maxwarnings)
warnings.set_minimum(args.minwarnings)

Expand Down
23 changes: 23 additions & 0 deletions src/mlx/warnings_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
PYTHON_XMLRUNNER_REGEX = r"(\s*(ERROR|FAILED) (\[\d+.\d\d\ds\]: \s*(.+)))\n?"
xmlrunner_pattern = re.compile(PYTHON_XMLRUNNER_REGEX)

COVERITY_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):) (CID) \d+ \(#(?P<curr>\d+) of (?P<max>\d+)\): (?P<checker>.+)\): (?P<classification>\w+), *(.+)\n?"
coverity_pattern = re.compile(COVERITY_WARNING_REGEX)


class WarningsChecker(object):
name = 'checker'
Expand Down Expand Up @@ -189,4 +192,24 @@ def check(self, content):
return


class CoverityChecker(RegexChecker):
name = 'coverity'
pattern = coverity_pattern
CLASSIFICATION = "Unclassified"

def check(self, content):
'''
Function for counting the number of warnings, but adopted for Coverity
output
Args:
content (str): The content to parse
'''
matches = re.finditer(self.pattern, content)
for match in matches:
if (match.group('curr') == match.group('max')) and \
(match.group('classification') in self.CLASSIFICATION):
self.count += 1
if self.verbose:
print(match.group(0).strip())

4 changes: 4 additions & 0 deletions tests/coverity_single_defect.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/src/somefile.c:80: CID 113396 (#1 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.
/src/somefile.c:82: CID 113396 (#2 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.
src/something/src/somefile.c:82: 1. misra_violation: Essential type of the left hand operand "0U" (unsigned) is not the same as that of the right operand "1U"(signed).

52 changes: 52 additions & 0 deletions tests/test_coverity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from mock import patch
from unittest import TestCase

from mlx.warnings import WarningsPlugin


class TestCoverityWarnings(TestCase):
def setUp(self):
self.warnings = WarningsPlugin(verbose=True)
self.warnings.activate_checker_name('coverity')

def test_no_warning_normal_text(self):
dut = 'This should not be treated as warning'
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 0)

def test_no_warning_but_still_command_output(self):
dut = 'src/something/src/somefile.c:82: 1. misra_violation: Essential type of the left hand operand "0U" (unsigned) is not the same as that of the right operand "1U"(signed).'
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 0)

def test_single_warning(self):
dut = '/src/somefile.c:82: CID 113396 (#2 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.'
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 1)
self.assertIn(dut, fake_out.getvalue())

def test_single_warning_count_one(self):
dut1 = '/src/somefile.c:80: CID 113396 (#1 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.'
dut2 = '/src/somefile.c:82: CID 113396 (#2 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.'
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut1)
self.warnings.check(dut2)
self.assertEqual(self.warnings.return_count(), 1)
self.assertIn(dut2, fake_out.getvalue())

def test_single_warning_real_output(self):
dut1 = '/src/somefile.c:80: CID 113396 (#1 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.'
dut2 = '/src/somefile.c:82: CID 113396 (#2 of 2): Coding standard violation (MISRA C-2012 Rule 10.1): Unclassified, Unspecified, Undecided, owner is nobody, first detected on 2017-07-27.'
dut3 = 'src/something/src/somefile.c:82: 1. misra_violation: Essential type of the left hand operand "0U" (unsigned) is not the same as that of the right operand "1U"(signed).'
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut1)
self.warnings.check(dut2)
self.warnings.check(dut3)
self.assertEqual(self.warnings.return_count(), 1)
self.assertIn(dut2, fake_out.getvalue())

4 changes: 4 additions & 0 deletions tests/test_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ def test_single_argument(self):
retval = warnings_wrapper(['--junit', 'tests/junit_single_fail.xml'])
self.assertEqual(1, retval)

def test_single_defect_coverity(self):
retval = warnings_wrapper(['--coverity', 'tests/coverity_single_defect.txt'])
self.assertEqual(1, retval)

def test_two_arguments(self):
retval = warnings_wrapper(['--junit', 'tests/junit_single_fail.xml', 'tests/junit_double_fail.xml'])
self.assertEqual(1 + 2, retval)
Expand Down

0 comments on commit 68ada70

Please sign in to comment.