blob: 176af57fb0bdf396d6c35768a383cf214c77c010 [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 """
19 def __init__(self, tests=()):
20 self._tests = []
21 self.addTests(tests)
22
23 def __repr__(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000024 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000025
26 def __eq__(self, other):
27 if not isinstance(other, self.__class__):
28 return NotImplemented
29 return list(self) == list(other)
30
31 def __ne__(self, other):
32 return not self == other
33
34 def __iter__(self):
35 return iter(self._tests)
36
37 def countTestCases(self):
38 cases = 0
39 for test in self:
40 cases += test.countTestCases()
41 return cases
42
43 def addTest(self, test):
44 # sanity checks
Florent Xicluna5d1155c2011-10-28 14:45:05 +020045 if not callable(test):
R. David Murray67d1bbd2010-01-29 17:55:47 +000046 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000047 if isinstance(test, type) and issubclass(test,
48 (case.TestCase, TestSuite)):
49 raise TypeError("TestCases and TestSuites must be instantiated "
50 "before passing them to addTest()")
51 self._tests.append(test)
52
53 def addTests(self, tests):
54 if isinstance(tests, str):
55 raise TypeError("tests must be an iterable of tests, not a string")
56 for test in tests:
57 self.addTest(test)
58
59 def run(self, result):
Andrew Svetloveb973682013-08-28 21:28:38 +030060 for index, test in enumerate(self):
Benjamin Petersonbed7d042009-07-19 21:01:52 +000061 if result.shouldStop:
62 break
63 test(result)
Andrew Svetloveb973682013-08-28 21:28:38 +030064 self._removeTestAtIndex(index)
Benjamin Petersonbed7d042009-07-19 21:01:52 +000065 return result
66
Andrew Svetloveb973682013-08-28 21:28:38 +030067 def _removeTestAtIndex(self, index):
68 """Stop holding a reference to the TestCase at index."""
69 try:
70 self._tests[index] = None
71 except TypeError:
72 # support for suite implementations that have overriden self._test
73 pass
74
Benjamin Petersonbed7d042009-07-19 21:01:52 +000075 def __call__(self, *args, **kwds):
76 return self.run(*args, **kwds)
77
78 def debug(self):
79 """Run the tests without collecting errors in a TestResult"""
80 for test in self:
81 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000082
83
84class TestSuite(BaseTestSuite):
85 """A test suite is a composite test consisting of a number of TestCases.
86
87 For use, create an instance of TestSuite, then add test case instances.
88 When all tests have been added, the suite can be passed to a test
89 runner, such as TextTestRunner. It will run the individual test cases
90 in the order in which they were added, aggregating the results. When
91 subclassing, do not forget to call the base class constructor.
92 """
93
Michael Foordbbea35f2010-11-01 21:09:03 +000094 def run(self, result, debug=False):
95 topLevel = False
96 if getattr(result, '_testRunEntered', False) is False:
97 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +000098
Andrew Svetloveb973682013-08-28 21:28:38 +030099 for index, test in enumerate(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000100 if result.shouldStop:
101 break
102
103 if _isnotsuite(test):
104 self._tearDownPreviousClass(test, result)
105 self._handleModuleFixture(test, result)
106 self._handleClassSetUp(test, result)
107 result._previousTestClass = test.__class__
108
109 if (getattr(test.__class__, '_classSetupFailed', False) or
110 getattr(result, '_moduleSetUpFailed', False)):
111 continue
112
Michael Foordbbea35f2010-11-01 21:09:03 +0000113 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000114 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000115 else:
116 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000117
Andrew Svetloveb973682013-08-28 21:28:38 +0300118 self._removeTestAtIndex(index)
119
Michael Foordbbea35f2010-11-01 21:09:03 +0000120 if topLevel:
121 self._tearDownPreviousClass(None, result)
122 self._handleModuleTearDown(result)
Michael Foordcca5be22010-12-19 04:07:28 +0000123 result._testRunEntered = False
Michael Foordbbea35f2010-11-01 21:09:03 +0000124 return result
125
126 def debug(self):
127 """Run the tests without collecting errors in a TestResult"""
128 debug = _DebugResult()
129 self.run(debug, True)
130
131 ################################
132
Benjamin Peterson847a4112010-03-14 15:04:17 +0000133 def _handleClassSetUp(self, test, result):
134 previousClass = getattr(result, '_previousTestClass', None)
135 currentClass = test.__class__
136 if currentClass == previousClass:
137 return
138 if result._moduleSetUpFailed:
139 return
140 if getattr(currentClass, "__unittest_skip__", False):
141 return
142
Michael Foord73162192010-05-08 17:10:05 +0000143 try:
144 currentClass._classSetupFailed = False
145 except TypeError:
146 # test may actually be a function
147 # so its class will be a builtin-type
148 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000149
150 setUpClass = getattr(currentClass, 'setUpClass', None)
151 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400152 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000153 try:
154 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000155 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000156 if isinstance(result, _DebugResult):
157 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000158 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000159 className = util.strclass(currentClass)
160 errorName = 'setUpClass (%s)' % className
161 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400162 finally:
163 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000164
165 def _get_previous_module(self, result):
166 previousModule = None
167 previousClass = getattr(result, '_previousTestClass', None)
168 if previousClass is not None:
169 previousModule = previousClass.__module__
170 return previousModule
171
172
173 def _handleModuleFixture(self, test, result):
174 previousModule = self._get_previous_module(result)
175 currentModule = test.__class__.__module__
176 if currentModule == previousModule:
177 return
178
179 self._handleModuleTearDown(result)
180
181
182 result._moduleSetUpFailed = False
183 try:
184 module = sys.modules[currentModule]
185 except KeyError:
186 return
187 setUpModule = getattr(module, 'setUpModule', None)
188 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400189 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000190 try:
191 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000192 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000193 if isinstance(result, _DebugResult):
194 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000195 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000196 errorName = 'setUpModule (%s)' % currentModule
197 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400198 finally:
199 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000200
201 def _addClassOrModuleLevelException(self, result, exception, errorName):
202 error = _ErrorHolder(errorName)
203 addSkip = getattr(result, 'addSkip', None)
204 if addSkip is not None and isinstance(exception, case.SkipTest):
205 addSkip(error, str(exception))
206 else:
207 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000208
209 def _handleModuleTearDown(self, result):
210 previousModule = self._get_previous_module(result)
211 if previousModule is None:
212 return
213 if result._moduleSetUpFailed:
214 return
215
216 try:
217 module = sys.modules[previousModule]
218 except KeyError:
219 return
220
221 tearDownModule = getattr(module, 'tearDownModule', None)
222 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400223 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000224 try:
225 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000226 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000227 if isinstance(result, _DebugResult):
228 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000229 errorName = 'tearDownModule (%s)' % previousModule
230 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400231 finally:
232 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000233
234 def _tearDownPreviousClass(self, test, result):
235 previousClass = getattr(result, '_previousTestClass', None)
236 currentClass = test.__class__
237 if currentClass == previousClass:
238 return
239 if getattr(previousClass, '_classSetupFailed', False):
240 return
241 if getattr(result, '_moduleSetUpFailed', False):
242 return
243 if getattr(previousClass, "__unittest_skip__", False):
244 return
245
246 tearDownClass = getattr(previousClass, 'tearDownClass', None)
247 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400248 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000249 try:
250 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000251 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000252 if isinstance(result, _DebugResult):
253 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000254 className = util.strclass(previousClass)
255 errorName = 'tearDownClass (%s)' % className
256 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400257 finally:
258 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000259
260
261class _ErrorHolder(object):
262 """
263 Placeholder for a TestCase inside a result. As far as a TestResult
264 is concerned, this looks exactly like a unit test. Used to insert
265 arbitrary errors into a test suite run.
266 """
267 # Inspired by the ErrorHolder from Twisted:
268 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
269
270 # attribute used by TestResult._exc_info_to_string
271 failureException = None
272
273 def __init__(self, description):
274 self.description = description
275
276 def id(self):
277 return self.description
278
279 def shortDescription(self):
280 return None
281
282 def __repr__(self):
283 return "<ErrorHolder description=%r>" % (self.description,)
284
285 def __str__(self):
286 return self.id()
287
288 def run(self, result):
289 # could call result.addError(...) - but this test-like object
290 # shouldn't be run anyway
291 pass
292
293 def __call__(self, result):
294 return self.run(result)
295
296 def countTestCases(self):
297 return 0
298
299def _isnotsuite(test):
300 "A crude way to tell apart testcases and suites with duck-typing"
301 try:
302 iter(test)
303 except TypeError:
304 return True
305 return False
Michael Foordb8748742010-06-10 16:16:08 +0000306
307
308class _DebugResult(object):
309 "Used by the TestSuite to hold previous class when running in debug."
310 _previousTestClass = None
311 _moduleSetUpFailed = False
312 shouldStop = False