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