blob: c0ae2aeeb66f8080945f7a537a37f655f09bf54b [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)
Michael Foord5657ff82010-12-19 14:12:23 +0000110 result._testRunEntered = False
Michael Foorde5dc24e2010-11-01 22:11:53 +0000111 return result
112
113 def debug(self):
114 """Run the tests without collecting errors in a TestResult"""
115 debug = _DebugResult()
116 self.run(debug, True)
117
118 ################################
119
Michael Foord5ffa3252010-03-07 22:04:55 +0000120 def _handleClassSetUp(self, test, result):
121 previousClass = getattr(result, '_previousTestClass', None)
122 currentClass = test.__class__
123 if currentClass == previousClass:
124 return
125 if result._moduleSetUpFailed:
126 return
127 if getattr(currentClass, "__unittest_skip__", False):
128 return
129
Michael Foord9c164af2010-05-08 17:06:25 +0000130 try:
131 currentClass._classSetupFailed = False
132 except TypeError:
133 # test may actually be a function
134 # so its class will be a builtin-type
135 pass
Michael Foord5ffa3252010-03-07 22:04:55 +0000136
137 setUpClass = getattr(currentClass, 'setUpClass', None)
138 if setUpClass is not None:
139 try:
140 setUpClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000141 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000142 if isinstance(result, _DebugResult):
143 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000144 currentClass._classSetupFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000145 className = util.strclass(currentClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000146 errorName = 'setUpClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000147 self._addClassOrModuleLevelException(result, e, errorName)
148
Michael Foord5ffa3252010-03-07 22:04:55 +0000149
150 def _get_previous_module(self, result):
151 previousModule = None
152 previousClass = getattr(result, '_previousTestClass', None)
153 if previousClass is not None:
154 previousModule = previousClass.__module__
155 return previousModule
156
157
158 def _handleModuleFixture(self, test, result):
159 previousModule = self._get_previous_module(result)
160 currentModule = test.__class__.__module__
161 if currentModule == previousModule:
162 return
163
164 self._handleModuleTearDown(result)
165
Michael Foord5ffa3252010-03-07 22:04:55 +0000166 result._moduleSetUpFailed = False
167 try:
168 module = sys.modules[currentModule]
169 except KeyError:
170 return
171 setUpModule = getattr(module, 'setUpModule', None)
172 if setUpModule is not None:
173 try:
174 setUpModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000175 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000176 if isinstance(result, _DebugResult):
177 raise
Michael Foord5ffa3252010-03-07 22:04:55 +0000178 result._moduleSetUpFailed = True
Michael Foord20e287c2010-06-05 19:38:42 +0000179 errorName = 'setUpModule (%s)' % currentModule
180 self._addClassOrModuleLevelException(result, e, errorName)
181
182 def _addClassOrModuleLevelException(self, result, exception, errorName):
183 error = _ErrorHolder(errorName)
184 addSkip = getattr(result, 'addSkip', None)
185 if addSkip is not None and isinstance(exception, case.SkipTest):
186 addSkip(error, str(exception))
187 else:
188 result.addError(error, sys.exc_info())
Michael Foord5ffa3252010-03-07 22:04:55 +0000189
190 def _handleModuleTearDown(self, result):
191 previousModule = self._get_previous_module(result)
192 if previousModule is None:
193 return
194 if result._moduleSetUpFailed:
195 return
196
197 try:
198 module = sys.modules[previousModule]
199 except KeyError:
200 return
201
202 tearDownModule = getattr(module, 'tearDownModule', None)
203 if tearDownModule is not None:
204 try:
205 tearDownModule()
Michael Foord20e287c2010-06-05 19:38:42 +0000206 except Exception as e:
Michael Foord0fedb282010-06-08 22:44:52 +0000207 if isinstance(result, _DebugResult):
208 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000209 errorName = 'tearDownModule (%s)' % previousModule
210 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +0000211
212 def _tearDownPreviousClass(self, test, result):
213 previousClass = getattr(result, '_previousTestClass', None)
214 currentClass = test.__class__
215 if currentClass == previousClass:
216 return
217 if getattr(previousClass, '_classSetupFailed', False):
218 return
219 if getattr(result, '_moduleSetUpFailed', False):
220 return
221 if getattr(previousClass, "__unittest_skip__", False):
222 return
223
224 tearDownClass = getattr(previousClass, 'tearDownClass', None)
225 if tearDownClass is not None:
226 try:
227 tearDownClass()
Michael Foord20e287c2010-06-05 19:38:42 +0000228 except Exception, e:
Michael Foord0fedb282010-06-08 22:44:52 +0000229 if isinstance(result, _DebugResult):
230 raise
Michael Foord20e287c2010-06-05 19:38:42 +0000231 className = util.strclass(previousClass)
Michael Foorddb919f02010-06-05 20:59:00 +0000232 errorName = 'tearDownClass (%s)' % className
Michael Foord20e287c2010-06-05 19:38:42 +0000233 self._addClassOrModuleLevelException(result, e, errorName)
Michael Foord5ffa3252010-03-07 22:04:55 +0000234
235
236class _ErrorHolder(object):
237 """
238 Placeholder for a TestCase inside a result. As far as a TestResult
239 is concerned, this looks exactly like a unit test. Used to insert
240 arbitrary errors into a test suite run.
241 """
242 # Inspired by the ErrorHolder from Twisted:
243 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
244
245 # attribute used by TestResult._exc_info_to_string
246 failureException = None
247
248 def __init__(self, description):
249 self.description = description
250
251 def id(self):
252 return self.description
253
254 def shortDescription(self):
255 return None
256
257 def __repr__(self):
258 return "<ErrorHolder description=%r>" % (self.description,)
259
260 def __str__(self):
261 return self.id()
262
263 def run(self, result):
264 # could call result.addError(...) - but this test-like object
265 # shouldn't be run anyway
266 pass
267
268 def __call__(self, result):
269 return self.run(result)
270
271 def countTestCases(self):
272 return 0
273
274def _isnotsuite(test):
275 "A crude way to tell apart testcases and suites with duck-typing"
276 try:
277 iter(test)
278 except TypeError:
279 return True
280 return False
Michael Foord0fedb282010-06-08 22:44:52 +0000281
282
283class _DebugResult(object):
284 "Used by the TestSuite to hold previous class when running in debug."
285 _previousTestClass = None
286 _moduleSetUpFailed = False
287 shouldStop = False