1 """Test result object""" 2 3 import os 4 import sys 5 import traceback 6 7 from StringIO import StringIO 8 9 from . import util 10 from functools import wraps 11 12 __unittest = True 13 14 def 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 STDOUT_LINE = '\nStdout:\n%s' 23 STDERR_LINE = '\nStderr:\n%s' 24 25 26 class TestResult(object): 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 _testRunEntered = False 39 _moduleSetUpFailed = False 40 def __init__(self, stream=None, descriptions=None, verbosity=None): 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 printErrors(self): 57 "Called by TestRunner after test run" 58 59 def startTest(self, test): 60 "Called when the given test is about to be run" 61 self.testsRun += 1 62 self._mirrorOutput = False 63 self._setupStdout() 64 65 def _setupStdout(self): 66 if self.buffer: 67 if self._stderr_buffer is None: 68 self._stderr_buffer = StringIO() 69 self._stdout_buffer = StringIO() 70 sys.stdout = self._stdout_buffer 71 sys.stderr = self._stderr_buffer 72 73 def startTestRun(self): 74 """Called once before any tests are executed. 75 76 See startTest for a method called before each test. 77 """ 78 79 def stopTest(self, test): 80 """Called when the given test has been run""" 81 self._restoreStdout() 82 self._mirrorOutput = False 83 84 def _restoreStdout(self): 85 if self.buffer: 86 if self._mirrorOutput: 87 output = sys.stdout.getvalue() 88 error = sys.stderr.getvalue() 89 if output: 90 if not output.endswith('\n'): 91 output += '\n' 92 self._original_stdout.write(STDOUT_LINE % output) 93 if error: 94 if not error.endswith('\n'): 95 error += '\n' 96 self._original_stderr.write(STDERR_LINE % error) 97 98 sys.stdout = self._original_stdout 99 sys.stderr = self._original_stderr 100 self._stdout_buffer.seek(0) 101 self._stdout_buffer.truncate() 102 self._stderr_buffer.seek(0) 103 self._stderr_buffer.truncate() 104 105 def stopTestRun(self): 106 """Called once after all tests are executed. 107 108 See stopTest for a method called after each test. 109 """ 110 111 @failfast 112 def addError(self, test, err): 113 """Called when an error has occurred. 'err' is a tuple of values as 114 returned by sys.exc_info(). 115 """ 116 self.errors.append((test, self._exc_info_to_string(err, test))) 117 self._mirrorOutput = True 118 119 @failfast 120 def addFailure(self, test, err): 121 """Called when an error has occurred. 'err' is a tuple of values as 122 returned by sys.exc_info().""" 123 self.failures.append((test, self._exc_info_to_string(err, test))) 124 self._mirrorOutput = True 125 126 def addSuccess(self, test): 127 "Called when a test has completed successfully" 128 pass 129 130 def addSkip(self, test, reason): 131 """Called when a test is skipped.""" 132 self.skipped.append((test, reason)) 133 134 def addExpectedFailure(self, test, err): 135 """Called when an expected failure/error occured.""" 136 self.expectedFailures.append( 137 (test, self._exc_info_to_string(err, test))) 138 139 @failfast 140 def addUnexpectedSuccess(self, test): 141 """Called when a test was expected to fail, but succeed.""" 142 self.unexpectedSuccesses.append(test) 143 144 def wasSuccessful(self): 145 "Tells whether or not this result was a success" 146 return len(self.failures) == len(self.errors) == 0 147 148 def stop(self): 149 "Indicates that the tests should be aborted" 150 self.shouldStop = True 151 152 def _exc_info_to_string(self, err, test): 153 """Converts a sys.exc_info()-style tuple of values into a string.""" 154 exctype, value, tb = err 155 # Skip test runner traceback levels 156 while tb and self._is_relevant_tb_level(tb): 157 tb = tb.tb_next 158 159 if exctype is test.failureException: 160 # Skip assert*() traceback levels 161 length = self._count_relevant_tb_levels(tb) 162 msgLines = traceback.format_exception(exctype, value, tb, length) 163 else: 164 msgLines = traceback.format_exception(exctype, value, tb) 165 166 if self.buffer: 167 output = sys.stdout.getvalue() 168 error = sys.stderr.getvalue() 169 if output: 170 if not output.endswith('\n'): 171 output += '\n' 172 msgLines.append(STDOUT_LINE % output) 173 if error: 174 if not error.endswith('\n'): 175 error += '\n' 176 msgLines.append(STDERR_LINE % error) 177 return ''.join(msgLines) 178 179 180 def _is_relevant_tb_level(self, tb): 181 return '__unittest' in tb.tb_frame.f_globals 182 183 def _count_relevant_tb_levels(self, tb): 184 length = 0 185 while tb and not self._is_relevant_tb_level(tb): 186 length += 1 187 tb = tb.tb_next 188 return length 189 190 def __repr__(self): 191 return ("<%s run=%i errors=%i failures=%i>" % 192 (util.strclass(self.__class__), self.testsRun, len(self.errors), 193 len(self.failures))) 194