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

Side by Side Diff: fix_test_cases.py

Issue 19917006: Move all googletest related scripts into googletest/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/swarm_client
Patch Set: update README.py Created 7 years, 5 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
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 """Runs through isolate_test_cases.py all the tests cases in a google-test
7 executable, grabs the failures and traces them to generate a new .isolate.
8
9 This scripts requires a .isolated file. This file is generated from a .isolate
10 file. You can use 'GYP_DEFINES=test_isolation_mode=check ninja foo_test_run' to
11 generate it.
12 """
13
14 import json
15 import logging
16 import os
17 import subprocess
18 import sys
19 import tempfile
20
21 import isolate
22 import isolate_test_cases
23 import run_test_cases
24
25 ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
26
27
28 def with_tempfile(function):
29 """Creates a temporary file and calls the inner function."""
30 def hook(*args, **kwargs):
31 handle, tempfilepath = tempfile.mkstemp(prefix='fix_test_cases')
32 os.close(handle)
33 try:
34 return function(tempfilepath, *args, **kwargs)
35 finally:
36 try:
37 os.remove(tempfilepath)
38 except OSError, e:
39 print >> sys.stderr, 'Failed to remove %s: %s' % (tempfilepath, e)
40 return hook
41
42
43 def load_run_test_cases_results(run_test_cases_file):
44 """Loads a .run_test_cases result file.
45
46 Returns a tuple of two lists, (success, failures).
47 """
48 if not os.path.isfile(run_test_cases_file):
49 print >> sys.stderr, 'Failed to find %s' % run_test_cases_file
50 return None, None
51 with open(run_test_cases_file) as f:
52 try:
53 data = json.load(f)
54 except ValueError as e:
55 print >> sys.stderr, ('Unable to load json file, %s: %s' %
56 (run_test_cases_file, str(e)))
57 return None, None
58 failure = [
59 test for test, runs in data['test_cases'].iteritems()
60 if not any(run['returncode'] == 0 for run in runs)
61 ]
62 success = [
63 test for test, runs in data['test_cases'].iteritems()
64 if any(run['returncode'] == 0 for run in runs)
65 ]
66 return success, failure
67
68
69 def add_verbosity(cmd, verbosity):
70 """Adds --verbose flags to |cmd| depending on verbosity."""
71 if verbosity:
72 cmd.append('--verbose')
73 if verbosity > 1:
74 cmd.append('--verbose')
75
76
77 # This function requires 2 temporary files.
78 @with_tempfile
79 @with_tempfile
80 def run_tests(
81 tempfilepath_cases, tempfilepath_result, isolated, test_cases, verbosity):
82 """Runs all the test cases in an isolated environment."""
83 with open(tempfilepath_cases, 'w') as f:
84 f.write('\n'.join(test_cases))
85 cmd = [
86 sys.executable, os.path.join(ROOT_DIR, 'isolate.py'),
87 'run',
88 '--isolated', isolated,
89 ]
90 # Make sure isolate.py is verbose.
91 add_verbosity(cmd, verbosity)
92 cmd += [
93 '--',
94 # This assumes run_test_cases.py is used.
95 '--result', tempfilepath_result,
96 '--test-case-file', tempfilepath_cases,
97 # Do not retry; it's faster to trace flaky test than retrying each failing
98 # tests 3 times.
99 # Do not use --run-all, iterate multiple times instead.
100 '--retries', '0',
101 # Trace at most 25 test cases at a time. While this may seem particularly
102 # small, it is because the tracer doesn't scale well on Windows and tends to
103 # generate multi-gigabytes data files, that needs to be read N-times the
104 # number of test cases. On linux and OSX it's not that much a big deal but
105 # if there's 25 test cases that are broken, it's likely that there's a core
106 # file missing anyway.
107 '--max-failures', '25',
108 ]
109 # Make sure run_test_cases.py is verbose.
110 add_verbosity(cmd, verbosity)
111 logging.debug(cmd)
112 retcode = subprocess.call(cmd)
113 success, failures = load_run_test_cases_results(tempfilepath_result)
114 # Returning non-zero must match having failures.
115 assert bool(retcode) == (failures is None or bool(failures))
116 return success, failures
117
118
119 @with_tempfile
120 def trace_some(tempfilepath, isolated, test_cases, verbosity):
121 """Traces the test cases."""
122 with open(tempfilepath, 'w') as f:
123 f.write('\n'.join(test_cases))
124 cmd = [
125 sys.executable, os.path.join(ROOT_DIR, 'isolate_test_cases.py'),
126 '--isolated', isolated,
127 '--test-case-file', tempfilepath,
128 # Do not use --run-all here, we assume the test cases will pass inside the
129 # checkout.
130 ]
131 add_verbosity(cmd, verbosity)
132 logging.debug(cmd)
133 return subprocess.call(cmd)
134
135
136 def fix_all(isolated, all_test_cases, verbosity):
137 """Runs all the test cases in a gtest executable and trace the failing tests.
138
139 Returns True on success.
140
141 Makes sure the test passes afterward.
142 """
143 # These environment variables could have adverse side-effects.
144 # TODO(maruel): Be more intelligent about it, for now be safe.
145 blacklist = set(run_test_cases.KNOWN_GTEST_ENV_VARS) - set([
146 'GTEST_SHARD_INDEX', 'GTEST_TOTAL_SHARDS'])
147 for i in blacklist:
148 if i in os.environ:
149 print >> sys.stderr, 'Please unset %s' % i
150 return False
151
152 # Run until test cases remain to be tested.
153 remaining_test_cases = all_test_cases[:]
154 if not remaining_test_cases:
155 print >> sys.stderr, 'Didn\'t find any test case to run'
156 return 1
157
158 previous_failures = set()
159 had_failure = False
160 while remaining_test_cases:
161 # pylint is confused about with_tempfile.
162 # pylint: disable=E1120
163 print(
164 '\nTotal: %5d; Remaining: %5d' % (
165 len(all_test_cases), len(remaining_test_cases)))
166 success, failures = run_tests(isolated, remaining_test_cases, verbosity)
167 if success is None:
168 if had_failure:
169 print >> sys.stderr, 'Failed to run test cases'
170 return 1
171 # Maybe there's even enough things mapped to start the child process.
172 logging.info('Failed to run, trace one test case.')
173 had_failure = True
174 success = []
175 failures = [remaining_test_cases[0]]
176 print(
177 '\nTotal: %5d; Tried to run: %5d; Ran: %5d; Succeeded: %5d; Failed: %5d'
178 % (
179 len(all_test_cases),
180 len(remaining_test_cases),
181 len(success) + len(failures),
182 len(success),
183 len(failures)))
184
185 if not failures:
186 print('I\'m done. Have a nice day!')
187 return True
188
189 previous_failures.difference_update(success)
190 # If all the failures had already failed at least once.
191 if previous_failures.issuperset(failures):
192 print('The last trace didn\'t help, aborting.')
193 return False
194
195 # Test cases that passed to not need to be retried anymore.
196 remaining_test_cases = [
197 i for i in remaining_test_cases if i not in success and i not in failures
198 ]
199 # Make sure the failures at put at the end. This way if some tests fails
200 # simply because they are broken, and not because of test isolation, the
201 # other tests will still be traced.
202 remaining_test_cases.extend(failures)
203
204 # Trace the test cases and update the .isolate file.
205 print('\nTracing the %d failing tests.' % len(failures))
206 if trace_some(isolated, failures, verbosity):
207 logging.info('The tracing itself failed.')
208 return False
209 previous_failures.update(failures)
210
211
212 def main():
213 run_test_cases.run_isolated.disable_buffering()
214 parser = run_test_cases.OptionParserTestCases(
215 usage='%prog <options> -s <something.isolated>')
216 parser.add_option(
217 '-s', '--isolated',
218 help='The isolated file')
219 options, args = parser.parse_args()
220
221 if args:
222 parser.error('Unsupported arg: %s' % args)
223 isolate.parse_isolated_option(parser, options, os.getcwd(), True)
224
225 _, command, test_cases = isolate_test_cases.safely_load_isolated(
226 parser, options)
227 if not command:
228 parser.error('A command must be defined')
229 if not test_cases:
230 parser.error('No test case to run')
231 return not fix_all(options.isolated, test_cases, options.verbose)
232
233
234 if __name__ == '__main__':
235 sys.exit(main())
OLDNEW
« no previous file with comments | « README.py ('k') | googletest/README.py » ('j') | googletest/README.py » ('J')

Powered by Google App Engine
This is Rietveld 408576698