From 21cd4d4bd9957677fd45d6e41414fc2bb44fd06a Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Tue, 27 Jun 2017 15:50:49 +0200 Subject: [PATCH 01/11] Basic config parsing As per #7 simple implementation of config parsing. --- src/mlx/warnings.py | 49 ++++++++++--- tests/config_example.json | 17 +++++ tests/test_config.py | 149 ++++++++++++++++++++++++++++++++++++++ tox.ini | 2 +- 4 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 tests/config_example.json create mode 100644 tests/test_config.py diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index 70a84741..a709953f 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -1,6 +1,7 @@ import argparse import re import sys +import json 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): (.+))$" doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX) @@ -130,7 +131,7 @@ def __init__(self): class WarningsPlugin: - def __init__(self, sphinx = False, doxygen = False, junit = False): + def __init__(self, sphinx = False, doxygen = False, junit = False, configfile= None): ''' Function for initializing the parsers @@ -140,12 +141,17 @@ def __init__(self, sphinx = False, doxygen = False, junit = False): junit (bool, optional): enable junit parser ''' self.checkerList = {} - if sphinx: - self.activate_checker(SphinxChecker()) - if doxygen: - self.activate_checker(DoxyChecker()) - if junit: - self.activate_checker(JUnitChecker()) + if configfile is not None: + with open(configfile, 'r') as f: + config = json.load(f) + self.config_parser(config) + else: + if sphinx: + self.activate_checker(SphinxChecker()) + if doxygen: + self.activate_checker(DoxyChecker()) + if junit: + self.activate_checker(JUnitChecker()) self.warn_min = 0 self.warn_max = 0 @@ -246,6 +252,24 @@ def return_check_limits(self, name = None): return 0 + def config_parser(self, config): + ''' Parsing configuration dict extracted by previously opened json file + + Args: + config (dict): json dump of the configuration + ''' + self.publicCheckers = [SphinxChecker(), DoxyChecker(), JUnitChecker()] + # activate checker + for checker in self.publicCheckers: + try: + if bool(config[checker.name]['enabled']): + self.activate_checker(checker) + self.get_checker(checker.name).set_maximum(int(config[checker.name]['max'])) + self.get_checker(checker.name).set_minimum(int(config[checker.name]['min'])) + print("Config parsing for {name} completed".format(name=checker.name)) + except KeyError as e: + print("Uncomplete config. Missing: {key}".format(key=e)) + def main(): parser = argparse.ArgumentParser(prog='mlx-warnings') @@ -257,13 +281,18 @@ def main(): help='Maximum amount of warnings accepted') parser.add_argument('--minwarnings', type=int, required=False, default=0, help='Minimum amount of warnings accepted') + parser.add_argument('-c', '--config', dest='configfile', action='store', required=False) parser.add_argument('logfile', help='Logfile that might contain warnings') args = parser.parse_args() - warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit) - warnings.set_maximum(args.maxwarnings) - warnings.set_minimum(args.minwarnings) + # Read config file + if args.configfile is not None: + warnings = WarningsPlugin(configfile=args.configfile) + else: + warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit) + warnings.set_maximum(args.maxwarnings) + warnings.set_minimum(args.minwarnings) for line in open(args.logfile, 'r'): warnings.check(line) diff --git a/tests/config_example.json b/tests/config_example.json new file mode 100644 index 00000000..206315b6 --- /dev/null +++ b/tests/config_example.json @@ -0,0 +1,17 @@ +{ + "sphinx":{ + "enabled": true, + "min": 0, + "max": 0 + }, + "doxygen":{ + "enabled": false, + "min": 0, + "max": 0 + }, + "junit":{ + "enabled": false, + "min": 0, + "max": 0 + } +} diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 00000000..fba48689 --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,149 @@ +from unittest import TestCase + +from mlx.warnings import WarningsPlugin, SphinxChecker, DoxyChecker, JUnitChecker + + +class TestConfig(TestCase): + def test_configfile_parsing(self): + warnings = WarningsPlugin(configfile="tests/config_example.json") + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 0) + warnings.check('') + self.assertEqual(warnings.return_count(), 0) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 1) + warnings.check('This should not be treated as warning2') + self.assertEqual(warnings.return_count(), 1) + + def test_partial_sphinx_config_parsing(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 0, + 'max': 0 + } + } + + warnings.config_parser(tmpjson) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 0) + warnings.check('') + self.assertEqual(warnings.return_count(), 0) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 1) + + def test_partial_doxygen_config_parsing(self): + warnings = WarningsPlugin() + tmpjson = { + 'doxygen': { + 'enabled': True, + 'min': 0, + 'max': 0 + } + } + + warnings.config_parser(tmpjson) + warnings.check('') + self.assertEqual(warnings.return_count(), 0) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 0) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 1) + + def test_partial_junit_config_parsing(self): + warnings = WarningsPlugin() + tmpjson = { + 'junit': { + 'enabled': True, + 'min': 0, + 'max': 0 + } + } + + warnings.config_parser(tmpjson) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 0) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 0) + warnings.check('') + self.assertEqual(warnings.return_count(), 1) + + def test_sphinx_config_max(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 0, + 'max': 5 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(SphinxChecker().name).get_maximum(), 5) + + def test_doxygen_config_max(self): + warnings = WarningsPlugin() + tmpjson = { + 'doxygen': { + 'enabled': True, + 'min': 0, + 'max': 5 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(DoxyChecker().name).get_maximum(), 5) + + def test_junit_config_max(self): + warnings = WarningsPlugin() + tmpjson = { + 'junit': { + 'enabled': True, + 'min': 0, + 'max': 5 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(JUnitChecker().name).get_maximum(), 5) + + def test_sphinx_config_min(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 5, + 'max': 7 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(SphinxChecker().name).get_minimum(), 5) + + def test_doxygen_config_min(self): + warnings = WarningsPlugin() + tmpjson = { + 'doxygen': { + 'enabled': True, + 'min': 5, + 'max': 7 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(DoxyChecker().name).get_minimum(), 5) + + def test_junit_config_min(self): + warnings = WarningsPlugin() + tmpjson = { + 'junit': { + 'enabled': True, + 'min': 5, + 'max': 7 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(JUnitChecker().name).get_minimum(), 5) + diff --git a/tox.ini b/tox.ini index f989813a..80f8171f 100644 --- a/tox.ini +++ b/tox.ini @@ -71,8 +71,8 @@ deps = skip_install = true commands = python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} -u flake8 src tests setup.py + check-manifest {toxinidir} -u [testenv:coveralls] deps = From 422ee1f3994ea6914b9cf9fb4c6cb3c1aaf46f38 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Tue, 27 Jun 2017 15:58:04 +0200 Subject: [PATCH 02/11] Config parser is added feature and although it does not break backwards compatibility it does add a new dimension --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c53f79e8..049251f4 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import find_packages, setup PROJECT_URL = 'https://github.com/melexis/warnings-plugin' -VERSION = '0.0.7' +VERSION = '0.1.0' def read(*names, **kwargs): From f1196265a0a55060f542775fc5c5fb988562d4cf Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Wed, 7 Mar 2018 22:58:25 +0100 Subject: [PATCH 03/11] Minor adjustments after merge to get up-to-date with recent changes XML parser is a bit more strict than it was, so it does not take strings nicely --- src/mlx/warnings.py | 3 +-- tests/test_config.py | 16 +++++++++++----- tox.ini | 1 - 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index f530e058..f84df283 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -191,7 +191,6 @@ def warnings_wrapper(args): parser.add_argument('--version', action='version', version='%(prog)s {version}'.format(version=pkg_resources.require('mlx.warnings')[0].version)) parser.add_argument('--config', dest='configfile', action='store', required=False) - parser.add_argument('logfile', nargs='+', help='Logfile (or command) that might contain warnings') parser.add_argument('flags', nargs=argparse.REMAINDER, help='Possible not-used flags from above are considered as command flags') @@ -199,7 +198,7 @@ def warnings_wrapper(args): # Read config file if args.configfile is not None: - warnings = WarningsPlugin(configfile=args.configfile) + warnings = WarningsPlugin(configfile=args.configfile) else: warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit, verbose=args.verbose) warnings.set_maximum(args.maxwarnings) diff --git a/tests/test_config.py b/tests/test_config.py index fba48689..7ce76182 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,4 +1,5 @@ from unittest import TestCase +from xml.etree.ElementTree import ParseError from mlx.warnings import WarningsPlugin, SphinxChecker, DoxyChecker, JUnitChecker @@ -28,7 +29,8 @@ def test_partial_sphinx_config_parsing(self): warnings.config_parser(tmpjson) warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 0) - warnings.check('') + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 0) warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 1) @@ -44,7 +46,8 @@ def test_partial_doxygen_config_parsing(self): } warnings.config_parser(tmpjson) - warnings.check('') + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 0) warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 0) @@ -62,11 +65,14 @@ def test_partial_junit_config_parsing(self): } warnings.config_parser(tmpjson) - warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + with self.assertRaises(ParseError): + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 0) - warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + with self.assertRaises(ParseError): + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 0) - warnings.check('') + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 1) def test_sphinx_config_max(self): diff --git a/tox.ini b/tox.ini index 5ef5821c..61c2795e 100644 --- a/tox.ini +++ b/tox.ini @@ -81,7 +81,6 @@ skip_install = true commands = python setup.py check --strict --metadata --restructuredtext flake8 src tests setup.py - check-manifest {toxinidir} -u [testenv:coveralls] deps = From d3c10be17f193d5027b9fb071a6b9692acbb99b1 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 11:43:42 +0100 Subject: [PATCH 04/11] Grace nicely instead of failing for parse error If we want to have multiple parsers active we need to fail gracefully already inside parser --- src/mlx/warnings_checker.py | 21 ++++++++----- tests/test_config.py | 62 ++++++++++++++++++++++++++++++++++--- tests/test_junit.py | 4 +-- tests/test_warnings.py | 10 ++---- 4 files changed, 74 insertions(+), 23 deletions(-) diff --git a/src/mlx/warnings_checker.py b/src/mlx/warnings_checker.py index 4d4acb46..4727ea06 100644 --- a/src/mlx/warnings_checker.py +++ b/src/mlx/warnings_checker.py @@ -4,6 +4,8 @@ import abc import re from junitparser import JUnitXml, Failure, Error +from xml.etree.ElementTree import ParseError + 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) @@ -166,14 +168,17 @@ def check(self, content): Args: content (str): The content to parse ''' - result = JUnitXml.fromstring(content.encode('utf-8')) - 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 + try: + result = JUnitXml.fromstring(content.encode('utf-8')) + 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 + except ParseError as _: + return diff --git a/tests/test_config.py b/tests/test_config.py index 7ce76182..0fb23a92 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,4 @@ from unittest import TestCase -from xml.etree.ElementTree import ParseError from mlx.warnings import WarningsPlugin, SphinxChecker, DoxyChecker, JUnitChecker @@ -65,15 +64,68 @@ def test_partial_junit_config_parsing(self): } warnings.config_parser(tmpjson) - with self.assertRaises(ParseError): - warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 0) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 0) - with self.assertRaises(ParseError): - warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 1) + + def test_doxy_junit_options_config_parsing(self): + warnings = WarningsPlugin() + tmpjson = { + 'doxygen': { + 'enabled': True, + 'min': 0, + 'max': 0 + }, + 'junit': { + 'enabled': True, + 'min': 0, + 'max': 0 + } + + } + warnings.config_parser(tmpjson) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 0) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 1) with open('tests/junit_single_fail.xml', 'r') as xmlfile: warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 2) + + def test_sphinx_doxy_config_parsing(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 0, + 'max': 0 + }, + 'doxygen': { + 'enabled': True, + 'min': 0, + 'max': 0 + } + } + + warnings.config_parser(tmpjson) + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 0) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 1) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 2) + with open('tests/junit_single_fail.xml', 'r') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 2) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 3) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 4) def test_sphinx_config_max(self): warnings = WarningsPlugin() diff --git a/tests/test_junit.py b/tests/test_junit.py index 358dfcac..ce91b83a 100644 --- a/tests/test_junit.py +++ b/tests/test_junit.py @@ -6,7 +6,6 @@ from unittest import TestCase from mlx.warnings import WarningsPlugin -from xml.etree.ElementTree import ParseError class TestJUnitFailures(TestCase): @@ -34,7 +33,6 @@ def test_dual_warning(self): self.assertRegexpMatches(fake_out.getvalue(), 'mysecondfai1ure') def test_invalid_xml(self): - with self.assertRaises(ParseError): - self.warnings.check('this is not xml') + self.warnings.check('this is not xml') self.assertEqual(self.warnings.return_count(), 0) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index d4dffbae..4866f912 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -1,7 +1,6 @@ from unittest import TestCase from mlx.warnings import WarningsPlugin -from xml.etree.ElementTree import ParseError class TestWarningsPlugin(TestCase): @@ -50,14 +49,11 @@ def test_junit_warning_only(self): with open('tests/junit_single_fail.xml') as xmlfile: warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 1) - with self.assertRaises(ParseError): - warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 1) - with self.assertRaises(ParseError): - warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 1) - with self.assertRaises(ParseError): - warnings.check('This should not be treated as warning2') + warnings.check('This should not be treated as warning2') self.assertEqual(warnings.return_count(), 1) From 366c82d58c64e81c9802ad9b64e6859b289c9360 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 13:46:01 +0100 Subject: [PATCH 05/11] Removing mutually exclusive group and adding some description to config argument --- src/mlx/warnings.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index f84df283..90e8bf5b 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -175,10 +175,9 @@ def config_parser(self, config): def warnings_wrapper(args): parser = argparse.ArgumentParser(prog='mlx-warnings') - group = parser.add_mutually_exclusive_group(required=True) - 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('-d', '--doxygen', dest='doxygen', action='store_true') + parser.add_argument('-s', '--sphinx', dest='sphinx', action='store_true') + parser.add_argument('-j', '--junit', dest='junit', action='store_true') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true') parser.add_argument('--command', dest='command', action='store_true', help='Treat program arguments as command to execute to obtain data') @@ -189,8 +188,7 @@ def warnings_wrapper(args): parser.add_argument('--minwarnings', type=int, required=False, default=0, help='Minimum amount of warnings accepted') parser.add_argument('--version', action='version', version='%(prog)s {version}'.format(version=pkg_resources.require('mlx.warnings')[0].version)) - parser.add_argument('--config', dest='configfile', action='store', required=False) - + parser.add_argument('--config', dest='configfile', action='store', required=False, help='Config file in JSON format provides toggle of checkers and their limits') parser.add_argument('logfile', nargs='+', help='Logfile (or command) that might contain warnings') parser.add_argument('flags', nargs=argparse.REMAINDER, help='Possible not-used flags from above are considered as command flags') From b7f42465253da7a23678b11419472f2e21e3c664 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 13:49:53 +0100 Subject: [PATCH 06/11] Update unit tests for multiple arguments --- tests/test_warnings.py | 47 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/test_warnings.py b/tests/test_warnings.py index 4866f912..57f197ac 100644 --- a/tests/test_warnings.py +++ b/tests/test_warnings.py @@ -56,4 +56,51 @@ def test_junit_warning_only(self): warnings.check('This should not be treated as warning2') self.assertEqual(warnings.return_count(), 1) + def test_doxy_sphinx_warning(self): + warnings = WarningsPlugin(True, True, False) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 1) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 2) + with open('tests/junit_single_fail.xml') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 2) + warnings.check('This should not be treated as warning2') + self.assertEqual(warnings.return_count(), 2) + + def test_doxy_junit_warning(self): + warnings = WarningsPlugin(False, True, True) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 1) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 1) + with open('tests/junit_single_fail.xml') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 2) + warnings.check('This should not be treated as warning2') + self.assertEqual(warnings.return_count(), 2) + + def test_sphinx_junit_warning(self): + warnings = WarningsPlugin(True, False, True) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 0) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 1) + with open('tests/junit_single_fail.xml') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 2) + warnings.check('This should not be treated as warning2') + self.assertEqual(warnings.return_count(), 2) + + def test_all_warning(self): + warnings = WarningsPlugin(True, True, True) + warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') + self.assertEqual(warnings.return_count(), 1) + warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 2) + with open('tests/junit_single_fail.xml') as xmlfile: + warnings.check(xmlfile.read()) + self.assertEqual(warnings.return_count(), 3) + warnings.check('This should not be treated as warning2') + self.assertEqual(warnings.return_count(), 3) From 6956d34889d24889736bbbf148b1dd011f4b7f37 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 14:14:05 +0100 Subject: [PATCH 07/11] Testing enable and limits of all checkers at once --- tests/test_config.py | 50 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/test_config.py b/tests/test_config.py index 0fb23a92..45beac60 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -166,6 +166,31 @@ def test_junit_config_max(self): warnings.config_parser(tmpjson) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_maximum(), 5) + def test_all_config_max(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 0, + 'max': 4 + }, + 'doxygen': { + 'enabled': True, + 'min': 0, + 'max': 5 + }, + 'junit': { + 'enabled': True, + 'min': 0, + 'max': 6 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(SphinxChecker().name).get_maximum(), 4) + self.assertEqual(warnings.get_checker(DoxyChecker().name).get_maximum(), 5) + self.assertEqual(warnings.get_checker(JUnitChecker().name).get_maximum(), 6) + def test_sphinx_config_min(self): warnings = WarningsPlugin() tmpjson = { @@ -205,3 +230,28 @@ def test_junit_config_min(self): warnings.config_parser(tmpjson) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_minimum(), 5) + def test_all_config_min(self): + warnings = WarningsPlugin() + tmpjson = { + 'sphinx': { + 'enabled': True, + 'min': 4, + 'max': 7 + }, + 'doxygen': { + 'enabled': True, + 'min': 3, + 'max': 7 + }, + 'junit': { + 'enabled': True, + 'min': 5, + 'max': 7 + } + } + + warnings.config_parser(tmpjson) + self.assertEqual(warnings.get_checker(SphinxChecker().name).get_minimum(), 4) + self.assertEqual(warnings.get_checker(DoxyChecker().name).get_minimum(), 3) + self.assertEqual(warnings.get_checker(JUnitChecker().name).get_minimum(), 5) + From a465a6882d1ae17a7230bfcf50b73602b245be25 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 16:31:54 +0100 Subject: [PATCH 08/11] Adding unit tests and grouping the excluding stuff Configuration file and command line arguments cannot be passed in together. Since argparse does not provide this option (tried mutually_exclusive_group) it was done hackishly. --- src/mlx/warnings.py | 23 ++++++++++++++--------- tests/test_integration.py | 10 ++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index 90e8bf5b..25d9062f 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -170,25 +170,27 @@ def config_parser(self, config): self.get_checker(checker.name).set_minimum(int(config[checker.name]['min'])) print("Config parsing for {name} completed".format(name=checker.name)) except KeyError as e: - print("Uncomplete config. Missing: {key}".format(key=e)) + print("Incomplete config. Missing: {key}".format(key=e)) def warnings_wrapper(args): parser = argparse.ArgumentParser(prog='mlx-warnings') - parser.add_argument('-d', '--doxygen', dest='doxygen', action='store_true') - parser.add_argument('-s', '--sphinx', dest='sphinx', action='store_true') - parser.add_argument('-j', '--junit', dest='junit', action='store_true') + group1 = parser.add_argument_group('Configuration command line options') + 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') + group1.add_argument('-m', '--maxwarnings', type=int, required=False, default=0, + help='Maximum amount of warnings accepted') + group1.add_argument('--minwarnings', type=int, required=False, default=0, + help='Minimum amount of warnings accepted') + group2 = parser.add_argument_group('Configuration file with options') + group2.add_argument('--config', dest='configfile', action='store', required=False, help='Config file in JSON format provides toggle of checkers and their limits') parser.add_argument('-v', '--verbose', dest='verbose', action='store_true') parser.add_argument('--command', dest='command', action='store_true', help='Treat program arguments as command to execute to obtain data') parser.add_argument('--ignore-retval', dest='ignore', action='store_true', help='Ignore return value of the executed command') - 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, - help='Minimum amount of warnings accepted') parser.add_argument('--version', action='version', version='%(prog)s {version}'.format(version=pkg_resources.require('mlx.warnings')[0].version)) - parser.add_argument('--config', dest='configfile', action='store', required=False, help='Config file in JSON format provides toggle of checkers and their limits') parser.add_argument('logfile', nargs='+', help='Logfile (or command) that might contain warnings') parser.add_argument('flags', nargs=argparse.REMAINDER, help='Possible not-used flags from above are considered as command flags') @@ -196,6 +198,9 @@ 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): + print("Configfile cannot be provided with other arguments") + sys.exit(2) warnings = WarningsPlugin(configfile=args.configfile) else: warnings = WarningsPlugin(sphinx=args.sphinx, doxygen=args.doxygen, junit=args.junit, verbose=args.verbose) diff --git a/tests/test_integration.py b/tests/test_integration.py index e9556a35..0366a6a4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -82,3 +82,13 @@ def test_min(self): def test_min_but_still_ok(self): retval = warnings_wrapper(['--junit', '--maxwarnings', '100', '--minwarnings', '2', 'tests/junit*.xml']) self.assertEqual(0, retval) + + def test_configfile_ok(self): + retval = warnings_wrapper(['--config', 'tests/config_example.json', 'tests/junit_single_fail.xml']) + self.assertEqual(0, retval) + + def test_configfile_exclude_commadline(self): + with self.assertRaises(SystemExit) as ex: + warnings_wrapper(['--config', 'tests/config_example.json', '--junit', 'tests/junit_single_fail.xml']) + self.assertEqual(2, ex.exception.code) + From 3f3cc23b8d764a3696dc50c5c595880ac72e9c17 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Fri, 23 Mar 2018 16:56:06 +0100 Subject: [PATCH 09/11] Updated readme with config file description --- README.rst | 74 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index 4c27a6f0..43da1ec1 100644 --- a/README.rst +++ b/README.rst @@ -123,7 +123,16 @@ The command returns (shell $? variable): - value 0 when the number of counted warnings is within the supplied minimum and maximum limits: ok, - number of counted warnings (positive) when the counter number is not within those limit. -------------------------- +--------------------------- +Simple Command line options +--------------------------- + +Plugin has two forms of passing the arguments to checkers. The command line +option which enables checkers and sets minimum and maximum to each checker +individually, or the configuration file option which provides more flexibility +and also traceability as it resides inside repository and provides option to +adjust minimum and maximum per individual checker. + Parse for Sphinx warnings ------------------------- @@ -135,17 +144,16 @@ command: # command line log file mlx-warnings doc_log.txt --sphinx # command line command execution - mlx-warnings --command --sphinx + mlx-warnings --sphinx --command # explicitly as python module for log file python3 -m mlx.warnings --sphinx doc_log.txt python -m mlx.warnings --sphinx doc_log.txt # explicitly as python module - python3 -m mlx.warnings --command --sphinx - python -m mlx.warnings --command --sphinx + python3 -m mlx.warnings --sphinx --command + python -m mlx.warnings --sphinx --command --------------------------- Parse for Doxygen warnings -------------------------- @@ -157,17 +165,16 @@ command: # command line log file mlx-warnings doc_log.txt --doxygen # command line command execution - mlx-warnings --command --doxygen + mlx-warnings --doxygen --command # explicitly as python module for log file python3 -m mlx.warnings --doxygen doc_log.txt python -m mlx.warnings --doxygen doc_log.txt # explicitly as python module - python3 -m mlx.warnings --command --doxygen - python -m mlx.warnings --command --doxygen + python3 -m mlx.warnings --doxygen --command + python -m mlx.warnings --doxygen --command ------------------------- Parse for JUnit failures ------------------------ @@ -179,14 +186,57 @@ command: # command line log file mlx-warnings junit_output.xml --junit # command line command execution - mlx-warnings --command --junit + mlx-warnings --junit --command # explicitly as python module for log file python3 -m mlx.warnings --junit junit_output.xml python -m mlx.warnings --junit junit_output.xml # explicitly as python module - python3 -m mlx.warnings --command --junit - python -m mlx.warnings --command --junit + python3 -m mlx.warnings --junit --command + python -m mlx.warnings --junit --command + + +---------------------------------- +Configuration file to pass options +---------------------------------- + +Beside command line, you can pass options through the configuration file. +Configuration file is in JSON format with a simple structure. + +.. code-block:: json + + { + "sphinx":{ + "enabled": true, + "min": 0, + "max": 0 + }, + "doxygen":{ + "enabled": false, + "min": 0, + "max": 0 + }, + "junit":{ + "enabled": false, + "min": 0, + "max": 0 + } + } + +First key is `checkername`, then it contains a boolean value for key `enabled`, +value for minimum number of warnings with key `min` and value for maximum +number of warnings with key `max`. This structure allows simple expansion. + +To run the plugin with configuration file you simply pass `--config` flag with +path to configuration file + +.. code-block:: bash + + # command line log file + mlx-warnings --config pathtoconfig.json junit_output.xml + # command line command execution + mlx-warnings --config patchtoconfig.json --command + ------------- Other options From be43cc105b10e63e70b4e663289f31ac3a370091 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Mon, 26 Mar 2018 09:20:35 +0200 Subject: [PATCH 10/11] Typo fixup in unittest name --- tests/test_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_integration.py b/tests/test_integration.py index 0366a6a4..dd86e8fd 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -87,7 +87,7 @@ def test_configfile_ok(self): retval = warnings_wrapper(['--config', 'tests/config_example.json', 'tests/junit_single_fail.xml']) self.assertEqual(0, retval) - def test_configfile_exclude_commadline(self): + def test_configfile_exclude_commandline(self): with self.assertRaises(SystemExit) as ex: warnings_wrapper(['--config', 'tests/config_example.json', '--junit', 'tests/junit_single_fail.xml']) self.assertEqual(2, ex.exception.code) From 114df927c1b33e0261314e307d66269ba0b04cf0 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Mon, 26 Mar 2018 09:26:41 +0200 Subject: [PATCH 11/11] Renaming the config_parser to config_parser_json This is to avoid the problem in case someone thinks this is configparser (.ini) from official python library. --- src/mlx/warnings.py | 4 ++-- tests/test_config.py | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index 25d9062f..4d0f0e56 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -33,7 +33,7 @@ def __init__(self, sphinx = False, doxygen = False, junit = False, verbose = Fal if configfile is not None: with open(configfile, 'r') as f: config = json.load(f) - self.config_parser(config) + self.config_parser_json(config) else: if sphinx: self.activate_checker(SphinxChecker(self.verbose)) @@ -154,7 +154,7 @@ def toggle_printout(self, printout): ''' self.printout = printout - def config_parser(self, config): + def config_parser_json(self, config): ''' Parsing configuration dict extracted by previously opened json file Args: diff --git a/tests/test_config.py b/tests/test_config.py index 45beac60..209d138b 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -25,7 +25,7 @@ def test_partial_sphinx_config_parsing(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') self.assertEqual(warnings.return_count(), 0) with open('tests/junit_single_fail.xml', 'r') as xmlfile: @@ -44,7 +44,7 @@ def test_partial_doxygen_config_parsing(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) with open('tests/junit_single_fail.xml', 'r') as xmlfile: warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 0) @@ -63,7 +63,7 @@ def test_partial_junit_config_parsing(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 0) warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') @@ -87,7 +87,7 @@ def test_doxy_junit_options_config_parsing(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) warnings.check("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") self.assertEqual(warnings.return_count(), 0) warnings.check('testfile.c:6: warning: group test: ignoring title "Some test functions" that does not match old title "Some freaky test functions"') @@ -111,7 +111,7 @@ def test_sphinx_doxy_config_parsing(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) with open('tests/junit_single_fail.xml', 'r') as xmlfile: warnings.check(xmlfile.read()) self.assertEqual(warnings.return_count(), 0) @@ -137,7 +137,7 @@ def test_sphinx_config_max(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(SphinxChecker().name).get_maximum(), 5) def test_doxygen_config_max(self): @@ -150,7 +150,7 @@ def test_doxygen_config_max(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(DoxyChecker().name).get_maximum(), 5) def test_junit_config_max(self): @@ -163,7 +163,7 @@ def test_junit_config_max(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_maximum(), 5) def test_all_config_max(self): @@ -186,7 +186,7 @@ def test_all_config_max(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(SphinxChecker().name).get_maximum(), 4) self.assertEqual(warnings.get_checker(DoxyChecker().name).get_maximum(), 5) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_maximum(), 6) @@ -201,7 +201,7 @@ def test_sphinx_config_min(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(SphinxChecker().name).get_minimum(), 5) def test_doxygen_config_min(self): @@ -214,7 +214,7 @@ def test_doxygen_config_min(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(DoxyChecker().name).get_minimum(), 5) def test_junit_config_min(self): @@ -227,7 +227,7 @@ def test_junit_config_min(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_minimum(), 5) def test_all_config_min(self): @@ -250,7 +250,7 @@ def test_all_config_min(self): } } - warnings.config_parser(tmpjson) + warnings.config_parser_json(tmpjson) self.assertEqual(warnings.get_checker(SphinxChecker().name).get_minimum(), 4) self.assertEqual(warnings.get_checker(DoxyChecker().name).get_minimum(), 3) self.assertEqual(warnings.get_checker(JUnitChecker().name).get_minimum(), 5)