blob: 8e0a64322bbe3edae863fe7f04c94a100c373de2 [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Test result object"""
2
Benjamin Petersonb48af542010-04-11 20:43:16 +00003import io
4import sys
Benjamin Petersonbed7d042009-07-19 21:01:52 +00005import traceback
6
7from . import util
Benjamin Peterson8769fd82010-03-22 01:13:48 +00008from functools import wraps
Benjamin Petersonbed7d042009-07-19 21:01:52 +00009
Benjamin Petersondccc1fc2010-03-22 00:15:53 +000010__unittest = True
11
Benjamin Peterson8769fd82010-03-22 01:13:48 +000012def failfast(method):
13 @wraps(method)
14 def inner(self, *args, **kw):
15 if getattr(self, 'failfast', False):
16 self.stop()
17 return method(self, *args, **kw)
18 return inner
Benjamin Petersonbed7d042009-07-19 21:01:52 +000019
Benjamin Petersonb48af542010-04-11 20:43:16 +000020STDOUT_LINE = '\nStdout:\n%s'
21STDERR_LINE = '\nStderr:\n%s'
22
23
Benjamin Petersonbed7d042009-07-19 21:01:52 +000024class TestResult(object):
25 """Holder for test result information.
26
27 Test results are automatically managed by the TestCase and TestSuite
28 classes, and do not need to be explicitly manipulated by writers of tests.
29
30 Each instance holds the total number of tests run, and collections of
31 failures and errors that occurred among those test runs. The collections
32 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
33 formatted traceback of the error that occurred.
34 """
Benjamin Peterson847a4112010-03-14 15:04:17 +000035 _previousTestClass = None
Michael Foordbbea35f2010-11-01 21:09:03 +000036 _testRunEntered = False
Benjamin Peterson847a4112010-03-14 15:04:17 +000037 _moduleSetUpFailed = False
38 def __init__(self, stream=None, descriptions=None, verbosity=None):
Benjamin Peterson8769fd82010-03-22 01:13:48 +000039 self.failfast = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +000040 self.failures = []
41 self.errors = []
42 self.testsRun = 0
43 self.skipped = []
44 self.expectedFailures = []
45 self.unexpectedSuccesses = []
46 self.shouldStop = False
Benjamin Petersonb48af542010-04-11 20:43:16 +000047 self.buffer = False
48 self._stdout_buffer = None
49 self._stderr_buffer = None
50 self._original_stdout = sys.stdout
51 self._original_stderr = sys.stderr
52 self._mirrorOutput = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +000053
Benjamin Peterson847a4112010-03-14 15:04:17 +000054 def printErrors(self):
55 "Called by TestRunner after test run"
56
Benjamin Petersonbed7d042009-07-19 21:01:52 +000057 def startTest(self, test):
58 "Called when the given test is about to be run"
Michael Foord34c94622010-02-10 15:51:42 +000059 self.testsRun += 1
Benjamin Petersonb48af542010-04-11 20:43:16 +000060 self._mirrorOutput = False
Michael Foord42ec7cb2011-03-17 13:44:18 -040061 self._setupStdout()
62
63 def _setupStdout(self):
Benjamin Petersonb48af542010-04-11 20:43:16 +000064 if self.buffer:
65 if self._stderr_buffer is None:
66 self._stderr_buffer = io.StringIO()
67 self._stdout_buffer = io.StringIO()
68 sys.stdout = self._stdout_buffer
69 sys.stderr = self._stderr_buffer
Benjamin Petersonbed7d042009-07-19 21:01:52 +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 Foord34c94622010-02-10 15:51:42 +000078 """Called when the given test has been run"""
Michael Foord42ec7cb2011-03-17 13:44:18 -040079 self._restoreStdout()
80 self._mirrorOutput = False
81
82 def _restoreStdout(self):
Benjamin Petersonb48af542010-04-11 20:43:16 +000083 if self.buffer:
84 if self._mirrorOutput:
85 output = sys.stdout.getvalue()
86 error = sys.stderr.getvalue()
87 if output:
88 if not output.endswith('\n'):
89 output += '\n'
90 self._original_stdout.write(STDOUT_LINE % output)
91 if error:
92 if not error.endswith('\n'):
93 error += '\n'
94 self._original_stderr.write(STDERR_LINE % error)
95
96 sys.stdout = self._original_stdout
97 sys.stderr = self._original_stderr
98 self._stdout_buffer.seek(0)
99 self._stdout_buffer.truncate()
100 self._stderr_buffer.seek(0)
101 self._stderr_buffer.truncate()
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000102
103 def stopTestRun(self):
104 """Called once after all tests are executed.
105
106 See stopTest for a method called after each test.
107 """
108
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000109 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000110 def addError(self, test, err):
111 """Called when an error has occurred. 'err' is a tuple of values as
112 returned by sys.exc_info().
113 """
114 self.errors.append((test, self._exc_info_to_string(err, test)))
Benjamin Petersonb48af542010-04-11 20:43:16 +0000115 self._mirrorOutput = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000116
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000117 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000118 def addFailure(self, test, err):
119 """Called when an error has occurred. 'err' is a tuple of values as
120 returned by sys.exc_info()."""
121 self.failures.append((test, self._exc_info_to_string(err, test)))
Benjamin Petersonb48af542010-04-11 20:43:16 +0000122 self._mirrorOutput = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000123
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100124 def addSubTest(self, test, subtest, err):
125 """Called at the end of a subtest.
126 'err' is None if the subtest ended successfully, otherwise it's a
127 tuple of values as returned by sys.exc_info().
128 """
129 # By default, we don't do anything with successful subtests, but
130 # more sophisticated test results might want to record them.
131 if err is not None:
Antoine Pitrou18f22982014-11-23 15:55:11 +0100132 if getattr(self, 'failfast', False):
133 self.stop()
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100134 if issubclass(err[0], test.failureException):
135 errors = self.failures
136 else:
137 errors = self.errors
Antoine Pitrou22e162f2013-03-29 17:55:24 +0100138 errors.append((subtest, self._exc_info_to_string(err, test)))
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100139 self._mirrorOutput = True
140
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000141 def addSuccess(self, test):
142 "Called when a test has completed successfully"
143 pass
144
145 def addSkip(self, test, reason):
146 """Called when a test is skipped."""
147 self.skipped.append((test, reason))
148
149 def addExpectedFailure(self, test, err):
150 """Called when an expected failure/error occured."""
151 self.expectedFailures.append(
152 (test, self._exc_info_to_string(err, test)))
153
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000154 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000155 def addUnexpectedSuccess(self, test):
156 """Called when a test was expected to fail, but succeed."""
157 self.unexpectedSuccesses.append(test)
158
159 def wasSuccessful(self):
Gregory P. Smith5a6d4bf2014-01-20 01:11:18 -0800160 """Tells whether or not this result was a success."""
161 # The hasattr check is for test_result's OldResult test. That
162 # way this method works on objects that lack the attribute.
163 # (where would such result intances come from? old stored pickles?)
164 return ((len(self.failures) == len(self.errors) == 0) and
165 (not hasattr(self, 'unexpectedSuccesses') or
166 len(self.unexpectedSuccesses) == 0))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000167
168 def stop(self):
Gregory P. Smith5a6d4bf2014-01-20 01:11:18 -0800169 """Indicates that the tests should be aborted."""
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000170 self.shouldStop = True
171
172 def _exc_info_to_string(self, err, test):
173 """Converts a sys.exc_info()-style tuple of values into a string."""
174 exctype, value, tb = err
175 # Skip test runner traceback levels
176 while tb and self._is_relevant_tb_level(tb):
177 tb = tb.tb_next
Benjamin Petersonb48af542010-04-11 20:43:16 +0000178
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000179 if exctype is test.failureException:
180 # Skip assert*() traceback levels
181 length = self._count_relevant_tb_levels(tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000182 msgLines = traceback.format_exception(exctype, value, tb, length)
183 else:
Michael Foordd23ea062010-05-02 21:00:22 +0000184 msgLines = traceback.format_exception(exctype, value, tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000185
186 if self.buffer:
187 output = sys.stdout.getvalue()
188 error = sys.stderr.getvalue()
189 if output:
190 if not output.endswith('\n'):
191 output += '\n'
192 msgLines.append(STDOUT_LINE % output)
193 if error:
194 if not error.endswith('\n'):
195 error += '\n'
196 msgLines.append(STDERR_LINE % error)
197 return ''.join(msgLines)
198
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000199
200 def _is_relevant_tb_level(self, tb):
Benjamin Petersondccc1fc2010-03-22 00:15:53 +0000201 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000202
203 def _count_relevant_tb_levels(self, tb):
204 length = 0
205 while tb and not self._is_relevant_tb_level(tb):
206 length += 1
207 tb = tb.tb_next
208 return length
209
210 def __repr__(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000211 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000212 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Benjamin Peterson847a4112010-03-14 15:04:17 +0000213 len(self.failures)))