annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/unittest/suite.py @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 """TestSuite"""
jpayne@69 2
jpayne@69 3 import sys
jpayne@69 4
jpayne@69 5 from . import case
jpayne@69 6 from . import util
jpayne@69 7
jpayne@69 8 __unittest = True
jpayne@69 9
jpayne@69 10
jpayne@69 11 def _call_if_exists(parent, attr):
jpayne@69 12 func = getattr(parent, attr, lambda: None)
jpayne@69 13 func()
jpayne@69 14
jpayne@69 15
jpayne@69 16 class BaseTestSuite(object):
jpayne@69 17 """A simple test suite that doesn't provide class or module shared fixtures.
jpayne@69 18 """
jpayne@69 19 _cleanup = True
jpayne@69 20
jpayne@69 21 def __init__(self, tests=()):
jpayne@69 22 self._tests = []
jpayne@69 23 self._removed_tests = 0
jpayne@69 24 self.addTests(tests)
jpayne@69 25
jpayne@69 26 def __repr__(self):
jpayne@69 27 return "<%s tests=%s>" % (util.strclass(self.__class__), list(self))
jpayne@69 28
jpayne@69 29 def __eq__(self, other):
jpayne@69 30 if not isinstance(other, self.__class__):
jpayne@69 31 return NotImplemented
jpayne@69 32 return list(self) == list(other)
jpayne@69 33
jpayne@69 34 def __iter__(self):
jpayne@69 35 return iter(self._tests)
jpayne@69 36
jpayne@69 37 def countTestCases(self):
jpayne@69 38 cases = self._removed_tests
jpayne@69 39 for test in self:
jpayne@69 40 if test:
jpayne@69 41 cases += test.countTestCases()
jpayne@69 42 return cases
jpayne@69 43
jpayne@69 44 def addTest(self, test):
jpayne@69 45 # sanity checks
jpayne@69 46 if not callable(test):
jpayne@69 47 raise TypeError("{} is not callable".format(repr(test)))
jpayne@69 48 if isinstance(test, type) and issubclass(test,
jpayne@69 49 (case.TestCase, TestSuite)):
jpayne@69 50 raise TypeError("TestCases and TestSuites must be instantiated "
jpayne@69 51 "before passing them to addTest()")
jpayne@69 52 self._tests.append(test)
jpayne@69 53
jpayne@69 54 def addTests(self, tests):
jpayne@69 55 if isinstance(tests, str):
jpayne@69 56 raise TypeError("tests must be an iterable of tests, not a string")
jpayne@69 57 for test in tests:
jpayne@69 58 self.addTest(test)
jpayne@69 59
jpayne@69 60 def run(self, result):
jpayne@69 61 for index, test in enumerate(self):
jpayne@69 62 if result.shouldStop:
jpayne@69 63 break
jpayne@69 64 test(result)
jpayne@69 65 if self._cleanup:
jpayne@69 66 self._removeTestAtIndex(index)
jpayne@69 67 return result
jpayne@69 68
jpayne@69 69 def _removeTestAtIndex(self, index):
jpayne@69 70 """Stop holding a reference to the TestCase at index."""
jpayne@69 71 try:
jpayne@69 72 test = self._tests[index]
jpayne@69 73 except TypeError:
jpayne@69 74 # support for suite implementations that have overridden self._tests
jpayne@69 75 pass
jpayne@69 76 else:
jpayne@69 77 # Some unittest tests add non TestCase/TestSuite objects to
jpayne@69 78 # the suite.
jpayne@69 79 if hasattr(test, 'countTestCases'):
jpayne@69 80 self._removed_tests += test.countTestCases()
jpayne@69 81 self._tests[index] = None
jpayne@69 82
jpayne@69 83 def __call__(self, *args, **kwds):
jpayne@69 84 return self.run(*args, **kwds)
jpayne@69 85
jpayne@69 86 def debug(self):
jpayne@69 87 """Run the tests without collecting errors in a TestResult"""
jpayne@69 88 for test in self:
jpayne@69 89 test.debug()
jpayne@69 90
jpayne@69 91
jpayne@69 92 class TestSuite(BaseTestSuite):
jpayne@69 93 """A test suite is a composite test consisting of a number of TestCases.
jpayne@69 94
jpayne@69 95 For use, create an instance of TestSuite, then add test case instances.
jpayne@69 96 When all tests have been added, the suite can be passed to a test
jpayne@69 97 runner, such as TextTestRunner. It will run the individual test cases
jpayne@69 98 in the order in which they were added, aggregating the results. When
jpayne@69 99 subclassing, do not forget to call the base class constructor.
jpayne@69 100 """
jpayne@69 101
jpayne@69 102 def run(self, result, debug=False):
jpayne@69 103 topLevel = False
jpayne@69 104 if getattr(result, '_testRunEntered', False) is False:
jpayne@69 105 result._testRunEntered = topLevel = True
jpayne@69 106
jpayne@69 107 for index, test in enumerate(self):
jpayne@69 108 if result.shouldStop:
jpayne@69 109 break
jpayne@69 110
jpayne@69 111 if _isnotsuite(test):
jpayne@69 112 self._tearDownPreviousClass(test, result)
jpayne@69 113 self._handleModuleFixture(test, result)
jpayne@69 114 self._handleClassSetUp(test, result)
jpayne@69 115 result._previousTestClass = test.__class__
jpayne@69 116
jpayne@69 117 if (getattr(test.__class__, '_classSetupFailed', False) or
jpayne@69 118 getattr(result, '_moduleSetUpFailed', False)):
jpayne@69 119 continue
jpayne@69 120
jpayne@69 121 if not debug:
jpayne@69 122 test(result)
jpayne@69 123 else:
jpayne@69 124 test.debug()
jpayne@69 125
jpayne@69 126 if self._cleanup:
jpayne@69 127 self._removeTestAtIndex(index)
jpayne@69 128
jpayne@69 129 if topLevel:
jpayne@69 130 self._tearDownPreviousClass(None, result)
jpayne@69 131 self._handleModuleTearDown(result)
jpayne@69 132 result._testRunEntered = False
jpayne@69 133 return result
jpayne@69 134
jpayne@69 135 def debug(self):
jpayne@69 136 """Run the tests without collecting errors in a TestResult"""
jpayne@69 137 debug = _DebugResult()
jpayne@69 138 self.run(debug, True)
jpayne@69 139
jpayne@69 140 ################################
jpayne@69 141
jpayne@69 142 def _handleClassSetUp(self, test, result):
jpayne@69 143 previousClass = getattr(result, '_previousTestClass', None)
jpayne@69 144 currentClass = test.__class__
jpayne@69 145 if currentClass == previousClass:
jpayne@69 146 return
jpayne@69 147 if result._moduleSetUpFailed:
jpayne@69 148 return
jpayne@69 149 if getattr(currentClass, "__unittest_skip__", False):
jpayne@69 150 return
jpayne@69 151
jpayne@69 152 try:
jpayne@69 153 currentClass._classSetupFailed = False
jpayne@69 154 except TypeError:
jpayne@69 155 # test may actually be a function
jpayne@69 156 # so its class will be a builtin-type
jpayne@69 157 pass
jpayne@69 158
jpayne@69 159 setUpClass = getattr(currentClass, 'setUpClass', None)
jpayne@69 160 if setUpClass is not None:
jpayne@69 161 _call_if_exists(result, '_setupStdout')
jpayne@69 162 try:
jpayne@69 163 setUpClass()
jpayne@69 164 except Exception as e:
jpayne@69 165 if isinstance(result, _DebugResult):
jpayne@69 166 raise
jpayne@69 167 currentClass._classSetupFailed = True
jpayne@69 168 className = util.strclass(currentClass)
jpayne@69 169 self._createClassOrModuleLevelException(result, e,
jpayne@69 170 'setUpClass',
jpayne@69 171 className)
jpayne@69 172 finally:
jpayne@69 173 _call_if_exists(result, '_restoreStdout')
jpayne@69 174 if currentClass._classSetupFailed is True:
jpayne@69 175 currentClass.doClassCleanups()
jpayne@69 176 if len(currentClass.tearDown_exceptions) > 0:
jpayne@69 177 for exc in currentClass.tearDown_exceptions:
jpayne@69 178 self._createClassOrModuleLevelException(
jpayne@69 179 result, exc[1], 'setUpClass', className,
jpayne@69 180 info=exc)
jpayne@69 181
jpayne@69 182 def _get_previous_module(self, result):
jpayne@69 183 previousModule = None
jpayne@69 184 previousClass = getattr(result, '_previousTestClass', None)
jpayne@69 185 if previousClass is not None:
jpayne@69 186 previousModule = previousClass.__module__
jpayne@69 187 return previousModule
jpayne@69 188
jpayne@69 189
jpayne@69 190 def _handleModuleFixture(self, test, result):
jpayne@69 191 previousModule = self._get_previous_module(result)
jpayne@69 192 currentModule = test.__class__.__module__
jpayne@69 193 if currentModule == previousModule:
jpayne@69 194 return
jpayne@69 195
jpayne@69 196 self._handleModuleTearDown(result)
jpayne@69 197
jpayne@69 198
jpayne@69 199 result._moduleSetUpFailed = False
jpayne@69 200 try:
jpayne@69 201 module = sys.modules[currentModule]
jpayne@69 202 except KeyError:
jpayne@69 203 return
jpayne@69 204 setUpModule = getattr(module, 'setUpModule', None)
jpayne@69 205 if setUpModule is not None:
jpayne@69 206 _call_if_exists(result, '_setupStdout')
jpayne@69 207 try:
jpayne@69 208 setUpModule()
jpayne@69 209 except Exception as e:
jpayne@69 210 try:
jpayne@69 211 case.doModuleCleanups()
jpayne@69 212 except Exception as exc:
jpayne@69 213 self._createClassOrModuleLevelException(result, exc,
jpayne@69 214 'setUpModule',
jpayne@69 215 currentModule)
jpayne@69 216 if isinstance(result, _DebugResult):
jpayne@69 217 raise
jpayne@69 218 result._moduleSetUpFailed = True
jpayne@69 219 self._createClassOrModuleLevelException(result, e,
jpayne@69 220 'setUpModule',
jpayne@69 221 currentModule)
jpayne@69 222 finally:
jpayne@69 223 _call_if_exists(result, '_restoreStdout')
jpayne@69 224
jpayne@69 225 def _createClassOrModuleLevelException(self, result, exc, method_name,
jpayne@69 226 parent, info=None):
jpayne@69 227 errorName = f'{method_name} ({parent})'
jpayne@69 228 self._addClassOrModuleLevelException(result, exc, errorName, info)
jpayne@69 229
jpayne@69 230 def _addClassOrModuleLevelException(self, result, exception, errorName,
jpayne@69 231 info=None):
jpayne@69 232 error = _ErrorHolder(errorName)
jpayne@69 233 addSkip = getattr(result, 'addSkip', None)
jpayne@69 234 if addSkip is not None and isinstance(exception, case.SkipTest):
jpayne@69 235 addSkip(error, str(exception))
jpayne@69 236 else:
jpayne@69 237 if not info:
jpayne@69 238 result.addError(error, sys.exc_info())
jpayne@69 239 else:
jpayne@69 240 result.addError(error, info)
jpayne@69 241
jpayne@69 242 def _handleModuleTearDown(self, result):
jpayne@69 243 previousModule = self._get_previous_module(result)
jpayne@69 244 if previousModule is None:
jpayne@69 245 return
jpayne@69 246 if result._moduleSetUpFailed:
jpayne@69 247 return
jpayne@69 248
jpayne@69 249 try:
jpayne@69 250 module = sys.modules[previousModule]
jpayne@69 251 except KeyError:
jpayne@69 252 return
jpayne@69 253
jpayne@69 254 tearDownModule = getattr(module, 'tearDownModule', None)
jpayne@69 255 if tearDownModule is not None:
jpayne@69 256 _call_if_exists(result, '_setupStdout')
jpayne@69 257 try:
jpayne@69 258 tearDownModule()
jpayne@69 259 except Exception as e:
jpayne@69 260 if isinstance(result, _DebugResult):
jpayne@69 261 raise
jpayne@69 262 self._createClassOrModuleLevelException(result, e,
jpayne@69 263 'tearDownModule',
jpayne@69 264 previousModule)
jpayne@69 265 finally:
jpayne@69 266 _call_if_exists(result, '_restoreStdout')
jpayne@69 267 try:
jpayne@69 268 case.doModuleCleanups()
jpayne@69 269 except Exception as e:
jpayne@69 270 self._createClassOrModuleLevelException(result, e,
jpayne@69 271 'tearDownModule',
jpayne@69 272 previousModule)
jpayne@69 273
jpayne@69 274 def _tearDownPreviousClass(self, test, result):
jpayne@69 275 previousClass = getattr(result, '_previousTestClass', None)
jpayne@69 276 currentClass = test.__class__
jpayne@69 277 if currentClass == previousClass:
jpayne@69 278 return
jpayne@69 279 if getattr(previousClass, '_classSetupFailed', False):
jpayne@69 280 return
jpayne@69 281 if getattr(result, '_moduleSetUpFailed', False):
jpayne@69 282 return
jpayne@69 283 if getattr(previousClass, "__unittest_skip__", False):
jpayne@69 284 return
jpayne@69 285
jpayne@69 286 tearDownClass = getattr(previousClass, 'tearDownClass', None)
jpayne@69 287 if tearDownClass is not None:
jpayne@69 288 _call_if_exists(result, '_setupStdout')
jpayne@69 289 try:
jpayne@69 290 tearDownClass()
jpayne@69 291 except Exception as e:
jpayne@69 292 if isinstance(result, _DebugResult):
jpayne@69 293 raise
jpayne@69 294 className = util.strclass(previousClass)
jpayne@69 295 self._createClassOrModuleLevelException(result, e,
jpayne@69 296 'tearDownClass',
jpayne@69 297 className)
jpayne@69 298 finally:
jpayne@69 299 _call_if_exists(result, '_restoreStdout')
jpayne@69 300 previousClass.doClassCleanups()
jpayne@69 301 if len(previousClass.tearDown_exceptions) > 0:
jpayne@69 302 for exc in previousClass.tearDown_exceptions:
jpayne@69 303 className = util.strclass(previousClass)
jpayne@69 304 self._createClassOrModuleLevelException(result, exc[1],
jpayne@69 305 'tearDownClass',
jpayne@69 306 className,
jpayne@69 307 info=exc)
jpayne@69 308
jpayne@69 309
jpayne@69 310 class _ErrorHolder(object):
jpayne@69 311 """
jpayne@69 312 Placeholder for a TestCase inside a result. As far as a TestResult
jpayne@69 313 is concerned, this looks exactly like a unit test. Used to insert
jpayne@69 314 arbitrary errors into a test suite run.
jpayne@69 315 """
jpayne@69 316 # Inspired by the ErrorHolder from Twisted:
jpayne@69 317 # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
jpayne@69 318
jpayne@69 319 # attribute used by TestResult._exc_info_to_string
jpayne@69 320 failureException = None
jpayne@69 321
jpayne@69 322 def __init__(self, description):
jpayne@69 323 self.description = description
jpayne@69 324
jpayne@69 325 def id(self):
jpayne@69 326 return self.description
jpayne@69 327
jpayne@69 328 def shortDescription(self):
jpayne@69 329 return None
jpayne@69 330
jpayne@69 331 def __repr__(self):
jpayne@69 332 return "<ErrorHolder description=%r>" % (self.description,)
jpayne@69 333
jpayne@69 334 def __str__(self):
jpayne@69 335 return self.id()
jpayne@69 336
jpayne@69 337 def run(self, result):
jpayne@69 338 # could call result.addError(...) - but this test-like object
jpayne@69 339 # shouldn't be run anyway
jpayne@69 340 pass
jpayne@69 341
jpayne@69 342 def __call__(self, result):
jpayne@69 343 return self.run(result)
jpayne@69 344
jpayne@69 345 def countTestCases(self):
jpayne@69 346 return 0
jpayne@69 347
jpayne@69 348 def _isnotsuite(test):
jpayne@69 349 "A crude way to tell apart testcases and suites with duck-typing"
jpayne@69 350 try:
jpayne@69 351 iter(test)
jpayne@69 352 except TypeError:
jpayne@69 353 return True
jpayne@69 354 return False
jpayne@69 355
jpayne@69 356
jpayne@69 357 class _DebugResult(object):
jpayne@69 358 "Used by the TestSuite to hold previous class when running in debug."
jpayne@69 359 _previousTestClass = None
jpayne@69 360 _moduleSetUpFailed = False
jpayne@69 361 shouldStop = False