diff --git a/openlp/.version b/openlp/.version index 4a36342fc..ae32d75e3 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -3.0.0 +3.0.0.dev6+dbea8bf56 \ No newline at end of file diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 6c243bdd1..fa651b810 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -100,6 +100,25 @@ def trace_error_handler(logger): logger.error(log_string) +def path_to_module(path): + """ + Convert a path to a module name (i.e openlp.core.common) + + :param pathlib.Path path: The path to convert to a module name. + :return: The module name. + :rtype: str + """ + module_path = path.with_suffix('') + return 'openlp.' + '.'.join(module_path.parts) + + +def import_openlp_module(module_name): + """ + Refactor module import out for testability. In Python 3.11, mock.patch and import_module do not play along nicely. + """ + importlib.import_module(module_name) + + def extension_loader(glob_pattern, excluded_files=None): """ A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and @@ -119,25 +138,13 @@ def extension_loader(glob_pattern, excluded_files=None): log.debug('Attempting to import %s', extension_path) module_name = path_to_module(extension_path) try: - importlib.import_module(module_name) + import_openlp_module(module_name) except (ImportError, OSError): # On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X) log.exception('Failed to import {module_name} on path {extension_path}' .format(module_name=module_name, extension_path=extension_path)) -def path_to_module(path): - """ - Convert a path to a module name (i.e openlp.core.common) - - :param pathlib.Path path: The path to convert to a module name. - :return: The module name. - :rtype: str - """ - module_path = path.with_suffix('') - return 'openlp.' + '.'.join(module_path.parts) - - def get_frozen_path(frozen_option, non_frozen_option): """ Return a path based on the system status. diff --git a/openlp/core/common/enum.py b/openlp/core/common/enum.py index a693925f7..2ce440802 100644 --- a/openlp/core/common/enum.py +++ b/openlp/core/common/enum.py @@ -112,6 +112,15 @@ class ServiceItemType(IntEnum): Image = 2 Command = 3 + @staticmethod + def parse(value): + if value in [1, '1', 'Text', 'ServiceItemType.Text']: + return ServiceItemType.Text + elif value in [2, '2', 'Image', 'ServiceItemType.Image']: + return ServiceItemType.Image + elif value in [3, '3', 'Command', 'ServiceItemType.Command']: + return ServiceItemType.Command + @unique class PluginStatus(IntEnum): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 81e706c05..15ce36312 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -463,7 +463,7 @@ class ServiceItem(RegistryProperties): header = service_item['serviceitem']['header'] self.title = header['title'] self.name = header['name'] - self.service_item_type = header['type'] + self.service_item_type = ServiceItemType.parse(header['type']) self.theme = header['theme'] self.add_icon() self.raw_footer = header['footer'] @@ -891,7 +891,7 @@ class ServiceItem(RegistryProperties): data_dict = { 'title': self.title, 'name': self.name, - 'type': str(self.service_item_type), + 'type': self.service_item_type, 'theme': self.theme, 'footer': self.raw_footer, 'audit': self.audit, diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index f9bf18a0e..3a9ffdf2f 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -24,7 +24,7 @@ presentations from a variety of document formats. """ import logging import os - +from pathlib import Path from openlp.core.common import extension_loader, sha256_file_hash from openlp.core.common.i18n import translate @@ -84,7 +84,9 @@ class PresentationPlugin(Plugin): has_old_scheme = True # Migrate each file presentation_paths = self.settings.value('presentations/presentations files') or [] - for path in presentation_paths: + for presentation_path in presentation_paths: + # Typecast to Path object + path = Path(presentation_path) # check to see if the file exists before trying to process it. if not path.exists(): continue diff --git a/tests/openlp_core/common/test_init.py b/tests/openlp_core/common/test_init.py index 750cd3d9c..fe5cc51f1 100644 --- a/tests/openlp_core/common/test_init.py +++ b/tests/openlp_core/common/test_init.py @@ -77,7 +77,7 @@ def test_extension_loader_files_found(): Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'), Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'), Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \ - patch('openlp.core.common.importlib.import_module') as mocked_import_module: + patch('openlp.core.common.import_openlp_module') as mocked_import_module: # WHEN: Calling `extension_loader` with a list of files to exclude extension_loader('glob', ['file2.py', 'file3.py']) @@ -97,7 +97,7 @@ def test_extension_loader_import_error(): return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[ Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \ - patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \ + patch('openlp.core.common.import_openlp_module', side_effect=ImportError()), \ patch('openlp.core.common.log') as mocked_logger: # WHEN: Calling `extension_loader` @@ -116,7 +116,7 @@ def test_extension_loader_os_error(): return_value=Path('/', 'app', 'dir', 'openlp')), \ patch.object(Path, 'glob', return_value=[ Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \ - patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \ + patch('openlp.core.common.import_openlp_module', side_effect=OSError()), \ patch('openlp.core.common.log') as mocked_logger: # WHEN: Calling `extension_loader` diff --git a/tests/openlp_core/lib/test_lib.py b/tests/openlp_core/lib/test_lib.py index 4e12d7ea4..c46f5aa51 100644 --- a/tests/openlp_core/lib/test_lib.py +++ b/tests/openlp_core/lib/test_lib.py @@ -273,7 +273,7 @@ def test_image_to_byte_base_64(): assert 'byte_array base64ified' == result, 'The result should be the return value of the mocked base64 method' -def test_create_thumb_with_size(): +def test_create_thumb_with_size(registry): """ Test the create_thumb() function with a given size. """ @@ -308,7 +308,7 @@ def test_create_thumb_with_size(): pass -def test_create_thumb_no_size(): +def test_create_thumb_no_size(registry): """ Test the create_thumb() function with no size specified. """ @@ -343,7 +343,7 @@ def test_create_thumb_no_size(): pass -def test_create_thumb_invalid_size(): +def test_create_thumb_invalid_size(registry): """ Test the create_thumb() function with invalid size specified. """ @@ -379,7 +379,7 @@ def test_create_thumb_invalid_size(): pass -def test_create_thumb_width_only(): +def test_create_thumb_width_only(registry): """ Test the create_thumb() function with a size of only width specified. """ @@ -415,7 +415,7 @@ def test_create_thumb_width_only(): pass -def test_create_thumb_height_only(): +def test_create_thumb_height_only(registry): """ Test the create_thumb() function with a size of only height specified. """ @@ -451,7 +451,7 @@ def test_create_thumb_height_only(): pass -def test_create_thumb_empty_img(): +def test_create_thumb_empty_img(registry): """ Test the create_thumb() function with a size of only height specified. """ @@ -502,7 +502,7 @@ def test_create_thumb_empty_img(): @patch('openlp.core.lib.QtGui.QImageReader') @patch('openlp.core.lib.build_icon') -def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader): +def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader, registry): """ Test that build_icon() is run against the image_path when the thumbnail fails to be created """ @@ -537,7 +537,7 @@ def test_check_item_selected_true(): assert result is True, 'The result should be True' -def test_check_item_selected_false(): +def test_check_item_selected_false(registry): """ Test that the check_item_selected() function returns False when there are no selected indexes. """ @@ -608,7 +608,7 @@ def test_validate_thumb_file_exists_and_older(): assert result is False, 'The result should be False' -def test_resize_thumb(): +def test_resize_thumb(registry): """ Test the resize_thumb() function """ @@ -630,7 +630,7 @@ def test_resize_thumb(): assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' -def test_resize_thumb_ignoring_aspect_ratio(): +def test_resize_thumb_ignoring_aspect_ratio(registry): """ Test the resize_thumb() function ignoring aspect ratio """ @@ -652,7 +652,7 @@ def test_resize_thumb_ignoring_aspect_ratio(): assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' -def test_resize_thumb_width_aspect_ratio(): +def test_resize_thumb_width_aspect_ratio(registry): """ Test the resize_thumb() function using the image's width as the reference """ @@ -670,7 +670,7 @@ def test_resize_thumb_width_aspect_ratio(): assert wanted_width == result_size.width(), 'The image should have the requested width.' -def test_resize_thumb_same_aspect_ratio(): +def test_resize_thumb_same_aspect_ratio(registry): """ Test the resize_thumb() function when the image and the wanted aspect ratio are the same """ diff --git a/tests/openlp_core/lib/test_serviceitem.py b/tests/openlp_core/lib/test_serviceitem.py index 7c7c8c1de..630bfa551 100644 --- a/tests/openlp_core/lib/test_serviceitem.py +++ b/tests/openlp_core/lib/test_serviceitem.py @@ -862,7 +862,7 @@ def test_to_dict_text_item(mocked_sha256_file_hash, state_media, settings, servi ], 'theme': None, 'title': 'Amazing Grace', - 'type': 'ServiceItemType.Text', + 'type': ServiceItemType.Text, 'data': {'authors': 'John Newton', 'title': 'amazing grace@'} } assert result == expected_dict @@ -905,7 +905,7 @@ def test_to_dict_image_item(state_media, settings, service_item_env): ], 'theme': -1, 'title': 'Images', - 'type': 'ServiceItemType.Image', + 'type': ServiceItemType.Image, 'data': {} } assert result == expected_dict @@ -961,7 +961,7 @@ def test_to_dict_presentation_item(mocked_image_uri, mocked_get_data_path, state ], 'theme': None, 'title': '', - 'type': 'ServiceItemType.Command', + 'type': ServiceItemType.Command, 'data': {} } assert result == expected_dict diff --git a/tests/openlp_plugins/presentations/test_plugin.py b/tests/openlp_plugins/presentations/test_plugin.py index 80a4af59e..16ede9359 100644 --- a/tests/openlp_plugins/presentations/test_plugin.py +++ b/tests/openlp_plugins/presentations/test_plugin.py @@ -21,7 +21,14 @@ """ This module contains tests for the plugin class Presentation plugin. """ +from pathlib import Path +from unittest.mock import MagicMock, patch + from openlp.plugins.presentations.presentationplugin import PresentationPlugin +from openlp.plugins.presentations.lib.presentationtab import PresentationTab + + +TEST_RESOURCES_PATH = Path(__file__) / '..' / '..' / '..' / 'resources' def test_plugin_about(): @@ -34,3 +41,33 @@ def test_plugin_about(): 'programs. The choice of available presentation programs is ' 'available to the user in a drop down box.' ) + + +def test_creaste_settings_tab(qapp, state, registry, settings): + """Test creating the settings tab""" + # GIVEN: A Presentations plugin + presentations_plugin = PresentationPlugin() + + # WHEN: create_settings_tab is run + presentations_plugin.create_settings_tab(None) + + # THEN: A settings tab should have been created + assert isinstance(presentations_plugin.settings_tab, PresentationTab) + + +@patch('openlp.plugins.presentations.presentationplugin.Manager') +def test_initialise(MockedManager, state, registry, mock_settings): + """Test that initialising the plugin works correctly""" + # GIVEN: Some initial values needed for intialisation and a presentations plugin + mock_settings.setValue.side_effect = [None, [str(TEST_RESOURCES_PATH / 'presentations' / 'test.ppt')]] + mocked_main_window = MagicMock() + registry.register('main_window', mocked_main_window) + presentations_plugin = PresentationPlugin() + presentations_plugin.media_item = MagicMock() + + # WHEN: initialise() is called + presentations_plugin.initialise() + + # THEN: Nothing should break, and everything should be called + mock_settings.setValue.assert_called_with('presentations/thumbnail_scheme', 'sha256file') + mock_settings.remove.assert_called_once_with('presentations/presentations files')