From 903e85083d3b3640ba3eb728a9936bea05209434 Mon Sep 17 00:00:00 2001 From: Federico Ressi Date: Sat, 2 Mar 2019 11:22:00 +0100 Subject: [PATCH] Rebase fixture management on top fo fixtures library. Change-Id: Idb34d4bebc1be6d753e4805f58a1a71c84ee15b7 --- requirements.txt | 2 + tobiko/__init__.py | 15 +- tobiko/common/managers/fixture.py | 247 +++++++++++------------------- tobiko/tests/test_fixture.py | 143 +++++++++-------- 4 files changed, 183 insertions(+), 224 deletions(-) diff --git a/requirements.txt b/requirements.txt index e807d7d5d..34edc9032 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,6 @@ # Tobiko framework requirements +fixtures>=3.0.0 # Apache-2.0/BSD keystoneauth1>=2.0.0 # Apache-2.0 oslo.config>=5.2.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-novaclient>=9.1.0 # Apache-2.0 stestr>=2.0 # Apache-2.0 +six>=1.10.0 # MIT testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD diff --git a/tobiko/__init__.py b/tobiko/__init__.py index 1e7a05660..ee37cc5e3 100644 --- a/tobiko/__init__.py +++ b/tobiko/__init__.py @@ -22,13 +22,12 @@ load_module = loader_manager.load_module discover_testcases = testcase_manager.discover_testcases -Fixture = fixture_manager.Fixture -fixture = fixture_manager.fixture is_fixture = fixture_manager.is_fixture get_fixture = fixture_manager.get_fixture -create_fixture = fixture_manager.create_fixture -delete_fixture = fixture_manager.delete_fixture -get_required_fixtures = fixture_manager.get_required_fixtures -discover_required_fixtures = fixture_manager.discover_required_fixtures -create_fixtures = fixture_manager.create_fixtures -delete_fixtures = fixture_manager.delete_fixtures +get_fixture_name = fixture_manager.get_fixture_name +setup_fixture = fixture_manager.setup_fixture +cleanup_fixture = fixture_manager.cleanup_fixture +list_required_fixtures = fixture_manager.list_required_fixtures +iter_required_fixtures = fixture_manager.iter_required_fixtures +setup_required_fixtures = fixture_manager.setup_required_fixtures +cleanup_required_fixtures = fixture_manager.cleanup_required_fixtures diff --git a/tobiko/common/managers/fixture.py b/tobiko/common/managers/fixture.py index c41df4728..c1629f5ae 100644 --- a/tobiko/common/managers/fixture.py +++ b/tobiko/common/managers/fixture.py @@ -13,22 +13,18 @@ # under the License. from __future__ import absolute_import -import abc -import contextlib import inspect +import fixtures import six import tobiko -def fixture(obj): - obj.__tobiko_fixture__ = True - return object - - 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): @@ -36,183 +32,109 @@ def get_fixture(obj, manager=None): return manager.get_fixture(obj) -def create_fixture(obj, manager=None): - manager = manager or FIXTURES - return manager.create_fixture(obj) +def get_fixture_name(obj): + return get_fixture(obj).__tobiko_fixture_name__ -def delete_fixture(obj, manager=None): - manager = manager or FIXTURES - return manager.delete_fixture(obj) +def setup_fixture(obj, manager=None): + get_fixture(obj, manager=manager).setUp() -def get_required_fixtures(obj, manager=None): - manager = manager or FIXTURES - return manager.get_required_fixtures(obj) +def cleanup_fixture(obj, manager=None): + get_fixture(obj, manager=manager).cleanUp() -def discover_required_fixtures(objects, manager=None): - manager = manager or FIXTURES - return sorted(set(manager.discover_required_fixtures(objects, manager))) - - -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): +def iter_required_fixtures(objects): + objects = list(objects) + while objects: + obj = objects.pop() if isinstance(obj, six.string_types): - if name != obj: - msg = ("Fixture name mismatch: " - "{!r} != {!r}").format(name, obj.fixture_name) - raise ValueError(msg) - obj = tobiko.load_object(name) + 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) - 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): - if issubclass(obj, Fixture): - return obj(fixture_name=name) - elif inspect.isgeneratorfunction(obj): - return ContextFixture( - 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) + 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): - - __tobiko_fixture__ = True - - def __init__(self, fixture_name): - self.fixture_name = fixture_name - - @abc.abstractmethod - def create_fixture(self): - pass - - def delete_fixture(self): - pass +def setup_required_fixtures(objects, manager=None): + manager = manager or FIXTURES + for _fixture in iter_required_fixtures(objects=objects): + manager.get_fixture(_fixture).setUp() -class FunctionFixture(Fixture): - - def __init__(self, fixture_name, function): - super(FunctionFixture, self).__init__(fixture_name=fixture_name) - assert callable(function) - self.function = function - - def create_fixture(self): - return self.function() +def cleanup_required_fixtures(objects, manager=None): + manager = manager or FIXTURES + for _fixture in iter_required_fixtures(objects=objects): + manager.get_fixture(_fixture).cleanUp() -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): - super(ContextFixture, self).__init__(fixture_name=fixture_name) - self.context = context + if (inspect.isclass(obj) and issubclass(obj, fixtures.Fixture)): + obj = obj() - def create_fixture(self): - return self.context.__enter__() + if isinstance(obj, fixtures.Fixture): + obj.__tobiko_fixture__ = True + obj.__tobiko_fixture_name__ = name + return obj - def delete_fixture(self): - return self.context.__exit__(None, None, None) + raise TypeError("Invalid fixture object type: {!r}".format(object)) def get_object_name(obj): if isinstance(obj, six.string_types): return obj - if is_fixture(obj): - name = getattr(obj, 'fixture_name', None) - if name: - return name - - name = getattr(obj, '__qualname__', None) + name = getattr(obj, '__tobiko_fixture_name__', None) if name: - return obj.__module__ + '.' + name + return name module = inspect.getmodule(obj).__name__ - if inspect.isclass(obj): - return module + '.' + obj.__name__ - parent_class = getattr(obj, 'im_class', None) - if parent_class: - return module + parent_class.__name__ + '.' + obj.__name__ + if six.PY2: + # Below code is only for old Python versions + if inspect.isclass(obj): + # This doesn't work for nested classes + return module + '.' + obj.__name__ - if inspect.isfunction(obj): - return module + '.' + obj.func_name + method_class = getattr(obj, 'im_class', None) + 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) raise TypeError(msg) @@ -231,3 +153,20 @@ def get_default_param_values(obj): # Use old deprecated function 'getargspec' return list(inspect.getargspec(obj).defaults or # pylint: disable=W1505 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() diff --git a/tobiko/tests/test_fixture.py b/tobiko/tests/test_fixture.py index 8fa2fb1b5..a5b04ea28 100644 --- a/tobiko/tests/test_fixture.py +++ b/tobiko/tests/test_fixture.py @@ -13,90 +13,109 @@ # under the License. from __future__ import absolute_import +import fixtures + import tobiko from tobiko.tests import base -class TestFixture(tobiko.Fixture): +class MyFixtureClass(fixtures.Fixture): - created = False - deleted = False + def _setUp(self): + self.setup_executed = True + self.addCleanup(self._cleanUp) - def reset(self): - self.created = False - self.deleted = False + def _cleanUp(self): + self.cleanup_executed = True - def create_fixture(self): - self.created = True - return 'created' - def delete_fixture(self): - self.deleted = True - return 'deleted' +MY_FIXTURE_NAME = __name__ + '.' + MyFixtureClass.__name__ 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): - self._test_get_fixture(self.fixture_name) + self._test_get_fixture(MY_FIXTURE_NAME, fixture_type=MyFixtureClass) 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) - self.assertIs(self.fixture, fixture) - self.assertFalse(fixture.created) - self.assertFalse(fixture.deleted) + self.assertIsInstance(fixture, fixture_type) + self.assertIs(fixture, tobiko.get_fixture(obj)) - def test_create_fixture_by_name(self): - self._test_create_fixture(self.fixture_name) + def test_get_name(self): + 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): - self._test_create_fixture(self.fixture_type) + def test_setup_fixture_by_name(self): + self._test_setup_fixture(MY_FIXTURE_NAME) - def _test_create_fixture(self, obj): - result = tobiko.create_fixture(obj) - self.assertEqual('created', result) - self.assertTrue(self.fixture.created) - self.assertFalse(self.fixture.deleted) + def test_setup_fixture_by_type(self): + self._test_setup_fixture(MyFixtureClass) - def test_delete_fixture_by_name(self): - self._test_delete_fixture(self.fixture_name) + def _test_setup_fixture(self, obj): + fixture = tobiko.get_fixture(obj) + fixture.setup_executed = False - def test_delete_fixture_by_type(self): - self._test_delete_fixture(self.fixture_type) + tobiko.setup_fixture(obj) - def _test_delete_fixture(self, obj=TestFixture): - result = tobiko.delete_fixture(obj) - self.assertEqual('deleted', result) - self.assertFalse(self.fixture.created) - self.assertTrue(self.fixture.deleted) + self.assertTrue(fixture.setup_executed) - def test_get_required_fixtures_from_method_by_type( - self, _required_fixture=TestFixture): - result = tobiko.get_required_fixtures(self.id()) - self.assertEqual([self.fixture_name], result) + def test_cleanup_fixture_by_name(self): + self._test_cleanup_fixture(MY_FIXTURE_NAME) - def test_get_required_fixtures_from_test_class( - self, _required_fixture=TestFixture): - result = tobiko.get_required_fixtures(FixtureTypeTest) - self.assertEqual([self.fixture_name], result) + def test_cleanup_fixture_by_type(self): + self._test_cleanup_fixture(MyFixtureClass) + + 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)