blob: 44bf1862e4f9e893bea404d293a8e3f9685df678 [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""Test result object"""
2
Benjamin Petersonb48af542010-04-11 20:43:16 +00003import os
4import io
5import sys
Benjamin Petersonbed7d042009-07-19 21:01:52 +00006import traceback
7
8from . import util
Benjamin Peterson8769fd82010-03-22 01:13:48 +00009from functools import wraps
Benjamin Petersonbed7d042009-07-19 21:01:52 +000010
Benjamin Petersondccc1fc2010-03-22 00:15:53 +000011__unittest = True
12
Benjamin Peterson8769fd82010-03-22 01:13:48 +000013def failfast(method):
14 @wraps(method)
15 def inner(self, *args, **kw):
16 if getattr(self, 'failfast', False):
17 self.stop()
18 return method(self, *args, **kw)
19 return inner
Benjamin Petersonbed7d042009-07-19 21:01:52 +000020
Benjamin Petersonb48af542010-04-11 20:43:16 +000021STDOUT_LINE = '\nStdout:\n%s'
22STDERR_LINE = '\nStderr:\n%s'
23
24
Benjamin Petersonbed7d042009-07-19 21:01:52 +000025class TestResult(object):
26 """Holder for test result information.
27
28 Test results are automatically managed by the TestCase and TestSuite
29 classes, and do not need to be explicitly manipulated by writers of tests.
30
31 Each instance holds the total number of tests run, and collections of
32 failures and errors that occurred among those test runs. The collections
33 contain tuples of (testcase, exceptioninfo), where exceptioninfo is the
34 formatted traceback of the error that occurred.
35 """
Benjamin Peterson847a4112010-03-14 15:04:17 +000036 _previousTestClass = None
Michael Foordbbea35f2010-11-01 21:09:03 +000037 _testRunEntered = False
Benjamin Peterson847a4112010-03-14 15:04:17 +000038 _moduleSetUpFailed = False
39 def __init__(self, stream=None, descriptions=None, verbosity=None):
Benjamin Peterson8769fd82010-03-22 01:13:48 +000040 self.failfast = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +000041 self.failures = []
42 self.errors = []
43 self.testsRun = 0
44 self.skipped = []
45 self.expectedFailures = []
46 self.unexpectedSuccesses = []
47 self.shouldStop = False
Benjamin Petersonb48af542010-04-11 20:43:16 +000048 self.buffer = False
49 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
125 def addSuccess(self, test):
126 "Called when a test has completed successfully"
127 pass
128
129 def addSkip(self, test, reason):
130 """Called when a test is skipped."""
131 self.skipped.append((test, reason))
132
133 def addExpectedFailure(self, test, err):
134 """Called when an expected failure/error occured."""
135 self.expectedFailures.append(
136 (test, self._exc_info_to_string(err, test)))
137
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000138 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000139 def addUnexpectedSuccess(self, test):
140 """Called when a test was expected to fail, but succeed."""
141 self.unexpectedSuccesses.append(test)
142
143 def wasSuccessful(self):
144 "Tells whether or not this result was a success"
145 return len(self.failures) == len(self.errors) == 0
146
147 def stop(self):
148 "Indicates that the tests should be aborted"
149 self.shouldStop = True
150
151 def _exc_info_to_string(self, err, test):
152 """Converts a sys.exc_info()-style tuple of values into a string."""
153 exctype, value, tb = err
154 # Skip test runner traceback levels
155 while tb and self._is_relevant_tb_level(tb):
156 tb = tb.tb_next
Benjamin Petersonb48af542010-04-11 20:43:16 +0000157
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000158 if exctype is test.failureException:
159 # Skip assert*() traceback levels
160 length = self._count_relevant_tb_levels(tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000161 msgLines = traceback.format_exception(exctype, value, tb, length)
162 else:
Michael Foordd23ea062010-05-02 21:00:22 +0000163 msgLines = traceback.format_exception(exctype, value, tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000164
165 if self.buffer:
166 output = sys.stdout.getvalue()
167 error = sys.stderr.getvalue()
168 if output:
169 if not output.endswith('\n'):
170 output += '\n'
171 msgLines.append(STDOUT_LINE % output)
172 if error:
173 if not error.endswith('\n'):
174 error += '\n'
175 msgLines.append(STDERR_LINE % error)
176 return ''.join(msgLines)
177
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000178
179 def _is_relevant_tb_level(self, tb):
Benjamin Petersondccc1fc2010-03-22 00:15:53 +0000180 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000181
182 def _count_relevant_tb_levels(self, tb):
183 length = 0
184 while tb and not self._is_relevant_tb_level(tb):
185 length += 1
186 tb = tb.tb_next
187 return length
188
189 def __repr__(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000190 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000191 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Benjamin Peterson847a4112010-03-14 15:04:17 +0000192 len(self.failures)))