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