blob: a543e8191a51d79ea7d2e4e0b759d0b01452d4ec [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
Benjamin Peterson847a4112010-03-14 15:04:17 +000011class BaseTestSuite(object):
12 """A simple test suite that doesn't provide class or module shared fixtures.
Benjamin Petersonbed7d042009-07-19 21:01:52 +000013 """
14 def __init__(self, tests=()):
15 self._tests = []
16 self.addTests(tests)
17
18 def __repr__(self):
Benjamin Peterson4ac9ce42009-10-04 14:49:41 +000019 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000020
21 def __eq__(self, other):
22 if not isinstance(other, self.__class__):
23 return NotImplemented
24 return list(self) == list(other)
25
26 def __ne__(self, other):
27 return not self == other
28
29 def __iter__(self):
30 return iter(self._tests)
31
32 def countTestCases(self):
33 cases = 0
34 for test in self:
35 cases += test.countTestCases()
36 return cases
37
38 def addTest(self, test):
39 # sanity checks
40 if not hasattr(test, '__call__'):
R. David Murray67d1bbd2010-01-29 17:55:47 +000041 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersonbed7d042009-07-19 21:01:52 +000042 if isinstance(test, type) and issubclass(test,
43 (case.TestCase, TestSuite)):
44 raise TypeError("TestCases and TestSuites must be instantiated "
45 "before passing them to addTest()")
46 self._tests.append(test)
47
48 def addTests(self, tests):
49 if isinstance(tests, str):
50 raise TypeError("tests must be an iterable of tests, not a string")
51 for test in tests:
52 self.addTest(test)
53
54 def run(self, result):
55 for test in self:
56 if result.shouldStop:
57 break
58 test(result)
59 return result
60
61 def __call__(self, *args, **kwds):
62 return self.run(*args, **kwds)
63
64 def debug(self):
65 """Run the tests without collecting errors in a TestResult"""
66 for test in self:
67 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +000068
69
70class TestSuite(BaseTestSuite):
71 """A test suite is a composite test consisting of a number of TestCases.
72
73 For use, create an instance of TestSuite, then add test case instances.
74 When all tests have been added, the suite can be passed to a test
75 runner, such as TextTestRunner. It will run the individual test cases
76 in the order in which they were added, aggregating the results. When
77 subclassing, do not forget to call the base class constructor.
78 """
79
80
81 def run(self, result):
82 self._wrapped_run(result)
83 self._tearDownPreviousClass(None, result)
84 self._handleModuleTearDown(result)
85 return result
86
Michael Foordb8748742010-06-10 16:16:08 +000087 def debug(self):
88 """Run the tests without collecting errors in a TestResult"""
89 debug = _DebugResult()
90 self._wrapped_run(debug, True)
91 self._tearDownPreviousClass(None, debug)
92 self._handleModuleTearDown(debug)
93
Benjamin Peterson847a4112010-03-14 15:04:17 +000094 ################################
95 # private methods
Michael Foordb8748742010-06-10 16:16:08 +000096 def _wrapped_run(self, result, debug=False):
Benjamin Peterson847a4112010-03-14 15:04:17 +000097 for test in self:
98 if result.shouldStop:
99 break
100
101 if _isnotsuite(test):
102 self._tearDownPreviousClass(test, result)
103 self._handleModuleFixture(test, result)
104 self._handleClassSetUp(test, result)
105 result._previousTestClass = test.__class__
106
107 if (getattr(test.__class__, '_classSetupFailed', False) or
108 getattr(result, '_moduleSetUpFailed', False)):
109 continue
110
111 if hasattr(test, '_wrapped_run'):
112 test._wrapped_run(result)
Michael Foordb8748742010-06-10 16:16:08 +0000113 elif 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
118 def _handleClassSetUp(self, test, result):
119 previousClass = getattr(result, '_previousTestClass', None)
120 currentClass = test.__class__
121 if currentClass == previousClass:
122 return
123 if result._moduleSetUpFailed:
124 return
125 if getattr(currentClass, "__unittest_skip__", False):
126 return
127
Michael Foord73162192010-05-08 17:10:05 +0000128 try:
129 currentClass._classSetupFailed = False
130 except TypeError:
131 # test may actually be a function
132 # so its class will be a builtin-type
133 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000134
135 setUpClass = getattr(currentClass, 'setUpClass', None)
136 if setUpClass is not None:
137 try:
138 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000139 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000140 if isinstance(result, _DebugResult):
141 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000142 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000143 className = util.strclass(currentClass)
144 errorName = 'setUpClass (%s)' % className
145 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000146
147 def _get_previous_module(self, result):
148 previousModule = None
149 previousClass = getattr(result, '_previousTestClass', None)
150 if previousClass is not None:
151 previousModule = previousClass.__module__
152 return previousModule
153
154
155 def _handleModuleFixture(self, test, result):
156 previousModule = self._get_previous_module(result)
157 currentModule = test.__class__.__module__
158 if currentModule == previousModule:
159 return
160
161 self._handleModuleTearDown(result)
162
163
164 result._moduleSetUpFailed = False
165 try:
166 module = sys.modules[currentModule]
167 except KeyError:
168 return
169 setUpModule = getattr(module, 'setUpModule', None)
170 if setUpModule is not None:
171 try:
172 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000173 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000174 if isinstance(result, _DebugResult):
175 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000176 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000177 errorName = 'setUpModule (%s)' % currentModule
178 self._addClassOrModuleLevelException(result, e, errorName)
179
180 def _addClassOrModuleLevelException(self, result, exception, errorName):
181 error = _ErrorHolder(errorName)
182 addSkip = getattr(result, 'addSkip', None)
183 if addSkip is not None and isinstance(exception, case.SkipTest):
184 addSkip(error, str(exception))
185 else:
186 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000187
188 def _handleModuleTearDown(self, result):
189 previousModule = self._get_previous_module(result)
190 if previousModule is None:
191 return
192 if result._moduleSetUpFailed:
193 return
194
195 try:
196 module = sys.modules[previousModule]
197 except KeyError:
198 return
199
200 tearDownModule = getattr(module, 'tearDownModule', None)
201 if tearDownModule is not None:
202 try:
203 tearDownModule()
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
Michael Foord520ed0a2010-06-05 21:12:23 +0000207 errorName = 'tearDownModule (%s)' % previousModule
208 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000209
210 def _tearDownPreviousClass(self, test, result):
211 previousClass = getattr(result, '_previousTestClass', None)
212 currentClass = test.__class__
213 if currentClass == previousClass:
214 return
215 if getattr(previousClass, '_classSetupFailed', False):
216 return
217 if getattr(result, '_moduleSetUpFailed', False):
218 return
219 if getattr(previousClass, "__unittest_skip__", False):
220 return
221
222 tearDownClass = getattr(previousClass, 'tearDownClass', None)
223 if tearDownClass is not None:
224 try:
225 tearDownClass()
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 className = util.strclass(previousClass)
230 errorName = 'tearDownClass (%s)' % className
231 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000232
Benjamin Peterson847a4112010-03-14 15:04:17 +0000233
234
235class _ErrorHolder(object):
236 """
237 Placeholder for a TestCase inside a result. As far as a TestResult
238 is concerned, this looks exactly like a unit test. Used to insert
239 arbitrary errors into a test suite run.
240 """
241 # Inspired by the ErrorHolder from Twisted:
242 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
243
244 # attribute used by TestResult._exc_info_to_string
245 failureException = None
246
247 def __init__(self, description):
248 self.description = description
249
250 def id(self):
251 return self.description
252
253 def shortDescription(self):
254 return None
255
256 def __repr__(self):
257 return "<ErrorHolder description=%r>" % (self.description,)
258
259 def __str__(self):
260 return self.id()
261
262 def run(self, result):
263 # could call result.addError(...) - but this test-like object
264 # shouldn't be run anyway
265 pass
266
267 def __call__(self, result):
268 return self.run(result)
269
270 def countTestCases(self):
271 return 0
272
273def _isnotsuite(test):
274 "A crude way to tell apart testcases and suites with duck-typing"
275 try:
276 iter(test)
277 except TypeError:
278 return True
279 return False
Michael Foordb8748742010-06-10 16:16:08 +0000280
281
282class _DebugResult(object):
283 "Used by the TestSuite to hold previous class when running in debug."
284 _previousTestClass = None
285 _moduleSetUpFailed = False
286 shouldStop = False