blob: 04d35b883b3a30648939d3580613c188cee35d43 [file] [log] [blame]
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00001"""TestSuite"""
2
Michael Foord5ffa3252010-03-07 22:04:55 +00003import sys
4
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00005from . import case
Michael Foorde91ea562009-09-13 19:07:03 +00006from . import util
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +00007
Michael Foordb1aa30f2010-03-22 00:06:30 +00008__unittest = True
9
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000010
Michael Foord5ffa3252010-03-07 22:04:55 +000011class BaseTestSuite(object):
12 """A simple test suite that doesn't provide class or module shared fixtures.
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000013 """
14 def __init__(self, tests=()):
15 self._tests = []
16 self.addTests(tests)
17
18 def __repr__(self):
Michael Foorde91ea562009-09-13 19:07:03 +000019 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +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 # Can't guarantee hash invariant, so flag as unhashable
30 __hash__ = None
31
32 def __iter__(self):
33 return iter(self._tests)
34
35 def countTestCases(self):
36 cases = 0
37 for test in self:
38 cases += test.countTestCases()
39 return cases
40
41 def addTest(self, test):
42 # sanity checks
43 if not hasattr(test, '__call__'):
R. David Murrayb5d74002010-01-28 21:16:33 +000044 raise TypeError("{} is not callable".format(repr(test)))
Benjamin Petersond7b0eeb2009-07-19 20:18:21 +000045 if isinstance(test, type) and issubclass(test,
46 (case.TestCase, TestSuite)):
47 raise TypeError("TestCases and TestSuites must be instantiated "
48 "before passing them to addTest()")
49 self._tests.append(test)
50
51 def addTests(self, tests):
52 if isinstance(tests, basestring):
53 raise TypeError("tests must be an iterable of tests, not a string")
54 for test in tests:
55 self.addTest(test)
56
57 def run(self, result):
58 for test in self:
59 if result.shouldStop:
60 break
61 test(result)
62 return result
63
64 def __call__(self, *args, **kwds):
65 return self.run(*args, **kwds)
66
67 def debug(self):
68 """Run the tests without collecting errors in a TestResult"""
69 for test in self:
70 test.debug()
Michael Foord5ffa3252010-03-07 22:04:55 +000071
72
73class TestSuite(BaseTestSuite):
74 """A test suite is a composite test consisting of a number of TestCases.
75
76 For use, create an instance of TestSuite, then add test case instances.
77 When all tests have been added, the suite can be passed to a test
78 runner, such as TextTestRunner. It will run the individual test cases
79 in the order in which they were added, aggregating the results. When
80 subclassing, do not forget to call the base class constructor.
81 """
82
83
84 def run(self, result):
85 self._wrapped_run(result)
86 self._tearDownPreviousClass(None, result)
87 self._handleModuleTearDown(result)
88 return result
89
Michael Foord0fedb282010-06-08 22:44:52 +000090 def debug(self):
91 """Run the tests without collecting errors in a TestResult"""
92 debug = _DebugResult()
93 self._wrapped_run(debug, True)
94 self._tearDownPreviousClass(None, debug)
95 self._handleModuleTearDown(debug)
96
Michael Foord5ffa3252010-03-07 22:04:55 +000097 ################################
98 # private methods
Michael Foord0fedb282010-06-08 22:44:52 +000099 def _wrapped_run(self, result, debug=False):
Michael Foord5ffa3252010-03-07 22:04:55 +0000100 for test in self:
101 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
114 if hasattr(test, '_wrapped_run'):
115 test._wrapped_run(result)
Michael Foord0fedb282010-06-08 22:44:52 +0000116 elif not debug:
Michael Foord5ffa3252010-03-07 22:04:55 +0000117 test(result)
Michael Foord0fedb282010-06-08 22:44:52 +0000118 else:
119 test.debug()
Michael Foord5ffa3252010-03-07 22:04:55 +0000120
121 def _handleClassSetUp(self, test, result):
122 previousClass = getattr(result, '_previousTestClass', None)
123 currentClass = test.__class__
124 if currentClass == previousClass:
125 return
126 if result._moduleSetUpFailed:
127 return
128 if getattr(currentClass, "__unittest_skip__", False):
129 return
130
Michael Foord9c164af2010-05-08 17:06:25 +0000131 try:
132 currentClass._classSetupFailed = False
133 except TypeError:
134 # test may actually be a function
135 # so its class will be a builtin-type
136 pass
Michael Foord5ffa3252010-03-07 22:04:55 +0000137
138 setUpClass = getattr(currentClass, 'setUpClass', None)
139 if setUpClass is not None:
140 try:
141 setUpClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000142 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000143 if isinstance(result, _DebugResult):
144 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000145 currentClass._classSetupFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000146 className = util.strclass(currentClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000147 errorName = 'setUpClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000148 self._addClassOrModuleLevelException(result, e, errorName)
149
Michael Foord5ffa3252010-03-07 22:04:55 +0000150
151 def _get_previous_module(self, result):
152 previousModule = None
153 previousClass = getattr(result, '_previousTestClass', None)
154 if previousClass is not None:
155 previousModule = previousClass.__module__
156 return previousModule
157
158
159 def _handleModuleFixture(self, test, result):
160 previousModule = self._get_previous_module(result)
161 currentModule = test.__class__.__module__
162 if currentModule == previousModule:
163 return
164
165 self._handleModuleTearDown(result)
166
Michael Foord5ffa3252010-03-07 22:04:55 +0000167 result._moduleSetUpFailed = False
168 try:
169 module = sys.modules[currentModule]
170 except KeyError:
171 return
172 setUpModule = getattr(module, 'setUpModule', None)
173 if setUpModule is not None:
174 try:
175 setUpModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000176 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000177 if isinstance(result, _DebugResult):
178 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000179 result._moduleSetUpFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000180 errorName = 'setUpModule (%s)' % currentModule
181 self._addClassOrModuleLevelException(result, e, errorName)
182
183 def _addClassOrModuleLevelException(self, result, exception, errorName):
184 error = _ErrorHolder(errorName)
185 addSkip = getattr(result, 'addSkip', None)
186 if addSkip is not None and isinstance(exception, case.SkipTest):
187 addSkip(error, str(exception))
188 else:
189 result.addError(error, sys.exc_info())
Michael Foord5ffa3252010-03-07 22:04:55 +0000190
191 def _handleModuleTearDown(self, result):
192 previousModule = self._get_previous_module(result)
193 if previousModule is None:
194 return
195 if result._moduleSetUpFailed:
196 return
197
198 try:
199 module = sys.modules[previousModule]
200 except KeyError:
201 return
202
203 tearDownModule = getattr(module, 'tearDownModule', None)
204 if tearDownModule is not None:
205 try:
206 tearDownModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000207 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000208 if isinstance(result, _DebugResult):
209 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000210 errorName = 'tearDownModule (%s)' % previousModule
211 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +0000212
213 def _tearDownPreviousClass(self, test, result):
214 previousClass = getattr(result, '_previousTestClass', None)
215 currentClass = test.__class__
216 if currentClass == previousClass:
217 return
218 if getattr(previousClass, '_classSetupFailed', False):
219 return
220 if getattr(result, '_moduleSetUpFailed', False):
221 return
222 if getattr(previousClass, "__unittest_skip__", False):
223 return
224
225 tearDownClass = getattr(previousClass, 'tearDownClass', None)
226 if tearDownClass is not None:
227 try:
228 tearDownClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000229 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000230 if isinstance(result, _DebugResult):
231 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000232 className = util.strclass(previousClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000233 errorName = 'tearDownClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000234 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +0000235
236
237class _ErrorHolder(object):
238 """
239 Placeholder for a TestCase inside a result. As far as a TestResult
240 is concerned, this looks exactly like a unit test. Used to insert
241 arbitrary errors into a test suite run.
242 """
243 # Inspired by the ErrorHolder from Twisted:
244 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
245
246 # attribute used by TestResult._exc_info_to_string
247 failureException = None
248
249 def __init__(self, description):
250 self.description = description
251
252 def id(self):
253 return self.description
254
255 def shortDescription(self):
256 return None
257
258 def __repr__(self):
259 return "<ErrorHolder description=%r>" % (self.description,)
260
261 def __str__(self):
262 return self.id()
263
264 def run(self, result):
265 # could call result.addError(...) - but this test-like object
266 # shouldn't be run anyway
267 pass
268
269 def __call__(self, result):
270 return self.run(result)
271
272 def countTestCases(self):
273 return 0
274
275def _isnotsuite(test):
276 "A crude way to tell apart testcases and suites with duck-typing"
277 try:
278 iter(test)
279 except TypeError:
280 return True
281 return False
Michael Foord0fedb282010-06-08 22:44:52 +0000282
283
284class _DebugResult(object):
285 "Used by the TestSuite to hold previous class when running in debug."
286 _previousTestClass = None
287 _moduleSetUpFailed = False
288 shouldStop = False