Rebase fixture management on top fo fixtures library.

Change-Id: Idb34d4bebc1be6d753e4805f58a1a71c84ee15b7
This commit is contained in:
Federico Ressi 2019-03-02 11:22:00 +01:00
parent 88943f69f5
commit 903e85083d
4 changed files with 183 additions and 224 deletions

View File

@ -1,5 +1,6 @@
# Tobiko framework requirements # Tobiko framework requirements
fixtures>=3.0.0 # Apache-2.0/BSD
keystoneauth1>=2.0.0 # Apache-2.0 keystoneauth1>=2.0.0 # Apache-2.0
oslo.config>=5.2.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0
@ -7,5 +8,6 @@ python-heatclient>=1.5.0 # Apache-2.0
python-neutronclient>=6.7.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0
python-novaclient>=9.1.0 # Apache-2.0 python-novaclient>=9.1.0 # Apache-2.0
stestr>=2.0 # Apache-2.0 stestr>=2.0 # Apache-2.0
six>=1.10.0 # MIT
testtools>=2.2.0 # MIT testtools>=2.2.0 # MIT
testscenarios>=0.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD

View File

@ -22,13 +22,12 @@ load_module = loader_manager.load_module
discover_testcases = testcase_manager.discover_testcases discover_testcases = testcase_manager.discover_testcases
Fixture = fixture_manager.Fixture
fixture = fixture_manager.fixture
is_fixture = fixture_manager.is_fixture is_fixture = fixture_manager.is_fixture
get_fixture = fixture_manager.get_fixture get_fixture = fixture_manager.get_fixture
create_fixture = fixture_manager.create_fixture get_fixture_name = fixture_manager.get_fixture_name
delete_fixture = fixture_manager.delete_fixture setup_fixture = fixture_manager.setup_fixture
get_required_fixtures = fixture_manager.get_required_fixtures cleanup_fixture = fixture_manager.cleanup_fixture
discover_required_fixtures = fixture_manager.discover_required_fixtures list_required_fixtures = fixture_manager.list_required_fixtures
create_fixtures = fixture_manager.create_fixtures iter_required_fixtures = fixture_manager.iter_required_fixtures
delete_fixtures = fixture_manager.delete_fixtures setup_required_fixtures = fixture_manager.setup_required_fixtures
cleanup_required_fixtures = fixture_manager.cleanup_required_fixtures

View File

@ -13,22 +13,18 @@
# under the License. # under the License.
from __future__ import absolute_import from __future__ import absolute_import
import abc
import contextlib
import inspect import inspect
import fixtures
import six import six
import tobiko import tobiko
def fixture(obj):
obj.__tobiko_fixture__ = True
return object
def is_fixture(obj): def is_fixture(obj):
return getattr(obj, '__tobiko_fixture__', False) return (getattr(obj, '__tobiko_fixture__', False) or
isinstance(obj, fixtures.Fixture) or
(inspect.isclass(obj) and issubclass(obj, fixtures.Fixture)))
def get_fixture(obj, manager=None): def get_fixture(obj, manager=None):
@ -36,183 +32,109 @@ def get_fixture(obj, manager=None):
return manager.get_fixture(obj) return manager.get_fixture(obj)
def create_fixture(obj, manager=None): def get_fixture_name(obj):
manager = manager or FIXTURES return get_fixture(obj).__tobiko_fixture_name__
return manager.create_fixture(obj)
def delete_fixture(obj, manager=None): def setup_fixture(obj, manager=None):
manager = manager or FIXTURES get_fixture(obj, manager=manager).setUp()
return manager.delete_fixture(obj)
def get_required_fixtures(obj, manager=None): def cleanup_fixture(obj, manager=None):
manager = manager or FIXTURES get_fixture(obj, manager=manager).cleanUp()
return manager.get_required_fixtures(obj)
def discover_required_fixtures(objects, manager=None): def iter_required_fixtures(objects):
manager = manager or FIXTURES objects = list(objects)
return sorted(set(manager.discover_required_fixtures(objects, manager))) while objects:
obj = objects.pop()
def create_fixtures(objects, manager=None):
manager = manager or FIXTURES
for _fixture in discover_required_fixtures(objects=objects,
manager=manager):
manager.create_fixture(_fixture)
def delete_fixtures(objects, manager=None):
manager = manager or FIXTURES
for _fixture in discover_required_fixtures(objects=objects,
manager=manager):
return manager.delete_fixture(_fixture)
class FixtureManager(object):
def __init__(self):
self.fixtures = {}
def get_fixture(self, obj):
name = get_object_name(obj)
_fixture = self.fixtures.get(name)
if _fixture is None:
_fixture = self.init_fixture(name=name, obj=obj)
assert isinstance(_fixture, Fixture)
self.fixtures[name] = _fixture
return _fixture
def create_fixture(self, obj):
return self.get_fixture(obj).create_fixture()
def delete_fixture(self, obj):
return self.get_fixture(obj).delete_fixture()
def init_fixture(self, obj, name):
if isinstance(obj, six.string_types): if isinstance(obj, six.string_types):
if name != obj: object_id = obj
msg = ("Fixture name mismatch: " obj = tobiko.load_object(object_id)
"{!r} != {!r}").format(name, obj.fixture_name) else:
raise ValueError(msg) object_id = get_object_name(obj)
obj = tobiko.load_object(name)
if is_fixture(obj):
yield object_id
elif inspect.isfunction(obj) or inspect.ismethod(obj):
for default in get_default_param_values(obj):
if is_fixture(default):
yield get_object_name(default)
if inspect.ismodule(obj):
members = [obj for _, obj in inspect.getmembers(obj)
if (inspect.isfunction(obj) or
inspect.isclass(obj))]
objects.extend(members)
if isinstance(obj, Fixture):
if name != obj.fixture_name:
msg = ("Fixture name mismatch: "
"{!r} != {!r}").format(name, obj.fixture_name)
raise ValueError(msg)
return obj
elif inspect.isclass(obj): elif inspect.isclass(obj):
if issubclass(obj, Fixture): members = [obj for _, obj in inspect.getmembers(obj)
return obj(fixture_name=name) if (inspect.isfunction(obj) or
elif inspect.isgeneratorfunction(obj): inspect.ismethod(obj))]
return ContextFixture( objects.extend(members)
fixture_name=name, context=contextlib.contextmanager(obj))
elif inspect.isfunction(obj):
return FunctionFixture(fixture_name=name, function=obj)
raise TypeError("Invalid fixture object type: {!r}".format(object))
def get_required_fixtures(self, obj):
return sorted(set(self.discover_required_fixtures([obj])))
def discover_required_fixtures(self, objects):
objects = list(objects)
while objects:
obj = objects.pop()
if isinstance(obj, six.string_types):
object_id = obj
obj = tobiko.load_object(object_id)
else:
object_id = get_object_name(obj)
if is_fixture(obj):
yield object_id
elif inspect.isfunction(obj) or inspect.ismethod(obj):
for default in get_default_param_values(obj):
if is_fixture(default):
yield get_object_name(default)
if inspect.ismodule(obj):
members = [obj for _, obj in inspect.getmembers(obj)
if (inspect.isfunction(obj) or
inspect.isclass(obj))]
objects.extend(members)
elif inspect.isclass(obj):
members = [obj for _, obj in inspect.getmembers(obj)
if (inspect.isfunction(obj) or
inspect.ismethod(obj))]
objects.extend(members)
FIXTURES = FixtureManager() def list_required_fixtures(objects):
return sorted(set(iter_required_fixtures(objects)))
class Fixture(object): def setup_required_fixtures(objects, manager=None):
manager = manager or FIXTURES
__tobiko_fixture__ = True for _fixture in iter_required_fixtures(objects=objects):
manager.get_fixture(_fixture).setUp()
def __init__(self, fixture_name):
self.fixture_name = fixture_name
@abc.abstractmethod
def create_fixture(self):
pass
def delete_fixture(self):
pass
class FunctionFixture(Fixture): def cleanup_required_fixtures(objects, manager=None):
manager = manager or FIXTURES
def __init__(self, fixture_name, function): for _fixture in iter_required_fixtures(objects=objects):
super(FunctionFixture, self).__init__(fixture_name=fixture_name) manager.get_fixture(_fixture).cleanUp()
assert callable(function)
self.function = function
def create_fixture(self):
return self.function()
class ContextFixture(Fixture): def init_fixture(obj, name):
if isinstance(obj, six.string_types):
obj = tobiko.load_object(name)
def __init__(self, fixture_name, context): if (inspect.isclass(obj) and issubclass(obj, fixtures.Fixture)):
super(ContextFixture, self).__init__(fixture_name=fixture_name) obj = obj()
self.context = context
def create_fixture(self): if isinstance(obj, fixtures.Fixture):
return self.context.__enter__() obj.__tobiko_fixture__ = True
obj.__tobiko_fixture_name__ = name
return obj
def delete_fixture(self): raise TypeError("Invalid fixture object type: {!r}".format(object))
return self.context.__exit__(None, None, None)
def get_object_name(obj): def get_object_name(obj):
if isinstance(obj, six.string_types): if isinstance(obj, six.string_types):
return obj return obj
if is_fixture(obj): name = getattr(obj, '__tobiko_fixture_name__', None)
name = getattr(obj, 'fixture_name', None)
if name:
return name
name = getattr(obj, '__qualname__', None)
if name: if name:
return obj.__module__ + '.' + name return name
module = inspect.getmodule(obj).__name__ module = inspect.getmodule(obj).__name__
if inspect.isclass(obj):
return module + '.' + obj.__name__
parent_class = getattr(obj, 'im_class', None) if six.PY2:
if parent_class: # Below code is only for old Python versions
return module + parent_class.__name__ + '.' + obj.__name__ if inspect.isclass(obj):
# This doesn't work for nested classes
return module + '.' + obj.__name__
if inspect.isfunction(obj): method_class = getattr(obj, 'im_class', None)
return module + '.' + obj.func_name if method_class:
# This doesn't work for nested classes
return module + method_class.__name__ + '.' + obj.__name__
if inspect.isfunction(obj):
return module + '.' + obj.func_name
else:
# Only Python 3 defines __qualname__
name = getattr(obj, '__qualname__', None)
if name:
return module + '.' + name
msg = "Unable to get fixture name from object {!r}".format(obj) msg = "Unable to get fixture name from object {!r}".format(obj)
raise TypeError(msg) raise TypeError(msg)
@ -231,3 +153,20 @@ def get_default_param_values(obj):
# Use old deprecated function 'getargspec' # Use old deprecated function 'getargspec'
return list(inspect.getargspec(obj).defaults or # pylint: disable=W1505 return list(inspect.getargspec(obj).defaults or # pylint: disable=W1505
tuple()) tuple())
class FixtureManager(object):
def __init__(self):
self.fixtures = {}
def get_fixture(self, obj, init=init_fixture):
name = get_object_name(obj)
_fixture = self.fixtures.get(name)
if _fixture is None:
self.fixtures[name] = _fixture = init(name=name, obj=obj)
assert isinstance(_fixture, fixtures.Fixture)
return _fixture
FIXTURES = FixtureManager()

View File

@ -13,90 +13,109 @@
# under the License. # under the License.
from __future__ import absolute_import from __future__ import absolute_import
import fixtures
import tobiko import tobiko
from tobiko.tests import base from tobiko.tests import base
class TestFixture(tobiko.Fixture): class MyFixtureClass(fixtures.Fixture):
created = False def _setUp(self):
deleted = False self.setup_executed = True
self.addCleanup(self._cleanUp)
def reset(self): def _cleanUp(self):
self.created = False self.cleanup_executed = True
self.deleted = False
def create_fixture(self):
self.created = True
return 'created'
def delete_fixture(self): MY_FIXTURE_NAME = __name__ + '.' + MyFixtureClass.__name__
self.deleted = True
return 'deleted'
class FixtureTypeTest(base.TobikoTest): class FixtureTypeTest(base.TobikoTest):
fixture_type = TestFixture
fixture_name = __name__ + '.' + TestFixture.__name__
@classmethod
def setUpClass(cls):
super(FixtureTypeTest, cls).setUpClass()
cls.fixture = tobiko.get_fixture(cls.fixture_name)
def setUp(self):
super(FixtureTypeTest, self).setUp()
self.fixture.reset()
def test_fixture_type(self):
self.assertIsInstance(self.fixture, self.fixture_type)
def test_fixture_name(self):
self.assertEqual(self.fixture_name, self.fixture.fixture_name)
def test_get_fixture_by_name(self): def test_get_fixture_by_name(self):
self._test_get_fixture(self.fixture_name) self._test_get_fixture(MY_FIXTURE_NAME, fixture_type=MyFixtureClass)
def test_get_fixture_by_type(self): def test_get_fixture_by_type(self):
self._test_get_fixture(self.fixture_type) self._test_get_fixture(MyFixtureClass, fixture_type=MyFixtureClass)
def _test_get_fixture(self, obj): def _test_get_fixture(self, obj, fixture_type):
fixture = tobiko.get_fixture(obj) fixture = tobiko.get_fixture(obj)
self.assertIs(self.fixture, fixture) self.assertIsInstance(fixture, fixture_type)
self.assertFalse(fixture.created) self.assertIs(fixture, tobiko.get_fixture(obj))
self.assertFalse(fixture.deleted)
def test_create_fixture_by_name(self): def test_get_name(self):
self._test_create_fixture(self.fixture_name) fixture = tobiko.get_fixture(MY_FIXTURE_NAME)
result = tobiko.get_fixture_name(fixture)
self.assertEqual(MY_FIXTURE_NAME, result)
def test_create_fixture_by_type(self): def test_setup_fixture_by_name(self):
self._test_create_fixture(self.fixture_type) self._test_setup_fixture(MY_FIXTURE_NAME)
def _test_create_fixture(self, obj): def test_setup_fixture_by_type(self):
result = tobiko.create_fixture(obj) self._test_setup_fixture(MyFixtureClass)
self.assertEqual('created', result)
self.assertTrue(self.fixture.created)
self.assertFalse(self.fixture.deleted)
def test_delete_fixture_by_name(self): def _test_setup_fixture(self, obj):
self._test_delete_fixture(self.fixture_name) fixture = tobiko.get_fixture(obj)
fixture.setup_executed = False
def test_delete_fixture_by_type(self): tobiko.setup_fixture(obj)
self._test_delete_fixture(self.fixture_type)
def _test_delete_fixture(self, obj=TestFixture): self.assertTrue(fixture.setup_executed)
result = tobiko.delete_fixture(obj)
self.assertEqual('deleted', result)
self.assertFalse(self.fixture.created)
self.assertTrue(self.fixture.deleted)
def test_get_required_fixtures_from_method_by_type( def test_cleanup_fixture_by_name(self):
self, _required_fixture=TestFixture): self._test_cleanup_fixture(MY_FIXTURE_NAME)
result = tobiko.get_required_fixtures(self.id())
self.assertEqual([self.fixture_name], result)
def test_get_required_fixtures_from_test_class( def test_cleanup_fixture_by_type(self):
self, _required_fixture=TestFixture): self._test_cleanup_fixture(MyFixtureClass)
result = tobiko.get_required_fixtures(FixtureTypeTest)
self.assertEqual([self.fixture_name], result) def _test_cleanup_fixture(self, obj):
fixture = tobiko.get_fixture(obj)
fixture.setUp()
tobiko.cleanup_fixture(obj)
self.assertTrue(fixture.cleanup_executed)
def test_list_required_fixtures_from_module(self):
result = tobiko.list_required_fixtures([__name__])
self.assertEqual([MY_FIXTURE_NAME], result)
def test_list_required_fixtures_from_testcase_type(self):
result = tobiko.list_required_fixtures([FixtureTypeTest])
self.assertEqual([MY_FIXTURE_NAME], result)
def test_list_required_fixtures_from_fixture_type(self):
result = tobiko.list_required_fixtures([MyFixtureClass])
self.assertEqual([MY_FIXTURE_NAME], result)
def test_list_required_fixtures_from_fixture_name(self):
result = tobiko.list_required_fixtures([MY_FIXTURE_NAME])
self.assertEqual([MY_FIXTURE_NAME], result)
def test_list_required_fixtures_from_method(
self, fixture_type=MyFixtureClass):
result = tobiko.list_required_fixtures([self.id()])
self.assertEqual([MY_FIXTURE_NAME], result)
self.assertIsInstance(tobiko.get_fixture(MY_FIXTURE_NAME),
fixture_type)
def test_list_required_fixtures_from_fixture_object(self):
fixture = tobiko.get_fixture(MY_FIXTURE_NAME)
result = tobiko.list_required_fixtures([fixture])
self.assertEqual([MY_FIXTURE_NAME], result)
def test_setup_required_fixtures(self, fixture_type=MyFixtureClass):
fixture = tobiko.get_fixture(fixture_type)
fixture.setup_executed = False
tobiko.setup_required_fixtures([self.id()])
self.assertTrue(fixture.setup_executed)
def test_cleanup_required_fixtures(self, fixture_type=MyFixtureClass):
fixture = tobiko.get_fixture(fixture_type)
fixture.cleanup_executed = False
fixture.setUp()
tobiko.cleanup_required_fixtures([self.id()])
self.assertTrue(fixture.setup_executed)
self.assertTrue(fixture.cleanup_executed)