OLD | NEW |
| (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 """Traces each test cases of a google-test executable individually. | |
7 | |
8 Gives detailed information about each test case. The logs can be read afterward | |
9 with ./trace_inputs.py read -l /path/to/executable.logs | |
10 """ | |
11 | |
12 import logging | |
13 import multiprocessing | |
14 import os | |
15 import sys | |
16 import time | |
17 | |
18 import run_test_cases | |
19 import trace_inputs | |
20 | |
21 BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
22 ROOT_DIR = os.path.dirname(os.path.dirname(BASE_DIR)) | |
23 | |
24 | |
25 class Tracer(object): | |
26 def __init__(self, tracer, cmd, cwd_dir, progress): | |
27 # Constants | |
28 self.tracer = tracer | |
29 self.cmd = cmd[:] | |
30 self.cwd_dir = cwd_dir | |
31 self.progress = progress | |
32 | |
33 def map(self, test_case): | |
34 """Traces a single test case and returns its output.""" | |
35 cmd = self.cmd[:] | |
36 cmd.append('--gtest_filter=%s' % test_case) | |
37 tracename = test_case.replace('/', '-') | |
38 | |
39 out = [] | |
40 for retry in range(5): | |
41 start = time.time() | |
42 returncode, output = self.tracer.trace( | |
43 cmd, self.cwd_dir, tracename, True) | |
44 duration = time.time() - start | |
45 # TODO(maruel): Define a way to detect if an strace log is valid. | |
46 valid = True | |
47 out.append( | |
48 { | |
49 'test_case': test_case, | |
50 'returncode': returncode, | |
51 'duration': duration, | |
52 'valid': valid, | |
53 'output': output, | |
54 }) | |
55 logging.debug( | |
56 'Tracing %s done: %d, %.1fs' % (test_case, returncode, duration)) | |
57 if retry: | |
58 self.progress.update_item( | |
59 '%s - %d' % (test_case, retry), True, not valid) | |
60 else: | |
61 self.progress.update_item(test_case, True, not valid) | |
62 if valid: | |
63 break | |
64 return out | |
65 | |
66 | |
67 def trace_test_cases(cmd, cwd_dir, test_cases, jobs, logname): | |
68 """Traces test cases one by one.""" | |
69 assert os.path.isabs(cwd_dir) and os.path.isdir(cwd_dir) | |
70 | |
71 if not test_cases: | |
72 return 0 | |
73 | |
74 # Resolve any symlink. | |
75 cwd_dir = os.path.realpath(cwd_dir) | |
76 assert os.path.isdir(cwd_dir) | |
77 | |
78 progress = run_test_cases.Progress(len(test_cases)) | |
79 with run_test_cases.ThreadPool(jobs or multiprocessing.cpu_count()) as pool: | |
80 api = trace_inputs.get_api() | |
81 api.clean_trace(logname) | |
82 with api.get_tracer(logname) as tracer: | |
83 function = Tracer(tracer, cmd, cwd_dir, progress).map | |
84 for test_case in test_cases: | |
85 pool.add_task(function, test_case) | |
86 | |
87 pool.join(progress, 0.1) | |
88 print('') | |
89 return 0 | |
90 | |
91 | |
92 def main(): | |
93 """CLI frontend to validate arguments.""" | |
94 parser = run_test_cases.OptionParserTestCases( | |
95 usage='%prog <options> [gtest]', | |
96 description=sys.modules['__main__'].__doc__) | |
97 parser.format_description = lambda *_: parser.description | |
98 parser.add_option( | |
99 '-o', '--out', | |
100 help='output file, defaults to <executable>.test_cases') | |
101 options, args = parser.parse_args() | |
102 | |
103 if not args: | |
104 parser.error( | |
105 'Please provide the executable line to run, if you need fancy things ' | |
106 'like xvfb, start this script from *inside* xvfb, it\'ll be much faster' | |
107 '.') | |
108 | |
109 cmd = run_test_cases.fix_python_path(args) | |
110 | |
111 if not options.out: | |
112 options.out = '%s.test_cases' % cmd[-1] | |
113 | |
114 test_cases = parser.process_gtest_options(cmd, options) | |
115 | |
116 # Then run them. | |
117 return trace_test_cases( | |
118 cmd, | |
119 os.getcwd(), | |
120 test_cases, | |
121 options.jobs, | |
122 # TODO(maruel): options.timeout, | |
123 options.out) | |
124 | |
125 | |
126 if __name__ == '__main__': | |
127 sys.exit(main()) | |
OLD | NEW |