1#!/usr/bin/env python 2 3""" 4A simple utility to redo the failed/errored tests. 5 6You need to specify the session directory in order for this script to locate the 7tests which need to be re-run. 8 9See also dotest.py, the test driver running the test suite. 10 11Type: 12 13./dotest.py -h 14 15for help. 16""" 17 18import os, sys, datetime 19import re 20 21# If True, redo with no '-t' option for the test driver. 22no_trace = False 23 24# To be filled with the filterspecs found in the session logs. 25redo_specs = [] 26 27# The filename components to match for. Only files with the contained component names 28# will be considered for re-run. Examples: ['X86_64', 'clang']. 29filename_components = [] 30 31do_delay = False 32 33# There is a known bug with respect to comp_specs and arch_specs, in that if we 34# encountered "-C clang" and "-C gcc" when visiting the session files, both 35# compilers will end up in the invocation of the test driver when rerunning. 36# That is: ./dotest -v -C clang^gcc ... -f ...". Ditto for "-A" flags. 37 38# The "-C compiler" for comp_specs. 39comp_specs = set() 40# The "-A arch" for arch_specs. 41arch_specs = set() 42 43def usage(): 44 print"""\ 45Usage: redo.py [-F filename_component] [-n] [session_dir] [-d] 46where options: 47-F : only consider the test for re-run if the session filename conatins the filename component 48 for example: -F x86_64 49-n : when running the tests, do not turn on trace mode, i.e, no '-t' option 50 is passed to the test driver (this will run the tests faster) 51-d : pass -d down to the test driver (introduces a delay so you can attach with a debugger) 52 53and session_dir specifies the session directory which contains previously 54recorded session infos for all the test cases which either failed or errored. 55 56If sessin_dir is left unspecified, this script uses the heuristic to find the 57possible session directories with names starting with %Y-%m-%d- (for example, 582012-01-23-) and employs the one with the latest timestamp.""" 59 sys.exit(0) 60 61def where(session_dir, test_dir): 62 """Returns the full path to the session directory; None if non-existent.""" 63 abspath = os.path.abspath(session_dir) 64 if os.path.isdir(abspath): 65 return abspath 66 67 session_dir_path = os.path.join(test_dir, session_dir) 68 if os.path.isdir(session_dir_path): 69 return session_dir_path 70 71 return None 72 73# This is the pattern for the line from the log file to redo a test. 74# We want the filter spec. 75filter_pattern = re.compile("^\./dotest\.py.*-f (.*)$") 76comp_pattern = re.compile(" -C ([^ ]+) ") 77arch_pattern = re.compile(" -A ([^ ]+) ") 78def redo(suffix, dir, names): 79 """Visitor function for os.path.walk(path, visit, arg).""" 80 global redo_specs 81 global comp_specs 82 global arch_specs 83 global filter_pattern 84 global comp_pattern 85 global arch_pattern 86 global filename_components 87 global do_delay 88 89 for name in names: 90 if name.endswith(suffix): 91 #print "Find a log file:", name 92 if name.startswith("Error") or name.startswith("Failure"): 93 if filename_components: 94 if not all([comp in name for comp in filename_components]): 95 continue 96 with open(os.path.join(dir, name), 'r') as log: 97 content = log.read() 98 for line in content.splitlines(): 99 match = filter_pattern.match(line) 100 if match: 101 filterspec = match.group(1) 102 print "adding filterspec:", filterspec 103 redo_specs.append(filterspec) 104 comp = comp_pattern.search(line) 105 if comp: 106 comp_specs.add(comp.group(1)) 107 arch = arch_pattern.search(line) 108 if arch: 109 arch_specs.add(arch.group(1)) 110 else: 111 continue 112 113def main(): 114 """Read the session directory and run the failed test cases one by one.""" 115 global no_trace 116 global redo_specs 117 global filename_components 118 global do_delay 119 120 test_dir = sys.path[0] 121 if not test_dir: 122 test_dir = os.getcwd() 123 if not test_dir.endswith('test'): 124 print "This script expects to reside in lldb's test directory." 125 sys.exit(-1) 126 127 index = 1 128 while index < len(sys.argv): 129 if sys.argv[index].startswith('-h') or sys.argv[index].startswith('--help'): 130 usage() 131 132 if sys.argv[index].startswith('-'): 133 # We should continue processing... 134 pass 135 else: 136 # End of option processing. 137 break 138 139 if sys.argv[index] == '-F': 140 # Increment by 1 to fetch the filename component spec. 141 index += 1 142 if index >= len(sys.argv) or sys.argv[index].startswith('-'): 143 usage() 144 filename_components.append(sys.argv[index]) 145 elif sys.argv[index] == '-n': 146 no_trace = True 147 elif sys.argv[index] == '-d': 148 do_delay = True 149 150 index += 1 151 152 if index < len(sys.argv): 153 # Get the specified session directory. 154 session_dir = sys.argv[index] 155 else: 156 # Use heuristic to find the latest session directory. 157 name = datetime.datetime.now().strftime("%Y-%m-%d-") 158 dirs = [d for d in os.listdir(os.getcwd()) if d.startswith(name)] 159 if len(dirs) == 0: 160 print "No default session directory found, please specify it explicitly." 161 usage() 162 session_dir = max(dirs, key=os.path.getmtime) 163 if not session_dir or not os.path.exists(session_dir): 164 print "No default session directory found, please specify it explicitly." 165 usage() 166 167 #print "The test directory:", test_dir 168 session_dir_path = where(session_dir, test_dir) 169 170 print "Using session dir path:", session_dir_path 171 os.chdir(test_dir) 172 os.path.walk(session_dir_path, redo, ".log") 173 174 if not redo_specs: 175 print "No failures/errors recorded within the session directory, please specify a different session directory.\n" 176 usage() 177 178 filters = " -f ".join(redo_specs) 179 compilers = '' 180 for comp in comp_specs: 181 compilers += " -C %s" % (comp) 182 archs = '' 183 for arch in arch_specs: 184 archs += "--arch %s " % (arch) 185 186 command = "./dotest.py %s %s -v %s %s -f " % (compilers, archs, "" if no_trace else "-t", "-d" if do_delay else "") 187 188 189 print "Running %s" % (command + filters) 190 os.system(command + filters) 191 192if __name__ == '__main__': 193 main() 194