From ba61ed8efde5290bdd9d7eae76164c26d8b26920 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 15:59:55 +0100 Subject: [PATCH 01/18] Add --code-quality feature --- src/mlx/regex_checker.py | 63 +++++++++++++++++++++++++++++++++---- src/mlx/warnings.py | 26 +++++++++++++-- src/mlx/warnings_checker.py | 12 ++++++- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 490914d2..5f79772d 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -1,23 +1,34 @@ +import hashlib import re from mlx.warnings_checker import WarningsChecker -DOXYGEN_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):\s*([Ww]arning|[Ee]rror)|<.+>:-?\d+(?::\s*([Ww]arning|[Ee]rror))?): ((?!notes).+(?:(?!\s*(?:[Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*(\b[Nn]otice|\b[Ww]arning|\b[Ee]rror): (?!notes)(.+)\n?" +DOXYGEN_WARNING_REGEX = r"(?:(?P(?:[/.]|[A-Za-z]).+?):(?P-?\d+):\s*(?P[Ww]arning|[Ee]rror)|<.+>:(?P-?\d+)(?::\s*(?P[Ww]arning|[Ee]rror))?): (?P.+(?:(?!\s*([Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*(?P[Nn]otice|[Ww]arning|[Ee]rror): (?!notes)(?P.+)\n?" doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX) -SPHINX_WARNING_REGEX = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|CRITICAL):\s*(.+)$" -sphinx_pattern = re.compile(SPHINX_WARNING_REGEX) +SPHINX_WARNING_REGEX = r"(?m)^(?:((?P.+?):(?P\d+|None)?):?\s*)?(?PDEBUG|INFO|WARNING|ERROR|SEVERE|CRITICAL):\s*(?P.+)$" +ssphinx_pattern = re.compile(SPHINX_WARNING_REGEX) -PYTHON_XMLRUNNER_REGEX = r"(\s*(ERROR|FAILED) (\[\d+.\d\d\ds\]: \s*(.+)))\n?" +PYTHON_XMLRUNNER_REGEX = r"(\s*(?PERROR|FAILED) (\[\d+\.\d{3}s\]: \s*(?P.+)))\n?" xmlrunner_pattern = re.compile(PYTHON_XMLRUNNER_REGEX) -COVERITY_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):) (CID) \d+ \(#(?P\d+) of (?P\d+)\): (?P.+)\): (?P\w+), *(.+)\n?" +COVERITY_WARNING_REGEX = r"(?:((?:[/.]|[A-Za-z]).+?):(-?\d+):) (CID) \d+ \(#(?P\d+) of (?P\d+)\): (?P.+\)): (?P\w+), *(.+)\n?" coverity_pattern = re.compile(COVERITY_WARNING_REGEX) class RegexChecker(WarningsChecker): name = 'regex' pattern = None + SEVERITY_MAP = { + 'debug': 'info', + 'info': 'info', + 'notice': 'info', + 'warning': 'major', + 'error': 'critical', + 'severe': 'critical', + 'critical': 'critical', + 'failed': 'critical', + } def check(self, content): ''' Function for counting the number of warnings in a specific text @@ -33,6 +44,46 @@ def check(self, content): self.count += 1 self.counted_warnings.append(match_string) self.print_when_verbose(match_string) + if self.cq_enabled: + self.add_code_quality_finding(match) + + def add_code_quality_finding(self, match): + finding = { + "severity": "minor", + "location": { + "path": self.cq_default_path, + "lines": { + "begin": 1, + } + } + } + groups = {name: result for name, result in match.groupdict().items() if result} + print(groups) + for name, result in groups.items(): + if name.startswith("description"): + finding["description"] = result + break + else: + return # no description was found, which is the bare minimum + for name, result in groups.items(): + if name.startswith("severity"): + finding["severity"] = self.SEVERITY_MAP[result.lower()] + break + for name, result in groups.items(): + if name.startswith("path"): + finding["location"]["path"] = result + break + for name, result in groups.items(): + if name.startswith("line"): + print(result) + try: + lineno = int(result, 0) + except TypeError: + lineno = 1 + finding["location"]["lines"]["begin"] = lineno + break + finding["fingerprint"] = hashlib.md5(str(finding).encode('utf8')).hexdigest() + self.cq_findings.append(finding) class CoverityChecker(RegexChecker): @@ -66,7 +117,7 @@ class DoxyChecker(RegexChecker): class SphinxChecker(RegexChecker): name = 'sphinx' pattern = sphinx_pattern - sphinx_deprecation_regex = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$" + sphinx_deprecation_regex = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$" #r"(?m)^(?:(?P.+?:(?P\d+|None)?):?\s*)?(?PDEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(?P.+)$" sphinx_deprecation_regex_in_match = "RemovedInSphinx\\d+Warning" def include_sphinx_deprecation(self): diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index e62c761e..abe97147 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -21,13 +21,14 @@ class WarningsPlugin: - def __init__(self, verbose=False, config_file=None): + def __init__(self, verbose=False, config_file=None, cq_enabled=False): ''' Function for initializing the parsers Args: verbose (bool): optional - enable verbose logging config_file (Path): optional - configuration file with setup + cq_enabled (bool): optional - enable generation of Code Quality report ''' self.activated_checkers = {} self.verbose = verbose @@ -47,6 +48,7 @@ def __init__(self, verbose=False, config_file=None): self.warn_max = 0 self.count = 0 self.printout = False + self.cq_enabled = cq_enabled def activate_checker(self, checker): ''' @@ -56,6 +58,7 @@ def activate_checker(self, checker): checker (WarningsChecker): checker object ''' checker.reset() + checker.cq_enabled = self.cq_enabled and checker.name in ('doxygen', 'sphinx', 'xmlrunner') self.activated_checkers[checker.name] = checker def activate_checker_name(self, name): @@ -201,6 +204,18 @@ def write_counted_warnings(self, out_file): for checker in self.activated_checkers.values(): open_file.write("\n".join(checker.counted_warnings) + "\n") + def write_code_quality_report(self, out_file): + ''' Generates the Code Quality report artifact as a JSON file that implements a subset of the Code Climate spec + + Args: + out_file (str): Location for the output file + ''' + results = [] + for checker in self.activated_checkers.values(): + results.extend(checker.cq_findings) + with open(out_file, 'w', encoding='utf-8', newline='\n') as open_file: + json.dump(results, open_file, indent=4, sort_keys=False) + def warnings_wrapper(args): parser = argparse.ArgumentParser(prog='mlx-warnings') @@ -226,6 +241,8 @@ def warnings_wrapper(args): help="Sphinx checker will include warnings matching (RemovedInSphinx\\d+Warning) regex") parser.add_argument('-o', '--output', help='Output file that contains all counted warnings') + parser.add_argument('-C', '--code-quality', + help='Output Code Quality report artifact for GitLab CI') 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') @@ -237,6 +254,7 @@ def warnings_wrapper(args): help='Possible not-used flags from above are considered as command flags') args = parser.parse_args(args) + code_quality_enabled = bool(args.code_quality) # Read config file if args.configfile is not None: @@ -245,9 +263,9 @@ def warnings_wrapper(args): if checker_flags or warning_args: print("Configfile cannot be provided with other arguments") sys.exit(2) - warnings = WarningsPlugin(verbose=args.verbose, config_file=args.configfile) + warnings = WarningsPlugin(verbose=args.verbose, config_file=args.configfile, cq_enabled=code_quality_enabled) else: - warnings = WarningsPlugin(verbose=args.verbose) + warnings = WarningsPlugin(verbose=args.verbose, cq_enabled=code_quality_enabled) if args.sphinx: warnings.activate_checker_name('sphinx') if args.doxygen: @@ -294,6 +312,8 @@ def warnings_wrapper(args): warnings.return_count() if args.output: warnings.write_counted_warnings(args.output) + if args.code_quality: + warnings.write_code_quality_report(args.code_quality) return warnings.return_check_limits() diff --git a/src/mlx/warnings_checker.py b/src/mlx/warnings_checker.py index 071de290..d4da00a0 100644 --- a/src/mlx/warnings_checker.py +++ b/src/mlx/warnings_checker.py @@ -13,7 +13,13 @@ def __init__(self, verbose=False): verbose (bool): Enable/disable verbose logging ''' self.verbose = verbose - self.reset() + self.count = 0 + self.warn_min = 0 + self.warn_max = 0 + self._counted_warnings = [] + self.cq_findings = [] + self.cq_enabled = False + self.cq_default_path = '.gitlab-ci.yml' self.exclude_patterns = [] self.include_patterns = [] @@ -23,6 +29,8 @@ def reset(self): self.warn_min = 0 self.warn_max = 0 self._counted_warnings = [] + self.cq_findings = [] + self.cq_enabled = False @property def counted_warnings(self): @@ -153,6 +161,8 @@ def parse_config(self, config): self.set_maximum(int(config['max'])) self.set_minimum(int(config['min'])) self.add_patterns(config.get("exclude"), self.exclude_patterns) + if 'cq_default_path' in config: + self.cq_default_path = config['cq_default_path'] def _is_excluded(self, content): ''' Checks if the specific text must be excluded based on the configured regexes for exclusion and inclusion. From 3c51b54ccfa02f6091ea25a26dd0b0cd016dc64b Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 16:07:29 +0100 Subject: [PATCH 02/18] Revert removal of '\b' but simplify --- src/mlx/regex_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 5f79772d..6ce0c7c2 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -3,7 +3,7 @@ from mlx.warnings_checker import WarningsChecker -DOXYGEN_WARNING_REGEX = r"(?:(?P(?:[/.]|[A-Za-z]).+?):(?P-?\d+):\s*(?P[Ww]arning|[Ee]rror)|<.+>:(?P-?\d+)(?::\s*(?P[Ww]arning|[Ee]rror))?): (?P.+(?:(?!\s*([Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*(?P[Nn]otice|[Ww]arning|[Ee]rror): (?!notes)(?P.+)\n?" +DOXYGEN_WARNING_REGEX = r"(?:(?P(?:[/.]|[A-Za-z]).+?):(?P-?\d+):\s*(?P[Ww]arning|[Ee]rror)|<.+>:(?P-?\d+)(?::\s*(?P[Ww]arning|[Ee]rror))?): (?P.+(?:(?!\s*([Nn]otice|[Ww]arning|[Ee]rror): )[^/<\n][^:\n][^/\n].+)*)|\s*\b(?P[Nn]otice|[Ww]arning|[Ee]rror): (?!notes)(?P.+)\n?" doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX) SPHINX_WARNING_REGEX = r"(?m)^(?:((?P.+?):(?P\d+|None)?):?\s*)?(?PDEBUG|INFO|WARNING|ERROR|SEVERE|CRITICAL):\s*(?P.+)$" From ce266e5d95729f27eed0f0e7ed1a2c090112f853 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 16:08:06 +0100 Subject: [PATCH 03/18] Fix typo --- src/mlx/regex_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 6ce0c7c2..85ecda20 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -7,7 +7,7 @@ doxy_pattern = re.compile(DOXYGEN_WARNING_REGEX) SPHINX_WARNING_REGEX = r"(?m)^(?:((?P.+?):(?P\d+|None)?):?\s*)?(?PDEBUG|INFO|WARNING|ERROR|SEVERE|CRITICAL):\s*(?P.+)$" -ssphinx_pattern = re.compile(SPHINX_WARNING_REGEX) +sphinx_pattern = re.compile(SPHINX_WARNING_REGEX) PYTHON_XMLRUNNER_REGEX = r"(\s*(?PERROR|FAILED) (\[\d+\.\d{3}s\]: \s*(?P.+)))\n?" xmlrunner_pattern = re.compile(PYTHON_XMLRUNNER_REGEX) From dc47e69116f32e7d93507c4515d1debaa20eeead Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 16:40:49 +0100 Subject: [PATCH 04/18] Fix AttributeError when using config file --- src/mlx/warnings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index abe97147..3458be8c 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -32,6 +32,7 @@ def __init__(self, verbose=False, config_file=None, cq_enabled=False): ''' self.activated_checkers = {} self.verbose = verbose + self.cq_enabled = cq_enabled self.public_checkers = [SphinxChecker(self.verbose), DoxyChecker(self.verbose), JUnitChecker(self.verbose), XMLRunnerChecker(self.verbose), CoverityChecker(self.verbose), RobotChecker(self.verbose)] @@ -48,7 +49,6 @@ def __init__(self, verbose=False, config_file=None, cq_enabled=False): self.warn_max = 0 self.count = 0 self.printout = False - self.cq_enabled = cq_enabled def activate_checker(self, checker): ''' From 0a87cd03166a2c5f63bbeea78ca5605ef6e32656 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 18:08:40 +0100 Subject: [PATCH 05/18] Determine relative path when warning contains absolute path --- src/mlx/regex_checker.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 85ecda20..7abd3602 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -1,5 +1,6 @@ import hashlib import re +from pathlib import Path from mlx.warnings_checker import WarningsChecker @@ -71,7 +72,10 @@ def add_code_quality_finding(self, match): break for name, result in groups.items(): if name.startswith("path"): - finding["location"]["path"] = result + path = Path(result) + if path.is_absolute(): + path = path.relative_to(Path.cwd()) + finding["location"]["path"] = str(path) break for name, result in groups.items(): if name.startswith("line"): From df579cc0482e1a27509e682caac3a67dac6f89b8 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 18:10:35 +0100 Subject: [PATCH 06/18] Remove debug prints --- src/mlx/regex_checker.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 7abd3602..576ef5d7 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -59,7 +59,6 @@ def add_code_quality_finding(self, match): } } groups = {name: result for name, result in match.groupdict().items() if result} - print(groups) for name, result in groups.items(): if name.startswith("description"): finding["description"] = result @@ -79,7 +78,6 @@ def add_code_quality_finding(self, match): break for name, result in groups.items(): if name.startswith("line"): - print(result) try: lineno = int(result, 0) except TypeError: From b1866388be98f605d613b057af6898a2d4bfe578 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 18:10:52 +0100 Subject: [PATCH 07/18] Change default severity level from minor to major --- src/mlx/regex_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 576ef5d7..d6f6623c 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -50,7 +50,7 @@ def check(self, content): def add_code_quality_finding(self, match): finding = { - "severity": "minor", + "severity": "major", "location": { "path": self.cq_default_path, "lines": { From e8da655368e62f4efd4ab5c74f03fccf6a13df11 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 18:15:44 +0100 Subject: [PATCH 08/18] Remove useless comment --- src/mlx/regex_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index d6f6623c..2cac341f 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -119,7 +119,7 @@ class DoxyChecker(RegexChecker): class SphinxChecker(RegexChecker): name = 'sphinx' pattern = sphinx_pattern - sphinx_deprecation_regex = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$" #r"(?m)^(?:(?P.+?:(?P\d+|None)?):?\s*)?(?PDEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(?P.+)$" + sphinx_deprecation_regex = r"(?m)^(?:(.+?:(?:\d+|None)?):?\s*)?(DEBUG|INFO|WARNING|ERROR|SEVERE|(?:\w+Sphinx\d+Warning)):\s*(.+)$" sphinx_deprecation_regex_in_match = "RemovedInSphinx\\d+Warning" def include_sphinx_deprecation(self): From ae71169fd08f4435d1d48438acf9b287f1bae9a8 Mon Sep 17 00:00:00 2001 From: jce Date: Fri, 17 Feb 2023 18:22:50 +0100 Subject: [PATCH 09/18] Remove useless reset function --- src/mlx/warnings.py | 1 - src/mlx/warnings_checker.py | 9 --------- 2 files changed, 10 deletions(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index 3458be8c..677b5594 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -57,7 +57,6 @@ def activate_checker(self, checker): Args: checker (WarningsChecker): checker object ''' - checker.reset() checker.cq_enabled = self.cq_enabled and checker.name in ('doxygen', 'sphinx', 'xmlrunner') self.activated_checkers[checker.name] = checker diff --git a/src/mlx/warnings_checker.py b/src/mlx/warnings_checker.py index d4da00a0..ce84a01a 100644 --- a/src/mlx/warnings_checker.py +++ b/src/mlx/warnings_checker.py @@ -23,15 +23,6 @@ def __init__(self, verbose=False): self.exclude_patterns = [] self.include_patterns = [] - def reset(self): - ''' Reset function (resets min, max and counter values) ''' - self.count = 0 - self.warn_min = 0 - self.warn_max = 0 - self._counted_warnings = [] - self.cq_findings = [] - self.cq_enabled = False - @property def counted_warnings(self): ''' List: list of counted warnings (str) ''' From a70558a5e340b609522eeed10774ea50aaf60479 Mon Sep 17 00:00:00 2001 From: Jasper Craeghs Date: Mon, 20 Feb 2023 15:22:59 +0100 Subject: [PATCH 10/18] Document --code-quality feature --- README.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.rst b/README.rst index 8bc8f1ab..cf5a3f76 100644 --- a/README.rst +++ b/README.rst @@ -289,6 +289,7 @@ input file. When this setting is missing, the default value ``true`` is used. .. _`Robot Framework`: https://robotframework.org/ .. _`--xunit report.xml`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#xunit-compatible-result-file +.. _configuration file: ---------------------------------- Configuration File to Pass Options @@ -302,11 +303,13 @@ Configuration file is in JSON or YAML_ format with a simple structure. { "sphinx": { "enabled": false, + "cq_default_path": "doc/source/conf.py", "min": 0, "max": 0 }, "doxygen": { "enabled": false, + "cq_default_path": "doc/doxygen/Doxyfile", "min": 0, "max": 0 }, @@ -414,6 +417,15 @@ Example entries: JUnit/RobotFramework: test_warn_plugin_double_fail.myfirstfai1ure: Is our warnings plugin able to trace this random failure msg? +Code Quality report + +Use `-C, --code-quality` to let the plugin generate `a Code Quality report`_ for GitLab CI. All counted +Sphinx, Doxygen and XMLRunner will be included. Other checker types are not supported by this feature. The report is +a JSON file that implements `a subset of the Code Climate spec`_. Declare this file `as an artifact`_ of the +`code_quality` CI job. +If a warning doesn't contain a path, `"cq_default_path"` from the :ref:`configuration file` will be used. +If not configured, `.gitlab-ci.yml` will be used as a fallback path. + ======================= Issues and New Features ======================= @@ -431,3 +443,6 @@ There is a Contribution guide available if you would like to get involved in development of the plugin. We encourage anyone to contribute to our repository. .. _YAML: https://yaml.org/spec/1.2.2/ +.. _a Code Quality report: https://docs.gitlab.com/ee/ci/testing/code_quality.html +.. _a subset of the Code Climate spec: https://docs.gitlab.com/ee/ci/testing/code_quality.html#implement-a-custom-tool +.. _as an artifact: https://docs.gitlab.com/ee/ci/testing/code_quality.html#download-output-in-json-format From e8658688f245a8300420fc181fa2837085636847 Mon Sep 17 00:00:00 2001 From: Jasper Craeghs Date: Mon, 20 Feb 2023 16:22:23 +0100 Subject: [PATCH 11/18] Also catch ValueError in case lineno is 'None' --- src/mlx/regex_checker.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mlx/regex_checker.py b/src/mlx/regex_checker.py index 2cac341f..ebf8761f 100644 --- a/src/mlx/regex_checker.py +++ b/src/mlx/regex_checker.py @@ -80,7 +80,7 @@ def add_code_quality_finding(self, match): if name.startswith("line"): try: lineno = int(result, 0) - except TypeError: + except (TypeError, ValueError): lineno = 1 finding["location"]["lines"]["begin"] = lineno break From 6312f42c344aaa66d9406ba56cafd25b0b46004d Mon Sep 17 00:00:00 2001 From: Jasper Craeghs Date: Mon, 20 Feb 2023 16:23:25 +0100 Subject: [PATCH 12/18] Enable more checkers; configure cq_default_path --- tests/test_config.py | 10 +++++----- tests/test_in/config_example.json | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/tests/test_config.py b/tests/test_config.py index a7ee5ff9..9b8c7acc 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -16,15 +16,15 @@ class TestConfig(TestCase): def test_configfile_parsing(self): warnings = WarningsPlugin(config_file=(TEST_IN_DIR / "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) + self.assertEqual(warnings.return_count(), 1) 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("/home/bljah/test/index.rst:5: WARNING: toctree contains reference to nonexisting document u'installation'") + self.assertEqual(warnings.return_count(), 2) warnings.check('This should not be treated as warning2') - self.assertEqual(warnings.return_count(), 1) + self.assertEqual(warnings.return_count(), 2) warnings.check('ERROR [0.000s]: test_some_error_test (something.anything.somewhere)') - self.assertEqual(warnings.return_count(), 1) + self.assertEqual(warnings.return_count(), 3) def _helper_exclude(self, warnings): with patch('sys.stdout', new=StringIO()) as verbose_output: diff --git a/tests/test_in/config_example.json b/tests/test_in/config_example.json index 8f467311..768d5cb0 100644 --- a/tests/test_in/config_example.json +++ b/tests/test_in/config_example.json @@ -1,11 +1,13 @@ { "sphinx": { "enabled": true, + "cq_default_path": "doc/source/conf.py", "min": 0, "max": 0 }, "doxygen": { - "enabled": false, + "enabled": true, + "cq_default_path": "doc/doxygen/Doxyfile", "min": 0, "max": 0 }, @@ -15,12 +17,12 @@ "max": 0 }, "xmlrunner": { - "enabled": false, + "enabled": true, "min": 0, "max": 0 }, "coverity": { - "enabled": false, + "enabled": true, "min": 0, "max": 0 }, From ac9780d678c994172838d2bb3a79b1ab860e27ff Mon Sep 17 00:00:00 2001 From: Jasper Craeghs Date: Mon, 20 Feb 2023 17:02:06 +0100 Subject: [PATCH 13/18] Add trailing newline to JSON file --- src/mlx/warnings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mlx/warnings.py b/src/mlx/warnings.py index 677b5594..20edd14e 100644 --- a/src/mlx/warnings.py +++ b/src/mlx/warnings.py @@ -212,8 +212,9 @@ def write_code_quality_report(self, out_file): results = [] for checker in self.activated_checkers.values(): results.extend(checker.cq_findings) + content = json.dumps(results, indent=4, sort_keys=False) with open(out_file, 'w', encoding='utf-8', newline='\n') as open_file: - json.dump(results, open_file, indent=4, sort_keys=False) + open_file.write(f"{content}\n") def warnings_wrapper(args): From dcfff2e5b68fcbdd328e3980b7112add1875b8ca Mon Sep 17 00:00:00 2001 From: Jasper Craeghs Date: Mon, 20 Feb 2023 17:16:46 +0100 Subject: [PATCH 14/18] Test code quality report --- tests/test_in/code_quality.json | 79 ++++++++++++++++++++++++++++++++ tests/test_in/mixed_warnings.txt | 19 ++++++++ tests/test_integration.py | 12 +++++ 3 files changed, 110 insertions(+) create mode 100644 tests/test_in/code_quality.json create mode 100644 tests/test_in/mixed_warnings.txt diff --git a/tests/test_in/code_quality.json b/tests/test_in/code_quality.json new file mode 100644 index 00000000..cd223261 --- /dev/null +++ b/tests/test_in/code_quality.json @@ -0,0 +1,79 @@ +[ + { + "severity": "major", + "location": { + "path": "git\\test\\index.rst", + "lines": { + "begin": 1 + } + }, + "description": "toctree contains reference to nonexisting document u'installation'", + "fingerprint": "2120c76e2a54a3b2212733c15ba62db3" + }, + { + "severity": "major", + "location": { + "path": "doc/source/conf.py", + "lines": { + "begin": 1 + } + }, + "description": "List item 'CL-UNDEFINED_CL_ITEM' in merge/pull request 138 is not defined as a checklist-item.", + "fingerprint": "b4a2d90bef6fb0a6cad87463d5830080" + }, + { + "severity": "info", + "location": { + "path": "doc/doxygen/Doxyfile", + "lines": { + "begin": 1 + } + }, + "description": "Output directory `doc/doxygen/framework' does not exist. I have created it for you.", + "fingerprint": "256cb9d4b9b3c05ba755e5da30b2afb2" + }, + { + "severity": "critical", + "location": { + "path": "\\home\\user\\myproject\\helper\\SimpleTimer.h", + "lines": { + "begin": 19 + } + }, + "description": "Unexpected character `\"'", + "fingerprint": "7c47a45e109d17f63562af16f258d242" + }, + { + "severity": "major", + "location": { + "path": "doc/doxygen/Doxyfile", + "lines": { + "begin": 1 + } + }, + "description": "The following parameters of sofa::component::odesolver::EulerKaapiSolver::v_peq(VecId v, VecId a, double f) are not documented:", + "fingerprint": "9c91370059e40eb07289790f2b9d6a75" + }, + { + "severity": "critical", + "location": { + "path": "doc/doxygen/Doxyfile", + "lines": { + "begin": 1 + } + }, + "description": "Could not read image `/home/user/myproject/html/struct_foo_graph.png' generated by dot!", + "fingerprint": "bdd0d0b7cc25b1f4414f630c2351bf3c" + }, + { + "severity": "critical", + "location": { + "path": ".gitlab-ci.yml", + "lines": { + "begin": 1 + } + }, + "description": "test_some_error_test (something.anything.somewhere)'", + "fingerprint": "cd09ae46ee5361570fd59de78b454e11" + } +] diff --git a/tests/test_in/mixed_warnings.txt b/tests/test_in/mixed_warnings.txt new file mode 100644 index 00000000..4a25ccb6 --- /dev/null +++ b/tests/test_in/mixed_warnings.txt @@ -0,0 +1,19 @@ +# Doxygen +Notice: Output directory `doc/doxygen/framework' does not exist. I have created it for you. +/home/user/myproject/helper/SimpleTimer.h:19: Error: Unexpected character `"' +:1: Warning: The following parameters of sofa::component::odesolver::EulerKaapiSolver::v_peq(VecId v, VecId a, double f) are not documented: + parameter 'v' + parameter 'a' +error: Could not read image `/home/user/myproject/html/struct_foo_graph.png' generated by dot! + +# Sphinx +/usr/local/lib/python3.7/dist-packages/sphinx_rtd_theme/search.html:20: RemovedInSphinx30Warning: To modify script_files in the theme is deprecated. Please insert a