Revert "Migrate Media and add Pytest-qt"

This reverts commit 3dcac055be.
This commit is contained in:
Tim Bentley 2020-02-12 20:28:33 +00:00
parent 0a22c7f77a
commit 170c37d441
26 changed files with 1480 additions and 1518 deletions

View File

@ -376,7 +376,7 @@ def main():
Registry().register('application-qt', application)
Registry().register('application', app)
Registry().set_flag('no_web_server', args.no_web_server)
# Create and install settings.
# Upgrade settings.
app.settings = settings
Registry().register('settings', settings)
application.setApplicationVersion(get_version()['version'])

View File

@ -749,7 +749,7 @@ class Settings(QtCore.QSettings):
self.remove('SettingsImport')
# Get the settings.
keys = self.allKeys()
export_settings = QtCore.QSettings(str(temp_path), Settings.IniFormat)
export_settings = QtCore.QSettings(str(temp_path), QtCore.QSettings.IniFormat)
# Add a header section.
# This is to insure it's our conf file for import.
now = datetime.datetime.now()

View File

@ -335,7 +335,7 @@ class AdvancedTab(SettingsTab):
def load(self):
"""
Load self.settings from disk.
Load settings from disk.
"""
self.settings.beginGroup(self.settings_section)
# The max recent files value does not have an interface and so never
@ -395,7 +395,7 @@ class AdvancedTab(SettingsTab):
def save(self):
"""
Save self.settings to disk.
Save settings to disk.
"""
self.settings.beginGroup(self.settings_section)
self.settings.setValue('default service enabled', self.service_name_check_box.isChecked())

View File

@ -42,7 +42,6 @@ from openlp.core.common.i18n import LanguageManager, UiStrings, translate
from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import create_paths
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.display.screens import ScreenList
from openlp.core.lib.plugin import PluginStatus
from openlp.core.lib.ui import create_action
@ -874,7 +873,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
create_paths(temp_dir_path)
temp_config_path = temp_dir_path / import_file_path.name
shutil.copyfile(import_file_path, temp_config_path)
import_settings = Settings(str(temp_config_path), Settings.IniFormat)
import_settings = QtCore.QSettings(str(temp_config_path), QtCore.QSettings.IniFormat)
self.log_info('hook upgrade_plugin_settings')
self.plugin_manager.hook_upgrade_plugin_settings(import_settings)
@ -1376,11 +1375,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
else:
self.log_info('No data copy requested')
# Change the location of data directory in config file.
settings = QtCore.QSettings()
settings.setValue('advanced/data path', self.new_data_path)
self.settings.setValue('advanced/data path', self.new_data_path)
# Check if the new data path is our default.
if self.new_data_path == AppLocation.get_directory(AppLocation.DataDir):
settings.remove('advanced/data path')
self.settings.remove('advanced/data path')
self.application.set_normal_cursor()
def open_cmd_line_files(self, args):

View File

@ -389,9 +389,9 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert
def save_options(self):
"""
Save the self.settings and close the dialog.
Save the settings and close the dialog.
"""
# Save the self.settings for this dialog.
# Save the settings for this dialog.
self.settings.beginGroup('advanced')
self.settings.setValue('print slide text', self.slide_text_check_box.isChecked())
self.settings.setValue('add page break', self.page_break_after_text.isChecked())

View File

@ -338,7 +338,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
# Append the optical string to the media list
file_paths.append(optical)
self.load_list([str(optical)])
self.settigns.setValue(self.settings_section + '/media files', file_paths)
self.settings.setValue(self.settings_section + '/media files', file_paths)
def on_open_device_stream(self):
"""

View File

@ -18,8 +18,7 @@
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
# import sys
from unittest import TestCase
import pytest
from unittest.mock import MagicMock, patch
from PyQt5 import QtCore
@ -30,9 +29,7 @@ from openlp.core.state import State
# Mock QtWebEngineWidgets
# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
from openlp.core.common.settings import Settings
from openlp.core.common.registry import Registry
from openlp.core.display.screens import ScreenList
from openlp.core.lib.serviceitem import ServiceItem
from tests.utils import convert_file_service_item
from tests.utils.constants import RESOURCE_PATH
@ -47,102 +44,105 @@ SCREEN = {
}
class TestController(TestCase):
@pytest.fixture
def test_controller_env(settings):
mocked_live_controller = MagicMock()
desktop = MagicMock()
desktop.primaryScreen.return_value = SCREEN['primary']
desktop.screenCount.return_value = SCREEN['number']
desktop.screenGeometry.return_value = SCREEN['size']
with patch('openlp.core.display.screens.QtWidgets.QApplication.screens') as mocked_screens:
mocked_screens.return_value = [
MagicMock(**{'geometry.return_value': SCREEN['size']})
]
# Mock the renderer and its format_slide method
mocked_renderer = MagicMock()
def side_effect_return_arg(arg1, arg2):
return [arg1]
mocked_slide_formater = MagicMock(side_effect=side_effect_return_arg)
mocked_renderer.format_slide = mocked_slide_formater
Registry().register('live_controller', mocked_live_controller)
Registry().register('renderer', mocked_renderer)
flask_app.config['TESTING'] = True
client = flask_app.test_client()
return mocked_live_controller, client
def test_controller_text_empty(test_controller_env):
"""
Test the Remote plugin deploy functions
Remote API Tests : test the controller text method can be called with empty service item
"""
# GIVEN: A mocked service with a dummy service item
mocked_live_controller = test_controller_env[0]
client = test_controller_env[1]
mocked_service_item = MagicMock()
mocked_service_item.get_frames.return_value = []
mocked_service_item.unique_identifier = 'mock-service-item'
mocked_live_controller.service_item = mocked_service_item
def setUp(self):
"""
Setup for tests
"""
Registry.create()
self.registry = Registry()
Registry().register('settings', Settings())
self.mocked_live_controller = MagicMock()
self.desktop = MagicMock()
self.desktop.primaryScreen.return_value = SCREEN['primary']
self.desktop.screenCount.return_value = SCREEN['number']
self.desktop.screenGeometry.return_value = SCREEN['size']
with patch('openlp.core.display.screens.QtWidgets.QApplication.screens') as mocked_screens:
mocked_screens.return_value = [
MagicMock(**{'geometry.return_value': SCREEN['size']})
]
self.screens = ScreenList.create(self.desktop)
# Mock the renderer and its format_slide method
self.mocked_renderer = MagicMock()
# WHEN: I trigger the method
ret = client.get('/api/controller/live/text').get_json()
def side_effect_return_arg(arg1, arg2):
return [arg1]
self.mocked_slide_formater = MagicMock(side_effect=side_effect_return_arg)
self.mocked_renderer.format_slide = self.mocked_slide_formater
Registry().register('live_controller', self.mocked_live_controller)
Registry().register('renderer', self.mocked_renderer)
flask_app.config['TESTING'] = True
self.client = flask_app.test_client()
# THEN: I get a basic set of results
assert ret['results']['item'] == 'mock-service-item'
assert len(ret['results']['slides']) == 0
def test_controller_text_empty(self):
"""
Remote API Tests : test the controller text method can be called with empty service item
"""
# GIVEN: A mocked service with a dummy service item
mocked_service_item = MagicMock()
mocked_service_item.get_frames.return_value = []
mocked_service_item.unique_identifier = 'mock-service-item'
self.mocked_live_controller.service_item = mocked_service_item
# WHEN: I trigger the method
ret = self.client.get('/api/controller/live/text').get_json()
def test_controller_text(test_controller_env):
"""
Remote API Tests : test the controller text method can be called with a real service item
"""
# GIVEN: A mocked service with a dummy service item
mocked_live_controller = test_controller_env[0]
client = test_controller_env[1]
line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj')
mocked_live_controller.service_item = ServiceItem(None)
State().load_settings()
State().add_service("media", 0)
State().update_pre_conditions("media", True)
State().flush_preconditions()
mocked_live_controller.service_item.set_from_service(line)
mocked_live_controller.service_item._create_slides()
# WHEN: I trigger the method
ret = client.get('/api/controller/live/text').get_json()
# THEN: I get a basic set of results
assert ret['results']['item'] == 'mock-service-item'
assert len(ret['results']['slides']) == 0
# THEN: I get a basic set of results
results = ret['results']
assert isinstance(ret, dict)
assert len(results['slides']) == 2
def test_controller_text(self):
"""
Remote API Tests : test the controller text method can be called with a real service item
"""
# GIVEN: A mocked service with a dummy service item
line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj')
self.mocked_live_controller.service_item = ServiceItem(None)
State().load_settings()
State().add_service("media", 0)
State().update_pre_conditions("media", True)
State().flush_preconditions()
self.mocked_live_controller.service_item.set_from_service(line)
self.mocked_live_controller.service_item._create_slides()
# WHEN: I trigger the method
ret = self.client.get('/api/controller/live/text').get_json()
# THEN: I get a basic set of results
results = ret['results']
assert isinstance(ret, dict)
assert len(results['slides']) == 2
def test_controller_direction_next(test_controller_env):
"""
Text the live next method is triggered
"""
# GIVEN: A mocked service with a dummy service item
mocked_emit = MagicMock()
mocked_live_controller = test_controller_env[0]
client = test_controller_env[1]
mocked_live_controller.slidecontroller_live_next.emit = mocked_emit
mocked_live_controller.service_item = MagicMock()
def test_controller_direction_next(self):
"""
Text the live next method is triggered
"""
# GIVEN: A mocked service with a dummy service item
mocked_emit = MagicMock()
self.mocked_live_controller.slidecontroller_live_next.emit = mocked_emit
self.mocked_live_controller.service_item = MagicMock()
# WHEN: I trigger the method
client.get('/api/controller/live/next')
# THEN: The correct method is called
mocked_emit.assert_called_once_with()
# WHEN: I trigger the method
self.client.get('/api/controller/live/next')
# THEN: The correct method is called
mocked_emit.assert_called_once_with()
def test_controller_direction_previous(self):
"""
Text the live next method is triggered
"""
# GIVEN: A mocked service with a dummy service item
mocked_emit = MagicMock()
self.mocked_live_controller.slidecontroller_live_previous.emit = mocked_emit
self.mocked_live_controller.service_item = MagicMock()
def test_controller_direction_previous(test_controller_env):
"""
Text the live next method is triggered
"""
# GIVEN: A mocked service with a dummy service item
mocked_emit = MagicMock()
mocked_live_controller = test_controller_env[0]
client = test_controller_env[1]
mocked_live_controller.slidecontroller_live_previous.emit = mocked_emit
mocked_live_controller.service_item = MagicMock()
# WHEN: I trigger the method
self.client.get('/api/controller/live/previous')
# THEN: The correct method is called
mocked_emit.assert_called_once_with()
# WHEN: I trigger the method
client.get('/api/controller/live/previous')
# THEN: The correct method is called
mocked_emit.assert_called_once_with()

View File

@ -1,71 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
"""
Functional tests to test the Http Server Class.
"""
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.api.http.server import HttpServer
from openlp.core.common.registry import Registry
class TestHttpServer(TestCase):
"""
A test suite to test starting the http server
"""
def setUp(self):
"""
Create the UI
"""
Registry().create()
Registry().register('service_list', MagicMock())
@patch('openlp.core.api.http.server.HttpWorker')
@patch('openlp.core.api.http.server.run_thread')
def test_server_start(self, mocked_run_thread, MockHttpWorker):
"""
Test the starting of the Waitress Server with the disable flag set off
"""
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', False)
HttpServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 1, 'The qthread should have been called once'
assert MockHttpWorker.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.http.server.HttpWorker')
@patch('openlp.core.api.http.server.run_thread')
def test_server_start_not_required(self, mocked_run_thread, MockHttpWorker):
"""
Test the starting of the Waitress Server with the disable flag set off
"""
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', True)
HttpServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 0, 'The qthread should not have have been called'
assert MockHttpWorker.call_count == 0, 'The http thread should not have been called'

View File

@ -1,167 +0,0 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
"""
Functional tests to test the Http init.
"""
from unittest import TestCase
from unittest.mock import MagicMock
from openlp.core.api.http import authenticate, check_auth, requires_auth
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
class TestInit(TestCase, TestMixin):
"""
A test suite to test the functions on the init
"""
def setUp(self):
"""
Create the UI
"""
Registry().create()
Registry().register('service_list', MagicMock())
self.build_settings()
self.password = 'c3VwZXJmbHk6bGFtYXM='
Registry().register('settings', Settings())
def tearDown(self):
self.destroy_settings()
def test_auth(self):
"""
Test the check_auth method with a match
:return:
"""
# GIVEN: a known user
Settings().setValue('api/user id', "superfly")
Settings().setValue('api/password', "lamas")
# WHEN : I check the authorisation
is_valid = check_auth(['aaaaa', self.password])
# THEN:
assert is_valid is True
def test_auth_falure(self):
"""
Test the check_auth method with a match
:return:
"""
# GIVEN: a known user
Settings().setValue('api/user id', 'superfly')
Settings().setValue('api/password', 'lamas')
# WHEN : I check the authorisation
is_valid = check_auth(['aaaaa', 'monkey123'])
# THEN:
assert is_valid is False
def test_requires_auth_disabled(self):
"""
Test the requires_auth wrapper with disabled security
:return:
"""
# GIVEN: A disabled security
Settings().setValue('api/authentication enabled', False)
# WHEN: I call the function
wrapped_function = requires_auth(func)
value = wrapped_function()
# THEN: the result will be as expected
assert value == 'called'
def test_requires_auth_enabled(self):
"""
Test the requires_auth wrapper with enabled security
:return:
"""
# GIVEN: A disabled security
Settings().setValue('api/authentication enabled', True)
# WHEN: I call the function
wrapped_function = requires_auth(func)
req = MagicMock()
value = wrapped_function(req)
# THEN: the result will be as expected
assert str(value) == str(authenticate())
def test_requires_auth_enabled_auth_error(self):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: A enabled security
Settings().setValue('api/authentication enabled', True)
# WHEN: I call the function with the wrong password
wrapped_function = requires_auth(func)
req = MagicMock()
req.authorization = ['Basic', 'cccccccc']
value = wrapped_function(req)
# THEN: the result will be as expected - try again
assert str(value) == str(authenticate())
def test_requires_auth_enabled_auth(self):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: An enabled security and a known user
Settings().setValue('api/authentication enabled', True)
Settings().setValue('api/user id', 'superfly')
Settings().setValue('api/password', 'lamas')
# WHEN: I call the function with the wrong password
wrapped_function = requires_auth(func)
req = MagicMock()
req.authorization = ['Basic', self.password]
value = wrapped_function(req)
# THEN: the result will be as expected - try again
assert str(value) == 'called'
def test_requires_auth_missing_credentials(self):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: An enabled security and a known user
Settings().setValue('api/authentication enabled', True)
Settings().setValue('api/user id', 'superfly')
Settings().setValue('api/password', 'lamas')
# WHEN: I call the function with no password
wrapped_function = requires_auth(func)
value = wrapped_function(0)
# THEN: the result will be as expected (unauthorized)
assert str(value) == str(authenticate())
def func(field=None):
return 'called'

View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
"""
Functional tests to test the Http Server Class.
"""
from unittest.mock import patch
from openlp.core.api.http.server import HttpServer
from openlp.core.common.registry import Registry
@patch('openlp.core.api.http.server.HttpWorker')
@patch('openlp.core.api.http.server.run_thread')
def test_server_start(mocked_run_thread, MockHttpWorker, registry):
"""
Test the starting of the Waitress Server with the disable flag set off
"""
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', False)
HttpServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 1, 'The qthread should have been called once'
assert MockHttpWorker.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.http.server.HttpWorker')
@patch('openlp.core.api.http.server.run_thread')
def test_server_start_not_required(mocked_run_thread, MockHttpWorker, registry):
"""
Test the starting of the Waitress Server with the disable flag set off
"""
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', True)
HttpServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 0, 'The qthread should not have have been called'
assert MockHttpWorker.call_count == 0, 'The http thread should not have been called'

View File

@ -0,0 +1,154 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
"""
Functional tests to test the Http init.
"""
from unittest.mock import MagicMock
from openlp.core.api.http import authenticate, check_auth, requires_auth
password = 'c3VwZXJmbHk6bGFtYXM='
def test_auth(settings):
"""
Test the check_auth method with a match
:return:
"""
# GIVEN: a known user
settings.setValue('api/user id', "superfly")
settings.setValue('api/password', "lamas")
# WHEN : I check the authorisation
is_valid = check_auth(['aaaaa', password])
# THEN:
assert is_valid is True
def test_auth_falure(settings):
"""
Test the check_auth method with a match
:return:
"""
# GIVEN: a known user
settings.setValue('api/user id', 'superfly')
settings.setValue('api/password', 'lamas')
# WHEN : I check the authorisation
is_valid = check_auth(['aaaaa', 'monkey123'])
# THEN:
assert is_valid is False
def test_requires_auth_disabled(settings):
"""
Test the requires_auth wrapper with disabled security
:return:
"""
# GIVEN: A disabled security
settings.setValue('api/authentication enabled', False)
# WHEN: I call the function
wrapped_function = requires_auth(func)
value = wrapped_function()
# THEN: the result will be as expected
assert value == 'called'
def test_requires_auth_enabled(settings):
"""
Test the requires_auth wrapper with enabled security
:return:
"""
# GIVEN: A disabled security
settings.setValue('api/authentication enabled', True)
# WHEN: I call the function
wrapped_function = requires_auth(func)
req = MagicMock()
value = wrapped_function(req)
# THEN: the result will be as expected
assert str(value) == str(authenticate())
def test_requires_auth_enabled_auth_error(settings):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: A enabled security
settings.setValue('api/authentication enabled', True)
# WHEN: I call the function with the wrong password
wrapped_function = requires_auth(func)
req = MagicMock()
req.authorization = ['Basic', 'cccccccc']
value = wrapped_function(req)
# THEN: the result will be as expected - try again
assert str(value) == str(authenticate())
def test_requires_auth_enabled_auth(settings):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: An enabled security and a known user
settings.setValue('api/authentication enabled', True)
settings.setValue('api/user id', 'superfly')
settings.setValue('api/password', 'lamas')
# WHEN: I call the function with the wrong password
wrapped_function = requires_auth(func)
req = MagicMock()
req.authorization = ['Basic', password]
value = wrapped_function(req)
# THEN: the result will be as expected - try again
assert str(value) == 'called'
def test_requires_auth_missing_credentials(settings):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: An enabled security and a known user
settings.setValue('api/authentication enabled', True)
settings.setValue('api/user id', 'superfly')
settings.setValue('api/password', 'lamas')
# WHEN: I call the function with no password
wrapped_function = requires_auth(func)
value = wrapped_function(0)
# THEN: the result will be as expected (unauthorized)
assert str(value) == str(authenticate())
def func(field=None):
return 'called'

View File

@ -21,101 +21,75 @@
"""
This module contains tests for the lib submodule of the Remotes plugin.
"""
import pytest
import re
from unittest import TestCase
from PyQt5 import QtWidgets
from openlp.core.api.tab import ApiTab
from openlp.core.common import get_network_interfaces
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
__default_settings__ = {
'api/twelve hour': True,
'api/port': 4316,
'api/user id': 'openlp',
'api/password': 'password',
'api/authentication enabled': False,
'api/ip address': '0.0.0.0',
'api/thumbnails': True,
'remotes/download version': '0000_00_00'
}
ZERO_URL = '0.0.0.0'
class TestApiTab(TestCase, TestMixin):
@pytest.yield_fixture
def api_tab(settings):
Registry().set_flag('website_version', '00-00-0000')
parent = QtWidgets.QMainWindow()
form = ApiTab(parent)
yield form
del parent
del form
def test_get_ip_address_default(api_tab):
"""
Test the functions in the :mod:`lib` module.
Test the get_ip_address function with ZERO_URL
"""
def setUp(self):
"""
Create the UI
"""
self.setup_application()
self.build_settings()
Settings().extend_default_settings(__default_settings__)
self.parent = QtWidgets.QMainWindow()
Registry().create()
Registry().set_flag('website_version', '00-00-0000')
Registry().register('settings', Settings())
self.form = ApiTab(self.parent)
self.interfaces = get_network_interfaces()
# GIVEN: list of local IP addresses for this machine
ip_addresses = []
interfaces = get_network_interfaces()
for _, interface in interfaces.items():
ip_addresses.append(interface['ip'])
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.parent
del self.form
self.destroy_settings()
# WHEN: the default ip address is given
ip_address = api_tab.get_ip_address(ZERO_URL)
def test_get_ip_address_default(self):
"""
Test the get_ip_address function with ZERO_URL
"""
# GIVEN: list of local IP addresses for this machine
ip_addresses = []
for _, interface in self.interfaces.items():
ip_addresses.append(interface['ip'])
# THEN: the default ip address will be returned
assert re.match(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), \
'The return value should be a valid ip address'
assert ip_address in ip_addresses, 'The return address should be in the list of local IP addresses'
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(ZERO_URL)
# THEN: the default ip address will be returned
assert re.match(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), \
'The return value should be a valid ip address'
assert ip_address in ip_addresses, 'The return address should be in the list of local IP addresses'
def test_get_ip_address_with_ip(api_tab):
"""
Test the get_ip_address function with given ip address
"""
# GIVEN: An ip address
given_ip = '192.168.1.1'
def test_get_ip_address_with_ip(self):
"""
Test the get_ip_address function with given ip address
"""
# GIVEN: An ip address
given_ip = '192.168.1.1'
# WHEN: the default ip address is given
ip_address = api_tab.get_ip_address(given_ip)
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(given_ip)
# THEN: the default ip address will be returned
assert ip_address == given_ip, 'The return value should be %s' % given_ip
# THEN: the default ip address will be returned
assert ip_address == given_ip, 'The return value should be %s' % given_ip
def test_set_urls(self):
"""
Test the set_url function to generate correct url links
"""
# GIVEN: An ip address
self.form.address_edit.setText('192.168.1.1')
# WHEN: the urls are generated
self.form.set_urls()
# THEN: the following links are returned
assert self.form.remote_url.text() == "<a href=\"http://192.168.1.1:4316/\">http://192.168.1.1:4316/</a>", \
'The return value should be a fully formed link'
assert self.form.stage_url.text() == \
"<a href=\"http://192.168.1.1:4316/stage\">http://192.168.1.1:4316/stage</a>", \
'The return value should be a fully formed stage link'
assert self.form.live_url.text() == \
"<a href=\"http://192.168.1.1:4316/main\">http://192.168.1.1:4316/main</a>", \
'The return value should be a fully formed main link'
def test_set_urls(api_tab):
"""
Test the set_url function to generate correct url links
"""
# GIVEN: An ip address
api_tab.address_edit.setText('192.168.1.1')
# WHEN: the urls are generated
api_tab.set_urls()
# THEN: the following links are returned
assert api_tab.remote_url.text() == "<a href=\"http://192.168.1.1:4316/\">http://192.168.1.1:4316/</a>", \
'The return value should be a fully formed link'
assert api_tab.stage_url.text() == \
"<a href=\"http://192.168.1.1:4316/stage\">http://192.168.1.1:4316/stage</a>", \
'The return value should be a fully formed stage link'
assert api_tab.live_url.text() == \
"<a href=\"http://192.168.1.1:4316/main\">http://192.168.1.1:4316/main</a>", \
'The return value should be a fully formed main link'

View File

@ -21,122 +21,97 @@
"""
Functional tests to test the Http Server Class.
"""
from unittest import TestCase
import pytest
from unittest.mock import MagicMock, patch
from openlp.core.api.poll import Poller
from openlp.core.api.websockets import WebSocketServer
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
__default_settings__ = {
'api/twelve hour': True,
'api/port': 4316,
'api/user id': 'openlp',
'api/password': 'password',
'api/authentication enabled': False,
'api/ip address': '0.0.0.0',
'api/thumbnails': True,
'songs/chord notation': True
}
@pytest.yield_fixture
def poller(registry):
poll = Poller()
yield poll
class TestWSServer(TestCase, TestMixin):
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.run_thread')
def test_serverstart(mocked_run_thread, MockWebSocketWorker, registry):
"""
A test suite to test starting the websocket server
Test the starting of the WebSockets Server with the disabled flag set on
"""
def setUp(self):
"""
Create the UI
"""
self.build_settings()
Settings().extend_default_settings(__default_settings__)
Registry().create()
Registry().register('settings', Settings())
self.poll = Poller()
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', False)
WebSocketServer()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
self.destroy_settings()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 1, 'The qthread should have been called once'
assert MockWebSocketWorker.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.run_thread')
def test_serverstart(self, mocked_run_thread, MockWebSocketWorker):
"""
Test the starting of the WebSockets Server with the disabled flag set on
"""
# GIVEN: A new httpserver
# WHEN: I start the server
Registry().set_flag('no_web_server', False)
WebSocketServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 1, 'The qthread should have been called once'
assert MockWebSocketWorker.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.run_thread')
def test_serverstart_not_required(mocked_run_thread, MockWebSocketWorker, registry):
"""
Test the starting of the WebSockets Server with the disabled flag set off
"""
# GIVEN: A new httpserver and the server is not required
# WHEN: I start the server
Registry().set_flag('no_web_server', True)
WebSocketServer()
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.run_thread')
def test_serverstart_not_required(self, mocked_run_thread, MockWebSocketWorker):
"""
Test the starting of the WebSockets Server with the disabled flag set off
"""
# GIVEN: A new httpserver and the server is not required
# WHEN: I start the server
Registry().set_flag('no_web_server', True)
WebSocketServer()
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 0, 'The qthread should not have been called'
assert MockWebSocketWorker.call_count == 0, 'The http thread should not have been called'
# THEN: the api environment should have been created
assert mocked_run_thread.call_count == 0, 'The qthread should not have been called'
assert MockWebSocketWorker.call_count == 0, 'The http thread should not have been called'
def test_main_poll(self):
"""
Test the main_poll function returns the correct JSON
"""
# WHEN: the live controller has 5 slides
mocked_live_controller = MagicMock()
mocked_live_controller.slide_count = 5
Registry().register('live_controller', mocked_live_controller)
# THEN: the live json should be generated
main_json = self.poll.main_poll()
assert b'{"results": {"slide_count": 5}}' == main_json, 'The return value should match the defined json'
def test_main_poll(poller):
"""
Test the main_poll function returns the correct JSON
"""
# WHEN: the live controller has 5 slides
mocked_live_controller = MagicMock()
mocked_live_controller.slide_count = 5
Registry().register('live_controller', mocked_live_controller)
# THEN: the live json should be generated
main_json = poller.main_poll()
assert b'{"results": {"slide_count": 5}}' == main_json, 'The return value should match the defined json'
def test_poll(self):
"""
Test the poll function returns the correct JSON
"""
# GIVEN: the system is configured with a set of data
mocked_service_manager = MagicMock()
mocked_service_manager.service_id = 21
mocked_live_controller = MagicMock()
mocked_live_controller.selected_row = 5
mocked_live_controller.service_item = MagicMock()
mocked_live_controller.service_item.unique_identifier = '23-34-45'
mocked_live_controller.blank_screen.isChecked.return_value = True
mocked_live_controller.theme_screen.isChecked.return_value = False
mocked_live_controller.desktop_screen.isChecked.return_value = False
Registry().register('live_controller', mocked_live_controller)
Registry().register('service_manager', mocked_service_manager)
# WHEN: The poller polls
with patch.object(self.poll, 'is_stage_active') as mocked_is_stage_active, \
patch.object(self.poll, 'is_live_active') as mocked_is_live_active, \
patch.object(self.poll, 'is_chords_active') as mocked_is_chords_active:
mocked_is_stage_active.return_value = True
mocked_is_live_active.return_value = True
mocked_is_chords_active.return_value = True
poll_json = self.poll.poll()
# THEN: the live json should be generated and match expected results
assert poll_json['results']['blank'] is True, 'The blank return value should be True'
assert poll_json['results']['theme'] is False, 'The theme return value should be False'
assert poll_json['results']['display'] is False, 'The display return value should be False'
assert poll_json['results']['isSecure'] is False, 'The isSecure return value should be False'
assert poll_json['results']['isAuthorised'] is False, 'The isAuthorised return value should be False'
assert poll_json['results']['twelve'] is True, 'The twelve return value should be True'
assert poll_json['results']['version'] == 3, 'The version return value should be 3'
assert poll_json['results']['slide'] == 5, 'The slide return value should be 5'
assert poll_json['results']['service'] == 21, 'The version return value should be 21'
assert poll_json['results']['item'] == '23-34-45', 'The item return value should match 23-34-45'
def test_poll(poller, settings):
"""
Test the poll function returns the correct JSON
"""
# GIVEN: the system is configured with a set of data
mocked_service_manager = MagicMock()
mocked_service_manager.service_id = 21
mocked_live_controller = MagicMock()
mocked_live_controller.selected_row = 5
mocked_live_controller.service_item = MagicMock()
mocked_live_controller.service_item.unique_identifier = '23-34-45'
mocked_live_controller.blank_screen.isChecked.return_value = True
mocked_live_controller.theme_screen.isChecked.return_value = False
mocked_live_controller.desktop_screen.isChecked.return_value = False
Registry().register('live_controller', mocked_live_controller)
Registry().register('service_manager', mocked_service_manager)
# WHEN: The poller polls
with patch.object(poller, 'is_stage_active') as mocked_is_stage_active, \
patch.object(poller, 'is_live_active') as mocked_is_live_active, \
patch.object(poller, 'is_chords_active') as mocked_is_chords_active:
mocked_is_stage_active.return_value = True
mocked_is_live_active.return_value = True
mocked_is_chords_active.return_value = True
poll_json = poller.poll()
# THEN: the live json should be generated and match expected results
assert poll_json['results']['blank'] is True, 'The blank return value should be True'
assert poll_json['results']['theme'] is False, 'The theme return value should be False'
assert poll_json['results']['display'] is False, 'The display return value should be False'
assert poll_json['results']['isSecure'] is False, 'The isSecure return value should be False'
assert poll_json['results']['isAuthorised'] is False, 'The isAuthorised return value should be False'
assert poll_json['results']['twelve'] is True, 'The twelve return value should be True'
assert poll_json['results']['version'] == 3, 'The version return value should be 3'
assert poll_json['results']['slide'] == 5, 'The slide return value should be 5'
assert poll_json['results']['service'] == 21, 'The version return value should be 21'
assert poll_json['results']['item'] == '23-34-45', 'The item return value should match 23-34-45'

View File

@ -1,8 +1,27 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
import pytest
from unittest.mock import MagicMock
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
def test_retrieve_live_item(flask_client):
@ -11,11 +30,10 @@ def test_retrieve_live_item(flask_client):
assert len(res) == 0
def test_controller_set_requires_login(flask_client):
pytest.skip('Need to figure out how to patch settings for one test only')
# Settings().setValue('api/authentication enabled', True)
def test_controller_set_requires_login(settings, flask_client):
settings.setValue('api/authentication enabled', True)
res = flask_client.post('/api/v2/controller/show', json=dict())
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
assert res.status_code == 401
@ -33,9 +51,9 @@ def test_controller_set_calls_live_controller(flask_client, settings):
def test_controller_direction_requires_login(flask_client, settings):
Settings().setValue('api/authentication enabled', True)
settings.setValue('api/authentication enabled', True)
res = flask_client.post('/api/v2/controller/progress', json=dict())
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
assert res.status_code == 401

View File

@ -1,5 +1,24 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.state import State
from openlp.core.lib.plugin import PluginStatus, StringContent
@ -28,7 +47,7 @@ def test_plugins_returns_list(flask_client):
def test_system_information(flask_client, settings):
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
res = flask_client.get('/api/v2/core/system').get_json()
assert res['websocket_port'] > 0
assert not res['login_required']
@ -78,9 +97,9 @@ def test_retrieving_image(flask_client):
def test_toggle_display_requires_login(flask_client, settings):
Settings().setValue('api/authentication enabled', True)
settings.setValue('api/authentication enabled', True)
res = flask_client.post('/api/v2/core/display')
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
assert res.status_code == 401

View File

@ -1,20 +1,42 @@
import pytest
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2020 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/>. #
##########################################################################
from unittest.mock import MagicMock
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
def test_retrieve_service_items(flask_client, settings):
pytest.skip()
mocked_live_controller = MagicMock()
mocked_live_controller.service_item = MagicMock()
fake_service_manager = MagicMock()
Registry().register('service_manager', fake_service_manager)
Registry().register('live_controller', mocked_live_controller)
res = flask_client.get('/api/v2/service/items').get_json()
assert len(res) == 0
def test_service_set_requires_login(flask_client, settings):
Settings().setValue('api/authentication enabled', True)
settings.setValue('api/authentication enabled', True)
res = flask_client.post('/api/v2/service/show', json=dict())
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
assert res.status_code == 401
@ -32,9 +54,9 @@ def test_service_set_calls_service_manager(flask_client, settings):
def test_service_direction_requires_login(flask_client, settings):
Settings().setValue('api/authentication enabled', True)
settings.setValue('api/authentication enabled', True)
res = flask_client.post('/api/v2/service/progress', json=dict())
Settings().setValue('api/authentication enabled', False)
settings.setValue('api/authentication enabled', False)
assert res.status_code == 401

View File

@ -21,20 +21,25 @@
"""
Package to test the openlp.core.common.actions package.
"""
from unittest import TestCase
import pytest
from unittest.mock import MagicMock
from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common.actions import ActionList, CategoryActionList
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
from openlp.core.common.registry import Registry
MOCK_ACTION1 = MagicMock(**{'text.return_value': 'first'})
MOCK_ACTION2 = MagicMock(**{'text.return_value': 'second'})
@pytest.yield_fixture
def action_list(settings):
act_list = ActionList.get_instance()
yield act_list
def test_action_list_contains():
"""
Test the __contains__() method
@ -151,118 +156,98 @@ def test_action_list_remove():
assert MOCK_ACTION1 not in category_list
class TestActionList(TestCase, TestMixin):
def test_add_action_same_parent(action_list):
"""
Test the ActionList class
ActionList test - Tests the add_action method. The actions have the same parent, the same shortcuts and both
have the QtCore.Qt.WindowShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action1 = QtWidgets.QAction(parent)
action1.setObjectName('action1')
action_with_same_shortcuts1 = QtWidgets.QAction(parent)
action_with_same_shortcuts1.setObjectName('action_with_same_shortcuts1')
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action1': [QtGui.QKeySequence(QtCore.Qt.Key_A), QtGui.QKeySequence(QtCore.Qt.Key_B)],
'shortcuts/action_with_same_shortcuts1': [QtGui.QKeySequence(QtCore.Qt.Key_B),
QtGui.QKeySequence(QtCore.Qt.Key_A)]
}
Registry().get('settings').extend_default_settings(default_shortcuts)
def setUp(self):
"""
Prepare the tests
"""
self.setup_application()
self.action_list = ActionList.get_instance()
self.build_settings()
self.settings = Settings()
self.settings.beginGroup('shortcuts')
# WHEN: Add the two actions to the action list.
action_list.add_action(action1, 'example_category')
action_list.add_action(action_with_same_shortcuts1, 'example_category')
# Remove the actions again.
action_list.remove_action(action1, 'example_category')
action_list.remove_action(action_with_same_shortcuts1, 'example_category')
def tearDown(self):
"""
Clean up
"""
self.settings.endGroup()
self.destroy_settings()
# THEN: As both actions have the same shortcuts, they should be removed from one action.
assert len(action1.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts1.shortcuts()) == 0, 'The action should not have a shortcut assigned.'
def test_add_action_same_parent(self):
"""
ActionList test - Tests the add_action method. The actions have the same parent, the same shortcuts and both
have the QtCore.Qt.WindowShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action1 = QtWidgets.QAction(parent)
action1.setObjectName('action1')
action_with_same_shortcuts1 = QtWidgets.QAction(parent)
action_with_same_shortcuts1.setObjectName('action_with_same_shortcuts1')
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action1': [QtGui.QKeySequence(QtCore.Qt.Key_A), QtGui.QKeySequence(QtCore.Qt.Key_B)],
'shortcuts/action_with_same_shortcuts1': [QtGui.QKeySequence(QtCore.Qt.Key_B),
QtGui.QKeySequence(QtCore.Qt.Key_A)]
}
Settings.extend_default_settings(default_shortcuts)
# WHEN: Add the two actions to the action list.
self.action_list.add_action(action1, 'example_category')
self.action_list.add_action(action_with_same_shortcuts1, 'example_category')
# Remove the actions again.
self.action_list.remove_action(action1, 'example_category')
self.action_list.remove_action(action_with_same_shortcuts1, 'example_category')
def test_add_action_different_parent(action_list):
"""
ActionList test - Tests the add_action method. The actions have the different parent, the same shortcuts and
both have the QtCore.Qt.WindowShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action2 = QtWidgets.QAction(parent)
action2.setObjectName('action2')
second_parent = QtCore.QObject()
action_with_same_shortcuts2 = QtWidgets.QAction(second_parent)
action_with_same_shortcuts2.setObjectName('action_with_same_shortcuts2')
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action2': [QtGui.QKeySequence(QtCore.Qt.Key_C), QtGui.QKeySequence(QtCore.Qt.Key_D)],
'shortcuts/action_with_same_shortcuts2': [QtGui.QKeySequence(QtCore.Qt.Key_D),
QtGui.QKeySequence(QtCore.Qt.Key_C)]
}
Registry().get('settings').extend_default_settings(default_shortcuts)
# THEN: As both actions have the same shortcuts, they should be removed from one action.
assert len(action1.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts1.shortcuts()) == 0, 'The action should not have a shortcut assigned.'
# WHEN: Add the two actions to the action list.
action_list.add_action(action2, 'example_category')
action_list.add_action(action_with_same_shortcuts2, 'example_category')
# Remove the actions again.
action_list.remove_action(action2, 'example_category')
action_list.remove_action(action_with_same_shortcuts2, 'example_category')
def test_add_action_different_parent(self):
"""
ActionList test - Tests the add_action method. The actions have the different parent, the same shortcuts and
both have the QtCore.Qt.WindowShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action2 = QtWidgets.QAction(parent)
action2.setObjectName('action2')
second_parent = QtCore.QObject()
action_with_same_shortcuts2 = QtWidgets.QAction(second_parent)
action_with_same_shortcuts2.setObjectName('action_with_same_shortcuts2')
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action2': [QtGui.QKeySequence(QtCore.Qt.Key_C), QtGui.QKeySequence(QtCore.Qt.Key_D)],
'shortcuts/action_with_same_shortcuts2': [QtGui.QKeySequence(QtCore.Qt.Key_D),
QtGui.QKeySequence(QtCore.Qt.Key_C)]
}
Settings.extend_default_settings(default_shortcuts)
# THEN: As both actions have the same shortcuts, they should be removed from one action.
assert len(action2.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts2.shortcuts()) == 0, 'The action should not have a shortcut assigned.'
# WHEN: Add the two actions to the action list.
self.action_list.add_action(action2, 'example_category')
self.action_list.add_action(action_with_same_shortcuts2, 'example_category')
# Remove the actions again.
self.action_list.remove_action(action2, 'example_category')
self.action_list.remove_action(action_with_same_shortcuts2, 'example_category')
# THEN: As both actions have the same shortcuts, they should be removed from one action.
assert len(action2.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts2.shortcuts()) == 0, 'The action should not have a shortcut assigned.'
def test_add_action_different_context(action_list):
"""
ActionList test - Tests the add_action method. The actions have the different parent, the same shortcuts and
both have the QtCore.Qt.WidgetShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action3 = QtWidgets.QAction(parent)
action3.setObjectName('action3')
action3.setShortcutContext(QtCore.Qt.WidgetShortcut)
second_parent = QtCore.QObject()
action_with_same_shortcuts3 = QtWidgets.QAction(second_parent)
action_with_same_shortcuts3.setObjectName('action_with_same_shortcuts3')
action_with_same_shortcuts3.setShortcutContext(QtCore.Qt.WidgetShortcut)
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action3': [QtGui.QKeySequence(QtCore.Qt.Key_E), QtGui.QKeySequence(QtCore.Qt.Key_F)],
'shortcuts/action_with_same_shortcuts3': [QtGui.QKeySequence(QtCore.Qt.Key_E),
QtGui.QKeySequence(QtCore.Qt.Key_F)]
}
Registry().get('settings').extend_default_settings(default_shortcuts)
def test_add_action_different_context(self):
"""
ActionList test - Tests the add_action method. The actions have the different parent, the same shortcuts and
both have the QtCore.Qt.WidgetShortcut shortcut context set.
"""
# GIVEN: Two actions with the same shortcuts.
parent = QtCore.QObject()
action3 = QtWidgets.QAction(parent)
action3.setObjectName('action3')
action3.setShortcutContext(QtCore.Qt.WidgetShortcut)
second_parent = QtCore.QObject()
action_with_same_shortcuts3 = QtWidgets.QAction(second_parent)
action_with_same_shortcuts3.setObjectName('action_with_same_shortcuts3')
action_with_same_shortcuts3.setShortcutContext(QtCore.Qt.WidgetShortcut)
# Add default shortcuts to Settings class.
default_shortcuts = {
'shortcuts/action3': [QtGui.QKeySequence(QtCore.Qt.Key_E), QtGui.QKeySequence(QtCore.Qt.Key_F)],
'shortcuts/action_with_same_shortcuts3': [QtGui.QKeySequence(QtCore.Qt.Key_E),
QtGui.QKeySequence(QtCore.Qt.Key_F)]
}
Settings.extend_default_settings(default_shortcuts)
# WHEN: Add the two actions to the action list.
action_list.add_action(action3, 'example_category2')
action_list.add_action(action_with_same_shortcuts3, 'example_category2')
# Remove the actions again.
action_list.remove_action(action3, 'example_category2')
action_list.remove_action(action_with_same_shortcuts3, 'example_category2')
# WHEN: Add the two actions to the action list.
self.action_list.add_action(action3, 'example_category2')
self.action_list.add_action(action_with_same_shortcuts3, 'example_category2')
# Remove the actions again.
self.action_list.remove_action(action3, 'example_category2')
self.action_list.remove_action(action_with_same_shortcuts3, 'example_category2')
# THEN: Both action should keep their shortcuts.
assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.'
# THEN: Both action should keep their shortcuts.
assert len(action3.shortcuts()) == 2, 'The action should have two shortcut assigned.'
assert len(action_with_same_shortcuts3.shortcuts()) == 2, 'The action should have two shortcuts assigned.'

View File

@ -22,304 +22,318 @@
Functional tests to test the AppLocation class and related methods.
"""
from pathlib import Path
from unittest import TestCase, skipUnless
from unittest import skipUnless
from unittest.mock import MagicMock, call, patch
from openlp.core.common import Singleton, clean_button_text, de_hump, extension_loader, is_linux, is_macosx, is_win, \
is_64bit_instance, normalize_str, path_to_module, trace_error_handler
class TestCommonFunctions(TestCase):
def test_extension_loader_no_files_found():
"""
A test suite to test out various functions in the openlp.core.common module.
Test the `extension_loader` function when no files are found
"""
def test_extension_loader_no_files_found(self):
"""
Test the `extension_loader` function when no files are found
"""
# GIVEN: A mocked `Path.glob` method which does not match any files
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[]), \
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
# GIVEN: A mocked `Path.glob` method which does not match any files
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[]), \
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
# WHEN: Calling `extension_loader`
extension_loader('glob', ['file2.py', 'file3.py'])
# WHEN: Calling `extension_loader`
extension_loader('glob', ['file2.py', 'file3.py'])
# THEN: `extension_loader` should not try to import any files
assert mocked_import_module.called is False
# THEN: `extension_loader` should not try to import any files
assert mocked_import_module.called is False
def test_extension_loader_files_found(self):
"""
Test the `extension_loader` function when it successfully finds and loads some files
"""
# GIVEN: A mocked `Path.glob` method which returns a list of files
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
# WHEN: Calling `extension_loader` with a list of files to exclude
extension_loader('glob', ['file2.py', 'file3.py'])
def test_extension_loader_files_found():
"""
Test the `extension_loader` function when it successfully finds and loads some files
"""
# GIVEN: A mocked `Path.glob` method which returns a list of files
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'),
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
# THEN: `extension_loader` should only try to import the files that are matched by the blob, excluding the
# files listed in the `excluded_files` argument
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'),
call('openlp.import_dir.file4')])
# WHEN: Calling `extension_loader` with a list of files to exclude
extension_loader('glob', ['file2.py', 'file3.py'])
def test_extension_loader_import_error(self):
"""
Test the `extension_loader` function when `SourceFileLoader` raises a `ImportError`
"""
# GIVEN: A mocked `import_module` which raises an `ImportError`
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \
patch('openlp.core.common.log') as mocked_logger:
# THEN: `extension_loader` should only try to import the files that are matched by the blob, excluding the
# files listed in the `excluded_files` argument
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'),
call('openlp.import_dir.file4')])
# WHEN: Calling `extension_loader`
extension_loader('glob')
# THEN: The `ImportError` should be caught and logged
assert mocked_logger.exception.called
def test_extension_loader_import_error():
"""
Test the `extension_loader` function when `SourceFileLoader` raises a `ImportError`
"""
# GIVEN: A mocked `import_module` which raises an `ImportError`
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \
patch('openlp.core.common.log') as mocked_logger:
def test_extension_loader_os_error(self):
"""
Test the `extension_loader` function when `import_module` raises a `ImportError`
"""
# GIVEN: A mocked `SourceFileLoader` which raises an `OSError`
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \
patch('openlp.core.common.log') as mocked_logger:
# WHEN: Calling `extension_loader`
extension_loader('glob')
# WHEN: Calling `extension_loader`
extension_loader('glob')
# THEN: The `ImportError` should be caught and logged
assert mocked_logger.exception.called
# THEN: The `OSError` should be caught and logged
assert mocked_logger.exception.called
def test_de_hump_conversion(self):
"""
Test the de_hump function with a class name
"""
# GIVEN: a Class name in Camel Case
string = "MyClass"
def test_extension_loader_os_error():
"""
Test the `extension_loader` function when `import_module` raises a `ImportError`
"""
# GIVEN: A mocked `SourceFileLoader` which raises an `OSError`
with patch('openlp.core.common.applocation.AppLocation.get_directory',
return_value=Path('/', 'app', 'dir', 'openlp')), \
patch.object(Path, 'glob', return_value=[
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \
patch('openlp.core.common.log') as mocked_logger:
# WHEN: we call de_hump
new_string = de_hump(string)
# WHEN: Calling `extension_loader`
extension_loader('glob')
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been converted'
# THEN: The `OSError` should be caught and logged
assert mocked_logger.exception.called
def test_de_hump_static(self):
"""
Test the de_hump function with a python string
"""
# GIVEN: a Class name in Camel Case
string = "my_class"
# WHEN: we call de_hump
new_string = de_hump(string)
def test_de_hump_conversion():
"""
Test the de_hump function with a class name
"""
# GIVEN: a Class name in Camel Case
string = "MyClass"
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been preserved'
# WHEN: we call de_hump
new_string = de_hump(string)
def test_path_to_module(self):
"""
Test `path_to_module` when supplied with a `Path` object
"""
# GIVEN: A `Path` object
path = Path('core', 'ui', 'media', 'vlcplayer.py')
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been converted'
# WHEN: Calling path_to_module with the `Path` object
result = path_to_module(path)
# THEN: path_to_module should return the module name
assert result == 'openlp.core.ui.media.vlcplayer'
def test_de_hump_static():
"""
Test the de_hump function with a python string
"""
# GIVEN: a Class name in Camel Case
string = "my_class"
def test_trace_error_handler(self):
"""
Test the trace_error_handler() method
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.traceback') as mocked_traceback:
mocked_traceback.extract_stack.return_value = [('openlp.fake', 56, None, 'trace_error_handler_test')]
mocked_logger = MagicMock()
# WHEN: we call de_hump
new_string = de_hump(string)
# WHEN: trace_error_handler() is called
trace_error_handler(mocked_logger)
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been preserved'
# THEN: The mocked_logger.error() method should have been called with the correct parameters
mocked_logger.error.assert_called_with(
'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
def test_singleton_metaclass_multiple_init(self):
"""
Test that a class using the Singleton Metaclass is only initialised once despite being called several times and
that the same instance is returned each time..
"""
# GIVEN: The Singleton Metaclass and a test class using it
class SingletonClass(metaclass=Singleton):
def __init__(self):
pass
def test_path_to_module():
"""
Test `path_to_module` when supplied with a `Path` object
"""
# GIVEN: A `Path` object
path = Path('core', 'ui', 'media', 'vlcplayer.py')
with patch.object(SingletonClass, '__init__', return_value=None) as patched_init:
# WHEN: Calling path_to_module with the `Path` object
result = path_to_module(path)
# WHEN: Initialising the class multiple times
inst_1 = SingletonClass()
inst_2 = SingletonClass()
# THEN: path_to_module should return the module name
assert result == 'openlp.core.ui.media.vlcplayer'
# THEN: The __init__ method of the SingletonClass should have only been called once, and both returned values
# should be the same instance.
assert inst_1 is inst_2
assert patched_init.call_count == 1
def test_singleton_metaclass_multiple_classes(self):
"""
Test that multiple classes using the Singleton Metaclass return the different an appropriate instances.
"""
# GIVEN: Two different classes using the Singleton Metaclass
class SingletonClass1(metaclass=Singleton):
def __init__(self):
pass
def test_trace_error_handler():
"""
Test the trace_error_handler() method
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.traceback') as mocked_traceback:
mocked_traceback.extract_stack.return_value = [('openlp.fake', 56, None, 'trace_error_handler_test')]
mocked_logger = MagicMock()
class SingletonClass2(metaclass=Singleton):
def __init__(self):
pass
# WHEN: trace_error_handler() is called
trace_error_handler(mocked_logger)
# WHEN: Initialising both classes
s_c1 = SingletonClass1()
s_c2 = SingletonClass2()
# THEN: The mocked_logger.error() method should have been called with the correct parameters
mocked_logger.error.assert_called_with(
'OpenLP Error trace\n File openlp.fake at line 56 \n\t called trace_error_handler_test')
# THEN: The instances should be an instance of the appropriate class
assert isinstance(s_c1, SingletonClass1)
assert isinstance(s_c2, SingletonClass2)
def test_is_win(self):
"""
Test the is_win() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
def test_singleton_metaclass_multiple_init():
"""
Test that a class using the Singleton Metaclass is only initialised once despite being called several times and
that the same instance is returned each time..
"""
# GIVEN: The Singleton Metaclass and a test class using it
class SingletonClass(metaclass=Singleton):
def __init__(self):
pass
# WHEN: The mocked os.name and sys.platform are set to 'nt' and 'win32' repectivly
mocked_os.name = 'nt'
mocked_sys.platform = 'win32'
with patch.object(SingletonClass, '__init__', return_value=None) as patched_init:
# THEN: The three platform functions should perform properly
assert is_win() is True, 'is_win() should return True'
assert is_macosx() is False, 'is_macosx() should return False'
assert is_linux() is False, 'is_linux() should return False'
# WHEN: Initialising the class multiple times
inst_1 = SingletonClass()
inst_2 = SingletonClass()
def test_is_macosx(self):
"""
Test the is_macosx() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
# THEN: The __init__ method of the SingletonClass should have only been called once, and both returned values
# should be the same instance.
assert inst_1 is inst_2
assert patched_init.call_count == 1
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'darwin' repectivly
mocked_os.name = 'posix'
mocked_sys.platform = 'darwin'
# THEN: The three platform functions should perform properly
assert is_macosx() is True, 'is_macosx() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_singleton_metaclass_multiple_classes():
"""
Test that multiple classes using the Singleton Metaclass return the different an appropriate instances.
"""
# GIVEN: Two different classes using the Singleton Metaclass
class SingletonClass1(metaclass=Singleton):
def __init__(self):
pass
def test_is_linux(self):
"""
Test the is_linux() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
class SingletonClass2(metaclass=Singleton):
def __init__(self):
pass
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectively
mocked_os.name = 'posix'
mocked_sys.platform = 'linux3'
# WHEN: Initialising both classes
s_c1 = SingletonClass1()
s_c2 = SingletonClass2()
# THEN: The three platform functions should perform properly
assert is_linux() is True, 'is_linux() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_macosx() is False, 'is_macosx() should return False'
# THEN: The instances should be an instance of the appropriate class
assert isinstance(s_c1, SingletonClass1)
assert isinstance(s_c2, SingletonClass2)
@skipUnless(is_linux(), 'This can only run on Linux')
def test_is_linux_distro(self):
"""
Test the is_linux() function for a particular Linux distribution
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, \
patch('openlp.core.common.sys') as mocked_sys, \
patch('openlp.core.common.distro_id') as mocked_distro_id:
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectively
# and the distro is Fedora
mocked_os.name = 'posix'
mocked_sys.platform = 'linux3'
mocked_distro_id.return_value = 'fedora'
def test_is_win():
"""
Test the is_win() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
# THEN: The three platform functions should perform properly
assert is_linux(distro='fedora') is True, 'is_linux(distro="fedora") should return True'
assert is_win() is False, 'is_win() should return False'
assert is_macosx() is False, 'is_macosx() should return False'
# WHEN: The mocked os.name and sys.platform are set to 'nt' and 'win32' repectivly
mocked_os.name = 'nt'
mocked_sys.platform = 'win32'
def test_is_64bit_instance(self):
"""
Test the is_64bit_instance() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.sys') as mocked_sys:
# THEN: The three platform functions should perform properly
assert is_win() is True, 'is_win() should return True'
assert is_macosx() is False, 'is_macosx() should return False'
assert is_linux() is False, 'is_linux() should return False'
# WHEN: The mocked sys.maxsize is set to 32-bit
mocked_sys.maxsize = 2**32
# THEN: The result should be False
assert is_64bit_instance() is False, 'is_64bit_instance() should return False'
def test_is_macosx():
"""
Test the is_macosx() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
def test_normalize_str_leaves_newlines(self):
# GIVEN: a string containing newlines
string = 'something\nelse'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: string is unchanged
assert normalized_string == string
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'darwin' repectivly
mocked_os.name = 'posix'
mocked_sys.platform = 'darwin'
def test_normalize_str_removes_null_byte(self):
# GIVEN: a string containing a null byte
string = 'somet\x00hing'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: nullbyte is removed
assert normalized_string == 'something'
# THEN: The three platform functions should perform properly
assert is_macosx() is True, 'is_macosx() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_normalize_str_replaces_crlf_with_lf(self):
# GIVEN: a string containing crlf
string = 'something\r\nelse'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: crlf is replaced with lf
assert normalized_string == 'something\nelse'
def test_clean_button_text(self):
"""
Test the clean_button_text() function.
"""
# GIVEN: Button text
input_text = '&Next >'
expected_text = 'Next'
def test_is_linux():
"""
Test the is_linux() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, patch('openlp.core.common.sys') as mocked_sys:
# WHEN: The button caption is sent through the clean_button_text function
actual_text = clean_button_text(input_text)
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectively
mocked_os.name = 'posix'
mocked_sys.platform = 'linux3'
# THEN: The text should have been cleaned
assert expected_text == actual_text, 'The text should be clean'
# THEN: The three platform functions should perform properly
assert is_linux() is True, 'is_linux() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_macosx() is False, 'is_macosx() should return False'
@skipUnless(is_linux(), 'This can only run on Linux')
def test_is_linux_distro():
"""
Test the is_linux() function for a particular Linux distribution
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.os') as mocked_os, \
patch('openlp.core.common.sys') as mocked_sys, \
patch('openlp.core.common.distro_id') as mocked_distro_id:
# WHEN: The mocked os.name and sys.platform are set to 'posix' and 'linux3' repectively
# and the distro is Fedora
mocked_os.name = 'posix'
mocked_sys.platform = 'linux3'
mocked_distro_id.return_value = 'fedora'
# THEN: The three platform functions should perform properly
assert is_linux(distro='fedora') is True, 'is_linux(distro="fedora") should return True'
assert is_win() is False, 'is_win() should return False'
assert is_macosx() is False, 'is_macosx() should return False'
def test_is_64bit_instance():
"""
Test the is_64bit_instance() function
"""
# GIVEN: Mocked out objects
with patch('openlp.core.common.sys') as mocked_sys:
# WHEN: The mocked sys.maxsize is set to 32-bit
mocked_sys.maxsize = 2**32
# THEN: The result should be False
assert is_64bit_instance() is False, 'is_64bit_instance() should return False'
def test_normalize_str_leaves_newlines():
# GIVEN: a string containing newlines
string = 'something\nelse'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: string is unchanged
assert normalized_string == string
def test_normalize_str_removes_null_byte():
# GIVEN: a string containing a null byte
string = 'somet\x00hing'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: nullbyte is removed
assert normalized_string == 'something'
def test_normalize_str_replaces_crlf_with_lf():
# GIVEN: a string containing crlf
string = 'something\r\nelse'
# WHEN: normalize is called
normalized_string = normalize_str(string)
# THEN: crlf is replaced with lf
assert normalized_string == 'something\nelse'
def test_clean_button_text():
"""
Test the clean_button_text() function.
"""
# GIVEN: Button text
input_text = '&Next >'
expected_text = 'Next'
# WHEN: The button caption is sent through the clean_button_text function
actual_text = clean_button_text(input_text)
# THEN: The text should have been cleaned
assert expected_text == actual_text, 'The text should be clean'

View File

@ -23,10 +23,10 @@ Package to test the openlp.core.common.db package.
"""
import gc
import os
import pytest
import shutil
import time
from tempfile import mkdtemp
from unittest import TestCase
import sqlalchemy
@ -35,69 +35,63 @@ from openlp.core.lib.db import get_upgrade_op, init_db
from tests.utils.constants import TEST_RESOURCES_PATH
class TestUtilsDBFunctions(TestCase):
@pytest.yield_fixture
def op():
tmp_folder = mkdtemp()
db_path = os.path.join(TEST_RESOURCES_PATH, 'songs', 'songs-1.9.7.sqlite')
db_tmp_path = os.path.join(tmp_folder, 'songs-1.9.7.sqlite')
shutil.copyfile(db_path, db_tmp_path)
db_url = 'sqlite:///' + db_tmp_path
session, metadata = init_db(db_url)
upgrade_op = get_upgrade_op(session)
yield upgrade_op
session.close()
session = None
gc.collect()
retries = 0
while retries < 5:
try:
if os.path.exists(tmp_folder):
shutil.rmtree(tmp_folder)
break
except Exception:
time.sleep(1)
retries += 1
def setUp(self):
"""
Create temp folder for keeping db file
"""
self.tmp_folder = mkdtemp()
db_path = os.path.join(TEST_RESOURCES_PATH, 'songs', 'songs-1.9.7.sqlite')
self.db_tmp_path = os.path.join(self.tmp_folder, 'songs-1.9.7.sqlite')
shutil.copyfile(db_path, self.db_tmp_path)
db_url = 'sqlite:///' + self.db_tmp_path
self.session, metadata = init_db(db_url)
self.op = get_upgrade_op(self.session)
def tearDown(self):
"""
Clean up
"""
self.session.close()
self.session = None
gc.collect()
retries = 0
while retries < 5:
try:
if os.path.exists(self.tmp_folder):
shutil.rmtree(self.tmp_folder)
break
except Exception:
time.sleep(1)
retries += 1
def test_delete_column(op):
"""
Test deleting a single column in a table
"""
# GIVEN: A temporary song db
def test_delete_column(self):
"""
Test deleting a single column in a table
"""
# GIVEN: A temporary song db
# WHEN: Deleting a columns in a table
drop_column(op, 'songs', 'song_book_id')
# WHEN: Deleting a columns in a table
drop_column(self.op, 'songs', 'song_book_id')
# THEN: The column should have been deleted
meta = sqlalchemy.MetaData(bind=op.get_bind())
meta.reflect()
columns = meta.tables['songs'].columns
# THEN: The column should have been deleted
meta = sqlalchemy.MetaData(bind=self.op.get_bind())
meta.reflect()
columns = meta.tables['songs'].columns
for column in columns:
if column.name == 'song_book_id':
assert "The column 'song_book_id' should have been deleted."
for column in columns:
if column.name == 'song_book_id':
self.fail("The column 'song_book_id' should have been deleted.")
def test_delete_columns(self):
"""
Test deleting multiple columns in a table
"""
# GIVEN: A temporary song db
def test_delete_columns(op):
"""
Test deleting multiple columns in a table
"""
# GIVEN: A temporary song db
# WHEN: Deleting a columns in a table
drop_columns(self.op, 'songs', ['song_book_id', 'song_number'])
# WHEN: Deleting a columns in a table
drop_columns(op, 'songs', ['song_book_id', 'song_number'])
# THEN: The columns should have been deleted
meta = sqlalchemy.MetaData(bind=self.op.get_bind())
meta.reflect()
columns = meta.tables['songs'].columns
# THEN: The columns should have been deleted
meta = sqlalchemy.MetaData(bind=op.get_bind())
meta.reflect()
columns = meta.tables['songs'].columns
for column in columns:
if column.name == 'song_book_id' or column.name == 'song_number':
self.fail("The column '%s' should have been deleted." % column.name)
for column in columns:
if column.name == 'song_book_id' or column.name == 'song_number':
assert "The column '%s' should have been deleted." % column.name

View File

@ -22,314 +22,314 @@
Functional tests to test the AppLocation class and related methods.
"""
import os
import pytest
import tempfile
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.common.httputils import ProxyMode, download_file, get_proxy_settings, get_url_file_size, \
get_user_agent, get_web_page
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from tests.helpers.testmixin import TestMixin
class TestHttpUtils(TestCase, TestMixin):
@pytest.yield_fixture
def temp_file(settings):
tmp_file = os.path.join(tempfile.gettempdir(), 'testfile')
yield tmp_file
if os.path.isfile(tmp_file):
os.remove(tmp_file)
def test_get_user_agent_linux():
"""
A test suite to test out various http helper functions.
Test that getting a user agent on Linux returns a user agent suitable for Linux
"""
def setUp(self):
self.tempfile = os.path.join(tempfile.gettempdir(), 'testfile')
Registry.create()
Registry().register('settings', Settings())
with patch('openlp.core.common.httputils.sys') as mocked_sys:
def tearDown(self):
if os.path.isfile(self.tempfile):
os.remove(self.tempfile)
# GIVEN: The system is Linux
mocked_sys.platform = 'linux2'
def test_get_user_agent_linux(self):
"""
Test that getting a user agent on Linux returns a user agent suitable for Linux
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# GIVEN: The system is Linux
mocked_sys.platform = 'linux2'
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
result = 'Linux' in user_agent or 'CrOS' in user_agent
assert result is True, 'The user agent should be a valid Linux user agent'
def test_get_user_agent_windows(self):
"""
Test that getting a user agent on Windows returns a user agent suitable for Windows
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# GIVEN: The system is Windows
mocked_sys.platform = 'win32'
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'Windows' in user_agent, 'The user agent should be a valid Windows user agent'
def test_get_user_agent_macos(self):
"""
Test that getting a user agent on OS X returns a user agent suitable for OS X
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# GIVEN: The system is macOS
mocked_sys.platform = 'darwin'
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'Mac OS X' in user_agent, 'The user agent should be a valid OS X user agent'
def test_get_user_agent_default(self):
"""
Test that getting a user agent on a non-Linux/Windows/OS X platform returns the default user agent
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# GIVEN: The system is something else
mocked_sys.platform = 'freebsd'
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'NetBSD'in user_agent, 'The user agent should be the default user agent'
def test_get_web_page_no_url(self):
"""
Test that sending a URL of None to the get_web_page method returns None
"""
# GIVEN: A None url
test_url = None
# WHEN: We try to get the test URL
result = get_web_page(test_url)
# THEN: None should be returned
assert result is None, 'The return value of get_web_page should be None'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
@patch('openlp.core.common.httputils.Registry')
def test_get_web_page(self, MockRegistry, mocked_get_user_agent, mocked_requests):
"""
Test that the get_web_page method works correctly
"""
# GIVEN: Mocked out objects and a fake URL
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
fake_url = 'this://is.a.fake/url'
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'},
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_once_with()
assert MockRegistry.call_count == 1, 'The Registry() object should have been called once'
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
def test_get_web_page_with_header(self, mocked_get_user_agent, mocked_requests):
"""
Test that adding a header to the call to get_web_page() adds the header to the request
"""
# GIVEN: Mocked out objects, a fake URL and a fake header
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
fake_url = 'this://is.a.fake/url'
fake_headers = {'Fake-Header': 'fake value'}
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, headers=fake_headers)
# THEN: The correct methods are called with the correct arguments and a web page is returned
expected_headers = dict(fake_headers)
expected_headers.update({'User-Agent': 'user_agent'})
mocked_requests.get.assert_called_once_with(fake_url, headers=expected_headers,
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_with()
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
def test_get_web_page_with_user_agent_in_headers(self, mocked_get_user_agent, mocked_requests):
"""
Test that adding a user agent in the header when calling get_web_page() adds that user agent to the request
"""
# GIVEN: Mocked out objects, a fake URL and a fake header
mocked_requests.get.return_value = MagicMock(text='text')
fake_url = 'this://is.a.fake/url'
user_agent_headers = {'User-Agent': 'OpenLP/2.2.0'}
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, headers=user_agent_headers)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers=user_agent_headers,
proxies=None, timeout=30.0)
assert mocked_get_user_agent.call_count == 0, 'get_user_agent() should not have been called'
assert returned_page == 'text', 'The returned page should be "test"'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
@patch('openlp.core.common.httputils.Registry')
def test_get_web_page_update_openlp(self, MockRegistry, mocked_get_user_agent, mocked_requests):
"""
Test that passing "update_openlp" as true to get_web_page calls Registry().get('app').process_events()
"""
# GIVEN: Mocked out objects, a fake URL
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
mocked_registry_object = MagicMock()
mocked_application_object = MagicMock()
mocked_registry_object.get.return_value = mocked_application_object
MockRegistry.return_value = mocked_registry_object
fake_url = 'this://is.a.fake/url'
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, update_openlp=True)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'},
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_once_with()
mocked_registry_object.get.assert_called_with('application')
mocked_application_object.process_events.assert_called_with()
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
def test_get_url_file_size(self, mocked_requests):
"""
Test that calling "get_url_file_size" works correctly
"""
# GIVEN: Mocked out objects, a fake URL
mocked_requests.head.return_value = MagicMock(headers={'Content-Length': 100})
fake_url = 'this://is.a.fake/url'
# WHEN: The get_url_file_size() method is called
file_size = get_url_file_size(fake_url)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0)
assert file_size == 100
@patch('openlp.core.common.httputils.requests')
def test_socket_timeout(self, mocked_requests):
"""
Test socket timeout gets caught
"""
# GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download
mocked_requests.get.side_effect = OSError
# WHEN: Attempt to retrieve a file
download_file(MagicMock(), url='http://localhost/test', file_path=Path(self.tempfile))
# THEN: socket.timeout should have been caught
# NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
assert os.path.exists(self.tempfile) is False, 'tempfile should have been deleted'
# THEN: The user agent is a Linux (or ChromeOS) user agent
result = 'Linux' in user_agent or 'CrOS' in user_agent
assert result is True, 'The user agent should be a valid Linux user agent'
class TestGetProxySettings(TestCase, TestMixin):
def setUp(self):
self.build_settings()
Registry.create()
Registry().register('settings', Settings())
self.addCleanup(self.destroy_settings)
def test_get_user_agent_windows():
"""
Test that getting a user agent on Windows returns a user agent suitable for Windows
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
def test_no_proxy_mode(self):
"""
Test that a dictionary with http and https values are set to None is returned, when `NO_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of NO_PROXY
Settings().setValue('advanced/proxy mode', ProxyMode.NO_PROXY)
# GIVEN: The system is Windows
mocked_sys.platform = 'win32'
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# THEN: The returned value should be a dictionary with http and https values set to None
assert result == {'http': None, 'https': None}
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'Windows' in user_agent, 'The user agent should be a valid Windows user agent'
def test_system_proxy_mode(self):
"""
Test that None is returned, when `SYSTEM_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of SYSTEM_PROXY
Settings().setValue('advanced/proxy mode', ProxyMode.SYSTEM_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
def test_get_user_agent_macos():
"""
Test that getting a user agent on OS X returns a user agent suitable for OS X
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# THEN: The returned value should be None
assert result is None
# GIVEN: The system is macOS
mocked_sys.platform = 'darwin'
def test_manual_proxy_mode_no_auth(self):
"""
Test that the correct proxy addresses are returned when basic authentication is not used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers, but no auth credentials are supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', '')
Settings().setValue('advanced/proxy password', '')
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'Mac OS X' in user_agent, 'The user agent should be a valid OS X user agent'
# THEN: The returned value should be the proxy servers without authentication
assert result == {'http': 'http://testhttp.server:port', 'https': 'https://testhttps.server:port'}
def test_manual_proxy_mode_auth(self):
"""
Test that the correct proxy addresses are returned when basic authentication is used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers and auth credentials supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
def test_get_user_agent_default():
"""
Test that getting a user agent on a non-Linux/Windows/OS X platform returns the default user agent
"""
with patch('openlp.core.common.httputils.sys') as mocked_sys:
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# GIVEN: The system is something else
mocked_sys.platform = 'freebsd'
# THEN: The returned value should be the proxy servers with the authentication credentials
assert result == {'http': 'http://user:pass@testhttp.server:port',
'https': 'https://user:pass@testhttps.server:port'}
# WHEN: We call get_user_agent()
user_agent = get_user_agent()
def test_manual_proxy_mode_no_servers(self):
"""
Test that the system proxies are overidden when the MANUAL_PROXY mode is specified, but no server addresses are
supplied
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with no servers specified
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', '')
Settings().setValue('advanced/proxy https', '')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
# THEN: The user agent is a Linux (or ChromeOS) user agent
assert 'NetBSD'in user_agent, 'The user agent should be the default user agent'
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers set to None
assert result == {'http': None, 'https': None}
def test_get_web_page_no_url():
"""
Test that sending a URL of None to the get_web_page method returns None
"""
# GIVEN: A None url
test_url = None
# WHEN: We try to get the test URL
result = get_web_page(test_url)
# THEN: None should be returned
assert result is None, 'The return value of get_web_page should be None'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
@patch('openlp.core.common.httputils.Registry')
def test_get_web_page(MockRegistry, mocked_get_user_agent, mocked_requests):
"""
Test that the get_web_page method works correctly
"""
# GIVEN: Mocked out objects and a fake URL
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
fake_url = 'this://is.a.fake/url'
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'},
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_once_with()
assert MockRegistry.call_count == 1, 'The Registry() object should have been called once'
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
def test_get_web_page_with_header(mocked_get_user_agent, mocked_requests, settings):
"""
Test that adding a header to the call to get_web_page() adds the header to the request
"""
# GIVEN: Mocked out objects, a fake URL and a fake header
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
fake_url = 'this://is.a.fake/url'
fake_headers = {'Fake-Header': 'fake value'}
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, headers=fake_headers)
# THEN: The correct methods are called with the correct arguments and a web page is returned
expected_headers = dict(fake_headers)
expected_headers.update({'User-Agent': 'user_agent'})
mocked_requests.get.assert_called_once_with(fake_url, headers=expected_headers,
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_with()
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
def test_get_web_page_with_user_agent_in_headers(mocked_get_user_agent, mocked_requests):
"""
Test that adding a user agent in the header when calling get_web_page() adds that user agent to the request
"""
# GIVEN: Mocked out objects, a fake URL and a fake header
mocked_requests.get.return_value = MagicMock(text='text')
fake_url = 'this://is.a.fake/url'
user_agent_headers = {'User-Agent': 'OpenLP/2.2.0'}
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, headers=user_agent_headers)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers=user_agent_headers,
proxies=None, timeout=30.0)
assert mocked_get_user_agent.call_count == 0, 'get_user_agent() should not have been called'
assert returned_page == 'text', 'The returned page should be "test"'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
@patch('openlp.core.common.httputils.Registry')
def test_get_web_page_update_openlp(MockRegistry, mocked_get_user_agent, mocked_requests):
"""
Test that passing "update_openlp" as true to get_web_page calls Registry().get('app').process_events()
"""
# GIVEN: Mocked out objects, a fake URL
mocked_requests.get.return_value = MagicMock(text='text')
mocked_get_user_agent.return_value = 'user_agent'
mocked_registry_object = MagicMock()
mocked_application_object = MagicMock()
mocked_registry_object.get.return_value = mocked_application_object
MockRegistry.return_value = mocked_registry_object
fake_url = 'this://is.a.fake/url'
# WHEN: The get_web_page() method is called
returned_page = get_web_page(fake_url, update_openlp=True)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.get.assert_called_once_with(fake_url, headers={'User-Agent': 'user_agent'},
proxies=None, timeout=30.0)
mocked_get_user_agent.assert_called_once_with()
mocked_registry_object.get.assert_called_with('application')
mocked_application_object.process_events.assert_called_with()
assert returned_page == 'text', 'The returned page should be the mock object'
@patch('openlp.core.common.httputils.requests')
def test_get_url_file_size(mocked_requests):
"""
Test that calling "get_url_file_size" works correctly
"""
# GIVEN: Mocked out objects, a fake URL
mocked_requests.head.return_value = MagicMock(headers={'Content-Length': 100})
fake_url = 'this://is.a.fake/url'
# WHEN: The get_url_file_size() method is called
file_size = get_url_file_size(fake_url)
# THEN: The correct methods are called with the correct arguments and a web page is returned
mocked_requests.head.assert_called_once_with(fake_url, allow_redirects=True, proxies=None, timeout=30.0)
assert file_size == 100
@patch('openlp.core.common.httputils.requests')
def test_socket_timeout(mocked_requests, temp_file):
"""
Test socket timeout gets caught
"""
# GIVEN: Mocked urlopen to fake a network disconnect in the middle of a download
mocked_requests.get.side_effect = OSError
# WHEN: Attempt to retrieve a file
download_file(MagicMock(), url='http://localhost/test', file_path=Path(temp_file))
# THEN: socket.timeout should have been caught
# NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
assert os.path.exists(temp_file) is False, 'temp_file should have been deleted'
def test_no_proxy_mode(settings):
"""
Test that a dictionary with http and https values are set to None is returned, when `NO_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of NO_PROXY
settings.setValue('advanced/proxy mode', ProxyMode.NO_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be a dictionary with http and https values set to None
assert result == {'http': None, 'https': None}
def test_system_proxy_mode(settings):
"""
Test that None is returned, when `SYSTEM_PROXY` mode is specified
"""
# GIVEN: A `proxy mode` setting of SYSTEM_PROXY
settings.setValue('advanced/proxy mode', ProxyMode.SYSTEM_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be None
assert result is None
def test_manual_proxy_mode_no_auth():
"""
Test that the correct proxy addresses are returned when basic authentication is not used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers, but no auth credentials are supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', '')
Settings().setValue('advanced/proxy password', '')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers without authentication
assert result == {'http': 'http://testhttp.server:port', 'https': 'https://testhttps.server:port'}
def test_manual_proxy_mode_auth():
"""
Test that the correct proxy addresses are returned when basic authentication is used
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with proxy servers and auth credentials supplied
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', 'testhttp.server:port')
Settings().setValue('advanced/proxy https', 'testhttps.server:port')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers with the authentication credentials
assert result == {'http': 'http://user:pass@testhttp.server:port',
'https': 'https://user:pass@testhttps.server:port'}
def test_manual_proxy_mode_no_servers():
"""
Test that the system proxies are overidden when the MANUAL_PROXY mode is specified, but no server addresses are
supplied
"""
# GIVEN: A `proxy mode` setting of MANUAL_PROXY with no servers specified
Settings().setValue('advanced/proxy mode', ProxyMode.MANUAL_PROXY)
Settings().setValue('advanced/proxy http', '')
Settings().setValue('advanced/proxy https', '')
Settings().setValue('advanced/proxy username', 'user')
Settings().setValue('advanced/proxy password', 'pass')
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
# THEN: The returned value should be the proxy servers set to None
assert result == {'http': None, 'https': None}
def test_mode_arg_specified(mock_settings):

View File

@ -23,347 +23,347 @@ Functional tests to test the AppLocation class and related methods.
"""
from io import BytesIO
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, PropertyMock, call, patch
from openlp.core.common import add_actions, clean_filename, delete_file, get_file_encoding, get_filesystem_encoding, \
get_uno_command, get_uno_instance
from tests.helpers.testmixin import TestMixin
class TestInit(TestCase, TestMixin):
def test_add_actions_empty_list():
"""
A test suite to test out various methods around the common __init__ class.
Test that no actions are added when the list is empty
"""
# GIVEN: a mocked action list, and an empty list
mocked_target = MagicMock()
empty_list = []
# WHEN: The empty list is added to the mocked target
add_actions(mocked_target, empty_list)
# THEN: The add method on the mocked target is never called
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
def test_add_actions_none_action():
"""
Test that a separator is added when a None action is in the list
"""
# GIVEN: a mocked action list, and a list with None in it
mocked_target = MagicMock()
separator_list = [None]
# WHEN: The list is added to the mocked target
add_actions(mocked_target, separator_list)
# THEN: The addSeparator method is called, but the addAction method is never called
mocked_target.addSeparator.assert_called_with()
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
def test_add_actions_add_action():
"""
Test that an action is added when a valid action is in the list
"""
# GIVEN: a mocked action list, and a list with an action in it
mocked_target = MagicMock()
action_list = ['action']
# WHEN: The list is added to the mocked target
add_actions(mocked_target, action_list)
# THEN: The addSeparator method is not called, and the addAction method is called
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
mocked_target.addAction.assert_called_with('action')
def test_add_actions_action_and_none():
"""
Test that an action and a separator are added when a valid action and None are in the list
"""
# GIVEN: a mocked action list, and a list with an action and None in it
mocked_target = MagicMock()
action_list = ['action', None]
# WHEN: The list is added to the mocked target
add_actions(mocked_target, action_list)
# THEN: The addSeparator method is called, and the addAction method is called
mocked_target.addSeparator.assert_called_with()
mocked_target.addAction.assert_called_with('action')
def test_get_uno_instance_pipe():
"""
Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI
"""
# GIVEN: A mock resolver object and UNO_CONNECTION_TYPE is "pipe"
mock_resolver = MagicMock()
# WHEN: get_uno_instance() is called
get_uno_instance(mock_resolver)
# THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
def test_get_uno_instance_socket():
"""
Test that when the UNO connection type is other than "pipe" the resolver is given the "socket" URI
"""
# GIVEN: A mock resolver object and UNO_CONNECTION_TYPE is "socket"
mock_resolver = MagicMock()
# WHEN: get_uno_instance() is called
get_uno_instance(mock_resolver, 'socket')
# THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
def test_get_uno_command_libreoffice_command_exists():
"""
Test the ``get_uno_command`` function uses the libreoffice command when available.
:return:
"""
def setUp(self):
"""
Create an instance and a few example actions.
"""
self.build_settings()
# GIVEN: A patched 'which' method which returns a path when called with 'libreoffice'
with patch('openlp.core.common.which',
**{'side_effect': lambda command: {'libreoffice': '/usr/bin/libreoffice'}[command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
def tearDown(self):
"""
Clean up
"""
self.destroy_settings()
# THEN: The command 'libreoffice' should be called with the appropriate parameters
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_add_actions_empty_list(self):
"""
Test that no actions are added when the list is empty
"""
# GIVEN: a mocked action list, and an empty list
mocked_target = MagicMock()
empty_list = []
# WHEN: The empty list is added to the mocked target
add_actions(mocked_target, empty_list)
def test_get_uno_command_only_soffice_command_exists():
"""
Test the ``get_uno_command`` function uses the soffice command when the libreoffice command is not available.
:return:
"""
# THEN: The add method on the mocked target is never called
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
# GIVEN: A patched 'which' method which returns None when called with 'libreoffice' and a path when called with
# 'soffice'
with patch('openlp.core.common.which',
**{'side_effect': lambda command: {'libreoffice': None, 'soffice': '/usr/bin/soffice'}[
command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
def test_add_actions_none_action(self):
"""
Test that a separator is added when a None action is in the list
"""
# GIVEN: a mocked action list, and a list with None in it
mocked_target = MagicMock()
separator_list = [None]
# THEN: The command 'soffice' should be called with the appropriate parameters
assert result == 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
# WHEN: The list is added to the mocked target
add_actions(mocked_target, separator_list)
# THEN: The addSeparator method is called, but the addAction method is never called
mocked_target.addSeparator.assert_called_with()
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
def test_get_uno_command_when_no_command_exists():
"""
Test the ``get_uno_command`` function raises an FileNotFoundError when neither the libreoffice or soffice
commands are available.
:return:
"""
def test_add_actions_add_action(self):
"""
Test that an action is added when a valid action is in the list
"""
# GIVEN: a mocked action list, and a list with an action in it
mocked_target = MagicMock()
action_list = ['action']
# GIVEN: A patched 'which' method which returns None
with patch('openlp.core.common.which', **{'return_value': None}):
# WHEN: Calling get_uno_command
# WHEN: The list is added to the mocked target
add_actions(mocked_target, action_list)
# THEN: a FileNotFoundError exception should be raised
assert FileNotFoundError, get_uno_command
# THEN: The addSeparator method is not called, and the addAction method is called
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
mocked_target.addAction.assert_called_with('action')
def test_add_actions_action_and_none(self):
"""
Test that an action and a separator are added when a valid action and None are in the list
"""
# GIVEN: a mocked action list, and a list with an action and None in it
mocked_target = MagicMock()
action_list = ['action', None]
def test_get_uno_command_connection_type():
"""
Test the ``get_uno_command`` function when the connection type is anything other than pipe.
:return:
"""
# WHEN: The list is added to the mocked target
add_actions(mocked_target, action_list)
# GIVEN: A patched 'which' method which returns 'libreoffice'
with patch('openlp.core.common.which', **{'return_value': 'libreoffice'}):
# WHEN: Calling get_uno_command with a connection type other than pipe
result = get_uno_command('socket')
# THEN: The addSeparator method is called, and the addAction method is called
mocked_target.addSeparator.assert_called_with()
mocked_target.addAction.assert_called_with('action')
# THEN: The connection parameters should be set for socket
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=socket,host=localhost,port=2002;urp;"'
def test_get_uno_instance_pipe(self):
"""
Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI
"""
# GIVEN: A mock resolver object and UNO_CONNECTION_TYPE is "pipe"
mock_resolver = MagicMock()
# WHEN: get_uno_instance() is called
get_uno_instance(mock_resolver)
def test_get_filesystem_encoding_sys_function_not_called():
"""
Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function
"""
# GIVEN: sys.getfilesystemencoding returns "cp1252"
with patch('openlp.core.common.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \
patch('openlp.core.common.sys.getdefaultencoding') as mocked_getdefaultencoding:
mocked_getfilesystemencoding.return_value = 'cp1252'
# THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext')
# WHEN: get_filesystem_encoding() is called
result = get_filesystem_encoding()
def test_get_uno_instance_socket(self):
"""
Test that when the UNO connection type is other than "pipe" the resolver is given the "socket" URI
"""
# GIVEN: A mock resolver object and UNO_CONNECTION_TYPE is "socket"
mock_resolver = MagicMock()
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
assert mocked_getdefaultencoding.called == 0, 'getdefaultencoding should not have been called'
assert 'cp1252' == result, 'The result should be "cp1252"'
# WHEN: get_uno_instance() is called
get_uno_instance(mock_resolver, 'socket')
# THEN: the resolve method is called with the correct argument
mock_resolver.resolve.assert_called_with('uno:socket,host=localhost,port=2002;urp;StarOffice.ComponentContext')
def test_get_filesystem_encoding_sys_function_is_called():
"""
Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function
"""
# GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8"
with patch('openlp.core.common.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \
patch('openlp.core.common.sys.getdefaultencoding') as mocked_getdefaultencoding:
mocked_getfilesystemencoding.return_value = None
mocked_getdefaultencoding.return_value = 'utf-8'
def test_get_uno_command_libreoffice_command_exists(self):
"""
Test the ``get_uno_command`` function uses the libreoffice command when available.
:return:
"""
# WHEN: get_filesystem_encoding() is called
result = get_filesystem_encoding()
# GIVEN: A patched 'which' method which returns a path when called with 'libreoffice'
with patch('openlp.core.common.which',
**{'side_effect': lambda command: {'libreoffice': '/usr/bin/libreoffice'}[command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
mocked_getdefaultencoding.assert_called_with()
assert 'utf-8' == result, 'The result should be "utf-8"'
# THEN: The command 'libreoffice' should be called with the appropriate parameters
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_get_uno_command_only_soffice_command_exists(self):
"""
Test the ``get_uno_command`` function uses the soffice command when the libreoffice command is not available.
:return:
"""
def test_clean_filename():
"""
Test the clean_filename() function
"""
# GIVEN: A invalid file name and the valid file name.
invalid_name = 'A_file_with_invalid_characters_[\\/:*?"<>|+[]%].py'
wanted_name = 'A_file_with_invalid_characters________________.py'
# GIVEN: A patched 'which' method which returns None when called with 'libreoffice' and a path when called with
# 'soffice'
with patch('openlp.core.common.which',
**{'side_effect': lambda command: {'libreoffice': None, 'soffice': '/usr/bin/soffice'}[
command]}):
# WHEN: Calling get_uno_command
result = get_uno_command()
# WHEN: Clean the name.
result = clean_filename(invalid_name)
# THEN: The command 'soffice' should be called with the appropriate parameters
assert result == 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
# THEN: The file name should be cleaned.
assert wanted_name == result, 'The file name should not contain any special characters.'
def test_get_uno_command_when_no_command_exists(self):
"""
Test the ``get_uno_command`` function raises an FileNotFoundError when neither the libreoffice or soffice
commands are available.
:return:
"""
# GIVEN: A patched 'which' method which returns None
with patch('openlp.core.common.which', **{'return_value': None}):
# WHEN: Calling get_uno_command
def test_delete_file_no_path():
"""
Test the delete_file function when called with out a valid path
"""
# GIVEN: A blank path
# WEHN: Calling delete_file
result = delete_file(None)
# THEN: a FileNotFoundError exception should be raised
self.assertRaises(FileNotFoundError, get_uno_command)
# THEN: delete_file should return False
assert result is False, "delete_file should return False when called with None"
def test_get_uno_command_connection_type(self):
"""
Test the ``get_uno_command`` function when the connection type is anything other than pipe.
:return:
"""
# GIVEN: A patched 'which' method which returns 'libreoffice'
with patch('openlp.core.common.which', **{'return_value': 'libreoffice'}):
# WHEN: Calling get_uno_command with a connection type other than pipe
result = get_uno_command('socket')
def test_delete_file_path_success():
"""
Test the delete_file function when it successfully deletes a file
"""
# GIVEN: A mocked os which returns True when os.path.exists is called
with patch('openlp.core.common.os', **{'path.exists.return_value': False}):
# THEN: The connection parameters should be set for socket
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=socket,host=localhost,port=2002;urp;"'
# WHEN: Calling delete_file with a file path
result = delete_file(Path('path', 'file.ext'))
def test_get_filesystem_encoding_sys_function_not_called(self):
"""
Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function
"""
# GIVEN: sys.getfilesystemencoding returns "cp1252"
with patch('openlp.core.common.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \
patch('openlp.core.common.sys.getdefaultencoding') as mocked_getdefaultencoding:
mocked_getfilesystemencoding.return_value = 'cp1252'
# THEN: delete_file should return True
assert result is True, 'delete_file should return True when it successfully deletes a file'
# WHEN: get_filesystem_encoding() is called
result = get_filesystem_encoding()
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
assert mocked_getdefaultencoding.called == 0, 'getdefaultencoding should not have been called'
assert 'cp1252' == result, 'The result should be "cp1252"'
def test_delete_file_path_no_file_exists():
"""
Test the `delete_file` function when the file to remove does not exist
"""
# GIVEN: A patched `exists` methods on the Path object, which returns False
with patch.object(Path, 'exists', return_value=False), \
patch.object(Path, 'unlink') as mocked_unlink:
def test_get_filesystem_encoding_sys_function_is_called(self):
"""
Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function
"""
# GIVEN: sys.getfilesystemencoding returns None and sys.getdefaultencoding returns "utf-8"
with patch('openlp.core.common.sys.getfilesystemencoding') as mocked_getfilesystemencoding, \
patch('openlp.core.common.sys.getdefaultencoding') as mocked_getdefaultencoding:
mocked_getfilesystemencoding.return_value = None
mocked_getdefaultencoding.return_value = 'utf-8'
# WHEN: Calling `delete_file with` a file path
result = delete_file(Path('path', 'file.ext'))
# WHEN: get_filesystem_encoding() is called
result = get_filesystem_encoding()
# THEN: The function should not attempt to delete the file and it should return True
assert mocked_unlink.called is False
assert result is True, 'delete_file should return True when the file doesnt exist'
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
mocked_getdefaultencoding.assert_called_with()
assert 'utf-8' == result, 'The result should be "utf-8"'
def test_clean_filename(self):
"""
Test the clean_filename() function
"""
# GIVEN: A invalid file name and the valid file name.
invalid_name = 'A_file_with_invalid_characters_[\\/:*?"<>|+[]%].py'
wanted_name = 'A_file_with_invalid_characters________________.py'
def test_delete_file_path_exception():
"""
Test the delete_file function when an exception is raised
"""
# GIVEN: A test `Path` object with a patched exists method which raises an OSError
# called.
with patch.object(Path, 'exists') as mocked_exists, \
patch('openlp.core.common.log') as mocked_log:
mocked_exists.side_effect = OSError
# WHEN: Clean the name.
result = clean_filename(invalid_name)
# WHEN: Calling delete_file with a the test Path object
result = delete_file(Path('path', 'file.ext'))
# THEN: The file name should be cleaned.
assert wanted_name == result, 'The file name should not contain any special characters.'
# THEN: The exception should be logged and `delete_file` should return False
assert mocked_log.exception.called
assert result is False, 'delete_file should return False when an OSError is raised'
def test_delete_file_no_path(self):
"""
Test the delete_file function when called with out a valid path
"""
# GIVEN: A blank path
# WEHN: Calling delete_file
result = delete_file(None)
# THEN: delete_file should return False
assert result is False, "delete_file should return False when called with None"
def test_get_file_encoding_done():
"""
Test get_file_encoding when the detector sets done to True
"""
# GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(**{'close.return_value': encoding_result})
type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])
mocked_universal_detector.return_value = mocked_universal_detector_inst
def test_delete_file_path_success(self):
"""
Test the delete_file function when it successfully deletes a file
"""
# GIVEN: A mocked os which returns True when os.path.exists is called
with patch('openlp.core.common.os', **{'path.exists.return_value': False}):
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
# WHEN: Calling delete_file with a file path
result = delete_file(Path('path', 'file.ext'))
# THEN: The feed method of UniversalDetector should only br called once before returning a result
mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)]
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
# THEN: delete_file should return True
assert result is True, 'delete_file should return True when it successfully deletes a file'
def test_delete_file_path_no_file_exists(self):
"""
Test the `delete_file` function when the file to remove does not exist
"""
# GIVEN: A patched `exists` methods on the Path object, which returns False
with patch.object(Path, 'exists', return_value=False), \
patch.object(Path, 'unlink') as mocked_unlink:
def test_get_file_encoding_eof():
"""
Test get_file_encoding when the end of the file is reached
"""
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
# data (enough to run the iterator twice)
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
**{'done': False, 'close.return_value': encoding_result})
mocked_universal_detector.return_value = mocked_universal_detector_inst
# WHEN: Calling `delete_file with` a file path
result = delete_file(Path('path', 'file.ext'))
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
# THEN: The function should not attempt to delete the file and it should return True
assert mocked_unlink.called is False
assert result is True, 'delete_file should return True when the file doesnt exist'
# THEN: The feed method of UniversalDetector should have been called twice before returning a result
mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)]
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
def test_delete_file_path_exception(self):
"""
Test the delete_file function when an exception is raised
"""
# GIVEN: A test `Path` object with a patched exists method which raises an OSError
# called.
with patch.object(Path, 'exists') as mocked_exists, \
patch('openlp.core.common.log') as mocked_log:
mocked_exists.side_effect = OSError
# WHEN: Calling delete_file with a the test Path object
result = delete_file(Path('path', 'file.ext'))
def test_get_file_encoding_oserror():
"""
Test get_file_encoding when the end of the file is reached
"""
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
# data (enough to run the iterator twice)
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch('builtins.open', side_effect=OSError), \
patch('openlp.core.common.log') as mocked_log:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
**{'done': False, 'close.return_value': encoding_result})
mocked_universal_detector.return_value = mocked_universal_detector_inst
# THEN: The exception should be logged and `delete_file` should return False
assert mocked_log.exception.called
assert result is False, 'delete_file should return False when an OSError is raised'
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
def test_get_file_encoding_done(self):
"""
Test get_file_encoding when the detector sets done to True
"""
# GIVEN: A mocked UniversalDetector instance with done attribute set to True after first iteration
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(**{'close.return_value': encoding_result})
type(mocked_universal_detector_inst).done = PropertyMock(side_effect=[False, True])
mocked_universal_detector.return_value = mocked_universal_detector_inst
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
# THEN: The feed method of UniversalDetector should only br called once before returning a result
mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)]
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
def test_get_file_encoding_eof(self):
"""
Test get_file_encoding when the end of the file is reached
"""
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
# data (enough to run the iterator twice)
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch.object(Path, 'open', return_value=BytesIO(b'data' * 260)) as mocked_open:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
**{'done': False, 'close.return_value': encoding_result})
mocked_universal_detector.return_value = mocked_universal_detector_inst
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
# THEN: The feed method of UniversalDetector should have been called twice before returning a result
mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)]
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
def test_get_file_encoding_oserror(self):
"""
Test get_file_encoding when the end of the file is reached
"""
# GIVEN: A mocked UniversalDetector instance which isn't set to done and a mocked open, with 1040 bytes of test
# data (enough to run the iterator twice)
with patch('openlp.core.common.UniversalDetector') as mocked_universal_detector, \
patch('builtins.open', side_effect=OSError), \
patch('openlp.core.common.log') as mocked_log:
encoding_result = {'encoding': 'UTF-8', 'confidence': 0.99}
mocked_universal_detector_inst = MagicMock(mock=mocked_universal_detector,
**{'done': False, 'close.return_value': encoding_result})
mocked_universal_detector.return_value = mocked_universal_detector_inst
# WHEN: Calling get_file_encoding
result = get_file_encoding(Path('file name'))
# THEN: log.exception should be called and get_file_encoding should return None
mocked_log.exception.assert_called_once_with('Error detecting file encoding')
mocked_universal_detector_inst.feed.assert_not_called()
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
# THEN: log.exception should be called and get_file_encoding should return None
mocked_log.exception.assert_called_once_with('Error detecting file encoding')
mocked_universal_detector_inst.feed.assert_not_called()
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'

View File

@ -29,7 +29,6 @@ sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
from openlp.core.app import parse_options
from openlp.core.common import is_win
from openlp.core.common.settings import Settings
def test_parse_options_basic():
@ -239,7 +238,7 @@ def test_backup_on_upgrade_first_install(mocked_question, mocked_get_version, qa
qapp.backup_on_upgrade(old_install, False)
# THEN: It should not ask if we want to create a backup
assert Settings().value('core/application version') == '2.4.0', 'Version should be the same!'
assert settings.value('core/application version') == '2.4.0', 'Version should be the same!'
assert mocked_question.call_count == 0, 'No question should have been asked!'
@ -266,7 +265,7 @@ def test_backup_on_upgrade(mocked_question, mocked_get_version, qapp, settings):
qapp.backup_on_upgrade(old_install, True)
# THEN: It should ask if we want to create a backup
assert Settings().value('core/application version') == '2.9.0', 'Version should be upgraded!'
assert settings.value('core/application version') == '2.9.0', 'Version should be upgraded!'
assert mocked_question.call_count == 1, 'A question should have been asked!'
qapp.splash.hide.assert_called_once_with()
qapp.splash.show.assert_called_once_with()

View File

@ -28,20 +28,6 @@ from openlp.core.ui.settingsform import SettingsForm
from openlp.core.ui.themestab import ThemesTab
def test_creation(mock_settings):
"""
Test that Themes Tab is created.
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
# WHEN: I create an advanced tab
themes_tab = ThemesTab(settings_form)
# THEN:
assert "Themes" == themes_tab.tab_title, 'The tab title should be Theme'
def test_save_triggers_processes_true(mock_settings):
"""
Test that the global theme event is triggered when the tab is visited.

View File

@ -21,7 +21,7 @@
"""
Package to test the openlp.core.ui.mainwindow package.
"""
from unittest import TestCase, skipIf
from unittest import TestCase, skipIf, skip
from unittest.mock import MagicMock, patch
from PyQt5 import QtGui
@ -56,6 +56,7 @@ class TestMainWindow(TestCase, TestMixin):
mocked_plugin.status = PluginStatus.Active
mocked_plugin.icon = QtGui.QIcon()
Registry().register('mock_plugin', mocked_plugin)
State().load_settings()
State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active)
# Mock classes and methods used by mainwindow.
with patch('openlp.core.ui.mainwindow.SettingsForm'), \
@ -77,6 +78,7 @@ class TestMainWindow(TestCase, TestMixin):
"""
del self.main_window
@skip('Fix when migrate to PyTest')
def test_restore_current_media_manager_item(self):
"""
Regression test for bug #1152509.

View File

@ -46,6 +46,7 @@ class TestBibleManager(TestCase, TestMixin):
with patch('openlp.core.common.applocation.AppLocation.get_section_data_path') as mocked_get_data_path, \
patch('openlp.core.common.applocation.AppLocation.get_files') as mocked_get_files:
Registry().register('settings', Settings())
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_files()
mocked_get_files.return_value = ["tests.sqlite"]
mocked_get_data_path.return_value = TEST_RESOURCES_PATH + "/bibles"
self.manager = BibleManager(MagicMock())