diff --git a/.bzrignore b/.bzrignore index c7abfd456..01c96078a 100644 --- a/.bzrignore +++ b/.bzrignore @@ -44,3 +44,4 @@ coverage tags output htmlcov +openlp-test-projectordb.sqlite diff --git a/openlp.py b/openlp.py index b367faf07..02287cd5e 100755 --- a/openlp.py +++ b/openlp.py @@ -27,7 +27,7 @@ import faulthandler import multiprocessing import sys -from openlp.core import main +from openlp.core.app import main from openlp.core.common import is_win, is_macosx from openlp.core.common.applocation import AppLocation from openlp.core.common.path import create_paths diff --git a/openlp/__init__.py b/openlp/__init__.py index 16d57e9af..2074ec39a 100644 --- a/openlp/__init__.py +++ b/openlp/__init__.py @@ -22,7 +22,3 @@ """ The :mod:`openlp` module contains all the project produced OpenLP functionality """ - -from openlp import core, plugins - -__all__ = ['core', 'plugins'] diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 9e0a352e4..b3fa9b6a6 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -26,400 +26,3 @@ The :mod:`core` module provides all core application functions All the core functions of the OpenLP application including the GUI, settings, logging and a plugin framework are contained within the openlp.core module. """ -import argparse -import logging -import sys -import time -from datetime import datetime -from traceback import format_exception - -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 OpenLPMixin -from openlp.core.common.path import create_paths, copytree -from openlp.core.common.registry import Registry -from openlp.core.common.settings import Settings -from openlp.core.version import check_for_update, get_version -from openlp.core.lib import ScreenList -from openlp.core.resources import qInitResources -from openlp.core.ui import SplashScreen -from openlp.core.ui.exceptionform import ExceptionForm -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 - - -__all__ = ['OpenLP', 'main'] - - -log = logging.getLogger() - - -class OpenLP(OpenLPMixin, QtWidgets.QApplication): - """ - The core application class. This class inherits from Qt's QApplication - class in order to provide the core of the application. - """ - - args = [] - - def exec(self): - """ - Override exec method to allow the shared memory to be released on exit - """ - self.is_event_loop_active = True - result = QtWidgets.QApplication.exec() - self.shared_memory.detach() - return result - - def run(self, args): - """ - Run the OpenLP application. - - :param args: Some Args - """ - self.is_event_loop_active = False - # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use. - # 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: - args.remove('OpenLP') - self.args.extend(args) - # Decide how many screens we have and their size - screens = ScreenList.create(self.desktop()) - # First time checks in settings - has_run_wizard = Settings().value('core/has run wizard') - if not has_run_wizard: - ftw = FirstTimeForm() - ftw.initialize(screens) - if ftw.exec() == QtWidgets.QDialog.Accepted: - Settings().setValue('core/has run wizard', True) - elif ftw.was_cancelled: - QtCore.QCoreApplication.exit() - sys.exit() - # Correct stylesheet bugs - application_stylesheet = get_application_stylesheet() - if application_stylesheet: - self.setStyleSheet(application_stylesheet) - can_show_splash = Settings().value('core/show splash') - if can_show_splash: - self.splash = SplashScreen() - self.splash.show() - # make sure Qt really display the splash screen - self.processEvents() - # Check if OpenLP has been upgrade and if a backup of data should be created - self.backup_on_upgrade(has_run_wizard, can_show_splash) - # start the main app window - self.main_window = MainWindow() - Registry().execute('bootstrap_initialise') - Registry().execute('bootstrap_post_set_up') - Registry().initialise = False - self.main_window.show() - if can_show_splash: - # now kill the splashscreen - self.splash.finish(self.main_window) - log.debug('Splashscreen closed') - # make sure Qt really display the splash screen - self.processEvents() - self.main_window.repaint() - self.processEvents() - if not has_run_wizard: - self.main_window.first_time() - if Settings().value('core/update check'): - check_for_update(self.main_window) - self.main_window.is_display_blank() - self.main_window.app_startup() - return self.exec() - - def is_already_running(self): - """ - 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 - - def is_data_path_missing(self): - """ - Check if the data folder path exists. - """ - data_folder_path = AppLocation.get_data_path() - if not data_folder_path.exists(): - log.critical('Database was not found in: %s', data_folder_path) - status = QtWidgets.QMessageBox.critical( - None, translate('OpenLP', 'Data Directory Error'), - translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}\n\nThe location of the data folder ' - 'was previously changed from the OpenLP\'s default location. If the data was ' - 'stored on removable device, that device needs to be made available.\n\nYou may ' - 'reset the data location back to the default location, or you can try to make the ' - 'current location available.\n\nDo you want to reset to the default data location? ' - 'If not, OpenLP will be closed so you can try to fix the the problem.') - .format(path=data_folder_path), - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), - QtWidgets.QMessageBox.No) - if status == QtWidgets.QMessageBox.No: - # If answer was "No", return "True", it will shutdown OpenLP in def main - log.info('User requested termination') - return True - # If answer was "Yes", remove the custom data path thus resetting the default location. - Settings().remove('advanced/data path') - log.info('Database location has been reset to the default settings.') - return False - - def hook_exception(self, exc_type, value, traceback): - """ - Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where - users cannot see it and cannot report when we encounter these problems. - - :param exc_type: The class of exception. - :param value: The actual exception object. - :param traceback: A traceback object with the details of where the exception occurred. - """ - # We can't log.exception here because the last exception no longer exists, we're actually busy handling it. - log.critical(''.join(format_exception(exc_type, value, traceback))) - if not hasattr(self, 'exception_form'): - self.exception_form = ExceptionForm() - self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback))) - self.set_normal_cursor() - is_splash_visible = False - if hasattr(self, 'splash') and self.splash.isVisible(): - is_splash_visible = True - self.splash.hide() - self.exception_form.exec() - if is_splash_visible: - self.splash.show() - - def backup_on_upgrade(self, has_run_wizard, can_show_splash): - """ - Check if OpenLP has been upgraded, and ask if a backup of data should be made - - :param has_run_wizard: OpenLP has been run before - :param can_show_splash: Should OpenLP show the splash screen - """ - data_version = Settings().value('core/application version') - openlp_version = get_version()['version'] - # New installation, no need to create backup - if not has_run_wizard: - Settings().setValue('core/application version', openlp_version) - # If data_version is different from the current version ask if we should backup the data folder - elif data_version != openlp_version: - if can_show_splash and self.splash.isVisible(): - self.splash.hide() - if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'), - translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n' - 'a backup of the old data folder?'), - defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: - # Create copy of data folder - data_folder_path = AppLocation.get_data_path() - timestamp = time.strftime("%Y%m%d-%H%M%S") - data_folder_backup_path = data_folder_path.with_name(data_folder_path.name + '-' + timestamp) - try: - copytree(data_folder_path, data_folder_backup_path) - except OSError: - QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'), - translate('OpenLP', 'Backup of the data folder failed!')) - return - message = translate('OpenLP', - 'A backup of the data folder has been created at:\n\n' - '{text}').format(text=data_folder_backup_path) - QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) - - # Update the version in the settings - Settings().setValue('core/application version', openlp_version) - if can_show_splash: - self.splash.show() - - def process_events(self): - """ - Wrapper to make ProcessEvents visible and named correctly - """ - self.processEvents() - - def set_busy_cursor(self): - """ - Sets the Busy Cursor for the Application - """ - self.setOverrideCursor(QtCore.Qt.BusyCursor) - self.processEvents() - - def set_normal_cursor(self): - """ - Sets the Normal Cursor for the Application - """ - self.restoreOverrideCursor() - self.processEvents() - - def event(self, event): - """ - Enables platform specific event handling i.e. direct file opening on OS X - - :param event: The event - """ - if event.type() == QtCore.QEvent.FileOpen: - file_name = event.file() - log.debug('Got open file event for {name}!'.format(name=file_name)) - self.args.insert(0, file_name) - return True - # Mac OS X should restore app window when user clicked on the OpenLP icon - # in the Dock bar. However, OpenLP consists of multiple windows and this - # does not work. This workaround fixes that. - # The main OpenLP window is restored when it was previously minimized. - elif event.type() == QtCore.QEvent.ApplicationActivate: - if is_macosx() and hasattr(self, 'main_window'): - if self.main_window.isMinimized(): - # Copied from QWidget.setWindowState() docs on how to restore and activate a minimized window - # while preserving its maximized and/or full-screen state. - self.main_window.setWindowState(self.main_window.windowState() & ~QtCore.Qt.WindowMinimized | - QtCore.Qt.WindowActive) - return True - return QtWidgets.QApplication.event(self, event) - - -def parse_options(args=None): - """ - Parse the command line arguments - - :param args: list of command line arguments - :return: a tuple of parsed options of type optparse.Value and a list of remaining argsZ - """ - # Set up command line options. - parser = argparse.ArgumentParser(prog='openlp.py') - parser.add_argument('-e', '--no-error-form', dest='no_error_form', action='store_true', - help='Disable the error notification form.') - 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).') - 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('-s', '--style', dest='style', help='Set the Qt5 style (passed directly to Qt5).') - parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_false', - help='Turn off the Web and Socket Server ') - parser.add_argument('rargs', nargs='?', default=[]) - # Parse command line options and deal with them. Use args supplied pragmatically if possible. - return parser.parse_args(args) if args else parser.parse_args() - - -def set_up_logging(log_path): - """ - Setup our logging using log_path - - :param openlp.core.common.path.Path log_path: The file to save the log to. - :rtype: None - """ - create_paths(log_path, do_not_log=True) - file_path = log_path / 'openlp.log' - # TODO: FileHandler accepts a Path object in Py3.6 - logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8') - logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s')) - log.addHandler(logfile) - if log.isEnabledFor(logging.DEBUG): - print('Logging to: {name}'.format(name=file_path)) - - -def main(args=None): - """ - The main function which parses command line options and then runs - - :param args: Some args - """ - args = parse_options(args) - qt_args = [] - if args and args.loglevel.lower() in ['d', 'debug']: - log.setLevel(logging.DEBUG) - elif args and args.loglevel.lower() in ['w', 'warning']: - log.setLevel(logging.WARNING) - else: - log.setLevel(logging.INFO) - if args and args.style: - qt_args.extend(['-style', args.style]) - # Throw the rest of the arguments at Qt, just in case. - qt_args.extend(args.rargs) - # Bug #1018855: Set the WM_CLASS property in X11 - if not is_win() and not is_macosx(): - qt_args.append('OpenLP') - # Initialise the resources - qInitResources() - # Now create and actually run the application. - application = OpenLP(qt_args) - application.setOrganizationName('OpenLP') - application.setOrganizationDomain('openlp.org') - application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) - application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True) - if args and args.portable: - application.setApplicationName('OpenLPPortable') - Settings.setDefaultFormat(Settings.IniFormat) - # Get location OpenLPPortable.ini - portable_path = (AppLocation.get_directory(AppLocation.AppDir) / '..' / '..').resolve() - data_path = portable_path / 'Data' - set_up_logging(portable_path / 'Other') - log.info('Running portable') - portable_settings_path = data_path / 'OpenLP.ini' - # Make this our settings file - log.info('INI file: {name}'.format(name=portable_settings_path)) - Settings.set_filename(str(portable_settings_path)) - portable_settings = Settings() - # Set our data path - log.info('Data path: {name}'.format(name=data_path)) - # Point to our data path - portable_settings.setValue('advanced/data path', data_path) - portable_settings.setValue('advanced/is portable', True) - portable_settings.sync() - else: - application.setApplicationName('OpenLP') - set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) - Registry.create() - Registry().register('application', application) - 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() - # 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() - sys.exit() - # Upgrade settings. - settings = Settings() - if settings.can_upgrade(): - now = datetime.now() - # Only back up if OpenLP has previously run. - if settings.value('core/has run wizard'): - back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf') - log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}' - .format(back_up_path=back_up_path)) - QtWidgets.QMessageBox.information( - None, translate('OpenLP', 'Settings Upgrade'), - translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}') - .format(back_up_path=back_up_path)) - settings.export(back_up_path) - settings.upgrade_settings() - # First time checks in settings - if not Settings().value('core/has run wizard'): - if not FirstTimeLanguageForm().exec(): - # if cancel then stop processing - sys.exit() - # i18n Set Language - language = LanguageManager.get_language() - translators = LanguageManager.get_translators(language) - for translator in translators: - if not translator.isEmpty(): - application.installTranslator(translator) - if not translators: - log.debug('Could not find translators.') - if args and not args.no_error_form: - sys.excepthook = application.hook_exception - sys.exit(application.run(qt_args)) diff --git a/openlp/core/app.py b/openlp/core/app.py new file mode 100644 index 000000000..7c3938cfe --- /dev/null +++ b/openlp/core/app.py @@ -0,0 +1,425 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 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 # +############################################################################### + +""" +The :mod:`core` module provides all core application functions + +All the core functions of the OpenLP application including the GUI, settings, +logging and a plugin framework are contained within the openlp.core module. +""" +import argparse +import logging +import sys +import time +from datetime import datetime +from traceback import format_exception + +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 OpenLPMixin +from openlp.core.common.path import create_paths, copytree +from openlp.core.common.registry import Registry +from openlp.core.common.settings import Settings +from openlp.core.version import check_for_update, get_version +from openlp.core.display.screens import ScreenList +from openlp.core.resources import qInitResources +from openlp.core.ui import SplashScreen +from openlp.core.ui.exceptionform import ExceptionForm +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 + + +__all__ = ['OpenLP', 'main'] + + +log = logging.getLogger() + + +class OpenLP(OpenLPMixin, QtWidgets.QApplication): + """ + The core application class. This class inherits from Qt's QApplication + class in order to provide the core of the application. + """ + + args = [] + + def exec(self): + """ + Override exec method to allow the shared memory to be released on exit + """ + self.is_event_loop_active = True + result = QtWidgets.QApplication.exec() + self.shared_memory.detach() + return result + + def run(self, args): + """ + Run the OpenLP application. + + :param args: Some Args + """ + self.is_event_loop_active = False + # On Windows, the args passed into the constructor are ignored. Not very handy, so set the ones we want to use. + # 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: + args.remove('OpenLP') + self.args.extend(args) + # Decide how many screens we have and their size + screens = ScreenList.create(self.desktop()) + # First time checks in settings + has_run_wizard = Settings().value('core/has run wizard') + if not has_run_wizard: + ftw = FirstTimeForm() + ftw.initialize(screens) + if ftw.exec() == QtWidgets.QDialog.Accepted: + Settings().setValue('core/has run wizard', True) + elif ftw.was_cancelled: + QtCore.QCoreApplication.exit() + sys.exit() + # Correct stylesheet bugs + application_stylesheet = get_application_stylesheet() + if application_stylesheet: + self.setStyleSheet(application_stylesheet) + can_show_splash = Settings().value('core/show splash') + if can_show_splash: + self.splash = SplashScreen() + self.splash.show() + # make sure Qt really display the splash screen + self.processEvents() + # Check if OpenLP has been upgrade and if a backup of data should be created + self.backup_on_upgrade(has_run_wizard, can_show_splash) + # start the main app window + self.main_window = MainWindow() + Registry().execute('bootstrap_initialise') + Registry().execute('bootstrap_post_set_up') + Registry().initialise = False + self.main_window.show() + if can_show_splash: + # now kill the splashscreen + self.splash.finish(self.main_window) + log.debug('Splashscreen closed') + # make sure Qt really display the splash screen + self.processEvents() + self.main_window.repaint() + self.processEvents() + if not has_run_wizard: + self.main_window.first_time() + if Settings().value('core/update check'): + check_for_update(self.main_window) + self.main_window.is_display_blank() + self.main_window.app_startup() + return self.exec() + + def is_already_running(self): + """ + 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 + + def is_data_path_missing(self): + """ + Check if the data folder path exists. + """ + data_folder_path = AppLocation.get_data_path() + if not data_folder_path.exists(): + log.critical('Database was not found in: %s', data_folder_path) + status = QtWidgets.QMessageBox.critical( + None, translate('OpenLP', 'Data Directory Error'), + translate('OpenLP', 'OpenLP data folder was not found in:\n\n{path}\n\nThe location of the data folder ' + 'was previously changed from the OpenLP\'s default location. If the data was ' + 'stored on removable device, that device needs to be made available.\n\nYou may ' + 'reset the data location back to the default location, or you can try to make the ' + 'current location available.\n\nDo you want to reset to the default data location? ' + 'If not, OpenLP will be closed so you can try to fix the the problem.') + .format(path=data_folder_path), + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), + QtWidgets.QMessageBox.No) + if status == QtWidgets.QMessageBox.No: + # If answer was "No", return "True", it will shutdown OpenLP in def main + log.info('User requested termination') + return True + # If answer was "Yes", remove the custom data path thus resetting the default location. + Settings().remove('advanced/data path') + log.info('Database location has been reset to the default settings.') + return False + + def hook_exception(self, exc_type, value, traceback): + """ + Add an exception hook so that any uncaught exceptions are displayed in this window rather than somewhere where + users cannot see it and cannot report when we encounter these problems. + + :param exc_type: The class of exception. + :param value: The actual exception object. + :param traceback: A traceback object with the details of where the exception occurred. + """ + # We can't log.exception here because the last exception no longer exists, we're actually busy handling it. + log.critical(''.join(format_exception(exc_type, value, traceback))) + if not hasattr(self, 'exception_form'): + self.exception_form = ExceptionForm() + self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exc_type, value, traceback))) + self.set_normal_cursor() + is_splash_visible = False + if hasattr(self, 'splash') and self.splash.isVisible(): + is_splash_visible = True + self.splash.hide() + self.exception_form.exec() + if is_splash_visible: + self.splash.show() + + def backup_on_upgrade(self, has_run_wizard, can_show_splash): + """ + Check if OpenLP has been upgraded, and ask if a backup of data should be made + + :param has_run_wizard: OpenLP has been run before + :param can_show_splash: Should OpenLP show the splash screen + """ + data_version = Settings().value('core/application version') + openlp_version = get_version()['version'] + # New installation, no need to create backup + if not has_run_wizard: + Settings().setValue('core/application version', openlp_version) + # If data_version is different from the current version ask if we should backup the data folder + elif data_version != openlp_version: + if can_show_splash and self.splash.isVisible(): + self.splash.hide() + if QtWidgets.QMessageBox.question(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'OpenLP has been upgraded, do you want to create\n' + 'a backup of the old data folder?'), + defaultButton=QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.Yes: + # Create copy of data folder + data_folder_path = AppLocation.get_data_path() + timestamp = time.strftime("%Y%m%d-%H%M%S") + data_folder_backup_path = data_folder_path.with_name(data_folder_path.name + '-' + timestamp) + try: + copytree(data_folder_path, data_folder_backup_path) + except OSError: + QtWidgets.QMessageBox.warning(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'Backup of the data folder failed!')) + return + message = translate('OpenLP', + 'A backup of the data folder has been created at:\n\n' + '{text}').format(text=data_folder_backup_path) + QtWidgets.QMessageBox.information(None, translate('OpenLP', 'Backup'), message) + + # Update the version in the settings + Settings().setValue('core/application version', openlp_version) + if can_show_splash: + self.splash.show() + + def process_events(self): + """ + Wrapper to make ProcessEvents visible and named correctly + """ + self.processEvents() + + def set_busy_cursor(self): + """ + Sets the Busy Cursor for the Application + """ + self.setOverrideCursor(QtCore.Qt.BusyCursor) + self.processEvents() + + def set_normal_cursor(self): + """ + Sets the Normal Cursor for the Application + """ + self.restoreOverrideCursor() + self.processEvents() + + def event(self, event): + """ + Enables platform specific event handling i.e. direct file opening on OS X + + :param event: The event + """ + if event.type() == QtCore.QEvent.FileOpen: + file_name = event.file() + log.debug('Got open file event for {name}!'.format(name=file_name)) + self.args.insert(0, file_name) + return True + # Mac OS X should restore app window when user clicked on the OpenLP icon + # in the Dock bar. However, OpenLP consists of multiple windows and this + # does not work. This workaround fixes that. + # The main OpenLP window is restored when it was previously minimized. + elif event.type() == QtCore.QEvent.ApplicationActivate: + if is_macosx() and hasattr(self, 'main_window'): + if self.main_window.isMinimized(): + # Copied from QWidget.setWindowState() docs on how to restore and activate a minimized window + # while preserving its maximized and/or full-screen state. + self.main_window.setWindowState(self.main_window.windowState() & ~QtCore.Qt.WindowMinimized | + QtCore.Qt.WindowActive) + return True + return QtWidgets.QApplication.event(self, event) + + +def parse_options(args=None): + """ + Parse the command line arguments + + :param args: list of command line arguments + :return: a tuple of parsed options of type optparse.Value and a list of remaining argsZ + """ + # Set up command line options. + parser = argparse.ArgumentParser(prog='openlp.py') + parser.add_argument('-e', '--no-error-form', dest='no_error_form', action='store_true', + help='Disable the error notification form.') + 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).') + 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('-s', '--style', dest='style', help='Set the Qt5 style (passed directly to Qt5).') + parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_false', + help='Turn off the Web and Socket Server ') + parser.add_argument('rargs', nargs='?', default=[]) + # Parse command line options and deal with them. Use args supplied pragmatically if possible. + return parser.parse_args(args) if args else parser.parse_args() + + +def set_up_logging(log_path): + """ + Setup our logging using log_path + + :param openlp.core.common.path.Path log_path: The file to save the log to. + :rtype: None + """ + create_paths(log_path, do_not_log=True) + file_path = log_path / 'openlp.log' + # TODO: FileHandler accepts a Path object in Py3.6 + logfile = logging.FileHandler(str(file_path), 'w', encoding='UTF-8') + logfile.setFormatter(logging.Formatter('%(asctime)s %(name)-55s %(levelname)-8s %(message)s')) + log.addHandler(logfile) + if log.isEnabledFor(logging.DEBUG): + print('Logging to: {name}'.format(name=file_path)) + + +def main(args=None): + """ + The main function which parses command line options and then runs + + :param args: Some args + """ + args = parse_options(args) + qt_args = [] + if args and args.loglevel.lower() in ['d', 'debug']: + log.setLevel(logging.DEBUG) + elif args and args.loglevel.lower() in ['w', 'warning']: + log.setLevel(logging.WARNING) + else: + log.setLevel(logging.INFO) + if args and args.style: + qt_args.extend(['-style', args.style]) + # Throw the rest of the arguments at Qt, just in case. + qt_args.extend(args.rargs) + # Bug #1018855: Set the WM_CLASS property in X11 + if not is_win() and not is_macosx(): + qt_args.append('OpenLP') + # Initialise the resources + qInitResources() + # Now create and actually run the application. + application = OpenLP(qt_args) + application.setOrganizationName('OpenLP') + application.setOrganizationDomain('openlp.org') + application.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True) + application.setAttribute(QtCore.Qt.AA_DontCreateNativeWidgetSiblings, True) + if args and args.portable: + application.setApplicationName('OpenLPPortable') + Settings.setDefaultFormat(Settings.IniFormat) + # Get location OpenLPPortable.ini + portable_path = (AppLocation.get_directory(AppLocation.AppDir) / '..' / '..').resolve() + data_path = portable_path / 'Data' + set_up_logging(portable_path / 'Other') + log.info('Running portable') + portable_settings_path = data_path / 'OpenLP.ini' + # Make this our settings file + log.info('INI file: {name}'.format(name=portable_settings_path)) + Settings.set_filename(str(portable_settings_path)) + portable_settings = Settings() + # Set our data path + log.info('Data path: {name}'.format(name=data_path)) + # Point to our data path + portable_settings.setValue('advanced/data path', data_path) + portable_settings.setValue('advanced/is portable', True) + portable_settings.sync() + else: + application.setApplicationName('OpenLP') + set_up_logging(AppLocation.get_directory(AppLocation.CacheDir)) + Registry.create() + Registry().register('application', application) + 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() + # 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() + sys.exit() + # Upgrade settings. + settings = Settings() + if settings.can_upgrade(): + now = datetime.now() + # Only back up if OpenLP has previously run. + if settings.value('core/has run wizard'): + back_up_path = AppLocation.get_data_path() / (now.strftime('%Y-%m-%d %H-%M') + '.conf') + log.info('Settings about to be upgraded. Existing settings are being backed up to {back_up_path}' + .format(back_up_path=back_up_path)) + QtWidgets.QMessageBox.information( + None, translate('OpenLP', 'Settings Upgrade'), + translate('OpenLP', 'Your settings are about to upgraded. A backup will be created at {back_up_path}') + .format(back_up_path=back_up_path)) + settings.export(back_up_path) + settings.upgrade_settings() + # First time checks in settings + if not Settings().value('core/has run wizard'): + if not FirstTimeLanguageForm().exec(): + # if cancel then stop processing + sys.exit() + # i18n Set Language + language = LanguageManager.get_language() + translators = LanguageManager.get_translators(language) + for translator in translators: + if not translator.isEmpty(): + application.installTranslator(translator) + if not translators: + log.debug('Could not find translators.') + if args and not args.no_error_form: + sys.excepthook = application.hook_exception + sys.exit(application.run(qt_args)) diff --git a/openlp/core/display/__init__.py b/openlp/core/display/__init__.py new file mode 100644 index 000000000..dea4ef4db --- /dev/null +++ b/openlp/core/display/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 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 # +############################################################################### +""" +The :mod:`~openlp.core.display` module contains all the code related to rendering and output +""" diff --git a/openlp/core/lib/renderer.py b/openlp/core/display/renderer.py similarity index 99% rename from openlp/core/lib/renderer.py rename to openlp/core/display/renderer.py index fa617a38b..fd040d9c6 100644 --- a/openlp/core/lib/renderer.py +++ b/openlp/core/display/renderer.py @@ -29,10 +29,11 @@ from openlp.core.common.mixins import OpenLPMixin, RegistryMixin from openlp.core.common.path import path_to_str from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.common.settings import Settings -from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \ - build_lyrics_format_css, build_lyrics_outline_css, build_chords_css +from openlp.core.display.screens import ScreenList +from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ServiceItem, expand_tags, build_chords_css, \ + build_lyrics_format_css, build_lyrics_outline_css from openlp.core.common import ThemeLevel -from openlp.core.ui import MainDisplay +from openlp.core.ui.maindisplay import MainDisplay VERSE = 'The Lord said to {r}Noah{/r}: \n' \ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n' \ diff --git a/openlp/core/lib/screen.py b/openlp/core/display/screens.py similarity index 99% rename from openlp/core/lib/screen.py rename to openlp/core/display/screens.py index 6b2655ed7..0a1a4cd28 100644 --- a/openlp/core/lib/screen.py +++ b/openlp/core/display/screens.py @@ -23,7 +23,6 @@ The :mod:`screen` module provides management functionality for a machines' displays. """ - import logging import copy diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index e6aa6c100..0f4078420 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -612,7 +612,6 @@ def create_separated_list(string_list): from .exceptions import ValidationError -from .screen import ScreenList from .formattingtags import FormattingTags from .plugin import PluginStatus, StringContent, Plugin from .pluginmanager import PluginManager @@ -620,7 +619,6 @@ from .settingstab import SettingsTab from .serviceitem import ServiceItem, ServiceItemType, ItemCapabilities from .htmlbuilder import build_html, build_lyrics_format_css, build_lyrics_outline_css, build_chords_css from .imagemanager import ImageManager -from .renderer import Renderer from .mediamanageritem import MediaManagerItem from .projector.db import ProjectorDB, Projector from .projector.pjlink import PJLink diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 151a2c5e8..81a1b659d 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -33,7 +33,8 @@ from PyQt5 import QtCore from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings -from openlp.core.lib import ScreenList, resize_image, image_to_byte +from openlp.core.display.screens import ScreenList +from openlp.core.lib import resize_image, image_to_byte log = logging.getLogger(__name__) diff --git a/openlp/core/lib/theme.py b/openlp/core/lib/theme.py index 9690fe340..92d12ebfd 100644 --- a/openlp/core/lib/theme.py +++ b/openlp/core/lib/theme.py @@ -30,7 +30,8 @@ from lxml import etree, objectify from openlp.core.common import de_hump from openlp.core.common.applocation import AppLocation from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder -from openlp.core.lib import ScreenList, str_to_bool, get_text_file_string +from openlp.core.display.screens import ScreenList +from openlp.core.lib import str_to_bool, get_text_file_string log = logging.getLogger(__name__) diff --git a/openlp/core/ui/generaltab.py b/openlp/core/ui/generaltab.py index 8e83636cc..8488f13a1 100644 --- a/openlp/core/ui/generaltab.py +++ b/openlp/core/ui/generaltab.py @@ -31,7 +31,8 @@ from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.path import Path from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings -from openlp.core.lib import SettingsTab, ScreenList +from openlp.core.display.screens import ScreenList +from openlp.core.lib import SettingsTab from openlp.core.ui.lib import ColorButton, PathEdit log = logging.getLogger(__name__) diff --git a/openlp/core/ui/lib/dockwidget.py b/openlp/core/ui/lib/dockwidget.py index d8bcab0fa..398d1e674 100644 --- a/openlp/core/ui/lib/dockwidget.py +++ b/openlp/core/ui/lib/dockwidget.py @@ -28,7 +28,8 @@ import logging from PyQt5 import QtWidgets -from openlp.core.lib import ScreenList, build_icon +from openlp.core.display.screens import ScreenList +from openlp.core.lib import build_icon log = logging.getLogger(__name__) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 46e9a82e2..d7f2264ee 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -40,7 +40,8 @@ from openlp.core.common.mixins import OpenLPMixin from openlp.core.common.path import path_to_str from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.common.settings import Settings -from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ServiceItem, ImageSource, build_html, expand_tags, image_to_byte from openlp.core.lib.theme import BackgroundType from openlp.core.ui import HideMode, AlertLocation, DisplayControllerType diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 8cc83d474..26a8b921a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -43,17 +43,19 @@ from openlp.core.common.i18n import LanguageManager, UiStrings, translate from openlp.core.common.path import Path, copyfile, create_paths, path_to_str, str_to_path from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.common.settings import Settings -from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon +from openlp.core.display.screens import ScreenList +from openlp.core.display.renderer import Renderer +from openlp.core.lib import PluginManager, ImageManager, PluginStatus, build_icon from openlp.core.lib.ui import create_action from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \ ShortcutListForm, FormattingTagForm, PreviewController from openlp.core.ui.firsttimeform import FirstTimeForm -from openlp.core.ui.media import MediaController -from openlp.core.ui.printserviceform import PrintServiceForm -from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.lib.dockwidget import OpenLPDockWidget from openlp.core.ui.lib.filedialog import FileDialog from openlp.core.ui.lib.mediadockmanager import MediaDockManager +from openlp.core.ui.media import MediaController +from openlp.core.ui.printserviceform import PrintServiceForm +from openlp.core.ui.projector.manager import ProjectorManager from openlp.core.ui.style import PROGRESSBAR_STYLE, get_library_stylesheet from openlp.core.version import get_version diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 10530f011..52e200886 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -36,8 +36,8 @@ from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.mixins import OpenLPMixin, RegistryMixin from openlp.core.common.registry import Registry, RegistryProperties from openlp.core.common.settings import Settings -from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, ScreenList, build_icon, \ - build_html +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ItemCapabilities, ServiceItem, ImageSource, ServiceItemAction, build_icon, build_html from openlp.core.lib.ui import create_action from openlp.core.ui.lib.toolbar import OpenLPToolbar from openlp.core.ui.lib.listpreviewwidget import ListPreviewWidget diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index 1ff1bf9fc..befa93601 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -38,7 +38,7 @@ from PyQt5 import QtCore from openlp.core.common import delete_file, get_uno_command, get_uno_instance, is_win from openlp.core.common.registry import Registry -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument, \ TextType diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 024732934..26eb87d85 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -29,7 +29,7 @@ from openlp.core.common import check_binary_exists, is_win from openlp.core.common.applocation import AppLocation from openlp.core.common.path import which from openlp.core.common.settings import Settings -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument if is_win(): diff --git a/openlp/plugins/presentations/lib/powerpointcontroller.py b/openlp/plugins/presentations/lib/powerpointcontroller.py index ad3735c45..df46dc7d5 100644 --- a/openlp/plugins/presentations/lib/powerpointcontroller.py +++ b/openlp/plugins/presentations/lib/powerpointcontroller.py @@ -31,7 +31,7 @@ from openlp.core.common import is_win, trace_error_handler from openlp.core.common.i18n import UiStrings from openlp.core.common.registry import Registry from openlp.core.common.settings import Settings -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from openlp.core.lib.ui import critical_error_message_box, translate from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 722cd209d..0a403df37 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -26,7 +26,7 @@ from xml.etree import ElementTree from openlp.core.common import is_win from openlp.core.common.applocation import AppLocation -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument if is_win(): diff --git a/tests/functional/openlp_core_api/__init__.py b/tests/functional/openlp_core/api/__init__.py similarity index 100% rename from tests/functional/openlp_core_api/__init__.py rename to tests/functional/openlp_core/api/__init__.py diff --git a/tests/functional/openlp_core_api_http/__init__.py b/tests/functional/openlp_core/api/http/__init__.py similarity index 100% rename from tests/functional/openlp_core_api_http/__init__.py rename to tests/functional/openlp_core/api/http/__init__.py diff --git a/tests/functional/openlp_core_api_http/test_error.py b/tests/functional/openlp_core/api/http/test_error.py similarity index 100% rename from tests/functional/openlp_core_api_http/test_error.py rename to tests/functional/openlp_core/api/http/test_error.py diff --git a/tests/functional/openlp_core_api_http/test_http.py b/tests/functional/openlp_core/api/http/test_http.py similarity index 100% rename from tests/functional/openlp_core_api_http/test_http.py rename to tests/functional/openlp_core/api/http/test_http.py diff --git a/tests/functional/openlp_core_api_http/test_wsgiapp.py b/tests/functional/openlp_core/api/http/test_wsgiapp.py similarity index 100% rename from tests/functional/openlp_core_api_http/test_wsgiapp.py rename to tests/functional/openlp_core/api/http/test_wsgiapp.py diff --git a/tests/functional/openlp_core_api/test_deploy.py b/tests/functional/openlp_core/api/test_deploy.py similarity index 98% rename from tests/functional/openlp_core_api/test_deploy.py rename to tests/functional/openlp_core/api/test_deploy.py index 273894b99..be36fb9c7 100644 --- a/tests/functional/openlp_core_api/test_deploy.py +++ b/tests/functional/openlp_core/api/test_deploy.py @@ -27,7 +27,7 @@ from unittest import TestCase from openlp.core.api.deploy import deploy_zipfile -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestRemoteDeploy(TestCase): diff --git a/tests/functional/openlp_core_api/test_tab.py b/tests/functional/openlp_core/api/test_tab.py similarity index 100% rename from tests/functional/openlp_core_api/test_tab.py rename to tests/functional/openlp_core/api/test_tab.py diff --git a/tests/functional/openlp_core_api/test_websockets.py b/tests/functional/openlp_core/api/test_websockets.py similarity index 100% rename from tests/functional/openlp_core_api/test_websockets.py rename to tests/functional/openlp_core/api/test_websockets.py diff --git a/tests/functional/openlp_core_common/__init__.py b/tests/functional/openlp_core/common/__init__.py similarity index 100% rename from tests/functional/openlp_core_common/__init__.py rename to tests/functional/openlp_core/common/__init__.py diff --git a/tests/functional/openlp_core_common/test_actions.py b/tests/functional/openlp_core/common/test_actions.py similarity index 100% rename from tests/functional/openlp_core_common/test_actions.py rename to tests/functional/openlp_core/common/test_actions.py diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core/common/test_applocation.py similarity index 100% rename from tests/functional/openlp_core_common/test_applocation.py rename to tests/functional/openlp_core/common/test_applocation.py diff --git a/tests/functional/openlp_core_common/test_common.py b/tests/functional/openlp_core/common/test_common.py similarity index 100% rename from tests/functional/openlp_core_common/test_common.py rename to tests/functional/openlp_core/common/test_common.py diff --git a/tests/functional/openlp_core_common/test_db.py b/tests/functional/openlp_core/common/test_db.py similarity index 100% rename from tests/functional/openlp_core_common/test_db.py rename to tests/functional/openlp_core/common/test_db.py diff --git a/tests/functional/openlp_core_common/test_httputils.py b/tests/functional/openlp_core/common/test_httputils.py similarity index 100% rename from tests/functional/openlp_core_common/test_httputils.py rename to tests/functional/openlp_core/common/test_httputils.py diff --git a/tests/functional/openlp_core_common/test_i18n.py b/tests/functional/openlp_core/common/test_i18n.py similarity index 100% rename from tests/functional/openlp_core_common/test_i18n.py rename to tests/functional/openlp_core/common/test_i18n.py diff --git a/tests/functional/openlp_core_common/test_init.py b/tests/functional/openlp_core/common/test_init.py similarity index 100% rename from tests/functional/openlp_core_common/test_init.py rename to tests/functional/openlp_core/common/test_init.py diff --git a/tests/functional/openlp_core_common/test_json.py b/tests/functional/openlp_core/common/test_json.py similarity index 100% rename from tests/functional/openlp_core_common/test_json.py rename to tests/functional/openlp_core/common/test_json.py diff --git a/tests/functional/openlp_core_common/test_mixins.py b/tests/functional/openlp_core/common/test_mixins.py similarity index 100% rename from tests/functional/openlp_core_common/test_mixins.py rename to tests/functional/openlp_core/common/test_mixins.py diff --git a/tests/functional/openlp_core_common/test_path.py b/tests/functional/openlp_core/common/test_path.py similarity index 100% rename from tests/functional/openlp_core_common/test_path.py rename to tests/functional/openlp_core/common/test_path.py diff --git a/tests/functional/openlp_core_common/test_projector_utilities.py b/tests/functional/openlp_core/common/test_projector_utilities.py similarity index 100% rename from tests/functional/openlp_core_common/test_projector_utilities.py rename to tests/functional/openlp_core/common/test_projector_utilities.py diff --git a/tests/functional/openlp_core_common/test_registry.py b/tests/functional/openlp_core/common/test_registry.py similarity index 100% rename from tests/functional/openlp_core_common/test_registry.py rename to tests/functional/openlp_core/common/test_registry.py diff --git a/tests/functional/openlp_core_common/test_settings.py b/tests/functional/openlp_core/common/test_settings.py similarity index 100% rename from tests/functional/openlp_core_common/test_settings.py rename to tests/functional/openlp_core/common/test_settings.py diff --git a/tests/functional/openlp_core/display/__init__.py b/tests/functional/openlp_core/display/__init__.py new file mode 100644 index 000000000..96d55e7e8 --- /dev/null +++ b/tests/functional/openlp_core/display/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 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 # +############################################################################### +""" +Test the :mod:`~openlp.core.display` module +""" diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core/display/test_renderer.py similarity index 90% rename from tests/functional/openlp_core_lib/test_renderer.py rename to tests/functional/openlp_core/display/test_renderer.py index 55036449b..c30fe083b 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core/display/test_renderer.py @@ -28,8 +28,9 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore from openlp.core.common.registry import Registry -from openlp.core.lib import Renderer, ScreenList, ServiceItem, FormattingTags -from openlp.core.lib.renderer import words_split, get_start_tags +from openlp.core.display.screens import ScreenList +from openlp.core.display.renderer import Renderer, words_split, get_start_tags +from openlp.core.lib import ServiceItem from openlp.core.lib.theme import Theme @@ -83,15 +84,16 @@ class TestRenderer(TestCase): Test the default layout calculations """ # GIVEN: A new renderer instance. - renderer = Renderer() # WHEN: given the default screen size has been created. - # THEN: The renderer have created a default screen. - self.assertEqual(renderer.width, 1024, 'The base renderer should be a live controller') - self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller') - self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller') - self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller') + renderer = Renderer() - @patch('openlp.core.lib.renderer.FormattingTags.get_html_tags') + # THEN: The renderer have created a default screen. + assert renderer.width == 1024, 'The base renderer should be a live controller' + assert renderer.height == 768, 'The base renderer should be a live controller' + assert renderer.screen_ratio == 0.75, 'The base renderer should be a live controller' + assert renderer.footer_start == 691, 'The base renderer should be a live controller' + + @patch('openlp.core.display.renderer.FormattingTags.get_html_tags') def test_get_start_tags(self, mocked_get_html_tags): """ Test the get_start_tags() method @@ -179,10 +181,10 @@ class TestRenderer(TestCase): # THEN: The blanks have been removed. self.assertListEqual(result_words, expected_words) - @patch('openlp.core.lib.renderer.QtWebKitWidgets.QWebView') - @patch('openlp.core.lib.renderer.build_lyrics_format_css') - @patch('openlp.core.lib.renderer.build_lyrics_outline_css') - @patch('openlp.core.lib.renderer.build_chords_css') + @patch('openlp.core.display.renderer.QtWebKitWidgets.QWebView') + @patch('openlp.core.display.renderer.build_lyrics_format_css') + @patch('openlp.core.display.renderer.build_lyrics_outline_css') + @patch('openlp.core.display.renderer.build_chords_css') def test_set_text_rectangle(self, mock_build_chords_css, mock_outline_css, mock_lyrics_css, mock_webview): """ Test set_text_rectangle returns a proper html string diff --git a/tests/functional/openlp_core_lib/test_screen.py b/tests/functional/openlp_core/display/test_screens.py similarity index 98% rename from tests/functional/openlp_core_lib/test_screen.py rename to tests/functional/openlp_core/display/test_screens.py index 448f7f8d7..258f3b69a 100644 --- a/tests/functional/openlp_core_lib/test_screen.py +++ b/tests/functional/openlp_core/display/test_screens.py @@ -28,7 +28,7 @@ from unittest.mock import MagicMock from PyQt5 import QtCore, QtWidgets from openlp.core.common.registry import Registry -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList SCREEN = { 'primary': False, diff --git a/tests/functional/openlp_core_lib/__init__.py b/tests/functional/openlp_core/lib/__init__.py similarity index 100% rename from tests/functional/openlp_core_lib/__init__.py rename to tests/functional/openlp_core/lib/__init__.py diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core/lib/test_db.py similarity index 97% rename from tests/functional/openlp_core_lib/test_db.py rename to tests/functional/openlp_core/lib/test_db.py index 51aa08830..871980498 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core/lib/test_db.py @@ -34,7 +34,6 @@ from sqlalchemy import MetaData from openlp.core.common.path import Path from openlp.core.lib.db import init_db, get_upgrade_op, delete_database, upgrade_db -from openlp.core.lib.projector import upgrade as pjlink_upgrade class TestDB(TestCase): @@ -163,16 +162,16 @@ class TestDB(TestCase): mocked_delete_file.assert_called_with(test_location) self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)') - @patch('tests.functional.openlp_core_lib.test_db.pjlink_upgrade') - def test_skip_db_upgrade_with_no_database(self, mocked_upgrade): + def test_skip_db_upgrade_with_no_database(self): """ Test the upgrade_db function does not try to update a missing database """ # GIVEN: Database URL that does not (yet) exist url = 'sqlite:///{tmp}/test_db.sqlite'.format(tmp=self.tmp_folder) + mocked_upgrade = MagicMock() # WHEN: We attempt to upgrade a non-existant database - upgrade_db(url, pjlink_upgrade) + upgrade_db(url, mocked_upgrade) # THEN: upgrade should NOT have been called self.assertFalse(mocked_upgrade.called, 'Database upgrade function should NOT have been called') diff --git a/tests/functional/openlp_core_lib/test_formattingtags.py b/tests/functional/openlp_core/lib/test_formattingtags.py similarity index 100% rename from tests/functional/openlp_core_lib/test_formattingtags.py rename to tests/functional/openlp_core/lib/test_formattingtags.py diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core/lib/test_htmlbuilder.py similarity index 100% rename from tests/functional/openlp_core_lib/test_htmlbuilder.py rename to tests/functional/openlp_core/lib/test_htmlbuilder.py diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py similarity index 96% rename from tests/functional/openlp_core_lib/test_image_manager.py rename to tests/functional/openlp_core/lib/test_image_manager.py index 8472b9395..c39f6e8f4 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core/lib/test_image_manager.py @@ -31,12 +31,12 @@ from unittest.mock import patch from PyQt5 import QtGui from openlp.core.common.registry import Registry -from openlp.core.lib import ImageManager, ScreenList -from openlp.core.lib.imagemanager import Priority +from openlp.core.display.screens import ScreenList +from openlp.core.lib.imagemanager import ImageManager, Priority from tests.helpers.testmixin import TestMixin -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestImageManager(TestCase, TestMixin): @@ -152,8 +152,8 @@ class TestImageManager(TestCase, TestMixin): # Allow the queue to process. self.lock.release() # Request some "data". - image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4) - image_object = self.image_manager.get_image(TEST_PATH, image3) + self.image_manager.get_image_bytes(TEST_PATH, image4) + self.image_manager.get_image(TEST_PATH, image3) # Now the mocked methods/functions do not have to sleep anymore. self.sleep_time = 0 # Wait for the queue to finish. diff --git a/tests/functional/openlp_core_lib/test_lib.py b/tests/functional/openlp_core/lib/test_lib.py similarity index 99% rename from tests/functional/openlp_core_lib/test_lib.py rename to tests/functional/openlp_core/lib/test_lib.py index 83a496a7b..f2bfaf79c 100644 --- a/tests/functional/openlp_core_lib/test_lib.py +++ b/tests/functional/openlp_core/lib/test_lib.py @@ -33,7 +33,7 @@ from openlp.core.lib import FormattingTags, build_icon, check_item_selected, cle create_separated_list, create_thumb, expand_chords, expand_chords_for_printing, expand_tags, find_formatting_tags, \ get_text_file_string, image_to_byte, resize_image, str_to_bool, validate_thumb -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources')) class TestLib(TestCase): diff --git a/tests/functional/openlp_core_lib/test_mediamanageritem.py b/tests/functional/openlp_core/lib/test_mediamanageritem.py similarity index 100% rename from tests/functional/openlp_core_lib/test_mediamanageritem.py rename to tests/functional/openlp_core/lib/test_mediamanageritem.py diff --git a/tests/functional/openlp_core_lib/test_path.py b/tests/functional/openlp_core/lib/test_path.py similarity index 100% rename from tests/functional/openlp_core_lib/test_path.py rename to tests/functional/openlp_core/lib/test_path.py diff --git a/tests/functional/openlp_core_lib/test_pluginmanager.py b/tests/functional/openlp_core/lib/test_pluginmanager.py similarity index 100% rename from tests/functional/openlp_core_lib/test_pluginmanager.py rename to tests/functional/openlp_core/lib/test_pluginmanager.py diff --git a/tests/functional/openlp_core_lib/test_projector_constants.py b/tests/functional/openlp_core/lib/test_projector_constants.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_constants.py rename to tests/functional/openlp_core/lib/test_projector_constants.py diff --git a/tests/functional/openlp_core_lib/test_projector_db.py b/tests/functional/openlp_core/lib/test_projector_db.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_db.py rename to tests/functional/openlp_core/lib/test_projector_db.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_base.py b/tests/functional/openlp_core/lib/test_projector_pjlink_base.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_base.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_base.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py b/tests/functional/openlp_core/lib/test_projector_pjlink_cmd_routing.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_cmd_routing.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_cmd_routing.py diff --git a/tests/functional/openlp_core_lib/test_projector_pjlink_commands.py b/tests/functional/openlp_core/lib/test_projector_pjlink_commands.py similarity index 100% rename from tests/functional/openlp_core_lib/test_projector_pjlink_commands.py rename to tests/functional/openlp_core/lib/test_projector_pjlink_commands.py diff --git a/tests/functional/openlp_core_lib/test_serviceitem.py b/tests/functional/openlp_core/lib/test_serviceitem.py similarity index 99% rename from tests/functional/openlp_core_lib/test_serviceitem.py rename to tests/functional/openlp_core/lib/test_serviceitem.py index a70bcf989..78ad018fb 100644 --- a/tests/functional/openlp_core_lib/test_serviceitem.py +++ b/tests/functional/openlp_core/lib/test_serviceitem.py @@ -57,7 +57,7 @@ RENDERED_VERSE = 'The Lord said to Noa 'e'\ 'n of the Lord\n' FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456'] -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources', 'service')) +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'service')) class TestServiceItem(TestCase): diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py similarity index 100% rename from tests/functional/openlp_core_lib/test_theme.py rename to tests/functional/openlp_core/lib/test_theme.py diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core/lib/test_ui.py similarity index 100% rename from tests/functional/openlp_core_lib/test_ui.py rename to tests/functional/openlp_core/lib/test_ui.py diff --git a/tests/functional/openlp_core/test_app.py b/tests/functional/openlp_core/test_app.py new file mode 100644 index 000000000..0f5633c3e --- /dev/null +++ b/tests/functional/openlp_core/test_app.py @@ -0,0 +1,378 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2017 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 os +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 tests.helpers.testmixin import TestMixin + +TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources')) + + +def test_parse_options_basic(): + """ + Test the parse options process works + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = [] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_debug(): + """ + Test the parse options process works for debug only + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['-l debug'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_debug_and_portable(): + """ + Test the parse options process works for debug and portable + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['--portable'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +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'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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.style is None, 'There are no style flags to be processed' + assert args.rargs == [], 'The service file should be blank' + + +def test_parse_options_file(): + """ + Test the parse options process works with a file + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['dummy_temp'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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' + assert args.style is None, 'There are no style flags to be processed' + assert args.rargs == 'dummy_temp', 'The service file should not be blank' + + +def test_parse_options_file_and_debug(): + """ + Test the parse options process works with a file and the debug log level + """ + # GIVEN: a a set of system arguments. + sys.argv[1:] = ['-l debug', 'dummy_temp'] + # WHEN: We we parse them to expand to options + args = parse_options(None) + # 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.style is None, 'There are no style flags to be processed' + assert args.rargs == 'dummy_temp', 'The service file should not be blank' + + +@skip('Figure out why this is causing a segfault') +class TestOpenLP(TestCase): + """ + Test the OpenLP app class + """ + @patch('openlp.core.app.QtWidgets.QApplication.exec') + def test_exec(self, mocked_exec): + """ + Test the exec method + """ + # GIVEN: An app + app = OpenLP([]) + app.shared_memory = MagicMock() + mocked_exec.return_value = False + + # WHEN: exec() is called + result = app.exec() + + # THEN: The right things should be called + assert app.is_event_loop_active is True + mocked_exec.assert_called_once_with() + app.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 + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.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') + @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.Yes + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.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 + + @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 + app = OpenLP([]) + + # WHEN: is_already_running() is called + result = app.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 + + def test_process_events(self): + """ + Test that the app.process_events() method simply calls the Qt method + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: process_events() is called + with patch.object(app, 'processEvents') as mocked_processEvents: + app.process_events() + + # THEN: processEvents was called + mocked_processEvents.assert_called_once_with() + + def test_set_busy_cursor(self): + """ + Test that the set_busy_cursor() method sets the cursor + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: set_busy_cursor() is called + with patch.object(app, 'setOverrideCursor') as mocked_setOverrideCursor, \ + patch.object(app, 'processEvents') as mocked_processEvents: + app.set_busy_cursor() + + # THEN: The cursor should have been set + mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) + mocked_processEvents.assert_called_once_with() + + def test_set_normal_cursor(self): + """ + Test that the set_normal_cursor() method resets the cursor + """ + # GIVEN: An app + app = OpenLP([]) + + # WHEN: set_normal_cursor() is called + with patch.object(app, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \ + patch.object(app, 'processEvents') as mocked_processEvents: + app.set_normal_cursor() + + # THEN: The cursor should have been set + mocked_restoreOverrideCursor.assert_called_once_with() + mocked_processEvents.assert_called_once_with() + + +class TestInit(TestCase, TestMixin): + def setUp(self): + self.build_settings() + with patch('openlp.core.app.OpenLPMixin.__init__') as constructor: + constructor.return_value = None + self.openlp = OpenLP(list()) + + def tearDown(self): + self.destroy_settings() + del self.openlp + + def test_event(self): + """ + Test the reimplemented event method + """ + # GIVEN: A file path and a QEvent. + file_path = os.path.join(TEST_PATH, 'church.jpg') + mocked_file_method = MagicMock(return_value=file_path) + event = QtCore.QEvent(QtCore.QEvent.FileOpen) + event.file = mocked_file_method + + # WHEN: Call the vent method. + result = self.openlp.event(event) + + # THEN: The path should be inserted. + self.assertTrue(result, "The method should have returned True.") + mocked_file_method.assert_called_once_with() + self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") + + @patch('openlp.core.app.is_macosx') + def test_application_activate_event(self, mocked_is_macosx): + """ + Test that clicking on the dock icon on Mac OS X restores the main window if it is minimized + """ + # GIVEN: Mac OS X and an ApplicationActivate event + mocked_is_macosx.return_value = True + event = MagicMock() + event.type.return_value = QtCore.QEvent.ApplicationActivate + mocked_main_window = MagicMock() + self.openlp.main_window = mocked_main_window + + # WHEN: The icon in the dock is clicked + result = self.openlp.event(event) + + # THEN: + self.assertTrue(result, "The method should have returned True.") + # self.assertFalse(self.openlp.main_window.isMinimized()) + + @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): + """ + Test that we don't try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are the same + old_install = False + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.2.0') + mocked_get_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtWidgets.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install, False) + + # THEN: It should not ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be the same!') + self.assertEqual(mocked_question.call_count, 0, 'No question should have been asked!') + + @patch('openlp.core.app.get_version') + @patch('openlp.core.app.QtWidgets.QMessageBox.question') + def test_backup_on_upgrade(self, mocked_question, mocked_get_version): + """ + Test that we try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are different + old_install = True + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.0.5') + self.openlp.splash = MagicMock() + self.openlp.splash.isVisible.return_value = True + mocked_get_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtWidgets.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install, True) + + # THEN: It should ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!') + self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!') + self.openlp.splash.hide.assert_called_once_with() + self.openlp.splash.show.assert_called_once_with() diff --git a/tests/functional/openlp_core/test_init.py b/tests/functional/openlp_core/test_init.py deleted file mode 100644 index dfe0c34cb..000000000 --- a/tests/functional/openlp_core/test_init.py +++ /dev/null @@ -1,273 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 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 import OpenLP, parse_options - - -class TestInitFunctions(TestCase): - - def test_parse_options_basic(self): - """ - Test the parse options process works - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = [] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_debug(self): - """ - Test the parse options process works for debug only - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_debug_and_portable(self): - """ - Test the parse options process works for debug and portable - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['--portable'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertTrue(args.portable, 'The portable flag should be set to true') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_all_no_file(self): - """ - Test the parse options process works with two options - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug', '-d'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertTrue(args.dev_version, 'The dev_version flag should be True') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, [], 'The service file should be blank') - - def test_parse_options_file(self): - """ - Test the parse options process works with a file - - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['dummy_temp'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank') - - def test_parse_options_file_and_debug(self): - """ - Test the parse options process works with a file and the debug log level - """ - # GIVEN: a a set of system arguments. - sys.argv[1:] = ['-l debug', 'dummy_temp'] - # WHEN: We we parse them to expand to options - args = parse_options(None) - # THEN: the following fields will have been extracted. - self.assertFalse(args.dev_version, 'The dev_version flag should be False') - self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug') - self.assertFalse(args.no_error_form, 'The no_error_form should be set to False') - self.assertFalse(args.portable, 'The portable flag should be set to false') - self.assertEquals(args.style, None, 'There are no style flags to be processed') - self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank') - - -@skip('Figure out why this is causing a segfault') -class TestOpenLP(TestCase): - """ - Test the OpenLP app class - """ - @patch('openlp.core.QtWidgets.QApplication.exec') - def test_exec(self, mocked_exec): - """ - Test the exec method - """ - # GIVEN: An app - app = OpenLP([]) - app.shared_memory = MagicMock() - mocked_exec.return_value = False - - # WHEN: exec() is called - result = app.exec() - - # THEN: The right things should be called - assert app.is_event_loop_active is True - mocked_exec.assert_called_once_with() - app.shared_memory.detach.assert_called_once_with() - assert result is False - - @patch('openlp.core.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 - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.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.QtWidgets.QMessageBox.critical') - @patch('openlp.core.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.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.Yes - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.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 - - @patch('openlp.core.QtWidgets.QMessageBox.critical') - @patch('openlp.core.QtWidgets.QMessageBox.StandardButtons') - @patch('openlp.core.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 - app = OpenLP([]) - - # WHEN: is_already_running() is called - result = app.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 - - def test_process_events(self): - """ - Test that the app.process_events() method simply calls the Qt method - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: process_events() is called - with patch.object(app, 'processEvents') as mocked_processEvents: - app.process_events() - - # THEN: processEvents was called - mocked_processEvents.assert_called_once_with() - - def test_set_busy_cursor(self): - """ - Test that the set_busy_cursor() method sets the cursor - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: set_busy_cursor() is called - with patch.object(app, 'setOverrideCursor') as mocked_setOverrideCursor, \ - patch.object(app, 'processEvents') as mocked_processEvents: - app.set_busy_cursor() - - # THEN: The cursor should have been set - mocked_setOverrideCursor.assert_called_once_with(QtCore.Qt.BusyCursor) - mocked_processEvents.assert_called_once_with() - - def test_set_normal_cursor(self): - """ - Test that the set_normal_cursor() method resets the cursor - """ - # GIVEN: An app - app = OpenLP([]) - - # WHEN: set_normal_cursor() is called - with patch.object(app, 'restoreOverrideCursor') as mocked_restoreOverrideCursor, \ - patch.object(app, 'processEvents') as mocked_processEvents: - app.set_normal_cursor() - - # THEN: The cursor should have been set - mocked_restoreOverrideCursor.assert_called_once_with() - mocked_processEvents.assert_called_once_with() diff --git a/tests/functional/openlp_core_ui/__init__.py b/tests/functional/openlp_core/ui/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui/__init__.py rename to tests/functional/openlp_core/ui/__init__.py diff --git a/tests/functional/openlp_core_ui_lib/__init__.py b/tests/functional/openlp_core/ui/lib/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/__init__.py rename to tests/functional/openlp_core/ui/lib/__init__.py diff --git a/tests/functional/openlp_core_ui_lib/test_colorbutton.py b/tests/functional/openlp_core/ui/lib/test_colorbutton.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_colorbutton.py rename to tests/functional/openlp_core/ui/lib/test_colorbutton.py diff --git a/tests/functional/openlp_core_ui_lib/test_filedialog.py b/tests/functional/openlp_core/ui/lib/test_filedialog.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_filedialog.py rename to tests/functional/openlp_core/ui/lib/test_filedialog.py diff --git a/tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py b/tests/functional/openlp_core/ui/lib/test_listpreviewwidget.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_listpreviewwidget.py rename to tests/functional/openlp_core/ui/lib/test_listpreviewwidget.py diff --git a/tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py b/tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_listwidgetwithdnd.py rename to tests/functional/openlp_core/ui/lib/test_listwidgetwithdnd.py diff --git a/tests/functional/openlp_core_ui_lib/test_pathedit.py b/tests/functional/openlp_core/ui/lib/test_pathedit.py similarity index 100% rename from tests/functional/openlp_core_ui_lib/test_pathedit.py rename to tests/functional/openlp_core/ui/lib/test_pathedit.py diff --git a/tests/functional/openlp_core_ui_media/__init__.py b/tests/functional/openlp_core/ui/media/__init__.py similarity index 100% rename from tests/functional/openlp_core_ui_media/__init__.py rename to tests/functional/openlp_core/ui/media/__init__.py diff --git a/tests/functional/openlp_core_ui_media/test_mediacontroller.py b/tests/functional/openlp_core/ui/media/test_mediacontroller.py similarity index 100% rename from tests/functional/openlp_core_ui_media/test_mediacontroller.py rename to tests/functional/openlp_core/ui/media/test_mediacontroller.py diff --git a/tests/functional/openlp_core_ui_media/test_systemplayer.py b/tests/functional/openlp_core/ui/media/test_systemplayer.py similarity index 100% rename from tests/functional/openlp_core_ui_media/test_systemplayer.py rename to tests/functional/openlp_core/ui/media/test_systemplayer.py diff --git a/tests/functional/openlp_core_ui_media/test_vlcplayer.py b/tests/functional/openlp_core/ui/media/test_vlcplayer.py similarity index 100% rename from tests/functional/openlp_core_ui_media/test_vlcplayer.py rename to tests/functional/openlp_core/ui/media/test_vlcplayer.py diff --git a/tests/functional/openlp_core_ui_media/test_webkitplayer.py b/tests/functional/openlp_core/ui/media/test_webkitplayer.py similarity index 100% rename from tests/functional/openlp_core_ui_media/test_webkitplayer.py rename to tests/functional/openlp_core/ui/media/test_webkitplayer.py diff --git a/tests/functional/openlp_core_ui/test_aboutform.py b/tests/functional/openlp_core/ui/test_aboutform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_aboutform.py rename to tests/functional/openlp_core/ui/test_aboutform.py diff --git a/tests/functional/openlp_core_ui/test_advancedtab.py b/tests/functional/openlp_core/ui/test_advancedtab.py similarity index 100% rename from tests/functional/openlp_core_ui/test_advancedtab.py rename to tests/functional/openlp_core/ui/test_advancedtab.py diff --git a/tests/functional/openlp_core_ui/test_exceptionform.py b/tests/functional/openlp_core/ui/test_exceptionform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_exceptionform.py rename to tests/functional/openlp_core/ui/test_exceptionform.py diff --git a/tests/functional/openlp_core_ui/test_first_time.py b/tests/functional/openlp_core/ui/test_first_time.py similarity index 100% rename from tests/functional/openlp_core_ui/test_first_time.py rename to tests/functional/openlp_core/ui/test_first_time.py diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core/ui/test_firsttimeform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_firsttimeform.py rename to tests/functional/openlp_core/ui/test_firsttimeform.py diff --git a/tests/functional/openlp_core_ui/test_formattingtagscontroller.py b/tests/functional/openlp_core/ui/test_formattingtagscontroller.py similarity index 100% rename from tests/functional/openlp_core_ui/test_formattingtagscontroller.py rename to tests/functional/openlp_core/ui/test_formattingtagscontroller.py diff --git a/tests/functional/openlp_core_ui/test_formattingtagsform.py b/tests/functional/openlp_core/ui/test_formattingtagsform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_formattingtagsform.py rename to tests/functional/openlp_core/ui/test_formattingtagsform.py diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core/ui/test_maindisplay.py similarity index 99% rename from tests/functional/openlp_core_ui/test_maindisplay.py rename to tests/functional/openlp_core/ui/test_maindisplay.py index ee8ada17d..1516418f4 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core/ui/test_maindisplay.py @@ -30,7 +30,8 @@ from PyQt5 import QtCore from openlp.core.common import is_macosx from openlp.core.common.path import Path from openlp.core.common.registry import Registry -from openlp.core.lib import ScreenList, PluginManager +from openlp.core.display.screens import ScreenList +from openlp.core.lib import PluginManager from openlp.core.ui import MainDisplay, AudioPlayer from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core/ui/test_mainwindow.py similarity index 100% rename from tests/functional/openlp_core_ui/test_mainwindow.py rename to tests/functional/openlp_core/ui/test_mainwindow.py diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core/ui/test_media.py similarity index 100% rename from tests/functional/openlp_core_ui/test_media.py rename to tests/functional/openlp_core/ui/test_media.py diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core/ui/test_servicemanager.py similarity index 100% rename from tests/functional/openlp_core_ui/test_servicemanager.py rename to tests/functional/openlp_core/ui/test_servicemanager.py diff --git a/tests/functional/openlp_core_ui/test_settingsform.py b/tests/functional/openlp_core/ui/test_settingsform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_settingsform.py rename to tests/functional/openlp_core/ui/test_settingsform.py diff --git a/tests/functional/openlp_core_ui/test_shortcutlistdialog.py b/tests/functional/openlp_core/ui/test_shortcutlistdialog.py similarity index 100% rename from tests/functional/openlp_core_ui/test_shortcutlistdialog.py rename to tests/functional/openlp_core/ui/test_shortcutlistdialog.py diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core/ui/test_slidecontroller.py similarity index 99% rename from tests/functional/openlp_core_ui/test_slidecontroller.py rename to tests/functional/openlp_core/ui/test_slidecontroller.py index d86610ab3..145f779ac 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core/ui/test_slidecontroller.py @@ -27,8 +27,8 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui -from openlp.core import Registry -from openlp.core.lib import ImageSource, ServiceItemAction +from openlp.core.common.registry import Registry +from openlp.core.lib import ServiceItemAction from openlp.core.ui import SlideController, LiveController, PreviewController from openlp.core.ui.slidecontroller import InfoLabel, WIDE_MENU, NON_TEXT_MENU diff --git a/tests/functional/openlp_core_ui/test_style.py b/tests/functional/openlp_core/ui/test_style.py similarity index 100% rename from tests/functional/openlp_core_ui/test_style.py rename to tests/functional/openlp_core/ui/test_style.py diff --git a/tests/functional/openlp_core_ui/test_themeform.py b/tests/functional/openlp_core/ui/test_themeform.py similarity index 100% rename from tests/functional/openlp_core_ui/test_themeform.py rename to tests/functional/openlp_core/ui/test_themeform.py diff --git a/tests/functional/openlp_core_ui/test_thememanager.py b/tests/functional/openlp_core/ui/test_thememanager.py similarity index 100% rename from tests/functional/openlp_core_ui/test_thememanager.py rename to tests/functional/openlp_core/ui/test_thememanager.py diff --git a/tests/functional/openlp_core_ui/test_themetab.py b/tests/functional/openlp_core/ui/test_themetab.py similarity index 100% rename from tests/functional/openlp_core_ui/test_themetab.py rename to tests/functional/openlp_core/ui/test_themetab.py diff --git a/tests/functional/openlp_plugins/media/test_mediaitem.py b/tests/functional/openlp_plugins/media/test_mediaitem.py index d0ed7499f..4f753c237 100644 --- a/tests/functional/openlp_plugins/media/test_mediaitem.py +++ b/tests/functional/openlp_plugins/media/test_mediaitem.py @@ -27,7 +27,7 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore -from openlp.core import Settings +from openlp.core.common.settings import Settings from openlp.core.common.path import Path from openlp.plugins.media.lib.mediaitem import MediaMediaItem diff --git a/tests/functional/openlp_plugins/media/test_mediaplugin.py b/tests/functional/openlp_plugins/media/test_mediaplugin.py index b7cda1fd6..3ab5b3034 100644 --- a/tests/functional/openlp_plugins/media/test_mediaplugin.py +++ b/tests/functional/openlp_plugins/media/test_mediaplugin.py @@ -23,9 +23,9 @@ Test the media plugin """ from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import patch -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.plugins.media.mediaplugin import MediaPlugin, process_check_binary from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index d3debccf0..a7281e062 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -33,7 +33,7 @@ from PyQt5 import QtCore, QtGui from openlp.plugins.presentations.lib.pdfcontroller import PdfController, PdfDocument from openlp.core.common.settings import Settings from openlp.core.common.path import Path -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from tests.utils.constants import TEST_RESOURCES_PATH from tests.helpers.testmixin import TestMixin diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 9a92e3788..191bc4e5a 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -30,11 +30,11 @@ from urllib.error import URLError from PyQt5 import QtWidgets -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.core.common.path import Path from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker from openlp.plugins.songs.lib import Song -from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGIN_PAGE, LOGOUT_URL, BASE_URL +from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL from tests.helpers.songfileimport import SongImportTestHelper from tests.helpers.testmixin import TestMixin @@ -308,7 +308,7 @@ class TestSongSelectImport(TestCase, TestMixin): importer.run_search = True # WHEN: The stop method is called - results = importer.stop() + importer.stop() # THEN: Searching should have stopped self.assertFalse(importer.run_search, 'Searching should have been stopped') diff --git a/tests/functional/openlp_plugins/songusage/test_songusage.py b/tests/functional/openlp_plugins/songusage/test_songusage.py index 728ab9c47..92f972271 100644 --- a/tests/functional/openlp_plugins/songusage/test_songusage.py +++ b/tests/functional/openlp_plugins/songusage/test_songusage.py @@ -25,7 +25,7 @@ This module contains tests for the Songusage plugin. from unittest import TestCase from unittest.mock import MagicMock, patch -from openlp.core import Registry +from openlp.core.common.registry import Registry from openlp.plugins.songusage.lib import upgrade from openlp.plugins.songusage.lib.db import init_schema from openlp.plugins.songusage.songusageplugin import SongUsagePlugin diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py deleted file mode 100644 index 19acccbcd..000000000 --- a/tests/functional/test_init.py +++ /dev/null @@ -1,137 +0,0 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2017 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 # -############################################################################### -""" -Package to test the openlp.core.__init__ package. -""" -import os -from unittest import TestCase -from unittest.mock import MagicMock, patch - -from PyQt5 import QtCore, QtWidgets - -from openlp.core import OpenLP -from openlp.core.common.settings import Settings - -from tests.helpers.testmixin import TestMixin - -TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources')) - - -class TestInit(TestCase, TestMixin): - def setUp(self): - self.build_settings() - with patch('openlp.core.OpenLPMixin.__init__') as constructor: - constructor.return_value = None - self.openlp = OpenLP(list()) - - def tearDown(self): - self.destroy_settings() - del self.openlp - - def test_event(self): - """ - Test the reimplemented event method - """ - # GIVEN: A file path and a QEvent. - file_path = os.path.join(TEST_PATH, 'church.jpg') - mocked_file_method = MagicMock(return_value=file_path) - event = QtCore.QEvent(QtCore.QEvent.FileOpen) - event.file = mocked_file_method - - # WHEN: Call the vent method. - result = self.openlp.event(event) - - # THEN: The path should be inserted. - self.assertTrue(result, "The method should have returned True.") - mocked_file_method.assert_called_once_with() - self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") - - @patch('openlp.core.is_macosx') - def test_application_activate_event(self, mocked_is_macosx): - """ - Test that clicking on the dock icon on Mac OS X restores the main window if it is minimized - """ - # GIVEN: Mac OS X and an ApplicationActivate event - mocked_is_macosx.return_value = True - event = MagicMock() - event.type.return_value = QtCore.QEvent.ApplicationActivate - mocked_main_window = MagicMock() - self.openlp.main_window = mocked_main_window - - # WHEN: The icon in the dock is clicked - result = self.openlp.event(event) - - # THEN: - self.assertTrue(result, "The method should have returned True.") - # self.assertFalse(self.openlp.main_window.isMinimized()) - - def test_backup_on_upgrade_first_install(self): - """ - Test that we don't try to backup on a new install - """ - # GIVEN: Mocked data version and OpenLP version which are the same - old_install = False - MOCKED_VERSION = { - 'full': '2.2.0-bzr000', - 'version': '2.2.0', - 'build': 'bzr000' - } - Settings().setValue('core/application version', '2.2.0') - with patch('openlp.core.get_version') as mocked_get_version,\ - patch('openlp.core.QtWidgets.QMessageBox.question') as mocked_question: - mocked_get_version.return_value = MOCKED_VERSION - mocked_question.return_value = QtWidgets.QMessageBox.No - - # WHEN: We check if a backup should be created - self.openlp.backup_on_upgrade(old_install, False) - - # THEN: It should not ask if we want to create a backup - self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be the same!') - self.assertEqual(mocked_question.call_count, 0, 'No question should have been asked!') - - def test_backup_on_upgrade(self): - """ - Test that we try to backup on a new install - """ - # GIVEN: Mocked data version and OpenLP version which are different - old_install = True - MOCKED_VERSION = { - 'full': '2.2.0-bzr000', - 'version': '2.2.0', - 'build': 'bzr000' - } - Settings().setValue('core/application version', '2.0.5') - self.openlp.splash = MagicMock() - self.openlp.splash.isVisible.return_value = True - with patch('openlp.core.get_version') as mocked_get_version, \ - patch('openlp.core.QtWidgets.QMessageBox.question') as mocked_question: - mocked_get_version.return_value = MOCKED_VERSION - mocked_question.return_value = QtWidgets.QMessageBox.No - - # WHEN: We check if a backup should be created - self.openlp.backup_on_upgrade(old_install, True) - - # THEN: It should ask if we want to create a backup - self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!') - self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!') - self.openlp.splash.hide.assert_called_once_with() - self.openlp.splash.show.assert_called_once_with() diff --git a/tests/interfaces/openlp_core_ui/test_servicemanager.py b/tests/interfaces/openlp_core_ui/test_servicemanager.py index 3d7e41532..1deb1a97f 100644 --- a/tests/interfaces/openlp_core_ui/test_servicemanager.py +++ b/tests/interfaces/openlp_core_ui/test_servicemanager.py @@ -26,10 +26,9 @@ from unittest import TestCase from unittest.mock import MagicMock, patch from openlp.core.common.registry import Registry -from openlp.core.lib import ScreenList, ServiceItem, ItemCapabilities +from openlp.core.display.screens import ScreenList +from openlp.core.lib import ServiceItem, ItemCapabilities from openlp.core.ui.mainwindow import MainWindow -from openlp.core.ui.servicemanager import ServiceManagerList -from openlp.core.lib.serviceitem import ServiceItem from tests.helpers.testmixin import TestMixin @@ -48,18 +47,18 @@ class TestServiceManager(TestCase, TestMixin): ScreenList.create(self.app.desktop()) Registry().register('application', MagicMock()) # Mock classes and methods used by mainwindow. - with patch('openlp.core.ui.mainwindow.SettingsForm') as mocked_settings_form, \ - patch('openlp.core.ui.mainwindow.ImageManager') as mocked_image_manager, \ - patch('openlp.core.ui.mainwindow.LiveController') as mocked_live_controller, \ - patch('openlp.core.ui.mainwindow.PreviewController') as mocked_preview_controller, \ - patch('openlp.core.ui.mainwindow.OpenLPDockWidget') as mocked_dock_widget, \ - patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox') as mocked_q_tool_box_class, \ - patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget') as mocked_add_dock_method, \ - patch('openlp.core.ui.mainwindow.ThemeManager') as mocked_theme_manager, \ - patch('openlp.core.ui.mainwindow.ProjectorManager') as mocked_projector_manager, \ - patch('openlp.core.ui.mainwindow.Renderer') as mocked_renderer, \ - patch('openlp.core.ui.mainwindow.websockets.WebSocketServer') as mocked_websocketserver, \ - patch('openlp.core.ui.mainwindow.server.HttpServer') as mocked_httpserver: + with patch('openlp.core.ui.mainwindow.SettingsForm'), \ + patch('openlp.core.ui.mainwindow.ImageManager'), \ + patch('openlp.core.ui.mainwindow.LiveController'), \ + patch('openlp.core.ui.mainwindow.PreviewController'), \ + patch('openlp.core.ui.mainwindow.OpenLPDockWidget'), \ + patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox'), \ + patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget'), \ + patch('openlp.core.ui.mainwindow.ThemeManager'), \ + patch('openlp.core.ui.mainwindow.ProjectorManager'), \ + patch('openlp.core.ui.mainwindow.Renderer'), \ + patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \ + patch('openlp.core.ui.mainwindow.server.HttpServer'): self.main_window = MainWindow() self.service_manager = Registry().get('service_manager') diff --git a/tests/interfaces/openlp_core_ui/test_settings_form.py b/tests/interfaces/openlp_core_ui/test_settings_form.py index 9c93dc51e..f4ec5ccbc 100644 --- a/tests/interfaces/openlp_core_ui/test_settings_form.py +++ b/tests/interfaces/openlp_core_ui/test_settings_form.py @@ -29,7 +29,7 @@ from PyQt5 import QtCore, QtTest from openlp.core.common.registry import Registry from openlp.core.ui import settingsform -from openlp.core.lib import ScreenList +from openlp.core.display.screens import ScreenList from tests.helpers.testmixin import TestMixin