blob: e8fbdc31fed7091711414525dcbd593376dd6de6 [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
Michael Foorde5dc24e2010-11-01 22:11:53 +000083 def run(self, result, debug=False):
84 topLevel = False
85 if getattr(result, '_testRunEntered', False) is False:
86 result._testRunEntered = topLevel = True
Michael Foord5ffa3252010-03-07 22:04:55 +000087
Michael Foord5ffa3252010-03-07 22:04:55 +000088 for test in self:
89 if result.shouldStop:
90 break
91
92 if _isnotsuite(test):
93 self._tearDownPreviousClass(test, result)
94 self._handleModuleFixture(test, result)
95 self._handleClassSetUp(test, result)
96 result._previousTestClass = test.__class__
97
98 if (getattr(test.__class__, '_classSetupFailed', False) or
99 getattr(result, '_moduleSetUpFailed', False)):
100 continue
101
Michael Foorde5dc24e2010-11-01 22:11:53 +0000102 if not debug:
Michael Foord5ffa3252010-03-07 22:04:55 +0000103 test(result)
Michael Foord0fedb282010-06-08 22:44:52 +0000104 else:
105 test.debug()
Michael Foord5ffa3252010-03-07 22:04:55 +0000106
Michael Foorde5dc24e2010-11-01 22:11:53 +0000107 if topLevel:
108 self._tearDownPreviousClass(None, result)
109 self._handleModuleTearDown(result)
110 return result
111
112 def debug(self):
113 """Run the tests without collecting errors in a TestResult"""
114 debug = _DebugResult()
115 self.run(debug, True)
116
117 ################################
118
Michael Foord5ffa3252010-03-07 22:04:55 +0000119 def _handleClassSetUp(self, test, result):
120 previousClass = getattr(result, '_previousTestClass', None)
121 currentClass = test.__class__
122 if currentClass == previousClass:
123 return
124 if result._moduleSetUpFailed:
125 return
126 if getattr(currentClass, "__unittest_skip__", False):
127 return
128
Michael Foord9c164af2010-05-08 17:06:25 +0000129 try:
130 currentClass._classSetupFailed = False
131 except TypeError:
132 # test may actually be a function
133 # so its class will be a builtin-type
134 pass
Michael Foord5ffa3252010-03-07 22:04:55 +0000135
136 setUpClass = getattr(currentClass, 'setUpClass', None)
137 if setUpClass is not None:
138 try:
139 setUpClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000140 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000141 if isinstance(result, _DebugResult):
142 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000143 currentClass._classSetupFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000144 className = util.strclass(currentClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000145 errorName = 'setUpClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000146 self._addClassOrModuleLevelException(result, e, errorName)
147
Michael Foord5ffa3252010-03-07 22:04:55 +0000148
149 def _get_previous_module(self, result):
150 previousModule = None
151 previousClass = getattr(result, '_previousTestClass', None)
152 if previousClass is not None:
153 previousModule = previousClass.__module__
154 return previousModule
155
156
157 def _handleModuleFixture(self, test, result):
158 previousModule = self._get_previous_module(result)
159 currentModule = test.__class__.__module__
160 if currentModule == previousModule:
161 return
162
163 self._handleModuleTearDown(result)
164
Michael Foord5ffa3252010-03-07 22:04:55 +0000165 result._moduleSetUpFailed = False
166 try:
167 module = sys.modules[currentModule]
168 except KeyError:
169 return
170 setUpModule = getattr(module, 'setUpModule', None)
171 if setUpModule is not None:
172 try:
173 setUpModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000174 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000175 if isinstance(result, _DebugResult):
176 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000177 result._moduleSetUpFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000178 errorName = 'setUpModule (%s)' % currentModule
179 self._addClassOrModuleLevelException(result, e, errorName)
180
181 def _addClassOrModuleLevelException(self, result, exception, errorName):
182 error = _ErrorHolder(errorName)
183 addSkip = getattr(result, 'addSkip', None)
184 if addSkip is not None and isinstance(exception, case.SkipTest):
185 addSkip(error, str(exception))
186 else:
187 result.addError(error, sys.exc_info())
Michael Foord5ffa3252010-03-07 22:04:55 +0000188
189 def _handleModuleTearDown(self, result):
190 previousModule = self._get_previous_module(result)
191 if previousModule is None:
192 return
193 if result._moduleSetUpFailed:
194 return
195
196 try:
197 module = sys.modules[previousModule]
198 except KeyError:
199 return
200
201 tearDownModule = getattr(module, 'tearDownModule', None)
202 if tearDownModule is not None:
203 try:
204 tearDownModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000205 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000206 if isinstance(result, _DebugResult):
207 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000208 errorName = 'tearDownModule (%s)' % previousModule
209 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +0000210
211 def _tearDownPreviousClass(self, test, result):
212 previousClass = getattr(result, '_previousTestClass', None)
213 currentClass = test.__class__
214 if currentClass == previousClass:
215 return
216 if getattr(previousClass, '_classSetupFailed', False):
217 return
218 if getattr(result, '_moduleSetUpFailed', False):
219 return
220 if getattr(previousClass, "__unittest_skip__", False):
221 return
222
223 tearDownClass = getattr(previousClass, 'tearDownClass', None)
224 if tearDownClass is not None:
225 try:
226 tearDownClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000227 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000228 if isinstance(result, _DebugResult):
229 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000230 className = util.strclass(previousClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000231 errorName = 'tearDownClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000232 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +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 Foord0fedb282010-06-08 22:44:52 +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