Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
michael@0 | 2 | # Use of this source code is governed by a BSD-style license that can be |
michael@0 | 3 | # found in the LICENSE file. |
michael@0 | 4 | |
michael@0 | 5 | |
michael@0 | 6 | import json |
michael@0 | 7 | import logging |
michael@0 | 8 | import os |
michael@0 | 9 | import time |
michael@0 | 10 | import traceback |
michael@0 | 11 | |
michael@0 | 12 | import buildbot_report |
michael@0 | 13 | import constants |
michael@0 | 14 | |
michael@0 | 15 | |
michael@0 | 16 | class BaseTestResult(object): |
michael@0 | 17 | """A single result from a unit test.""" |
michael@0 | 18 | |
michael@0 | 19 | def __init__(self, name, log): |
michael@0 | 20 | self.name = name |
michael@0 | 21 | self.log = log.replace('\r', '') |
michael@0 | 22 | |
michael@0 | 23 | |
michael@0 | 24 | class SingleTestResult(BaseTestResult): |
michael@0 | 25 | """Result information for a single test. |
michael@0 | 26 | |
michael@0 | 27 | Args: |
michael@0 | 28 | full_name: Full name of the test. |
michael@0 | 29 | start_date: Date in milliseconds when the test began running. |
michael@0 | 30 | dur: Duration of the test run in milliseconds. |
michael@0 | 31 | log: An optional string listing any errors. |
michael@0 | 32 | """ |
michael@0 | 33 | |
michael@0 | 34 | def __init__(self, full_name, start_date, dur, log=''): |
michael@0 | 35 | BaseTestResult.__init__(self, full_name, log) |
michael@0 | 36 | name_pieces = full_name.rsplit('#') |
michael@0 | 37 | if len(name_pieces) > 1: |
michael@0 | 38 | self.test_name = name_pieces[1] |
michael@0 | 39 | self.class_name = name_pieces[0] |
michael@0 | 40 | else: |
michael@0 | 41 | self.class_name = full_name |
michael@0 | 42 | self.test_name = full_name |
michael@0 | 43 | self.start_date = start_date |
michael@0 | 44 | self.dur = dur |
michael@0 | 45 | |
michael@0 | 46 | |
michael@0 | 47 | class TestResults(object): |
michael@0 | 48 | """Results of a test run.""" |
michael@0 | 49 | |
michael@0 | 50 | def __init__(self): |
michael@0 | 51 | self.ok = [] |
michael@0 | 52 | self.failed = [] |
michael@0 | 53 | self.crashed = [] |
michael@0 | 54 | self.unknown = [] |
michael@0 | 55 | self.timed_out = False |
michael@0 | 56 | self.overall_fail = False |
michael@0 | 57 | |
michael@0 | 58 | @staticmethod |
michael@0 | 59 | def FromRun(ok=None, failed=None, crashed=None, timed_out=False, |
michael@0 | 60 | overall_fail=False): |
michael@0 | 61 | ret = TestResults() |
michael@0 | 62 | ret.ok = ok or [] |
michael@0 | 63 | ret.failed = failed or [] |
michael@0 | 64 | ret.crashed = crashed or [] |
michael@0 | 65 | ret.timed_out = timed_out |
michael@0 | 66 | ret.overall_fail = overall_fail |
michael@0 | 67 | return ret |
michael@0 | 68 | |
michael@0 | 69 | @staticmethod |
michael@0 | 70 | def FromTestResults(results): |
michael@0 | 71 | """Combines a list of results in a single TestResults object.""" |
michael@0 | 72 | ret = TestResults() |
michael@0 | 73 | for t in results: |
michael@0 | 74 | ret.ok += t.ok |
michael@0 | 75 | ret.failed += t.failed |
michael@0 | 76 | ret.crashed += t.crashed |
michael@0 | 77 | ret.unknown += t.unknown |
michael@0 | 78 | if t.timed_out: |
michael@0 | 79 | ret.timed_out = True |
michael@0 | 80 | if t.overall_fail: |
michael@0 | 81 | ret.overall_fail = True |
michael@0 | 82 | return ret |
michael@0 | 83 | |
michael@0 | 84 | @staticmethod |
michael@0 | 85 | def FromPythonException(test_name, start_date_ms, exc_info): |
michael@0 | 86 | """Constructs a TestResults with exception information for the given test. |
michael@0 | 87 | |
michael@0 | 88 | Args: |
michael@0 | 89 | test_name: name of the test which raised an exception. |
michael@0 | 90 | start_date_ms: the starting time for the test. |
michael@0 | 91 | exc_info: exception info, ostensibly from sys.exc_info(). |
michael@0 | 92 | |
michael@0 | 93 | Returns: |
michael@0 | 94 | A TestResults object with a SingleTestResult in the failed list. |
michael@0 | 95 | """ |
michael@0 | 96 | exc_type, exc_value, exc_traceback = exc_info |
michael@0 | 97 | trace_info = ''.join(traceback.format_exception(exc_type, exc_value, |
michael@0 | 98 | exc_traceback)) |
michael@0 | 99 | log_msg = 'Exception:\n' + trace_info |
michael@0 | 100 | duration_ms = (int(time.time()) * 1000) - start_date_ms |
michael@0 | 101 | |
michael@0 | 102 | exc_result = SingleTestResult( |
michael@0 | 103 | full_name='PythonWrapper#' + test_name, |
michael@0 | 104 | start_date=start_date_ms, |
michael@0 | 105 | dur=duration_ms, |
michael@0 | 106 | log=(str(exc_type) + ' ' + log_msg)) |
michael@0 | 107 | |
michael@0 | 108 | results = TestResults() |
michael@0 | 109 | results.failed.append(exc_result) |
michael@0 | 110 | return results |
michael@0 | 111 | |
michael@0 | 112 | def _Log(self, sorted_list): |
michael@0 | 113 | for t in sorted_list: |
michael@0 | 114 | logging.critical(t.name) |
michael@0 | 115 | if t.log: |
michael@0 | 116 | logging.critical(t.log) |
michael@0 | 117 | |
michael@0 | 118 | def GetAllBroken(self): |
michael@0 | 119 | """Returns the all broken tests including failed, crashed, unknown.""" |
michael@0 | 120 | return self.failed + self.crashed + self.unknown |
michael@0 | 121 | |
michael@0 | 122 | def LogFull(self, test_group, test_suite, build_type): |
michael@0 | 123 | """Output broken test logs, summarize in a log file and the test output.""" |
michael@0 | 124 | # Output all broken tests or 'passed' if none broken. |
michael@0 | 125 | logging.critical('*' * 80) |
michael@0 | 126 | logging.critical('Final result') |
michael@0 | 127 | if self.failed: |
michael@0 | 128 | logging.critical('Failed:') |
michael@0 | 129 | self._Log(sorted(self.failed)) |
michael@0 | 130 | if self.crashed: |
michael@0 | 131 | logging.critical('Crashed:') |
michael@0 | 132 | self._Log(sorted(self.crashed)) |
michael@0 | 133 | if self.unknown: |
michael@0 | 134 | logging.critical('Unknown:') |
michael@0 | 135 | self._Log(sorted(self.unknown)) |
michael@0 | 136 | if not self.GetAllBroken(): |
michael@0 | 137 | logging.critical('Passed') |
michael@0 | 138 | logging.critical('*' * 80) |
michael@0 | 139 | |
michael@0 | 140 | # Summarize in a log file, if tests are running on bots. |
michael@0 | 141 | if test_group and test_suite and os.environ.get('BUILDBOT_BUILDERNAME'): |
michael@0 | 142 | log_file_path = os.path.join(constants.CHROME_DIR, 'out', |
michael@0 | 143 | build_type, 'test_logs') |
michael@0 | 144 | if not os.path.exists(log_file_path): |
michael@0 | 145 | os.mkdir(log_file_path) |
michael@0 | 146 | full_file_name = os.path.join(log_file_path, test_group) |
michael@0 | 147 | if not os.path.exists(full_file_name): |
michael@0 | 148 | with open(full_file_name, 'w') as log_file: |
michael@0 | 149 | print >> log_file, '\n%s results for %s build %s:' % ( |
michael@0 | 150 | test_group, os.environ.get('BUILDBOT_BUILDERNAME'), |
michael@0 | 151 | os.environ.get('BUILDBOT_BUILDNUMBER')) |
michael@0 | 152 | log_contents = [' %s result : %d tests ran' % (test_suite, |
michael@0 | 153 | len(self.ok) + |
michael@0 | 154 | len(self.failed) + |
michael@0 | 155 | len(self.crashed) + |
michael@0 | 156 | len(self.unknown))] |
michael@0 | 157 | content_pairs = [('passed', len(self.ok)), ('failed', len(self.failed)), |
michael@0 | 158 | ('crashed', len(self.crashed))] |
michael@0 | 159 | for (result, count) in content_pairs: |
michael@0 | 160 | if count: |
michael@0 | 161 | log_contents.append(', %d tests %s' % (count, result)) |
michael@0 | 162 | with open(full_file_name, 'a') as log_file: |
michael@0 | 163 | print >> log_file, ''.join(log_contents) |
michael@0 | 164 | content = {'test_group': test_group, |
michael@0 | 165 | 'ok': [t.name for t in self.ok], |
michael@0 | 166 | 'failed': [t.name for t in self.failed], |
michael@0 | 167 | 'crashed': [t.name for t in self.failed], |
michael@0 | 168 | 'unknown': [t.name for t in self.unknown],} |
michael@0 | 169 | with open(os.path.join(log_file_path, 'results.json'), 'a') as json_file: |
michael@0 | 170 | print >> json_file, json.dumps(content) |
michael@0 | 171 | |
michael@0 | 172 | # Summarize in the test output. |
michael@0 | 173 | summary_string = 'Summary:\n' |
michael@0 | 174 | summary_string += 'RAN=%d\n' % (len(self.ok) + len(self.failed) + |
michael@0 | 175 | len(self.crashed) + len(self.unknown)) |
michael@0 | 176 | summary_string += 'PASSED=%d\n' % (len(self.ok)) |
michael@0 | 177 | summary_string += 'FAILED=%d %s\n' % (len(self.failed), |
michael@0 | 178 | [t.name for t in self.failed]) |
michael@0 | 179 | summary_string += 'CRASHED=%d %s\n' % (len(self.crashed), |
michael@0 | 180 | [t.name for t in self.crashed]) |
michael@0 | 181 | summary_string += 'UNKNOWN=%d %s\n' % (len(self.unknown), |
michael@0 | 182 | [t.name for t in self.unknown]) |
michael@0 | 183 | logging.critical(summary_string) |
michael@0 | 184 | return summary_string |
michael@0 | 185 | |
michael@0 | 186 | def PrintAnnotation(self): |
michael@0 | 187 | """Print buildbot annotations for test results.""" |
michael@0 | 188 | if self.timed_out: |
michael@0 | 189 | buildbot_report.PrintWarning() |
michael@0 | 190 | elif self.failed or self.crashed or self.overall_fail: |
michael@0 | 191 | buildbot_report.PrintError() |
michael@0 | 192 | else: |
michael@0 | 193 | print 'Step success!' # No annotation needed |