blob: 73f0e17f8a661f9c55ab3675e12c18eab24d0de1 [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
Michael Foordbbea35f2010-11-01 21:09:03 +000080 def run(self, result, debug=False):
81 topLevel = False
82 if getattr(result, '_testRunEntered', False) is False:
83 result._testRunEntered = topLevel = True
Benjamin Peterson847a4112010-03-14 15:04:17 +000084
Benjamin Peterson847a4112010-03-14 15:04:17 +000085 for test in self:
86 if result.shouldStop:
87 break
88
89 if _isnotsuite(test):
90 self._tearDownPreviousClass(test, result)
91 self._handleModuleFixture(test, result)
92 self._handleClassSetUp(test, result)
93 result._previousTestClass = test.__class__
94
95 if (getattr(test.__class__, '_classSetupFailed', False) or
96 getattr(result, '_moduleSetUpFailed', False)):
97 continue
98
Michael Foordbbea35f2010-11-01 21:09:03 +000099 if not debug:
Benjamin Peterson847a4112010-03-14 15:04:17 +0000100 test(result)
Michael Foordb8748742010-06-10 16:16:08 +0000101 else:
102 test.debug()
Benjamin Peterson847a4112010-03-14 15:04:17 +0000103
Michael Foordbbea35f2010-11-01 21:09:03 +0000104 if topLevel:
105 self._tearDownPreviousClass(None, result)
106 self._handleModuleTearDown(result)
107 return result
108
109 def debug(self):
110 """Run the tests without collecting errors in a TestResult"""
111 debug = _DebugResult()
112 self.run(debug, True)
113
114 ################################
115
Benjamin Peterson847a4112010-03-14 15:04:17 +0000116 def _handleClassSetUp(self, test, result):
117 previousClass = getattr(result, '_previousTestClass', None)
118 currentClass = test.__class__
119 if currentClass == previousClass:
120 return
121 if result._moduleSetUpFailed:
122 return
123 if getattr(currentClass, "__unittest_skip__", False):
124 return
125
Michael Foord73162192010-05-08 17:10:05 +0000126 try:
127 currentClass._classSetupFailed = False
128 except TypeError:
129 # test may actually be a function
130 # so its class will be a builtin-type
131 pass
Benjamin Peterson847a4112010-03-14 15:04:17 +0000132
133 setUpClass = getattr(currentClass, 'setUpClass', None)
134 if setUpClass is not None:
135 try:
136 setUpClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000137 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000138 if isinstance(result, _DebugResult):
139 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000140 currentClass._classSetupFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000141 className = util.strclass(currentClass)
142 errorName = 'setUpClass (%s)' % className
143 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000144
145 def _get_previous_module(self, result):
146 previousModule = None
147 previousClass = getattr(result, '_previousTestClass', None)
148 if previousClass is not None:
149 previousModule = previousClass.__module__
150 return previousModule
151
152
153 def _handleModuleFixture(self, test, result):
154 previousModule = self._get_previous_module(result)
155 currentModule = test.__class__.__module__
156 if currentModule == previousModule:
157 return
158
159 self._handleModuleTearDown(result)
160
161
162 result._moduleSetUpFailed = False
163 try:
164 module = sys.modules[currentModule]
165 except KeyError:
166 return
167 setUpModule = getattr(module, 'setUpModule', None)
168 if setUpModule is not None:
169 try:
170 setUpModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000171 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000172 if isinstance(result, _DebugResult):
173 raise
Benjamin Peterson847a4112010-03-14 15:04:17 +0000174 result._moduleSetUpFailed = True
Michael Foord520ed0a2010-06-05 21:12:23 +0000175 errorName = 'setUpModule (%s)' % currentModule
176 self._addClassOrModuleLevelException(result, e, errorName)
177
178 def _addClassOrModuleLevelException(self, result, exception, errorName):
179 error = _ErrorHolder(errorName)
180 addSkip = getattr(result, 'addSkip', None)
181 if addSkip is not None and isinstance(exception, case.SkipTest):
182 addSkip(error, str(exception))
183 else:
184 result.addError(error, sys.exc_info())
Benjamin Peterson847a4112010-03-14 15:04:17 +0000185
186 def _handleModuleTearDown(self, result):
187 previousModule = self._get_previous_module(result)
188 if previousModule is None:
189 return
190 if result._moduleSetUpFailed:
191 return
192
193 try:
194 module = sys.modules[previousModule]
195 except KeyError:
196 return
197
198 tearDownModule = getattr(module, 'tearDownModule', None)
199 if tearDownModule is not None:
200 try:
201 tearDownModule()
Michael Foord520ed0a2010-06-05 21:12:23 +0000202 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000203 if isinstance(result, _DebugResult):
204 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000205 errorName = 'tearDownModule (%s)' % previousModule
206 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000207
208 def _tearDownPreviousClass(self, test, result):
209 previousClass = getattr(result, '_previousTestClass', None)
210 currentClass = test.__class__
211 if currentClass == previousClass:
212 return
213 if getattr(previousClass, '_classSetupFailed', False):
214 return
215 if getattr(result, '_moduleSetUpFailed', False):
216 return
217 if getattr(previousClass, "__unittest_skip__", False):
218 return
219
220 tearDownClass = getattr(previousClass, 'tearDownClass', None)
221 if tearDownClass is not None:
222 try:
223 tearDownClass()
Michael Foord520ed0a2010-06-05 21:12:23 +0000224 except Exception as e:
Michael Foordb8748742010-06-10 16:16:08 +0000225 if isinstance(result, _DebugResult):
226 raise
Michael Foord520ed0a2010-06-05 21:12:23 +0000227 className = util.strclass(previousClass)
228 errorName = 'tearDownClass (%s)' % className
229 self._addClassOrModuleLevelException(result, e, errorName)
Benjamin Peterson847a4112010-03-14 15:04:17 +0000230
Benjamin Peterson847a4112010-03-14 15:04:17 +0000231
232
233class _ErrorHolder(object):
234 """
235 Placeholder for a TestCase inside a result. As far as a TestResult
236 is concerned, this looks exactly like a unit test. Used to insert
237 arbitrary errors into a test suite run.
238 """
239 # Inspired by the ErrorHolder from Twisted:
240 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
241
242 # attribute used by TestResult._exc_info_to_string
243 failureException = None
244
245 def __init__(self, description):
246 self.description = description
247
248 def id(self):
249 return self.description
250
251 def shortDescription(self):
252 return None
253
254 def __repr__(self):
255 return "<ErrorHolder description=%r>" % (self.description,)
256
257 def __str__(self):
258 return self.id()
259
260 def run(self, result):
261 # could call result.addError(...) - but this test-like object
262 # shouldn't be run anyway
263 pass
264
265 def __call__(self, result):
266 return self.run(result)
267
268 def countTestCases(self):
269 return 0
270
271def _isnotsuite(test):
272 "A crude way to tell apart testcases and suites with duck-typing"
273 try:
274 iter(test)
275 except TypeError:
276 return True
277 return False
Michael Foordb8748742010-06-10 16:16:08 +0000278
279
280class _DebugResult(object):
281 "Used by the TestSuite to hold previous class when running in debug."
282 _previousTestClass = None
283 _moduleSetUpFailed = False
284 shouldStop = False