blob: 6f45b6fe5f6039b94725b732e18eab5c01f4494b [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
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700152 failed = False
Michael Foord73162192010-05-08 17:10:05 +0000153 try:
154 currentClass._classSetupFailed = False
155 except TypeError:
156 # test may actually be a function
157 # so its class will be a builtin-type
158 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000159
160 setUpClass = getattr(currentClass, 'setUpClass', None)
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700161 doClassCleanups = getattr(currentClass, 'doClassCleanups', None)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000162 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400163 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000164 try:
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700165 try:
166 setUpClass()
167 except Exception as e:
168 if isinstance(result, _DebugResult):
169 raise
170 failed = True
171 try:
172 currentClass._classSetupFailed = True
173 except TypeError:
174 pass
175 className = util.strclass(currentClass)
176 self._createClassOrModuleLevelException(result, e,
177 'setUpClass',
178 className)
179 if failed and doClassCleanups is not None:
180 doClassCleanups()
181 for exc_info in currentClass.tearDown_exceptions:
182 self._createClassOrModuleLevelException(
183 result, exc_info[1], 'setUpClass', className,
184 info=exc_info)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400185 finally:
186 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000187
188 def _get_previous_module(self, result):
189 previousModule = None
190 previousClass = getattr(result, '_previousTestClass', None)
191 if previousClass is not None:
192 previousModule = previousClass.__module__
193 return previousModule
194
195
196 def _handleModuleFixture(self, test, result):
197 previousModule = self._get_previous_module(result)
198 currentModule = test.__class__.__module__
199 if currentModule == previousModule:
200 return
201
202 self._handleModuleTearDown(result)
203
204
205 result._moduleSetUpFailed = False
206 try:
207 module = sys.modules[currentModule]
208 except KeyError:
209 return
210 setUpModule = getattr(module, 'setUpModule', None)
211 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400212 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000213 try:
Lisa Roach0f221d02018-11-08 18:34:33 -0800214 try:
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700215 setUpModule()
216 except Exception as e:
217 if isinstance(result, _DebugResult):
218 raise
219 result._moduleSetUpFailed = True
220 self._createClassOrModuleLevelException(result, e,
Lisa Roach0f221d02018-11-08 18:34:33 -0800221 'setUpModule',
222 currentModule)
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700223 if result._moduleSetUpFailed:
224 try:
225 case.doModuleCleanups()
226 except Exception as e:
227 self._createClassOrModuleLevelException(result, e,
228 'setUpModule',
229 currentModule)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400230 finally:
231 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000232
Lisa Roach0f221d02018-11-08 18:34:33 -0800233 def _createClassOrModuleLevelException(self, result, exc, method_name,
234 parent, info=None):
235 errorName = f'{method_name} ({parent})'
236 self._addClassOrModuleLevelException(result, exc, errorName, info)
237
238 def _addClassOrModuleLevelException(self, result, exception, errorName,
239 info=None):
Michael Foord520ed0a2010-06-05 21:12:23 +0000240 error = _ErrorHolder(errorName)
241 addSkip = getattr(result, 'addSkip', None)
242 if addSkip is not None and isinstance(exception, case.SkipTest):
243 addSkip(error, str(exception))
244 else:
Lisa Roach0f221d02018-11-08 18:34:33 -0800245 if not info:
246 result.addError(error, sys.exc_info())
247 else:
248 result.addError(error, info)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000249
250 def _handleModuleTearDown(self, result):
251 previousModule = self._get_previous_module(result)
252 if previousModule is None:
253 return
254 if result._moduleSetUpFailed:
255 return
256
257 try:
258 module = sys.modules[previousModule]
259 except KeyError:
260 return
261
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700262 _call_if_exists(result, '_setupStdout')
263 try:
264 tearDownModule = getattr(module, 'tearDownModule', None)
265 if tearDownModule is not None:
266 try:
267 tearDownModule()
268 except Exception as e:
269 if isinstance(result, _DebugResult):
270 raise
271 self._createClassOrModuleLevelException(result, e,
272 'tearDownModule',
273 previousModule)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000274 try:
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700275 case.doModuleCleanups()
Michael Foord520ed0a2010-06-05 21:12:23 +0000276 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000277 if isinstance(result, _DebugResult):
278 raise
Lisa Roach0f221d02018-11-08 18:34:33 -0800279 self._createClassOrModuleLevelException(result, e,
280 'tearDownModule',
281 previousModule)
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700282 finally:
283 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000284
285 def _tearDownPreviousClass(self, test, result):
286 previousClass = getattr(result, '_previousTestClass', None)
287 currentClass = test.__class__
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700288 if currentClass == previousClass or previousClass is None:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000289 return
290 if getattr(previousClass, '_classSetupFailed', False):
291 return
292 if getattr(result, '_moduleSetUpFailed', False):
293 return
294 if getattr(previousClass, "__unittest_skip__", False):
295 return
296
297 tearDownClass = getattr(previousClass, 'tearDownClass', None)
Miss Islington (bot)d65fad02021-08-30 10:38:34 -0700298 doClassCleanups = getattr(previousClass, 'doClassCleanups', None)
299 if tearDownClass is None and doClassCleanups is None:
300 return
301
302 _call_if_exists(result, '_setupStdout')
303 try:
304 if tearDownClass is not None:
305 try:
306 tearDownClass()
307 except Exception as e:
308 if isinstance(result, _DebugResult):
309 raise
310 className = util.strclass(previousClass)
311 self._createClassOrModuleLevelException(result, e,
312 'tearDownClass',
313 className)
314 if doClassCleanups is not None:
315 doClassCleanups()
316 for exc_info in previousClass.tearDown_exceptions:
317 if isinstance(result, _DebugResult):
318 raise exc_info[1]
319 className = util.strclass(previousClass)
320 self._createClassOrModuleLevelException(result, exc_info[1],
321 'tearDownClass',
322 className,
323 info=exc_info)
324 finally:
325 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000326
327
328class _ErrorHolder(object):
329 """
330 Placeholder for a TestCase inside a result. As far as a TestResult
331 is concerned, this looks exactly like a unit test. Used to insert
332 arbitrary errors into a test suite run.
333 """
334 # Inspired by the ErrorHolder from Twisted:
335 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
336
337 # attribute used by TestResult._exc_info_to_string
338 failureException = None
339
340 def __init__(self, description):
341 self.description = description
342
343 def id(self):
344 return self.description
345
346 def shortDescription(self):
347 return None
348
349 def __repr__(self):
350 return "<ErrorHolder description=%r>" % (self.description,)
351
352 def __str__(self):
353 return self.id()
354
355 def run(self, result):
356 # could call result.addError(...) - but this test-like object
357 # shouldn't be run anyway
358 pass
359
360 def __call__(self, result):
361 return self.run(result)
362
363 def countTestCases(self):
364 return 0
365
366def _isnotsuite(test):
367 "A crude way to tell apart testcases and suites with duck-typing"
368 try:
369 iter(test)
370 except TypeError:
371 return True
372 return False
Michael Foordb8748742010-06-10 16:16:08 +0000373
374
375class _DebugResult(object):
376 "Used by the TestSuite to hold previous class when running in debug."
377 _previousTestClass = None
378 _moduleSetUpFailed = False
379 shouldStop = False