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

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

Issue 11045023: Move src/tools/isolate to src/tools/swarm_client as a DEPS. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Use r159961 Created 8 years, 2 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/README.py ('k') | tools/isolate/isolate.py » ('j') | 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 """Runs a test, grab the failures and trace them."""
7
8 import json
9 import os
10 import subprocess
11 import sys
12 import tempfile
13
14 import run_test_cases
15
16
17 XVFB_PATH = os.path.join('..', '..', 'testing', 'xvfb.py')
18
19
20 if sys.platform == 'win32':
21 import msvcrt # pylint: disable=F0401
22
23 def get_keyboard():
24 """Returns a letter from the keyboard if any.
25
26 This function returns immediately.
27 """
28 if msvcrt.kbhit():
29 return ord(msvcrt.getch())
30
31 else:
32 import select
33
34 def get_keyboard():
35 """Returns a letter from the keyboard if any, as soon as he pressed enter.
36
37 This function returns (almost) immediately.
38
39 The library doesn't give a way to just get the initial letter.
40 """
41 if select.select([sys.stdin], [], [], 0.00001)[0]:
42 return sys.stdin.read(1)
43
44
45 def trace_and_merge(result, test):
46 """Traces a single test case and merges the result back into .isolate."""
47 env = os.environ.copy()
48 env['RUN_TEST_CASES_RUN_ALL'] = '1'
49
50 print 'Starting trace of %s' % test
51 subprocess.call(
52 [
53 sys.executable, 'isolate.py', 'trace', '-r', result,
54 '--', '--gtest_filter=' + test,
55 ],
56 env=env)
57
58 print 'Starting merge of %s' % test
59 return not subprocess.call(
60 [sys.executable, 'isolate.py', 'merge', '-r', result])
61
62
63 def run_all(result, shard_index, shard_count):
64 """Runs all the tests. Returns the tests that failed or None on failure.
65
66 Assumes run_test_cases.py is implicitly called.
67 """
68 handle, result_file = tempfile.mkstemp(prefix='run_test_cases')
69 os.close(handle)
70 env = os.environ.copy()
71 env['RUN_TEST_CASES_RESULT_FILE'] = result_file
72 env['RUN_TEST_CASES_RUN_ALL'] = '1'
73 env['GTEST_SHARD_INDEX'] = str(shard_index)
74 env['GTEST_TOTAL_SHARDS'] = str(shard_count)
75 cmd = [sys.executable, 'isolate.py', 'run', '-r', result]
76 subprocess.call(cmd, env=env)
77 if not os.path.isfile(result_file):
78 print >> sys.stderr, 'Failed to find %s' % result_file
79 return None
80 with open(result_file) as f:
81 try:
82 data = json.load(f)
83 except ValueError as e:
84 print >> sys.stderr, ('Unable to load json file, %s: %s' %
85 (result_file, str(e)))
86 return None
87 os.remove(result_file)
88 return [
89 test for test, runs in data.iteritems()
90 if not any(not run['returncode'] for run in runs)
91 ]
92
93
94 def run(result, test):
95 """Runs a single test case in an isolated environment.
96
97 Returns True if the test passed.
98 """
99 return not subprocess.call([
100 sys.executable, 'isolate.py', 'run', '-r', result,
101 '--', '--gtest_filter=' + test,
102 ])
103
104
105 def run_normally(executable, test):
106 return not subprocess.call([
107 sys.executable, XVFB_PATH, os.path.dirname(executable), executable,
108 '--gtest_filter=' + test])
109
110
111 def diff_and_commit(test):
112 """Prints the diff and commit."""
113 subprocess.call(['git', 'diff'])
114 subprocess.call(['git', 'commit', '-a', '-m', test])
115
116
117 def trace_and_verify(result, test):
118 """Traces a test case, updates .isolate and makes sure it passes afterward.
119
120 Return None if the test was already passing, True on success.
121 """
122 trace_and_merge(result, test)
123 diff_and_commit(test)
124 print 'Verifying trace...'
125 return run(result, test)
126
127
128 def fix_all(result, shard_index, shard_count, executable):
129 """Runs all the test cases in a gtest executable and trace the failing tests.
130
131 Returns True on success.
132
133 Makes sure the test passes afterward.
134 """
135 # These could have adverse side-effects.
136 # TODO(maruel): Be more intelligent about it, for now be safe.
137 run_test_cases_env = ['RUN_TEST_CASES_RESULT_FILE', 'RUN_TEST_CASES_RUN_ALL']
138 for i in run_test_cases.KNOWN_GTEST_ENV_VARS + run_test_cases_env:
139 if i in os.environ:
140 print >> 'Please unset %s' % i
141 return False
142
143 test_cases = run_all(result, shard_index, shard_count)
144 if test_cases is None:
145 return False
146
147 print '\nFound %d broken test cases.' % len(test_cases)
148 if not test_cases:
149 return True
150
151 failed_alone = []
152 failures = []
153 fixed_tests = []
154 try:
155 for index, test_case in enumerate(test_cases):
156 if get_keyboard():
157 # Return early.
158 return True
159
160 try:
161 # Check if the test passes normally, because otherwise there is no
162 # reason to trace its failure.
163 if not run_normally(executable, test_case):
164 print '%s is broken when run alone, please fix the test.' % test_case
165 failed_alone.append(test_case)
166 continue
167
168 if not trace_and_verify(result, test_case):
169 failures.append(test_case)
170 print 'Failed to fix %s' % test_case
171 else:
172 fixed_tests.append(test_case)
173 except: # pylint: disable=W0702
174 failures.append(test_case)
175 print 'Failed to fix %s' % test_case
176 print '%d/%d' % (index+1, len(test_cases))
177 finally:
178 print 'Test cases fixed (%d):' % len(fixed_tests)
179 for fixed_test in fixed_tests:
180 print ' %s' % fixed_test
181 print ''
182
183 print 'Test cases still failing (%d):' % len(failures)
184 for failure in failures:
185 print ' %s' % failure
186
187 if failed_alone:
188 print ('Test cases that failed normally when run alone (%d):' %
189 len(failed_alone))
190 for failed in failed_alone:
191 print failed
192 return not failures
193
194
195 def main():
196 parser = run_test_cases.OptionParserWithTestSharding(
197 usage='%prog <option> [test]')
198 parser.add_option('-d', '--dir', default='../../out/Release',
199 help='The directory containing the the test executable and '
200 'result file. Defaults to %default')
201 options, args = parser.parse_args()
202
203 if len(args) != 1:
204 parser.error('Use with the name of the test only, e.g. unit_tests')
205
206 basename = args[0]
207 executable = os.path.join(options.dir, basename)
208 result = '%s.results' % executable
209 if sys.platform in('win32', 'cygwin'):
210 executable += '.exe'
211 if not os.path.isfile(executable):
212 print >> sys.stderr, (
213 '%s doesn\'t exist, please build %s_run' % (executable, basename))
214 return 1
215 if not os.path.isfile(result):
216 print >> sys.stderr, (
217 '%s doesn\'t exist, please build %s_run' % (result, basename))
218 return 1
219
220 return not fix_all(result, options.index, options.shards, executable)
221
222
223 if __name__ == '__main__':
224 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/isolate/README.py ('k') | tools/isolate/isolate.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698