blob: 76c472514e3f28d14e9e9546193d62a6a45d5b84 [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:
Antoine Pitroub5c66f82013-12-28 20:37:58 +010074 # support for suite implementations that have overriden 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)
169 errorName = 'setUpClass (%s)' % className
170 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400171 finally:
172 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000173
174 def _get_previous_module(self, result):
175 previousModule = None
176 previousClass = getattr(result, '_previousTestClass', None)
177 if previousClass is not None:
178 previousModule = previousClass.__module__
179 return previousModule
180
181
182 def _handleModuleFixture(self, test, result):
183 previousModule = self._get_previous_module(result)
184 currentModule = test.__class__.__module__
185 if currentModule == previousModule:
186 return
187
188 self._handleModuleTearDown(result)
189
190
191 result._moduleSetUpFailed = False
192 try:
193 module = sys.modules[currentModule]
194 except KeyError:
195 return
196 setUpModule = getattr(module, 'setUpModule', None)
197 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400198 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000199 try:
200 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000201 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000202 if isinstance(result, _DebugResult):
203 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000204 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000205 errorName = 'setUpModule (%s)' % currentModule
206 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400207 finally:
208 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000209
210 def _addClassOrModuleLevelException(self, result, exception, errorName):
211 error = _ErrorHolder(errorName)
212 addSkip = getattr(result, 'addSkip', None)
213 if addSkip is not None and isinstance(exception, case.SkipTest):
214 addSkip(error, str(exception))
215 else:
216 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000217
218 def _handleModuleTearDown(self, result):
219 previousModule = self._get_previous_module(result)
220 if previousModule is None:
221 return
222 if result._moduleSetUpFailed:
223 return
224
225 try:
226 module = sys.modules[previousModule]
227 except KeyError:
228 return
229
230 tearDownModule = getattr(module, 'tearDownModule', None)
231 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400232 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000233 try:
234 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000235 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000236 if isinstance(result, _DebugResult):
237 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000238 errorName = 'tearDownModule (%s)' % previousModule
239 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400240 finally:
241 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000242
243 def _tearDownPreviousClass(self, test, result):
244 previousClass = getattr(result, '_previousTestClass', None)
245 currentClass = test.__class__
246 if currentClass == previousClass:
247 return
248 if getattr(previousClass, '_classSetupFailed', False):
249 return
250 if getattr(result, '_moduleSetUpFailed', False):
251 return
252 if getattr(previousClass, "__unittest_skip__", False):
253 return
254
255 tearDownClass = getattr(previousClass, 'tearDownClass', None)
256 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400257 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000258 try:
259 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000260 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000261 if isinstance(result, _DebugResult):
262 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000263 className = util.strclass(previousClass)
264 errorName = 'tearDownClass (%s)' % className
265 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400266 finally:
267 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000268
269
270class _ErrorHolder(object):
271 """
272 Placeholder for a TestCase inside a result. As far as a TestResult
273 is concerned, this looks exactly like a unit test. Used to insert
274 arbitrary errors into a test suite run.
275 """
276 # Inspired by the ErrorHolder from Twisted:
277 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
278
279 # attribute used by TestResult._exc_info_to_string
280 failureException = None
281
282 def __init__(self, description):
283 self.description = description
284
285 def id(self):
286 return self.description
287
288 def shortDescription(self):
289 return None
290
291 def __repr__(self):
292 return "<ErrorHolder description=%r>" % (self.description,)
293
294 def __str__(self):
295 return self.id()
296
297 def run(self, result):
298 # could call result.addError(...) - but this test-like object
299 # shouldn't be run anyway
300 pass
301
302 def __call__(self, result):
303 return self.run(result)
304
305 def countTestCases(self):
306 return 0
307
308def _isnotsuite(test):
309 "A crude way to tell apart testcases and suites with duck-typing"
310 try:
311 iter(test)
312 except TypeError:
313 return True
314 return False
Michael Foordb8748742010-06-10 16:16:08 +0000315
316
317class _DebugResult(object):
318 "Used by the TestSuite to hold previous class when running in debug."
319 _previousTestClass = None
320 _moduleSetUpFailed = False
321 shouldStop = False