1*a6f3f22fSchristos#! python3 2*a6f3f22fSchristos# ========================================== 3*a6f3f22fSchristos# Unity Project - A Test Framework for C 4*a6f3f22fSchristos# Copyright (c) 2015 Alexander Mueller / XelaRellum@web.de 5*a6f3f22fSchristos# [Released under MIT License. Please refer to license.txt for details] 6*a6f3f22fSchristos# Based on the ruby script by Mike Karlesky, Mark VanderVoord, Greg Williams 7*a6f3f22fSchristos# ========================================== 8*a6f3f22fSchristosimport sys 9*a6f3f22fSchristosimport os 10*a6f3f22fSchristosimport re 11*a6f3f22fSchristosfrom glob import glob 12*a6f3f22fSchristos 13*a6f3f22fSchristosclass UnityTestSummary: 14*a6f3f22fSchristos def __init__(self): 15*a6f3f22fSchristos self.report = '' 16*a6f3f22fSchristos self.total_tests = 0 17*a6f3f22fSchristos self.failures = 0 18*a6f3f22fSchristos self.ignored = 0 19*a6f3f22fSchristos 20*a6f3f22fSchristos def run(self): 21*a6f3f22fSchristos # Clean up result file names 22*a6f3f22fSchristos results = [] 23*a6f3f22fSchristos for target in self.targets: 24*a6f3f22fSchristos results.append(target.replace('\\', '/')) 25*a6f3f22fSchristos 26*a6f3f22fSchristos # Dig through each result file, looking for details on pass/fail: 27*a6f3f22fSchristos failure_output = [] 28*a6f3f22fSchristos ignore_output = [] 29*a6f3f22fSchristos 30*a6f3f22fSchristos for result_file in results: 31*a6f3f22fSchristos lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) 32*a6f3f22fSchristos if len(lines) == 0: 33*a6f3f22fSchristos raise Exception("Empty test result file: %s" % result_file) 34*a6f3f22fSchristos 35*a6f3f22fSchristos details = self.get_details(result_file, lines) 36*a6f3f22fSchristos failures = details['failures'] 37*a6f3f22fSchristos ignores = details['ignores'] 38*a6f3f22fSchristos if len(failures) > 0: failure_output.append('\n'.join(failures)) 39*a6f3f22fSchristos if len(ignores) > 0: ignore_output.append('n'.join(ignores)) 40*a6f3f22fSchristos tests,failures,ignored = self.parse_test_summary('\n'.join(lines)) 41*a6f3f22fSchristos self.total_tests += tests 42*a6f3f22fSchristos self.failures += failures 43*a6f3f22fSchristos self.ignored += ignored 44*a6f3f22fSchristos 45*a6f3f22fSchristos if self.ignored > 0: 46*a6f3f22fSchristos self.report += "\n" 47*a6f3f22fSchristos self.report += "--------------------------\n" 48*a6f3f22fSchristos self.report += "UNITY IGNORED TEST SUMMARY\n" 49*a6f3f22fSchristos self.report += "--------------------------\n" 50*a6f3f22fSchristos self.report += "\n".join(ignore_output) 51*a6f3f22fSchristos 52*a6f3f22fSchristos if self.failures > 0: 53*a6f3f22fSchristos self.report += "\n" 54*a6f3f22fSchristos self.report += "--------------------------\n" 55*a6f3f22fSchristos self.report += "UNITY FAILED TEST SUMMARY\n" 56*a6f3f22fSchristos self.report += "--------------------------\n" 57*a6f3f22fSchristos self.report += '\n'.join(failure_output) 58*a6f3f22fSchristos 59*a6f3f22fSchristos self.report += "\n" 60*a6f3f22fSchristos self.report += "--------------------------\n" 61*a6f3f22fSchristos self.report += "OVERALL UNITY TEST SUMMARY\n" 62*a6f3f22fSchristos self.report += "--------------------------\n" 63*a6f3f22fSchristos self.report += "{total_tests} TOTAL TESTS {failures} TOTAL FAILURES {ignored} IGNORED\n".format(total_tests = self.total_tests, failures=self.failures, ignored=self.ignored) 64*a6f3f22fSchristos self.report += "\n" 65*a6f3f22fSchristos 66*a6f3f22fSchristos return self.report 67*a6f3f22fSchristos 68*a6f3f22fSchristos def set_targets(self, target_array): 69*a6f3f22fSchristos self.targets = target_array 70*a6f3f22fSchristos 71*a6f3f22fSchristos def set_root_path(self, path): 72*a6f3f22fSchristos self.root = path 73*a6f3f22fSchristos 74*a6f3f22fSchristos def usage(self, err_msg=None): 75*a6f3f22fSchristos print("\nERROR: ") 76*a6f3f22fSchristos if err_msg: 77*a6f3f22fSchristos print(err_msg) 78*a6f3f22fSchristos print("\nUsage: unity_test_summary.rb result_file_directory/ root_path/") 79*a6f3f22fSchristos print(" result_file_directory - The location of your results files.") 80*a6f3f22fSchristos print(" Defaults to current directory if not specified.") 81*a6f3f22fSchristos print(" Should end in / if specified.") 82*a6f3f22fSchristos print(" root_path - Helpful for producing more verbose output if using relative paths.") 83*a6f3f22fSchristos sys.exit(1) 84*a6f3f22fSchristos 85*a6f3f22fSchristos def get_details(self, result_file, lines): 86*a6f3f22fSchristos results = { 'failures': [], 'ignores': [], 'successes': [] } 87*a6f3f22fSchristos for line in lines: 88*a6f3f22fSchristos parts = line.split(':') 89*a6f3f22fSchristos if len(parts) != 5: 90*a6f3f22fSchristos continue 91*a6f3f22fSchristos src_file,src_line,test_name,status,msg = parts 92*a6f3f22fSchristos if len(self.root) > 0: 93*a6f3f22fSchristos line_out = "%s%s" % (self.root, line) 94*a6f3f22fSchristos else: 95*a6f3f22fSchristos line_out = line 96*a6f3f22fSchristos if status == 'IGNORE': 97*a6f3f22fSchristos results['ignores'].append(line_out) 98*a6f3f22fSchristos elif status == 'FAIL': 99*a6f3f22fSchristos results['failures'].append(line_out) 100*a6f3f22fSchristos elif status == 'PASS': 101*a6f3f22fSchristos results['successes'].append(line_out) 102*a6f3f22fSchristos return results 103*a6f3f22fSchristos 104*a6f3f22fSchristos def parse_test_summary(self, summary): 105*a6f3f22fSchristos m = re.search(r"([0-9]+) Tests ([0-9]+) Failures ([0-9]+) Ignored", summary) 106*a6f3f22fSchristos if not m: 107*a6f3f22fSchristos raise Exception("Couldn't parse test results: %s" % summary) 108*a6f3f22fSchristos 109*a6f3f22fSchristos return int(m.group(1)), int(m.group(2)), int(m.group(3)) 110*a6f3f22fSchristos 111*a6f3f22fSchristos 112*a6f3f22fSchristosif __name__ == '__main__': 113*a6f3f22fSchristos uts = UnityTestSummary() 114*a6f3f22fSchristos try: 115*a6f3f22fSchristos #look in the specified or current directory for result files 116*a6f3f22fSchristos if len(sys.argv) > 1: 117*a6f3f22fSchristos targets_dir = sys.argv[1] 118*a6f3f22fSchristos else: 119*a6f3f22fSchristos targets_dir = './' 120*a6f3f22fSchristos targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) 121*a6f3f22fSchristos if len(targets) == 0: 122*a6f3f22fSchristos raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) 123*a6f3f22fSchristos uts.set_targets(targets) 124*a6f3f22fSchristos 125*a6f3f22fSchristos #set the root path 126*a6f3f22fSchristos if len(sys.argv) > 2: 127*a6f3f22fSchristos root_path = sys.argv[2] 128*a6f3f22fSchristos else: 129*a6f3f22fSchristos root_path = os.path.split(__file__)[0] 130*a6f3f22fSchristos uts.set_root_path(root_path) 131*a6f3f22fSchristos 132*a6f3f22fSchristos #run the summarizer 133*a6f3f22fSchristos print(uts.run()) 134*a6f3f22fSchristos except Exception as e: 135*a6f3f22fSchristos uts.usage(e) 136