blob: 5cab30e688d458366237f70f9e4868a8a92116d4 [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 +000022NEWLINE = os.linesep
23STDOUT_LINE = '%sStdout:%s%%s' % (NEWLINE, NEWLINE)
24STDERR_LINE = '%sStderr:%s%%s' % (NEWLINE, NEWLINE)
25
26
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000027class TestResult(object):
28 """Holder for test result information.
29
30 Test results are automatically managed by the TestCase and TestSuite
31 classes, and do not need to be explicitly manipulated by writers of tests.
32
33 Each instance holds the total number of tests run, and collections of
34 failures and errors that occurred among those test runs. The collections
35 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
36 formatted traceback of the error that occurred.
37 """
Michael Foord5ffa3252010-03-07 22:04:55 +000038 _previousTestClass = None
39 _moduleSetUpFailed = False
Michael Foordd99ef9a2010-02-23 17:00:53 +000040 def __init__(self, stream=None, descriptions=None, verbosity=None):
Michael Foord1b9e9532010-03-22 01:01:34 +000041 self.failfast = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000042 self.failures = []
43 self.errors = []
44 self.testsRun = 0
45 self.skipped = []
46 self.expectedFailures = []
47 self.unexpectedSuccesses = []
48 self.shouldStop = False
Michael Foord5637f042010-04-02 21:42:47 +000049 self.buffer = False
50 self._stdout_buffer = StringIO()
51 self._stderr_buffer = StringIO()
Michael Foord58c1e782010-04-02 22:08:29 +000052 self._original_stdout = sys.stdout
53 self._original_stderr = sys.stderr
Michael Foord5637f042010-04-02 21:42:47 +000054 self._mirrorOutput = False
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000055
Michael Foordd99ef9a2010-02-23 17:00:53 +000056 def printErrors(self):
57 "Called by TestRunner after test run"
58
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000059 def startTest(self, test):
60 "Called when the given test is about to be run"
Michael Foorddb43b5a2010-02-10 14:25:12 +000061 self.testsRun += 1
Michael Foord5637f042010-04-02 21:42:47 +000062 self._mirrorOutput = False
63 if self.buffer:
64 sys.stdout = self._stdout_buffer
65 sys.stderr = self._stderr_buffer
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000066
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):
Michael Foorddb43b5a2010-02-10 14:25:12 +000074 """Called when the given test has been run"""
Michael Foord5637f042010-04-02 21:42:47 +000075 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(NEWLINE):
81 output += NEWLINE
Michael Foord58c1e782010-04-02 22:08:29 +000082 self._original_stdout.write(STDOUT_LINE % output)
Michael Foord5637f042010-04-02 21:42:47 +000083 if error:
84 if not error.endswith(NEWLINE):
85 error += NEWLINE
Michael Foord58c1e782010-04-02 22:08:29 +000086 self._original_stderr.write(STDERR_LINE % error)
Michael Foord5637f042010-04-02 21:42:47 +000087
Michael Foord25d79762010-04-02 22:30:56 +000088 sys.stdout = self._original_stdout
89 sys.stderr = self._original_stderr
Michael Foord5637f042010-04-02 21:42:47 +000090 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
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000095
96 def stopTestRun(self):
97 """Called once after all tests are executed.
98
99 See stopTest for a method called after each test.
100 """
101
Michael Foord1b9e9532010-03-22 01:01:34 +0000102 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000103 def addError(self, test, err):
104 """Called when an error has occurred. 'err' is a tuple of values as
105 returned by sys.exc_info().
106 """
107 self.errors.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000108 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000109
Michael Foord1b9e9532010-03-22 01:01:34 +0000110 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000111 def addFailure(self, test, err):
112 """Called when an error has occurred. 'err' is a tuple of values as
113 returned by sys.exc_info()."""
114 self.failures.append((test, self._exc_info_to_string(err, test)))
Michael Foord5637f042010-04-02 21:42:47 +0000115 self._mirrorOutput = True
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000116
117 def addSuccess(self, test):
118 "Called when a test has completed successfully"
119 pass
120
121 def addSkip(self, test, reason):
122 """Called when a test is skipped."""
123 self.skipped.append((test, reason))
124
125 def addExpectedFailure(self, test, err):
126 """Called when an expected failure/error occured."""
127 self.expectedFailures.append(
128 (test, self._exc_info_to_string(err, test)))
129
Michael Foord1b9e9532010-03-22 01:01:34 +0000130 @failfast
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000131 def addUnexpectedSuccess(self, test):
132 """Called when a test was expected to fail, but succeed."""
133 self.unexpectedSuccesses.append(test)
134
135 def wasSuccessful(self):
136 "Tells whether or not this result was a success"
137 return len(self.failures) == len(self.errors) == 0
138
139 def stop(self):
140 "Indicates that the tests should be aborted"
141 self.shouldStop = True
142
143 def _exc_info_to_string(self, err, test):
144 """Converts a sys.exc_info()-style tuple of values into a string."""
145 exctype, value, tb = err
146 # Skip test runner traceback levels
147 while tb and self._is_relevant_tb_level(tb):
148 tb = tb.tb_next
Michael Foord5637f042010-04-02 21:42:47 +0000149
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000150 if exctype is test.failureException:
151 # Skip assert*() traceback levels
152 length = self._count_relevant_tb_levels(tb)
Michael Foord5637f042010-04-02 21:42:47 +0000153 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(NEWLINE):
162 output += NEWLINE
163 msgLines.append(STDOUT_LINE % output)
164 if error:
165 if not error.endswith(NEWLINE):
166 error += NEWLINE
167 msgLines.append(STDERR_LINE % error)
168 return ''.join(msgLines)
169
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000170
171 def _is_relevant_tb_level(self, tb):
Michael Foordb1aa30f2010-03-22 00:06:30 +0000172 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000173
174 def _count_relevant_tb_levels(self, tb):
175 length = 0
176 while tb and not self._is_relevant_tb_level(tb):
177 length += 1
178 tb = tb.tb_next
179 return length
180
181 def __repr__(self):
Michael Foordae3db0a2010-02-22 23:28:32 +0000182 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +0000183 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Michael Foordae3db0a2010-02-22 23:28:32 +0000184 len(self.failures)))