blob: ca82765b9c72524ae9fd6810ce62629beafab6a8 [file] [log] [blame]
Benjamin Petersonbed7d042009-07-19 21:01:52 +00001"""TestSuite"""
2
Benjamin Peterson847a4112010-03-14 15:04:17 +00003import sys
4
Benjamin Petersonbed7d042009-07-19 21:01:52 +00005from . import case
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +00006from . import util
Benjamin Petersonbed7d042009-07-19 21:01:52 +00007
Benjamin Petersondccc1fc2010-03-22 00:15:53 +00008__unittest = True
9
Benjamin Petersonbed7d042009-07-19 21:01:52 +000010
Michael Foord42ec7cb2011-03-17 13:44:18 -040011def _call_if_exists(parent, attr):
12 func = getattr(parent, attr, lambda: None)
13 func()
14
15
Benjamin Peterson847a4112010-03-14 15:04:17 +000016class BaseTestSuite(object):
17 """A simple test suite that doesn't provide class or module shared fixtures.
Benjamin Petersonbed7d042009-07-19 21:01:52 +000018 """
Andrew Svetlov8913a6c2013-09-01 07:58:41 +030019 _cleanup = True
20
Benjamin Petersonbed7d042009-07-19 21:01:52 +000021 def __init__(self, tests=()):
22 self._tests = []
23 self.addTests(tests)
24
25 def __repr__(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000026 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000027
28 def __eq__(self, other):
29 if not isinstance(other, self.__class__):
30 return NotImplemented
31 return list(self) == list(other)
32
33 def __ne__(self, other):
34 return not self == other
35
36 def __iter__(self):
37 return iter(self._tests)
38
39 def countTestCases(self):
40 cases = 0
41 for test in self:
42 cases += test.countTestCases()
43 return cases
44
45 def addTest(self, test):
46 # sanity checks
Florent Xicluna5d1155c2011-10-28 14:45:05 +020047 if not callable(test):
R. David Murray67d1bbd2010-01-29 17:55:47 +000048 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000049 if isinstance(test, type) and issubclass(test,
50 (case.TestCase, TestSuite)):
51 raise TypeError("TestCases and TestSuites must be instantiated "
52 "before passing them to addTest()")
53 self._tests.append(test)
54
55 def addTests(self, tests):
56 if isinstance(tests, str):
57 raise TypeError("tests must be an iterable of tests, not a string")
58 for test in tests:
59 self.addTest(test)
60
61 def run(self, result):
Andrew Svetloveb973682013-08-28 21:28:38 +030062 for index, test in enumerate(self):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000063 if result.shouldStop:
64 break
65 test(result)
Andrew Svetlov8913a6c2013-09-01 07:58:41 +030066 if self._cleanup:
67 self._removeTestAtIndex(index)
Benjamin Petersonbed7d042009-07-19 21:01:52 +000068 return result
69
Andrew Svetloveb973682013-08-28 21:28:38 +030070 def _removeTestAtIndex(self, index):
71 """Stop holding a reference to the TestCase at index."""
72 try:
73 self._tests[index] = None
74 except TypeError:
75 # support for suite implementations that have overriden self._test
76 pass
77
Benjamin Petersonbed7d042009-07-19 21:01:52 +000078 def __call__(self, *args, **kwds):
79 return self.run(*args, **kwds)
80
81 def debug(self):
82 """Run the tests without collecting errors in a TestResult"""
83 for test in self:
84 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000085
86
87class TestSuite(BaseTestSuite):
88 """A test suite is a composite test consisting of a number of TestCases.
89
90 For use, create an instance of TestSuite, then add test case instances.
91 When all tests have been added, the suite can be passed to a test
92 runner, such as TextTestRunner. It will run the individual test cases
93 in the order in which they were added, aggregating the results. When
94 subclassing, do not forget to call the base class constructor.
95 """
96
Michael Foordbbea35f2010-11-01 21:09:03 +000097 def run(self, result, debug=False):
98 topLevel = False
99 if getattr(result, '_testRunEntered', False) is False:
100 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +0000101
Andrew Svetloveb973682013-08-28 21:28:38 +0300102 for index, test in enumerate(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000103 if result.shouldStop:
104 break
105
106 if _isnotsuite(test):
107 self._tearDownPreviousClass(test, result)
108 self._handleModuleFixture(test, result)
109 self._handleClassSetUp(test, result)
110 result._previousTestClass = test.__class__
111
112 if (getattr(test.__class__, '_classSetupFailed', False) or
113 getattr(result, '_moduleSetUpFailed', False)):
114 continue
115
Michael Foordbbea35f2010-11-01 21:09:03 +0000116 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000117 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000118 else:
119 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000120
Andrew Svetlov8913a6c2013-09-01 07:58:41 +0300121 if self._cleanup:
122 self._removeTestAtIndex(index)
Andrew Svetloveb973682013-08-28 21:28:38 +0300123
Michael Foordbbea35f2010-11-01 21:09:03 +0000124 if topLevel:
125 self._tearDownPreviousClass(None, result)
126 self._handleModuleTearDown(result)
Michael Foordcca5be22010-12-19 04:07:28 +0000127 result._testRunEntered = False
Michael Foordbbea35f2010-11-01 21:09:03 +0000128 return result
129
130 def debug(self):
131 """Run the tests without collecting errors in a TestResult"""
132 debug = _DebugResult()
133 self.run(debug, True)
134
135 ################################
136
Benjamin Peterson847a4112010-03-14 15:04:17 +0000137 def _handleClassSetUp(self, test, result):
138 previousClass = getattr(result, '_previousTestClass', None)
139 currentClass = test.__class__
140 if currentClass == previousClass:
141 return
142 if result._moduleSetUpFailed:
143 return
144 if getattr(currentClass, "__unittest_skip__", False):
145 return
146
Michael Foord73162192010-05-08 17:10:05 +0000147 try:
148 currentClass._classSetupFailed = False
149 except TypeError:
150 # test may actually be a function
151 # so its class will be a builtin-type
152 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000153
154 setUpClass = getattr(currentClass, 'setUpClass', None)
155 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400156 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000157 try:
158 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000159 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000160 if isinstance(result, _DebugResult):
161 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000162 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000163 className = util.strclass(currentClass)
164 errorName = 'setUpClass (%s)' % className
165 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400166 finally:
167 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000168
169 def _get_previous_module(self, result):
170 previousModule = None
171 previousClass = getattr(result, '_previousTestClass', None)
172 if previousClass is not None:
173 previousModule = previousClass.__module__
174 return previousModule
175
176
177 def _handleModuleFixture(self, test, result):
178 previousModule = self._get_previous_module(result)
179 currentModule = test.__class__.__module__
180 if currentModule == previousModule:
181 return
182
183 self._handleModuleTearDown(result)
184
185
186 result._moduleSetUpFailed = False
187 try:
188 module = sys.modules[currentModule]
189 except KeyError:
190 return
191 setUpModule = getattr(module, 'setUpModule', None)
192 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400193 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000194 try:
195 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000196 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000197 if isinstance(result, _DebugResult):
198 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000199 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000200 errorName = 'setUpModule (%s)' % currentModule
201 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400202 finally:
203 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000204
205 def _addClassOrModuleLevelException(self, result, exception, errorName):
206 error = _ErrorHolder(errorName)
207 addSkip = getattr(result, 'addSkip', None)
208 if addSkip is not None and isinstance(exception, case.SkipTest):
209 addSkip(error, str(exception))
210 else:
211 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000212
213 def _handleModuleTearDown(self, result):
214 previousModule = self._get_previous_module(result)
215 if previousModule is None:
216 return
217 if result._moduleSetUpFailed:
218 return
219
220 try:
221 module = sys.modules[previousModule]
222 except KeyError:
223 return
224
225 tearDownModule = getattr(module, 'tearDownModule', None)
226 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400227 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000228 try:
229 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000230 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000231 if isinstance(result, _DebugResult):
232 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000233 errorName = 'tearDownModule (%s)' % previousModule
234 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400235 finally:
236 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000237
238 def _tearDownPreviousClass(self, test, result):
239 previousClass = getattr(result, '_previousTestClass', None)
240 currentClass = test.__class__
241 if currentClass == previousClass:
242 return
243 if getattr(previousClass, '_classSetupFailed', False):
244 return
245 if getattr(result, '_moduleSetUpFailed', False):
246 return
247 if getattr(previousClass, "__unittest_skip__", False):
248 return
249
250 tearDownClass = getattr(previousClass, 'tearDownClass', None)
251 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400252 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000253 try:
254 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000255 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000256 if isinstance(result, _DebugResult):
257 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000258 className = util.strclass(previousClass)
259 errorName = 'tearDownClass (%s)' % className
260 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400261 finally:
262 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000263
264
265class _ErrorHolder(object):
266 """
267 Placeholder for a TestCase inside a result. As far as a TestResult
268 is concerned, this looks exactly like a unit test. Used to insert
269 arbitrary errors into a test suite run.
270 """
271 # Inspired by the ErrorHolder from Twisted:
272 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
273
274 # attribute used by TestResult._exc_info_to_string
275 failureException = None
276
277 def __init__(self, description):
278 self.description = description
279
280 def id(self):
281 return self.description
282
283 def shortDescription(self):
284 return None
285
286 def __repr__(self):
287 return "<ErrorHolder description=%r>" % (self.description,)
288
289 def __str__(self):
290 return self.id()
291
292 def run(self, result):
293 # could call result.addError(...) - but this test-like object
294 # shouldn't be run anyway
295 pass
296
297 def __call__(self, result):
298 return self.run(result)
299
300 def countTestCases(self):
301 return 0
302
303def _isnotsuite(test):
304 "A crude way to tell apart testcases and suites with duck-typing"
305 try:
306 iter(test)
307 except TypeError:
308 return True
309 return False
Michael Foordb8748742010-06-10 16:16:08 +0000310
311
312class _DebugResult(object):
313 "Used by the TestSuite to hold previous class when running in debug."
314 _previousTestClass = None
315 _moduleSetUpFailed = False
316 shouldStop = False