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