From 2ad5d74dc76d51488138835d879f9944d294e766 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20SEBAL?= Date: Wed, 19 Jan 2022 18:50:54 +0100 Subject: [PATCH] Fixes and features on the JUnit Python conversion script * Added python3 shebang * Renamed the script `unity_to_junit.py` as `stylize_as_junit.py` to match the Ruby file * Fixed a bug on where the script failed if the first entry slot of each result line is empty. Now falls back to the result file name * Rewrote the argument parsing to use argparse * Added a `--output` / `-o` option, to match the Ruby file --- ...{unity_to_junit.py => stylize_as_junit.py} | 89 +++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) rename auto/{unity_to_junit.py => stylize_as_junit.py} (60%) diff --git a/auto/unity_to_junit.py b/auto/stylize_as_junit.py similarity index 60% rename from auto/unity_to_junit.py rename to auto/stylize_as_junit.py index 71dd568..06c8659 100644 --- a/auto/unity_to_junit.py +++ b/auto/stylize_as_junit.py @@ -1,6 +1,15 @@ +#! python3 +# ========================================== +# Fork from Unity Project - A Test Framework for C +# Pull request on Gerrit in progress, the objective of this file is to be deleted when official Unity deliveries +# include that modification +# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de +# [Released under MIT License. Please refer to license.txt for details] +# ========================================== import sys import os from glob import glob +import argparse from pyparsing import * from junit_xml import TestSuite, TestCase @@ -14,6 +23,7 @@ class UnityTestSummary: self.ignored = 0 self.targets = 0 self.root = None + self.output = None self.test_suites = dict() def run(self): @@ -37,15 +47,18 @@ class UnityTestSummary: entry = entry_one | entry_two delimiter = Literal(':').suppress() - tc_result_line = Group(entry.setResultsName('tc_file_name') + delimiter + entry.setResultsName( - 'tc_line_nr') + delimiter + entry.setResultsName('tc_name') + delimiter + entry.setResultsName( - 'tc_status') + Optional( - delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") + # Format of a result line is `[file_name]:line:test_name:RESULT[:msg]` + tc_result_line = Group(ZeroOrMore(entry.setResultsName('tc_file_name')) + + delimiter + entry.setResultsName('tc_line_nr') + + delimiter + entry.setResultsName('tc_name') + + delimiter + entry.setResultsName('tc_status') + + Optional(delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") eol = LineEnd().suppress() sol = LineStart().suppress() blank_line = sol + eol + # Format of the summary line is `# Tests # Failures # Ignored` tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName( "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName( "tc_summary") @@ -67,7 +80,10 @@ class UnityTestSummary: tmp_tc_line = r['tc_line'] # get only the file name which will be used as the classname - file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] + if 'tc_file_name' in tmp_tc_line: + file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] + else: + file_name = result_file.strip("./") tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name) if 'tc_status' in tmp_tc_line: if str(tmp_tc_line['tc_status']) == 'IGNORE': @@ -96,7 +112,7 @@ class UnityTestSummary: for suite_name in self.test_suites: ts.append(TestSuite(suite_name, self.test_suites[suite_name])) - with open('result.xml', 'w') as f: + with open(self.output, 'w') as f: TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8') return self.report @@ -107,40 +123,39 @@ class UnityTestSummary: def set_root_path(self, path): self.root = path - @staticmethod - def usage(err_msg=None): - print("\nERROR: ") - if err_msg: - print(err_msg) - print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") - print(" result_file_directory - The location of your results files.") - print(" Defaults to current directory if not specified.") - print(" Should end in / if specified.") - print(" root_path - Helpful for producing more verbose output if using relative paths.") - sys.exit(1) + def set_output(self, output): + self.output = output if __name__ == '__main__': uts = UnityTestSummary() - try: - # look in the specified or current directory for result files - if len(sys.argv) > 1: - targets_dir = sys.argv[1] - else: - targets_dir = './' - targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) - if len(targets) == 0: - raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) - uts.set_targets(targets) + parser = argparse.ArgumentParser(description= + """Takes as input the collection of *.testpass and *.testfail result + files, and converts them to a JUnit formatted XML.""") + parser.add_argument('targets_dir', metavar='result_file_directory', + type=str, nargs='?', default='./', + help="""The location of your results files. + Defaults to current directory if not specified.""") + parser.add_argument('root_path', nargs='?', + default='os.path.split(__file__)[0]', + help="""Helpful for producing more verbose output if + using relative paths.""") + parser.add_argument('--output', '-o', type=str, default="result.xml", + help="""The name of the JUnit-formatted file (XML).""") + args = parser.parse_args() - # set the root path - if len(sys.argv) > 2: - root_path = sys.argv[2] - else: - root_path = os.path.split(__file__)[0] - uts.set_root_path(root_path) + if args.targets_dir[-1] != '/': + args.targets_dir+='/' + targets = list(map(lambda x: x.replace('\\', '/'), glob(args.targets_dir + '*.test*'))) + if len(targets) == 0: + raise Exception("No *.testpass or *.testfail files found in '%s'" % args.targets_dir) + uts.set_targets(targets) - # run the summarizer - print(uts.run()) - except Exception as e: - UnityTestSummary.usage(e) + # set the root path + uts.set_root_path(args.root_path) + + # set output + uts.set_output(args.output) + + # run the summarizer + print(uts.run())