blob: e7b8b4a9fd8d1575c0c41bd680da67478476a507 [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."""
Andrew Svetlova4121372013-08-31 20:55:25 +030069 return
Andrew Svetloveb973682013-08-28 21:28:38 +030070 try:
71 self._tests[index] = None
72 except TypeError:
73 # support for suite implementations that have overriden self._test
74 pass
75
Benjamin Petersonbed7d042009-07-19 21:01:52 +000076 def __call__(self, *args, **kwds):
77 return self.run(*args, **kwds)
78
79 def debug(self):
80 """Run the tests without collecting errors in a TestResult"""
81 for test in self:
82 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000083
84
85class TestSuite(BaseTestSuite):
86 """A test suite is a composite test consisting of a number of TestCases.
87
88 For use, create an instance of TestSuite, then add test case instances.
89 When all tests have been added, the suite can be passed to a test
90 runner, such as TextTestRunner. It will run the individual test cases
91 in the order in which they were added, aggregating the results. When
92 subclassing, do not forget to call the base class constructor.
93 """
94
Michael Foordbbea35f2010-11-01 21:09:03 +000095 def run(self, result, debug=False):
96 topLevel = False
97 if getattr(result, '_testRunEntered', False) is False:
98 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +000099
Andrew Svetloveb973682013-08-28 21:28:38 +0300100 for index, test in enumerate(self):
Benjamin Peterson847a4112010-03-14 15:04:17 +0000101 if result.shouldStop:
102 break
103
104 if _isnotsuite(test):
105 self._tearDownPreviousClass(test, result)
106 self._handleModuleFixture(test, result)
107 self._handleClassSetUp(test, result)
108 result._previousTestClass = test.__class__
109
110 if (getattr(test.__class__, '_classSetupFailed', False) or
111 getattr(result, '_moduleSetUpFailed', False)):
112 continue
113
Michael Foordbbea35f2010-11-01 21:09:03 +0000114 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000115 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000116 else:
117 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000118
Andrew Svetloveb973682013-08-28 21:28:38 +0300119 self._removeTestAtIndex(index)
120
Michael Foordbbea35f2010-11-01 21:09:03 +0000121 if topLevel:
122 self._tearDownPreviousClass(None, result)
123 self._handleModuleTearDown(result)
Michael Foordcca5be22010-12-19 04:07:28 +0000124 result._testRunEntered = False
Michael Foordbbea35f2010-11-01 21:09:03 +0000125 return result
126
127 def debug(self):
128 """Run the tests without collecting errors in a TestResult"""
129 debug = _DebugResult()
130 self.run(debug, True)
131
132 ################################
133
Benjamin Peterson847a4112010-03-14 15:04:17 +0000134 def _handleClassSetUp(self, test, result):
135 previousClass = getattr(result, '_previousTestClass', None)
136 currentClass = test.__class__
137 if currentClass == previousClass:
138 return
139 if result._moduleSetUpFailed:
140 return
141 if getattr(currentClass, "__unittest_skip__", False):
142 return
143
Michael Foord73162192010-05-08 17:10:05 +0000144 try:
145 currentClass._classSetupFailed = False
146 except TypeError:
147 # test may actually be a function
148 # so its class will be a builtin-type
149 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000150
151 setUpClass = getattr(currentClass, 'setUpClass', None)
152 if setUpClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400153 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000154 try:
155 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000156 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000157 if isinstance(result, _DebugResult):
158 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000159 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000160 className = util.strclass(currentClass)
161 errorName = 'setUpClass (%s)' % className
162 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400163 finally:
164 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000165
166 def _get_previous_module(self, result):
167 previousModule = None
168 previousClass = getattr(result, '_previousTestClass', None)
169 if previousClass is not None:
170 previousModule = previousClass.__module__
171 return previousModule
172
173
174 def _handleModuleFixture(self, test, result):
175 previousModule = self._get_previous_module(result)
176 currentModule = test.__class__.__module__
177 if currentModule == previousModule:
178 return
179
180 self._handleModuleTearDown(result)
181
182
183 result._moduleSetUpFailed = False
184 try:
185 module = sys.modules[currentModule]
186 except KeyError:
187 return
188 setUpModule = getattr(module, 'setUpModule', None)
189 if setUpModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400190 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000191 try:
192 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000193 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000194 if isinstance(result, _DebugResult):
195 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000196 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000197 errorName = 'setUpModule (%s)' % currentModule
198 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400199 finally:
200 _call_if_exists(result, '_restoreStdout')
Michael Foord520ed0a2010-06-05 21:12:23 +0000201
202 def _addClassOrModuleLevelException(self, result, exception, errorName):
203 error = _ErrorHolder(errorName)
204 addSkip = getattr(result, 'addSkip', None)
205 if addSkip is not None and isinstance(exception, case.SkipTest):
206 addSkip(error, str(exception))
207 else:
208 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000209
210 def _handleModuleTearDown(self, result):
211 previousModule = self._get_previous_module(result)
212 if previousModule is None:
213 return
214 if result._moduleSetUpFailed:
215 return
216
217 try:
218 module = sys.modules[previousModule]
219 except KeyError:
220 return
221
222 tearDownModule = getattr(module, 'tearDownModule', None)
223 if tearDownModule is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400224 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000225 try:
226 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000227 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000228 if isinstance(result, _DebugResult):
229 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000230 errorName = 'tearDownModule (%s)' % previousModule
231 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400232 finally:
233 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000234
235 def _tearDownPreviousClass(self, test, result):
236 previousClass = getattr(result, '_previousTestClass', None)
237 currentClass = test.__class__
238 if currentClass == previousClass:
239 return
240 if getattr(previousClass, '_classSetupFailed', False):
241 return
242 if getattr(result, '_moduleSetUpFailed', False):
243 return
244 if getattr(previousClass, "__unittest_skip__", False):
245 return
246
247 tearDownClass = getattr(previousClass, 'tearDownClass', None)
248 if tearDownClass is not None:
Michael Foord42ec7cb2011-03-17 13:44:18 -0400249 _call_if_exists(result, '_setupStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000250 try:
251 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000252 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000253 if isinstance(result, _DebugResult):
254 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000255 className = util.strclass(previousClass)
256 errorName = 'tearDownClass (%s)' % className
257 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord42ec7cb2011-03-17 13:44:18 -0400258 finally:
259 _call_if_exists(result, '_restoreStdout')
Benjamin Peterson847a4112010-03-14 15:04:17 +0000260
261
262class _ErrorHolder(object):
263 """
264 Placeholder for a TestCase inside a result. As far as a TestResult
265 is concerned, this looks exactly like a unit test. Used to insert
266 arbitrary errors into a test suite run.
267 """
268 # Inspired by the ErrorHolder from Twisted:
269 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
270
271 # attribute used by TestResult._exc_info_to_string
272 failureException = None
273
274 def __init__(self, description):
275 self.description = description
276
277 def id(self):
278 return self.description
279
280 def shortDescription(self):
281 return None
282
283 def __repr__(self):
284 return "<ErrorHolder description=%r>" % (self.description,)
285
286 def __str__(self):
287 return self.id()
288
289 def run(self, result):
290 # could call result.addError(...) - but this test-like object
291 # shouldn't be run anyway
292 pass
293
294 def __call__(self, result):
295 return self.run(result)
296
297 def countTestCases(self):
298 return 0
299
300def _isnotsuite(test):
301 "A crude way to tell apart testcases and suites with duck-typing"
302 try:
303 iter(test)
304 except TypeError:
305 return True
306 return False
Michael Foordb8748742010-06-10 16:16:08 +0000307
308
309class _DebugResult(object):
310 "Used by the TestSuite to hold previous class when running in debug."
311 _previousTestClass = None
312 _moduleSetUpFailed = False
313 shouldStop = False