This commit is contained in:
Andreas Preikschat 2013-02-16 19:50:55 +01:00
commit 73cce9ad13
6 changed files with 466 additions and 41 deletions

View File

@ -32,6 +32,7 @@ Provide plugin management
import os
import sys
import logging
import imp
from openlp.core.lib import Plugin, PluginStatus, Registry
@ -55,55 +56,44 @@ class PluginManager(object):
"""
log.info(u'Plugin manager Initialising')
Registry().register(u'plugin_manager', self)
if not plugin_dir in sys.path:
log.debug(u'Inserting %s into sys.path', plugin_dir)
sys.path.insert(0, plugin_dir)
self.basepath = os.path.abspath(plugin_dir)
log.debug(u'Base path %s ', self.basepath)
self.base_path = os.path.abspath(plugin_dir)
log.debug(u'Base path %s ', self.base_path)
self.plugins = []
log.info(u'Plugin manager Initialised')
def find_plugins(self, plugin_dir):
def find_plugins(self):
"""
Scan the directory ``plugin_dir`` for objects inheriting from the
``Plugin`` class.
``plugin_dir``
The directory to scan.
Scan a directory for objects inheriting from the ``Plugin`` class.
"""
log.info(u'Finding plugins')
startdepth = len(os.path.abspath(plugin_dir).split(os.sep))
log.debug(u'finding plugins in %s at depth %d',
unicode(plugin_dir), startdepth)
for root, dirs, files in os.walk(plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will hide it
# in settings dialog.
if sys.platform == 'darwin':
present_plugin_dir = os.path.join(plugin_dir, 'presentations')
# Ignore files from the presentation plugin directory.
if root.startswith(present_plugin_dir):
continue
start_depth = len(os.path.abspath(self.base_path).split(os.sep))
present_plugin_dir = os.path.join(self.base_path, 'presentations')
log.debug(u'finding plugins in %s at depth %d', unicode(self.base_path), start_depth)
for root, dirs, files in os.walk(self.base_path):
if sys.platform == 'darwin' and root.startswith(present_plugin_dir):
# TODO Presentation plugin is not yet working on Mac OS X.
# For now just ignore it. The following code will ignore files from the presentation plugin directory
# and thereby never import the plugin.
continue
for name in files:
if name.endswith(u'.py') and not name.startswith(u'__'):
path = os.path.abspath(os.path.join(root, name))
thisdepth = len(path.split(os.sep))
if thisdepth - startdepth > 2:
this_depth = len(path.split(os.sep))
if this_depth - start_depth > 2:
# skip anything lower down
break
modulename = os.path.splitext(path)[0]
prefix = os.path.commonprefix([self.basepath, path])
# hack off the plugin base path
modulename = modulename[len(prefix) + 1:]
modulename = modulename.replace(os.path.sep, '.')
module_name = name[:-3]
# import the modules
log.debug(u'Importing %s from %s. Depth %d', modulename, path, thisdepth)
log.debug(u'Importing %s from %s. Depth %d', module_name, root, this_depth)
try:
__import__(modulename, globals(), locals(), [])
# Use the "imp" library to try to get around a problem with the PyUNO library which
# monkey-patches the __import__ function to do some magic. This causes issues with our tests.
# First, try to find the module we want to import, searching the directory in root
fp, path_name, description = imp.find_module(module_name, [root])
# Then load the module (do the actual import) using the details from find_module()
imp.load_module(module_name, fp, path_name, description)
except ImportError, e:
log.exception(u'Failed to import module %s on path %s for reason %s',
modulename, path, e.args[0])
log.exception(u'Failed to import module %s on path %s: %s', module_name, path, e.args[0])
plugin_classes = Plugin.__subclasses__()
plugin_objects = []
for p in plugin_classes:
@ -142,7 +132,8 @@ class PluginManager(object):
for plugin in self.plugins:
if plugin.status is not PluginStatus.Disabled:
plugin.createSettingsTab(settings_form)
settings_form.plugins = self.plugins
if settings_form:
settings_form.plugins = self.plugins
def hook_import_menu(self, import_menu):
"""

View File

@ -487,8 +487,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.timer_id = 0
self.timer_version_id = 0
# Set up the path with plugins
plugin_path = AppLocation.get_directory(AppLocation.PluginsDir)
self.plugin_manager = PluginManager(plugin_path)
self.plugin_manager = PluginManager(AppLocation.get_directory(AppLocation.PluginsDir))
self.imageManager = ImageManager()
# Set up the interface
self.setupUi(self)
@ -541,7 +540,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
# Define the media Dock Manager
self.mediaDockManager = MediaDockManager(self.mediaToolBox)
log.info(u'Load Plugins')
self.plugin_manager.find_plugins(plugin_path)
self.plugin_manager.find_plugins()
# hook methods have to happen after find_plugins. Find plugins needs
# the controllers hence the hooks have moved from setupUI() to here
# Find and insert settings tabs

View File

@ -0,0 +1,373 @@
"""
Package to test the openlp.core.lib.pluginmanager package.
"""
from unittest import TestCase
from mock import MagicMock
from openlp.core.lib.pluginmanager import PluginManager
from openlp.core.lib import Registry, PluginStatus
class TestPluginManager(TestCase):
"""
Test the PluginManager class
"""
def setUp(self):
"""
Some pre-test setup required.
"""
Registry.create()
Registry().register(u'service_list', MagicMock())
def hook_media_manager_with_disabled_plugin_test(self):
"""
Test running the hook_media_manager() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_media_manager()
plugin_manager.hook_media_manager()
# THEN: The createMediaManagerItem() method should have been called
assert mocked_plugin.createMediaManagerItem.call_count == 0, \
u'The createMediaManagerItem() method should not have been called.'
def hook_media_manager_with_active_plugin_test(self):
"""
Test running the hook_media_manager() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_media_manager()
plugin_manager.hook_media_manager()
# THEN: The createMediaManagerItem() method should have been called
mocked_plugin.createMediaManagerItem.assert_called_with()
def hook_settings_tabs_with_disabled_plugin_and_no_form_test(self):
"""
Test running the hook_settings_tabs() method with a disabled plugin and no form
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_settings_tabs()
plugin_manager.hook_settings_tabs()
# THEN: The createSettingsTab() method should have been called
assert mocked_plugin.createMediaManagerItem.call_count == 0, \
u'The createMediaManagerItem() method should not have been called.'
def hook_settings_tabs_with_disabled_plugin_and_mocked_form_test(self):
"""
Test running the hook_settings_tabs() method with a disabled plugin and a mocked form
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_settings_form = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_settings_tabs()
plugin_manager.hook_settings_tabs(mocked_settings_form)
# THEN: The createSettingsTab() method should not have been called, but the plugins lists should be the same
assert mocked_plugin.createSettingsTab.call_count == 0, \
u'The createMediaManagerItem() method should not have been called.'
self.assertEqual(mocked_settings_form.plugins, plugin_manager.plugins,
u'The plugins on the settings form should be the same as the plugins in the plugin manager')
def hook_settings_tabs_with_active_plugin_and_no_form_test(self):
"""
Test running the hook_settings_tabs() method with an active plugin and no settings form
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_settings_tabs()
plugin_manager.hook_settings_tabs()
# THEN: The createSettingsTab() method should have been called
mocked_plugin.createSettingsTab.assert_called_with(None)
def hook_settings_tabs_with_active_plugin_and_mocked_form_test(self):
"""
Test running the hook_settings_tabs() method with an active plugin and a mocked settings form
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_settings_form = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_settings_tabs()
plugin_manager.hook_settings_tabs(mocked_settings_form)
# THEN: The createMediaManagerItem() method should have been called with the mocked settings form
mocked_plugin.createSettingsTab.assert_called_with(mocked_settings_form)
self.assertEqual(mocked_settings_form.plugins, plugin_manager.plugins,
u'The plugins on the settings form should be the same as the plugins in the plugin manager')
def hook_import_menu_with_disabled_plugin_test(self):
"""
Test running the hook_import_menu() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_import_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_import_menu()
plugin_manager.hook_import_menu(mocked_import_menu)
# THEN: The createMediaManagerItem() method should have been called
assert mocked_plugin.addImportMenuItem.call_count == 0, \
u'The addImportMenuItem() method should not have been called.'
def hook_import_menu_with_active_plugin_test(self):
"""
Test running the hook_import_menu() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_import_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_import_menu()
plugin_manager.hook_import_menu(mocked_import_menu)
# THEN: The addImportMenuItem() method should have been called
mocked_plugin.addImportMenuItem.assert_called_with(mocked_import_menu)
def hook_export_menu_with_disabled_plugin_test(self):
"""
Test running the hook_export_menu() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_export_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_export_menu()
plugin_manager.hook_export_menu(mocked_export_menu)
# THEN: The addExportMenuItem() method should have been called
assert mocked_plugin.addExportMenuItem.call_count == 0, \
u'The addExportMenuItem() method should not have been called.'
def hook_export_menu_with_active_plugin_test(self):
"""
Test running the hook_export_menu() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_export_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_export_menu()
plugin_manager.hook_export_menu(mocked_export_menu)
# THEN: The addExportMenuItem() method should have been called
mocked_plugin.addExportMenuItem.assert_called_with(mocked_export_menu)
def hook_tools_menu_with_disabled_plugin_test(self):
"""
Test running the hook_tools_menu() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_tools_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_tools_menu()
plugin_manager.hook_tools_menu(mocked_tools_menu)
# THEN: The addToolsMenuItem() method should have been called
assert mocked_plugin.addToolsMenuItem.call_count == 0, \
u'The addToolsMenuItem() method should not have been called.'
def hook_tools_menu_with_active_plugin_test(self):
"""
Test running the hook_tools_menu() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_tools_menu = MagicMock()
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run hook_tools_menu()
plugin_manager.hook_tools_menu(mocked_tools_menu)
# THEN: The addToolsMenuItem() method should have been called
mocked_plugin.addToolsMenuItem.assert_called_with(mocked_tools_menu)
def initialise_plugins_with_disabled_plugin_test(self):
"""
Test running the initialise_plugins() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_plugin.isActive.return_value = False
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run initialise_plugins()
plugin_manager.initialise_plugins()
# THEN: The isActive() method should have been called, and initialise() method should NOT have been called
mocked_plugin.isActive.assert_called_with()
assert mocked_plugin.initialise.call_count == 0, u'The initialise() method should not have been called.'
def initialise_plugins_with_active_plugin_test(self):
"""
Test running the initialise_plugins() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_plugin.isActive.return_value = True
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run initialise_plugins()
plugin_manager.initialise_plugins()
# THEN: The isActive() and initialise() methods should have been called
mocked_plugin.isActive.assert_called_with()
mocked_plugin.initialise.assert_called_with()
def finalise_plugins_with_disabled_plugin_test(self):
"""
Test running the finalise_plugins() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_plugin.isActive.return_value = False
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run finalise_plugins()
plugin_manager.finalise_plugins()
# THEN: The isActive() method should have been called, and initialise() method should NOT have been called
mocked_plugin.isActive.assert_called_with()
assert mocked_plugin.finalise.call_count == 0, u'The finalise() method should not have been called.'
def finalise_plugins_with_active_plugin_test(self):
"""
Test running the finalise_plugins() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_plugin.isActive.return_value = True
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run finalise_plugins()
plugin_manager.finalise_plugins()
# THEN: The isActive() and finalise() methods should have been called
mocked_plugin.isActive.assert_called_with()
mocked_plugin.finalise.assert_called_with()
def get_plugin_by_name_does_not_exist_test(self):
"""
Test running the get_plugin_by_name() method to find a plugin that does not exist
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.name = 'Mocked Plugin'
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run finalise_plugins()
result = plugin_manager.get_plugin_by_name('Missing Plugin')
# THEN: The isActive() and finalise() methods should have been called
self.assertIsNone(result, u'The result for get_plugin_by_name should be None')
def get_plugin_by_name_exists_test(self):
"""
Test running the get_plugin_by_name() method to find a plugin that exists
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.name = 'Mocked Plugin'
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run finalise_plugins()
result = plugin_manager.get_plugin_by_name('Mocked Plugin')
# THEN: The isActive() and finalise() methods should have been called
self.assertEqual(result, mocked_plugin, u'The result for get_plugin_by_name should be the mocked plugin')
def new_service_created_with_disabled_plugin_test(self):
"""
Test running the new_service_created() method with a disabled plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Disabled
mocked_plugin.isActive.return_value = False
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run finalise_plugins()
plugin_manager.new_service_created()
# THEN: The isActive() method should have been called, and initialise() method should NOT have been called
mocked_plugin.isActive.assert_called_with()
assert mocked_plugin.new_service_created.call_count == 0,\
u'The new_service_created() method should not have been called.'
def new_service_created_with_active_plugin_test(self):
"""
Test running the new_service_created() method with an active plugin
"""
# GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active
mocked_plugin = MagicMock()
mocked_plugin.status = PluginStatus.Active
mocked_plugin.isActive.return_value = True
plugin_manager = PluginManager('')
plugin_manager.plugins = [mocked_plugin]
# WHEN: We run new_service_created()
plugin_manager.new_service_created()
# THEN: The isActive() and finalise() methods should have been called
mocked_plugin.isActive.assert_called_with()
mocked_plugin.new_service_created.assert_called_with()

View File

@ -1,13 +1,15 @@
"""
Package to test the openlp.core.lib package.
Package to test the openlp.core.lib package.
"""
import os
from unittest import TestCase
from mock import MagicMock
from openlp.core.lib import Registry
TESTPATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
class TestRegistry(TestCase):

View File

@ -0,0 +1,60 @@
"""
Package to test the openlp.core.lib.pluginmanager package.
"""
import os
import sys
from tempfile import mkstemp
from unittest import TestCase
from mock import MagicMock, patch
from PyQt4 import QtGui
from openlp.core.lib.pluginmanager import PluginManager
from openlp.core.lib import Registry, Settings
class TestPluginManager(TestCase):
"""
Test the PluginManager class
"""
def setUp(self):
"""
Some pre-test setup required.
"""
fd, self.ini_file = mkstemp(u'.ini')
Settings().set_filename(self.ini_file)
Registry.create()
Registry().register(u'service_list', MagicMock())
self.app = QtGui.QApplication([])
self.main_window = QtGui.QMainWindow()
Registry().register(u'main_window', self.main_window)
self.plugins_dir = os.path.abspath(os.path.join(os.path.basename(__file__), u'..', u'openlp', u'plugins'))
def tearDown(self):
os.unlink(self.ini_file)
def find_plugins_test(self):
"""
Test the find_plugins() method to ensure it imports the correct plugins.
"""
# GIVEN: A plugin manager
plugin_manager = PluginManager(self.plugins_dir)
# WHEN: We mock out sys.platform to make it return "darwin" and then find the plugins
old_platform = sys.platform
sys.platform = u'darwin'
plugin_manager.find_plugins()
sys.platform = old_platform
# THEN: We should find the "Songs", "Bibles", etc in the plugins list
plugin_names = [plugin.name for plugin in plugin_manager.plugins]
assert u'songs' in plugin_names, u'There should be a "songs" plugin.'
assert u'bibles' in plugin_names, u'There should be a "bibles" plugin.'
assert u'presentations' not in plugin_names, u'There should NOT be a "presentations" plugin.'
assert u'images' in plugin_names, u'There should be a "images" plugin.'
assert u'media' in plugin_names, u'There should be a "media" plugin.'
assert u'custom' in plugin_names, u'There should be a "custom" plugin.'
assert u'songusage' in plugin_names, u'There should be a "songusage" plugin.'
assert u'alerts' in plugin_names, u'There should be a "alerts" plugin.'
assert u'remotes' in plugin_names, u'There should be a "remotes" plugin.'