OLD | NEW |
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 | 5 |
6 import json | 6 import json |
7 import logging | 7 import logging |
8 import os | 8 import os |
9 import re | 9 import re |
10 import time | 10 import time |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 | 50 |
51 | 51 |
52 class TestResults(object): | 52 class TestResults(object): |
53 """Results of a test run.""" | 53 """Results of a test run.""" |
54 | 54 |
55 def __init__(self): | 55 def __init__(self): |
56 self.ok = [] | 56 self.ok = [] |
57 self.failed = [] | 57 self.failed = [] |
58 self.crashed = [] | 58 self.crashed = [] |
59 self.unknown = [] | 59 self.unknown = [] |
60 self.timed_out = False | 60 self.timed_out = [] |
| 61 self.overall_timed_out = False |
61 self.overall_fail = False | 62 self.overall_fail = False |
62 self.device_exception = None | 63 self.device_exception = None |
63 | 64 |
64 @staticmethod | 65 @staticmethod |
65 def FromRun(ok=None, failed=None, crashed=None, timed_out=False, | 66 def FromRun(ok=None, failed=None, crashed=None, timed_out=None, |
66 overall_fail=False, device_exception=None): | 67 overall_timed_out=False, overall_fail=False, |
| 68 device_exception=None): |
67 ret = TestResults() | 69 ret = TestResults() |
68 ret.ok = ok or [] | 70 ret.ok = ok or [] |
69 ret.failed = failed or [] | 71 ret.failed = failed or [] |
70 ret.crashed = crashed or [] | 72 ret.crashed = crashed or [] |
71 ret.timed_out = timed_out | 73 ret.timed_out = timed_out or [] |
| 74 ret.overall_timed_out = overall_timed_out |
72 ret.overall_fail = overall_fail | 75 ret.overall_fail = overall_fail |
73 ret.device_exception = device_exception | 76 ret.device_exception = device_exception |
74 return ret | 77 return ret |
75 | 78 |
76 @staticmethod | 79 @staticmethod |
77 def FromTestResults(results): | 80 def FromTestResults(results): |
78 """Combines a list of results in a single TestResults object.""" | 81 """Combines a list of results in a single TestResults object.""" |
79 ret = TestResults() | 82 ret = TestResults() |
80 for t in results: | 83 for t in results: |
81 ret.ok += t.ok | 84 ret.ok += t.ok |
82 ret.failed += t.failed | 85 ret.failed += t.failed |
83 ret.crashed += t.crashed | 86 ret.crashed += t.crashed |
84 ret.unknown += t.unknown | 87 ret.unknown += t.unknown |
85 if t.timed_out: | 88 ret.timed_out += t.timed_out |
86 ret.timed_out = True | 89 if t.overall_timed_out: |
| 90 ret.overall_timed_out = True |
87 if t.overall_fail: | 91 if t.overall_fail: |
88 ret.overall_fail = True | 92 ret.overall_fail = True |
89 return ret | 93 return ret |
90 | 94 |
91 @staticmethod | 95 @staticmethod |
92 def FromPythonException(test_name, start_date_ms, exc_info): | 96 def FromPythonException(test_name, start_date_ms, exc_info): |
93 """Constructs a TestResults with exception information for the given test. | 97 """Constructs a TestResults with exception information for the given test. |
94 | 98 |
95 Args: | 99 Args: |
96 test_name: name of the test which raised an exception. | 100 test_name: name of the test which raised an exception. |
(...skipping 23 matching lines...) Expand all Loading... |
120 def DeviceExceptions(results): | 124 def DeviceExceptions(results): |
121 return set(filter(lambda t: t.device_exception, results)) | 125 return set(filter(lambda t: t.device_exception, results)) |
122 | 126 |
123 def _Log(self, sorted_list): | 127 def _Log(self, sorted_list): |
124 for t in sorted_list: | 128 for t in sorted_list: |
125 logging.critical(t.name) | 129 logging.critical(t.name) |
126 if t.log: | 130 if t.log: |
127 logging.critical(t.log) | 131 logging.critical(t.log) |
128 | 132 |
129 def GetAllBroken(self): | 133 def GetAllBroken(self): |
130 """Returns the all broken tests including failed, crashed, unknown.""" | 134 """Returns the all broken tests.""" |
131 return self.failed + self.crashed + self.unknown | 135 return self.failed + self.crashed + self.unknown + self.timed_out |
132 | 136 |
133 def _LogToFile(self, test_type, test_suite, build_type): | 137 def _LogToFile(self, test_type, test_suite, build_type): |
134 """Log results to local files which can be used for aggregation later.""" | 138 """Log results to local files which can be used for aggregation later.""" |
135 # TODO(frankf): Report tests that failed to run here too. | 139 # TODO(frankf): Report tests that failed to run here too. |
136 log_file_path = os.path.join(constants.CHROME_DIR, 'out', | 140 log_file_path = os.path.join(constants.CHROME_DIR, 'out', |
137 build_type, 'test_logs') | 141 build_type, 'test_logs') |
138 if not os.path.exists(log_file_path): | 142 if not os.path.exists(log_file_path): |
139 os.mkdir(log_file_path) | 143 os.mkdir(log_file_path) |
140 full_file_name = os.path.join( | 144 full_file_name = os.path.join( |
141 log_file_path, re.sub('\W', '_', test_type).lower() + '.log') | 145 log_file_path, re.sub('\W', '_', test_type).lower() + '.log') |
142 if not os.path.exists(full_file_name): | 146 if not os.path.exists(full_file_name): |
143 with open(full_file_name, 'w') as log_file: | 147 with open(full_file_name, 'w') as log_file: |
144 print >> log_file, '\n%s results for %s build %s:' % ( | 148 print >> log_file, '\n%s results for %s build %s:' % ( |
145 test_type, os.environ.get('BUILDBOT_BUILDERNAME'), | 149 test_type, os.environ.get('BUILDBOT_BUILDERNAME'), |
146 os.environ.get('BUILDBOT_BUILDNUMBER')) | 150 os.environ.get('BUILDBOT_BUILDNUMBER')) |
147 logging.info('Writing results to %s.' % full_file_name) | 151 logging.info('Writing results to %s.' % full_file_name) |
148 log_contents = [' %s result : %d tests ran' % (test_suite, | 152 log_contents = [' %s result : %d tests ran' % (test_suite, |
149 len(self.ok) + | 153 len(self.ok) + |
150 len(self.failed) + | 154 len(self.failed) + |
151 len(self.crashed) + | 155 len(self.crashed) + |
| 156 len(self.timed_out) + |
152 len(self.unknown))] | 157 len(self.unknown))] |
153 content_pairs = [('passed', len(self.ok)), ('failed', len(self.failed)), | 158 content_pairs = [('passed', len(self.ok)), |
154 ('crashed', len(self.crashed))] | 159 ('failed', len(self.failed)), |
| 160 ('crashed', len(self.crashed)), |
| 161 ('timed_out', len(self.timed_out)), |
| 162 ('unknown', len(self.unknown))] |
155 for (result, count) in content_pairs: | 163 for (result, count) in content_pairs: |
156 if count: | 164 if count: |
157 log_contents.append(', %d tests %s' % (count, result)) | 165 log_contents.append(', %d tests %s' % (count, result)) |
158 with open(full_file_name, 'a') as log_file: | 166 with open(full_file_name, 'a') as log_file: |
159 print >> log_file, ''.join(log_contents) | 167 print >> log_file, ''.join(log_contents) |
160 logging.info('Writing results to %s.' % full_file_name) | 168 logging.info('Writing results to %s.' % full_file_name) |
161 content = {'test_group': test_type, | 169 content = {'test_group': test_type, |
162 'ok': [t.name for t in self.ok], | 170 'ok': [t.name for t in self.ok], |
163 'failed': [t.name for t in self.failed], | 171 'failed': [t.name for t in self.failed], |
164 'crashed': [t.name for t in self.failed], | 172 'crashed': [t.name for t in self.failed], |
| 173 'timed_out': [t.name for t in self.timed_out], |
165 'unknown': [t.name for t in self.unknown],} | 174 'unknown': [t.name for t in self.unknown],} |
166 json_file_path = os.path.join(log_file_path, 'results.json') | 175 json_file_path = os.path.join(log_file_path, 'results.json') |
167 with open(json_file_path, 'a') as json_file: | 176 with open(json_file_path, 'a') as json_file: |
168 print >> json_file, json.dumps(content) | 177 print >> json_file, json.dumps(content) |
169 logging.info('Writing results to %s.' % json_file_path) | 178 logging.info('Writing results to %s.' % json_file_path) |
170 | 179 |
171 def _LogToFlakinessDashboard(self, test_type, test_package, flakiness_server): | 180 def _LogToFlakinessDashboard(self, test_type, test_package, flakiness_server): |
172 """Upload results to the flakiness dashboard""" | 181 """Upload results to the flakiness dashboard""" |
173 logging.info('Upload results for test type "%s", test package "%s" to %s' % | 182 logging.info('Upload results for test type "%s", test package "%s" to %s' % |
174 (test_type, test_package, flakiness_server)) | 183 (test_type, test_package, flakiness_server)) |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 """ | 230 """ |
222 # Output all broken tests or 'passed' if none broken. | 231 # Output all broken tests or 'passed' if none broken. |
223 logging.critical('*' * 80) | 232 logging.critical('*' * 80) |
224 logging.critical('Final result:') | 233 logging.critical('Final result:') |
225 if self.failed: | 234 if self.failed: |
226 logging.critical('Failed:') | 235 logging.critical('Failed:') |
227 self._Log(sorted(self.failed)) | 236 self._Log(sorted(self.failed)) |
228 if self.crashed: | 237 if self.crashed: |
229 logging.critical('Crashed:') | 238 logging.critical('Crashed:') |
230 self._Log(sorted(self.crashed)) | 239 self._Log(sorted(self.crashed)) |
| 240 if self.timed_out: |
| 241 logging.critical('Timed out:') |
| 242 self._Log(sorted(self.timed_out)) |
231 if self.unknown: | 243 if self.unknown: |
232 logging.critical('Unknown:') | 244 logging.critical('Unknown:') |
233 self._Log(sorted(self.unknown)) | 245 self._Log(sorted(self.unknown)) |
234 if not self.GetAllBroken(): | 246 if not self.GetAllBroken(): |
235 logging.critical('Passed') | 247 logging.critical('Passed') |
236 | 248 |
237 # Summarize in the test output. | 249 # Summarize in the test output. |
238 logging.critical('*' * 80) | 250 logging.critical('*' * 80) |
239 summary = ['Summary:\n'] | 251 summary = ['Summary:\n'] |
240 if all_tests: | 252 if all_tests: |
241 summary += ['TESTS_TO_RUN=%d\n' % len(all_tests)] | 253 summary += ['TESTS_TO_RUN=%d\n' % len(all_tests)] |
242 num_tests_ran = (len(self.ok) + len(self.failed) + | 254 num_tests_ran = (len(self.ok) + len(self.failed) + |
243 len(self.crashed) + len(self.unknown)) | 255 len(self.crashed) + len(self.unknown) + |
| 256 len(self.timed_out)) |
244 tests_passed = [t.name for t in self.ok] | 257 tests_passed = [t.name for t in self.ok] |
245 tests_failed = [t.name for t in self.failed] | 258 tests_failed = [t.name for t in self.failed] |
246 tests_crashed = [t.name for t in self.crashed] | 259 tests_crashed = [t.name for t in self.crashed] |
247 tests_unknown = [t.name for t in self.unknown] | 260 tests_unknown = [t.name for t in self.unknown] |
| 261 tests_timed_out = [t.name for t in self.timed_out] |
248 summary += ['RAN=%d\n' % (num_tests_ran), | 262 summary += ['RAN=%d\n' % (num_tests_ran), |
249 'PASSED=%d\n' % len(tests_passed), | 263 'PASSED=%d\n' % len(tests_passed), |
250 'FAILED=%d %s\n' % (len(tests_failed), tests_failed), | 264 'FAILED=%d %s\n' % (len(tests_failed), tests_failed), |
251 'CRASHED=%d %s\n' % (len(tests_crashed), tests_crashed), | 265 'CRASHED=%d %s\n' % (len(tests_crashed), tests_crashed), |
| 266 'TIMEDOUT=%d %s\n' % (len(tests_timed_out), tests_timed_out), |
252 'UNKNOWN=%d %s\n' % (len(tests_unknown), tests_unknown)] | 267 'UNKNOWN=%d %s\n' % (len(tests_unknown), tests_unknown)] |
253 if all_tests and num_tests_ran != len(all_tests): | 268 if all_tests and num_tests_ran != len(all_tests): |
254 # Add the list of tests we failed to run. | 269 # Add the list of tests we failed to run. |
255 tests_failed_to_run = list(set(all_tests) - set(tests_passed) - | 270 tests_failed_to_run = list(set(all_tests) - set(tests_passed) - |
256 set(tests_failed) - set(tests_crashed) - | 271 set(tests_failed) - set(tests_crashed) - |
257 set(tests_unknown)) | 272 set(tests_unknown) - set(tests_timed_out)) |
258 summary += ['FAILED_TO_RUN=%d %s\n' % (len(tests_failed_to_run), | 273 summary += ['FAILED_TO_RUN=%d %s\n' % (len(tests_failed_to_run), |
259 tests_failed_to_run)] | 274 tests_failed_to_run)] |
260 summary_string = ''.join(summary) | 275 summary_string = ''.join(summary) |
261 logging.critical(summary_string) | 276 logging.critical(summary_string) |
262 logging.critical('*' * 80) | 277 logging.critical('*' * 80) |
263 | 278 |
264 if os.environ.get('BUILDBOT_BUILDERNAME'): | 279 if os.environ.get('BUILDBOT_BUILDERNAME'): |
265 # It is possible to have multiple buildbot steps for the same | 280 # It is possible to have multiple buildbot steps for the same |
266 # instrumenation test package using different annotations. | 281 # instrumenation test package using different annotations. |
267 if annotation and len(annotation) == 1: | 282 if annotation and len(annotation) == 1: |
268 test_suite = annotation[0] | 283 test_suite = annotation[0] |
269 else: | 284 else: |
270 test_suite = test_package | 285 test_suite = test_package |
271 self._LogToFile(test_type, test_suite, build_type) | 286 self._LogToFile(test_type, test_suite, build_type) |
272 | 287 |
273 if flakiness_server: | 288 if flakiness_server: |
274 self._LogToFlakinessDashboard(test_type, test_package, flakiness_server) | 289 self._LogToFlakinessDashboard(test_type, test_package, flakiness_server) |
275 | 290 |
276 def PrintAnnotation(self): | 291 def PrintAnnotation(self): |
277 """Print buildbot annotations for test results.""" | 292 """Print buildbot annotations for test results.""" |
278 if self.failed or self.crashed or self.overall_fail or self.timed_out: | 293 if (self.failed or self.crashed or self.overall_fail or |
| 294 self.overall_timed_out): |
279 buildbot_report.PrintError() | 295 buildbot_report.PrintError() |
280 else: | 296 else: |
281 print 'Step success!' # No annotation needed | 297 print 'Step success!' # No annotation needed |
OLD | NEW |