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