blob: cb6d41cfc1ad9f4a2feb5a8cb97b909beab34d7d [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 Foord5637f042010-04-02 21:42:47 +000022
23_std_out = sys.stdout
24_std_err = sys.stderr
25
26NEWLINE = os.linesep
27STDOUT_LINE = '%sStdout:%s%%s' % (NEWLINE, NEWLINE)
28STDERR_LINE = '%sStderr:%s%%s' % (NEWLINE, NEWLINE)
29
30
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000031class TestResult(object):
32 """Holder for test result information.
33
34 Test results are automatically managed by the TestCase and TestSuite
35 classes, and do not need to be explicitly manipulated by writers of tests.
36
37 Each instance holds the total number of tests run, and collections of
38 failures and errors that occurred among those test runs. The collections
39 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
40 formatted traceback of the error that occurred.
41 """
Michael Foord5ffa3252010-03-07 22:04:55 +000042 _previousTestClass = None
43 _moduleSetUpFailed = False
Michael Foordd99ef9a2010-02-23 17:00:53 +000044 def __init__(self, stream=None, descriptions=None, verbosity=None):
Michael Foord1b9e9532010-03-22 01:01:34 +000045 self.failfast = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000046 self.failures = []
47 self.errors = []
48 self.testsRun = 0
49 self.skipped = []
50 self.expectedFailures = []
51 self.unexpectedSuccesses = []
52 self.shouldStop = False
Michael Foord5637f042010-04-02 21:42:47 +000053 self.buffer = False
54 self._stdout_buffer = StringIO()
55 self._stderr_buffer = StringIO()
Michael Foord58c1e782010-04-02 22:08:29 +000056 self._original_stdout = sys.stdout
57 self._original_stderr = sys.stderr
Michael Foord5637f042010-04-02 21:42:47 +000058 self._mirrorOutput = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000059
Michael Foordd99ef9a2010-02-23 17:00:53 +000060 def printErrors(self):
61 "Called by TestRunner after test run"
62
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000063 def startTest(self, test):
64 "Called when the given test is about to be run"
Michael Foorddb43b5a2010-02-10 14:25:12 +000065 self.testsRun += 1
Michael Foord5637f042010-04-02 21:42:47 +000066 self._mirrorOutput = False
67 if self.buffer:
68 sys.stdout = self._stdout_buffer
69 sys.stderr = self._stderr_buffer
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000070
71 def startTestRun(self):
72 """Called once before any tests are executed.
73
74 See startTest for a method called before each test.
75 """
76
77 def stopTest(self, test):
Michael Foorddb43b5a2010-02-10 14:25:12 +000078 """Called when the given test has been run"""
Michael Foord5637f042010-04-02 21:42:47 +000079 if self.buffer:
80 if self._mirrorOutput:
81 output = sys.stdout.getvalue()
82 error = sys.stderr.getvalue()
83 if output:
84 if not output.endswith(NEWLINE):
85 output += NEWLINE
Michael Foord58c1e782010-04-02 22:08:29 +000086 self._original_stdout.write(STDOUT_LINE % output)
Michael Foord5637f042010-04-02 21:42:47 +000087 if error:
88 if not error.endswith(NEWLINE):
89 error += NEWLINE
Michael Foord58c1e782010-04-02 22:08:29 +000090 self._original_stderr.write(STDERR_LINE % error)
Michael Foord5637f042010-04-02 21:42:47 +000091
92 sys.stdout = _std_out
93 sys.stderr = _std_err
94 self._stdout_buffer.seek(0)
95 self._stdout_buffer.truncate()
96 self._stderr_buffer.seek(0)
97 self._stderr_buffer.truncate()
98 self._mirrorOutput = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000099
100 def stopTestRun(self):
101 """Called once after all tests are executed.
102
103 See stopTest for a method called after each test.
104 """
105
Michael Foord1b9e9532010-03-22 01:01:34 +0000106 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000107 def addError(self, test, err):
108 """Called when an error has occurred. 'err' is a tuple of values as
109 returned by sys.exc_info().
110 """
111 self.errors.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000112 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000113
Michael Foord1b9e9532010-03-22 01:01:34 +0000114 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000115 def addFailure(self, test, err):
116 """Called when an error has occurred. 'err' is a tuple of values as
117 returned by sys.exc_info()."""
118 self.failures.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000119 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000120
121 def addSuccess(self, test):
122 "Called when a test has completed successfully"
123 pass
124
125 def addSkip(self, test, reason):
126 """Called when a test is skipped."""
127 self.skipped.append((test, reason))
128
129 def addExpectedFailure(self, test, err):
130 """Called when an expected failure/error occured."""
131 self.expectedFailures.append(
132 (test, self._exc_info_to_string(err, test)))
133
Michael Foord1b9e9532010-03-22 01:01:34 +0000134 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000135 def addUnexpectedSuccess(self, test):
136 """Called when a test was expected to fail, but succeed."""
137 self.unexpectedSuccesses.append(test)
138
139 def wasSuccessful(self):
140 "Tells whether or not this result was a success"
141 return len(self.failures) == len(self.errors) == 0
142
143 def stop(self):
144 "Indicates that the tests should be aborted"
145 self.shouldStop = True
146
147 def _exc_info_to_string(self, err, test):
148 """Converts a sys.exc_info()-style tuple of values into a string."""
149 exctype, value, tb = err
150 # Skip test runner traceback levels
151 while tb and self._is_relevant_tb_level(tb):
152 tb = tb.tb_next
Michael Foord5637f042010-04-02 21:42:47 +0000153
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000154 if exctype is test.failureException:
155 # Skip assert*() traceback levels
156 length = self._count_relevant_tb_levels(tb)
Michael Foord5637f042010-04-02 21:42:47 +0000157 msgLines = traceback.format_exception(exctype, value, tb, length)
158 else:
159 msgLines = traceback.format_exception(exctype, value, tb)
160
161 if self.buffer:
162 output = sys.stdout.getvalue()
163 error = sys.stderr.getvalue()
164 if output:
165 if not output.endswith(NEWLINE):
166 output += NEWLINE
167 msgLines.append(STDOUT_LINE % output)
168 if error:
169 if not error.endswith(NEWLINE):
170 error += NEWLINE
171 msgLines.append(STDERR_LINE % error)
172 return ''.join(msgLines)
173
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000174
175 def _is_relevant_tb_level(self, tb):
Michael Foordb1aa30f2010-03-22 00:06:30 +0000176 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000177
178 def _count_relevant_tb_levels(self, tb):
179 length = 0
180 while tb and not self._is_relevant_tb_level(tb):
181 length += 1
182 tb = tb.tb_next
183 return length
184
185 def __repr__(self):
Michael Foordae3db0a2010-02-22 23:28:32 +0000186 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000187 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Michael Foordae3db0a2010-02-22 23:28:32 +0000188 len(self.failures)))