1290001Sglebius#! python3
2290001Sglebius# ==========================================
3290001Sglebius#   Unity Project - A Test Framework for C
4290001Sglebius#   Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de
5290001Sglebius#   [Released under MIT License. Please refer to license.txt for details]
6290001Sglebius#   Based on the ruby script by  Mike Karlesky, Mark VanderVoord, Greg Williams
7290001Sglebius# ==========================================
8290001Sglebiusimport sys
9290001Sglebiusimport os
10290001Sglebiusimport re
11290001Sglebiusfrom glob import glob
12290001Sglebius
13290001Sglebiusclass UnityTestSummary:
14290001Sglebius    def __init__(self):
15290001Sglebius        self.report = ''
16290001Sglebius        self.total_tests = 0
17290001Sglebius        self.failures = 0
18290001Sglebius        self.ignored = 0
19290001Sglebius
20290001Sglebius    def run(self):
21290001Sglebius        # Clean up result file names
22290001Sglebius        results = []
23290001Sglebius        for target in self.targets:
24290001Sglebius            results.append(target.replace('\\', '/'))
25290001Sglebius
26290001Sglebius        # Dig through each result file, looking for details on pass/fail:
27290001Sglebius        failure_output = []
28290001Sglebius        ignore_output = []
29290001Sglebius
30290001Sglebius        for result_file in results:
31290001Sglebius            lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n')))
32290001Sglebius            if len(lines) == 0:
33290001Sglebius                raise Exception("Empty test result file: %s" % result_file)
34290001Sglebius
35290001Sglebius            details = self.get_details(result_file, lines)
36290001Sglebius            failures = details['failures']
37290001Sglebius            ignores = details['ignores']
38290001Sglebius            if len(failures) > 0: failure_output.append('\n'.join(failures))
39290001Sglebius            if len(ignores) > 0: ignore_output.append('n'.join(ignores))
40290001Sglebius            tests,failures,ignored = self.parse_test_summary('\n'.join(lines))
41290001Sglebius            self.total_tests += tests
42290001Sglebius            self.failures += failures
43290001Sglebius            self.ignored += ignored
44290001Sglebius
45290001Sglebius        if self.ignored > 0:
46290001Sglebius            self.report += "\n"
47290001Sglebius            self.report += "--------------------------\n"
48290001Sglebius            self.report += "UNITY IGNORED TEST SUMMARY\n"
49290001Sglebius            self.report += "--------------------------\n"
50290001Sglebius            self.report += "\n".join(ignore_output)
51290001Sglebius
52290001Sglebius        if self.failures > 0:
53290001Sglebius            self.report += "\n"
54290001Sglebius            self.report += "--------------------------\n"
55290001Sglebius            self.report += "UNITY FAILED TEST SUMMARY\n"
56290001Sglebius            self.report += "--------------------------\n"
57290001Sglebius            self.report += '\n'.join(failure_output)
58290001Sglebius
59290001Sglebius        self.report += "\n"
60290001Sglebius        self.report += "--------------------------\n"
61290001Sglebius        self.report += "OVERALL UNITY TEST SUMMARY\n"
62290001Sglebius        self.report += "--------------------------\n"
63290001Sglebius        self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored)
64290001Sglebius        self.report += "\n"
65290001Sglebius
66290001Sglebius        return self.report
67290001Sglebius
68290001Sglebius    def set_targets(self, target_array):
69290001Sglebius            self.targets = target_array
70290001Sglebius
71290001Sglebius    def set_root_path(self, path):
72290001Sglebius        self.root = path
73290001Sglebius
74290001Sglebius    def usage(self, err_msg=None):
75290001Sglebius        print("\nERROR: ")
76290001Sglebius        if err_msg:
77290001Sglebius            print(err_msg)
78290001Sglebius        print("\nUsage: unity_test_summary.rb result_file_directory/ root_path/")
79290001Sglebius        print("     result_file_directory - The location of your results files.")
80290001Sglebius        print("                             Defaults to current directory if not specified.")
81290001Sglebius        print("                             Should end in / if specified.")
82290001Sglebius        print("     root_path - Helpful for producing more verbose output if using relative paths.")
83290001Sglebius        sys.exit(1)
84290001Sglebius
85290001Sglebius    def get_details(self, result_file, lines):
86290001Sglebius        results = { 'failures': [], 'ignores': [], 'successes': [] }
87290001Sglebius        for line in lines:
88290001Sglebius            parts = line.split(':')
89290001Sglebius            if len(parts) != 5:
90290001Sglebius                continue
91290001Sglebius            src_file,src_line,test_name,status,msg = parts
92290001Sglebius            if len(self.root) > 0:
93290001Sglebius                line_out = "%s%s" % (self.root, line)
94290001Sglebius            else:
95290001Sglebius                line_out = line
96290001Sglebius            if status == 'IGNORE':
97290001Sglebius                results['ignores'].append(line_out)
98290001Sglebius            elif status == 'FAIL':
99290001Sglebius                results['failures'].append(line_out)
100290001Sglebius            elif status == 'PASS':
101290001Sglebius                results['successes'].append(line_out)
102290001Sglebius        return results
103290001Sglebius
104290001Sglebius    def parse_test_summary(self, summary):
105290001Sglebius        m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary)
106290001Sglebius        if not m:
107290001Sglebius            raise Exception("Couldn't parse test results: %s" % summary)
108290001Sglebius
109290001Sglebius        return int(m.group(1)), int(m.group(2)), int(m.group(3))
110290001Sglebius
111290001Sglebius
112290001Sglebiusif __name__ == '__main__':
113290001Sglebius  uts = UnityTestSummary()
114290001Sglebius  try:
115290001Sglebius    #look in the specified or current directory for result files
116290001Sglebius    if len(sys.argv) > 1:
117290001Sglebius        targets_dir = sys.argv[1]
118290001Sglebius    else:
119290001Sglebius        targets_dir = './'
120290001Sglebius    targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*')))
121290001Sglebius    if len(targets) == 0:
122290001Sglebius        raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir)
123290001Sglebius    uts.set_targets(targets)
124290001Sglebius
125290001Sglebius    #set the root path
126290001Sglebius    if len(sys.argv) > 2:
127290001Sglebius        root_path = sys.argv[2]
128290001Sglebius    else:
129290001Sglebius        root_path = os.path.split(__file__)[0]
130290001Sglebius    uts.set_root_path(root_path)
131290001Sglebius
132290001Sglebius    #run the summarizer
133290001Sglebius    print(uts.run())
134290001Sglebius  except Exception as e:
135290001Sglebius    uts.usage(e)
136