blob: b9045f4b07220fa4794110d04939307f2eedf751 [file] [log] [blame]
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001"""Test result object"""
2
Michael Foord5637f042010-04-02 21:42:47 +00003import os
4import sys
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00005import traceback
6
Michael Foord5637f042010-04-02 21:42:47 +00007from cStringIO import StringIO
8
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00009from . import util
Michael Foord1b9e9532010-03-22 01:01:34 +000010from functools import wraps
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000011
Michael Foordb1aa30f2010-03-22 00:06:30 +000012__unittest = True
13
Michael Foord1b9e9532010-03-22 01:01:34 +000014def 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
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000021
Michael Foord9b4ee122010-04-03 02:21:39 +000022STDOUT_LINE = '\nStdout:\n%s'
23STDERR_LINE = '\nStderr:\n%s'
Michael Foord5637f042010-04-02 21:42:47 +000024
25
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000026class 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 """
Michael Foord5ffa3252010-03-07 22:04:55 +000037 _previousTestClass = None
38 _moduleSetUpFailed = False
Michael Foordd99ef9a2010-02-23 17:00:53 +000039 def __init__(self, stream=None, descriptions=None, verbosity=None):
Michael Foord1b9e9532010-03-22 01:01:34 +000040 self.failfast = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000041 self.failures = []
42 self.errors = []
43 self.testsRun = 0
44 self.skipped = []
45 self.expectedFailures = []
46 self.unexpectedSuccesses = []
47 self.shouldStop = False
Michael Foord5637f042010-04-02 21:42:47 +000048 self.buffer = False
49 self._stdout_buffer = StringIO()
50 self._stderr_buffer = StringIO()
Michael Foord58c1e782010-04-02 22:08:29 +000051 self._original_stdout = sys.stdout
52 self._original_stderr = sys.stderr
Michael Foord5637f042010-04-02 21:42:47 +000053 self._mirrorOutput = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000054
Michael Foordd99ef9a2010-02-23 17:00:53 +000055 def printErrors(self):
56 "Called by TestRunner after test run"
57
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000058 def startTest(self, test):
59 "Called when the given test is about to be run"
Michael Foorddb43b5a2010-02-10 14:25:12 +000060 self.testsRun += 1
Michael Foord5637f042010-04-02 21:42:47 +000061 self._mirrorOutput = False
62 if self.buffer:
63 sys.stdout = self._stdout_buffer
64 sys.stderr = self._stderr_buffer
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000065
66 def startTestRun(self):
67 """Called once before any tests are executed.
68
69 See startTest for a method called before each test.
70 """
71
72 def stopTest(self, test):
Michael Foorddb43b5a2010-02-10 14:25:12 +000073 """Called when the given test has been run"""
Michael Foord5637f042010-04-02 21:42:47 +000074 if self.buffer:
75 if self._mirrorOutput:
76 output = sys.stdout.getvalue()
77 error = sys.stderr.getvalue()
78 if output:
Michael Foord9b4ee122010-04-03 02:21:39 +000079 if not output.endswith('\n'):
80 output += '\n'
Michael Foord58c1e782010-04-02 22:08:29 +000081 self._original_stdout.write(STDOUT_LINE % output)
Michael Foord5637f042010-04-02 21:42:47 +000082 if error:
Michael Foord9b4ee122010-04-03 02:21:39 +000083 if not error.endswith('\n'):
84 error += '\n'
Michael Foord58c1e782010-04-02 22:08:29 +000085 self._original_stderr.write(STDERR_LINE % error)
Michael Foord5637f042010-04-02 21:42:47 +000086
Michael Foord25d79762010-04-02 22:30:56 +000087 sys.stdout = self._original_stdout
88 sys.stderr = self._original_stderr
Michael Foord5637f042010-04-02 21:42:47 +000089 self._stdout_buffer.seek(0)
90 self._stdout_buffer.truncate()
91 self._stderr_buffer.seek(0)
92 self._stderr_buffer.truncate()
93 self._mirrorOutput = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000094
95 def stopTestRun(self):
96 """Called once after all tests are executed.
97
98 See stopTest for a method called after each test.
99 """
100
Michael Foord1b9e9532010-03-22 01:01:34 +0000101 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000102 def addError(self, test, err):
103 """Called when an error has occurred. 'err' is a tuple of values as
104 returned by sys.exc_info().
105 """
106 self.errors.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000107 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000108
Michael Foord1b9e9532010-03-22 01:01:34 +0000109 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000110 def addFailure(self, test, err):
111 """Called when an error has occurred. 'err' is a tuple of values as
112 returned by sys.exc_info()."""
113 self.failures.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000114 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000115
116 def addSuccess(self, test):
117 "Called when a test has completed successfully"
118 pass
119
120 def addSkip(self, test, reason):
121 """Called when a test is skipped."""
122 self.skipped.append((test, reason))
123
124 def addExpectedFailure(self, test, err):
125 """Called when an expected failure/error occured."""
126 self.expectedFailures.append(
127 (test, self._exc_info_to_string(err, test)))
128
Michael Foord1b9e9532010-03-22 01:01:34 +0000129 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000130 def addUnexpectedSuccess(self, test):
131 """Called when a test was expected to fail, but succeed."""
132 self.unexpectedSuccesses.append(test)
133
134 def wasSuccessful(self):
135 "Tells whether or not this result was a success"
136 return len(self.failures) == len(self.errors) == 0
137
138 def stop(self):
139 "Indicates that the tests should be aborted"
140 self.shouldStop = True
141
142 def _exc_info_to_string(self, err, test):
143 """Converts a sys.exc_info()-style tuple of values into a string."""
144 exctype, value, tb = err
145 # Skip test runner traceback levels
146 while tb and self._is_relevant_tb_level(tb):
147 tb = tb.tb_next
Michael Foord5637f042010-04-02 21:42:47 +0000148
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000149 if exctype is test.failureException:
150 # Skip assert*() traceback levels
151 length = self._count_relevant_tb_levels(tb)
Michael Foord5637f042010-04-02 21:42:47 +0000152 msgLines = traceback.format_exception(exctype, value, tb, length)
153 else:
154 msgLines = traceback.format_exception(exctype, value, tb)
155
156 if self.buffer:
157 output = sys.stdout.getvalue()
158 error = sys.stderr.getvalue()
159 if output:
Michael Foord9b4ee122010-04-03 02:21:39 +0000160 if not output.endswith('\n'):
161 output += '\n'
Michael Foord5637f042010-04-02 21:42:47 +0000162 msgLines.append(STDOUT_LINE % output)
163 if error:
Michael Foord9b4ee122010-04-03 02:21:39 +0000164 if not error.endswith('\n'):
165 error += '\n'
Michael Foord5637f042010-04-02 21:42:47 +0000166 msgLines.append(STDERR_LINE % error)
167 return ''.join(msgLines)
168
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000169
170 def _is_relevant_tb_level(self, tb):
Michael Foordb1aa30f2010-03-22 00:06:30 +0000171 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000172
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):
Michael Foordae3db0a2010-02-22 23:28:32 +0000181 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000182 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Michael Foordae3db0a2010-02-22 23:28:32 +0000183 len(self.failures)))