#!/usr/bin/env python """ A simple utility to redo the failed/errored tests. You need to specify the session directory in order for this script to locate the tests which need to be re-run. See also dotest.py, the test driver running the test suite. Type: ./dotest.py -h for help. """ import os, sys, datetime import re # If True, redo with no '-t' option for the test driver. no_trace = False # To be filled with the filterspecs found in the session logs. redo_specs = [] # The filename components to match for. Only files with the contained component names # will be considered for re-run. Examples: ['X86_64', 'clang']. filename_components = [] # There is a known bug with respect to comp_specs and arch_specs, in that if we # encountered "-C clang" and "-C gcc" when visiting the session files, both # compilers will end up in the invocation of the test driver when rerunning. # That is: ./dotest -v -C clang^gcc ... -f ...". Ditto for "-A" flags. # The "-C compiler" for comp_specs. comp_specs = set() # The "-A arch" for arch_specs. arch_specs = set() def usage(): print"""\ Usage: redo.py [-F filename_component] [-n] [session_dir] where options: -F : only consider the test for re-run if the session filename conatins the filename component for example: -F x86_64 -n : when running the tests, do not turn on trace mode, i.e, no '-t' option is passed to the test driver (this will run the tests faster) and session_dir specifies the session directory which contains previously recorded session infos for all the test cases which either failed or errored. If sessin_dir is left unspecified, this script uses the heuristic to find the possible session directories with names starting with %Y-%m-%d- (for example, 2012-01-23-) and employs the one with the latest timestamp.""" sys.exit(0) def where(session_dir, test_dir): """Returns the full path to the session directory; None if non-existent.""" abspath = os.path.abspath(session_dir) if os.path.isdir(abspath): return abspath session_dir_path = os.path.join(test_dir, session_dir) if os.path.isdir(session_dir_path): return session_dir_path return None # This is the pattern for the line from the log file to redo a test. # We want the filter spec. filter_pattern = re.compile("^\./dotest\.py.*-f (.*)$") comp_pattern = re.compile(" -C ([^ ]+) ") arch_pattern = re.compile(" -A ([^ ]+) ") def redo(suffix, dir, names): """Visitor function for os.path.walk(path, visit, arg).""" global redo_specs global comp_specs global arch_specs global filter_pattern global comp_pattern global arch_pattern global filename_components for name in names: if name.endswith(suffix): #print "Find a log file:", name if name.startswith("Error") or name.startswith("Failure"): if filename_components: if not all([comp in name for comp in filename_components]): continue with open(os.path.join(dir, name), 'r') as log: content = log.read() for line in content.splitlines(): match = filter_pattern.match(line) if match: filterspec = match.group(1) print "adding filterspec:", filterspec redo_specs.append(filterspec) comp = comp_pattern.search(line) if comp: comp_specs.add(comp.group(1)) arch = arch_pattern.search(line) if arch: arch_specs.add(arch.group(1)) else: continue def main(): """Read the session directory and run the failed test cases one by one.""" global no_trace global redo_specs global filename_components test_dir = sys.path[0] if not test_dir: test_dir = os.getcwd() if not test_dir.endswith('test'): print "This script expects to reside in lldb's test directory." sys.exit(-1) index = 1 while index < len(sys.argv): if sys.argv[index].startswith('-h'): usage() if sys.argv[index].startswith('-'): # We should continue processing... pass else: # End of option processing. break if sys.argv[index] == '-F': # Increment by 1 to fetch the filename component spec. index += 1 if index >= len(sys.argv) or sys.argv[index].startswith('-'): usage() filename_components.append(sys.argv[index]) elif sys.argv[index] == '-n': no_trace = True index += 1 if index < len(sys.argv): # Get the specified session directory. session_dir = sys.argv[index] else: # Use heuristic to find the latest session directory. name = datetime.datetime.now().strftime("%Y-%m-%d-") dirs = [d for d in os.listdir(os.getcwd()) if d.startswith(name)] if len(dirs) == 0: print "No default session directory found, please specify it explicitly." usage() session_dir = max(dirs, key=os.path.getmtime) if not session_dir or not os.path.exists(session_dir): print "No default session directory found, please specify it explicitly." usage() #print "The test directory:", test_dir session_dir_path = where(session_dir, test_dir) print "Using session dir path:", session_dir_path os.chdir(test_dir) os.path.walk(session_dir_path, redo, ".log") if not redo_specs: print "No failures/errors recorded within the session directory, please specify a different session directory.\n" usage() filters = " -f ".join(redo_specs) compilers = (" -C %s" % "^".join(comp_specs)) if comp_specs else None archs = (" -A %s" % "^".join(arch_specs)) if arch_specs else None command = "./dotest.py %s %s -v %s -f " % (compilers if compilers else "", archs if archs else "", "" if no_trace else "-t") print "Running %s" % (command + filters) os.system(command + filters) if __name__ == '__main__': main()