← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~raoul-snyman/openlp/imp into lp:openlp

 

Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/imp into lp:openlp.

Requested reviews:
  Jonathan Corwin (j-corwin)
  Tim Bentley (trb143)
  Andreas Preikschat (googol)

For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/imp/+merge/148870

Modify the way plugins are imported to use the "imp" module

PyUNO, the LibreOffice bridge module, monkey-patches the __import__ magic function which causes problems with some interface tests and possibly also the check_dependencies script. This changes the PluginManager to use the "imp" module instead, in the hopes that this is not affected by the __import__ issue.

Some functional tests and an interface test around the PluginManager have also been added.

Remove the superfluous argument to the find_plugins method.

http://ci.openlp.org/view/Specific%20Branch/job/OpenLP-Pull_and_Run_Functional_Tests/43/console
http://ci.openlp.org/view/Specific%20Branch/job/OpenLP-Pull_and_Run_Interface_Tests/4/console
-- 
https://code.launchpad.net/~raoul-snyman/openlp/imp/+merge/148870
Your team OpenLP Core is subscribed to branch lp:openlp.
=== modified file 'openlp/core/lib/pluginmanager.py'
--- openlp/core/lib/pluginmanager.py	2013-02-03 16:40:39 +0000
+++ openlp/core/lib/pluginmanager.py	2013-02-16 12:14:21 +0000
@@ -32,6 +32,7 @@
 import os
 import sys
 import logging
+import imp
 
 from openlp.core.lib import Plugin, PluginStatus, Registry
 
@@ -55,55 +56,44 @@
         """
         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 @@
         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):
         """

=== modified file 'openlp/core/ui/mainwindow.py'
--- openlp/core/ui/mainwindow.py	2013-02-10 16:10:32 +0000
+++ openlp/core/ui/mainwindow.py	2013-02-16 12:14:21 +0000
@@ -487,8 +487,7 @@
         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 @@
         # 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

=== added file 'tests/functional/openlp_core_lib/test_pluginmanager.py'
--- tests/functional/openlp_core_lib/test_pluginmanager.py	1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_core_lib/test_pluginmanager.py	2013-02-16 12:14:21 +0000
@@ -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()

=== modified file 'tests/functional/openlp_core_lib/test_registry.py'
--- tests/functional/openlp_core_lib/test_registry.py	2013-02-16 06:51:25 +0000
+++ tests/functional/openlp_core_lib/test_registry.py	2013-02-16 12:14:21 +0000
@@ -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):

=== added directory 'tests/interfaces/openlp_core_lib'
=== added file 'tests/interfaces/openlp_core_lib/__init__.py'
=== added file 'tests/interfaces/openlp_core_lib/test_pluginmanager.py'
--- tests/interfaces/openlp_core_lib/test_pluginmanager.py	1970-01-01 00:00:00 +0000
+++ tests/interfaces/openlp_core_lib/test_pluginmanager.py	2013-02-16 12:14:21 +0000
@@ -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.'


Follow ups