mirror of
https://github.com/python/cpython.git
synced 2025-08-04 00:48:58 +00:00
[3.9] bpo-43913: Fix bugs in cleaning up classes and modules in unittest. (GH-28006) (GH-28071)
* Functions registered with addModuleCleanup() were not called unless
the user defines tearDownModule() in their test module.
* Functions registered with addClassCleanup() were not called if
tearDownClass is set to None.
* Buffering in TestResult did not work with functions registered
with addClassCleanup() and addModuleCleanup().
* Errors in functions registered with addClassCleanup() and
addModuleCleanup() were not handled correctly in buffered and
debug modes.
* Errors in setUpModule() and functions registered with
addModuleCleanup() were reported in wrong order.
* And several lesser bugs..
(cherry picked from commit 08d9e597c8
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
720aef48b5
commit
9827710a40
4 changed files with 719 additions and 69 deletions
|
@ -149,6 +149,7 @@ class TestSuite(BaseTestSuite):
|
|||
if getattr(currentClass, "__unittest_skip__", False):
|
||||
return
|
||||
|
||||
failed = False
|
||||
try:
|
||||
currentClass._classSetupFailed = False
|
||||
except TypeError:
|
||||
|
@ -157,27 +158,32 @@ class TestSuite(BaseTestSuite):
|
|||
pass
|
||||
|
||||
setUpClass = getattr(currentClass, 'setUpClass', None)
|
||||
doClassCleanups = getattr(currentClass, 'doClassCleanups', None)
|
||||
if setUpClass is not None:
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
try:
|
||||
setUpClass()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
currentClass._classSetupFailed = True
|
||||
className = util.strclass(currentClass)
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpClass',
|
||||
className)
|
||||
try:
|
||||
setUpClass()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
failed = True
|
||||
try:
|
||||
currentClass._classSetupFailed = True
|
||||
except TypeError:
|
||||
pass
|
||||
className = util.strclass(currentClass)
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpClass',
|
||||
className)
|
||||
if failed and doClassCleanups is not None:
|
||||
doClassCleanups()
|
||||
for exc_info in currentClass.tearDown_exceptions:
|
||||
self._createClassOrModuleLevelException(
|
||||
result, exc_info[1], 'setUpClass', className,
|
||||
info=exc_info)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
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 _get_previous_module(self, result):
|
||||
previousModule = None
|
||||
|
@ -205,20 +211,22 @@ class TestSuite(BaseTestSuite):
|
|||
if setUpModule is not None:
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
try:
|
||||
setUpModule()
|
||||
except Exception as e:
|
||||
try:
|
||||
case.doModuleCleanups()
|
||||
except Exception as exc:
|
||||
self._createClassOrModuleLevelException(result, exc,
|
||||
setUpModule()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
result._moduleSetUpFailed = True
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpModule',
|
||||
currentModule)
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
result._moduleSetUpFailed = True
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpModule',
|
||||
currentModule)
|
||||
if result._moduleSetUpFailed:
|
||||
try:
|
||||
case.doModuleCleanups()
|
||||
except Exception as e:
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'setUpModule',
|
||||
currentModule)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
|
||||
|
@ -251,30 +259,33 @@ class TestSuite(BaseTestSuite):
|
|||
except KeyError:
|
||||
return
|
||||
|
||||
tearDownModule = getattr(module, 'tearDownModule', None)
|
||||
if tearDownModule is not None:
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
try:
|
||||
tearDownModule = getattr(module, 'tearDownModule', None)
|
||||
if tearDownModule is not None:
|
||||
try:
|
||||
tearDownModule()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownModule',
|
||||
previousModule)
|
||||
try:
|
||||
tearDownModule()
|
||||
case.doModuleCleanups()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownModule',
|
||||
previousModule)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
try:
|
||||
case.doModuleCleanups()
|
||||
except Exception as e:
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownModule',
|
||||
previousModule)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
|
||||
def _tearDownPreviousClass(self, test, result):
|
||||
previousClass = getattr(result, '_previousTestClass', None)
|
||||
currentClass = test.__class__
|
||||
if currentClass == previousClass:
|
||||
if currentClass == previousClass or previousClass is None:
|
||||
return
|
||||
if getattr(previousClass, '_classSetupFailed', False):
|
||||
return
|
||||
|
@ -284,27 +295,34 @@ class TestSuite(BaseTestSuite):
|
|||
return
|
||||
|
||||
tearDownClass = getattr(previousClass, 'tearDownClass', None)
|
||||
if tearDownClass is not None:
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
try:
|
||||
tearDownClass()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
className = util.strclass(previousClass)
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownClass',
|
||||
className)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
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)
|
||||
doClassCleanups = getattr(previousClass, 'doClassCleanups', None)
|
||||
if tearDownClass is None and doClassCleanups is None:
|
||||
return
|
||||
|
||||
_call_if_exists(result, '_setupStdout')
|
||||
try:
|
||||
if tearDownClass is not None:
|
||||
try:
|
||||
tearDownClass()
|
||||
except Exception as e:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise
|
||||
className = util.strclass(previousClass)
|
||||
self._createClassOrModuleLevelException(result, e,
|
||||
'tearDownClass',
|
||||
className)
|
||||
if doClassCleanups is not None:
|
||||
doClassCleanups()
|
||||
for exc_info in previousClass.tearDown_exceptions:
|
||||
if isinstance(result, _DebugResult):
|
||||
raise exc_info[1]
|
||||
className = util.strclass(previousClass)
|
||||
self._createClassOrModuleLevelException(result, exc_info[1],
|
||||
'tearDownClass',
|
||||
className,
|
||||
info=exc_info)
|
||||
finally:
|
||||
_call_if_exists(result, '_restoreStdout')
|
||||
|
||||
|
||||
class _ErrorHolder(object):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue