Skip to content

Commit

Permalink
Merge pull request #36 from melexis/verbose-flag
Browse files Browse the repository at this point in the history
Add verbose flag
  • Loading branch information
bavovanachte authored Aug 11, 2017
2 parents 2b3a081 + b22e0af commit fd73eb1
Show file tree
Hide file tree
Showing 9 changed files with 156 additions and 61 deletions.
11 changes: 11 additions & 0 deletions docs/authors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
=======
Authors
=======

Melexis Warnings plugin is mainly maintained by:

- Bavo Van Achte
- Crt Mori
- Stein Heselmans


12 changes: 7 additions & 5 deletions docs/design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,41 @@ Class diagram
@startuml
class WarningsPlugin {
#checkerList : WarningsChecker
+__init__(sphinx=False, doxygen=False, junit=False, verbose=False)
}

class WarningsChecker {
#min_count = 0
#max_count = 0
#count = 0
#verbose = False

#{abstract} __init__(name)
#{abstract} __init__(name, verbose=False)
+set_limits(min_count=0, max_count=0,
+{abstract}check(content)
+get_count()
}

class RegexChecker {
#{abstract} __init__(name, regex)
#{abstract} __init__(name, regex, verbose=False)
+check(content)
}

class SphinxChecker {
#{static} String name
#{static} String regex
+__init__()
+__init__(verbose=False)
}

class DoxyChecker {
#{static} String name
#{static} String regex
+__init__()
+__init__(verbose=False)
}

class JUnitChecker {
#{static} String name
+__init__()
+__init__(verbose=False)
+check(content)
}

Expand Down
60 changes: 38 additions & 22 deletions src/mlx/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,27 @@
import re
import sys
import abc
from junitparser import JUnitXml
from junitparser import JUnitXml, Failure, Error
import glob

DOXYGEN_WARNING_REGEX = r"(?:(?:((?:[/.]|[A-Za-z]:).+?):(-?\d+):\s*([Ww]arning|[Ee]rror)|<.+>:-?\d+(?::\s*([Ww]arning|[Ee]rror))?): (.+(?:\n(?!\s*(?:[Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*([Nn]otice|[Ww]arning|[Ee]rror): (.+))$"
DOXYGEN_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):\s*([Ww]arning|[Ee]rror)|<.+>:-?\d+(?::\s*([Ww]arning|[Ee]rror))?): (.+(?:(?!\s*(?:[Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*([Nn]otice|[Ww]arning|[Ee]rror): (.+)\n?"
doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX)

SPHINX_WARNING_REGEX = r"^(.+?:(?:\d+|None)): (DEBUG|INFO|WARNING|ERROR|SEVERE): (.+)\n?$"
SPHINX_WARNING_REGEX = r"(.+?:(?:\d+|None)):\s*(DEBUG|INFO|WARNING|ERROR|SEVERE):\s*(.+)\n?"
sphinx_pattern = re.compile(SPHINX_WARNING_REGEX)


class WarningsChecker(object):
name = 'checker'

def __init__(self, name):
def __init__(self, verbose=False):
''' Constructor
Args:
name (str): Name of the checker
verbose (bool): Enable/disable verbose logging
'''
self.name = name
self.verbose = verbose
self.reset()

def reset(self):
Expand Down Expand Up @@ -113,16 +115,17 @@ def return_check_limits(self):


class RegexChecker(WarningsChecker):
name = 'regex'
pattern = None

def __init__(self, name, pattern):
def __init__(self, verbose=False):
''' Constructor
Args:
name (str): Name of the checker
pattern (str): Regular expression used by the checker in order to find warnings
'''
super(RegexChecker, self).__init__(name=name)
self.pattern = pattern
super(RegexChecker, self).__init__(verbose=verbose)

def check(self, content):
'''
Expand All @@ -131,28 +134,33 @@ def check(self, content):
Args:
content (str): The content to parse
'''
self.count += len(re.findall(self.pattern, content))
matches = re.finditer(self.pattern, content)
for match in matches:
self.count += 1
if self.verbose:
print(match.group(0).strip())


class SphinxChecker(RegexChecker):
name = 'sphinx'

def __init__(self):
super(SphinxChecker, self).__init__(name=SphinxChecker.name, pattern=sphinx_pattern)
pattern = sphinx_pattern


class DoxyChecker(RegexChecker):
name = 'doxygen'

def __init__(self):
super(DoxyChecker, self).__init__(name=DoxyChecker.name, pattern=doxy_pattern)
pattern = doxy_pattern


class JUnitChecker(WarningsChecker):
name = 'junit'

def __init__(self):
super(JUnitChecker, self).__init__(name=JUnitChecker.name)
def __init__(self, verbose=False):
''' Constructor
Args:
verbose (bool): Enable/disable verbose logging
'''
super(JUnitChecker, self).__init__(verbose=verbose)

def check(self, content):
'''
Expand All @@ -162,28 +170,35 @@ def check(self, content):
content (str): The content to parse
'''
result = JUnitXml.fromstring(content)
if self.verbose:
for suite in result:
for testcase in filter(lambda testcase: isinstance(testcase.result, (Failure, Error)), suite):
print('{classname}.{testname}'.format(classname=testcase.classname,
testname=testcase.name))
result.update_statistics()
self.count += result.errors + result.failures


class WarningsPlugin:

def __init__(self, sphinx = False, doxygen = False, junit = False):
def __init__(self, sphinx = False, doxygen = False, junit = False, verbose = False):
'''
Function for initializing the parsers
Args:
sphinx (bool, optional): enable sphinx parser
doxygen (bool, optional): enable doxygen parser
junit (bool, optional): enable junit parser
verbose (bool, optional): enable verbose logging
'''
self.checkerList = {}
self.verbose = verbose
if sphinx:
self.activate_checker(SphinxChecker())
self.activate_checker(SphinxChecker(self.verbose))
if doxygen:
self.activate_checker(DoxyChecker())
self.activate_checker(DoxyChecker(self.verbose))
if junit:
self.activate_checker(JUnitChecker())
self.activate_checker(JUnitChecker(self.verbose))

self.warn_min = 0
self.warn_max = 0
Expand Down Expand Up @@ -291,6 +306,7 @@ def main():
group.add_argument('-d', '--doxygen', dest='doxygen', action='store_true')
group.add_argument('-s', '--sphinx', dest='sphinx', action='store_true')
group.add_argument('-j', '--junit', dest='junit', action='store_true')
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true')
parser.add_argument('-m', '--maxwarnings', type=int, required=False, default=0,
help='Maximum amount of warnings accepted')
parser.add_argument('--minwarnings', type=int, required=False, default=0,
Expand All @@ -300,7 +316,7 @@ def main():
parser.add_argument('logfile', nargs='+', help='Logfile that might contain warnings')
args = parser.parse_args()

warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit)
warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit, verbose=args.verbose)
warnings.set_maximum(args.maxwarnings)
warnings.set_minimum(args.minwarnings)

Expand Down
8 changes: 4 additions & 4 deletions tests/junit_double_fail.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<?xml version='1.0' encoding='utf-8'?>
<testsuites>
<testsuite errors="0" failures="1" name="test_warn_plugin_double_fail" skips="0" tests="2" time="33">
<testcase classname="test_warn_plugin_double_fail.foo" name="foo" time="22"/>
<testcase classname="test_warn_plugin_double_fail.bar" name="bar" time="44">
<testcase classname="test_warn_plugin_double_fail" name="myfirstgreatsu66ess" time="22"/>
<testcase classname="test_warn_plugin_double_fail" name="myfirstfai1ure" time="44">
<failure message="Is our warnings plugin able to trace this random failure msg?" />
</testcase>
</testsuite>
<testsuite errors="0" failures="0" name="test_warn_plugin_no_double_fail" skips="0" tests="1" time="33">
<testcase classname="test_warn_plugin_double_fail.john" name="john" time="33">
<testcase classname="test_warn_plugin_no_double_fail" name="mysecondfai1ure" time="33">
<failure message="Second failure">This time there is more content to the failure</failure>
</testcase>
<testcase classname="test_warn_plugin_no_double_fail.doe" name="doe" time="77"/>
<testcase classname="test_warn_plugin_no_double_fail" name="mysecondgreatsu66ess" time="77"/>
</testsuite>
</testsuites>
6 changes: 3 additions & 3 deletions tests/junit_no_fail.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<testsuites>
<testsuite errors="0" failures="0" name="test_warn_plugin_no_failures" skipped="0" tests="2" time="33">
<testcase classname="test_warn_plugin_ok.foo" name="foo" time="22"/>
<testcase classname="test_warn_plugin_ok.foo" name="bar" time="44"/>
<testsuite errors="0" failures="0" name="test_warn_plugin_ok" skipped="0" tests="2" time="33">
<testcase classname="test_warn_plugin_ok" name="foo" time="22"/>
<testcase classname="test_warn_plugin_ok" name="bar" time="44"/>
</testsuite>
</testsuites>
6 changes: 3 additions & 3 deletions tests/junit_single_fail.xml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<testsuites>
<testsuite errors="0" failures="1" name="test_warn_plugin_single_fail" skips="0" tests="2" time="33">
<testcase classname="test_warn_plugin_single_fail.foo" name="foo" time="22"/>
<testcase classname="test_warn_plugin_single_fail.bar" name="bar" time="44">
<testcase classname="test_warn_plugin_single_fail" name="myfirstgreatsu66ess" time="22"/>
<testcase classname="test_warn_plugin_single_fail" name="myfirstfai1ure" time="44">
<failure message="Is our warnings plugin able to trace this random failure msg?" />
</testcase>
</testsuite>
<testsuite errors="0" failures="0" name="test_warn_plugin_no_single_fail" skips="0" tests="1" time="33">
<testcase classname="test_warn_plugin_no_single_fail.john" name="john" time="33"/>
<testcase classname="test_warn_plugin_no_single_fail" name="mysecondgreatsu66ess" time="33"/>
</testsuite>
</testsuites>
40 changes: 33 additions & 7 deletions tests/test_doxygen.py
Original file line number Diff line number Diff line change
@@ -1,24 +1,50 @@
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 TestDoxygenWarnings(TestCase):
def setUp(self):
self.warnings = WarningsPlugin(False, True, False)
print(str(self.warnings))
self.warnings = WarningsPlugin(doxygen=True, verbose=True)

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

def test_single_warning(self):
self.warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"')
dut = 'testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"'
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 1)
self.assertRegexpMatches(fake_out.getvalue(), dut)

def test_single_warning_mixed(self):
self.warnings.check('This1 should not be treated as warning')
self.warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"')
self.warnings.check('This should not be treated as warning2')
dut1 = 'This1 should not be treated as warning'
dut2 = 'testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"'
dut3 = 'This should not be treated as warning2'
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.assertRegexpMatches(fake_out.getvalue(), dut2)

def test_multiline(self):
duterr1 = "testfile.c:6: warning: group test: ignoring title \"Some test functions\" that does not match old title \"Some freaky test functions\"\n"
duterr2 = "testfile.c:8: warning: group test: ignoring title \"Some test functions\" that does not match old title \"Some freaky test functions\"\n"
dut = "This1 should not be treated as warning\n"
dut += duterr1
dut += "This should not be treated as warning2\n"
dut += duterr2
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(dut)
self.assertEqual(self.warnings.return_count(), 2)
self.assertRegexpMatches(fake_out.getvalue(), duterr1)
self.assertRegexpMatches(fake_out.getvalue(), duterr2)

24 changes: 17 additions & 7 deletions tests/test_junit.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
try:
from StringIO import StringIO
except ImportError:
from io import StringIO
from mock import patch
from unittest import TestCase

from mlx.warnings import WarningsPlugin
Expand All @@ -6,22 +11,27 @@

class TestJUnitFailures(TestCase):
def setUp(self):
self.warnings = WarningsPlugin(False, False, True)
self.warnings = WarningsPlugin(junit=True, verbose=True)

def test_no_warning(self):
with open('tests/junit_no_fail.xml') as xmlfile:
self.warnings.check(xmlfile.read())
with open('tests/junit_no_fail.xml', 'r') as xmlfile:
self.warnings.check(xmlfile.read().encode('utf-8'))
self.assertEqual(self.warnings.return_count(), 0)

def test_single_warning(self):
with open('tests/junit_single_fail.xml') as xmlfile:
self.warnings.check(xmlfile.read())
with open('tests/junit_single_fail.xml', 'r') as xmlfile:
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(xmlfile.read().encode('utf-8'))
self.assertEqual(self.warnings.return_count(), 1)
self.assertRegexpMatches(fake_out.getvalue(), 'myfirstfai1ure')

def test_dual_warning(self):
with open('tests/junit_double_fail.xml') as xmlfile:
self.warnings.check(xmlfile.read())
with open('tests/junit_double_fail.xml', 'r') as xmlfile:
with patch('sys.stdout', new=StringIO()) as fake_out:
self.warnings.check(xmlfile.read().encode('utf-8'))
self.assertEqual(self.warnings.return_count(), 2)
self.assertRegexpMatches(fake_out.getvalue(), 'myfirstfai1ure')
self.assertRegexpMatches(fake_out.getvalue(), 'mysecondfai1ure')

def test_invalid_xml(self):
with self.assertRaises(ParseError):
Expand Down
Loading

0 comments on commit fd73eb1

Please sign in to comment.