blob: 4997d81b8f30fbf7eb81459c096832807da3b3c3 [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
34 def __ne__(self, other):
35 return not self == other
36
37 def __iter__(self):
38 return iter(self._tests)
39
40 def countTestCases(self):
Antoine Pitroub5c66f82013-12-28 20:37:58 +010041 cases = self._removed_tests
Benjamin Petersonbed7d042009-07-19 21:01:52 +000042 for test in self:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010043 if test:
44 cases += test.countTestCases()
Benjamin Petersonbed7d042009-07-19 21:01:52 +000045 return cases
46
47 def addTest(self, test):
48 # sanity checks
Florent Xicluna5d1155c2011-10-28 14:45:05 +020049 if not callable(test):
R. David Murray67d1bbd2010-01-29 17:55:47 +000050 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000051 if isinstance(test, type) and issubclass(test,
52 (case.TestCase, TestSuite)):
53 raise TypeError("TestCases and TestSuites must be instantiated "
54 "before passing them to addTest()")
55 self._tests.append(test)
56
57 def addTests(self, tests):
58 if isinstance(tests, str):
59 raise TypeError("tests must be an iterable of tests, not a string")
60 for test in tests:
61 self.addTest(test)
62
63 def run(self, result):
Andrew Svetloveb973682013-08-28 21:28:38 +030064 for index, test in enumerate(self):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000065 if result.shouldStop:
66 break
67 test(result)
Andrew Svetlov8913a6c2013-09-01 07:58:41 +030068 if self._cleanup:
69 self._removeTestAtIndex(index)
Benjamin Petersonbed7d042009-07-19 21:01:52 +000070 return result
71
Andrew Svetloveb973682013-08-28 21:28:38 +030072 def _removeTestAtIndex(self, index):
73 """Stop holding a reference to the TestCase at index."""
74 try:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010075 test = self._tests[index]
Andrew Svetloveb973682013-08-28 21:28:38 +030076 except TypeError:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010077 # support for suite implementations that have overriden self._tests
Andrew Svetloveb973682013-08-28 21:28:38 +030078 pass
Antoine Pitroub5c66f82013-12-28 20:37:58 +010079 else:
80 # Some unittest tests add non TestCase/TestSuite objects to
81 # the suite.
82 if hasattr(test, 'countTestCases'):
83 self._removed_tests += test.countTestCases()
84 self._tests[index] = None
Andrew Svetloveb973682013-08-28 21:28:38 +030085
Benjamin Petersonbed7d042009-07-19 21:01:52 +000086 def __call__(self, *args, **kwds):
87 return self.run(*args, **kwds)
88
89 def debug(self):
90 """Run the tests without collecting errors in a TestResult"""
91 for test in self:
92 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000093
94
95class TestSuite(BaseTestSuite):
96 """A test suite is a composite test consisting of a number of TestCases.
97
98 For use, create an instance of TestSuite, then add test case instances.
99 When all tests have been added, the suite can be passed to a test
100 runner, such as TextTestRunner. It will run the individual test cases
101 in the order in which they were added, aggregating the results. When
102 subclassing, do not forget to call the base class constructor.
103 """
104
Michael Foordbbea35f2010-11-01 21:09:03 +0000105 def run(self, result, debug=False):
106 topLevel = False
107 if getattr(result, '_testRunEntered', False) is False:
108 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +0000109
Andrew Svetloveb973682013-08-28 21:28:38 +0300110 for index, test in enumerate(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000111 if result.shouldStop:
112 break
113
114 if _isnotsuite(test):
115 self._tearDownPreviousClass(test, result)
116 self._handleModuleFixture(test, result)
117 self._handleClassSetUp(test, result)
118 result._previousTestClass = test.__class__
119
120 if (getattr(test.__class__, '_classSetupFailed', False) or
121 getattr(result, '_moduleSetUpFailed', False)):
122 continue
123
Michael Foordbbea35f2010-11-01 21:09:03 +0000124 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000125 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000126 else:
127 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000128
Andrew Svetlov8913a6c2013-09-01 07:58:41 +0300129 if self._cleanup:
130 self._removeTestAtIndex(index)
Andrew Svetloveb973682013-08-28 21:28:38 +0300131
Michael Foordbbea35f2010-11-01 21:09:03 +0000132 if topLevel:
133 self._tearDownPreviousClass(None, result)
134 self._handleModuleTearDown(result)
Michael Foordcca5be22010-12-19 04:07:28 +0000135 result._testRunEntered = False
Michael Foordbbea35f2010-11-01 21:09:03 +0000136 return result
137
138 def debug(self):
139 """Run the tests without collecting errors in a TestResult"""
140 debug = _DebugResult()
141 self.run(debug, True)
142
143 ################################
144
Benjamin Peterson847a4112010-03-14 15:04:17 +0000145 def _handleClassSetUp(self, test, result):
146 previousClass = getattr(result, '_previousTestClass', None)
147 currentClass = test.__class__
148 if currentClass == previousClass:
149 return
150 if result._moduleSetUpFailed:
151 return
152 if getattr(currentClass, "__unittest_skip__", False):
153 return
154
Michael Foord73162192010-05-08 17:10:05 +0000155 try:
156 currentClass._classSetupFailed = False
157 except TypeError:
158 # test may actually be a function
159 # so its class will be a builtin-type
160 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000161
162 setUpClass = getattr(currentClass, 'setUpClass', None)
163 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400164 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000165 try:
166 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000167 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000168 if isinstance(result, _DebugResult):
169 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000170 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000171 className = util.strclass(currentClass)
172 errorName = 'setUpClass (%s)' % className
173 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400174 finally:
175 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000176
177 def _get_previous_module(self, result):
178 previousModule = None
179 previousClass = getattr(result, '_previousTestClass', None)
180 if previousClass is not None:
181 previousModule = previousClass.__module__
182 return previousModule
183
184
185 def _handleModuleFixture(self, test, result):
186 previousModule = self._get_previous_module(result)
187 currentModule = test.__class__.__module__
188 if currentModule == previousModule:
189 return
190
191 self._handleModuleTearDown(result)
192
193
194 result._moduleSetUpFailed = False
195 try:
196 module = sys.modules[currentModule]
197 except KeyError:
198 return
199 setUpModule = getattr(module, 'setUpModule', None)
200 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400201 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000202 try:
203 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000204 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000205 if isinstance(result, _DebugResult):
206 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000207 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000208 errorName = 'setUpModule (%s)' % currentModule
209 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400210 finally:
211 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000212
213 def _addClassOrModuleLevelException(self, result, exception, errorName):
214 error = _ErrorHolder(errorName)
215 addSkip = getattr(result, 'addSkip', None)
216 if addSkip is not None and isinstance(exception, case.SkipTest):
217 addSkip(error, str(exception))
218 else:
219 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000220
221 def _handleModuleTearDown(self, result):
222 previousModule = self._get_previous_module(result)
223 if previousModule is None:
224 return
225 if result._moduleSetUpFailed:
226 return
227
228 try:
229 module = sys.modules[previousModule]
230 except KeyError:
231 return
232
233 tearDownModule = getattr(module, 'tearDownModule', None)
234 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400235 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000236 try:
237 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000238 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000239 if isinstance(result, _DebugResult):
240 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000241 errorName = 'tearDownModule (%s)' % previousModule
242 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400243 finally:
244 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000245
246 def _tearDownPreviousClass(self, test, result):
247 previousClass = getattr(result, '_previousTestClass', None)
248 currentClass = test.__class__
249 if currentClass == previousClass:
250 return
251 if getattr(previousClass, '_classSetupFailed', False):
252 return
253 if getattr(result, '_moduleSetUpFailed', False):
254 return
255 if getattr(previousClass, "__unittest_skip__", False):
256 return
257
258 tearDownClass = getattr(previousClass, 'tearDownClass', None)
259 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400260 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000261 try:
262 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000263 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000264 if isinstance(result, _DebugResult):
265 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000266 className = util.strclass(previousClass)
267 errorName = 'tearDownClass (%s)' % className
268 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400269 finally:
270 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000271
272
273class _ErrorHolder(object):
274 """
275 Placeholder for a TestCase inside a result. As far as a TestResult
276 is concerned, this looks exactly like a unit test. Used to insert
277 arbitrary errors into a test suite run.
278 """
279 # Inspired by the ErrorHolder from Twisted:
280 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
281
282 # attribute used by TestResult._exc_info_to_string
283 failureException = None
284
285 def __init__(self, description):
286 self.description = description
287
288 def id(self):
289 return self.description
290
291 def shortDescription(self):
292 return None
293
294 def __repr__(self):
295 return "<ErrorHolder description=%r>" % (self.description,)
296
297 def __str__(self):
298 return self.id()
299
300 def run(self, result):
301 # could call result.addError(...) - but this test-like object
302 # shouldn't be run anyway
303 pass
304
305 def __call__(self, result):
306 return self.run(result)
307
308 def countTestCases(self):
309 return 0
310
311def _isnotsuite(test):
312 "A crude way to tell apart testcases and suites with duck-typing"
313 try:
314 iter(test)
315 except TypeError:
316 return True
317 return False
Michael Foordb8748742010-06-10 16:16:08 +0000318
319
320class _DebugResult(object):
321 "Used by the TestSuite to hold previous class when running in debug."
322 _previousTestClass = None
323 _moduleSetUpFailed = False
324 shouldStop = False