Merge branch 'tests_1' into 'master'

Tests part 1

See merge request openlp/openlp!136
This commit is contained in:
Tim Bentley 2020-02-12 20:28:33 +00:00
commit 3bda907d4d
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):
"""
Test the Remote plugin deploy functions
"""
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']
@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']})
]
self.screens = ScreenList.create(self.desktop)
# Mock the renderer and its format_slide method
self.mocked_renderer = MagicMock()
mocked_renderer = MagicMock()
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()
def test_controller_text_empty(self):
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):
"""
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'
self.mocked_live_controller.service_item = mocked_service_item
mocked_live_controller.service_item = mocked_service_item
# WHEN: I trigger the method
ret = self.client.get('/api/controller/live/text').get_json()
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
def test_controller_text(self):
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')
self.mocked_live_controller.service_item = ServiceItem(None)
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()
mocked_live_controller.service_item.set_from_service(line)
mocked_live_controller.service_item._create_slides()
# WHEN: I trigger the method
ret = self.client.get('/api/controller/live/text').get_json()
ret = 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(self):
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()
self.mocked_live_controller.slidecontroller_live_next.emit = mocked_emit
self.mocked_live_controller.service_item = 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()
# WHEN: I trigger the method
self.client.get('/api/controller/live/next')
client.get('/api/controller/live/next')
# THEN: The correct method is called
mocked_emit.assert_called_once_with()
def test_controller_direction_previous(self):
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()
self.mocked_live_controller.slidecontroller_live_previous.emit = mocked_emit
self.mocked_live_controller.service_item = 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')
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,75 +21,48 @@
"""
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):
"""
Test the functions in the :mod:`lib` module.
"""
def setUp(self):
"""
Create the UI
"""
self.setup_application()
self.build_settings()
Settings().extend_default_settings(__default_settings__)
self.parent = QtWidgets.QMainWindow()
Registry().create()
@pytest.yield_fixture
def api_tab(settings):
Registry().set_flag('website_version', '00-00-0000')
Registry().register('settings', Settings())
self.form = ApiTab(self.parent)
self.interfaces = get_network_interfaces()
parent = QtWidgets.QMainWindow()
form = ApiTab(parent)
yield form
del parent
del form
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()
def test_get_ip_address_default(self):
def test_get_ip_address_default(api_tab):
"""
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():
interfaces = get_network_interfaces()
for _, interface in interfaces.items():
ip_addresses.append(interface['ip'])
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(ZERO_URL)
ip_address = api_tab.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(self):
def test_get_ip_address_with_ip(api_tab):
"""
Test the get_ip_address function with given ip address
"""
@ -97,25 +70,26 @@ class TestApiTab(TestCase, TestMixin):
given_ip = '192.168.1.1'
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(given_ip)
ip_address = api_tab.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
def test_set_urls(self):
def test_set_urls(api_tab):
"""
Test the set_url function to generate correct url links
"""
# GIVEN: An ip address
self.form.address_edit.setText('192.168.1.1')
api_tab.address_edit.setText('192.168.1.1')
# WHEN: the urls are generated
self.form.set_urls()
api_tab.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>", \
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 self.form.stage_url.text() == \
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 self.form.live_url.text() == \
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,51 +21,23 @@
"""
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):
"""
A test suite to test starting the websocket server
"""
def setUp(self):
"""
Create the UI
"""
self.build_settings()
Settings().extend_default_settings(__default_settings__)
Registry().create()
Registry().register('settings', Settings())
self.poll = Poller()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
self.destroy_settings()
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.run_thread')
def test_serverstart(self, mocked_run_thread, MockWebSocketWorker):
def test_serverstart(mocked_run_thread, MockWebSocketWorker, registry):
"""
Test the starting of the WebSockets Server with the disabled flag set on
"""
@ -78,9 +50,10 @@ class TestWSServer(TestCase, TestMixin):
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(self, mocked_run_thread, MockWebSocketWorker):
def test_serverstart_not_required(mocked_run_thread, MockWebSocketWorker, registry):
"""
Test the starting of the WebSockets Server with the disabled flag set off
"""
@ -93,7 +66,8 @@ class TestWSServer(TestCase, TestMixin):
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):
def test_main_poll(poller):
"""
Test the main_poll function returns the correct JSON
"""
@ -102,10 +76,11 @@ class TestWSServer(TestCase, TestMixin):
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()
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):
def test_poll(poller, settings):
"""
Test the poll function returns the correct JSON
"""
@ -122,13 +97,13 @@ class TestWSServer(TestCase, TestMixin):
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:
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 = self.poll.poll()
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'

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,29 +156,7 @@ def test_action_list_remove():
assert MOCK_ACTION1 not in category_list
class TestActionList(TestCase, TestMixin):
"""
Test the ActionList class
"""
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')
def tearDown(self):
"""
Clean up
"""
self.settings.endGroup()
self.destroy_settings()
def test_add_action_same_parent(self):
def test_add_action_same_parent(action_list):
"""
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.
@ -190,20 +173,21 @@ class TestActionList(TestCase, TestMixin):
'shortcuts/action_with_same_shortcuts1': [QtGui.QKeySequence(QtCore.Qt.Key_B),
QtGui.QKeySequence(QtCore.Qt.Key_A)]
}
Settings.extend_default_settings(default_shortcuts)
Registry().get('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')
action_list.add_action(action1, 'example_category')
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')
action_list.remove_action(action1, 'example_category')
action_list.remove_action(action_with_same_shortcuts1, 'example_category')
# 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_different_parent(self):
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.
@ -221,20 +205,21 @@ class TestActionList(TestCase, TestMixin):
'shortcuts/action_with_same_shortcuts2': [QtGui.QKeySequence(QtCore.Qt.Key_D),
QtGui.QKeySequence(QtCore.Qt.Key_C)]
}
Settings.extend_default_settings(default_shortcuts)
Registry().get('settings').extend_default_settings(default_shortcuts)
# 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')
action_list.add_action(action2, 'example_category')
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')
action_list.remove_action(action2, 'example_category')
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(self):
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.
@ -254,14 +239,14 @@ class TestActionList(TestCase, TestMixin):
'shortcuts/action_with_same_shortcuts3': [QtGui.QKeySequence(QtCore.Qt.Key_E),
QtGui.QKeySequence(QtCore.Qt.Key_F)]
}
Settings.extend_default_settings(default_shortcuts)
Registry().get('settings').extend_default_settings(default_shortcuts)
# 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')
action_list.add_action(action3, 'example_category2')
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')
action_list.remove_action(action3, 'example_category2')
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.'

View File

@ -22,18 +22,14 @@
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):
"""
A test suite to test out various functions in the openlp.core.common module.
"""
def test_extension_loader_no_files_found(self):
def test_extension_loader_no_files_found():
"""
Test the `extension_loader` function when no files are found
"""
@ -49,7 +45,8 @@ class TestCommonFunctions(TestCase):
# THEN: `extension_loader` should not try to import any files
assert mocked_import_module.called is False
def test_extension_loader_files_found(self):
def test_extension_loader_files_found():
"""
Test the `extension_loader` function when it successfully finds and loads some files
"""
@ -71,7 +68,8 @@ class TestCommonFunctions(TestCase):
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'),
call('openlp.import_dir.file4')])
def test_extension_loader_import_error(self):
def test_extension_loader_import_error():
"""
Test the `extension_loader` function when `SourceFileLoader` raises a `ImportError`
"""
@ -89,7 +87,8 @@ class TestCommonFunctions(TestCase):
# THEN: The `ImportError` should be caught and logged
assert mocked_logger.exception.called
def test_extension_loader_os_error(self):
def test_extension_loader_os_error():
"""
Test the `extension_loader` function when `import_module` raises a `ImportError`
"""
@ -107,7 +106,8 @@ class TestCommonFunctions(TestCase):
# THEN: The `OSError` should be caught and logged
assert mocked_logger.exception.called
def test_de_hump_conversion(self):
def test_de_hump_conversion():
"""
Test the de_hump function with a class name
"""
@ -120,7 +120,8 @@ class TestCommonFunctions(TestCase):
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been converted'
def test_de_hump_static(self):
def test_de_hump_static():
"""
Test the de_hump function with a python string
"""
@ -133,7 +134,8 @@ class TestCommonFunctions(TestCase):
# THEN: the new string should be converted to python format
assert new_string == "my_class", 'The class name should have been preserved'
def test_path_to_module(self):
def test_path_to_module():
"""
Test `path_to_module` when supplied with a `Path` object
"""
@ -146,7 +148,8 @@ class TestCommonFunctions(TestCase):
# THEN: path_to_module should return the module name
assert result == 'openlp.core.ui.media.vlcplayer'
def test_trace_error_handler(self):
def test_trace_error_handler():
"""
Test the trace_error_handler() method
"""
@ -162,7 +165,8 @@ class TestCommonFunctions(TestCase):
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):
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..
@ -183,7 +187,8 @@ class TestCommonFunctions(TestCase):
assert inst_1 is inst_2
assert patched_init.call_count == 1
def test_singleton_metaclass_multiple_classes(self):
def test_singleton_metaclass_multiple_classes():
"""
Test that multiple classes using the Singleton Metaclass return the different an appropriate instances.
"""
@ -204,7 +209,8 @@ class TestCommonFunctions(TestCase):
assert isinstance(s_c1, SingletonClass1)
assert isinstance(s_c2, SingletonClass2)
def test_is_win(self):
def test_is_win():
"""
Test the is_win() function
"""
@ -220,7 +226,8 @@ class TestCommonFunctions(TestCase):
assert is_macosx() is False, 'is_macosx() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_is_macosx(self):
def test_is_macosx():
"""
Test the is_macosx() function
"""
@ -236,7 +243,8 @@ class TestCommonFunctions(TestCase):
assert is_win() is False, 'is_win() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_is_linux(self):
def test_is_linux():
"""
Test the is_linux() function
"""
@ -252,8 +260,9 @@ class TestCommonFunctions(TestCase):
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(self):
def test_is_linux_distro():
"""
Test the is_linux() function for a particular Linux distribution
"""
@ -273,7 +282,8 @@ class TestCommonFunctions(TestCase):
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(self):
def test_is_64bit_instance():
"""
Test the is_64bit_instance() function
"""
@ -286,7 +296,8 @@ class TestCommonFunctions(TestCase):
# THEN: The result should be False
assert is_64bit_instance() is False, 'is_64bit_instance() should return False'
def test_normalize_str_leaves_newlines(self):
def test_normalize_str_leaves_newlines():
# GIVEN: a string containing newlines
string = 'something\nelse'
# WHEN: normalize is called
@ -294,7 +305,8 @@ class TestCommonFunctions(TestCase):
# THEN: string is unchanged
assert normalized_string == string
def test_normalize_str_removes_null_byte(self):
def test_normalize_str_removes_null_byte():
# GIVEN: a string containing a null byte
string = 'somet\x00hing'
# WHEN: normalize is called
@ -302,7 +314,8 @@ class TestCommonFunctions(TestCase):
# THEN: nullbyte is removed
assert normalized_string == 'something'
def test_normalize_str_replaces_crlf_with_lf(self):
def test_normalize_str_replaces_crlf_with_lf():
# GIVEN: a string containing crlf
string = 'something\r\nelse'
# WHEN: normalize is called
@ -310,7 +323,8 @@ class TestCommonFunctions(TestCase):
# THEN: crlf is replaced with lf
assert normalized_string == 'something\nelse'
def test_clean_button_text(self):
def test_clean_button_text():
"""
Test the clean_button_text() function.
"""

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):
def setUp(self):
"""
Create temp folder for keeping db file
"""
self.tmp_folder = mkdtemp()
@pytest.yield_fixture
def op():
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
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(self.tmp_folder):
shutil.rmtree(self.tmp_folder)
if os.path.exists(tmp_folder):
shutil.rmtree(tmp_folder)
break
except Exception:
time.sleep(1)
retries += 1
def test_delete_column(self):
def test_delete_column(op):
"""
Test deleting a single column in a table
"""
# GIVEN: A temporary song db
# WHEN: Deleting a columns in a table
drop_column(self.op, 'songs', 'song_book_id')
drop_column(op, 'songs', 'song_book_id')
# THEN: The column should have been deleted
meta = sqlalchemy.MetaData(bind=self.op.get_bind())
meta = sqlalchemy.MetaData(bind=op.get_bind())
meta.reflect()
columns = meta.tables['songs'].columns
for column in columns:
if column.name == 'song_book_id':
self.fail("The column 'song_book_id' should have been deleted.")
assert "The column 'song_book_id' should have been deleted."
def test_delete_columns(self):
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'])
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 = 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)
assert "The column '%s' should have been deleted." % column.name

View File

@ -22,32 +22,25 @@
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):
"""
A test suite to test out various http helper functions.
"""
def setUp(self):
self.tempfile = os.path.join(tempfile.gettempdir(), 'testfile')
Registry.create()
Registry().register('settings', Settings())
@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 tearDown(self):
if os.path.isfile(self.tempfile):
os.remove(self.tempfile)
def test_get_user_agent_linux(self):
def test_get_user_agent_linux():
"""
Test that getting a user agent on Linux returns a user agent suitable for Linux
"""
@ -63,7 +56,8 @@ class TestHttpUtils(TestCase, TestMixin):
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):
def test_get_user_agent_windows():
"""
Test that getting a user agent on Windows returns a user agent suitable for Windows
"""
@ -78,7 +72,8 @@ class TestHttpUtils(TestCase, TestMixin):
# 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):
def test_get_user_agent_macos():
"""
Test that getting a user agent on OS X returns a user agent suitable for OS X
"""
@ -93,7 +88,8 @@ class TestHttpUtils(TestCase, TestMixin):
# 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):
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
"""
@ -108,7 +104,8 @@ class TestHttpUtils(TestCase, TestMixin):
# 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):
def test_get_web_page_no_url():
"""
Test that sending a URL of None to the get_web_page method returns None
"""
@ -121,10 +118,11 @@ class TestHttpUtils(TestCase, TestMixin):
# 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):
def test_get_web_page(MockRegistry, mocked_get_user_agent, mocked_requests):
"""
Test that the get_web_page method works correctly
"""
@ -143,9 +141,10 @@ class TestHttpUtils(TestCase, TestMixin):
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):
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
"""
@ -166,9 +165,10 @@ class TestHttpUtils(TestCase, TestMixin):
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):
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
"""
@ -186,10 +186,11 @@ class TestHttpUtils(TestCase, TestMixin):
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):
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()
"""
@ -213,8 +214,9 @@ class TestHttpUtils(TestCase, TestMixin):
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):
def test_get_url_file_size(mocked_requests):
"""
Test that calling "get_url_file_size" works correctly
"""
@ -229,8 +231,9 @@ class TestHttpUtils(TestCase, TestMixin):
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):
def test_socket_timeout(mocked_requests, temp_file):
"""
Test socket timeout gets caught
"""
@ -238,26 +241,19 @@ class TestHttpUtils(TestCase, TestMixin):
mocked_requests.get.side_effect = OSError
# WHEN: Attempt to retrieve a file
download_file(MagicMock(), url='http://localhost/test', file_path=Path(self.tempfile))
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(self.tempfile) is False, 'tempfile should have been deleted'
assert os.path.exists(temp_file) is False, 'temp_file should have been deleted'
class TestGetProxySettings(TestCase, TestMixin):
def setUp(self):
self.build_settings()
Registry.create()
Registry().register('settings', Settings())
self.addCleanup(self.destroy_settings)
def test_no_proxy_mode(self):
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)
settings.setValue('advanced/proxy mode', ProxyMode.NO_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
@ -265,12 +261,13 @@ class TestGetProxySettings(TestCase, TestMixin):
# 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(self):
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)
settings.setValue('advanced/proxy mode', ProxyMode.SYSTEM_PROXY)
# WHEN: Calling `get_proxy_settings`
result = get_proxy_settings()
@ -278,7 +275,8 @@ class TestGetProxySettings(TestCase, TestMixin):
# THEN: The returned value should be None
assert result is None
def test_manual_proxy_mode_no_auth(self):
def test_manual_proxy_mode_no_auth():
"""
Test that the correct proxy addresses are returned when basic authentication is not used
"""
@ -295,7 +293,8 @@ class TestGetProxySettings(TestCase, TestMixin):
# 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):
def test_manual_proxy_mode_auth():
"""
Test that the correct proxy addresses are returned when basic authentication is used
"""
@ -313,7 +312,8 @@ class TestGetProxySettings(TestCase, TestMixin):
assert result == {'http': 'http://user:pass@testhttp.server:port',
'https': 'https://user:pass@testhttps.server:port'}
def test_manual_proxy_mode_no_servers(self):
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

View File

@ -23,32 +23,13 @@ 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):
"""
A test suite to test out various methods around the common __init__ class.
"""
def setUp(self):
"""
Create an instance and a few example actions.
"""
self.build_settings()
def tearDown(self):
"""
Clean up
"""
self.destroy_settings()
def test_add_actions_empty_list(self):
def test_add_actions_empty_list():
"""
Test that no actions are added when the list is empty
"""
@ -63,7 +44,8 @@ class TestInit(TestCase, TestMixin):
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(self):
def test_add_actions_none_action():
"""
Test that a separator is added when a None action is in the list
"""
@ -78,7 +60,8 @@ class TestInit(TestCase, TestMixin):
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(self):
def test_add_actions_add_action():
"""
Test that an action is added when a valid action is in the list
"""
@ -93,7 +76,8 @@ class TestInit(TestCase, TestMixin):
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):
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
"""
@ -108,7 +92,8 @@ class TestInit(TestCase, TestMixin):
mocked_target.addSeparator.assert_called_with()
mocked_target.addAction.assert_called_with('action')
def test_get_uno_instance_pipe(self):
def test_get_uno_instance_pipe():
"""
Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI
"""
@ -121,7 +106,8 @@ class TestInit(TestCase, TestMixin):
# 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(self):
def test_get_uno_instance_socket():
"""
Test that when the UNO connection type is other than "pipe" the resolver is given the "socket" URI
"""
@ -134,7 +120,8 @@ class TestInit(TestCase, TestMixin):
# 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(self):
def test_get_uno_command_libreoffice_command_exists():
"""
Test the ``get_uno_command`` function uses the libreoffice command when available.
:return:
@ -150,7 +137,8 @@ class TestInit(TestCase, TestMixin):
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_get_uno_command_only_soffice_command_exists(self):
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:
@ -168,7 +156,8 @@ class TestInit(TestCase, TestMixin):
assert result == 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_get_uno_command_when_no_command_exists(self):
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.
@ -180,9 +169,10 @@ class TestInit(TestCase, TestMixin):
# WHEN: Calling get_uno_command
# THEN: a FileNotFoundError exception should be raised
self.assertRaises(FileNotFoundError, get_uno_command)
assert FileNotFoundError, get_uno_command
def test_get_uno_command_connection_type(self):
def test_get_uno_command_connection_type():
"""
Test the ``get_uno_command`` function when the connection type is anything other than pipe.
:return:
@ -197,7 +187,8 @@ class TestInit(TestCase, TestMixin):
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=socket,host=localhost,port=2002;urp;"'
def test_get_filesystem_encoding_sys_function_not_called(self):
def test_get_filesystem_encoding_sys_function_not_called():
"""
Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function
"""
@ -214,7 +205,8 @@ class TestInit(TestCase, TestMixin):
assert mocked_getdefaultencoding.called == 0, 'getdefaultencoding should not have been called'
assert 'cp1252' == result, 'The result should be "cp1252"'
def test_get_filesystem_encoding_sys_function_is_called(self):
def test_get_filesystem_encoding_sys_function_is_called():
"""
Test the get_filesystem_encoding() function calls the sys.getdefaultencoding() function
"""
@ -232,7 +224,8 @@ class TestInit(TestCase, TestMixin):
mocked_getdefaultencoding.assert_called_with()
assert 'utf-8' == result, 'The result should be "utf-8"'
def test_clean_filename(self):
def test_clean_filename():
"""
Test the clean_filename() function
"""
@ -246,7 +239,8 @@ class TestInit(TestCase, TestMixin):
# THEN: The file name should be cleaned.
assert wanted_name == result, 'The file name should not contain any special characters.'
def test_delete_file_no_path(self):
def test_delete_file_no_path():
"""
Test the delete_file function when called with out a valid path
"""
@ -257,7 +251,8 @@ class TestInit(TestCase, TestMixin):
# THEN: delete_file should return False
assert result is False, "delete_file should return False when called with None"
def test_delete_file_path_success(self):
def test_delete_file_path_success():
"""
Test the delete_file function when it successfully deletes a file
"""
@ -270,7 +265,8 @@ class TestInit(TestCase, TestMixin):
# 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):
def test_delete_file_path_no_file_exists():
"""
Test the `delete_file` function when the file to remove does not exist
"""
@ -285,7 +281,8 @@ class TestInit(TestCase, TestMixin):
assert mocked_unlink.called is False
assert result is True, 'delete_file should return True when the file doesnt exist'
def test_delete_file_path_exception(self):
def test_delete_file_path_exception():
"""
Test the delete_file function when an exception is raised
"""
@ -302,7 +299,8 @@ class TestInit(TestCase, TestMixin):
assert mocked_log.exception.called
assert result is False, 'delete_file should return False when an OSError is raised'
def test_get_file_encoding_done(self):
def test_get_file_encoding_done():
"""
Test get_file_encoding when the detector sets done to True
"""
@ -323,7 +321,8 @@ class TestInit(TestCase, TestMixin):
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
def test_get_file_encoding_eof(self):
def test_get_file_encoding_eof():
"""
Test get_file_encoding when the end of the file is reached
"""
@ -345,7 +344,8 @@ class TestInit(TestCase, TestMixin):
mocked_universal_detector_inst.close.assert_called_once_with()
assert result == 'UTF-8'
def test_get_file_encoding_oserror(self):
def test_get_file_encoding_oserror():
"""
Test get_file_encoding when the end of the file is reached
"""

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())