From 0819a14b2e4c0a38db42660356d763e74b6f719a Mon Sep 17 00:00:00 2001 From: Dirk Moors Date: Wed, 9 Nov 2016 22:01:11 +0100 Subject: [PATCH] Implemented output to JUnit XML file --- src/flake8/checker.py | 71 +++++++++++++++++++++++++++++++++- src/flake8/main/application.py | 4 ++ src/flake8/main/options.py | 7 ++++ 3 files changed, 81 insertions(+), 1 deletion(-) diff --git a/src/flake8/checker.py b/src/flake8/checker.py index b4e22b2..4273741 100644 --- a/src/flake8/checker.py +++ b/src/flake8/checker.py @@ -57,7 +57,7 @@ class Manager(object): together and make our output deterministic. """ - def __init__(self, style_guide, arguments, checker_plugins): + def __init__(self, style_guide, arguments, checker_plugins, junit_xml_report): """Initialize our Manager instance. :param style_guide: @@ -70,11 +70,14 @@ class Manager(object): The plugins representing checks parsed from entry-points. :type checker_plugins: flake8.plugins.manager.Checkers + :param str junit_xml_report: + Filename to write a JUnit XML report to """ self.arguments = arguments self.style_guide = style_guide self.options = style_guide.options self.checks = checker_plugins + self.junit_xml_report = junit_xml_report self.jobs = self._job_count() self.process_queue = None self.results_queue = None @@ -292,13 +295,79 @@ class Manager(object): tuple(int, int) """ results_reported = results_found = 0 + all_results = {} for checker in self.checkers: + if checker.filename not in all_results: + all_results[checker.filename] = {'checker': checker} results = sorted(checker.results, key=lambda tup: (tup[1], tup[2])) + all_results[checker.filename]['errors'] = results results_reported += self._handle_results(checker.display_name, results) results_found += len(results) + + # Output JUnit XML + self.report_junit_xml(all_results) + return (results_found, results_reported) + def report_junit_xml(self, all_results): + """ + Report all results to a JUnit XML file + + :param dict all_results: A dictionary containing all results + """ + # If no junit_xml_report file was provided, just skip this + if not self.junit_xml_report: + return + + testcase_success_template = '' + testcase_failure_template = '' \ + '{long_message}' + testsuite_template = '' \ + '\n{testcase_nodes}\n' + + testcase_nodes = [] + num_failures = 0 + num_files = len(all_results) + for filename in sorted(all_results.keys()): + errors = all_results[filename]['errors'] + if errors: + num_failures += 1 + for error_code, line_number, column, text, physical_line in errors: + testcase_nodes.append(testcase_failure_template.format(**{ + 'classname': '', # n.a. + 'filename': filename, + 'line_number': line_number, + 'name': '%s: %s' % (filename, error_code), + 'time_elapsed': '0.1', # Not yet measured? + 'short_message': text, + 'long_message': 'lineno: %d, column: %d, code: %s, error: %s\n>>%s' % ( + line_number, column, error_code, text, physical_line) + })) + else: + testcase_nodes.append(testcase_success_template.format(**{ + 'classname': '', # n.a. + 'filename': filename, + 'line_number': 1, + 'name': filename, + 'time_elapsed': '0.1', # Not yet measured? + })) + + ouput = testsuite_template.format(**{ + 'num_errors': 0, + 'num_failures': num_failures, + 'num_skips': 0, + 'num_tests': num_files, + 'time_elapsed': 1, + 'testcase_nodes': '\n'.join(testcase_nodes) + }) + + with open(self.junit_xml_report, 'w') as junit_xml_file: + junit_xml_file.write(ouput) + def run_parallel(self): """Run the checkers in parallel.""" LOG.info('Starting %d process workers', self.jobs) diff --git a/src/flake8/main/application.py b/src/flake8/main/application.py index 88db0a8..9476b82 100644 --- a/src/flake8/main/application.py +++ b/src/flake8/main/application.py @@ -108,6 +108,9 @@ class Application(object): #: The parsed diff information self.parsed_diff = {} + #: JUnit XML + self.junit_xml_report = preliminary_opts.output_xmlfile + def exit(self): # type: () -> NoneType """Handle finalization and exiting the program. @@ -221,6 +224,7 @@ class Application(object): style_guide=self.guide, arguments=self.args, checker_plugins=self.check_plugins, + junit_xml_report=self.junit_xml_report ) def run_checks(self, files=None): diff --git a/src/flake8/main/options.py b/src/flake8/main/options.py index 7e9b79e..84ac6f5 100644 --- a/src/flake8/main/options.py +++ b/src/flake8/main/options.py @@ -27,6 +27,7 @@ def register_default_options(option_manager): - ``--exit-zero`` - ``-j``/``--jobs`` - ``--output-file`` + - ``--output-xmlfile`` - ``--tee`` - ``--append-config`` - ``--config`` @@ -175,6 +176,12 @@ def register_default_options(option_manager): help='Redirect report to a file.', ) + add_option( + '--output-xmlfile', default=None, type='string', parse_from_config=True, + # callback=callbacks.redirect_stdout, + help='Create JUnit XML report.', + ) + add_option( '--tee', default=False, parse_from_config=True, action='store_true', help='Write to stdout and output-file.',