blob: c7e3206d749bd3425f99f55cb8bb8962231bd87b [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
Robert Collinsf0c819a2015-03-06 13:46:35 +130048 self.tb_locals = False
Benjamin Petersonb48af542010-04-11 20:43:16 +000049 self._stdout_buffer = None
50 self._stderr_buffer = None
51 self._original_stdout = sys.stdout
52 self._original_stderr = sys.stderr
53 self._mirrorOutput = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +000054
Benjamin Peterson847a4112010-03-14 15:04:17 +000055 def printErrors(self):
56 "Called by TestRunner after test run"
57
Benjamin Petersonbed7d042009-07-19 21:01:52 +000058 def startTest(self, test):
59 "Called when the given test is about to be run"
Michael Foord34c94622010-02-10 15:51:42 +000060 self.testsRun += 1
Benjamin Petersonb48af542010-04-11 20:43:16 +000061 self._mirrorOutput = False
Michael Foord42ec7cb2011-03-17 13:44:18 -040062 self._setupStdout()
63
64 def _setupStdout(self):
Benjamin Petersonb48af542010-04-11 20:43:16 +000065 if self.buffer:
66 if self._stderr_buffer is None:
67 self._stderr_buffer = io.StringIO()
68 self._stdout_buffer = io.StringIO()
69 sys.stdout = self._stdout_buffer
70 sys.stderr = self._stderr_buffer
Benjamin Petersonbed7d042009-07-19 21:01:52 +000071
72 def startTestRun(self):
73 """Called once before any tests are executed.
74
75 See startTest for a method called before each test.
76 """
77
78 def stopTest(self, test):
Michael Foord34c94622010-02-10 15:51:42 +000079 """Called when the given test has been run"""
Michael Foord42ec7cb2011-03-17 13:44:18 -040080 self._restoreStdout()
81 self._mirrorOutput = False
82
83 def _restoreStdout(self):
Benjamin Petersonb48af542010-04-11 20:43:16 +000084 if self.buffer:
85 if self._mirrorOutput:
86 output = sys.stdout.getvalue()
87 error = sys.stderr.getvalue()
88 if output:
89 if not output.endswith('\n'):
90 output += '\n'
91 self._original_stdout.write(STDOUT_LINE % output)
92 if error:
93 if not error.endswith('\n'):
94 error += '\n'
95 self._original_stderr.write(STDERR_LINE % error)
96
97 sys.stdout = self._original_stdout
98 sys.stderr = self._original_stderr
99 self._stdout_buffer.seek(0)
100 self._stdout_buffer.truncate()
101 self._stderr_buffer.seek(0)
102 self._stderr_buffer.truncate()
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000103
104 def stopTestRun(self):
105 """Called once after all tests are executed.
106
107 See stopTest for a method called after each test.
108 """
109
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000110 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000111 def addError(self, test, err):
112 """Called when an error has occurred. 'err' is a tuple of values as
113 returned by sys.exc_info().
114 """
115 self.errors.append((test, self._exc_info_to_string(err, test)))
Benjamin Petersonb48af542010-04-11 20:43:16 +0000116 self._mirrorOutput = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000117
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000118 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000119 def addFailure(self, test, err):
120 """Called when an error has occurred. 'err' is a tuple of values as
121 returned by sys.exc_info()."""
122 self.failures.append((test, self._exc_info_to_string(err, test)))
Benjamin Petersonb48af542010-04-11 20:43:16 +0000123 self._mirrorOutput = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000124
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100125 def addSubTest(self, test, subtest, err):
126 """Called at the end of a subtest.
127 'err' is None if the subtest ended successfully, otherwise it's a
128 tuple of values as returned by sys.exc_info().
129 """
130 # By default, we don't do anything with successful subtests, but
131 # more sophisticated test results might want to record them.
132 if err is not None:
Antoine Pitrou18f22982014-11-23 15:55:11 +0100133 if getattr(self, 'failfast', False):
134 self.stop()
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100135 if issubclass(err[0], test.failureException):
136 errors = self.failures
137 else:
138 errors = self.errors
Antoine Pitrou22e162f2013-03-29 17:55:24 +0100139 errors.append((subtest, self._exc_info_to_string(err, test)))
Antoine Pitrouc9b3ef22013-03-20 20:16:47 +0100140 self._mirrorOutput = True
141
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000142 def addSuccess(self, test):
143 "Called when a test has completed successfully"
144 pass
145
146 def addSkip(self, test, reason):
147 """Called when a test is skipped."""
148 self.skipped.append((test, reason))
149
150 def addExpectedFailure(self, test, err):
Martin Panter46f50722016-05-26 05:35:26 +0000151 """Called when an expected failure/error occurred."""
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000152 self.expectedFailures.append(
153 (test, self._exc_info_to_string(err, test)))
154
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000155 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000156 def addUnexpectedSuccess(self, test):
157 """Called when a test was expected to fail, but succeed."""
158 self.unexpectedSuccesses.append(test)
159
160 def wasSuccessful(self):
Gregory P. Smith5a6d4bf2014-01-20 01:11:18 -0800161 """Tells whether or not this result was a success."""
162 # The hasattr check is for test_result's OldResult test. That
163 # way this method works on objects that lack the attribute.
164 # (where would such result intances come from? old stored pickles?)
165 return ((len(self.failures) == len(self.errors) == 0) and
166 (not hasattr(self, 'unexpectedSuccesses') or
167 len(self.unexpectedSuccesses) == 0))
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000168
169 def stop(self):
Gregory P. Smith5a6d4bf2014-01-20 01:11:18 -0800170 """Indicates that the tests should be aborted."""
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000171 self.shouldStop = True
172
173 def _exc_info_to_string(self, err, test):
174 """Converts a sys.exc_info()-style tuple of values into a string."""
175 exctype, value, tb = err
176 # Skip test runner traceback levels
177 while tb and self._is_relevant_tb_level(tb):
178 tb = tb.tb_next
Benjamin Petersonb48af542010-04-11 20:43:16 +0000179
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000180 if exctype is test.failureException:
181 # Skip assert*() traceback levels
182 length = self._count_relevant_tb_levels(tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000183 else:
Robert Collinsf0c819a2015-03-06 13:46:35 +1300184 length = None
185 tb_e = traceback.TracebackException(
186 exctype, value, tb, limit=length, capture_locals=self.tb_locals)
187 msgLines = list(tb_e.format())
Benjamin Petersonb48af542010-04-11 20:43:16 +0000188
189 if self.buffer:
190 output = sys.stdout.getvalue()
191 error = sys.stderr.getvalue()
192 if output:
193 if not output.endswith('\n'):
194 output += '\n'
195 msgLines.append(STDOUT_LINE % output)
196 if error:
197 if not error.endswith('\n'):
198 error += '\n'
199 msgLines.append(STDERR_LINE % error)
200 return ''.join(msgLines)
201
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000202
203 def _is_relevant_tb_level(self, tb):
Benjamin Petersondccc1fc2010-03-22 00:15:53 +0000204 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000205
206 def _count_relevant_tb_levels(self, tb):
207 length = 0
208 while tb and not self._is_relevant_tb_level(tb):
209 length += 1
210 tb = tb.tb_next
211 return length
212
213 def __repr__(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000214 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000215 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Benjamin Peterson847a4112010-03-14 15:04:17 +0000216 len(self.failures)))