1"""Test result object""" 2 3import sys 4import traceback 5import unittest 6 7from StringIO import StringIO 8 9from unittest2 import util 10from unittest2.compatibility import wraps 11 12__unittest = True 13 14def failfast(method): 15 @wraps(method) 16 def inner(self, *args, **kw): 17 if getattr(self, 'failfast', False): 18 self.stop() 19 return method(self, *args, **kw) 20 return inner 21 22 23STDOUT_LINE = '\nStdout:\n%s' 24STDERR_LINE = '\nStderr:\n%s' 25 26class TestResult(unittest.TestResult): 27 """Holder for test result information. 28 29 Test results are automatically managed by the TestCase and TestSuite 30 classes, and do not need to be explicitly manipulated by writers of tests. 31 32 Each instance holds the total number of tests run, and collections of 33 failures and errors that occurred among those test runs. The collections 34 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the 35 formatted traceback of the error that occurred. 36 """ 37 _previousTestClass = None 38 _moduleSetUpFailed = False 39 40 def __init__(self): 41 self.failfast = False 42 self.failures = [] 43 self.errors = [] 44 self.testsRun = 0 45 self.skipped = [] 46 self.expectedFailures = [] 47 self.unexpectedSuccesses = [] 48 self.shouldStop = False 49 self.buffer = False 50 self._stdout_buffer = None 51 self._stderr_buffer = None 52 self._original_stdout = sys.stdout 53 self._original_stderr = sys.stderr 54 self._mirrorOutput = False 55 56 def startTest(self, test): 57 "Called when the given test is about to be run" 58 self.testsRun += 1 59 self._mirrorOutput = False 60 if self.buffer: 61 if self._stderr_buffer is None: 62 self._stderr_buffer = StringIO() 63 self._stdout_buffer = StringIO() 64 sys.stdout = self._stdout_buffer 65 sys.stderr = self._stderr_buffer 66 67 def startTestRun(self): 68 """Called once before any tests are executed. 69 70 See startTest for a method called before each test. 71 """ 72 73 def stopTest(self, test): 74 """Called when the given test has been run""" 75 if self.buffer: 76 if self._mirrorOutput: 77 output = sys.stdout.getvalue() 78 error = sys.stderr.getvalue() 79 if output: 80 if not output.endswith('\n'): 81 output += '\n' 82 self._original_stdout.write(STDOUT_LINE % output) 83 if error: 84 if not error.endswith('\n'): 85 error += '\n' 86 self._original_stderr.write(STDERR_LINE % error) 87 88 sys.stdout = self._original_stdout 89 sys.stderr = self._original_stderr 90 self._stdout_buffer.seek(0) 91 self._stdout_buffer.truncate() 92 self._stderr_buffer.seek(0) 93 self._stderr_buffer.truncate() 94 self._mirrorOutput = False 95 96 97 def stopTestRun(self): 98 """Called once after all tests are executed. 99 100 See stopTest for a method called after each test. 101 """ 102 103 @failfast 104 def addError(self, test, err): 105 """Called when an error has occurred. 'err' is a tuple of values as 106 returned by sys.exc_info(). 107 """ 108 self.errors.append((test, self._exc_info_to_string(err, test))) 109 self._mirrorOutput = True 110 111 @failfast 112 def addFailure(self, test, err): 113 """Called when an error has occurred. 'err' is a tuple of values as 114 returned by sys.exc_info().""" 115 self.failures.append((test, self._exc_info_to_string(err, test))) 116 self._mirrorOutput = True 117 118 def addSuccess(self, test): 119 "Called when a test has completed successfully" 120 pass 121 122 def addSkip(self, test, reason): 123 """Called when a test is skipped.""" 124 self.skipped.append((test, reason)) 125 126 def addExpectedFailure(self, test, err, bugnumber): 127 """Called when an expected failure/error occured.""" 128 self.expectedFailures.append( 129 (test, self._exc_info_to_string(err, test))) 130 131 @failfast 132 def addUnexpectedSuccess(self, test, bugnumber): 133 """Called when a test was expected to fail, but succeed.""" 134 self.unexpectedSuccesses.append(test) 135 136 def wasSuccessful(self): 137 "Tells whether or not this result was a success" 138 return (len(self.failures) + len(self.errors) == 0) 139 140 def stop(self): 141 "Indicates that the tests should be aborted" 142 self.shouldStop = True 143 144 def _exc_info_to_string(self, err, test): 145 """Converts a sys.exc_info()-style tuple of values into a string.""" 146 exctype, value, tb = err 147 # Skip test runner traceback levels 148 while tb and self._is_relevant_tb_level(tb): 149 tb = tb.tb_next 150 if exctype is test.failureException: 151 # Skip assert*() traceback levels 152 length = self._count_relevant_tb_levels(tb) 153 msgLines = traceback.format_exception(exctype, value, tb, length) 154 else: 155 msgLines = traceback.format_exception(exctype, value, tb) 156 157 if self.buffer: 158 output = sys.stdout.getvalue() 159 error = sys.stderr.getvalue() 160 if output: 161 if not output.endswith('\n'): 162 output += '\n' 163 msgLines.append(STDOUT_LINE % output) 164 if error: 165 if not error.endswith('\n'): 166 error += '\n' 167 msgLines.append(STDERR_LINE % error) 168 return ''.join(msgLines) 169 170 def _is_relevant_tb_level(self, tb): 171 return '__unittest' in tb.tb_frame.f_globals 172 173 def _count_relevant_tb_levels(self, tb): 174 length = 0 175 while tb and not self._is_relevant_tb_level(tb): 176 length += 1 177 tb = tb.tb_next 178 return length 179 180 def __repr__(self): 181 return "<%s run=%i errors=%i failures=%i>" % \ 182 (util.strclass(self.__class__), self.testsRun, len(self.errors), 183 len(self.failures)) 184