blob: 92b1f91a1479b13c2b28344ed861686ff0113ffd [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
37 _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
61 if self.buffer:
62 if self._stderr_buffer is None:
63 self._stderr_buffer = io.StringIO()
64 self._stdout_buffer = io.StringIO()
65 sys.stdout = self._stdout_buffer
66 sys.stderr = self._stderr_buffer
Benjamin Petersonbed7d042009-07-19 21:01:52 +000067
68 def startTestRun(self):
69 """Called once before any tests are executed.
70
71 See startTest for a method called before each test.
72 """
73
74 def stopTest(self, test):
Michael Foord34c94622010-02-10 15:51:42 +000075 """Called when the given test has been run"""
Benjamin Petersonb48af542010-04-11 20:43:16 +000076 if self.buffer:
77 if self._mirrorOutput:
78 output = sys.stdout.getvalue()
79 error = sys.stderr.getvalue()
80 if output:
81 if not output.endswith('\n'):
82 output += '\n'
83 self._original_stdout.write(STDOUT_LINE % output)
84 if error:
85 if not error.endswith('\n'):
86 error += '\n'
87 self._original_stderr.write(STDERR_LINE % error)
88
89 sys.stdout = self._original_stdout
90 sys.stderr = self._original_stderr
91 self._stdout_buffer.seek(0)
92 self._stdout_buffer.truncate()
93 self._stderr_buffer.seek(0)
94 self._stderr_buffer.truncate()
95 self._mirrorOutput = False
Benjamin Petersonbed7d042009-07-19 21:01:52 +000096
97 def stopTestRun(self):
98 """Called once after all tests are executed.
99
100 See stopTest for a method called after each test.
101 """
102
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000103 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000104 def addError(self, test, err):
105 """Called when an error has occurred. 'err' is a tuple of values as
106 returned by sys.exc_info().
107 """
108 self.errors.append((test, self._exc_info_to_string(err, test)))
Benjamin Petersonb48af542010-04-11 20:43:16 +0000109 self._mirrorOutput = True
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000110
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000111 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000112 def addFailure(self, test, err):
113 """Called when an error has occurred. 'err' is a tuple of values as
114 returned by sys.exc_info()."""
115 self.failures.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
118 def addSuccess(self, test):
119 "Called when a test has completed successfully"
120 pass
121
122 def addSkip(self, test, reason):
123 """Called when a test is skipped."""
124 self.skipped.append((test, reason))
125
126 def addExpectedFailure(self, test, err):
127 """Called when an expected failure/error occured."""
128 self.expectedFailures.append(
129 (test, self._exc_info_to_string(err, test)))
130
Benjamin Peterson8769fd82010-03-22 01:13:48 +0000131 @failfast
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000132 def addUnexpectedSuccess(self, test):
133 """Called when a test was expected to fail, but succeed."""
134 self.unexpectedSuccesses.append(test)
135
136 def wasSuccessful(self):
137 "Tells whether or not this result was a success"
138 return len(self.failures) == len(self.errors) == 0
139
140 def stop(self):
141 "Indicates that the tests should be aborted"
142 self.shouldStop = True
143
144 def _exc_info_to_string(self, err, test):
145 """Converts a sys.exc_info()-style tuple of values into a string."""
146 exctype, value, tb = err
147 # Skip test runner traceback levels
148 while tb and self._is_relevant_tb_level(tb):
149 tb = tb.tb_next
Benjamin Petersonb48af542010-04-11 20:43:16 +0000150
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000151 if exctype is test.failureException:
152 # Skip assert*() traceback levels
153 length = self._count_relevant_tb_levels(tb)
Benjamin Petersonb48af542010-04-11 20:43:16 +0000154 msgLines = traceback.format_exception(exctype, value, tb, length)
155 else:
156 chain = exctype is not None
157 msgLines = traceback.format_exception(exctype, value, tb,
158 chain=chain)
159
160 if self.buffer:
161 output = sys.stdout.getvalue()
162 error = sys.stderr.getvalue()
163 if output:
164 if not output.endswith('\n'):
165 output += '\n'
166 msgLines.append(STDOUT_LINE % output)
167 if error:
168 if not error.endswith('\n'):
169 error += '\n'
170 msgLines.append(STDERR_LINE % error)
171 return ''.join(msgLines)
172
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000173
174 def _is_relevant_tb_level(self, tb):
Benjamin Petersondccc1fc2010-03-22 00:15:53 +0000175 return '__unittest' in tb.tb_frame.f_globals
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000176
177 def _count_relevant_tb_levels(self, tb):
178 length = 0
179 while tb and not self._is_relevant_tb_level(tb):
180 length += 1
181 tb = tb.tb_next
182 return length
183
184 def __repr__(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000185 return ("<%s run=%i errors=%i failures=%i>" %
Benjamin Petersonbed7d042009-07-19 21:01:52 +0000186 (util.strclass(self.__class__), self.testsRun, len(self.errors),
Benjamin Peterson847a4112010-03-14 15:04:17 +0000187 len(self.failures)))