diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index a6fd2a848..d248469c1 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -32,6 +32,7 @@ Provide plugin management import os import sys import logging +import imp from openlp.core.lib import Plugin, PluginStatus, Registry @@ -55,11 +56,8 @@ 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') @@ -73,37 +71,34 @@ class PluginManager(object): """ 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) + start_depth = len(os.path.abspath(plugin_dir).split(os.sep)) + present_plugin_dir = os.path.join(plugin_dir, 'presentations') + log.debug(u'finding plugins in %s at depth %d', unicode(plugin_dir), start_depth) 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 + 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 +137,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): """ diff --git a/openlp/plugins/bibles/lib/csvbible.py b/openlp/plugins/bibles/lib/csvbible.py index 4f491c5cc..7a91b4fca 100644 --- a/openlp/plugins/bibles/lib/csvbible.py +++ b/openlp/plugins/bibles/lib/csvbible.py @@ -55,7 +55,7 @@ There are two acceptable formats of the verses file. They are: or "Genesis",1,2,"And the earth was without form, and void; and...." -All CSV files are expected to use a comma (',') as the delimeter and double +All CSV files are expected to use a comma (',') as the delimiter and double quotes ('"') as the quote symbol. """ import logging diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core_lib/test_pluginmanager.py new file mode 100644 index 000000000..bad38d721 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_pluginmanager.py @@ -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() diff --git a/tests/functional/openlp_core_lib/test_registry.py b/tests/functional/openlp_core_lib/test_registry.py index f27eef3d8..17fe561d7 100644 --- a/tests/functional/openlp_core_lib/test_registry.py +++ b/tests/functional/openlp_core_lib/test_registry.py @@ -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): diff --git a/tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py b/tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py index 0a8775a7b..d5b45ec37 100644 --- a/tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py +++ b/tests/interfaces/openlp_plugins_songs_forms/test_authorsform.py @@ -1,12 +1,13 @@ """ Package to test the openlp.core.ui package. """ +import nose from unittest import TestCase from PyQt4 import QtGui -from openlp.plugins.songs.forms.authorsform import AuthorsForm from openlp.core.lib import Registry +from openlp.plugins.songs.forms.authorsform import AuthorsForm class TestAuthorsForm(TestCase):