123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- """
- Vendor unittest.TestSuite
- This is a modified version of python 3.8 unitest.TestSuite
- Odoo tests customisation combined with the need of a cross version compatibility
- started to make TestSuite and other unitest object more complicated than vendoring
- the part we need for Odoo. This versions is simplified in order
- to minimise the code to maintain
- - Removes expected failure support
- - Removes module setUp/tearDown support
- """
- import logging
- import sys
- from . import case
- from .common import HttpCase
- from .result import stats_logger
- from unittest import util, BaseTestSuite, TestCase
- __unittest = True
- class TestSuite(BaseTestSuite):
- """A test suite is a composite test consisting of a number of TestCases.
- For use, create an instance of TestSuite, then add test case instances.
- When all tests have been added, the suite can be passed to a test
- runner, such as TextTestRunner. It will run the individual test cases
- in the order in which they were added, aggregating the results. When
- subclassing, do not forget to call the base class constructor.
- """
- def run(self, result, debug=False):
- for test in self:
- assert isinstance(test, (TestCase))
- self._tearDownPreviousClass(test, result)
- self._handleClassSetUp(test, result)
- result._previousTestClass = test.__class__
- if not test.__class__._classSetupFailed:
- test(result)
- self._tearDownPreviousClass(None, result)
- return result
- def _handleClassSetUp(self, test, result):
- previousClass = result._previousTestClass
- currentClass = test.__class__
- if currentClass == previousClass:
- return
- if result._moduleSetUpFailed:
- return
- if getattr(currentClass, "__unittest_skip__", False):
- return
- currentClass._classSetupFailed = False
- try:
- currentClass.setUpClass()
- except Exception as e:
- currentClass._classSetupFailed = True
- className = util.strclass(currentClass)
- self._createClassOrModuleLevelException(result, e,
- 'setUpClass',
- className)
- finally:
- if currentClass._classSetupFailed is True:
- currentClass.doClassCleanups()
- if len(currentClass.tearDown_exceptions) > 0:
- for exc in currentClass.tearDown_exceptions:
- self._createClassOrModuleLevelException(
- result, exc[1], 'setUpClass', className,
- info=exc)
- def _createClassOrModuleLevelException(self, result, exception, method_name,
- parent, info=None):
- errorName = f'{method_name} ({parent})'
- error = _ErrorHolder(errorName)
- if isinstance(exception, case.SkipTest):
- result.addSkip(error, str(exception))
- else:
- if not info:
- result.addError(error, sys.exc_info())
- else:
- result.addError(error, info)
- def _tearDownPreviousClass(self, test, result):
- previousClass = result._previousTestClass
- currentClass = test.__class__
- if currentClass == previousClass:
- return
- if not previousClass:
- return
- if previousClass._classSetupFailed:
- return
- if getattr(previousClass, "__unittest_skip__", False):
- return
- try:
- previousClass.tearDownClass()
- except Exception as e:
- className = util.strclass(previousClass)
- self._createClassOrModuleLevelException(result, e,
- 'tearDownClass',
- className)
- finally:
- previousClass.doClassCleanups()
- if len(previousClass.tearDown_exceptions) > 0:
- for exc in previousClass.tearDown_exceptions:
- className = util.strclass(previousClass)
- self._createClassOrModuleLevelException(result, exc[1],
- 'tearDownClass',
- className,
- info=exc)
- class _ErrorHolder(object):
- """
- Placeholder for a TestCase inside a result. As far as a TestResult
- is concerned, this looks exactly like a unit test. Used to insert
- arbitrary errors into a test suite run.
- """
- # Inspired by the ErrorHolder from Twisted:
- # http://twistedmatrix.com/trac/browser/trunk/twisted/trial/runner.py
- # attribute used by TestResult._exc_info_to_string
- failureException = None
- def __init__(self, description):
- self.description = description
- def id(self):
- return self.description
- def shortDescription(self):
- return None
- def __repr__(self):
- return "<ErrorHolder description=%r>" % (self.description,)
- def __str__(self):
- return self.id()
- def run(self, result):
- # could call result.addError(...) - but this test-like object
- # shouldn't be run anyway
- pass
- def __call__(self, result):
- return self.run(result)
- def countTestCases(self):
- return 0
- class OdooSuite(TestSuite):
- def _handleClassSetUp(self, test, result):
- previous_test_class = result._previousTestClass
- if not (
- previous_test_class != type(test)
- and hasattr(result, 'stats')
- and stats_logger.isEnabledFor(logging.INFO)
- ):
- super()._handleClassSetUp(test, result)
- return
- test_class = type(test)
- test_id = f'{test_class.__module__}.{test_class.__qualname__}.setUpClass'
- with result.collectStats(test_id):
- super()._handleClassSetUp(test, result)
- def _tearDownPreviousClass(self, test, result):
- previous_test_class = result._previousTestClass
- if not (
- previous_test_class
- and previous_test_class != type(test)
- and hasattr(result, 'stats')
- and stats_logger.isEnabledFor(logging.INFO)
- ):
- super()._tearDownPreviousClass(test, result)
- return
- test_id = f'{previous_test_class.__module__}.{previous_test_class.__qualname__}.tearDownClass'
- with result.collectStats(test_id):
- super()._tearDownPreviousClass(test, result)
- def has_http_case(self):
- return self.countTestCases() and any(isinstance(test_case, HttpCase) for test_case in self)
|