blob: 41993f9cf69afce0144dc720443fa536d5803598 [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 = []
Antoine Pitroub5c66f82013-12-28 20:37:58 +010023 self._removed_tests = 0
Benjamin Petersonbed7d042009-07-19 21:01:52 +000024 self.addTests(tests)
25
26 def __repr__(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000027 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000028
29 def __eq__(self, other):
30 if not isinstance(other, self.__class__):
31 return NotImplemented
32 return list(self) == list(other)
33
Benjamin Petersonbed7d042009-07-19 21:01:52 +000034 def __iter__(self):
35 return iter(self._tests)
36
37 def countTestCases(self):
Antoine Pitroub5c66f82013-12-28 20:37:58 +010038 cases = self._removed_tests
Benjamin Petersonbed7d042009-07-19 21:01:52 +000039 for test in self:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010040 if test:
41 cases += test.countTestCases()
Benjamin Petersonbed7d042009-07-19 21:01:52 +000042 return cases
43
44 def addTest(self, test):
45 # sanity checks
Florent Xicluna5d1155c2011-10-28 14:45:05 +020046 if not callable(test):
R. David Murray67d1bbd2010-01-29 17:55:47 +000047 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000048 if isinstance(test, type) and issubclass(test,
49 (case.TestCase, TestSuite)):
50 raise TypeError("TestCases and TestSuites must be instantiated "
51 "before passing them to addTest()")
52 self._tests.append(test)
53
54 def addTests(self, tests):
55 if isinstance(tests, str):
56 raise TypeError("tests must be an iterable of tests, not a string")
57 for test in tests:
58 self.addTest(test)
59
60 def run(self, result):
Andrew Svetloveb973682013-08-28 21:28:38 +030061 for index, test in enumerate(self):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000062 if result.shouldStop:
63 break
64 test(result)
Andrew Svetlov8913a6c2013-09-01 07:58:41 +030065 if self._cleanup:
66 self._removeTestAtIndex(index)
Benjamin Petersonbed7d042009-07-19 21:01:52 +000067 return result
68
Andrew Svetloveb973682013-08-28 21:28:38 +030069 def _removeTestAtIndex(self, index):
70 """Stop holding a reference to the TestCase at index."""
71 try:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010072 test = self._tests[index]
Andrew Svetloveb973682013-08-28 21:28:38 +030073 except TypeError:
Martin Pantere26da7c2016-06-02 10:07:09 +000074 # support for suite implementations that have overridden self._tests
Andrew Svetloveb973682013-08-28 21:28:38 +030075 pass
Antoine Pitroub5c66f82013-12-28 20:37:58 +010076 else:
77 # Some unittest tests add non TestCase/TestSuite objects to
78 # the suite.
79 if hasattr(test, 'countTestCases'):
80 self._removed_tests += test.countTestCases()
81 self._tests[index] = None
Andrew Svetloveb973682013-08-28 21:28:38 +030082
Benjamin Petersonbed7d042009-07-19 21:01:52 +000083 def __call__(self, *args, **kwds):
84 return self.run(*args, **kwds)
85
86 def debug(self):
87 """Run the tests without collecting errors in a TestResult"""
88 for test in self:
89 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000090
91
92class TestSuite(BaseTestSuite):
93 """A test suite is a composite test consisting of a number of TestCases.
94
95 For use, create an instance of TestSuite, then add test case instances.
96 When all tests have been added, the suite can be passed to a test
97 runner, such as TextTestRunner. It will run the individual test cases
98 in the order in which they were added, aggregating the results. When
99 subclassing, do not forget to call the base class constructor.
100 """
101
Michael Foordbbea35f2010-11-01 21:09:03 +0000102 def run(self, result, debug=False):
103 topLevel = False
104 if getattr(result, '_testRunEntered', False) is False:
105 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +0000106
Andrew Svetloveb973682013-08-28 21:28:38 +0300107 for index, test in enumerate(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000108 if result.shouldStop:
109 break
110
111 if _isnotsuite(test):
112 self._tearDownPreviousClass(test, result)
113 self._handleModuleFixture(test, result)
114 self._handleClassSetUp(test, result)
115 result._previousTestClass = test.__class__
116
117 if (getattr(test.__class__, '_classSetupFailed', False) or
118 getattr(result, '_moduleSetUpFailed', False)):
119 continue
120
Michael Foordbbea35f2010-11-01 21:09:03 +0000121 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000122 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000123 else:
124 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000125
Andrew Svetlov8913a6c2013-09-01 07:58:41 +0300126 if self._cleanup:
127 self._removeTestAtIndex(index)
Andrew Svetloveb973682013-08-28 21:28:38 +0300128
Michael Foordbbea35f2010-11-01 21:09:03 +0000129 if topLevel:
130 self._tearDownPreviousClass(None, result)
131 self._handleModuleTearDown(result)
Michael Foordcca5be22010-12-19 04:07:28 +0000132 result._testRunEntered = False
Michael Foordbbea35f2010-11-01 21:09:03 +0000133 return result
134
135 def debug(self):
136 """Run the tests without collecting errors in a TestResult"""
137 debug = _DebugResult()
138 self.run(debug, True)
139
140 ################################
141
Benjamin Peterson847a4112010-03-14 15:04:17 +0000142 def _handleClassSetUp(self, test, result):
143 previousClass = getattr(result, '_previousTestClass', None)
144 currentClass = test.__class__
145 if currentClass == previousClass:
146 return
147 if result._moduleSetUpFailed:
148 return
149 if getattr(currentClass, "__unittest_skip__", False):
150 return
151
Michael Foord73162192010-05-08 17:10:05 +0000152 try:
153 currentClass._classSetupFailed = False
154 except TypeError:
155 # test may actually be a function
156 # so its class will be a builtin-type
157 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000158
159 setUpClass = getattr(currentClass, 'setUpClass', None)
160 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400161 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000162 try:
163 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000164 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000165 if isinstance(result, _DebugResult):
166 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000167 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000168 className = util.strclass(currentClass)
Lisa Roach0f221d02018-11-08 18:34:33 -0800169 self._createClassOrModuleLevelException(result, e,
170 'setUpClass',
171 className)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400172 finally:
173 _call_if_exists(result, '_restoreStdout')
Lisa Roach0f221d02018-11-08 18:34:33 -0800174 if currentClass._classSetupFailed is True:
175 currentClass.doClassCleanups()
176 if len(currentClass.tearDown_exceptions) > 0:
177 for exc in currentClass.tearDown_exceptions:
178 self._createClassOrModuleLevelException(
179 result, exc[1], 'setUpClass', className,
180 info=exc)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000181
182 def _get_previous_module(self, result):
183 previousModule = None
184 previousClass = getattr(result, '_previousTestClass', None)
185 if previousClass is not None:
186 previousModule = previousClass.__module__
187 return previousModule
188
189
190 def _handleModuleFixture(self, test, result):
191 previousModule = self._get_previous_module(result)
192 currentModule = test.__class__.__module__
193 if currentModule == previousModule:
194 return
195
196 self._handleModuleTearDown(result)
197
198
199 result._moduleSetUpFailed = False
200 try:
201 module = sys.modules[currentModule]
202 except KeyError:
203 return
204 setUpModule = getattr(module, 'setUpModule', None)
205 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400206 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000207 try:
208 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000209 except Exception as e:
Lisa Roach0f221d02018-11-08 18:34:33 -0800210 try:
211 case.doModuleCleanups()
212 except Exception as exc:
213 self._createClassOrModuleLevelException(result, exc,
214 'setUpModule',
215 currentModule)
Michael Foordb8748742010-06-10 16:16:08 +0000216 if isinstance(result, _DebugResult):
217 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000218 result._moduleSetUpFailed = True
Lisa Roach0f221d02018-11-08 18:34:33 -0800219 self._createClassOrModuleLevelException(result, e,
220 'setUpModule',
221 currentModule)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400222 finally:
223 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000224
Lisa Roach0f221d02018-11-08 18:34:33 -0800225 def _createClassOrModuleLevelException(self, result, exc, method_name,
226 parent, info=None):
227 errorName = f'{method_name} ({parent})'
228 self._addClassOrModuleLevelException(result, exc, errorName, info)
229
230 def _addClassOrModuleLevelException(self, result, exception, errorName,
231 info=None):
Michael Foord520ed0a2010-06-05 21:12:23 +0000232 error = _ErrorHolder(errorName)
233 addSkip = getattr(result, 'addSkip', None)
234 if addSkip is not None and isinstance(exception, case.SkipTest):
235 addSkip(error, str(exception))
236 else:
Lisa Roach0f221d02018-11-08 18:34:33 -0800237 if not info:
238 result.addError(error, sys.exc_info())
239 else:
240 result.addError(error, info)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000241
242 def _handleModuleTearDown(self, result):
243 previousModule = self._get_previous_module(result)
244 if previousModule is None:
245 return
246 if result._moduleSetUpFailed:
247 return
248
249 try:
250 module = sys.modules[previousModule]
251 except KeyError:
252 return
253
254 tearDownModule = getattr(module, 'tearDownModule', None)
255 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400256 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000257 try:
258 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000259 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000260 if isinstance(result, _DebugResult):
261 raise
Lisa Roach0f221d02018-11-08 18:34:33 -0800262 self._createClassOrModuleLevelException(result, e,
263 'tearDownModule',
264 previousModule)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400265 finally:
266 _call_if_exists(result, '_restoreStdout')
Lisa Roach0f221d02018-11-08 18:34:33 -0800267 try:
268 case.doModuleCleanups()
269 except Exception as e:
270 self._createClassOrModuleLevelException(result, e,
271 'tearDownModule',
272 previousModule)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000273
274 def _tearDownPreviousClass(self, test, result):
275 previousClass = getattr(result, '_previousTestClass', None)
276 currentClass = test.__class__
277 if currentClass == previousClass:
278 return
279 if getattr(previousClass, '_classSetupFailed', False):
280 return
281 if getattr(result, '_moduleSetUpFailed', False):
282 return
283 if getattr(previousClass, "__unittest_skip__", False):
284 return
285
286 tearDownClass = getattr(previousClass, 'tearDownClass', None)
287 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400288 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000289 try:
290 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000291 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000292 if isinstance(result, _DebugResult):
293 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000294 className = util.strclass(previousClass)
Lisa Roach0f221d02018-11-08 18:34:33 -0800295 self._createClassOrModuleLevelException(result, e,
296 'tearDownClass',
297 className)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400298 finally:
299 _call_if_exists(result, '_restoreStdout')
Lisa Roach0f221d02018-11-08 18:34:33 -0800300 previousClass.doClassCleanups()
301 if len(previousClass.tearDown_exceptions) > 0:
302 for exc in previousClass.tearDown_exceptions:
303 className = util.strclass(previousClass)
304 self._createClassOrModuleLevelException(result, exc[1],
305 'tearDownClass',
306 className,
307 info=exc)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000308
309
310class _ErrorHolder(object):
311 """
312 Placeholder for a TestCase inside a result. As far as a TestResult
313 is concerned, this looks exactly like a unit test. Used to insert
314 arbitrary errors into a test suite run.
315 """
316 # Inspired by the ErrorHolder from Twisted:
317 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
318
319 # attribute used by TestResult._exc_info_to_string
320 failureException = None
321
322 def __init__(self, description):
323 self.description = description
324
325 def id(self):
326 return self.description
327
328 def shortDescription(self):
329 return None
330
331 def __repr__(self):
332 return "<ErrorHolder description=%r>" % (self.description,)
333
334 def __str__(self):
335 return self.id()
336
337 def run(self, result):
338 # could call result.addError(...) - but this test-like object
339 # shouldn't be run anyway
340 pass
341
342 def __call__(self, result):
343 return self.run(result)
344
345 def countTestCases(self):
346 return 0
347
348def _isnotsuite(test):
349 "A crude way to tell apart testcases and suites with duck-typing"
350 try:
351 iter(test)
352 except TypeError:
353 return True
354 return False
Michael Foordb8748742010-06-10 16:16:08 +0000355
356
357class _DebugResult(object):
358 "Used by the TestSuite to hold previous class when running in debug."
359 _previousTestClass = None
360 _moduleSetUpFailed = False
361 shouldStop = False