135375f98Sopenharmony_ci#! python3
235375f98Sopenharmony_ci# ==========================================
335375f98Sopenharmony_ci#   Fork from Unity Project - A Test Framework for C
435375f98Sopenharmony_ci#   Pull request on Gerrit in progress, the objective of this file is to be deleted when official Unity deliveries
535375f98Sopenharmony_ci#   include that modification
635375f98Sopenharmony_ci#   Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de
735375f98Sopenharmony_ci#   [Released under MIT License. Please refer to license.txt for details]
835375f98Sopenharmony_ci# ==========================================
935375f98Sopenharmony_ciimport sys
1035375f98Sopenharmony_ciimport os
1135375f98Sopenharmony_cifrom glob import glob
1235375f98Sopenharmony_ciimport argparse
1335375f98Sopenharmony_ci
1435375f98Sopenharmony_cifrom pyparsing import *
1535375f98Sopenharmony_cifrom junit_xml import TestSuite, TestCase
1635375f98Sopenharmony_ci
1735375f98Sopenharmony_ci
1835375f98Sopenharmony_ciclass UnityTestSummary:
1935375f98Sopenharmony_ci    def __init__(self):
2035375f98Sopenharmony_ci        self.report = ''
2135375f98Sopenharmony_ci        self.total_tests = 0
2235375f98Sopenharmony_ci        self.failures = 0
2335375f98Sopenharmony_ci        self.ignored = 0
2435375f98Sopenharmony_ci        self.targets = 0
2535375f98Sopenharmony_ci        self.root = None
2635375f98Sopenharmony_ci        self.output = None
2735375f98Sopenharmony_ci        self.test_suites = dict()
2835375f98Sopenharmony_ci
2935375f98Sopenharmony_ci    def run(self):
3035375f98Sopenharmony_ci        # Clean up result file names
3135375f98Sopenharmony_ci        results = []
3235375f98Sopenharmony_ci        for target in self.targets:
3335375f98Sopenharmony_ci            results.append(target.replace('\\', '/'))
3435375f98Sopenharmony_ci
3535375f98Sopenharmony_ci        # Dig through each result file, looking for details on pass/fail:
3635375f98Sopenharmony_ci        for result_file in results:
3735375f98Sopenharmony_ci            lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
3835375f98Sopenharmony_ci            if len(lines) == 0:
3935375f98Sopenharmony_ci                raise Exception("Empty test result file: %s" % result_file)
4035375f98Sopenharmony_ci
4135375f98Sopenharmony_ci            # define an expression for your file reference
4235375f98Sopenharmony_ci            entry_one = Combine(
4335375f98Sopenharmony_ci                oneOf(list(alphas)) + ':/' +
4435375f98Sopenharmony_ci                Word(alphanums + '_-./'))
4535375f98Sopenharmony_ci
4635375f98Sopenharmony_ci            entry_two = Word(printables + ' ', excludeChars=':')
4735375f98Sopenharmony_ci            entry = entry_one | entry_two
4835375f98Sopenharmony_ci
4935375f98Sopenharmony_ci            delimiter = Literal(':').suppress()
5035375f98Sopenharmony_ci            # Format of a result line is `[file_name]:line:test_name:RESULT[:msg]`
5135375f98Sopenharmony_ci            tc_result_line = Group(ZeroOrMore(entry.setResultsName('tc_file_name'))
5235375f98Sopenharmony_ci                + delimiter + entry.setResultsName('tc_line_nr')
5335375f98Sopenharmony_ci                + delimiter + entry.setResultsName('tc_name')
5435375f98Sopenharmony_ci                + delimiter + entry.setResultsName('tc_status') +
5535375f98Sopenharmony_ci                Optional(delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line")
5635375f98Sopenharmony_ci
5735375f98Sopenharmony_ci            eol = LineEnd().suppress()
5835375f98Sopenharmony_ci            sol = LineStart().suppress()
5935375f98Sopenharmony_ci            blank_line = sol + eol
6035375f98Sopenharmony_ci
6135375f98Sopenharmony_ci            # Format of the summary line is `# Tests # Failures # Ignored`
6235375f98Sopenharmony_ci            tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName(
6335375f98Sopenharmony_ci                "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName(
6435375f98Sopenharmony_ci                "tc_summary")
6535375f98Sopenharmony_ci            tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result")
6635375f98Sopenharmony_ci
6735375f98Sopenharmony_ci            # run it and see...
6835375f98Sopenharmony_ci            pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line)
6935375f98Sopenharmony_ci            pp1.ignore(blank_line | OneOrMore("-"))
7035375f98Sopenharmony_ci
7135375f98Sopenharmony_ci            result = list()
7235375f98Sopenharmony_ci            for l in lines:
7335375f98Sopenharmony_ci                result.append((pp1.parseString(l)).asDict())
7435375f98Sopenharmony_ci            # delete empty results
7535375f98Sopenharmony_ci            result = filter(None, result)
7635375f98Sopenharmony_ci
7735375f98Sopenharmony_ci            tc_list = list()
7835375f98Sopenharmony_ci            for r in result:
7935375f98Sopenharmony_ci                if 'tc_line' in r:
8035375f98Sopenharmony_ci                    tmp_tc_line = r['tc_line']
8135375f98Sopenharmony_ci
8235375f98Sopenharmony_ci                    # get only the file name which will be used as the classname
8335375f98Sopenharmony_ci                    if 'tc_file_name' in tmp_tc_line:
8435375f98Sopenharmony_ci                        file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0]
8535375f98Sopenharmony_ci                    else:
8635375f98Sopenharmony_ci                        file_name = result_file.strip("./")
8735375f98Sopenharmony_ci                    tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name)
8835375f98Sopenharmony_ci                    if 'tc_status' in tmp_tc_line:
8935375f98Sopenharmony_ci                        if str(tmp_tc_line['tc_status']) == 'IGNORE':
9035375f98Sopenharmony_ci                            if 'tc_msg' in tmp_tc_line:
9135375f98Sopenharmony_ci                                tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'],
9235375f98Sopenharmony_ci                                                        output=r'[File]={0}, [Line]={1}'.format(
9335375f98Sopenharmony_ci                                                            tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
9435375f98Sopenharmony_ci                            else:
9535375f98Sopenharmony_ci                                tmp_tc.add_skipped_info(message=" ")
9635375f98Sopenharmony_ci                        elif str(tmp_tc_line['tc_status']) == 'FAIL':
9735375f98Sopenharmony_ci                            if 'tc_msg' in tmp_tc_line:
9835375f98Sopenharmony_ci                                tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'],
9935375f98Sopenharmony_ci                                                        output=r'[File]={0}, [Line]={1}'.format(
10035375f98Sopenharmony_ci                                                            tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr']))
10135375f98Sopenharmony_ci                            else:
10235375f98Sopenharmony_ci                                tmp_tc.add_failure_info(message=" ")
10335375f98Sopenharmony_ci
10435375f98Sopenharmony_ci                    tc_list.append((str(result_file), tmp_tc))
10535375f98Sopenharmony_ci
10635375f98Sopenharmony_ci            for k, v in tc_list:
10735375f98Sopenharmony_ci                try:
10835375f98Sopenharmony_ci                    self.test_suites[k].append(v)
10935375f98Sopenharmony_ci                except KeyError:
11035375f98Sopenharmony_ci                    self.test_suites[k] = [v]
11135375f98Sopenharmony_ci        ts = []
11235375f98Sopenharmony_ci        for suite_name in self.test_suites:
11335375f98Sopenharmony_ci            ts.append(TestSuite(suite_name, self.test_suites[suite_name]))
11435375f98Sopenharmony_ci
11535375f98Sopenharmony_ci        with open(self.output, 'w') as f:
11635375f98Sopenharmony_ci            TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8')
11735375f98Sopenharmony_ci
11835375f98Sopenharmony_ci        return self.report
11935375f98Sopenharmony_ci
12035375f98Sopenharmony_ci    def set_targets(self, target_array):
12135375f98Sopenharmony_ci        self.targets = target_array
12235375f98Sopenharmony_ci
12335375f98Sopenharmony_ci    def set_root_path(self, path):
12435375f98Sopenharmony_ci        self.root = path
12535375f98Sopenharmony_ci
12635375f98Sopenharmony_ci    def set_output(self, output):
12735375f98Sopenharmony_ci        self.output = output
12835375f98Sopenharmony_ci
12935375f98Sopenharmony_ci
13035375f98Sopenharmony_ciif __name__ == '__main__':
13135375f98Sopenharmony_ci    uts = UnityTestSummary()
13235375f98Sopenharmony_ci    parser = argparse.ArgumentParser(description=
13335375f98Sopenharmony_ci        """Takes as input the collection of *.testpass and *.testfail result
13435375f98Sopenharmony_ci        files, and converts them to a JUnit formatted XML.""")
13535375f98Sopenharmony_ci    parser.add_argument('targets_dir', metavar='result_file_directory',
13635375f98Sopenharmony_ci                        type=str, nargs='?', default='./',
13735375f98Sopenharmony_ci                        help="""The location of your results files.
13835375f98Sopenharmony_ci                        Defaults to current directory if not specified.""")
13935375f98Sopenharmony_ci    parser.add_argument('root_path', nargs='?',
14035375f98Sopenharmony_ci                        default='os.path.split(__file__)[0]',
14135375f98Sopenharmony_ci                        help="""Helpful for producing more verbose output if
14235375f98Sopenharmony_ci                        using relative paths.""")
14335375f98Sopenharmony_ci    parser.add_argument('--output', '-o', type=str, default="result.xml",
14435375f98Sopenharmony_ci                        help="""The name of the JUnit-formatted file (XML).""")
14535375f98Sopenharmony_ci    args = parser.parse_args()
14635375f98Sopenharmony_ci
14735375f98Sopenharmony_ci    if args.targets_dir[-1] != '/':
14835375f98Sopenharmony_ci        args.targets_dir+='/'
14935375f98Sopenharmony_ci    targets = list(map(lambda x: x.replace('\\', '/'), glob(args.targets_dir + '*.test*')))
15035375f98Sopenharmony_ci    if len(targets) == 0:
15135375f98Sopenharmony_ci        raise Exception("No *.testpass or *.testfail files found in '%s'" % args.targets_dir)
15235375f98Sopenharmony_ci    uts.set_targets(targets)
15335375f98Sopenharmony_ci
15435375f98Sopenharmony_ci    # set the root path
15535375f98Sopenharmony_ci    uts.set_root_path(args.root_path)
15635375f98Sopenharmony_ci
15735375f98Sopenharmony_ci    # set output
15835375f98Sopenharmony_ci    uts.set_output(args.output)
15935375f98Sopenharmony_ci
16035375f98Sopenharmony_ci    # run the summarizer
16135375f98Sopenharmony_ci    print(uts.run())
162