summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Rocco <drocco@thebrightlink.com>2013-06-30 08:52:14 -0400
committerDaniel Rocco <drocco@thebrightlink.com>2013-11-23 23:12:46 -0500
commitacaf47782e25b1a4eb6056e72034335d03e00e3b (patch)
tree9953b7d95fe6302ff502ee6294d94e3ee2792b71
parentfdde242a8ebf655e3bc81d753dc9d8b9869ed29e (diff)
downloadstevedore-acaf47782e25b1a4eb6056e72034335d03e00e3b.tar.gz
add test instance factory for the base, named, and hook managers
The class method make_test_instance constructs manager instances using a predefined list of extensions, rather than loading from entry points.
-rw-r--r--stevedore/extension.py35
-rw-r--r--stevedore/hook.py24
-rw-r--r--stevedore/named.py49
-rw-r--r--stevedore/tests/test_test_manager.py117
4 files changed, 219 insertions, 6 deletions
diff --git a/stevedore/extension.py b/stevedore/extension.py
index 1ac87e8..a29af8e 100644
--- a/stevedore/extension.py
+++ b/stevedore/extension.py
@@ -54,7 +54,7 @@ class ExtensionManager(object):
:param propagate_map_exceptions: Boolean controlling whether exceptions
are propagated up through the map call or whether they are logged and
then ignored
- :type invoke_on_load: bool
+ :type propagate_map_exceptions: bool
"""
@@ -64,10 +64,37 @@ class ExtensionManager(object):
invoke_kwds={},
propagate_map_exceptions=False):
self.namespace = namespace
+ extensions = self._load_plugins(invoke_on_load,
+ invoke_args,
+ invoke_kwds)
+ self._init_plugins(extensions,
+ propagate_map_exceptions=propagate_map_exceptions)
+
+ @classmethod
+ def make_test_instance(cls, extensions, propagate_map_exceptions=False):
+ """Construct a test ExtensionManager
+
+ Test instances are passed a list of extensions to work from rather
+ than loading them from entry points.
+
+ :param extensions: Pre-configured Extension instances to use
+ :type extensions: list of :class:`~stevedore.extension.Extension`
+ :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
+ :type propagate_map_exceptions: bool
+ :return: The manager instance, initialized for testing
+
+ """
+
+ o = cls.__new__(cls)
+ o._init_plugins(extensions,
+ propagate_map_exceptions=propagate_map_exceptions)
+ return o
+
+ def _init_plugins(self, extensions, propagate_map_exceptions=False):
+ self.extensions = extensions
self.propagate_map_exceptions = propagate_map_exceptions
- self.extensions = self._load_plugins(invoke_on_load,
- invoke_args,
- invoke_kwds)
self._extensions_by_name = None
ENTRY_POINT_CACHE = {}
diff --git a/stevedore/hook.py b/stevedore/hook.py
index c1cec7f..29f845a 100644
--- a/stevedore/hook.py
+++ b/stevedore/hook.py
@@ -32,6 +32,30 @@ 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 __getitem__(self, name):
"""Return the named extensions.
diff --git a/stevedore/named.py b/stevedore/named.py
index e26667f..9708f99 100644
--- a/stevedore/named.py
+++ b/stevedore/named.py
@@ -28,13 +28,15 @@ class NamedExtensionManager(ExtensionManager):
:param propagate_map_exceptions: Boolean controlling whether exceptions
are propagated up through the map call or whether they are logged and
then ignored
- :type invoke_on_load: bool
+ :type propagate_map_exceptions: bool
+
"""
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,
@@ -43,7 +45,50 @@ class NamedExtensionManager(ExtensionManager):
propagate_map_exceptions=propagate_map_exceptions,
)
- if name_order:
+ @classmethod
+ def make_test_instance(cls, available_extensions, names,
+ name_order=False, 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
+ :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
+
+ """
+
+ o = cls.__new__(cls)
+ o._names = names
+ o._name_order = name_order
+
+ # 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]
+ o._init_plugins(extensions,
+ propagate_map_exceptions=propagate_map_exceptions)
+ return o
+
+ def _init_plugins(self, extensions, propagate_map_exceptions=False):
+ super(NamedExtensionManager, self)._init_plugins(
+ extensions, propagate_map_exceptions=propagate_map_exceptions)
+
+ if self._name_order:
self.extensions = [self[n] for n in self._names]
def _load_one_plugin(self, ep, invoke_on_load, invoke_args, invoke_kwds):
diff --git a/stevedore/tests/test_test_manager.py b/stevedore/tests/test_test_manager.py
new file mode 100644
index 0000000..9b7830b
--- /dev/null
+++ b/stevedore/tests/test_test_manager.py
@@ -0,0 +1,117 @@
+from mock import Mock
+from stevedore import NamedExtensionManager, HookManager
+from stevedore.extension import Extension, ExtensionManager
+
+
+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)
+
+
+def test_extension_name_should_be_listed():
+ em = ExtensionManager.make_test_instance([test_extension])
+
+ assert test_extension.name in em.names()
+
+
+def test_iterator_should_yield_extension():
+ em = ExtensionManager.make_test_instance([test_extension])
+
+ assert test_extension == next(iter(em))
+
+
+def test_manager_should_allow_name_access():
+ em = ExtensionManager.make_test_instance([test_extension])
+
+ assert test_extension == em[test_extension.name]
+
+
+def test_manager_should_call():
+ em = ExtensionManager.make_test_instance([test_extension])
+ func = Mock()
+
+ em.map(func)
+
+ func.assert_called_once_with(test_extension)
+
+
+def test_manager_should_call_all():
+ em = ExtensionManager.make_test_instance([test_extension2,
+ test_extension])
+ func = Mock()
+
+ em.map(func)
+
+ func.assert_any_call(test_extension2)
+ func.assert_any_call(test_extension)
+
+
+def test_manager_should_eat_exceptions():
+ em = ExtensionManager.make_test_instance([test_extension])
+
+ func = Mock(side_effect=RuntimeError('hard coded error'))
+
+ results = em.map(func, 1, 2, a='A', b='B')
+ assert results == []
+
+
+def test_manager_should_propagate_exceptions():
+ em = ExtensionManager.make_test_instance([test_extension],
+ propagate_map_exceptions=True)
+ func = Mock(side_effect=RuntimeError('hard coded error'))
+
+ try:
+ em.map(func, 1, 2, a='A', b='B')
+ assert False, 'DID NOT RAISE!'
+ except RuntimeError:
+ pass
+
+
+def test_named_manager_should_include_named_extensions():
+ em = NamedExtensionManager.make_test_instance([excluded,
+ test_extension,
+ unwanted,
+ test_extension2],
+ ['test_extension',
+ 'another_one'])
+
+ extensions = list(em)
+
+ assert test_extension in extensions
+ assert test_extension2 in extensions
+
+
+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)
+
+ assert excluded not in extensions
+ assert unwanted not in extensions
+
+
+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')
+
+ 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')
+
+ extensions = em['test_extension']
+
+ assert test_extension2 not in extensions
+ assert excluded not in extensions
+ assert unwanted not in extensions