| 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 |