Home | History | Annotate | Download | only in test
      1 #!/usr/bin/env python
      2 
      3 """
      4 A simple utility to redo the failed/errored tests.
      5 
      6 You need to specify the session directory in order for this script to locate the
      7 tests which need to be re-run.
      8 
      9 See also dotest.py, the test driver running the test suite.
     10 
     11 Type:
     12 
     13 ./dotest.py -h
     14 
     15 for help.
     16 """
     17 
     18 import os, sys, datetime
     19 import re
     20 
     21 # If True, redo with no '-t' option for the test driver.
     22 no_trace = False
     23 
     24 # To be filled with the filterspecs found in the session logs.
     25 redo_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'].
     29 filename_components = []
     30 
     31 do_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.
     39 comp_specs = set()
     40 # The "-A arch" for arch_specs.
     41 arch_specs = set()
     42 
     43 def usage():
     44     print"""\
     45 Usage: redo.py [-F filename_component] [-n] [session_dir] [-d]
     46 where 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 
     53 and session_dir specifies the session directory which contains previously
     54 recorded session infos for all the test cases which either failed or errored.
     55 
     56 If sessin_dir is left unspecified, this script uses the heuristic to find the
     57 possible session directories with names starting with %Y-%m-%d- (for example,
     58 2012-01-23-) and employs the one with the latest timestamp."""
     59     sys.exit(0)
     60 
     61 def 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.
     75 filter_pattern = re.compile("^\./dotest\.py.*-f (.*)$")
     76 comp_pattern = re.compile(" -C ([^ ]+) ")
     77 arch_pattern = re.compile(" -A ([^ ]+) ")
     78 def 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 
    113 def 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 
    192 if __name__ == '__main__':
    193     main()
    194