This commit is contained in:
Raoul Snyman 2018-01-30 21:10:09 -07:00
commit 97172efba5
11 changed files with 317 additions and 293 deletions

View File

@ -36,6 +36,7 @@ from openlp.core.common import md5_hash
from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import translate
from openlp.core.common.mixins import RegistryProperties
from openlp.core.common.path import Path
from openlp.core.common.settings import Settings
from openlp.core.display.render import remove_tags, render_tags, render_chords
from openlp.core.lib import ImageSource, build_icon
@ -428,13 +429,13 @@ class ServiceItem(RegistryProperties):
self.has_original_files = True
if 'background_audio' in header:
self.background_audio = []
for filename in header['background_audio']:
# Give them real file paths.
filepath = str(filename)
if path:
for file_path in header['background_audio']:
# In OpenLP 3.0 we switched to storing Path objects in JSON files
if isinstance(file_path, str):
# Handle service files prior to OpenLP 3.0
# Windows can handle both forward and backward slashes, so we use ntpath to get the basename
filepath = os.path.join(path, ntpath.basename(str(filename)))
self.background_audio.append(filepath)
file_path = Path(path, ntpath.basename(file_path))
self.background_audio.append(file_path)
self.theme_overwritten = header.get('theme_overwritten', False)
if self.service_item_type == ServiceItemType.Text:
for slide in service_item['serviceitem']['data']:
@ -445,8 +446,8 @@ class ServiceItem(RegistryProperties):
if path:
self.has_original_files = False
for text_image in service_item['serviceitem']['data']:
filename = os.path.join(path, text_image)
self.add_from_image(filename, text_image, background)
file_path = os.path.join(path, text_image)
self.add_from_image(file_path, text_image, background)
else:
for text_image in service_item['serviceitem']['data']:
self.add_from_image(text_image['path'], text_image['title'], background)

View File

@ -400,9 +400,9 @@ class FirstTimeForm(QtWidgets.QWizard, UiFirstTimeWizard, RegistryProperties):
if item:
item.setIcon(build_icon(Path(gettempdir(), 'openlp', screenshot)))
def _download_progress(self, count, block_size):
def update_progress(self, count, block_size):
"""
Calculate and display the download progress.
Calculate and display the download progress. This method is called by download_file().
"""
increment = (count * block_size) - self.previous_size
self._increment_progress_bar(None, increment)

View File

@ -63,7 +63,7 @@ class UiFirstTimeWizard(object):
first_time_wizard.setOptions(QtWidgets.QWizard.IndependentPages | QtWidgets.QWizard.NoBackButtonOnStartPage |
QtWidgets.QWizard.NoBackButtonOnLastPage | QtWidgets.QWizard.HaveCustomButton1 |
QtWidgets.QWizard.HaveCustomButton2)
if is_macosx():
if is_macosx(): # pragma: nocover
first_time_wizard.setPixmap(QtWidgets.QWizard.BackgroundPixmap,
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
first_time_wizard.resize(634, 386)

View File

@ -1313,11 +1313,13 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
self.load_progress_bar.setValue(0)
self.application.process_events()
def increment_progress_bar(self):
def increment_progress_bar(self, increment=1):
"""
Increase the Progress Bar value by 1
Increase the Progress Bar by the value in increment.
:param int increment: The value you to increase the progress bar by.
"""
self.load_progress_bar.setValue(self.load_progress_bar.value() + 1)
self.load_progress_bar.setValue(self.load_progress_bar.value() + increment)
self.application.process_events()
def finished_progress_bar(self):
@ -1385,4 +1387,4 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, RegistryProperties):
if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding())
if filename.endswith(('.osz', '.oszl')):
self.service_manager_contents.load_file(filename)
self.service_manager_contents.load_file(Path(filename))

View File

@ -27,8 +27,9 @@ import json
import os
import shutil
import zipfile
from contextlib import suppress
from datetime import datetime, timedelta
from tempfile import mkstemp
from tempfile import NamedTemporaryFile
from PyQt5 import QtCore, QtGui, QtWidgets
@ -36,11 +37,13 @@ from openlp.core.common import ThemeLevel, delete_file
from openlp.core.common.actions import ActionList, CategoryOrder
from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, format_time, translate
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import Path, create_paths, str_to_path
from openlp.core.common.path import Path, str_to_path
from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.common.settings import Settings
from openlp.core.lib import ServiceItem, ItemCapabilities, PluginStatus, build_icon
from openlp.core.lib.exceptions import ValidationError
from openlp.core.lib.ui import critical_error_message_box, create_widget_action, find_and_set_in_combo_box
from openlp.core.ui import ServiceNoteForm, ServiceItemEditForm, StartTimeForm
from openlp.core.widgets.dialogs import FileDialog
@ -449,7 +452,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
else:
file_path = str_to_path(load_file)
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
self.load_file(str(file_path))
self.load_file(file_path)
def save_modified_service(self):
"""
@ -475,7 +478,7 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
elif result == QtWidgets.QMessageBox.Save:
self.decide_save_method()
sender = self.sender()
self.load_file(sender.data())
self.load_file(Path(sender.data()))
def new_file(self):
"""
@ -503,7 +506,32 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
service.append({'openlp_core': core})
return service
def save_file(self, field=None):
def get_write_file_list(self):
"""
Get a list of files used in the service and files that are missing.
:return: A list of files used in the service that exist, and a list of files that don't.
:rtype: (list[openlp.core.common.path.Path], list[openlp.core.common.path.Path])
"""
write_list = []
missing_list = []
for item in self.service_items:
if item['service_item'].uses_file():
for frame in item['service_item'].get_frames():
path_from = item['service_item'].get_frame_path(frame=frame)
if path_from in write_list or path_from in missing_list:
continue
if not os.path.exists(path_from):
missing_list.append(Path(path_from))
else:
write_list.append(Path(path_from))
for audio_path in item['service_item'].background_audio:
if audio_path in write_list:
continue
write_list.append(audio_path)
return write_list, missing_list
def save_file(self):
"""
Save the current service file.
@ -511,178 +539,74 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
there be an error when saving. Audio files are also copied into the service manager directory, and then packaged
into the zip file.
"""
if not self.file_name():
return self.save_file_as()
temp_file, temp_file_name = mkstemp('.osz', 'openlp_')
# We don't need the file handle.
os.close(temp_file)
self.log_debug(temp_file_name)
path_file_name = str(self.file_name())
path, file_name = os.path.split(path_file_name)
base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
file_path = self.file_name()
self.log_debug('ServiceManager.save_file - {name}'.format(name=file_path))
self.application.set_busy_cursor()
service = self.create_basic_service()
write_list = []
missing_list = []
audio_files = []
total_size = 0
self.application.set_busy_cursor()
# Number of items + 1 to zip it
self.main_window.display_progress_bar(len(self.service_items) + 1)
# Get list of missing files, and list of files to write
for item in self.service_items:
if not item['service_item'].uses_file():
continue
for frame in item['service_item'].get_frames():
path_from = item['service_item'].get_frame_path(frame=frame)
if path_from in write_list or path_from in missing_list:
continue
if not os.path.exists(path_from):
missing_list.append(path_from)
else:
write_list.append(path_from)
if missing_list:
self.application.set_normal_cursor()
title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
message = translate('OpenLP.ServiceManager',
'The following file(s) in the service are missing: {name}\n\n'
'These files will be removed if you continue to save.'
).format(name="\n\t".join(missing_list))
answer = QtWidgets.QMessageBox.critical(self, title, message,
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok |
QtWidgets.QMessageBox.Cancel))
if answer == QtWidgets.QMessageBox.Cancel:
self.main_window.finished_progress_bar()
return False
if not self._save_lite:
write_list, missing_list = self.get_write_file_list()
if missing_list:
self.application.set_normal_cursor()
title = translate('OpenLP.ServiceManager', 'Service File(s) Missing')
message = translate('OpenLP.ServiceManager',
'The following file(s) in the service are missing: {name}\n\n'
'These files will be removed if you continue to save.'
).format(name='\n\t'.join(missing_list))
answer = QtWidgets.QMessageBox.critical(self, title, message,
QtWidgets.QMessageBox.StandardButtons(
QtWidgets.QMessageBox.Ok | QtWidgets.QMessageBox.Cancel))
if answer == QtWidgets.QMessageBox.Cancel:
return False
# Check if item contains a missing file.
for item in list(self.service_items):
self.main_window.increment_progress_bar()
item['service_item'].remove_invalid_frames(missing_list)
if item['service_item'].missing_frames():
self.service_items.remove(item)
else:
service_item = item['service_item'].get_service_repr(self._save_lite)
if service_item['header']['background_audio']:
for i, file_name in enumerate(service_item['header']['background_audio']):
new_file = os.path.join('audio', item['service_item'].unique_identifier, str(file_name))
audio_files.append((file_name, new_file))
service_item['header']['background_audio'][i] = new_file
# Add the service item to the service.
service.append({'serviceitem': service_item})
self.repaint_service_list(-1, -1)
for file_item in write_list:
file_size = os.path.getsize(file_item)
total_size += file_size
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
service_content = json.dumps(service)
# Usual Zip file cannot exceed 2GiB, file with Zip64 cannot be extracted using unzip in UNIX.
allow_zip_64 = (total_size > 2147483648 + len(service_content))
self.log_debug('ServiceManager.save_file - allowZip64 is {text}'.format(text=allow_zip_64))
zip_file = None
success = True
self.main_window.increment_progress_bar()
try:
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, allow_zip_64)
# First we add service contents..
zip_file.writestr(service_file_name, service_content)
# Finally add all the listed media files.
for write_from in write_list:
zip_file.write(write_from, write_from)
for audio_from, audio_to in audio_files:
audio_from = str(audio_from)
audio_to = str(audio_to)
if audio_from.startswith('audio'):
# When items are saved, they get new unique_identifier. Let's copy the file to the new location.
# Unused files can be ignored, OpenLP automatically cleans up the service manager dir on exit.
audio_from = os.path.join(self.service_path, audio_from)
save_file = os.path.join(self.service_path, audio_to)
save_path = os.path.split(save_file)[0]
create_paths(Path(save_path))
if not os.path.exists(save_file):
shutil.copy(audio_from, save_file)
zip_file.write(audio_from, audio_to)
except OSError:
self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
success = False
finally:
if zip_file:
zip_file.close()
self.main_window.finished_progress_bar()
self.application.set_normal_cursor()
if success:
try:
shutil.copy(temp_file_name, path_file_name)
except (shutil.Error, PermissionError):
return self.save_file_as()
except OSError as ose:
QtWidgets.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager', 'An error occurred while writing the '
'service file: {error}').format(error=ose.strerror),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
success = False
self.main_window.add_recent_file(path_file_name)
self.set_modified(False)
delete_file(Path(temp_file_name))
return success
def save_local_file(self):
"""
Save the current service file but leave all the file references alone to point to the current machine.
This format is not transportable as it will not contain any files.
"""
if not self.file_name():
return self.save_file_as()
temp_file, temp_file_name = mkstemp('.oszl', 'openlp_')
# We don't need the file handle.
os.close(temp_file)
self.log_debug(temp_file_name)
path_file_name = str(self.file_name())
path, file_name = os.path.split(path_file_name)
base_name = os.path.splitext(file_name)[0]
service_file_name = '{name}.osj'.format(name=base_name)
self.log_debug('ServiceManager.save_file - {name}'.format(name=path_file_name))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', Path(path))
service = self.create_basic_service()
self.application.set_busy_cursor()
# Number of items + 1 to zip it
self.main_window.display_progress_bar(len(self.service_items) + 1)
for item in self.service_items:
self.main_window.increment_progress_bar()
if not self._save_lite:
item['service_item'].remove_invalid_frames(missing_list)
if item['service_item'].missing_frames():
self.service_items.remove(item)
continue
service_item = item['service_item'].get_service_repr(self._save_lite)
# TODO: check for file item on save.
# Add the service item to the service.
service.append({'serviceitem': service_item})
self.main_window.increment_progress_bar()
service_content = json.dumps(service)
zip_file = None
success = True
self.main_window.increment_progress_bar()
self.repaint_service_list(-1, -1)
service_content = json.dumps(service, cls=OpenLPJsonEncoder)
service_content_size = len(bytes(service_content, encoding='utf-8'))
total_size = service_content_size
for file_item in write_list:
total_size += file_item.stat().st_size
self.log_debug('ServiceManager.save_file - ZIP contents size is %i bytes' % total_size)
self.main_window.display_progress_bar(total_size)
try:
zip_file = zipfile.ZipFile(temp_file_name, 'w', zipfile.ZIP_STORED, True)
# First we add service contents.
zip_file.writestr(service_file_name, service_content)
except OSError:
self.log_exception('Failed to save service to disk: {name}'.format(name=temp_file_name))
self.main_window.error_message(translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager', 'There was an error saving your file.'))
success = False
finally:
if zip_file:
zip_file.close()
with NamedTemporaryFile(dir=str(file_path.parent), prefix='.') as temp_file, \
zipfile.ZipFile(temp_file, 'w') as zip_file:
# First we add service contents..
zip_file.writestr('service_data.osj', service_content)
self.main_window.increment_progress_bar(service_content_size)
# Finally add all the listed media files.
for write_path in write_list:
zip_file.write(str(write_path), str(write_path))
self.main_window.increment_progress_bar(write_path.stat().st_size)
with suppress(FileNotFoundError):
file_path.unlink()
os.link(temp_file.name, str(file_path))
Settings().setValue(self.main_window.service_manager_settings_section + '/last directory', file_path.parent)
except (PermissionError, OSError) as error:
self.log_exception('Failed to save service to disk: {name}'.format(name=file_path))
self.main_window.error_message(
translate('OpenLP.ServiceManager', 'Error Saving File'),
translate('OpenLP.ServiceManager',
'There was an error saving your file.\n\n{error}').format(error=error))
return self.save_file_as()
self.main_window.finished_progress_bar()
self.application.set_normal_cursor()
if success:
try:
shutil.copy(temp_file_name, path_file_name)
except (shutil.Error, PermissionError):
return self.save_file_as()
self.main_window.add_recent_file(path_file_name)
self.set_modified(False)
delete_file(Path(temp_file_name))
return success
self.main_window.add_recent_file(file_path)
self.set_modified(False)
return True
def save_file_as(self, field=None):
"""
@ -743,87 +667,49 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
"""
if not self.file_name():
return self.save_file_as()
if self._save_lite:
return self.save_local_file()
else:
return self.save_file()
return self.save_file()
def load_file(self, file_name):
def load_file(self, file_path):
"""
Load an existing service file
:param file_name:
:param file_path:
"""
if not file_name:
if not file_path.exists():
return False
file_name = str(file_name)
if not os.path.exists(file_name):
return False
zip_file = None
file_to = None
service_data = None
self.application.set_busy_cursor()
try:
zip_file = zipfile.ZipFile(file_name)
for zip_info in zip_file.infolist():
try:
ucs_file = zip_info.filename
except UnicodeDecodeError:
self.log_exception('file_name "{name}" is not valid UTF-8'.format(name=zip_info.file_name))
critical_error_message_box(message=translate('OpenLP.ServiceManager',
'File is not a valid service.\n The content encoding is not UTF-8.'))
continue
os_file = ucs_file.replace('/', os.path.sep)
os_file = os.path.basename(os_file)
self.log_debug('Extract file: {name}'.format(name=os_file))
zip_info.filename = os_file
zip_file.extract(zip_info, self.service_path)
if os_file.endswith('osj') or os_file.endswith('osd'):
p_file = os.path.join(self.service_path, os_file)
if 'p_file' in locals():
file_to = open(p_file, 'r')
if p_file.endswith('osj'):
items = json.load(file_to)
else:
critical_error_message_box(message=translate('OpenLP.ServiceManager',
'The service file you are trying to open is in an old '
'format.\n Please save it using OpenLP 2.0.2 or '
'greater.'))
return
file_to.close()
with zipfile.ZipFile(str(file_path)) as zip_file:
compressed_size = 0
for zip_info in zip_file.infolist():
compressed_size += zip_info.compress_size
self.main_window.display_progress_bar(compressed_size)
for zip_info in zip_file.infolist():
self.log_debug('Extract file: {name}'.format(name=zip_info.filename))
# The json file has been called 'service_data.osj' since OpenLP 3.0
if zip_info.filename == 'service_data.osj' or zip_info.filename.endswith('osj'):
with zip_file.open(zip_info, 'r') as json_file:
service_data = json_file.read()
else:
zip_info.filename = os.path.basename(zip_info.filename)
zip_file.extract(zip_info, str(self.service_path))
self.main_window.increment_progress_bar(zip_info.compress_size)
if service_data:
items = json.loads(service_data, cls=OpenLPJsonDecoder)
self.new_file()
self.set_file_name(str_to_path(file_name))
self.main_window.display_progress_bar(len(items))
self.process_service_items(items)
delete_file(Path(p_file))
self.main_window.add_recent_file(file_name)
self.set_file_name(file_path)
self.main_window.add_recent_file(file_path)
self.set_modified(False)
Settings().setValue('servicemanager/last file', Path(file_name))
Settings().setValue('servicemanager/last file', file_path)
else:
critical_error_message_box(message=translate('OpenLP.ServiceManager', 'File is not a valid service.'))
self.log_error('File contains no service data')
except (OSError, NameError):
self.log_exception('Problem loading service file {name}'.format(name=file_name))
critical_error_message_box(message=translate('OpenLP.ServiceManager',
'File could not be opened because it is corrupt.'))
except zipfile.BadZipFile:
if os.path.getsize(file_name) == 0:
self.log_exception('Service file is zero sized: {name}'.format(name=file_name))
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Empty File'),
translate('OpenLP.ServiceManager',
'This service file does not contain '
'any data.'))
else:
self.log_exception('Service file is cannot be extracted as zip: {name}'.format(name=file_name))
QtWidgets.QMessageBox.information(self, translate('OpenLP.ServiceManager', 'Corrupt File'),
translate('OpenLP.ServiceManager',
'This file is either corrupt or it is not an OpenLP 2 '
'service file.'))
self.application.set_normal_cursor()
return
finally:
if file_to:
file_to.close()
if zip_file:
zip_file.close()
raise ValidationError(msg='No service data found')
except (NameError, OSError, ValidationError, zipfile.BadZipFile) as e:
self.log_exception('Problem loading service file {name}'.format(name=file_path))
critical_error_message_box(
message=translate('OpenLP.ServiceManager',
'The service file {file_path} could not be loaded because it is either corrupt, or '
'not a valid OpenLP 2 or OpenLP 3 service file.'.format(file_path=file_path)))
self.main_window.finished_progress_bar()
self.application.set_normal_cursor()
self.repaint_service_list(-1, -1)
@ -838,7 +724,8 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
self.main_window.increment_progress_bar()
service_item = ServiceItem()
if 'openlp_core' in item:
item = item.get('openlp_core')
item = item['openlp_core']
self._save_lite = item.get('lite-service', False)
theme = item.get('service-theme', None)
if theme:
find_and_set_in_combo_box(self.theme_combo_box, theme, set_missing=False)
@ -863,9 +750,9 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
Load the last service item from the service manager when the service was last closed. Can be blank if there was
no service present.
"""
file_name = str_to_path(Settings().value('servicemanager/last file'))
if file_name:
self.load_file(file_name)
file_path = Settings().value('servicemanager/last file')
if file_path:
self.load_file(file_path)
def context_menu(self, point):
"""

View File

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Functional tests to test the remote index
"""
from unittest.mock import MagicMock, patch
from openlp.core.api.endpoint.remote import index
from openlp.core.api.endpoint.core import TRANSLATED_STRINGS
@patch('openlp.core.api.endpoint.remote.remote_endpoint')
def test_index(mocked_endpoint):
"""
Test the index method of the remote
"""
# GIVEN: A mocked Endpoint
mocked_endpoint.render_template.return_value = 'test template'
# WHEN: index is called
result = index(MagicMock(), 'index')
# THEN: The result should be "test template" and the right methods should have been called
mocked_endpoint.render_template.assert_called_once_with('index.mako', **TRANSLATED_STRINGS)
assert result == 'test template'

View File

@ -23,7 +23,7 @@
Package to test the openlp.core.lib.pluginmanager package.
"""
from unittest import TestCase
from unittest.mock import MagicMock
from unittest.mock import MagicMock, patch
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
@ -50,6 +50,32 @@ class TestPluginManager(TestCase):
Registry().register('main_window', self.mocked_main_window)
Registry().register('settings_form', self.mocked_settings_form)
def test_bootstrap_initialise(self):
"""
Test the PluginManager.bootstrap_initialise() method
"""
# GIVEN: A plugin manager with some mocked out methods
manager = PluginManager()
with patch.object(manager, 'find_plugins') as mocked_find_plugins, \
patch.object(manager, 'hook_settings_tabs') as mocked_hook_settings_tabs, \
patch.object(manager, 'hook_media_manager') as mocked_hook_media_manager, \
patch.object(manager, 'hook_import_menu') as mocked_hook_import_menu, \
patch.object(manager, 'hook_export_menu') as mocked_hook_export_menu, \
patch.object(manager, 'hook_tools_menu') as mocked_hook_tools_menu, \
patch.object(manager, 'initialise_plugins') as mocked_initialise_plugins:
# WHEN: bootstrap_initialise() is called
manager.bootstrap_initialise()
# THEN: The hook methods should have been called
mocked_find_plugins.assert_called_with()
mocked_hook_settings_tabs.assert_called_with()
mocked_hook_media_manager.assert_called_with()
mocked_hook_import_menu.assert_called_with()
mocked_hook_export_menu.assert_called_with()
mocked_hook_tools_menu.assert_called_with()
mocked_initialise_plugins.assert_called_with()
def test_hook_media_manager_with_disabled_plugin(self):
"""
Test running the hook_media_manager() method with a disabled plugin

View File

@ -27,6 +27,7 @@ from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.common import md5_hash
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 ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags
@ -351,5 +352,5 @@ class TestServiceItem(TestCase, TestMixin):
'"Amazing Grace! how sweet the s" has been returned as the title'
assert 'Twas grace that taught my hea' == service_item.get_frame_title(1), \
'"Twas grace that taught my hea" has been returned as the title'
assert '/test/amazing_grace.mp3' == service_item.background_audio[0], \
assert Path('/test/amazing_grace.mp3') == service_item.background_audio[0], \
'"/test/amazing_grace.mp3" should be in the background_audio list'

View File

@ -23,6 +23,7 @@
Package to test openlp.core.ui.mainwindow package.
"""
import os
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, patch
@ -87,14 +88,13 @@ class TestMainWindow(TestCase, TestMixin):
"""
# GIVEN a service as an argument to openlp
service = os.path.join(TEST_RESOURCES_PATH, 'service', 'test.osz')
self.main_window.arguments = [service]
# WHEN the argument is processed
with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
self.main_window.open_cmd_line_files(service)
# THEN the service from the arguments is loaded
mocked_load_file.assert_called_with(service)
mocked_load_file.assert_called_with(Path(service))
@patch('openlp.core.ui.servicemanager.ServiceManager.load_file')
def test_cmd_line_arg(self, mocked_load_file):
@ -253,3 +253,30 @@ class TestMainWindow(TestCase, TestMixin):
# THEN: projector_manager_dock.setVisible should had been called once
mocked_dock.setVisible.assert_called_once_with(False)
def test_increment_progress_bar_default_increment(self):
"""
Test that increment_progress_bar increments the progress bar by 1 when called without the `increment` arg.
"""
# GIVEN: A mocked progress bar
with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
# WHEN: Calling increment_progress_bar without the `increment` arg
self.main_window.increment_progress_bar()
# THEN: The progress bar value should have been incremented by 1
mocked_progress_bar.setValue.assert_called_once_with(1)
def test_increment_progress_bar_custom_increment(self):
"""
Test that increment_progress_bar increments the progress bar by the `increment` arg when called with the
`increment` arg with a set value.
"""
# GIVEN: A mocked progress bar
with patch.object(self.main_window, 'load_progress_bar', **{'value.return_value': 0}) as mocked_progress_bar:
# WHEN: Calling increment_progress_bar with `increment` set to 10
self.main_window.increment_progress_bar(increment=10)
# THEN: The progress bar value should have been incremented by 10
mocked_progress_bar.setValue.assert_called_once_with(10)

View File

@ -623,10 +623,10 @@ class TestServiceManager(TestCase):
# THEN: make_preview() should not have been called
assert mocked_make_preview.call_count == 0, 'ServiceManager.make_preview() should not be called'
@patch('openlp.core.ui.servicemanager.shutil.copy')
@patch('openlp.core.ui.servicemanager.zipfile')
@patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
@patch('openlp.core.ui.servicemanager.os')
def test_save_file_raises_permission_error(self, mocked_os, mocked_save_file_as, mocked_zipfile):
"""
Test that when a PermissionError is raised when trying to save a file, it is handled correctly
"""
@ -636,50 +636,22 @@ class TestServiceManager(TestCase):
Registry().register('main_window', mocked_main_window)
Registry().register('application', MagicMock())
service_manager = ServiceManager(None)
service_manager._service_path = os.path.join('temp', 'filename.osz')
service_manager._service_path = MagicMock()
service_manager._save_lite = False
service_manager.service_items = []
service_manager.service_theme = 'Default'
service_manager.service_manager_list = MagicMock()
mocked_save_file_as.return_value = True
mocked_zipfile.ZipFile.return_value = MagicMock()
mocked_shutil_copy.side_effect = PermissionError
mocked_os.link.side_effect = PermissionError
# WHEN: The service is saved and a PermissionError is thrown
# WHEN: The service is saved and a PermissionError is raised
result = service_manager.save_file()
# THEN: The "save_as" method is called to save the service
assert result is True
mocked_save_file_as.assert_called_with()
@patch('openlp.core.ui.servicemanager.shutil.copy')
@patch('openlp.core.ui.servicemanager.zipfile')
@patch('openlp.core.ui.servicemanager.ServiceManager.save_file_as')
def test_save_local_file_raises_permission_error(self, mocked_save_file_as, mocked_zipfile, mocked_shutil_copy):
"""
Test that when a PermissionError is raised when trying to save a local file, it is handled correctly
"""
# GIVEN: A service manager, a service to save
mocked_main_window = MagicMock()
mocked_main_window.service_manager_settings_section = 'servicemanager'
Registry().register('main_window', mocked_main_window)
Registry().register('application', MagicMock())
service_manager = ServiceManager(None)
service_manager._service_path = os.path.join('temp', 'filename.osz')
service_manager._save_lite = False
service_manager.service_items = []
service_manager.service_theme = 'Default'
mocked_save_file_as.return_value = True
mocked_zipfile.ZipFile.return_value = MagicMock()
mocked_shutil_copy.side_effect = PermissionError
# WHEN: The service is saved and a PermissionError is thrown
result = service_manager.save_local_file()
# THEN: The "save_as" method is called to save the service
assert result is True
mocked_save_file_as.assert_called_with()
@patch('openlp.core.ui.servicemanager.ServiceManager.regenerate_service_items')
def test_theme_change_global(self, mocked_regenerate_service_items):
"""

View File

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2018 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Test the openlp.core.ui package.
"""
from unittest.mock import MagicMock, patch
from openlp.core.ui import SingleColumnTableWidget
def test_single_col_widget_create():
"""
Test creating the SingleColumnTableWidget object
"""
# GIVEN: the SingleColumnTableWidget class
# WHEN: An object is created
widget = SingleColumnTableWidget(None)
# THEN: The object should have 1 column and no visible header
assert widget.columnCount() == 1, 'There should be only 1 column'
assert widget.horizontalHeader().isVisible() is False, 'The horizontal header should not be visible'
@patch('openlp.core.ui.QtWidgets.QTableWidget')
def test_single_col_widget_resize_event(MockQTableWidget):
"""
Test that the resizeEvent method does the right thing
"""
# GIVEN: An instance of a SingleColumnTableWidget and a mocked event
widget = SingleColumnTableWidget(None)
mocked_event = MagicMock()
mocked_event.size.return_value.width.return_value = 10
# WHEN: resizeEvent() is called
with patch.object(widget, 'columnCount') as mocked_column_count, \
patch.object(widget, 'setColumnWidth') as mocked_set_column_width, \
patch.object(widget, 'resizeRowsToContents') as mocked_resize_rows_to_contents:
mocked_column_count.return_value = 1
widget.resizeEvent(mocked_event)
# THEN: The correct calls should have been made
MockQTableWidget.resizeEvent.assert_called_once_with(widget, mocked_event)
mocked_column_count.assert_called_once_with()
mocked_set_column_width.assert_called_once_with(0, 10)
mocked_resize_rows_to_contents.assert_called_once_with()