1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun 5*4882a593Smuzhiyunimport sys 6*4882a593Smuzhiyunimport os 7*4882a593Smuzhiyunimport re 8*4882a593Smuzhiyun 9*4882a593Smuzhiyun# A parser that can be used to identify weather a line is a test result or a section statement. 10*4882a593Smuzhiyunclass PtestParser(object): 11*4882a593Smuzhiyun def __init__(self): 12*4882a593Smuzhiyun self.results = {} 13*4882a593Smuzhiyun self.sections = {} 14*4882a593Smuzhiyun 15*4882a593Smuzhiyun def parse(self, logfile): 16*4882a593Smuzhiyun test_regex = {} 17*4882a593Smuzhiyun test_regex['PASSED'] = re.compile(r"^PASS:(.+)") 18*4882a593Smuzhiyun test_regex['FAILED'] = re.compile(r"^FAIL:([^(]+)") 19*4882a593Smuzhiyun test_regex['SKIPPED'] = re.compile(r"^SKIP:(.+)") 20*4882a593Smuzhiyun 21*4882a593Smuzhiyun section_regex = {} 22*4882a593Smuzhiyun section_regex['begin'] = re.compile(r"^BEGIN: .*/(.+)/ptest") 23*4882a593Smuzhiyun section_regex['end'] = re.compile(r"^END: .*/(.+)/ptest") 24*4882a593Smuzhiyun section_regex['duration'] = re.compile(r"^DURATION: (.+)") 25*4882a593Smuzhiyun section_regex['exitcode'] = re.compile(r"^ERROR: Exit status is (.+)") 26*4882a593Smuzhiyun section_regex['timeout'] = re.compile(r"^TIMEOUT: .*/(.+)/ptest") 27*4882a593Smuzhiyun 28*4882a593Smuzhiyun # Cache markers so we don't take the re.search() hit all the time. 29*4882a593Smuzhiyun markers = ("PASS:", "FAIL:", "SKIP:", "BEGIN:", "END:", "DURATION:", "ERROR: Exit", "TIMEOUT:") 30*4882a593Smuzhiyun 31*4882a593Smuzhiyun def newsection(): 32*4882a593Smuzhiyun return { 'name': "No-section", 'log': [] } 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun current_section = newsection() 35*4882a593Smuzhiyun 36*4882a593Smuzhiyun with open(logfile, errors='replace') as f: 37*4882a593Smuzhiyun for line in f: 38*4882a593Smuzhiyun if not line.startswith(markers): 39*4882a593Smuzhiyun current_section['log'].append(line) 40*4882a593Smuzhiyun continue 41*4882a593Smuzhiyun 42*4882a593Smuzhiyun result = section_regex['begin'].search(line) 43*4882a593Smuzhiyun if result: 44*4882a593Smuzhiyun current_section['name'] = result.group(1) 45*4882a593Smuzhiyun continue 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun result = section_regex['end'].search(line) 48*4882a593Smuzhiyun if result: 49*4882a593Smuzhiyun if current_section['name'] != result.group(1): 50*4882a593Smuzhiyun bb.warn("Ptest END log section mismatch %s vs. %s" % (current_section['name'], result.group(1))) 51*4882a593Smuzhiyun if current_section['name'] in self.sections: 52*4882a593Smuzhiyun bb.warn("Ptest duplicate section for %s" % (current_section['name'])) 53*4882a593Smuzhiyun self.sections[current_section['name']] = current_section 54*4882a593Smuzhiyun del self.sections[current_section['name']]['name'] 55*4882a593Smuzhiyun current_section = newsection() 56*4882a593Smuzhiyun continue 57*4882a593Smuzhiyun 58*4882a593Smuzhiyun result = section_regex['timeout'].search(line) 59*4882a593Smuzhiyun if result: 60*4882a593Smuzhiyun if current_section['name'] != result.group(1): 61*4882a593Smuzhiyun bb.warn("Ptest TIMEOUT log section mismatch %s vs. %s" % (current_section['name'], result.group(1))) 62*4882a593Smuzhiyun current_section['timeout'] = True 63*4882a593Smuzhiyun continue 64*4882a593Smuzhiyun 65*4882a593Smuzhiyun for t in ['duration', 'exitcode']: 66*4882a593Smuzhiyun result = section_regex[t].search(line) 67*4882a593Smuzhiyun if result: 68*4882a593Smuzhiyun current_section[t] = result.group(1) 69*4882a593Smuzhiyun continue 70*4882a593Smuzhiyun 71*4882a593Smuzhiyun current_section['log'].append(line) 72*4882a593Smuzhiyun 73*4882a593Smuzhiyun for t in test_regex: 74*4882a593Smuzhiyun result = test_regex[t].search(line) 75*4882a593Smuzhiyun if result: 76*4882a593Smuzhiyun if current_section['name'] not in self.results: 77*4882a593Smuzhiyun self.results[current_section['name']] = {} 78*4882a593Smuzhiyun self.results[current_section['name']][result.group(1).strip()] = t 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun # Python performance for repeatedly joining long strings is poor, do it all at once at the end. 81*4882a593Smuzhiyun # For 2.1 million lines in a log this reduces 18 hours to 12s. 82*4882a593Smuzhiyun for section in self.sections: 83*4882a593Smuzhiyun self.sections[section]['log'] = "".join(self.sections[section]['log']) 84*4882a593Smuzhiyun 85*4882a593Smuzhiyun return self.results, self.sections 86*4882a593Smuzhiyun 87*4882a593Smuzhiyun # Log the results as files. The file name is the section name and the contents are the tests in that section. 88*4882a593Smuzhiyun def results_as_files(self, target_dir): 89*4882a593Smuzhiyun if not os.path.exists(target_dir): 90*4882a593Smuzhiyun raise Exception("Target directory does not exist: %s" % target_dir) 91*4882a593Smuzhiyun 92*4882a593Smuzhiyun for section in self.results: 93*4882a593Smuzhiyun prefix = 'No-section' 94*4882a593Smuzhiyun if section: 95*4882a593Smuzhiyun prefix = section 96*4882a593Smuzhiyun section_file = os.path.join(target_dir, prefix) 97*4882a593Smuzhiyun # purge the file contents if it exists 98*4882a593Smuzhiyun with open(section_file, 'w') as f: 99*4882a593Smuzhiyun for test_name in sorted(self.results[section]): 100*4882a593Smuzhiyun status = self.results[section][test_name] 101*4882a593Smuzhiyun f.write(status + ": " + test_name + "\n") 102*4882a593Smuzhiyun 103*4882a593Smuzhiyun 104*4882a593Smuzhiyun# ltp log parsing 105*4882a593Smuzhiyunclass LtpParser(object): 106*4882a593Smuzhiyun def __init__(self): 107*4882a593Smuzhiyun self.results = {} 108*4882a593Smuzhiyun self.section = {'duration': "", 'log': ""} 109*4882a593Smuzhiyun 110*4882a593Smuzhiyun def parse(self, logfile): 111*4882a593Smuzhiyun test_regex = {} 112*4882a593Smuzhiyun test_regex['PASSED'] = re.compile(r"PASS") 113*4882a593Smuzhiyun test_regex['FAILED'] = re.compile(r"FAIL") 114*4882a593Smuzhiyun test_regex['SKIPPED'] = re.compile(r"SKIP") 115*4882a593Smuzhiyun 116*4882a593Smuzhiyun with open(logfile, errors='replace') as f: 117*4882a593Smuzhiyun for line in f: 118*4882a593Smuzhiyun for t in test_regex: 119*4882a593Smuzhiyun result = test_regex[t].search(line) 120*4882a593Smuzhiyun if result: 121*4882a593Smuzhiyun self.results[line.split()[0].strip()] = t 122*4882a593Smuzhiyun 123*4882a593Smuzhiyun for test in self.results: 124*4882a593Smuzhiyun result = self.results[test] 125*4882a593Smuzhiyun self.section['log'] = self.section['log'] + ("%s: %s\n" % (result.strip()[:-2], test.strip())) 126*4882a593Smuzhiyun 127*4882a593Smuzhiyun return self.results, self.section 128*4882a593Smuzhiyun 129*4882a593Smuzhiyun 130*4882a593Smuzhiyun# ltp Compliance log parsing 131*4882a593Smuzhiyunclass LtpComplianceParser(object): 132*4882a593Smuzhiyun def __init__(self): 133*4882a593Smuzhiyun self.results = {} 134*4882a593Smuzhiyun self.section = {'duration': "", 'log': ""} 135*4882a593Smuzhiyun 136*4882a593Smuzhiyun def parse(self, logfile): 137*4882a593Smuzhiyun test_regex = {} 138*4882a593Smuzhiyun test_regex['FAILED'] = re.compile(r"FAIL") 139*4882a593Smuzhiyun 140*4882a593Smuzhiyun section_regex = {} 141*4882a593Smuzhiyun section_regex['test'] = re.compile(r"^Executing") 142*4882a593Smuzhiyun 143*4882a593Smuzhiyun with open(logfile, errors='replace') as f: 144*4882a593Smuzhiyun name = logfile 145*4882a593Smuzhiyun result = "PASSED" 146*4882a593Smuzhiyun for line in f: 147*4882a593Smuzhiyun regex_result = section_regex['test'].search(line) 148*4882a593Smuzhiyun if regex_result: 149*4882a593Smuzhiyun name = line.split()[1].strip() 150*4882a593Smuzhiyun 151*4882a593Smuzhiyun regex_result = test_regex['FAILED'].search(line) 152*4882a593Smuzhiyun if regex_result: 153*4882a593Smuzhiyun result = "FAILED" 154*4882a593Smuzhiyun self.results[name] = result 155*4882a593Smuzhiyun 156*4882a593Smuzhiyun for test in self.results: 157*4882a593Smuzhiyun result = self.results[test] 158*4882a593Smuzhiyun print (self.results) 159*4882a593Smuzhiyun self.section['log'] = self.section['log'] + ("%s: %s\n" % (result.strip()[:-2], test.strip())) 160*4882a593Smuzhiyun 161*4882a593Smuzhiyun return self.results, self.section 162