1import sys 2import os 3from glob import glob 4 5from pyparsing import * 6from junit_xml import TestSuite, TestCase 7 8 9class UnityTestSummary: 10 def __init__(self): 11 self.report = '' 12 self.total_tests = 0 13 self.failures = 0 14 self.ignored = 0 15 self.targets = 0 16 self.root = None 17 self.test_suites = dict() 18 19 def run(self): 20 # Clean up result file names 21 results = [] 22 for target in self.targets: 23 results.append(target.replace('\\', '/')) 24 25 # Dig through each result file, looking for details on pass/fail: 26 for result_file in results: 27 lines = list(map(lambda line: line.rstrip(), open(result_file, "r").read().split('\n'))) 28 if len(lines) == 0: 29 raise Exception("Empty test result file: %s" % result_file) 30 31 # define an expression for your file reference 32 entry_one = Combine( 33 oneOf(list(alphas)) + ':/' + 34 Word(alphanums + '_-./')) 35 36 entry_two = Word(printables + ' ', excludeChars=':') 37 entry = entry_one | entry_two 38 39 delimiter = Literal(':').suppress() 40 tc_result_line = Group(entry.setResultsName('tc_file_name') + delimiter + entry.setResultsName( 41 'tc_line_nr') + delimiter + entry.setResultsName('tc_name') + delimiter + entry.setResultsName( 42 'tc_status') + Optional( 43 delimiter + entry.setResultsName('tc_msg'))).setResultsName("tc_line") 44 45 eol = LineEnd().suppress() 46 sol = LineStart().suppress() 47 blank_line = sol + eol 48 49 tc_summary_line = Group(Word(nums).setResultsName("num_of_tests") + "Tests" + Word(nums).setResultsName( 50 "num_of_fail") + "Failures" + Word(nums).setResultsName("num_of_ignore") + "Ignored").setResultsName( 51 "tc_summary") 52 tc_end_line = Or(Literal("FAIL"), Literal('Ok')).setResultsName("tc_result") 53 54 # run it and see... 55 pp1 = tc_result_line | Optional(tc_summary_line | tc_end_line) 56 pp1.ignore(blank_line | OneOrMore("-")) 57 58 result = list() 59 for l in lines: 60 result.append((pp1.parseString(l)).asDict()) 61 # delete empty results 62 result = filter(None, result) 63 64 tc_list = list() 65 for r in result: 66 if 'tc_line' in r: 67 tmp_tc_line = r['tc_line'] 68 69 # get only the file name which will be used as the classname 70 file_name = tmp_tc_line['tc_file_name'].split('\\').pop().split('/').pop().rsplit('.', 1)[0] 71 tmp_tc = TestCase(name=tmp_tc_line['tc_name'], classname=file_name) 72 if 'tc_status' in tmp_tc_line: 73 if str(tmp_tc_line['tc_status']) == 'IGNORE': 74 if 'tc_msg' in tmp_tc_line: 75 tmp_tc.add_skipped_info(message=tmp_tc_line['tc_msg'], 76 output=r'[File]={0}, [Line]={1}'.format( 77 tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) 78 else: 79 tmp_tc.add_skipped_info(message=" ") 80 elif str(tmp_tc_line['tc_status']) == 'FAIL': 81 if 'tc_msg' in tmp_tc_line: 82 tmp_tc.add_failure_info(message=tmp_tc_line['tc_msg'], 83 output=r'[File]={0}, [Line]={1}'.format( 84 tmp_tc_line['tc_file_name'], tmp_tc_line['tc_line_nr'])) 85 else: 86 tmp_tc.add_failure_info(message=" ") 87 88 tc_list.append((str(result_file), tmp_tc)) 89 90 for k, v in tc_list: 91 try: 92 self.test_suites[k].append(v) 93 except KeyError: 94 self.test_suites[k] = [v] 95 ts = [] 96 for suite_name in self.test_suites: 97 ts.append(TestSuite(suite_name, self.test_suites[suite_name])) 98 99 with open('result.xml', 'w') as f: 100 TestSuite.to_file(f, ts, prettyprint='True', encoding='utf-8') 101 102 return self.report 103 104 def set_targets(self, target_array): 105 self.targets = target_array 106 107 def set_root_path(self, path): 108 self.root = path 109 110 @staticmethod 111 def usage(err_msg=None): 112 print("\nERROR: ") 113 if err_msg: 114 print(err_msg) 115 print("\nUsage: unity_test_summary.py result_file_directory/ root_path/") 116 print(" result_file_directory - The location of your results files.") 117 print(" Defaults to current directory if not specified.") 118 print(" Should end in / if specified.") 119 print(" root_path - Helpful for producing more verbose output if using relative paths.") 120 sys.exit(1) 121 122 123if __name__ == '__main__': 124 uts = UnityTestSummary() 125 try: 126 # look in the specified or current directory for result files 127 if len(sys.argv) > 1: 128 targets_dir = sys.argv[1] 129 else: 130 targets_dir = './' 131 targets = list(map(lambda x: x.replace('\\', '/'), glob(targets_dir + '*.test*'))) 132 if len(targets) == 0: 133 raise Exception("No *.testpass or *.testfail files found in '%s'" % targets_dir) 134 uts.set_targets(targets) 135 136 # set the root path 137 if len(sys.argv) > 2: 138 root_path = sys.argv[2] 139 else: 140 root_path = os.path.split(__file__)[0] 141 uts.set_root_path(root_path) 142 143 # run the summarizer 144 print(uts.run()) 145 except Exception as e: 146 UnityTestSummary.usage(e) 147