Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(405)

Side by Side Diff: tools/isolate/trace_test_cases.py

Issue 10377105: Add scripts to list or trace all test cases in a gtest executable. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « tools/isolate/trace_inputs.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """A manual version of trace_inputs.py that is specialized in tracing each
7 google-test test case individually.
8
9 This is mainly written to work around bugs in strace w.r.t. browser_tests.
10 """
11
12 import fnmatch
13 import json
14 import multiprocessing
15 import optparse
16 import os
17 import sys
18 import tempfile
19 import time
20
21 import list_test_cases
22 import trace_inputs
23
24 BASE_DIR = os.path.dirname(os.path.abspath(__file__))
25 ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR))
26
27
28 def trace_test_case(
29 test_case, executable, root_dir, cwd_dir, product_dir, leak):
30 """Traces a single test case and returns the .isolate compatible variable
31 dict.
32 """
33 # Resolve any symlink
34 root_dir = os.path.realpath(root_dir)
35
36 api = trace_inputs.get_api()
37 cmd = [executable, '--gtest_filter=%s' % test_case]
38
39 if not leak:
40 f, logname = tempfile.mkstemp(prefix='trace')
41 os.close(f)
42 else:
43 logname = '%s.%s.log' % (executable, test_case.replace('/', '-'))
44 f = None
45
46 try:
47 for i in range(3):
48 start = time.time()
49 returncode, output = trace_inputs.trace(
50 logname, cmd, os.path.join(root_dir, cwd_dir), api, True)
51 if returncode and i != 2:
52 print '\nFailed while running: %s' % ' '.join(cmd)
53 print 'Trying again!'
54 continue
55 duration = time.time() - start
56 try:
57 _, _, _, _, simplified = trace_inputs.load_trace(logname, root_dir, api)
58 break
59 except Exception:
60 print '\nFailed loading the trace for: %s' % ' '.join(cmd)
61 print 'Trying again!'
62
63 variables = trace_inputs.generate_dict(simplified, cwd_dir, product_dir)
64 return {
65 'case': test_case,
66 'variables': variables,
67 'result': returncode,
68 'duration': duration,
69 'output': output,
70 }
71 finally:
72 if f:
73 os.remove(logname)
74
75
76 def task(args):
77 """Adaptor for multiprocessing.Pool().imap_unordered().
78
79 It is executed asynchronously.
80 """
81 return trace_test_case(*args)
82
83
84 def get_test_cases(executable, skip):
85 """Returns the filtered list of test cases.
86
87 This is done synchronously.
88 """
89 try:
90 out = list_test_cases.gtest_list_tests(executable)
91 except list_test_cases.Failure, e:
92 print e.args[0]
93 return None
94
95 tests = list_test_cases.parse_gtest_cases(out)
96 tests = [t for t in tests if not any(fnmatch.fnmatch(t, s) for s in skip)]
97 print 'Found %d test cases in %s' % (len(tests), os.path.basename(executable))
98 return tests
99
100
101 def trace_test_cases(
102 executable, root_dir, cwd_dir, product_dir, leak, skip, jobs, timeout):
103 """Traces test cases one by one."""
104 tests = get_test_cases(executable, skip)
105 if not tests:
106 return
107
108 last_line = ''
109 out = {}
110 index = 0
111 pool = multiprocessing.Pool(processes=jobs)
112 start = time.time()
113 try:
114 g = ((t, executable, root_dir, cwd_dir, product_dir, leak) for t in tests)
115 it = pool.imap_unordered(task, g)
116 while True:
117 try:
118 result = it.next(timeout=timeout)
119 except StopIteration:
120 break
121 case = result.pop('case')
122 index += 1
123 line = '%d of %d (%.1f%%), %.1fs: %s' % (
124 index,
125 len(tests),
126 index * 100. / len(tests),
127 time.time() - start,
128 case)
129 sys.stdout.write(
130 '\r%s%s' % (line, ' ' * max(0, len(last_line) - len(line))))
131 sys.stdout.flush()
132 last_line = line
133 # TODO(maruel): Retry failed tests.
134 out[case] = result
135 return 0
136 except multiprocessing.TimeoutError, e:
137 print 'Got a timeout while processing a task item %s' % e
138 # Be sure to stop the pool on exception.
139 pool.terminate()
140 return 1
141 except Exception, e:
142 # Be sure to stop the pool on exception.
143 pool.terminate()
144 raise
145 finally:
146 with open('%s.test_cases' % executable, 'w') as f:
147 json.dump(out, f, indent=2, sort_keys=True)
148 pool.close()
149 pool.join()
150
151
152 def main():
153 """CLI frontend to validate arguments."""
154 parser = optparse.OptionParser(
155 usage='%prog <options> [gtest]')
156 parser.allow_interspersed_args = False
157 parser.add_option(
158 '-c', '--cwd',
159 default='chrome',
160 help='Signal to start the process from this relative directory. When '
161 'specified, outputs the inputs files in a way compatible for '
162 'gyp processing. Should be set to the relative path containing the '
163 'gyp file, e.g. \'chrome\' or \'net\'')
164 parser.add_option(
165 '-p', '--product-dir',
166 default='out/Release',
167 help='Directory for PRODUCT_DIR. Default: %default')
168 parser.add_option(
169 '--root-dir',
170 default=ROOT_DIR,
171 help='Root directory to base everything off. Default: %default')
172 parser.add_option(
173 '-l', '--leak',
174 action='store_true',
175 help='Leak trace files')
176 parser.add_option(
177 '-s', '--skip',
178 default=[],
179 action='append',
180 help='filter to apply to test cases to skip, wildcard-style')
181 parser.add_option(
182 '-j', '--jobs',
183 type='int',
184 help='number of parallel jobs')
185 parser.add_option(
186 '-t', '--timeout',
187 default=120,
188 type='int',
189 help='number of parallel jobs')
190 options, args = parser.parse_args()
191
192 if len(args) != 1:
193 parser.error(
194 'Please provide the executable line to run, if you need fancy things '
195 'like xvfb, start this script from inside xvfb, it\'ll be faster.')
196 executable = os.path.join(options.root_dir, options.product_dir, args[0])
197 return trace_test_cases(
198 executable,
199 options.root_dir,
200 options.cwd,
201 options.product_dir,
202 options.leak,
203 options.skip,
204 options.jobs,
205 options.timeout)
206
207
208 if __name__ == '__main__':
209 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/isolate/trace_inputs.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698