summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Rocco <drocco@thebrightlink.com>2013-11-23 23:07:07 -0500
committerDaniel Rocco <drocco@thebrightlink.com>2013-11-23 23:12:48 -0500
commite2d86a8a0901e30db3697e6a71bc191a8cec4cbd (patch)
treef586c066e7796fca0e82edaf09d3098f73be50dd
parent9c7558aeef8fcafca4b45d2c5bcf88e5a4e36790 (diff)
downloadstevedore-e2d86a8a0901e30db3697e6a71bc191a8cec4cbd.tar.gz
simplify test instance factory contract
After some discussion, the make_test_instance factories expect a final list of extensions, which simplifies creation and keeps responsibilities better focused. Users who wish to test their check functions should do so separately.
-rw-r--r--stevedore/dispatch.py3
-rw-r--r--stevedore/driver.py15
-rw-r--r--stevedore/enabled.py34
-rw-r--r--stevedore/extension.py24
-rw-r--r--stevedore/hook.py30
-rw-r--r--stevedore/named.py68
-rw-r--r--stevedore/tests/test_driver.py42
-rw-r--r--stevedore/tests/test_enabled.py38
-rw-r--r--stevedore/tests/test_test_manager.py219
9 files changed, 248 insertions, 225 deletions
diff --git a/stevedore/dispatch.py b/stevedore/dispatch.py
index 6536ee9..2ff455d 100644
--- a/stevedore/dispatch.py
+++ b/stevedore/dispatch.py
@@ -142,6 +142,9 @@ class NameDispatchExtensionManager(DispatchExtensionManager):
invoke_kwds=invoke_kwds,
propagate_map_exceptions=propagate_map_exceptions,
)
+
+ def _init_plugins(self, extensions):
+ super(NameDispatchExtensionManager, self)._init_plugins(extensions)
self.by_name = dict((e.name, e) for e in self.extensions)
def map(self, names, func, *args, **kwds):
diff --git a/stevedore/driver.py b/stevedore/driver.py
index d8bcd12..56e408d 100644
--- a/stevedore/driver.py
+++ b/stevedore/driver.py
@@ -32,7 +32,7 @@ class DriverManager(NamedExtensionManager):
)
@classmethod
- def make_test_instance(cls, available_extensions, name,
+ def make_test_instance(cls, extension, namespace='TESTING',
propagate_map_exceptions=False):
"""Construct a test DriverManager
@@ -41,12 +41,11 @@ class DriverManager(NamedExtensionManager):
the name argument to determine which of the available_extensions should
be used as the driver.
- :param available_extensions: Pre-configured Extension instances
- available for use
- :type available_extensions: list of
- :class:`~stevedore.extension.Extension`
- :param name: The name of the driver to use.
- :type name: str
+ :param extension: Pre-configured Extension instances
+ :type extension: list of :class:`~stevedore.extension.Extension`
+ :param namespace: The namespace for the manager; used only for
+ identification since the extensions are passed in.
+ :type namespace: str
:param propagate_map_exceptions: Boolean controlling whether exceptions
are propagated up through the map call or whether they are logged
and then ignored
@@ -56,7 +55,7 @@ class DriverManager(NamedExtensionManager):
"""
o = super(DriverManager, cls).make_test_instance(
- available_extensions, [name],
+ [extension], namespace=namespace,
propagate_map_exceptions=propagate_map_exceptions)
return o
diff --git a/stevedore/enabled.py b/stevedore/enabled.py
index f277216..b6260b1 100644
--- a/stevedore/enabled.py
+++ b/stevedore/enabled.py
@@ -47,40 +47,6 @@ class EnabledExtensionManager(ExtensionManager):
propagate_map_exceptions=propagate_map_exceptions,
)
- @classmethod
- def make_test_instance(cls, available_extensions, check_func,
- propagate_map_exceptions=False):
- """Construct a test EnabledExtensionManager
-
- Test instances are passed a list of extensions to work from rather
- than loading them from entry points, filtering the available
- extensions to only those extensions that pass a check function.
-
- :param available_extensions: Pre-configured Extension instances
- available for use
- :type available_extensions: list of
- :class:`~stevedore.extension.Extension`
- :param check_func: Function to determine which extensions to load.
- :type check_func: callable
- :param propagate_map_exceptions: Boolean controlling whether exceptions
- are propagated up through the map call or whether they are logged
- and then ignored
- :type propagate_map_exceptions: bool
- :return: The manager instance, initialized for testing
-
- """
-
- # simulate excluding plugins not passing check_func, which normally
- # happens in _load_one_plugin
- extensions = [extension for extension in available_extensions
- if check_func(extension)]
-
- o = super(EnabledExtensionManager, cls).make_test_instance(
- extensions, propagate_map_exceptions=propagate_map_exceptions)
-
- o.check_func = check_func
- return o
-
def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds):
ext = super(EnabledExtensionManager, self)._load_one_plugin(
ep, invoke_on_load, invoke_args, invoke_kwds,
diff --git a/stevedore/extension.py b/stevedore/extension.py
index ec71279..2e29a95 100644
--- a/stevedore/extension.py
+++ b/stevedore/extension.py
@@ -73,15 +73,16 @@ class ExtensionManager(object):
invoke_args=(),
invoke_kwds={},
propagate_map_exceptions=False):
- self.namespace = namespace
+ self._init_attributes(
+ namespace, propagate_map_exceptions=propagate_map_exceptions)
extensions = self._load_plugins(invoke_on_load,
invoke_args,
invoke_kwds)
- self._init_plugins(extensions,
- propagate_map_exceptions=propagate_map_exceptions)
+ self._init_plugins(extensions)
@classmethod
- def make_test_instance(cls, extensions, propagate_map_exceptions=False):
+ def make_test_instance(cls, extensions, namespace='TESTING',
+ propagate_map_exceptions=False):
"""Construct a test ExtensionManager
Test instances are passed a list of extensions to work from rather
@@ -89,6 +90,9 @@ class ExtensionManager(object):
:param extensions: Pre-configured Extension instances to use
:type extensions: list of :class:`~stevedore.extension.Extension`
+ :param namespace: The namespace for the manager; used only for
+ identification since the extensions are passed in.
+ :type namespace: str
:param propagate_map_exceptions: When calling map, controls whether
exceptions are propagated up through the map call or whether they
are logged and then ignored
@@ -98,13 +102,17 @@ class ExtensionManager(object):
"""
o = cls.__new__(cls)
- o._init_plugins(extensions,
- propagate_map_exceptions=propagate_map_exceptions)
+ o._init_attributes(namespace,
+ propagate_map_exceptions=propagate_map_exceptions)
+ o._init_plugins(extensions)
return o
- def _init_plugins(self, extensions, propagate_map_exceptions=False):
- self.extensions = extensions
+ def _init_attributes(self, namespace, propagate_map_exceptions=False):
+ self.namespace = namespace
self.propagate_map_exceptions = propagate_map_exceptions
+
+ def _init_plugins(self, extensions):
+ self.extensions = extensions
self._extensions_by_name = None
ENTRY_POINT_CACHE = {}
diff --git a/stevedore/hook.py b/stevedore/hook.py
index 29f845a..b7cdef8 100644
--- a/stevedore/hook.py
+++ b/stevedore/hook.py
@@ -23,7 +23,6 @@ class HookManager(NamedExtensionManager):
def __init__(self, namespace, name,
invoke_on_load=False, invoke_args=(), invoke_kwds={}):
- self._name = name
super(HookManager, self).__init__(
namespace,
[name],
@@ -32,29 +31,12 @@ class HookManager(NamedExtensionManager):
invoke_kwds=invoke_kwds,
)
- @classmethod
- def make_test_instance(cls, available_extensions, name):
- """Construct a test HookManager
-
- Test instances are passed a list of extensions to work from rather
- than loading them from entry points, filtering the available
- extensions to only those extensions whose name matches the name
- argument.
-
- :param available_extensions: Pre-configured Extension instances
- available for use
- :type available_extensions: list of
- :class:`~stevedore.extension.Extension`
- :param name: The name of the hooks to use.
- :type name: str
- :return: The manager instance, initialized for testing
-
- """
-
- o = super(HookManager, cls).make_test_instance(available_extensions,
- [name])
- o._name = name
- return o
+ def _init_attributes(self, namespace, names, name_order=False,
+ propagate_map_exceptions=False):
+ super(HookManager, self)._init_attributes(
+ namespace, names,
+ propagate_map_exceptions=propagate_map_exceptions)
+ self._name = names[0]
def __getitem__(self, name):
"""Return the named extensions.
diff --git a/stevedore/named.py b/stevedore/named.py
index 0b1ce97..b4f620c 100644
--- a/stevedore/named.py
+++ b/stevedore/named.py
@@ -35,35 +35,27 @@ class NamedExtensionManager(ExtensionManager):
def __init__(self, namespace, names,
invoke_on_load=False, invoke_args=(), invoke_kwds={},
name_order=False, propagate_map_exceptions=False):
- self._names = names
- self._name_order = name_order
- super(NamedExtensionManager, self).__init__(
- namespace,
- invoke_on_load=invoke_on_load,
- invoke_args=invoke_args,
- invoke_kwds=invoke_kwds,
- propagate_map_exceptions=propagate_map_exceptions,
- )
+ self._init_attributes(
+ namespace, names, name_order=name_order,
+ propagate_map_exceptions=propagate_map_exceptions)
+ extensions = self._load_plugins(invoke_on_load,
+ invoke_args,
+ invoke_kwds)
+ self._init_plugins(extensions)
@classmethod
- def make_test_instance(cls, available_extensions, names=None,
- name_order=False, propagate_map_exceptions=False):
+ def make_test_instance(cls, extensions, namespace='TESTING',
+ propagate_map_exceptions=False):
"""Construct a test NamedExtensionManager
- Test instances are passed a list of extensions to work from rather
- than loading them from entry points, filtering the available
- extensions to only those extensions whose names are listed in the
- names argument.
-
- :param available_extensions: Pre-configured Extension instances
- available for use
- :type available_extensions: list of
- :class:`~stevedore.extension.Extension`
- :param names: The names of the extensions to use.
- :type names: list(str)
- :param name_order: If true, sort the extensions to match the order
- used in ``names``.
- :type name_order: bool
+ Test instances are passed a list of extensions to use rather than
+ loading them from entry points.
+
+ :param extensions: Pre-configured Extension instances
+ :type extensions: list of :class:`~stevedore.extension.Extension`
+ :param namespace: The namespace for the manager; used only for
+ identification since the extensions are passed in.
+ :type namespace: str
:param propagate_map_exceptions: Boolean controlling whether exceptions
are propagated up through the map call or whether they are logged
and then ignored
@@ -73,22 +65,22 @@ class NamedExtensionManager(ExtensionManager):
"""
o = cls.__new__(cls)
- o.namespace = 'TESTING'
- o._names = names
- o._name_order = name_order
+ names = [e.name for e in extensions]
+ o._init_attributes(namespace, names,
+ propagate_map_exceptions=propagate_map_exceptions)
+ o._init_plugins(extensions)
+ return o
- # simulate excluding plugins not listed in names, which normally
- # happens in _load_one_plugin
- extensions = [extension for extension in available_extensions
- if extension.name in names]
+ def _init_attributes(self, namespace, names, name_order=False,
+ propagate_map_exceptions=False):
+ super(NamedExtensionManager, self)._init_attributes(
+ namespace, propagate_map_exceptions=propagate_map_exceptions)
- o._init_plugins(extensions,
- propagate_map_exceptions=propagate_map_exceptions)
- return o
+ self._names = names
+ self._name_order = name_order
- def _init_plugins(self, extensions, propagate_map_exceptions=False):
- super(NamedExtensionManager, self)._init_plugins(
- extensions, propagate_map_exceptions=propagate_map_exceptions)
+ def _init_plugins(self, extensions):
+ super(NamedExtensionManager, self)._init_plugins(extensions)
if self._name_order:
self.extensions = [self[n] for n in self._names]
diff --git a/stevedore/tests/test_driver.py b/stevedore/tests/test_driver.py
index df43110..70ec54c 100644
--- a/stevedore/tests/test_driver.py
+++ b/stevedore/tests/test_driver.py
@@ -1,21 +1,13 @@
"""Tests for stevedore.extension
"""
-from mock import Mock, sentinel
-from nose.tools import raises
import mock
import pkg_resources
from stevedore import driver
-from stevedore.extension import Extension
from stevedore.tests import test_extension
-mock_entry_point = Mock(module_name='test.extension', attrs=['obj'])
-a_driver = Extension('test_driver', mock_entry_point, sentinel.driver_plugin,
- sentinel.driver_obj)
-
-
def test_detect_plugins():
em = driver.DriverManager('stevedore.test.extension', 't1')
names = sorted(em.names())
@@ -67,37 +59,3 @@ def test_multiple_drivers():
except RuntimeError as err:
assert "Multiple" in str(err), str(err)
fep.assert_called_with('stevedore.test.multiple_drivers')
-
-
-@raises(RuntimeError)
-def test_instance_driver_not_found_should_raise():
- extension = Extension('a_driver', None, None, None)
- driver.DriverManager.make_test_instance(
- [extension], 'this_is_not_the_driver_you_are_looking_for')
-
-
-@raises(RuntimeError)
-def test_instance_multiple_drivers_found_should_raise():
- extension = Extension('test_driver', mock_entry_point, None, None)
- copycat = Extension('test_driver', mock_entry_point, None, None)
-
- driver.DriverManager.make_test_instance([extension, copycat],
- 'test_driver')
-
-
-def test_instance_should_find_named_driver():
- em = driver.DriverManager.make_test_instance([a_driver], 'test_driver')
- assert ['test_driver'] == em.names()
-
-
-def test_instance_call():
- def invoke(ext, *args, **kwds):
- return (ext.name, args, kwds)
- em = driver.DriverManager.make_test_instance([a_driver], 'test_driver')
- result = em(invoke, 'a', b='C')
- assert result == ('test_driver', ('a',), {'b': 'C'})
-
-
-def test_instance_driver():
- em = driver.DriverManager.make_test_instance([a_driver], 'test_driver')
- assert sentinel.driver_obj == em.driver
diff --git a/stevedore/tests/test_enabled.py b/stevedore/tests/test_enabled.py
index 92cb7ce..659e710 100644
--- a/stevedore/tests/test_enabled.py
+++ b/stevedore/tests/test_enabled.py
@@ -1,8 +1,4 @@
-from mock import Mock
-
from stevedore import enabled
-from stevedore.tests.test_test_manager import (test_extension, test_extension2,
- excluded, unwanted)
def test_enabled():
@@ -31,37 +27,3 @@ def test_enabled_after_load():
)
assert len(em.extensions) == 1
assert em.names() == ['t2']
-
-
-def test_enabled_manager_should_check_all_extensions():
- check_func = Mock()
-
- enabled.EnabledExtensionManager.make_test_instance([excluded,
- test_extension,
- unwanted,
- test_extension2],
- check_func)
-
- check_func.assert_any_call(test_extension)
- check_func.assert_any_call(test_extension2)
- check_func.assert_any_call(excluded)
- check_func.assert_any_call(unwanted)
-
-
-def test_enabled_manager_should_include_only_passing_extensions():
- def underscore_checker(extension):
- return '_' in extension.name
-
- em = enabled.EnabledExtensionManager.make_test_instance([excluded,
- test_extension,
- unwanted,
- test_extension2],
- underscore_checker)
-
- extensions = list(em)
-
- assert test_extension in extensions
- assert test_extension2 in extensions
-
- assert excluded not in extensions
- assert unwanted not in extensions
diff --git a/stevedore/tests/test_test_manager.py b/stevedore/tests/test_test_manager.py
index 2dd42c0..aa995b2 100644
--- a/stevedore/tests/test_test_manager.py
+++ b/stevedore/tests/test_test_manager.py
@@ -1,13 +1,40 @@
-from mock import Mock
+from mock import Mock, sentinel
from nose.tools import raises
-from stevedore import (ExtensionManager, NamedExtensionManager, HookManager)
+from stevedore import (ExtensionManager, NamedExtensionManager, HookManager,
+ DriverManager, EnabledExtensionManager)
+from stevedore.dispatch import (DispatchExtensionManager,
+ NameDispatchExtensionManager)
from stevedore.extension import Extension
test_extension = Extension('test_extension', None, None, None)
test_extension2 = Extension('another_one', None, None, None)
-excluded = Extension('excluded', None, None, None)
-unwanted = Extension('unwanted', None, None, None)
+
+mock_entry_point = Mock(module_name='test.extension', attrs=['obj'])
+a_driver = Extension('test_driver', mock_entry_point, sentinel.driver_plugin,
+ sentinel.driver_obj)
+
+
+# base ExtensionManager
+
+def test_instance_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = ExtensionManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+def test_instance_should_have_default_namespace():
+ em = ExtensionManager.make_test_instance([])
+
+ assert em.namespace
+
+
+def test_instance_should_use_supplied_namespace():
+ namespace = 'testing.1.2.3'
+ em = ExtensionManager.make_test_instance([], namespace=namespace)
+
+ assert namespace == em.namespace
def test_extension_name_should_be_listed():
@@ -76,50 +103,176 @@ def test_manager_should_propagate_exceptions():
em.map(func, 1, 2, a='A', b='B')
-def test_named_manager_should_include_named_extensions():
- em = NamedExtensionManager.make_test_instance([excluded,
- test_extension,
- unwanted,
- test_extension2],
- ['test_extension',
- 'another_one'])
+# NamedExtensionManager
+
+def test_named_manager_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = NamedExtensionManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+def test_named_manager_should_have_default_namespace():
+ em = NamedExtensionManager.make_test_instance([])
+
+ assert em.namespace
+
+
+def test_named_manager_should_use_supplied_namespace():
+ namespace = 'testing.1.2.3'
+ em = NamedExtensionManager.make_test_instance([], namespace=namespace)
+
+ assert namespace == em.namespace
- extensions = list(em)
- assert test_extension in extensions
- assert test_extension2 in extensions
+def test_named_manager_should_populate_names():
+ extensions = [test_extension, test_extension2]
+ em = NamedExtensionManager.make_test_instance(extensions)
+ assert ['test_extension', 'another_one'] == em.names()
-def test_named_manager_should_not_include_unnamed_extensions():
- em = NamedExtensionManager.make_test_instance([excluded,
- test_extension,
- unwanted,
- test_extension2],
- ['test_extension',
- 'another_one'])
- extensions = list(em)
+# HookManager
- assert excluded not in extensions
- assert unwanted not in extensions
+def test_hook_manager_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = HookManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+def test_hook_manager_should_be_first_extension_name():
+ extensions = [test_extension, test_extension2]
+ em = HookManager.make_test_instance(extensions)
+
+ # This will raise KeyError if the names don't match
+ assert em[test_extension.name]
+
+
+def test_hook_manager_should_have_default_namespace():
+ em = HookManager.make_test_instance([test_extension])
+
+ assert em.namespace
+
+
+def test_hook_manager_should_use_supplied_namespace():
+ namespace = 'testing.1.2.3'
+ em = HookManager.make_test_instance([test_extension], namespace=namespace)
+
+ assert namespace == em.namespace
def test_hook_manager_should_return_named_extensions():
hook1 = Extension('captain', None, None, None)
hook2 = Extension('captain', None, None, None)
- em = HookManager.make_test_instance([hook1, hook2], 'captain')
+ em = HookManager.make_test_instance([hook1, hook2])
assert [hook1, hook2] == em['captain']
-def test_hook_manager_should_not_include_unnamed_extensions():
- em = HookManager.make_test_instance([excluded, test_extension,
- unwanted, test_extension2],
- 'test_extension')
+# DriverManager
+
+def test_driver_manager_should_use_supplied_extension():
+ em = DriverManager.make_test_instance(a_driver)
+
+ assert [a_driver] == em.extensions
+
+
+def test_driver_manager_should_have_default_namespace():
+ em = DriverManager.make_test_instance(a_driver)
+
+ assert em.namespace
+
+
+def test_driver_manager_should_use_supplied_namespace():
+ namespace = 'testing.1.2.3'
+ em = DriverManager.make_test_instance(a_driver, namespace=namespace)
+
+ assert namespace == em.namespace
+
+
+def test_instance_should_use_driver_name():
+ em = DriverManager.make_test_instance(a_driver)
+
+ assert ['test_driver'] == em.names()
+
+
+def test_instance_call():
+ def invoke(ext, *args, **kwds):
+ return ext.name, args, kwds
+
+ em = DriverManager.make_test_instance(a_driver)
+ result = em(invoke, 'a', b='C')
+
+ assert result == ('test_driver', ('a',), {'b': 'C'})
+
+
+def test_instance_driver_property():
+ em = DriverManager.make_test_instance(a_driver)
+
+ assert sentinel.driver_obj == em.driver
+
+
+# EnabledExtensionManager
+
+def test_enabled_instance_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = EnabledExtensionManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+# DispatchExtensionManager
+
+def test_dispatch_instance_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = DispatchExtensionManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+def test_dispatch_map_should_invoke_filter_for_extensions():
+ em = DispatchExtensionManager.make_test_instance([test_extension,
+ test_extension2])
+
+ filter_func = Mock(return_value=False)
+
+ args = ('A',)
+ kw = {'big': 'Cheese'}
+
+ em.map(filter_func, None, *args, **kw)
+
+ filter_func.assert_any_call(test_extension, *args, **kw)
+ filter_func.assert_any_call(test_extension2, *args, **kw)
+
+
+# NameDispatchExtensionManager
+
+def test_name_dispatch_instance_should_use_supplied_extensions():
+ extensions = [test_extension, test_extension2]
+ em = NameDispatchExtensionManager.make_test_instance(extensions)
+
+ assert extensions == em.extensions
+
+
+def test_name_dispatch_instance_should_build_extension_name_map():
+ extensions = [test_extension, test_extension2]
+ em = NameDispatchExtensionManager.make_test_instance(extensions)
+
+ assert test_extension == em.by_name[test_extension.name]
+ assert test_extension2 == em.by_name[test_extension2.name]
+
+
+def test_named_dispatch_map_should_invoke_filter_for_extensions():
+ em = NameDispatchExtensionManager.make_test_instance([test_extension,
+ test_extension2])
+
+ func = Mock()
+
+ args = ('A',)
+ kw = {'BIGGER': 'Cheese'}
- extensions = em['test_extension']
+ em.map(['test_extension'], func, *args, **kw)
- assert test_extension2 not in extensions
- assert excluded not in extensions
- assert unwanted not in extensions
+ func.assert_called_once_with(test_extension, *args, **kw)