Fix handling of MultipleException
Change-Id: I187c364db9e41c437e9d6dbcbc6df7bfadba43bb
This commit is contained in:
parent
d41a755991
commit
5fe0a7d610
@ -33,6 +33,8 @@ fail = _asserts.fail
|
||||
TobikoException = _exception.TobikoException
|
||||
check_valid_type = _exception.check_valid_type
|
||||
exc_info = _exception.exc_info
|
||||
handle_multiple_exceptions = _exception.handle_multiple_exceptions
|
||||
list_exc_infos = _exception.list_exc_infos
|
||||
|
||||
is_fixture = _fixture.is_fixture
|
||||
get_fixture = _fixture.get_fixture
|
||||
|
@ -13,11 +13,13 @@
|
||||
# under the License.
|
||||
from __future__ import absolute_import
|
||||
|
||||
import contextlib
|
||||
import collections
|
||||
import sys
|
||||
|
||||
from oslo_log import log
|
||||
import six
|
||||
import testtools
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -107,3 +109,36 @@ def exc_info(reraise=True):
|
||||
info = ExceptionInfo(*sys.exc_info())
|
||||
info.reraise_on_exit = reraise
|
||||
return info
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def handle_multiple_exceptions():
|
||||
try:
|
||||
yield
|
||||
except testtools.MultipleExceptions as exc:
|
||||
exc_infos = list_exc_infos()
|
||||
if exc_infos:
|
||||
for info in exc_infos[1:]:
|
||||
LOG.exception("Unhandled exception:", exc_info=info)
|
||||
six.reraise(*exc_infos[0])
|
||||
else:
|
||||
LOG.debug('empty MultipleExceptions: %s', str(exc))
|
||||
|
||||
|
||||
def list_exc_infos(exc_info=None):
|
||||
# pylint: disable=redefined-outer-name
|
||||
exc_info = exc_info or sys.exc_info()
|
||||
result = []
|
||||
if exc_info[0]:
|
||||
visited = set()
|
||||
visiting = [exc_info]
|
||||
while visiting:
|
||||
exc_info = visiting.pop()
|
||||
_, exc, _ = exc_info
|
||||
if exc not in visited:
|
||||
visited.add(exc)
|
||||
if isinstance(exc, testtools.MultipleExceptions):
|
||||
visiting.extend(reversed(exc.args))
|
||||
else:
|
||||
result.append(exc_info)
|
||||
return result
|
||||
|
@ -23,6 +23,7 @@ import testtools
|
||||
|
||||
import tobiko
|
||||
from tobiko.common import _detail
|
||||
from tobiko.common import _exception
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
@ -98,33 +99,24 @@ def remove_fixture(obj, manager=None):
|
||||
def setup_fixture(obj, manager=None):
|
||||
'''Get registered fixture and setup it up'''
|
||||
fixture = get_fixture(obj, manager=manager)
|
||||
try:
|
||||
with _exception.handle_multiple_exceptions():
|
||||
fixture.setUp()
|
||||
except testtools.MultipleExceptions as ex:
|
||||
for exc_info in ex.args[1:]:
|
||||
LOG.exception("Error setting up fixture %r",
|
||||
fixture.fixture_name, exc_info=exc_info)
|
||||
six.reraise(*ex.args[0])
|
||||
return fixture
|
||||
|
||||
|
||||
def reset_fixture(obj, manager=None):
|
||||
'''Get registered fixture and setup it up'''
|
||||
'''Get registered fixture and reset it'''
|
||||
fixture = get_fixture(obj, manager=manager)
|
||||
try:
|
||||
with _exception.handle_multiple_exceptions():
|
||||
fixture.reset()
|
||||
except testtools.MultipleExceptions as ex:
|
||||
for exc_info in ex.args[1:]:
|
||||
LOG.exception("Error reseting fixture %r",
|
||||
fixture.fixture_name, exc_info=exc_info)
|
||||
six.reraise(*ex.args[0])
|
||||
return fixture
|
||||
|
||||
|
||||
def cleanup_fixture(obj, manager=None):
|
||||
'''Get registered fixture and clean it up'''
|
||||
fixture = get_fixture(obj, manager=manager)
|
||||
fixture.cleanUp()
|
||||
with _exception.handle_multiple_exceptions():
|
||||
fixture.cleanUp()
|
||||
return fixture
|
||||
|
||||
|
||||
|
@ -17,6 +17,7 @@ import sys
|
||||
|
||||
import tobiko
|
||||
from tobiko.tests import unit
|
||||
import testtools
|
||||
|
||||
|
||||
class SomeException(tobiko.TobikoException):
|
||||
@ -122,3 +123,95 @@ class TestExcInfo(unit.TobikoUnitTest):
|
||||
|
||||
reraised = self.assertRaises(RuntimeError, exc_info.reraise)
|
||||
self.assertIs(exc_value, reraised)
|
||||
|
||||
|
||||
class TestListExcInfo(unit.TobikoUnitTest):
|
||||
|
||||
def test_list_exc_info(self):
|
||||
result = tobiko.list_exc_infos()
|
||||
self.assertEqual([], result)
|
||||
|
||||
def test_list_exc_info_with_info(self):
|
||||
error = make_exception(RuntimeError, 'error')
|
||||
result = tobiko.list_exc_infos(exc_info=error)
|
||||
self.assertEqual([error], result)
|
||||
|
||||
def test_list_exc_info_handling_exception(self):
|
||||
try:
|
||||
raise RuntimeError('error')
|
||||
except RuntimeError:
|
||||
result = tobiko.list_exc_infos()
|
||||
self.assertEqual([sys.exc_info()], result)
|
||||
|
||||
def test_list_exc_info_handling_empty_multiple_exceptions(self):
|
||||
error = make_exception(testtools.MultipleExceptions)
|
||||
result = tobiko.list_exc_infos(exc_info=error)
|
||||
self.assertEqual([], result)
|
||||
|
||||
def test_list_exc_info_handling_multiple_exceptions(self):
|
||||
a = make_exception(RuntimeError, 'a')
|
||||
b = make_exception(ValueError, 'b')
|
||||
c = make_exception(TypeError, 'c')
|
||||
multi = make_exception(testtools.MultipleExceptions, a, b, c)
|
||||
result = tobiko.list_exc_infos(exc_info=multi)
|
||||
self.assertEqual([a, b, c], result)
|
||||
|
||||
def test_list_exc_info_handling_nested_multiple_exceptions(self):
|
||||
a = make_exception(RuntimeError, 'a')
|
||||
b = make_exception(ValueError, 'b')
|
||||
c = make_exception(TypeError, 'c')
|
||||
d = make_exception(IndexError, 'd')
|
||||
inner = make_exception(testtools.MultipleExceptions, b, c)
|
||||
multi = make_exception(testtools.MultipleExceptions, a, inner, d)
|
||||
result = tobiko.list_exc_infos(exc_info=multi)
|
||||
self.assertEqual([a, b, c, d], result)
|
||||
|
||||
|
||||
class TestHandleMultipleExceptions(unit.TobikoUnitTest):
|
||||
|
||||
def test_handle_multiple_exceptions(self):
|
||||
with tobiko.handle_multiple_exceptions():
|
||||
pass
|
||||
|
||||
def test_handle_multiple_exceptions_with_exception(self):
|
||||
def run():
|
||||
with tobiko.handle_multiple_exceptions():
|
||||
raise RuntimeError('error')
|
||||
self.assertRaises(RuntimeError, run)
|
||||
|
||||
def test_handle_multiple_exceptions_with_empty_multiple_exception(self):
|
||||
with tobiko.handle_multiple_exceptions():
|
||||
raise testtools.MultipleExceptions()
|
||||
|
||||
def test_handle_multiple_exceptions_with_multiple_exceptions(self):
|
||||
a = make_exception(TypeError, 'a')
|
||||
b = make_exception(ValueError, 'b')
|
||||
c = make_exception(RuntimeError, 'c')
|
||||
|
||||
def run():
|
||||
with tobiko.handle_multiple_exceptions():
|
||||
raise testtools.MultipleExceptions(a, b, c)
|
||||
|
||||
ex = self.assertRaises(TypeError, run)
|
||||
self.assertEqual(a[1], ex)
|
||||
|
||||
def test_handle_multiple_exceptions_with_nested_multiple_exceptions(self):
|
||||
a = make_exception(RuntimeError, 'a')
|
||||
b = make_exception(ValueError, 'b')
|
||||
c = make_exception(TypeError, 'c')
|
||||
d = make_exception(IndexError, 'd')
|
||||
inner = make_exception(testtools.MultipleExceptions, b, c)
|
||||
|
||||
def run():
|
||||
with tobiko.handle_multiple_exceptions():
|
||||
raise testtools.MultipleExceptions(a, inner, d)
|
||||
|
||||
ex = self.assertRaises(RuntimeError, run)
|
||||
self.assertEqual(a[1], ex)
|
||||
|
||||
|
||||
def make_exception(cls, *args, **kwargs):
|
||||
try:
|
||||
raise cls(*args, **kwargs)
|
||||
except cls:
|
||||
return sys.exc_info()
|
||||
|
Loading…
Reference in New Issue
Block a user