Test Migration and Cleanup - 1

This commit is contained in:
Tim Bentley 2021-04-24 16:55:38 +00:00 committed by Raoul Snyman
parent 5caca527e1
commit 9039fe04f8
211 changed files with 1526 additions and 2057 deletions

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Base directory for tests
"""
from PyQt5 import QtWidgets
# Only one QApplication can be created. Use QtWidgets.QApplication.instance() when you need to "create" a QApplication.
application = QtWidgets.QApplication([])
application.setApplicationName('OpenLP')
__all__ = ['application']

View File

@ -1,334 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.common.json package.
"""
import json
import os
from pathlib import Path
from unittest import TestCase
from unittest.mock import patch
from openlp.core.common import is_win
from openlp.core.common.json import JSONMixin, OpenLPJSONDecoder, OpenLPJSONEncoder, PathSerializer, _registered_classes
class BaseTestClass(object):
"""
Simple class to avoid repetition
"""
def __init__(self, a=None, b=None, c=None):
self.a = a
self.b = b
self.c = c
class TestJSONMixin(TestCase):
"""
Test the JSONMixin class
"""
def setUp(self):
self._registered_classes_patcher = patch.dict(_registered_classes, clear=True)
self.addCleanup(self._registered_classes_patcher.stop)
self._registered_classes_patcher.start()
def test_subclass_json_mixin(self):
"""
Test that a class is `registered` when subclassing JSONMixin
"""
# GIVEN: The JSONMixin class
# WHEN: Subclassing it
class TestClass(JSONMixin):
pass
# THEN: The TestClass should have been `registered`
assert _registered_classes['TestClass'] == TestClass
def test_subclass_json_mixin_alt_names(self):
"""
Test that a class is `registered` using the specified names when subclassing JSONMixin
"""
# GIVEN: The JSONMixin class
# WHEN: Subclassing it with custom names
class TestClass(JSONMixin, register_names=('AltName1', 'AltName2')):
pass
# THEN: The TestClass should have been registered with only those names
assert 'TestClass' not in _registered_classes
assert _registered_classes['AltName1'] == TestClass
assert _registered_classes['AltName2'] == TestClass
def test_encoding_json_mixin_subclass(self):
"""
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string
"""
# GIVEN: A instance of a subclass of the JSONMixin class
class TestClass(BaseTestClass, JSONMixin):
_json_keys = ['a', 'b']
instance = TestClass(a=1, c=2)
# WHEN: Serializing the instance
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
assert json_string == '{"a": 1, "json_meta": {"class": "TestClass", "version": 1}}'
def test_decoding_json_mixin_subclass(self):
"""
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string
"""
# GIVEN: A subclass of the JSONMixin class
class TestClass(BaseTestClass, JSONMixin):
_json_keys = ['a', 'b']
# WHEN: Deserializing a JSON representation of the TestClass
instance = json.loads(
'{"a": 1, "c": 2, "json_meta": {"class": "TestClass", "version": 1}}', cls=OpenLPJSONDecoder)
# THEN: Only the attributes specified by `_json_keys` should have been set
assert instance.__class__ == TestClass
assert instance.a == 1
assert instance.b is None
assert instance.c is None
def test_encoding_json_mixin_subclass_custom_name(self):
"""
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string when using a custom name
"""
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
_json_keys = ['a', 'b']
_name = 'AltName'
_version = 2
instance = TestClass(a=1, c=2)
# WHEN: Serializing the instance
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
assert json_string == '{"a": 1, "json_meta": {"class": "AltName", "version": 2}}'
def test_decoding_json_mixin_subclass_custom_name(self):
"""
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string when using a custom
name
"""
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
_json_keys = ['a', 'b']
_name = 'AltName'
_version = 2
# WHEN: Deserializing a JSON representation of the TestClass
instance = json.loads(
'{"a": 1, "c": 2, "json_meta": {"class": "AltName", "version": 2}}', cls=OpenLPJSONDecoder)
# THEN: Only the attributes specified by `_json_keys` should have been set
assert instance.__class__ == TestClass
assert instance.a == 1
assert instance.b is None
assert instance.c is None
class TestOpenLPJSONDecoder(TestCase):
"""
Test the OpenLPJsonDecoder class
"""
def test_object_hook_path_object(self):
"""
Test the object_hook method when called with a decoded Path JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJSONDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
result = instance.object_hook({'parts': ['test', 'path'], "json_meta": {"class": "Path", "version": 1}})
# THEN: A Path object should be returned
assert result == Path('test', 'path')
def test_object_hook_non_path_object(self):
"""
Test the object_hook method when called with a decoded JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJSONDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
with patch('openlp.core.common.json.Path') as mocked_path:
result = instance.object_hook({'key': 'value'})
# THEN: The object should be returned unchanged and a Path object should not have been initiated
assert result == {'key': 'value'}
assert mocked_path.called is False
def test_json_decode(self):
"""
Test the OpenLPJsonDecoder when decoding a JSON string
"""
# GIVEN: A JSON encoded string
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"key": "value", "json_meta": {"class": "Object"}}]'
# WHEN: Decoding the string using the OpenLPJsonDecoder class
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
# THEN: The object returned should be a python version of the JSON string
assert obj == [Path('test', 'path1'), Path('test', 'path2'), {'key': 'value', 'json_meta': {'class': 'Object'}}]
def test_json_decode_old_style(self):
"""
Test the OpenLPJsonDecoder when decoding a JSON string with an old-style Path object
"""
# GIVEN: A JSON encoded string
json_string = '[{"__Path__": ["test", "path1"]}, ' \
'{"__Path__": ["test", "path2"]}]'
# WHEN: Decoding the string using the OpenLPJsonDecoder class
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
# THEN: The object returned should be a python version of the JSON string
assert obj == [Path('test', 'path1'), Path('test', 'path2')]
class TestOpenLPJSONEncoder(TestCase):
"""
Test the OpenLPJSONEncoder class
"""
def test_default_path_object(self):
"""
Test the default method when called with a Path object
"""
# GIVEN: An instance of OpenLPJSONEncoder
instance = OpenLPJSONEncoder()
# WHEN: Calling the default method with a Path object
result = instance.default(Path('test', 'path'))
# THEN: A dictionary object that can be JSON encoded should be returned
assert result == {'parts': ('test', 'path'), "json_meta": {"class": "Path", "version": 1}}
def test_default_non_path_object(self):
"""
Test the default method when called with a object other than a Path object
"""
with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
# GIVEN: An instance of OpenLPJSONEncoder
instance = OpenLPJSONEncoder()
# WHEN: Calling the default method with a object other than a Path object
instance.default('invalid object')
# THEN: default method of the super class should have been called
mocked_super_default.assert_called_once_with('invalid object')
def test_json_encode(self):
"""
Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
"""
# GIVEN: A list of Path objects
obj = [Path('test', 'path1'), Path('test', 'path2')]
# WHEN: Encoding the object using the OpenLPJSONEncoder class
json_string = json.dumps(obj, cls=OpenLPJSONEncoder)
# THEN: The JSON string return should be a representation of the object encoded
assert json_string == '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}]'
class TestPathSerializer(TestCase):
def test_path_encode_json(self):
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = PathSerializer.encode_json(
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, extra=1, args=2)
# THEN: A Path object should have been returned
assert path == Path('path', 'to', 'fi.le')
def test_path_encode_json_base_path(self):
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON when the base_path arg is supplied.
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = PathSerializer.encode_json(
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, base_path=Path('/base'))
# THEN: A Path object should have been returned with an absolute path
assert path == Path('/', 'base', 'path', 'to', 'fi.le')
def test_path_json_object(self):
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object
obj = PathSerializer().json_object(path, extra=1, args=2)
# THEN: A JSON decodeable object should have been returned.
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
def test_path_json_object_is_js(self):
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object
"""
# GIVEN: A Path object from openlp.core.common.path
if is_win():
path = Path('c:\\', 'base', 'path', 'to', 'fi.le')
else:
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object
obj = PathSerializer().json_object(path, is_js=True, extra=1, args=2)
# THEN: A URI should be returned
if is_win():
assert obj == 'file:///c:/base/path/to/fi.le'
else:
assert obj == 'file:///base/path/to/fi.le'
def test_path_json_object_base_path(self):
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
base_path
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object with a base_path
obj = PathSerializer().json_object(path, base_path=Path('/', 'base'))
# THEN: A JSON decodable object should have been returned.
assert obj == {'parts': ('path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.ui package.
"""

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Interface tests
"""

View File

@ -1,99 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.lib.pluginmanager package.
"""
import shutil
import sys
from pathlib import Path
from tempfile import mkdtemp
from unittest import TestCase, skip
from unittest.mock import MagicMock, patch
from PyQt5 import QtWidgets
from openlp.core.common import is_win
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.state import State
from openlp.core.lib.pluginmanager import PluginManager
from tests.helpers.testmixin import TestMixin
class TestPluginManager(TestCase, TestMixin):
"""
Test the PluginManager class
"""
def setUp(self):
"""
Some pre-test setup required.
"""
self.setup_application()
self.build_settings()
self.temp_dir_path = Path(mkdtemp('openlp'))
Settings().setValue('advanced/data path', self.temp_dir_path)
Registry.create()
Registry().register('service_list', MagicMock())
self.main_window = QtWidgets.QMainWindow()
Registry().register('main_window', self.main_window)
def tearDown(self):
Settings().remove('advanced/data path')
self.destroy_settings()
del self.main_window
# On windows we need to manually garbage collect to close sqlalchemy files
# to avoid errors when temporary files are deleted.
if is_win():
import gc
gc.collect()
shutil.rmtree(self.temp_dir_path)
@skip
# This test is broken but totally unable to debug it.
@patch('openlp.plugins.songusage.songusageplugin.Manager')
@patch('openlp.plugins.songs.songsplugin.Manager')
@patch('openlp.plugins.images.imageplugin.Manager')
@patch('openlp.plugins.custom.customplugin.Manager')
@patch('openlp.plugins.alerts.alertsplugin.Manager')
def test_find_plugins(self, mocked_is1, mocked_is2, mocked_is3, mocked_is4, mocked_is5):
"""
Test the find_plugins() method to ensure it imports the correct plugins
"""
# GIVEN: A plugin manager
plugin_manager = PluginManager()
plugin_manager.bootstrap_initialise()
# WHEN: We mock out sys.platform to make it return "darwin" and then find the plugins
old_platform = sys.platform
sys.platform = 'darwin'
sys.platform = old_platform
# THEN: We should find the "Songs", "Bibles", etc in the plugins list
plugin_names = [plugin.name for plugin in State().list_plugins()]
assert 'songs' in plugin_names, 'There should be a "songs" plugin'
assert 'bibles' in plugin_names, 'There should be a "bibles" plugin'
assert 'presentations' in plugin_names, 'There should be a "presentations" plugin'
assert 'images' in plugin_names, 'There should be a "images" plugin'
assert 'media' in plugin_names, 'There should be a "media" plugin'
assert 'custom' in plugin_names, 'There should be a "custom" plugin'
assert 'songusage' in plugin_names, 'There should be a "songusage" plugin'
assert 'alerts' in plugin_names, 'There should be a "alerts" plugin'

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Module-level functions for the functional test suite
"""

View File

@ -1,86 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.ui.firsttimeform package.
"""
import pytest
from pathlib import Path
from unittest.mock import MagicMock, call, patch
from openlp.core.common.registry import Registry
from openlp.core.ui.firsttimeform import ThemeListWidgetItem
from openlp.core.ui.icons import UiIcons
sample_theme_data = {'file_name': 'BlueBurst.otz', 'sha256': 'sha_256_hash',
'thumbnail': 'BlueBurst.png', 'title': 'Blue Burst'}
@pytest.fixture()
def mocked_set_icon(mock_settings):
move_to_thread_patcher = patch('openlp.core.ui.firsttimeform.DownloadWorker.moveToThread').start()
set_icon_patcher = patch('openlp.core.ui.firsttimeform.ThemeListWidgetItem.setIcon').start()
q_thread_patcher = patch('openlp.core.ui.firsttimeform.QtCore.QThread').start()
mocked_app = MagicMock()
mocked_app.worker_threads = {}
mocked_main_window = MagicMock()
Registry().remove('application')
Registry().register('application', mocked_app)
Registry().register('main_window', mocked_main_window)
yield set_icon_patcher
move_to_thread_patcher.stop()
set_icon_patcher.stop()
q_thread_patcher.stop()
def test_failed_download(mocked_set_icon):
"""
Test that icon get set to indicate a failure when `DownloadWorker` emits the download_failed signal
"""
# GIVEN: An instance of `DownloadWorker`
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
worker_threads = Registry().get('application').worker_threads
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
# WHEN: `DownloadWorker` emits the `download_failed` signal
worker.download_failed.emit()
# THEN: Then the initial loading icon should have been replaced by the exception icon
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(UiIcons().exception)])
@patch('openlp.core.ui.firsttimeform.build_icon')
def test_successful_download(mocked_build_icon, mocked_set_icon):
"""
Test that the downloaded thumbnail is set as the icon when `DownloadWorker` emits the `download_succeeded`
signal
"""
# GIVEN: An instance of `DownloadWorker`
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
worker_threads = Registry().get('application').worker_threads
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
test_path = Path('downlaoded', 'file')
# WHEN: `DownloadWorker` emits the `download_succeeded` signal
worker.download_succeeded.emit(test_path)
# THEN: An icon should have been built from the downloaded file and used to replace the loading icon
mocked_build_icon.assert_called_once_with(test_path)
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(mocked_build_icon())])

View File

@ -1,545 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.lib package.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common.settings import Settings
from openlp.core.common.registry import Registry
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
from openlp.core.ui.servicemanager import ServiceManager
from tests.helpers.testmixin import TestMixin
class TestServiceManager(TestCase, TestMixin):
"""
Test the service manager
"""
def _create_mock_action(self, name, **kwargs):
"""
Create a fake action with some "real" attributes
"""
action = QtWidgets.QAction(self.service_manager)
action.setObjectName(name)
if kwargs.get('triggers'):
action.triggered.connect(kwargs.pop('triggers'))
self.service_manager.toolbar.actions[name] = action
return action
def setUp(self):
"""
Create the UI
"""
Registry.create()
self.setup_application()
Registry().register('application', MagicMock())
Registry().register('main_window', MagicMock())
Registry().register('settings', Settings())
self.service_manager = ServiceManager()
self.add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action')
self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
self.mocked_add_toolbar_action.side_effect = self._create_mock_action
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
self.add_toolbar_action_patcher.stop()
del self.service_manager
def test_basic_service_manager(self):
"""
Test the Service Manager UI Functionality
"""
# GIVEN: A New Service Manager instance
# WHEN I have set up the display
self.service_manager.setup_ui(self.service_manager)
# THEN the count of items should be zero
assert self.service_manager.service_manager_list.topLevelItemCount() == 0, \
'The service manager list should be empty '
@patch('openlp.core.ui.servicemanager.QtWidgets.QTreeWidget.itemAt')
@patch('openlp.core.ui.servicemanager.QtWidgets.QWidget.mapToGlobal')
@patch('openlp.core.ui.servicemanager.QtWidgets.QMenu.exec')
def test_default_context_menu(self, mocked_exec, mocked_mapToGlobal, mocked_item_at_method):
"""
Test the context_menu() method with a default service item
"""
# GIVEN: A service item added
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
mocked_item.data.return_value = 1
self.service_manager.setup_ui(self.service_manager)
# A service item without capabilities.
service_item = ServiceItem()
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_edit_context_menu(self):
"""
Test the context_menu() method with a edit service item
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanEdit)
service_item.edit_id = 1
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_maintain_context_menu(self):
"""
Test the context_menu() method with a maintain
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanMaintain)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_loopy_context_menu(self):
"""
Test the context_menu() method with a loop
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanLoop)
service_item.slides.append("One")
service_item.slides.append("Two")
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_start_time_context_menu(self):
"""
Test the context_menu() method with a start time
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_auto_start_context_menu(self):
"""
Test the context_menu() method with can auto start
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
self.service_manager.rename_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.rename_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_click_on_new_service(self):
"""
Test the on_new_service event handler is called by the UI
"""
# GIVEN: An initial form
mocked_event = MagicMock()
self.service_manager.on_new_service_clicked = mocked_event
self.service_manager.setup_ui(self.service_manager)
# WHEN displaying the UI and pressing cancel
new_service = self.service_manager.toolbar.actions['newService']
new_service.trigger()
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
def test_expand_selection_on_right_arrow(self):
"""
Test that a right arrow key press event calls the on_expand_selection function
"""
# GIVEN a mocked expand function
self.service_manager.on_expand_selection = MagicMock()
# WHEN the right arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Right, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_expand_selection function should have been called.
self.service_manager.on_expand_selection.assert_called_once_with()
def test_collapse_selection_on_left_arrow(self):
"""
Test that a left arrow key press event calls the on_collapse_selection function
"""
# GIVEN a mocked collapse function
self.service_manager.on_collapse_selection = MagicMock()
# WHEN the left arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Left, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_collapse_selection function should have been called.
self.service_manager.on_collapse_selection.assert_called_once_with()
def test_move_selection_down_on_down_arrow(self):
"""
Test that a down arrow key press event calls the on_move_selection_down function
"""
# GIVEN a mocked move down function
self.service_manager.on_move_selection_down = MagicMock()
# WHEN the down arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_move_selection_down function should have been called.
self.service_manager.on_move_selection_down.assert_called_once_with()
def test_move_selection_up_on_up_arrow(self):
"""
Test that an up arrow key press event calls the on_move_selection_up function
"""
# GIVEN a mocked move up function
self.service_manager.on_move_selection_up = MagicMock()
# WHEN the up arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_move_selection_up function should have been called.
self.service_manager.on_move_selection_up.assert_called_once_with()
def test_delete_selection_on_delete_key(self):
"""
Test that a delete key press event calls the on_delete_from_service function
"""
# GIVEN a mocked on_delete_from_service function
self.service_manager.on_delete_from_service = MagicMock()
# WHEN the delete key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_delete_from_service function should have been called.
self.service_manager.on_delete_from_service.assert_called_once_with()
def _setup_service_manager_list(self):
self.service_manager.expanded = MagicMock()
self.service_manager.collapsed = MagicMock()
verse_1 = QtWidgets.QTreeWidgetItem(0)
verse_2 = QtWidgets.QTreeWidgetItem(0)
song_item = QtWidgets.QTreeWidgetItem(0)
song_item.addChild(verse_1)
song_item.addChild(verse_2)
self.service_manager.setup_ui(self.service_manager)
self.service_manager.service_manager_list.addTopLevelItem(song_item)
return verse_1, verse_2, song_item
def test_on_expand_selection(self):
"""
Test that the on_expand_selection function successfully expands an item and moves to its first child
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(song_item)
# Reset expanded function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.expanded = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_expand_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
above_selected_index = self.service_manager.service_manager_list.indexAbove(selected_index)
assert self.service_manager.service_manager_list.isExpanded(above_selected_index) is True, \
'Item should have been expanded'
self.service_manager.expanded.assert_called_once_with(song_item)
def test_on_collapse_selection_with_parent_selected(self):
"""
Test that the on_collapse_selection function successfully collapses an item
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(song_item)
self.service_manager.service_manager_list.expandItem(song_item)
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.collapsed = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_collapse_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
'Item should have been collapsed'
assert self.service_manager.service_manager_list.currentItem() == song_item, \
'Top item should have been selected'
self.service_manager.collapsed.assert_called_once_with(song_item)
def test_on_collapse_selection_with_child_selected(self):
"""
Test that the on_collapse_selection function successfully collapses child's parent item
and moves selection to its parent.
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(verse_2)
self.service_manager.service_manager_list.expandItem(song_item)
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.collapsed = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_collapse_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
'Item should have been collapsed'
assert self.service_manager.service_manager_list.currentItem() == song_item, \
'Top item should have been selected'
self.service_manager.collapsed.assert_called_once_with(song_item)
def test_replace_service_item(self):
"""
Tests that the replace_service_item function replaces items as expected
"""
# GIVEN a service item list and a new item which name and edit_id match a service item
self.service_manager.repaint_service_list = MagicMock()
Registry().register('live_controller', MagicMock())
item1 = MagicMock()
item1.edit_id = 'abcd'
item1.name = 'itemA'
item2 = MagicMock()
item2.edit_id = 'abcd'
item2.name = 'itemB'
item3 = MagicMock()
item3.edit_id = 'cfgh'
item3.name = 'itemA'
self.service_manager.service_items = [
{'service_item': item1},
{'service_item': item2},
{'service_item': item3}
]
new_item = MagicMock()
new_item.edit_id = 'abcd'
new_item.name = 'itemA'
# WHEN replace_service_item is called
self.service_manager.replace_service_item(new_item)
# THEN new_item should replace item1, and only replaces that one item
assert self.service_manager.service_items[0]['service_item'] == new_item
new_item.merge.assert_called_once_with(item1)

View File

@ -1,123 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Interface tests to test the themeManager class and related methods.
"""
import pytest
from pathlib import Path
from unittest.mock import MagicMock, patch
from openlp.core.common.settings import Settings
from openlp.core.ui.thememanager import ThemeManager
@pytest.fixture()
def theme_manager(settings):
thm = ThemeManager()
return thm
def test_theme_manager_initialise(theme_manager):
"""
Test the thememanager initialise - basic test
"""
# GIVEN: A new a call to initialise
theme_manager.setup_ui = MagicMock()
theme_manager.build_theme_path = MagicMock()
Settings().setValue('themes/global theme', 'my_theme')
# WHEN: the initialisation is run
theme_manager.bootstrap_initialise()
# THEN:
theme_manager.setup_ui.assert_called_once_with(theme_manager)
assert theme_manager.global_theme == 'my_theme'
theme_manager.build_theme_path.assert_called_once_with()
@patch('openlp.core.ui.thememanager.create_paths')
@patch('openlp.core.ui.thememanager.AppLocation.get_section_data_path')
def test_build_theme_path(mocked_get_section_data_path, mocked_create_paths, theme_manager):
"""
Test the thememanager build_theme_path
"""
# GIVEN: A mocked out AppLocation.get_directory() and mocked create_paths
mocked_get_section_data_path.return_value = Path('tests/my_theme')
# WHEN: the build_theme_path is run
theme_manager.build_theme_path()
# THEN: The theme path and the thumb path should be correct
assert theme_manager.theme_path == Path('tests/my_theme')
assert theme_manager.thumb_path == Path('tests/my_theme/thumbnails')
mocked_create_paths.assert_called_once_with(Path('tests/my_theme'), Path('tests/my_theme/thumbnails'))
def test_click_on_new_theme(theme_manager):
"""
Test the on_add_theme event handler is called by the UI
"""
# GIVEN: An initial form
Settings().setValue('themes/global theme', 'my_theme')
mocked_event = MagicMock()
theme_manager.on_add_theme = mocked_event
theme_manager.setup_ui(theme_manager)
# WHEN displaying the UI and pressing cancel
new_theme = theme_manager.toolbar.actions['newTheme']
new_theme.trigger()
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
@patch('openlp.core.ui.themeform.ThemeForm._setup')
@patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
def test_bootstrap_post(mocked_rename_form, mocked_theme_form, theme_manager):
"""
Test the functions of bootstrap_post_setup are called.
"""
# GIVEN:
theme_manager.theme_path = MagicMock()
# WHEN:
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
theme_manager.bootstrap_post_set_up()
# THEN:
assert theme_manager.progress_form is not None
assert theme_manager.theme_form is not None
assert theme_manager.file_rename_form is not None
def test_bootstrap_completion(theme_manager):
"""
Test the functions of bootstrap_post_setup are called.
"""
# GIVEN:
theme_manager.load_themes = MagicMock()
theme_manager.upgrade_themes = MagicMock()
# WHEN:
theme_manager.bootstrap_completion()
# THEN:
theme_manager.upgrade_themes.assert_called_once()
theme_manager.load_themes.assert_called_once()

View File

@ -1,143 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Module to test the EditCustomForm.
"""
import pytest
from unittest.mock import MagicMock, call
from PyQt5 import QtCore, QtGui, QtTest, QtWidgets
from openlp.core.common.registry import Registry
from openlp.core.widgets.edits import HistoryComboBox, SearchEdit
class SearchTypes(object):
"""
Types of search
"""
First = 0
Second = 1
SECOND_PLACEHOLDER_TEXT = "Second Placeholder Text"
SEARCH_TYPES = [(SearchTypes.First, QtGui.QIcon(), "First", "First Placeholder Text"),
(SearchTypes.Second, QtGui.QIcon(), "Second", SECOND_PLACEHOLDER_TEXT)]
@pytest.fixture()
def search_edit(mock_settings):
main_window = QtWidgets.QMainWindow()
Registry().register('main_window', main_window)
Registry().remove('settings')
Registry().register('settings', MagicMock(**{'value.return_value': SearchTypes.First}))
s_edit = SearchEdit(main_window, 'settings_section')
# To complete set up we have to set the search types.
s_edit.set_search_types(SEARCH_TYPES)
return s_edit
@pytest.fixture()
def combo(mock_settings):
main_window = QtWidgets.QMainWindow()
Registry().register('main_window', main_window)
s_combo = HistoryComboBox(main_window)
return s_combo
def test_set_search_types(search_edit):
"""
Test setting the search types of the search edit.
"""
# GIVEN: The search edit with the search types set. NOTE: The set_search_types(types) is called in the setUp()
# method!
# WHEN:
# THEN: The first search type should be the first one in the list. The selected type should be saved in the
# settings
assert search_edit.current_search_type() == SearchTypes.First, \
"The first search type should be selected."
Registry().get('settings').setValue.assert_called_once_with('settings_section/last used search type', 0)
def test_set_current_search_type(search_edit):
"""
Test if changing the search type works.
"""
# GIVEN:
# WHEN: Change the search type
result = search_edit.set_current_search_type(SearchTypes.Second)
# THEN:
assert result is True, "The call should return success (True)."
assert search_edit.current_search_type() == SearchTypes.Second, \
"The search type should be SearchTypes.Second"
assert search_edit.placeholderText() == SECOND_PLACEHOLDER_TEXT, \
"The correct placeholder text should be 'Second Placeholder Text'."
Registry().get('settings').setValue.assert_has_calls(
[call('settings_section/last used search type', 0), call('settings_section/last used search type', 1)])
def test_clear_button_visibility(search_edit):
"""
Test if the clear button is hidden/shown correctly.
"""
# GIVEN: Everything is left to its defaults (hidden).
assert search_edit.clear_button.isHidden(), "Pre condition not met. Button should be hidden."
# WHEN: Type something in the search edit.
QtTest.QTest.keyPress(search_edit, QtCore.Qt.Key_A)
QtTest.QTest.keyRelease(search_edit, QtCore.Qt.Key_A)
# THEN: The clear button should not be hidden any more.
assert not search_edit.clear_button.isHidden(), "The clear button should be visible."
def test_press_clear_button(search_edit):
"""
Check if the search edit behaves correctly when pressing the clear button.
"""
# GIVEN: A search edit with text.
QtTest.QTest.keyPress(search_edit, QtCore.Qt.Key_A)
QtTest.QTest.keyRelease(search_edit, QtCore.Qt.Key_A)
# WHEN: Press the clear button.
QtTest.QTest.mouseClick(search_edit.clear_button, QtCore.Qt.LeftButton)
# THEN: The search edit text should be cleared and the button be hidden.
assert not search_edit.text(), "The search edit should not have any text."
assert search_edit.clear_button.isHidden(), "The clear button should be hidden."
def test_history_combo_get_items(combo):
"""
Test the getItems() method
"""
# GIVEN: The combo.
# WHEN: Add two items.
combo.addItem('test1')
combo.addItem('test2')
# THEN: The list of items should contain both strings.
assert combo.getItems() == ['test1', 'test2']

View File

@ -1,92 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.widgets.views.
"""
import pytest
from unittest.mock import patch
from PyQt5 import QtWidgets
from openlp.core.common.registry import Registry
from openlp.core.lib.serviceitem import ServiceItem
from openlp.core.widgets.views import ListPreviewWidget
from tests.utils.osdinteraction import read_service_from_file
@pytest.fixture()
def preview_widget(settings):
main_window = QtWidgets.QMainWindow()
Registry().register('main_window', main_window)
p_widget = ListPreviewWidget(main_window, 2)
return p_widget
def test_initial_slide_count(preview_widget):
"""
Test the initial slide count .
"""
# GIVEN: A new ListPreviewWidget instance.
# WHEN: No SlideItem has been added yet.
# THEN: The count of items should be zero.
assert preview_widget.slide_count() == 0, 'The slide list should be empty.'
def test_initial_slide_number(preview_widget):
"""
Test the initial current slide number.
"""
# GIVEN: A new ListPreviewWidget instance.
# WHEN: No SlideItem has been added yet.
# THEN: The number of the current item should be -1.
assert preview_widget.current_slide_number() == -1, 'The slide number should be -1.'
def test_replace_service_item(preview_widget, state_media):
"""
Test item counts and current number with a service item.
"""
# GIVEN: A ServiceItem with two frames.
service_item = ServiceItem(None)
service = read_service_from_file('serviceitem_image_3.osj')
with patch('os.path.exists') and patch('openlp.core.lib.serviceitem.sha256_file_hash'):
service_item.set_from_service(service[0])
# WHEN: Added to the preview widget.
preview_widget.replace_service_item(service_item, 1, 1)
# THEN: The slide count and number should fit.
assert preview_widget.slide_count() == 2, 'The slide count should be 2.'
assert preview_widget.current_slide_number() == 1, 'The current slide number should be 1.'
def test_change_slide(preview_widget, state_media):
"""
Test the change_slide method.
"""
# GIVEN: A ServiceItem with two frames content.
service_item = ServiceItem(None)
service = read_service_from_file('serviceitem_image_3.osj')
with patch('os.path.exists') and patch('openlp.core.lib.serviceitem.sha256_file_hash'):
service_item.set_from_service(service[0])
# WHEN: Added to the preview widget and switched to the second frame.
preview_widget.replace_service_item(service_item, 1, 0)
preview_widget.change_slide(1)
# THEN: The current_slide_number should reflect the change.
assert preview_widget.current_slide_number() == 1, 'The current slide number should be 1.'

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,167 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.plugins.planningcenter.planningcenterplugin package.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from PyQt5 import QtWidgets
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.state import State
from openlp.core.ui.icons import UiIcons
from openlp.core.ui.settingsform import SettingsForm
from openlp.plugins.planningcenter.planningcenterplugin import PlanningCenterPlugin
from tests.helpers.testmixin import TestMixin
class TestPlanningCenterPlugin(TestCase, TestMixin):
"""
Test the PlanningcenterPlugin class
"""
def setUp(self):
"""
Create the UI
"""
self.setup_application()
self.registry = Registry()
Registry.create()
State().load_settings()
Registry().register('settings', Settings())
self.plugin = PlanningCenterPlugin()
self.settings_form = SettingsForm()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.registry
del self.plugin
del self.settings_form
def test_class_init_defaults(self):
"""
Test that the plugin class is instantiated with the correct defaults
"""
# GIVEN: A PlanningcenterPlugin Class
# WHEN: the class has been through __init__
# THEN:
# planningcenter form is set to None
self.assertEqual(self.plugin.planningcenter_form, None, "Init plugin set to None")
# icon is set correctly
self.assertEqual(self.plugin.icon, UiIcons().planning_center, "Init icon set to planning_center icon")
# weight is -1
self.assertEqual(self.plugin.weight, -1, "Init weight set to -1")
# the planning_center module is registered active
self.assertEqual(State().is_module_active('planning_center'), True, "Init State() is active")
def test_initialise(self):
"""
Test that the initialise function can be called and it passes a call along
to its parent class
"""
# GIVEN: A PlanningcenterPlugin Class
# WHEN: initialise has been called on the class
with patch('openlp.plugins.planningcenter.planningcenterplugin.PlanningCenterPlugin.import_planning_center',
create=True):
return_value = self.plugin.initialise()
# THEN:
# the function returns and does not fail... it doesn't do much at this point, so this
# is mainly to improve test coverage
self.assertEqual(return_value, None, "Initialise was called on the class and it didn't crash")
def test_import_menu_item_added(self):
"""
Test that the add_import_menu_item function adds the menu item
"""
# GIVEN: A PlanningcenterPlugin Class
# WHEN: add_import_menu_item is called
import_menu = QtWidgets.QMenu()
self.plugin.add_import_menu_item(import_menu)
self.plugin.import_planning_center.setVisible(True)
# THEN:
# the menu should not be empty
self.assertEqual(import_menu.isEmpty(), False, "Menu Item is populated")
@patch('openlp.plugins.planningcenter.forms.selectplanform.SelectPlanForm.exec')
@patch('openlp.core.ui.settingsform.SettingsForm.exec')
def test_on_import_planning_center_triggered_with_auth_settings(self, mock_editauth_exec, mock_selectplan_exec):
"""
Test that the on_import_planning_center_triggered function correctly returns
the correct form to display.
"""
# GIVEN: A PlanningCenterPlugin Class with mocked exec calls on both
# PlanningCenter forms and settings set
application_id = 'abc'
secret = '123'
Settings().setValue('planningcenter/application_id', application_id)
Settings().setValue('planningcenter/secret', secret)
# init the planning center plugin so we have default values defined for Settings()
# WHEN: on_import_planning_center_triggered is called
self.plugin.on_import_planning_center_triggered()
# THEN:
self.assertEqual(mock_selectplan_exec.call_count, 1, "Select Plan Form was shown")
self.assertEqual(mock_editauth_exec.call_count, 0, "Edit Auth Form was not shown")
@patch('openlp.plugins.planningcenter.forms.selectplanform.SelectPlanForm.exec')
@patch('openlp.core.ui.settingsform.SettingsForm.exec')
def test_on_import_planning_center_triggered_without_auth_settings(self, mock_editauth_exec, mock_selectplan_exec):
"""
Test that the on_import_planning_center_triggered function correctly returns
the correct form to display.
"""
# GIVEN: A PlanningCenterPlugin Class with mocked exec calls on both
# PlanningCenter forms and settings set
application_id = ''
secret = ''
Settings().setValue('planningcenter/application_id', application_id)
Settings().setValue('planningcenter/secret', secret)
# init the planning center plugin so we have default values defined for Settings()
# WHEN: on_import_planning_center_triggered is called
self.plugin.on_import_planning_center_triggered()
# THEN:
self.assertEqual(mock_selectplan_exec.call_count, 0, "Select Plan Form was not shown")
self.assertEqual(mock_editauth_exec.call_count, 1, "Edit Auth Form was shown")
def test_about(self):
"""
Test that the about function returns text.
"""
# GIVEN: A PlanningCenterPlugin Class
# WHEN: about() is called
return_value = self.plugin.about()
# THEN:
self.assertGreater(len(return_value), 0, "About function returned some text")
def test_finalise(self):
"""
Test that the finalise function cleans up after the plugin
"""
# GIVEN: A PlanningcenterPlugin Class with a bunch of mocks
self.plugin.import_planning_center = MagicMock()
# WHEN: finalise has been called on the class
self.plugin.finalise()
# THEN: it cleans up after itself
self.plugin.import_planning_center.setVisible.assert_called_once_with(False)

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################

View File

@ -21,6 +21,7 @@
""" """
Functional tests to test the AppLocation class and related methods. Functional tests to test the AppLocation class and related methods.
""" """
import os
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from unittest import skipUnless from unittest import skipUnless
@ -34,7 +35,7 @@ from openlp.core.common import Singleton, add_actions, clean_filename, clean_but
trace_error_handler, verify_ip_address trace_error_handler, verify_ip_address
from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT
from tests.utils.constants import TEST_RESOURCES_PATH
test_non_ascii_string = '이것은 한국어 시험 문자열' test_non_ascii_string = '이것은 한국어 시험 문자열'
test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201' test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201'
@ -441,7 +442,8 @@ def test_sha256_file_hash():
Test SHA256 file hash Test SHA256 file hash
""" """
# GIVEN: A mocked Path object # GIVEN: A mocked Path object
filename = Path('tests/resources/presentations/test.ppt') ppt_path = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'test.ppt')
filename = Path(ppt_path)
# WHEN: Given a known salt+data # WHEN: Given a known salt+data
result = sha256_file_hash(filename) result = sha256_file_hash(filename)

View File

@ -0,0 +1,333 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.common.json package.
"""
import json
import os
from pathlib import Path
from unittest import TestCase
from unittest.mock import patch
from openlp.core.common import is_win
from openlp.core.common.json import JSONMixin, OpenLPJSONDecoder, OpenLPJSONEncoder, PathSerializer, _registered_classes
class BaseTestClass(object):
"""
Simple class to avoid repetition
"""
def __init__(self, a=None, b=None, c=None):
self.a = a
self.b = b
self.c = c
class TestJSONMixin(TestCase):
"""
Test the JSONMixin class
"""
def setUp(self):
self._registered_classes_patcher = patch.dict(_registered_classes, clear=True)
self.addCleanup(self._registered_classes_patcher.stop)
self._registered_classes_patcher.start()
def test_subclass_json_mixin(self):
"""
Test that a class is `registered` when subclassing JSONMixin
"""
# GIVEN: The JSONMixin class
# WHEN: Subclassing it
class TestClass(JSONMixin):
pass
# THEN: The TestClass should have been `registered`
assert _registered_classes['TestClass'] == TestClass
def test_subclass_json_mixin_alt_names(self):
"""
Test that a class is `registered` using the specified names when subclassing JSONMixin
"""
# GIVEN: The JSONMixin class
# WHEN: Subclassing it with custom names
class TestClass(JSONMixin, register_names=('AltName1', 'AltName2')):
pass
# THEN: The TestClass should have been registered with only those names
assert 'TestClass' not in _registered_classes
assert _registered_classes['AltName1'] == TestClass
assert _registered_classes['AltName2'] == TestClass
def test_encoding_json_mixin_subclass(self):
"""
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string
"""
# GIVEN: A instance of a subclass of the JSONMixin class
class TestClass(BaseTestClass, JSONMixin):
_json_keys = ['a', 'b']
instance = TestClass(a=1, c=2)
# WHEN: Serializing the instance
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
assert json_string == '{"a": 1, "json_meta": {"class": "TestClass", "version": 1}}'
def test_decoding_json_mixin_subclass(self):
"""
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string
"""
# GIVEN: A subclass of the JSONMixin class
class TestClass(BaseTestClass, JSONMixin):
_json_keys = ['a', 'b']
# WHEN: Deserializing a JSON representation of the TestClass
instance = json.loads(
'{"a": 1, "c": 2, "json_meta": {"class": "TestClass", "version": 1}}', cls=OpenLPJSONDecoder)
# THEN: Only the attributes specified by `_json_keys` should have been set
assert instance.__class__ == TestClass
assert instance.a == 1
assert instance.b is None
assert instance.c is None
def test_encoding_json_mixin_subclass_custom_name(self):
"""
Test that an instance of a JSONMixin subclass is properly serialized to a JSON string when using a custom name
"""
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
_json_keys = ['a', 'b']
_name = 'AltName'
_version = 2
instance = TestClass(a=1, c=2)
# WHEN: Serializing the instance
json_string = json.dumps(instance, cls=OpenLPJSONEncoder)
# THEN: Only the attributes specified by `_json_keys` should be serialized, and only if they have been set
assert json_string == '{"a": 1, "json_meta": {"class": "AltName", "version": 2}}'
def test_decoding_json_mixin_subclass_custom_name(self):
"""
Test that an instance of a JSONMixin subclass is properly deserialized from a JSON string when using a custom
name
"""
# GIVEN: A instance of a subclass of the JSONMixin class with a custom name
class TestClass(BaseTestClass, JSONMixin, register_names=('AltName', )):
_json_keys = ['a', 'b']
_name = 'AltName'
_version = 2
# WHEN: Deserializing a JSON representation of the TestClass
instance = json.loads(
'{"a": 1, "c": 2, "json_meta": {"class": "AltName", "version": 2}}', cls=OpenLPJSONDecoder)
# THEN: Only the attributes specified by `_json_keys` should have been set
assert instance.__class__ == TestClass
assert instance.a == 1
assert instance.b is None
assert instance.c is None
def test_object_hook_path_object():
"""
Test the object_hook method when called with a decoded Path JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJSONDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
result = instance.object_hook({'parts': ['test', 'path'], "json_meta": {"class": "Path", "version": 1}})
# THEN: A Path object should be returned
assert result == Path('test', 'path')
def test_object_hook_non_path_object():
"""
Test the object_hook method when called with a decoded JSON object
"""
# GIVEN: An instance of OpenLPJsonDecoder
instance = OpenLPJSONDecoder()
# WHEN: Calling the object_hook method with a decoded JSON object which contains a Path
with patch('openlp.core.common.json.Path') as mocked_path:
result = instance.object_hook({'key': 'value'})
# THEN: The object should be returned unchanged and a Path object should not have been initiated
assert result == {'key': 'value'}
assert mocked_path.called is False
def test_json_decode():
"""
Test the OpenLPJsonDecoder when decoding a JSON string
"""
# GIVEN: A JSON encoded string
json_string = '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"key": "value", "json_meta": {"class": "Object"}}]'
# WHEN: Decoding the string using the OpenLPJsonDecoder class
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
# THEN: The object returned should be a python version of the JSON string
assert obj == [Path('test', 'path1'), Path('test', 'path2'), {'key': 'value', 'json_meta': {'class': 'Object'}}]
def test_json_decode_old_style():
"""
Test the OpenLPJsonDecoder when decoding a JSON string with an old-style Path object
"""
# GIVEN: A JSON encoded string
json_string = '[{"__Path__": ["test", "path1"]}, ' \
'{"__Path__": ["test", "path2"]}]'
# WHEN: Decoding the string using the OpenLPJsonDecoder class
obj = json.loads(json_string, cls=OpenLPJSONDecoder)
# THEN: The object returned should be a python version of the JSON string
assert obj == [Path('test', 'path1'), Path('test', 'path2')]
def test_default_path_object():
"""
Test the default method when called with a Path object
"""
# GIVEN: An instance of OpenLPJSONEncoder
instance = OpenLPJSONEncoder()
# WHEN: Calling the default method with a Path object
result = instance.default(Path('test', 'path'))
# THEN: A dictionary object that can be JSON encoded should be returned
assert result == {'parts': ('test', 'path'), "json_meta": {"class": "Path", "version": 1}}
def test_default_non_path_object():
"""
Test the default method when called with a object other than a Path object
"""
with patch('openlp.core.common.json.JSONEncoder.default') as mocked_super_default:
# GIVEN: An instance of OpenLPJSONEncoder
instance = OpenLPJSONEncoder()
# WHEN: Calling the default method with a object other than a Path object
instance.default('invalid object')
# THEN: default method of the super class should have been called
mocked_super_default.assert_called_once_with('invalid object')
def test_json_encode():
"""
Test the OpenLPJsonDEncoder when encoding an object conatining Path objects
"""
# GIVEN: A list of Path objects
obj = [Path('test', 'path1'), Path('test', 'path2')]
# WHEN: Encoding the object using the OpenLPJSONEncoder class
json_string = json.dumps(obj, cls=OpenLPJSONEncoder)
# THEN: The JSON string return should be a representation of the object encoded
assert json_string == '[{"parts": ["test", "path1"], "json_meta": {"class": "Path", "version": 1}}, ' \
'{"parts": ["test", "path2"], "json_meta": {"class": "Path", "version": 1}}]'
def test_path_encode_json():
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = PathSerializer.encode_json(
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, extra=1, args=2)
# THEN: A Path object should have been returned
assert path == Path('path', 'to', 'fi.le')
def test_path_encode_json_base_path():
"""
Test that `Path.encode_json` returns a Path object from a dictionary representation of a Path object decoded
from JSON when the base_path arg is supplied.
"""
# GIVEN: A Path object from openlp.core.common.path
# WHEN: Calling encode_json, with a dictionary representation
path = PathSerializer.encode_json(
{'parts': ['path', 'to', 'fi.le'], "json_meta": {"class": "Path", "version": 1}}, base_path=Path('/base'))
# THEN: A Path object should have been returned with an absolute path
assert path == Path('/', 'base', 'path', 'to', 'fi.le')
def test_path_json_object():
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object
obj = PathSerializer().json_object(path, extra=1, args=2)
# THEN: A JSON decodeable object should have been returned.
assert obj == {'parts': (os.sep, 'base', 'path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}
def test_path_json_object_is_js():
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object
"""
# GIVEN: A Path object from openlp.core.common.path
if is_win():
path = Path('c:\\', 'base', 'path', 'to', 'fi.le')
else:
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object
obj = PathSerializer().json_object(path, is_js=True, extra=1, args=2)
# THEN: A URI should be returned
if is_win():
assert obj == 'file:///c:/base/path/to/fi.le'
else:
assert obj == 'file:///base/path/to/fi.le'
def test_path_json_object_base_path():
"""
Test that `Path.json_object` creates a JSON decode-able object from a Path object, that is relative to the
base_path
"""
# GIVEN: A Path object from openlp.core.common.path
path = Path('/base', 'path', 'to', 'fi.le')
# WHEN: Calling json_object with a base_path
obj = PathSerializer().json_object(path, base_path=Path('/', 'base'))
# THEN: A JSON decodable object should have been returned.
assert obj == {'parts': ('path', 'to', 'fi.le'), "json_meta": {"class": "Path", "version": 1}}

View File

@ -21,14 +21,23 @@
""" """
Package to test the openlp.core.lib.pluginmanager package. Package to test the openlp.core.lib.pluginmanager package.
""" """
import shutil
import sys
import pytest import pytest
from pathlib import Path
from tempfile import mkdtemp
from unittest import TestCase, skip
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from PyQt5 import QtWidgets
from openlp.core.state import State from openlp.core.state import State
from openlp.core.common import is_win
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib.plugin import PluginStatus from openlp.core.lib.plugin import PluginStatus
from openlp.core.lib.pluginmanager import PluginManager from openlp.core.lib.pluginmanager import PluginManager
from tests.helpers.testmixin import TestMixin
@pytest.fixture() @pytest.fixture()
@ -536,3 +545,64 @@ def test_new_service_created_with_active_plugin(registry, state):
# THEN: The is_active() and finalise() methods should have been called # THEN: The is_active() and finalise() methods should have been called
mocked_plugin.is_active.assert_called_with() mocked_plugin.is_active.assert_called_with()
mocked_plugin.new_service_created.assert_called_with() mocked_plugin.new_service_created.assert_called_with()
class TestPluginManager(TestCase, TestMixin):
"""
Test the PluginManager class
"""
def setUp(self):
"""
Some pre-test setup required.
"""
self.setup_application()
self.build_settings()
self.temp_dir_path = Path(mkdtemp('openlp'))
Settings().setValue('advanced/data path', self.temp_dir_path)
Registry.create()
Registry().register('service_list', MagicMock())
self.main_window = QtWidgets.QMainWindow()
Registry().register('main_window', self.main_window)
def tearDown(self):
Settings().remove('advanced/data path')
self.destroy_settings()
del self.main_window
# On windows we need to manually garbage collect to close sqlalchemy files
# to avoid errors when temporary files are deleted.
if is_win():
import gc
gc.collect()
shutil.rmtree(self.temp_dir_path)
@skip
# This test is broken but totally unable to debug it.
@patch('openlp.plugins.songusage.songusageplugin.Manager')
@patch('openlp.plugins.songs.songsplugin.Manager')
@patch('openlp.plugins.images.imageplugin.Manager')
@patch('openlp.plugins.custom.customplugin.Manager')
@patch('openlp.plugins.alerts.alertsplugin.Manager')
def test_find_plugins(self, mocked_is1, mocked_is2, mocked_is3, mocked_is4, mocked_is5):
"""
Test the find_plugins() method to ensure it imports the correct plugins
"""
# GIVEN: A plugin manager
plugin_manager = PluginManager()
plugin_manager.bootstrap_initialise()
# WHEN: We mock out sys.platform to make it return "darwin" and then find the plugins
old_platform = sys.platform
sys.platform = 'darwin'
sys.platform = old_platform
# THEN: We should find the "Songs", "Bibles", etc in the plugins list
plugin_names = [plugin.name for plugin in State().list_plugins()]
assert 'songs' in plugin_names, 'There should be a "songs" plugin'
assert 'bibles' in plugin_names, 'There should be a "bibles" plugin'
assert 'presentations' in plugin_names, 'There should be a "presentations" plugin'
assert 'images' in plugin_names, 'There should be a "images" plugin'
assert 'media' in plugin_names, 'There should be a "media" plugin'
assert 'custom' in plugin_names, 'There should be a "custom" plugin'
assert 'songusage' in plugin_names, 'There should be a "songusage" plugin'
assert 'alerts' in plugin_names, 'There should be a "alerts" plugin'

View File

@ -30,6 +30,7 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem from openlp.core.ui.firsttimeform import FirstTimeForm, ThemeListWidgetItem
from openlp.core.ui.firsttimewizard import RemotePage, ThemeListWidget from openlp.core.ui.firsttimewizard import RemotePage, ThemeListWidget
from openlp.core.ui.icons import UiIcons
INVALID_CONFIG = """ INVALID_CONFIG = """
@ -59,6 +60,23 @@ def download_env(registry):
run_thread_patcher.stop() run_thread_patcher.stop()
@pytest.fixture()
def mocked_set_icon(mock_settings):
move_to_thread_patcher = patch('openlp.core.ui.firsttimeform.DownloadWorker.moveToThread').start()
set_icon_patcher = patch('openlp.core.ui.firsttimeform.ThemeListWidgetItem.setIcon').start()
q_thread_patcher = patch('openlp.core.ui.firsttimeform.QtCore.QThread').start()
mocked_app = MagicMock()
mocked_app.worker_threads = {}
mocked_main_window = MagicMock()
Registry().remove('application')
Registry().register('application', mocked_app)
Registry().register('main_window', mocked_main_window)
yield set_icon_patcher
move_to_thread_patcher.stop()
set_icon_patcher.stop()
q_thread_patcher.stop()
def test_init_sample_data(download_env): def test_init_sample_data(download_env):
""" """
Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated Test that the theme data is loaded correctly in to a ThemeListWidgetItem object when instantiated
@ -428,3 +446,39 @@ def test_theme_list_widget_resize(ftf_app):
# THEN: Check that the correct calculations were done # THEN: Check that the correct calculations were done
mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140)) mocked_setGridSize.assert_called_once_with(QtCore.QSize(149, 140))
def test_failed_download(mocked_set_icon):
"""
Test that icon get set to indicate a failure when `DownloadWorker` emits the download_failed signal
"""
# GIVEN: An instance of `DownloadWorker`
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
worker_threads = Registry().get('application').worker_threads
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
# WHEN: `DownloadWorker` emits the `download_failed` signal
worker.download_failed.emit()
# THEN: Then the initial loading icon should have been replaced by the exception icon
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(UiIcons().exception)])
@patch('openlp.core.ui.firsttimeform.build_icon')
def test_successful_download(mocked_build_icon, mocked_set_icon):
"""
Test that the downloaded thumbnail is set as the icon when `DownloadWorker` emits the `download_succeeded`
signal
"""
# GIVEN: An instance of `DownloadWorker`
instance = ThemeListWidgetItem('url', sample_theme_data, MagicMock()) # noqa Overcome GC issue
worker_threads = Registry().get('application').worker_threads
worker = worker_threads['thumbnail_download_BlueBurst.png']['worker']
test_path = Path('downlaoded', 'file')
# WHEN: `DownloadWorker` emits the `download_succeeded` signal
worker.download_succeeded.emit(test_path)
# THEN: An icon should have been built from the downloaded file and used to replace the loading icon
mocked_build_icon.assert_called_once_with(test_path)
mocked_set_icon.assert_has_calls([call(UiIcons().picture), call(mocked_build_icon())])

View File

@ -21,17 +21,23 @@
""" """
Package to test the openlp.core.ui.slidecontroller package. Package to test the openlp.core.ui.slidecontroller package.
""" """
import PyQt5
from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
import PyQt5 from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import ThemeLevel from openlp.core.common import ThemeLevel
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.common.enum import ServiceItemType from openlp.core.common.enum import ServiceItemType
from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem
from openlp.core.ui.servicemanager import ServiceManager from openlp.core.ui.servicemanager import ServiceManager
from openlp.core.widgets.toolbar import OpenLPToolbar from openlp.core.widgets.toolbar import OpenLPToolbar
from tests.helpers.testmixin import TestMixin
def test_initial_service_manager(registry): def test_initial_service_manager(registry):
""" """
@ -756,3 +762,515 @@ def test_theme_change_song(mocked_regenerate_service_items, registry):
# THEN: The the theme toolbar should be visible # THEN: The the theme toolbar should be visible
assert service_manager.toolbar.actions['theme_combo_box'].isVisible() is True, \ assert service_manager.toolbar.actions['theme_combo_box'].isVisible() is True, \
'The visibility should be True' 'The visibility should be True'
class TestServiceManager(TestCase, TestMixin):
"""
Test the service manager
"""
def _create_mock_action(self, name, **kwargs):
"""
Create a fake action with some "real" attributes
"""
action = QtWidgets.QAction(self.service_manager)
action.setObjectName(name)
if kwargs.get('triggers'):
action.triggered.connect(kwargs.pop('triggers'))
self.service_manager.toolbar.actions[name] = action
return action
def setUp(self):
"""
Create the UI
"""
Registry.create()
self.setup_application()
Registry().register('application', MagicMock())
Registry().register('main_window', MagicMock())
Registry().register('settings', Settings())
self.service_manager = ServiceManager()
self.add_toolbar_action_patcher = patch('openlp.core.ui.servicemanager.OpenLPToolbar.add_toolbar_action')
self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start()
self.mocked_add_toolbar_action.side_effect = self._create_mock_action
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
self.add_toolbar_action_patcher.stop()
del self.service_manager
def test_basic_service_manager(self):
"""
Test the Service Manager UI Functionality
"""
# GIVEN: A New Service Manager instance
# WHEN I have set up the display
self.service_manager.setup_ui(self.service_manager)
# THEN the count of items should be zero
assert self.service_manager.service_manager_list.topLevelItemCount() == 0, \
'The service manager list should be empty '
@patch('openlp.core.ui.servicemanager.QtWidgets.QTreeWidget.itemAt')
@patch('openlp.core.ui.servicemanager.QtWidgets.QWidget.mapToGlobal')
@patch('openlp.core.ui.servicemanager.QtWidgets.QMenu.exec')
def test_default_context_menu(self, mocked_exec, mocked_mapToGlobal, mocked_item_at_method):
"""
Test the context_menu() method with a default service item
"""
# GIVEN: A service item added
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
mocked_item.data.return_value = 1
self.service_manager.setup_ui(self.service_manager)
# A service item without capabilities.
service_item = ServiceItem()
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_edit_context_menu(self):
"""
Test the context_menu() method with a edit service item
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanEdit)
service_item.edit_id = 1
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_maintain_context_menu(self):
"""
Test the context_menu() method with a maintain
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanMaintain)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_loopy_context_menu(self):
"""
Test the context_menu() method with a loop
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanLoop)
service_item.slides.append("One")
service_item.slides.append("Two")
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_start_time_context_menu(self):
"""
Test the context_menu() method with a start time
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.HasVariableStartTime)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.auto_start_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_auto_start_context_menu(self):
"""
Test the context_menu() method with can auto start
"""
# GIVEN: A service item added
self.service_manager.setup_ui(self.service_manager)
with patch('PyQt5.QtWidgets.QTreeWidget.itemAt') as mocked_item_at_method, \
patch('PyQt5.QtWidgets.QWidget.mapToGlobal'), \
patch('PyQt5.QtWidgets.QMenu.exec'):
mocked_item = MagicMock()
mocked_item.parent.return_value = None
mocked_item_at_method.return_value = mocked_item
# We want 1 to be returned for the position
mocked_item.data.return_value = 1
# A service item without capabilities.
service_item = ServiceItem()
service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
self.service_manager.service_items = [{'service_item': service_item}]
q_point = None
# Mocked actions.
self.service_manager.edit_action.setVisible = MagicMock()
self.service_manager.create_custom_action.setVisible = MagicMock()
self.service_manager.maintain_action.setVisible = MagicMock()
self.service_manager.notes_action.setVisible = MagicMock()
self.service_manager.time_action.setVisible = MagicMock()
self.service_manager.auto_start_action.setVisible = MagicMock()
self.service_manager.rename_action.setVisible = MagicMock()
# WHEN: Show the context menu.
self.service_manager.context_menu(q_point)
# THEN: The following actions should be not visible.
self.service_manager.edit_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.create_custom_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.maintain_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.notes_action.setVisible.assert_called_with(True), 'The action should be set visible.'
self.service_manager.time_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
self.service_manager.auto_start_action.setVisible.assert_called_with(True), \
'The action should be set visible.'
self.service_manager.rename_action.setVisible.assert_called_once_with(False), \
'The action should be set invisible.'
def test_click_on_new_service(self):
"""
Test the on_new_service event handler is called by the UI
"""
# GIVEN: An initial form
mocked_event = MagicMock()
self.service_manager.on_new_service_clicked = mocked_event
self.service_manager.setup_ui(self.service_manager)
# WHEN displaying the UI and pressing cancel
new_service = self.service_manager.toolbar.actions['newService']
new_service.trigger()
assert mocked_event.call_count == 1, 'The on_new_service_clicked method should have been called once'
def test_expand_selection_on_right_arrow(self):
"""
Test that a right arrow key press event calls the on_expand_selection function
"""
# GIVEN a mocked expand function
self.service_manager.on_expand_selection = MagicMock()
# WHEN the right arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Right, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_expand_selection function should have been called.
self.service_manager.on_expand_selection.assert_called_once_with()
def test_collapse_selection_on_left_arrow(self):
"""
Test that a left arrow key press event calls the on_collapse_selection function
"""
# GIVEN a mocked collapse function
self.service_manager.on_collapse_selection = MagicMock()
# WHEN the left arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Left, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_collapse_selection function should have been called.
self.service_manager.on_collapse_selection.assert_called_once_with()
def test_move_selection_down_on_down_arrow(self):
"""
Test that a down arrow key press event calls the on_move_selection_down function
"""
# GIVEN a mocked move down function
self.service_manager.on_move_selection_down = MagicMock()
# WHEN the down arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Down, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_move_selection_down function should have been called.
self.service_manager.on_move_selection_down.assert_called_once_with()
def test_move_selection_up_on_up_arrow(self):
"""
Test that an up arrow key press event calls the on_move_selection_up function
"""
# GIVEN a mocked move up function
self.service_manager.on_move_selection_up = MagicMock()
# WHEN the up arrow key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Up, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_move_selection_up function should have been called.
self.service_manager.on_move_selection_up.assert_called_once_with()
def test_delete_selection_on_delete_key(self):
"""
Test that a delete key press event calls the on_delete_from_service function
"""
# GIVEN a mocked on_delete_from_service function
self.service_manager.on_delete_from_service = MagicMock()
# WHEN the delete key event is called
self.service_manager.setup_ui(self.service_manager)
event = QtGui.QKeyEvent(QtCore.QEvent.KeyPress, QtCore.Qt.Key_Delete, QtCore.Qt.NoModifier)
self.service_manager.service_manager_list.keyPressEvent(event)
# THEN the on_delete_from_service function should have been called.
self.service_manager.on_delete_from_service.assert_called_once_with()
def _setup_service_manager_list(self):
self.service_manager.expanded = MagicMock()
self.service_manager.collapsed = MagicMock()
verse_1 = QtWidgets.QTreeWidgetItem(0)
verse_2 = QtWidgets.QTreeWidgetItem(0)
song_item = QtWidgets.QTreeWidgetItem(0)
song_item.addChild(verse_1)
song_item.addChild(verse_2)
self.service_manager.setup_ui(self.service_manager)
self.service_manager.service_manager_list.addTopLevelItem(song_item)
return verse_1, verse_2, song_item
def test_on_expand_selection(self):
"""
Test that the on_expand_selection function successfully expands an item and moves to its first child
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(song_item)
# Reset expanded function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.expanded = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_expand_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
above_selected_index = self.service_manager.service_manager_list.indexAbove(selected_index)
assert self.service_manager.service_manager_list.isExpanded(above_selected_index) is True, \
'Item should have been expanded'
self.service_manager.expanded.assert_called_once_with(song_item)
def test_on_collapse_selection_with_parent_selected(self):
"""
Test that the on_collapse_selection function successfully collapses an item
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(song_item)
self.service_manager.service_manager_list.expandItem(song_item)
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.collapsed = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_collapse_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
'Item should have been collapsed'
assert self.service_manager.service_manager_list.currentItem() == song_item, \
'Top item should have been selected'
self.service_manager.collapsed.assert_called_once_with(song_item)
def test_on_collapse_selection_with_child_selected(self):
"""
Test that the on_collapse_selection function successfully collapses child's parent item
and moves selection to its parent.
"""
# GIVEN a mocked servicemanager list
verse_1, verse_2, song_item = self._setup_service_manager_list()
self.service_manager.service_manager_list.setCurrentItem(verse_2)
self.service_manager.service_manager_list.expandItem(song_item)
# Reset collapsed function in case it has been called and/or changed in initialisation of the service manager.
self.service_manager.collapsed = MagicMock()
# WHEN on_expand_selection is called
self.service_manager.on_collapse_selection()
# THEN selection should be expanded
selected_index = self.service_manager.service_manager_list.currentIndex()
assert self.service_manager.service_manager_list.isExpanded(selected_index) is False, \
'Item should have been collapsed'
assert self.service_manager.service_manager_list.currentItem() == song_item, \
'Top item should have been selected'
self.service_manager.collapsed.assert_called_once_with(song_item)
def test_replace_service_item(self):
"""
Tests that the replace_service_item function replaces items as expected
"""
# GIVEN a service item list and a new item which name and edit_id match a service item
self.service_manager.repaint_service_list = MagicMock()
Registry().register('live_controller', MagicMock())
item1 = MagicMock()
item1.edit_id = 'abcd'
item1.name = 'itemA'
item2 = MagicMock()
item2.edit_id = 'abcd'
item2.name = 'itemB'
item3 = MagicMock()
item3.edit_id = 'cfgh'
item3.name = 'itemA'
self.service_manager.service_items = [
{'service_item': item1},
{'service_item': item2},
{'service_item': item3}
]
new_item = MagicMock()
new_item.edit_id = 'abcd'
new_item.name = 'itemA'
# WHEN replace_service_item is called
self.service_manager.replace_service_item(new_item)
# THEN new_item should replace item1, and only replaces that one item
assert self.service_manager.service_items[0]['service_item'] == new_item
new_item.merge.assert_called_once_with(item1)

View File

@ -21,6 +21,7 @@
""" """
Package to test the openlp.core.ui.thememanager package. Package to test the openlp.core.ui.thememanager package.
""" """
import pytest
import os import os
import shutil import shutil
from pathlib import Path from pathlib import Path
@ -30,10 +31,17 @@ from unittest.mock import ANY, Mock, MagicMock, patch, call, sentinel
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.ui.thememanager import ThemeManager from openlp.core.ui.thememanager import ThemeManager
from tests.utils.constants import RESOURCE_PATH from tests.utils.constants import RESOURCE_PATH
@pytest.fixture()
def theme_manager(settings):
thm = ThemeManager()
return thm
@patch('openlp.core.ui.thememanager.zipfile.ZipFile.__init__') @patch('openlp.core.ui.thememanager.zipfile.ZipFile.__init__')
@patch('openlp.core.ui.thememanager.zipfile.ZipFile.write') @patch('openlp.core.ui.thememanager.zipfile.ZipFile.write')
def test_export_theme(mocked_zipfile_write, mocked_zipfile_init, registry): def test_export_theme(mocked_zipfile_write, mocked_zipfile_init, registry):
@ -421,3 +429,91 @@ def test_update_preview_images(registry):
assert theme_manager.save_preview.call_args_list == [call('Default', 'preview'), call('Test', 'preview')] assert theme_manager.save_preview.call_args_list == [call('Default', 'preview'), call('Test', 'preview')]
theme_manager.progress_form.close.assert_called_once_with() theme_manager.progress_form.close.assert_called_once_with()
theme_manager.load_themes.assert_called_once_with() theme_manager.load_themes.assert_called_once_with()
def test_theme_manager_initialise(theme_manager):
"""
Test the thememanager initialise - basic test
"""
# GIVEN: A new a call to initialise
theme_manager.setup_ui = MagicMock()
theme_manager.build_theme_path = MagicMock()
Settings().setValue('themes/global theme', 'my_theme')
# WHEN: the initialisation is run
theme_manager.bootstrap_initialise()
# THEN:
theme_manager.setup_ui.assert_called_once_with(theme_manager)
assert theme_manager.global_theme == 'my_theme'
theme_manager.build_theme_path.assert_called_once_with()
@patch('openlp.core.ui.thememanager.create_paths')
@patch('openlp.core.ui.thememanager.AppLocation.get_section_data_path')
def test_build_theme_path(mocked_get_section_data_path, mocked_create_paths, theme_manager):
"""
Test the thememanager build_theme_path
"""
# GIVEN: A mocked out AppLocation.get_directory() and mocked create_paths
mocked_get_section_data_path.return_value = Path('tests/my_theme')
# WHEN: the build_theme_path is run
theme_manager.build_theme_path()
# THEN: The theme path and the thumb path should be correct
assert theme_manager.theme_path == Path('tests/my_theme')
assert theme_manager.thumb_path == Path('tests/my_theme/thumbnails')
mocked_create_paths.assert_called_once_with(Path('tests/my_theme'), Path('tests/my_theme/thumbnails'))
def test_click_on_new_theme(theme_manager):
"""
Test the on_add_theme event handler is called by the UI
"""
# GIVEN: An initial form
Settings().setValue('themes/global theme', 'my_theme')
mocked_event = MagicMock()
theme_manager.on_add_theme = mocked_event
theme_manager.setup_ui(theme_manager)
# WHEN displaying the UI and pressing cancel
new_theme = theme_manager.toolbar.actions['newTheme']
new_theme.trigger()
assert mocked_event.call_count == 1, 'The on_add_theme method should have been called once'
@patch('openlp.core.ui.themeform.ThemeForm._setup')
@patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
def test_bootstrap_post(mocked_rename_form, mocked_theme_form, theme_manager):
"""
Test the functions of bootstrap_post_setup are called.
"""
# GIVEN:
theme_manager.theme_path = MagicMock()
# WHEN:
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
theme_manager.bootstrap_post_set_up()
# THEN:
assert theme_manager.progress_form is not None
assert theme_manager.theme_form is not None
assert theme_manager.file_rename_form is not None
def test_bootstrap_completion(theme_manager):
"""
Test the functions of bootstrap_post_setup are called.
"""
# GIVEN:
theme_manager.load_themes = MagicMock()
theme_manager.upgrade_themes = MagicMock()
# WHEN:
theme_manager.bootstrap_completion()
# THEN:
theme_manager.upgrade_themes.assert_called_once()
theme_manager.load_themes.assert_called_once()

Some files were not shown because too many files have changed in this diff Show More