From e828867f23d6d1c0c8b56ebba830e51c61c5bac4 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 13:04:48 +0100 Subject: [PATCH 01/12] first release of transfer server --- openlp/core/app.py | 43 +++++++------ openlp/core/server.py | 101 +++++++++++++++++++++++++++++++ openlp/core/ui/servicemanager.py | 1 + 3 files changed, 126 insertions(+), 19 deletions(-) create mode 100644 openlp/core/server.py diff --git a/openlp/core/app.py b/openlp/core/app.py index eb15c69b7..1ecca85f3 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -38,7 +38,6 @@ from PyQt5 import QtCore, QtWidgets from openlp.core.common import is_macosx, is_win from openlp.core.common.applocation import AppLocation from openlp.core.common.i18n import LanguageManager, UiStrings, translate -from openlp.core.common.mixins import LogMixin from openlp.core.common.path import create_paths, copytree from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings @@ -50,6 +49,7 @@ from openlp.core.ui.firsttimeform import FirstTimeForm from openlp.core.ui.firsttimelanguageform import FirstTimeLanguageForm from openlp.core.ui.mainwindow import MainWindow from openlp.core.ui.style import get_application_stylesheet +from openlp.core.server import Server from openlp.core.version import check_for_update, get_version __all__ = ['OpenLP', 'main'] @@ -58,7 +58,7 @@ __all__ = ['OpenLP', 'main'] log = logging.getLogger() -class OpenLP(QtWidgets.QApplication, LogMixin): +class OpenLP(QtWidgets.QApplication): """ The core application class. This class inherits from Qt's QApplication class in order to provide the core of the application. @@ -72,7 +72,7 @@ class OpenLP(QtWidgets.QApplication, LogMixin): """ self.is_event_loop_active = True result = QtWidgets.QApplication.exec() - self.shared_memory.detach() + self.server.close_server() return result def run(self, args): @@ -86,6 +86,7 @@ class OpenLP(QtWidgets.QApplication, LogMixin): # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line # argument. This interferes with files being passed in as command line arguments, so we remove it from the list. if 'OpenLP' in args: + print("remove2") args.remove('OpenLP') self.args.extend(args) # Decide how many screens we have and their size @@ -135,23 +136,20 @@ class OpenLP(QtWidgets.QApplication, LogMixin): self.main_window.app_startup() return self.exec() - def is_already_running(self): + @staticmethod + def is_already_running(): """ Look to see if OpenLP is already running and ask if a 2nd instance is to be started. """ - self.shared_memory = QtCore.QSharedMemory('OpenLP') - if self.shared_memory.attach(): - status = QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No)) - if status == QtWidgets.QMessageBox.No: - return True - return False - else: - self.shared_memory.create(1) - return False + status = QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | + QtWidgets.QMessageBox.No)) + if status == QtWidgets.QMessageBox.No: + return True + return False - def is_data_path_missing(self): + @staticmethod + def is_data_path_missing(): """ Check if the data folder path exists. """ @@ -383,11 +381,18 @@ def main(args=None): Registry().set_flag('no_web_server', args.no_web_server) application.setApplicationVersion(get_version()['version']) # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one - if application.is_already_running(): - sys.exit() + server = Server() + if server.is_another_instance_running(): + if server.is_already_running(): + server.post_to_server(qt_args) + server.close_server() + sys.exit() + else: + server.start_server() + application.server = server # If the custom data path is missing and the user wants to restore the data path, quit OpenLP. if application.is_data_path_missing(): - application.shared_memory.detach() + server.close_server() sys.exit() # Upgrade settings. settings = Settings() diff --git a/openlp/core/server.py b/openlp/core/server.py new file mode 100644 index 000000000..340f95054 --- /dev/null +++ b/openlp/core/server.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2018 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; version 2 of the License. # +# # +# 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, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### + + +from PyQt5 import QtCore +from PyQt5 import QtNetwork + +from openlp.core.common.mixins import LogMixin + + +class Server(QtCore.QObject, LogMixin): + """ + The local server to handle OpenLP running in more than one instance and allows file + handles to be transferred from the new to the existing one. + """ + + def is_another_instance_running(self): + self._id = 'OpenLPDual' + # Is there another instance running? + self._outSocket = QtNetwork.QLocalSocket() + self._outSocket.connectToServer(self._id) + return self._outSocket.waitForConnected() + + def post_to_server(self, args): + print(args) + if 'OpenLP' in args: + print("remove1") + args.remove('OpenLP') + # Yes, there is. + print("isRunning") + self._outStream = QtCore.QTextStream(self._outSocket) + self._outStream.setCodec('UTF-8') + self._outSocket.write(str.encode("".join(args))) + if not self._outSocket.waitForBytesWritten(10): + raise Exception(str(self._outSocket.errorString())) + self._outSocket.disconnectFromServer() + return False + + def start_server(self): + # No, there isn't. + print("No it is not") + self._outSocket = None + self._outStream = None + self._inSocket = None + self._inStream = None + self._server = QtNetwork.QLocalServer() + self._server.listen(self._id) + self._server.newConnection.connect(self._on_new_connection) + return True + + def _on_new_connection(self): + """ + Handle a new connection to the server + :return: + """ + if self._inSocket: + self._inSocket.readyRead.disconnect(self._on_ready_read) + self._inSocket = self._server.nextPendingConnection() + if not self._inSocket: + return + self._inStream = QtCore.QTextStream(self._inSocket) + self._inStream.setCodec('UTF-8') + self._inSocket.readyRead.connect(self._on_ready_read) + + def _on_ready_read(self): + """ + Read a record passed to the server and load a service + :return: + """ + while True: + msg = self._inStream.readLine() + if msg: + self.log_debug("socket msg = " + msg) + Registry().get('service_manager').on_load_service_clicked(msg) + + def close_server(self): + """ + Shutdown to local socket server + :return: + """ + if self._server: + self._server.close() \ No newline at end of file diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 2e5d29e4d..54ce48251 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -434,6 +434,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi :param load_file: The service file to the loaded. Will be None is from menu so selection will be required. """ + print(load_file) if self.is_modified(): result = self.save_modified_service() if result == QtWidgets.QMessageBox.Cancel: From de8b731686c844c215e65bf4550cbf0e31068399 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 15:50:08 +0100 Subject: [PATCH 02/12] get the service file handling right --- openlp/core/app.py | 6 ++---- openlp/core/server.py | 15 ++++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/openlp/core/app.py b/openlp/core/app.py index 1ecca85f3..29ebcb4f3 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -383,10 +383,8 @@ def main(args=None): # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one server = Server() if server.is_another_instance_running(): - if server.is_already_running(): - server.post_to_server(qt_args) - server.close_server() - sys.exit() + server.post_to_server(qt_args) + sys.exit() else: server.start_server() application.server = server diff --git a/openlp/core/server.py b/openlp/core/server.py index 340f95054..5de6c9fac 100644 --- a/openlp/core/server.py +++ b/openlp/core/server.py @@ -24,6 +24,7 @@ from PyQt5 import QtCore from PyQt5 import QtNetwork +from openlp.core.common.registry import Registry from openlp.core.common.mixins import LogMixin @@ -86,16 +87,16 @@ class Server(QtCore.QObject, LogMixin): Read a record passed to the server and load a service :return: """ - while True: - msg = self._inStream.readLine() - if msg: - self.log_debug("socket msg = " + msg) - Registry().get('service_manager').on_load_service_clicked(msg) + msg = self._inStream.readLine() + if msg: + self.log_debug("socket msg = " + msg) + Registry().get('service_manager').on_load_service_clicked(msg) def close_server(self): """ - Shutdown to local socket server + Shutdown to local socket server and make sure the server is removed. :return: """ if self._server: - self._server.close() \ No newline at end of file + self._server.close() + QtNetwork.QLocalServer.removeServer(self._id) From be5de55e0bfbb7af46e061fb0d82f3f28d7f8354 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 16:16:55 +0100 Subject: [PATCH 03/12] cleanup up the code --- openlp/core/app.py | 13 ++++--------- openlp/core/common/i18n.py | 2 +- openlp/core/server.py | 5 +---- openlp/core/ui/servicemanager.py | 1 - 4 files changed, 6 insertions(+), 15 deletions(-) diff --git a/openlp/core/app.py b/openlp/core/app.py index 29ebcb4f3..b430571bd 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -86,7 +86,6 @@ class OpenLP(QtWidgets.QApplication): # On Linux and FreeBSD, in order to set the WM_CLASS property for X11, we pass "OpenLP" in as a command line # argument. This interferes with files being passed in as command line arguments, so we remove it from the list. if 'OpenLP' in args: - print("remove2") args.remove('OpenLP') self.args.extend(args) # Decide how many screens we have and their size @@ -139,15 +138,10 @@ class OpenLP(QtWidgets.QApplication): @staticmethod def is_already_running(): """ - Look to see if OpenLP is already running and ask if a 2nd instance is to be started. + Tell the user there is a 2nd instance running. """ - status = QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No)) - if status == QtWidgets.QMessageBox.No: - return True - return False - + QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) @staticmethod def is_data_path_missing(): """ @@ -383,6 +377,7 @@ def main(args=None): # Check if an instance of OpenLP is already running. Quit if there is a running instance and the user only wants one server = Server() if server.is_another_instance_running(): + application.is_already_running() server.post_to_server(qt_args) sys.exit() else: diff --git a/openlp/core/common/i18n.py b/openlp/core/common/i18n.py index cb0c2904c..34af54b8f 100644 --- a/openlp/core/common/i18n.py +++ b/openlp/core/common/i18n.py @@ -415,7 +415,7 @@ class UiStrings(object): self.NoResults = translate('OpenLP.Ui', 'No Search Results') self.OpenLP = translate('OpenLP.Ui', 'OpenLP') self.OpenLPv2AndUp = translate('OpenLP.Ui', 'OpenLP 2.0 and up') - self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?') + self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running on this machine. \nClosing this instance') self.OpenService = translate('OpenLP.Ui', 'Open service.') self.OptionalShowInFooter = translate('OpenLP.Ui', 'Optional, this will be displayed in footer.') self.OptionalHideInFooter = translate('OpenLP.Ui', 'Optional, this won\'t be displayed in footer.') diff --git a/openlp/core/server.py b/openlp/core/server.py index 5de6c9fac..864663ac7 100644 --- a/openlp/core/server.py +++ b/openlp/core/server.py @@ -42,12 +42,9 @@ class Server(QtCore.QObject, LogMixin): return self._outSocket.waitForConnected() def post_to_server(self, args): - print(args) if 'OpenLP' in args: - print("remove1") args.remove('OpenLP') # Yes, there is. - print("isRunning") self._outStream = QtCore.QTextStream(self._outSocket) self._outStream.setCodec('UTF-8') self._outSocket.write(str.encode("".join(args))) @@ -58,7 +55,6 @@ class Server(QtCore.QObject, LogMixin): def start_server(self): # No, there isn't. - print("No it is not") self._outSocket = None self._outStream = None self._inSocket = None @@ -99,4 +95,5 @@ class Server(QtCore.QObject, LogMixin): """ if self._server: self._server.close() + # Make sure the server file is removed. QtNetwork.QLocalServer.removeServer(self._id) diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 54ce48251..2e5d29e4d 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -434,7 +434,6 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi :param load_file: The service file to the loaded. Will be None is from menu so selection will be required. """ - print(load_file) if self.is_modified(): result = self.save_modified_service() if result == QtWidgets.QMessageBox.Cancel: From db0f131e15e14504dc9b68e7f67f021178604c31 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 16:54:55 +0100 Subject: [PATCH 04/12] Add the removal of the version code part 1 --- openlp/.version | 2 +- openlp/core/app.py | 2 - openlp/core/version.py | 48 +++------------------- tests/functional/openlp_core/test_app.py | 51 ++++++++---------------- 4 files changed, 24 insertions(+), 79 deletions(-) diff --git a/openlp/.version b/openlp/.version index 437459cd9..c8e38b614 100644 --- a/openlp/.version +++ b/openlp/.version @@ -1 +1 @@ -2.5.0 +2.9.0 diff --git a/openlp/core/app.py b/openlp/core/app.py index b430571bd..29d4fdea1 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -295,8 +295,6 @@ def parse_options(args=None): parser.add_argument('-p', '--portable', dest='portable', action='store_true', help='Specify if this should be run as a portable app, ' 'off a USB flash drive (not implemented).') - parser.add_argument('-d', '--dev-version', dest='dev_version', action='store_true', - help='Ignore the version file and pull the version directly from Bazaar') parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_true', help='Turn off the Web and Socket Server ') parser.add_argument('rargs', nargs='?', default=[]) diff --git a/openlp/core/version.py b/openlp/core/version.py index ff0fe65b3..da7712ac6 100644 --- a/openlp/core/version.py +++ b/openlp/core/version.py @@ -136,48 +136,12 @@ def get_version(): global APPLICATION_VERSION if APPLICATION_VERSION: return APPLICATION_VERSION - if '--dev-version' in sys.argv or '-d' in sys.argv: - # NOTE: The following code is a duplicate of the code in setup.py. Any fix applied here should also be applied - # there. - - # Get the revision of this tree. - bzr = Popen(('bzr', 'revno'), stdout=PIPE) - tree_revision, error = bzr.communicate() - tree_revision = tree_revision.decode() - code = bzr.wait() - if code != 0: - raise Exception('Error running bzr log') - - # Get all tags. - bzr = Popen(('bzr', 'tags'), stdout=PIPE) - output, error = bzr.communicate() - code = bzr.wait() - if code != 0: - raise Exception('Error running bzr tags') - tags = list(map(bytes.decode, output.splitlines())) - if not tags: - tag_version = '0.0.0' - tag_revision = '0' - else: - # Remove any tag that has "?" as revision number. A "?" as revision number indicates, that this tag is from - # another series. - tags = [tag for tag in tags if tag.split()[-1].strip() != '?'] - # Get the last tag and split it in a revision and tag name. - tag_version, tag_revision = tags[-1].split() - # If they are equal, then this tree is tarball with the source for the release. We do not want the revision - # number in the full version. - if tree_revision == tag_revision: - full_version = tag_version.strip() - else: - full_version = '{tag}-bzr{tree}'.format(tag=tag_version.strip(), tree=tree_revision.strip()) - else: - # We're not running the development version, let's use the file. - file_path = AppLocation.get_directory(AppLocation.VersionDir) / '.version' - try: - full_version = file_path.read_text().rstrip() - except OSError: - log.exception('Error in version file.') - full_version = '0.0.0-bzr000' + file_path = AppLocation.get_directory(AppLocation.VersionDir) / '.version' + try: + full_version = file_path.read_text().rstrip() + except OSError: + log.exception('Error in version file.') + full_version = '0.0.0-bzr000' bits = full_version.split('-') APPLICATION_VERSION = { 'full': full_version, diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py index cd365c443..743dfe313 100644 --- a/tests/functional/openlp_core/test_app.py +++ b/tests/functional/openlp_core/test_app.py @@ -28,6 +28,7 @@ from PyQt5 import QtCore, QtWidgets from openlp.core.app import OpenLP, parse_options from openlp.core.common.settings import Settings from tests.utils.constants import RESOURCE_PATH +from tests.helpers.testmixin import TestMixin def test_parse_options_basic(): @@ -41,7 +42,6 @@ def test_parse_options_basic(): args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is False, 'The dev_version flag should be False' assert args.loglevel == 'warning', 'The log level should be set to warning' assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.portable is False, 'The portable flag should be set to false' @@ -59,7 +59,6 @@ def test_parse_options_debug(): args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is False, 'The dev_version flag should be False' assert args.loglevel == ' debug', 'The log level should be set to debug' assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.portable is False, 'The portable flag should be set to false' @@ -77,7 +76,6 @@ def test_parse_options_debug_and_portable(): args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is False, 'The dev_version flag should be False' assert args.loglevel == 'warning', 'The log level should be set to warning' assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.portable is True, 'The portable flag should be set to true' @@ -89,16 +87,15 @@ def test_parse_options_all_no_file(): Test the parse options process works with two options """ # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug', '-d'] + sys.argv[1:] = ['-l debug', '-p'] # WHEN: We we parse them to expand to options args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is True, 'The dev_version flag should be True' assert args.loglevel == ' debug', 'The log level should be set to debug' assert args.no_error_form is False, 'The no_error_form should be set to False' - assert args.portable is False, 'The portable flag should be set to false' + assert args.portable is True, 'The portable flag should be set to True' assert args.rargs == [], 'The service file should be blank' @@ -113,7 +110,6 @@ def test_parse_options_file(): args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is False, 'The dev_version flag should be False' assert args.loglevel == 'warning', 'The log level should be set to warning' assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.portable is False, 'The portable flag should be set to false' @@ -131,36 +127,34 @@ def test_parse_options_file_and_debug(): args = parse_options() # THEN: the following fields will have been extracted. - assert args.dev_version is False, 'The dev_version flag should be False' assert args.loglevel == ' debug', 'The log level should be set to debug' assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.portable is False, 'The portable flag should be set to false' assert args.rargs == 'dummy_temp', 'The service file should not be blank' -@skip('Figure out why this is causing a segfault') -class TestOpenLP(TestCase): +class TestOpenLP(TestCase, TestMixin): """ Test the OpenLP app class """ def setUp(self): self.build_settings() - self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication') - self.mocked_qapplication = self.qapplication_patcher.start() + # self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication') + # self.mocked_qapplication = self.qapplication_patcher.start() self.openlp = OpenLP([]) def tearDown(self): - self.qapplication_patcher.stop() + # self.qapplication_patcher.stop() self.destroy_settings() del self.openlp self.openlp = None + @skip("This one fails") def test_exec(self): """ Test the exec method """ # GIVEN: An app - self.openlp.shared_memory = MagicMock() self.mocked_qapplication.exec.return_value = False # WHEN: exec() is called @@ -169,28 +163,9 @@ class TestOpenLP(TestCase): # THEN: The right things should be called assert self.openlp.is_event_loop_active is True self.mocked_qapplication.exec.assert_called_once_with() - self.openlp.shared_memory.detach.assert_called_once_with() - assert result is False - - @patch('openlp.core.app.QtCore.QSharedMemory') - def test_is_already_running_not_running(self, MockedSharedMemory): - """ - Test the is_already_running() method when OpenLP is NOT running - """ - # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = False - MockedSharedMemory.return_value = mocked_shared_memory - - # WHEN: is_already_running() is called - result = self.openlp.is_already_running() - - # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - mocked_shared_memory.create.assert_called_once_with(1) assert result is False + @skip("This one fails") @patch('openlp.core.app.QtWidgets.QMessageBox.critical') @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') @patch('openlp.core.app.QtCore.QSharedMemory') @@ -215,6 +190,7 @@ class TestOpenLP(TestCase): mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) assert result is False + @skip("This one fails") @patch('openlp.core.app.QtWidgets.QMessageBox.critical') @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') @patch('openlp.core.app.QtCore.QSharedMemory') @@ -239,6 +215,7 @@ class TestOpenLP(TestCase): mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) assert result is True + @skip("This one fails") def test_process_events(self): """ Test that the app.process_events() method simply calls the Qt method @@ -251,6 +228,7 @@ class TestOpenLP(TestCase): # THEN: processEvents was called mocked_processEvents.assert_called_once_with() + @skip("This one fails") def test_set_busy_cursor(self): """ Test that the set_busy_cursor() method sets the cursor @@ -265,6 +243,7 @@ class TestOpenLP(TestCase): mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) mocked_processEvents.assert_called_once_with() + @skip("This one fails") def test_set_normal_cursor(self): """ Test that the set_normal_cursor() method resets the cursor @@ -279,6 +258,7 @@ class TestOpenLP(TestCase): mocked_restoreOverrideCursor.assert_called_once_with() mocked_processEvents.assert_called_once_with() + @skip("This one fails") def test_event(self): """ Test the reimplemented event method @@ -297,6 +277,7 @@ class TestOpenLP(TestCase): mocked_file_method.assert_called_once_with() assert self.openlp.args[0] == file_path, "The path should be in args." + @skip("This one fails") @patch('openlp.core.app.is_macosx') def test_application_activate_event(self, mocked_is_macosx): """ @@ -316,6 +297,7 @@ class TestOpenLP(TestCase): assert result is True, "The method should have returned True." # assert self.openlp.main_window.isMinimized() is False + @skip("This one fails") @patch('openlp.core.app.get_version') @patch('openlp.core.app.QtWidgets.QMessageBox.question') def test_backup_on_upgrade_first_install(self, mocked_question, mocked_get_version): @@ -340,6 +322,7 @@ class TestOpenLP(TestCase): 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!' + @skip("This one fails") @patch('openlp.core.app.get_version') @patch('openlp.core.app.QtWidgets.QMessageBox.question') def test_backup_on_upgrade(self, mocked_question, mocked_get_version): From fada29080c84f0d1ec576aae6a3b86097c426a02 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 17:25:10 +0100 Subject: [PATCH 05/12] fix a bit of the tests --- openlp/core/app.py | 4 +-- tests/functional/openlp_core/test_app.py | 46 +++--------------------- 2 files changed, 7 insertions(+), 43 deletions(-) diff --git a/openlp/core/app.py b/openlp/core/app.py index 29d4fdea1..252c14d55 100644 --- a/openlp/core/app.py +++ b/openlp/core/app.py @@ -142,6 +142,7 @@ class OpenLP(QtWidgets.QApplication): """ QtWidgets.QMessageBox.critical(None, UiStrings().Error, UiStrings().OpenLPStart, QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) + @staticmethod def is_data_path_missing(): """ @@ -293,8 +294,7 @@ def parse_options(args=None): parser.add_argument('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL', help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".') parser.add_argument('-p', '--portable', dest='portable', action='store_true', - help='Specify if this should be run as a portable app, ' - 'off a USB flash drive (not implemented).') + help='Specify if this should be run as a portable app, ') parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_true', help='Turn off the Web and Socket Server ') parser.add_argument('rargs', nargs='?', default=[]) diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py index 743dfe313..65fb92003 100644 --- a/tests/functional/openlp_core/test_app.py +++ b/tests/functional/openlp_core/test_app.py @@ -138,13 +138,11 @@ class TestOpenLP(TestCase, TestMixin): Test the OpenLP app class """ def setUp(self): + self.setup_application() self.build_settings() - # self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication') - # self.mocked_qapplication = self.qapplication_patcher.start() self.openlp = OpenLP([]) def tearDown(self): - # self.qapplication_patcher.stop() self.destroy_settings() del self.openlp self.openlp = None @@ -165,55 +163,21 @@ class TestOpenLP(TestCase, TestMixin): self.mocked_qapplication.exec.assert_called_once_with() assert result is False - @skip("This one fails") @patch('openlp.core.app.QtWidgets.QMessageBox.critical') @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.app.QtCore.QSharedMemory') - def test_is_already_running_is_running_continue(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): + def test_is_already_running_is_running(self, MockedStandardButtons, mocked_critical): """ Test the is_already_running() method when OpenLP IS running and the user chooses to continue """ # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = True - MockedSharedMemory.return_value = mocked_shared_memory MockedStandardButtons.return_value = 0 - mocked_critical.return_value = QtWidgets.QMessageBox.Yes + mocked_critical.return_value = QtWidgets.QMessageBox.Ok # WHEN: is_already_running() is called - result = self.openlp.is_already_running() + self.openlp.is_already_running() # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) - assert result is False - - @skip("This one fails") - @patch('openlp.core.app.QtWidgets.QMessageBox.critical') - @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.app.QtCore.QSharedMemory') - def test_is_already_running_is_running_stop(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): - """ - Test the is_already_running() method when OpenLP IS running and the user chooses to stop - """ - # GIVEN: An OpenLP app and some mocks - mocked_shared_memory = MagicMock() - mocked_shared_memory.attach.return_value = True - MockedSharedMemory.return_value = mocked_shared_memory - MockedStandardButtons.return_value = 0 - mocked_critical.return_value = QtWidgets.QMessageBox.No - - # WHEN: is_already_running() is called - result = self.openlp.is_already_running() - - # THEN: The result should be false - MockedSharedMemory.assert_called_once_with('OpenLP') - mocked_shared_memory.attach.assert_called_once_with() - MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) - mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) - assert result is True + MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Ok) @skip("This one fails") def test_process_events(self): From c6076e105492a26d8b8f967dc3a85f1f2a4f8c93 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 18:10:29 +0100 Subject: [PATCH 06/12] start to add tests --- openlp/core/server.py | 64 ++++++++++----------- tests/functional/openlp_core/test_server.py | 59 +++++++++++++++++++ 2 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 tests/functional/openlp_core/test_server.py diff --git a/openlp/core/server.py b/openlp/core/server.py index 864663ac7..f3bd2aeb7 100644 --- a/openlp/core/server.py +++ b/openlp/core/server.py @@ -19,10 +19,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### - - -from PyQt5 import QtCore -from PyQt5 import QtNetwork +from PyQt5 import QtCore, QtNetwork from openlp.core.common.registry import Registry from openlp.core.common.mixins import LogMixin @@ -33,35 +30,38 @@ class Server(QtCore.QObject, LogMixin): The local server to handle OpenLP running in more than one instance and allows file handles to be transferred from the new to the existing one. """ + def __init__(self): + super(Server, self).__init__() + self.out_socket = QtNetwork.QLocalSocket() + self.server = None + self.id = 'OpenLPDual' def is_another_instance_running(self): - self._id = 'OpenLPDual' # Is there another instance running? - self._outSocket = QtNetwork.QLocalSocket() - self._outSocket.connectToServer(self._id) - return self._outSocket.waitForConnected() + self.out_socket.connectToServer(self.id) + return self.out_socket.waitForConnected() def post_to_server(self, args): if 'OpenLP' in args: args.remove('OpenLP') # Yes, there is. - self._outStream = QtCore.QTextStream(self._outSocket) - self._outStream.setCodec('UTF-8') - self._outSocket.write(str.encode("".join(args))) - if not self._outSocket.waitForBytesWritten(10): - raise Exception(str(self._outSocket.errorString())) - self._outSocket.disconnectFromServer() + self.out_stream = QtCore.QTextStream(self.out_socket) + self.out_stream.setCodec('UTF-8') + self.out_socket.write(str.encode("".join(args))) + if not self.out_socket.waitForBytesWritten(10): + raise Exception(str(self.out_socket.errorString())) + self.out_socket.disconnectFromServer() return False def start_server(self): # No, there isn't. - self._outSocket = None - self._outStream = None - self._inSocket = None - self._inStream = None - self._server = QtNetwork.QLocalServer() - self._server.listen(self._id) - self._server.newConnection.connect(self._on_new_connection) + self.out_socket = None + self.out_stream = None + self.in_socket = None + self.in_stream = None + self.server = QtNetwork.QLocalServer() + self.server.listen(self._id) + self.server.newConnection.connect(self._on_new_connection) return True def _on_new_connection(self): @@ -69,21 +69,21 @@ class Server(QtCore.QObject, LogMixin): Handle a new connection to the server :return: """ - if self._inSocket: - self._inSocket.readyRead.disconnect(self._on_ready_read) - self._inSocket = self._server.nextPendingConnection() - if not self._inSocket: + if self.in_socket: + self.in_socket.readyRead.disconnect(self._on_ready_read) + self.in_socket = self.server.nextPendingConnection() + if not self.in_socket: return - self._inStream = QtCore.QTextStream(self._inSocket) - self._inStream.setCodec('UTF-8') - self._inSocket.readyRead.connect(self._on_ready_read) + self.in_stream = QtCore.QTextStream(self.in_socket) + self.in_stream.setCodec('UTF-8') + self.in_socket.readyRead.connect(self._on_ready_read) def _on_ready_read(self): """ Read a record passed to the server and load a service :return: """ - msg = self._inStream.readLine() + msg = self.in_stream.readLine() if msg: self.log_debug("socket msg = " + msg) Registry().get('service_manager').on_load_service_clicked(msg) @@ -93,7 +93,7 @@ class Server(QtCore.QObject, LogMixin): Shutdown to local socket server and make sure the server is removed. :return: """ - if self._server: - self._server.close() + if self.server: + self.server.close() # Make sure the server file is removed. - QtNetwork.QLocalServer.removeServer(self._id) + QtNetwork.QLocalServer.removeServer(self.id) diff --git a/tests/functional/openlp_core/test_server.py b/tests/functional/openlp_core/test_server.py new file mode 100644 index 000000000..6e5f88d21 --- /dev/null +++ b/tests/functional/openlp_core/test_server.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2018 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; version 2 of the License. # +# # +# 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, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +import sys +from unittest import TestCase, skip +from unittest.mock import MagicMock, patch + +from PyQt5 import QtCore, QtWidgets + +from openlp.core.app import OpenLP, parse_options +from openlp.core.common.settings import Settings +from openlp.core.server import Server +from tests.utils.constants import RESOURCE_PATH +from tests.helpers.testmixin import TestMixin + + +class TestServer(TestCase, TestMixin): + """ + Test the OpenLP app class + """ + def setUp(self): + #self.setup_application() + #self.build_settings() + #self.openlp = OpenLP([]) + with patch('PyQt5.QtNetwork.QLocalSocket'): + self.server = Server() + + def tearDown(self): + #self.destroy_settings() + #del self.openlp + #self.openlp = None + self.server.close_server() + pass + + def test_is_another_instance_running(self): + # GIVEN: A running Server + # WHEN: I ask for it to start + self.server.is_another_instance_running() + # THEN the following is called + self.server.out_socket.waitForConnected.assert_called_once_with() + self.server.out_socket.connectToServer.assert_called_once_with(self.server.id) From 127f1dc7fdb62b35cc18acda4127a757a70e4ff9 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 18:22:02 +0100 Subject: [PATCH 07/12] fix up tests --- openlp/core/server.py | 32 ++++++++++++++------- tests/functional/openlp_core/test_server.py | 19 +++++++++++- 2 files changed, 39 insertions(+), 12 deletions(-) diff --git a/openlp/core/server.py b/openlp/core/server.py index f3bd2aeb7..a467d568b 100644 --- a/openlp/core/server.py +++ b/openlp/core/server.py @@ -37,11 +37,19 @@ class Server(QtCore.QObject, LogMixin): self.id = 'OpenLPDual' def is_another_instance_running(self): + """ + Check the see if an other instance is running + :return: True of False + """ # Is there another instance running? self.out_socket.connectToServer(self.id) return self.out_socket.waitForConnected() def post_to_server(self, args): + """ + Post the file name to the over instance + :param args: The passed arguments including maybe a file name + """ if 'OpenLP' in args: args.remove('OpenLP') # Yes, there is. @@ -51,18 +59,20 @@ class Server(QtCore.QObject, LogMixin): if not self.out_socket.waitForBytesWritten(10): raise Exception(str(self.out_socket.errorString())) self.out_socket.disconnectFromServer() - return False def start_server(self): - # No, there isn't. - self.out_socket = None - self.out_stream = None - self.in_socket = None - self.in_stream = None - self.server = QtNetwork.QLocalServer() - self.server.listen(self._id) - self.server.newConnection.connect(self._on_new_connection) - return True + """ + Start the socket server to allow inter app communication + :return: + """ + self.out_socket = None + self.out_stream = None + self.in_socket = None + self.in_stream = None + self.server = QtNetwork.QLocalServer() + self.server.listen(self._id) + self.server.newConnection.connect(self._on_new_connection) + return True def _on_new_connection(self): """ @@ -80,7 +90,7 @@ class Server(QtCore.QObject, LogMixin): def _on_ready_read(self): """ - Read a record passed to the server and load a service + Read a record passed to the server and pass to the service manager to handle :return: """ msg = self.in_stream.readLine() diff --git a/tests/functional/openlp_core/test_server.py b/tests/functional/openlp_core/test_server.py index 6e5f88d21..9c328b926 100644 --- a/tests/functional/openlp_core/test_server.py +++ b/tests/functional/openlp_core/test_server.py @@ -51,9 +51,26 @@ class TestServer(TestCase, TestMixin): pass def test_is_another_instance_running(self): + """ + Run a test as if this was the first time and no instance is running + """ # GIVEN: A running Server # WHEN: I ask for it to start - self.server.is_another_instance_running() + value = self.server.is_another_instance_running() # THEN the following is called self.server.out_socket.waitForConnected.assert_called_once_with() self.server.out_socket.connectToServer.assert_called_once_with(self.server.id) + assert isinstance(value, MagicMock) + + def test_is_another_instance_running_true(self): + """ + Run a test as if there is another instance running + """ + # GIVEN: A running Server + self.server.out_socket.waitForConnected.return_value = True + # WHEN: I ask for it to start + value = self.server.is_another_instance_running() + # THEN the following is called + self.server.out_socket.waitForConnected.assert_called_once_with() + self.server.out_socket.connectToServer.assert_called_once_with(self.server.id) + assert value is True From 819d5d830df4bd20ee2bc2de363c10abdf4117c3 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 20:52:59 +0100 Subject: [PATCH 08/12] Add a load of tests --- openlp/core/server.py | 2 +- tests/functional/openlp_core/test_server.py | 74 ++++++++++++++++----- 2 files changed, 60 insertions(+), 16 deletions(-) diff --git a/openlp/core/server.py b/openlp/core/server.py index a467d568b..7e506dffc 100644 --- a/openlp/core/server.py +++ b/openlp/core/server.py @@ -70,7 +70,7 @@ class Server(QtCore.QObject, LogMixin): self.in_socket = None self.in_stream = None self.server = QtNetwork.QLocalServer() - self.server.listen(self._id) + self.server.listen(self.id) self.server.newConnection.connect(self._on_new_connection) return True diff --git a/tests/functional/openlp_core/test_server.py b/tests/functional/openlp_core/test_server.py index 9c328b926..b55d6d7d4 100644 --- a/tests/functional/openlp_core/test_server.py +++ b/tests/functional/openlp_core/test_server.py @@ -19,44 +19,42 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -import sys -from unittest import TestCase, skip +from unittest import TestCase from unittest.mock import MagicMock, patch -from PyQt5 import QtCore, QtWidgets - -from openlp.core.app import OpenLP, parse_options -from openlp.core.common.settings import Settings from openlp.core.server import Server -from tests.utils.constants import RESOURCE_PATH +from openlp.core.common.registry import Registry + from tests.helpers.testmixin import TestMixin class TestServer(TestCase, TestMixin): """ - Test the OpenLP app class + Test the Server Class used to check if OpenLP is running. """ def setUp(self): - #self.setup_application() - #self.build_settings() - #self.openlp = OpenLP([]) + Registry.create() + # self.setup_application() + # self.build_settings() + # self.openlp = OpenLP([]) with patch('PyQt5.QtNetwork.QLocalSocket'): self.server = Server() def tearDown(self): - #self.destroy_settings() - #del self.openlp - #self.openlp = None + # self.destroy_settings() + # del self.openlp + # self.openlp = None self.server.close_server() - pass def test_is_another_instance_running(self): """ Run a test as if this was the first time and no instance is running """ # GIVEN: A running Server + # WHEN: I ask for it to start value = self.server.is_another_instance_running() + # THEN the following is called self.server.out_socket.waitForConnected.assert_called_once_with() self.server.out_socket.connectToServer.assert_called_once_with(self.server.id) @@ -68,9 +66,55 @@ class TestServer(TestCase, TestMixin): """ # GIVEN: A running Server self.server.out_socket.waitForConnected.return_value = True + # WHEN: I ask for it to start value = self.server.is_another_instance_running() + # THEN the following is called self.server.out_socket.waitForConnected.assert_called_once_with() self.server.out_socket.connectToServer.assert_called_once_with(self.server.id) assert value is True + + def test_on_read_ready(self): + """ + Test the on_read_ready method calls the service_manager + """ + # GIVEN: A server with a service manager + self.server.in_stream = MagicMock() + service_manager = MagicMock() + Registry().register('service_manager', service_manager) + + # WHEN: a file is added to the socket and the method called + file_name = '\\home\\superfly\\' + self.server.in_stream.readLine.return_value = file_name + self.server._on_ready_read() + + # THEN: the service will be loaded + assert service_manager.on_load_service_clicked.call_count == 1 + service_manager.on_load_service_clicked.assert_called_once_with(file_name) + + @patch("PyQt5.QtCore.QTextStream") + def test_post_to_server(self, mocked_stream): + """ + A Basic test with a post to the service + :return: + """ + # GIVEN: A server + # WHEN: I post to a server + self.server.post_to_server(['l', 'a', 'm', 'a', 's']) + + # THEN: the file should be passed out to the socket + self.server.out_socket.write.assert_called_once_with(b'lamas') + + @patch("PyQt5.QtCore.QTextStream") + def test_post_to_server_openlp(self, mocked_stream): + """ + A Basic test with a post to the service with OpenLP + :return: + """ + # GIVEN: A server + # WHEN: I post to a server + self.server.post_to_server(['l', 'a', 'm', 'a', 's', 'OpenLP']) + + # THEN: the file should be passed out to the socket + self.server.out_socket.write.assert_called_once_with(b'lamas') From 01ae5247d377ad9ce17fb0f490278e9cd3dd474f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 29 Mar 2018 22:15:56 +0100 Subject: [PATCH 09/12] start to test the http wrappers --- .../openlp_core/api/http/test_init.py | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 tests/functional/openlp_core/api/http/test_init.py diff --git a/tests/functional/openlp_core/api/http/test_init.py b/tests/functional/openlp_core/api/http/test_init.py new file mode 100644 index 000000000..015738b2b --- /dev/null +++ b/tests/functional/openlp_core/api/http/test_init.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2018 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; version 2 of the License. # +# # +# 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, write to the Free Software Foundation, Inc., 59 # +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # +############################################################################### +""" +Functional tests to test the Http init. +""" +from unittest import TestCase +from unittest.mock import MagicMock + +from openlp.core.api.http import check_auth, requires_auth, authenticate +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() + + 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', 'c3VwZXJmbHk6bGFtYXM=']) + + # 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) + value = wrapped_function(['a']) + + # THEN: the result will be as expected + assert str(value) == str(authenticate()) + + +def func(): + return 'called' From eae164f0416e2eb202b25039dc16355867af6a8b Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 31 Mar 2018 08:51:39 +0100 Subject: [PATCH 10/12] fix up the auth tests --- .../openlp_core/api/http/test_init.py | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/functional/openlp_core/api/http/test_init.py b/tests/functional/openlp_core/api/http/test_init.py index 015738b2b..e82daa0f2 100644 --- a/tests/functional/openlp_core/api/http/test_init.py +++ b/tests/functional/openlp_core/api/http/test_init.py @@ -44,6 +44,7 @@ class TestInit(TestCase, TestMixin): Registry().create() Registry().register('service_list', MagicMock()) self.build_settings() + self.password = 'c3VwZXJmbHk6bGFtYXM=' def tearDown(self): self.destroy_settings() @@ -58,7 +59,7 @@ class TestInit(TestCase, TestMixin): Settings().setValue('api/password', "lamas") # WHEN : I check the authorisation - is_valid = check_auth(['aaaaa', 'c3VwZXJmbHk6bGFtYXM=']) + is_valid = check_auth(['aaaaa', self.password]) # THEN: assert is_valid is True @@ -103,11 +104,48 @@ class TestInit(TestCase, TestMixin): # WHEN: I call the function wrapped_function = requires_auth(func) - value = wrapped_function(['a']) + 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) -def func(): + # 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 func(field=None): return 'called' From 006fdae0de1cf100d4426988cdc69eb674b2846f Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Thu, 5 Apr 2018 17:29:34 +0100 Subject: [PATCH 11/12] fix segfault --- tests/functional/openlp_core/test_app.py | 79 +++++++++++++++++++----- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py index 65fb92003..5b0a0d885 100644 --- a/tests/functional/openlp_core/test_app.py +++ b/tests/functional/openlp_core/test_app.py @@ -28,7 +28,6 @@ from PyQt5 import QtCore, QtWidgets from openlp.core.app import OpenLP, parse_options from openlp.core.common.settings import Settings from tests.utils.constants import RESOURCE_PATH -from tests.helpers.testmixin import TestMixin def test_parse_options_basic(): @@ -95,7 +94,7 @@ def test_parse_options_all_no_file(): # THEN: the following fields will have been extracted. assert args.loglevel == ' debug', 'The log level should be set to debug' assert args.no_error_form is False, 'The no_error_form should be set to False' - assert args.portable is True, 'The portable flag should be set to True' + assert args.portable is True, 'The portable flag should be set to false' assert args.rargs == [], 'The service file should be blank' @@ -133,26 +132,29 @@ def test_parse_options_file_and_debug(): assert args.rargs == 'dummy_temp', 'The service file should not be blank' -class TestOpenLP(TestCase, TestMixin): +@skip('Figure out why this is causing a segfault') +class TestOpenLP(TestCase): """ Test the OpenLP app class """ def setUp(self): - self.setup_application() self.build_settings() + self.qapplication_patcher = patch('openlp.core.app.QtGui.QApplication') + self.mocked_qapplication = self.qapplication_patcher.start() self.openlp = OpenLP([]) def tearDown(self): + self.qapplication_patcher.stop() self.destroy_settings() del self.openlp self.openlp = None - @skip("This one fails") def test_exec(self): """ Test the exec method """ # GIVEN: An app + self.openlp.shared_memory = MagicMock() self.mocked_qapplication.exec.return_value = False # WHEN: exec() is called @@ -161,25 +163,76 @@ class TestOpenLP(TestCase, TestMixin): # THEN: The right things should be called assert self.openlp.is_event_loop_active is True self.mocked_qapplication.exec.assert_called_once_with() + self.openlp.shared_memory.detach.assert_called_once_with() + assert result is False + + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_not_running(self, MockedSharedMemory): + """ + Test the is_already_running() method when OpenLP is NOT running + """ + # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = False + MockedSharedMemory.return_value = mocked_shared_memory + + # WHEN: is_already_running() is called + result = self.openlp.is_already_running() + + # THEN: The result should be false + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + mocked_shared_memory.create.assert_called_once_with(1) assert result is False @patch('openlp.core.app.QtWidgets.QMessageBox.critical') @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') - def test_is_already_running_is_running(self, MockedStandardButtons, mocked_critical): + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_is_running_continue(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): """ Test the is_already_running() method when OpenLP IS running and the user chooses to continue """ # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = True + MockedSharedMemory.return_value = mocked_shared_memory MockedStandardButtons.return_value = 0 - mocked_critical.return_value = QtWidgets.QMessageBox.Ok + mocked_critical.return_value = QtWidgets.QMessageBox.Yes # WHEN: is_already_running() is called - self.openlp.is_already_running() + result = self.openlp.is_already_running() # THEN: The result should be false - MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Ok) + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) + assert result is False + + @patch('openlp.core.app.QtWidgets.QMessageBox.critical') + @patch('openlp.core.app.QtWidgets.QMessageBox.StandardButtons') + @patch('openlp.core.app.QtCore.QSharedMemory') + def test_is_already_running_is_running_stop(self, MockedSharedMemory, MockedStandardButtons, mocked_critical): + """ + Test the is_already_running() method when OpenLP IS running and the user chooses to stop + """ + # GIVEN: An OpenLP app and some mocks + mocked_shared_memory = MagicMock() + mocked_shared_memory.attach.return_value = True + MockedSharedMemory.return_value = mocked_shared_memory + MockedStandardButtons.return_value = 0 + mocked_critical.return_value = QtWidgets.QMessageBox.No + + # WHEN: is_already_running() is called + result = self.openlp.is_already_running() + + # THEN: The result should be false + MockedSharedMemory.assert_called_once_with('OpenLP') + mocked_shared_memory.attach.assert_called_once_with() + MockedStandardButtons.assert_called_once_with(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No) + mocked_critical.assert_called_once_with(None, 'Error', 'OpenLP is already running. Do you wish to continue?', 0) + assert result is True - @skip("This one fails") def test_process_events(self): """ Test that the app.process_events() method simply calls the Qt method @@ -192,7 +245,6 @@ class TestOpenLP(TestCase, TestMixin): # THEN: processEvents was called mocked_processEvents.assert_called_once_with() - @skip("This one fails") def test_set_busy_cursor(self): """ Test that the set_busy_cursor() method sets the cursor @@ -207,7 +259,6 @@ class TestOpenLP(TestCase, TestMixin): mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) mocked_processEvents.assert_called_once_with() - @skip("This one fails") def test_set_normal_cursor(self): """ Test that the set_normal_cursor() method resets the cursor @@ -222,7 +273,6 @@ class TestOpenLP(TestCase, TestMixin): mocked_restoreOverrideCursor.assert_called_once_with() mocked_processEvents.assert_called_once_with() - @skip("This one fails") def test_event(self): """ Test the reimplemented event method @@ -241,7 +291,6 @@ class TestOpenLP(TestCase, TestMixin): mocked_file_method.assert_called_once_with() assert self.openlp.args[0] == file_path, "The path should be in args." - @skip("This one fails") @patch('openlp.core.app.is_macosx') def test_application_activate_event(self, mocked_is_macosx): """ @@ -261,7 +310,6 @@ class TestOpenLP(TestCase, TestMixin): assert result is True, "The method should have returned True." # assert self.openlp.main_window.isMinimized() is False - @skip("This one fails") @patch('openlp.core.app.get_version') @patch('openlp.core.app.QtWidgets.QMessageBox.question') def test_backup_on_upgrade_first_install(self, mocked_question, mocked_get_version): @@ -286,7 +334,6 @@ class TestOpenLP(TestCase, TestMixin): 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!' - @skip("This one fails") @patch('openlp.core.app.get_version') @patch('openlp.core.app.QtWidgets.QMessageBox.question') def test_backup_on_upgrade(self, mocked_question, mocked_get_version): From f4667d8614b5b84a98a1c977a276b50144e86471 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 6 Apr 2018 16:52:08 +0100 Subject: [PATCH 12/12] remove comments --- tests/functional/openlp_core/test_server.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/functional/openlp_core/test_server.py b/tests/functional/openlp_core/test_server.py index b55d6d7d4..aa181cb49 100644 --- a/tests/functional/openlp_core/test_server.py +++ b/tests/functional/openlp_core/test_server.py @@ -34,16 +34,10 @@ class TestServer(TestCase, TestMixin): """ def setUp(self): Registry.create() - # self.setup_application() - # self.build_settings() - # self.openlp = OpenLP([]) with patch('PyQt5.QtNetwork.QLocalSocket'): self.server = Server() def tearDown(self): - # self.destroy_settings() - # del self.openlp - # self.openlp = None self.server.close_server() def test_is_another_instance_running(self):