forked from openlp/openlp
Head
This commit is contained in:
commit
d0a9423982
@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# OpenLP - Open Source Lyrics Projection #
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
@ -184,7 +184,8 @@ class OpenLP(QtGui.QApplication):
|
|||||||
``traceback``
|
``traceback``
|
||||||
A traceback object with the details of where the exception occurred.
|
A traceback object with the details of where the exception occurred.
|
||||||
"""
|
"""
|
||||||
log.exception(''.join(format_exception(exctype, value, traceback)))
|
# We can't log.exception here because the last exception no longer exists, we're actually busy handling it.
|
||||||
|
log.critical(''.join(format_exception(exctype, value, traceback)))
|
||||||
if not hasattr(self, 'exception_form'):
|
if not hasattr(self, 'exception_form'):
|
||||||
self.exception_form = ExceptionForm()
|
self.exception_form = ExceptionForm()
|
||||||
self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback)))
|
self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback)))
|
||||||
|
@ -83,6 +83,8 @@ class UiStrings(object):
|
|||||||
self.Error = translate('OpenLP.Ui', 'Error')
|
self.Error = translate('OpenLP.Ui', 'Error')
|
||||||
self.Export = translate('OpenLP.Ui', 'Export')
|
self.Export = translate('OpenLP.Ui', 'Export')
|
||||||
self.File = translate('OpenLP.Ui', 'File')
|
self.File = translate('OpenLP.Ui', 'File')
|
||||||
|
self.FileNotFound = translate('OpenLP.Ui', 'File Not Found')
|
||||||
|
self.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.')
|
||||||
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
|
self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit')
|
||||||
self.Help = translate('OpenLP.Ui', 'Help')
|
self.Help = translate('OpenLP.Ui', 'Help')
|
||||||
self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours')
|
self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours')
|
||||||
|
@ -330,6 +330,7 @@ def create_separated_list(string_list):
|
|||||||
|
|
||||||
|
|
||||||
from .registry import Registry
|
from .registry import Registry
|
||||||
|
from .filedialog import FileDialog
|
||||||
from .screen import ScreenList
|
from .screen import ScreenList
|
||||||
from .listwidgetwithdnd import ListWidgetWithDnD
|
from .listwidgetwithdnd import ListWidgetWithDnD
|
||||||
from .treewidgetwithdnd import TreeWidgetWithDnD
|
from .treewidgetwithdnd import TreeWidgetWithDnD
|
||||||
@ -345,4 +346,3 @@ from .dockwidget import OpenLPDockWidget
|
|||||||
from .imagemanager import ImageManager
|
from .imagemanager import ImageManager
|
||||||
from .renderer import Renderer
|
from .renderer import Renderer
|
||||||
from .mediamanageritem import MediaManagerItem
|
from .mediamanageritem import MediaManagerItem
|
||||||
|
|
||||||
|
66
openlp/core/lib/filedialog.py
Normal file
66
openlp/core/lib/filedialog.py
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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 #
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
"""
|
||||||
|
Provide a work around for a bug in QFileDialog <https://bugs.launchpad.net/openlp/+bug/1209515>
|
||||||
|
"""
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from urllib import parse
|
||||||
|
|
||||||
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
|
from openlp.core.common import UiStrings
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class FileDialog(QtGui.QFileDialog):
|
||||||
|
"""
|
||||||
|
Subclass QFileDialog to work round a bug
|
||||||
|
"""
|
||||||
|
@staticmethod
|
||||||
|
def getOpenFileNames(parent, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Reimplement getOpenFileNames to fix the way it returns some file names that url encoded when selecting multiple
|
||||||
|
files
|
||||||
|
"""
|
||||||
|
files = QtGui.QFileDialog.getOpenFileNames(parent, *args, **kwargs)
|
||||||
|
file_list = []
|
||||||
|
for file in files:
|
||||||
|
if not os.path.exists(file):
|
||||||
|
log.info('File %s not found. Attempting to unquote.')
|
||||||
|
file = parse.unquote(file)
|
||||||
|
if not os.path.exists(file):
|
||||||
|
log.error('File %s not found.' % file)
|
||||||
|
QtGui.QMessageBox.information(parent, UiStrings().FileNotFound,
|
||||||
|
UiStrings().FileNotFoundMessage % file)
|
||||||
|
continue
|
||||||
|
log.info('File %s found.')
|
||||||
|
file_list.append(file)
|
||||||
|
return file_list
|
@ -36,7 +36,7 @@ import re
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.common import Settings, UiStrings, translate
|
from openlp.core.common import Settings, UiStrings, translate
|
||||||
from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \
|
from openlp.core.lib import FileDialog, OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \
|
||||||
ServiceItemContext, Registry
|
ServiceItemContext, Registry
|
||||||
from openlp.core.lib.searchedit import SearchEdit
|
from openlp.core.lib.searchedit import SearchEdit
|
||||||
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
from openlp.core.lib.ui import create_widget_action, critical_error_message_box
|
||||||
@ -319,7 +319,7 @@ class MediaManagerItem(QtGui.QWidget):
|
|||||||
"""
|
"""
|
||||||
Add a file to the list widget to make it available for showing
|
Add a file to the list widget to make it available for showing
|
||||||
"""
|
"""
|
||||||
files = QtGui.QFileDialog.getOpenFileNames(self, self.on_new_prompt,
|
files = FileDialog.getOpenFileNames(self, self.on_new_prompt,
|
||||||
Settings().value(self.settings_section + '/last directory'), self.on_new_file_masks)
|
Settings().value(self.settings_section + '/last directory'), self.on_new_file_masks)
|
||||||
log.info('New files(s) %s', files)
|
log.info('New files(s) %s', files)
|
||||||
if files:
|
if files:
|
||||||
|
@ -283,7 +283,7 @@ class AdvancedTab(SettingsTab):
|
|||||||
self.service_name_day.setItemText(0, translate('OpenLP.AdvancedTab', 'Monday'))
|
self.service_name_day.setItemText(0, translate('OpenLP.AdvancedTab', 'Monday'))
|
||||||
self.service_name_day.setItemText(1, translate('OpenLP.AdvancedTab', 'Tuesday'))
|
self.service_name_day.setItemText(1, translate('OpenLP.AdvancedTab', 'Tuesday'))
|
||||||
self.service_name_day.setItemText(2, translate('OpenLP.AdvancedTab', 'Wednesday'))
|
self.service_name_day.setItemText(2, translate('OpenLP.AdvancedTab', 'Wednesday'))
|
||||||
self.service_name_day.setItemText(3, translate('OpenLP.AdvancedTab', 'Thurdsday'))
|
self.service_name_day.setItemText(3, translate('OpenLP.AdvancedTab', 'Thursday'))
|
||||||
self.service_name_day.setItemText(4, translate('OpenLP.AdvancedTab', 'Friday'))
|
self.service_name_day.setItemText(4, translate('OpenLP.AdvancedTab', 'Friday'))
|
||||||
self.service_name_day.setItemText(5, translate('OpenLP.AdvancedTab', 'Saturday'))
|
self.service_name_day.setItemText(5, translate('OpenLP.AdvancedTab', 'Saturday'))
|
||||||
self.service_name_day.setItemText(6, translate('OpenLP.AdvancedTab', 'Sunday'))
|
self.service_name_day.setItemText(6, translate('OpenLP.AdvancedTab', 'Sunday'))
|
||||||
|
@ -101,7 +101,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
|||||||
"""
|
"""
|
||||||
Constructor.
|
Constructor.
|
||||||
"""
|
"""
|
||||||
super(ExceptionForm, self).__init__(self.main_window)
|
super(ExceptionForm, self).__init__()
|
||||||
self.setupUi(self)
|
self.setupUi(self)
|
||||||
self.settings_section = 'crashreport'
|
self.settings_section = 'crashreport'
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ from xml.etree.ElementTree import ElementTree, XML
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
|
from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate
|
||||||
from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \
|
from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \
|
||||||
check_item_selected, create_thumb, validate_thumb
|
check_item_selected, create_thumb, validate_thumb
|
||||||
from openlp.core.lib.theme import ThemeXML, BackgroundType
|
from openlp.core.lib.theme import ThemeXML, BackgroundType
|
||||||
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
from openlp.core.lib.ui import critical_error_message_box, create_widget_action
|
||||||
@ -365,7 +365,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper):
|
|||||||
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
|
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
|
||||||
those files. This process will load both OpenLP version 1 and version 2 themes.
|
those files. This process will load both OpenLP version 1 and version 2 themes.
|
||||||
"""
|
"""
|
||||||
files = QtGui.QFileDialog.getOpenFileNames(self,
|
files = FileDialog.getOpenFileNames(self,
|
||||||
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
translate('OpenLP.ThemeManager', 'Select Theme Import File'),
|
||||||
Settings().value(self.settings_section + '/last directory import'),
|
Settings().value(self.settings_section + '/last directory import'),
|
||||||
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
|
translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)'))
|
||||||
|
@ -379,7 +379,7 @@ class BSExtract(object):
|
|||||||
send_error_message('parse')
|
send_error_message('parse')
|
||||||
return None
|
return None
|
||||||
content = content.find_all('li')
|
content = content.find_all('li')
|
||||||
return [book.contents[0].contents[0] for book in content]
|
return [book.contents[0].contents[0] for book in content if len(book.contents[0].contents)]
|
||||||
|
|
||||||
def _get_application(self):
|
def _get_application(self):
|
||||||
"""
|
"""
|
||||||
|
@ -128,6 +128,15 @@ from openlp.core.common import AppLocation, Settings, translate
|
|||||||
from openlp.core.lib import Registry, PluginStatus, StringContent, image_to_byte
|
from openlp.core.lib import Registry, PluginStatus, StringContent, image_to_byte
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
FILE_TYPES = {
|
||||||
|
'.html': 'text/html',
|
||||||
|
'.css': 'text/css',
|
||||||
|
'.js': 'application/javascript',
|
||||||
|
'.jpg': 'image/jpeg',
|
||||||
|
'.gif': 'image/gif',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.png': 'image/png'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class HttpRouter(object):
|
class HttpRouter(object):
|
||||||
@ -346,30 +355,13 @@ class HttpRouter(object):
|
|||||||
path = os.path.normpath(os.path.join(self.html_dir, file_name))
|
path = os.path.normpath(os.path.join(self.html_dir, file_name))
|
||||||
if not path.startswith(self.html_dir):
|
if not path.startswith(self.html_dir):
|
||||||
return self.do_not_found()
|
return self.do_not_found()
|
||||||
ext = os.path.splitext(file_name)[1]
|
content = None
|
||||||
html = None
|
ext, content_type = self.get_content_type(path)
|
||||||
if ext == '.html':
|
|
||||||
self.send_header('Content-type', 'text/html')
|
|
||||||
variables = self.template_vars
|
|
||||||
html = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
|
|
||||||
elif ext == '.css':
|
|
||||||
self.send_header('Content-type', 'text/css')
|
|
||||||
elif ext == '.js':
|
|
||||||
self.send_header('Content-type', 'application/javascript')
|
|
||||||
elif ext == '.jpg':
|
|
||||||
self.send_header('Content-type', 'image/jpeg')
|
|
||||||
elif ext == '.gif':
|
|
||||||
self.send_header('Content-type', 'image/gif')
|
|
||||||
elif ext == '.ico':
|
|
||||||
self.send_header('Content-type', 'image/x-icon')
|
|
||||||
elif ext == '.png':
|
|
||||||
self.send_header('Content-type', 'image/png')
|
|
||||||
else:
|
|
||||||
self.send_header('Content-type', 'text/plain')
|
|
||||||
file_handle = None
|
file_handle = None
|
||||||
try:
|
try:
|
||||||
if html:
|
if ext == '.html':
|
||||||
content = html
|
variables = self.template_vars
|
||||||
|
content = Template(filename=path, input_encoding='utf-8', output_encoding='utf-8').render(**variables)
|
||||||
else:
|
else:
|
||||||
file_handle = open(path, 'rb')
|
file_handle = open(path, 'rb')
|
||||||
log.debug('Opened %s' % path)
|
log.debug('Opened %s' % path)
|
||||||
@ -380,8 +372,22 @@ class HttpRouter(object):
|
|||||||
finally:
|
finally:
|
||||||
if file_handle:
|
if file_handle:
|
||||||
file_handle.close()
|
file_handle.close()
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header('Content-type', content_type)
|
||||||
|
self.end_headers()
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
def get_content_type(self, file_name):
|
||||||
|
"""
|
||||||
|
Examines the extension of the file and determines
|
||||||
|
what the content_type should be, defaults to text/plain
|
||||||
|
Returns the extension and the content_type
|
||||||
|
"""
|
||||||
|
content_type = 'text/plain'
|
||||||
|
ext = os.path.splitext(file_name)[1]
|
||||||
|
content_type = FILE_TYPES.get(ext, 'text/plain')
|
||||||
|
return ext, content_type
|
||||||
|
|
||||||
def poll(self):
|
def poll(self):
|
||||||
"""
|
"""
|
||||||
Poll OpenLP to determine the current slide number and item name.
|
Poll OpenLP to determine the current slide number and item name.
|
||||||
|
@ -39,7 +39,7 @@ import shutil
|
|||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
|
|
||||||
from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate
|
from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate
|
||||||
from openlp.core.lib import Registry, PluginStatus, MediaType, create_separated_list
|
from openlp.core.lib import FileDialog, Registry, PluginStatus, MediaType, create_separated_list
|
||||||
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box
|
||||||
from openlp.plugins.songs.lib import VerseType, clean_song
|
from openlp.plugins.songs.lib import VerseType, clean_song
|
||||||
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
|
from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile
|
||||||
@ -758,7 +758,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
|
|||||||
Loads file(s) from the filesystem.
|
Loads file(s) from the filesystem.
|
||||||
"""
|
"""
|
||||||
filters = '%s (*)' % UiStrings().AllFiles
|
filters = '%s (*)' % UiStrings().AllFiles
|
||||||
filenames = QtGui.QFileDialog.getOpenFileNames(self,
|
filenames = FileDialog.getOpenFileNames(self,
|
||||||
translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', filters)
|
translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', filters)
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
item = QtGui.QListWidgetItem(os.path.split(str(filename))[1])
|
item = QtGui.QListWidgetItem(os.path.split(str(filename))[1])
|
||||||
|
@ -37,7 +37,7 @@ from PyQt4 import QtCore, QtGui
|
|||||||
|
|
||||||
from openlp.core.common import UiStrings, translate
|
from openlp.core.common import UiStrings, translate
|
||||||
from openlp.core.common import Settings
|
from openlp.core.common import Settings
|
||||||
from openlp.core.lib import Registry
|
from openlp.core.lib import FileDialog, Registry
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
from openlp.core.ui.wizard import OpenLPWizard, WizardStrings
|
||||||
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
|
from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect
|
||||||
@ -246,7 +246,7 @@ class SongImportForm(OpenLPWizard):
|
|||||||
if filters:
|
if filters:
|
||||||
filters += ';;'
|
filters += ';;'
|
||||||
filters += '%s (*)' % UiStrings().AllFiles
|
filters += '%s (*)' % UiStrings().AllFiles
|
||||||
filenames = QtGui.QFileDialog.getOpenFileNames(self, title,
|
filenames = FileDialog.getOpenFileNames(self, title,
|
||||||
Settings().value(self.plugin.settings_section + '/last directory import'), filters)
|
Settings().value(self.plugin.settings_section + '/last directory import'), filters)
|
||||||
if filenames:
|
if filenames:
|
||||||
listbox.addItems(filenames)
|
listbox.addItems(filenames)
|
||||||
|
@ -102,10 +102,10 @@ class SongBeamerImport(SongImport):
|
|||||||
"""
|
"""
|
||||||
Receive a single file or a list of files to import.
|
Receive a single file or a list of files to import.
|
||||||
"""
|
"""
|
||||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
|
||||||
if not isinstance(self.import_source, list):
|
if not isinstance(self.import_source, list):
|
||||||
return
|
return
|
||||||
for file in self.import_source:
|
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||||
|
for import_file in self.import_source:
|
||||||
# TODO: check that it is a valid SongBeamer file
|
# TODO: check that it is a valid SongBeamer file
|
||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
@ -113,12 +113,13 @@ class SongBeamerImport(SongImport):
|
|||||||
self.currentVerse = ''
|
self.currentVerse = ''
|
||||||
self.currentVerseType = VerseType.tags[VerseType.Verse]
|
self.currentVerseType = VerseType.tags[VerseType.Verse]
|
||||||
read_verses = False
|
read_verses = False
|
||||||
file_name = os.path.split(file)[1]
|
file_name = os.path.split(import_file)[1]
|
||||||
if os.path.isfile(file):
|
if os.path.isfile(import_file):
|
||||||
detect_file = open(file, 'r')
|
# First open in binary mode to detect the encoding
|
||||||
|
detect_file = open(import_file, 'rb')
|
||||||
details = chardet.detect(detect_file.read())
|
details = chardet.detect(detect_file.read())
|
||||||
detect_file.close()
|
detect_file.close()
|
||||||
infile = codecs.open(file, 'r', details['encoding'])
|
infile = codecs.open(import_file, 'r', details['encoding'])
|
||||||
song_data = infile.readlines()
|
song_data = infile.readlines()
|
||||||
infile.close()
|
infile.close()
|
||||||
else:
|
else:
|
||||||
@ -149,7 +150,7 @@ class SongBeamerImport(SongImport):
|
|||||||
self.replaceHtmlTags()
|
self.replaceHtmlTags()
|
||||||
self.addVerse(self.currentVerse, self.currentVerseType)
|
self.addVerse(self.currentVerse, self.currentVerseType)
|
||||||
if not self.finish():
|
if not self.finish():
|
||||||
self.logError(file)
|
self.logError(import_file)
|
||||||
|
|
||||||
def replaceHtmlTags(self):
|
def replaceHtmlTags(self):
|
||||||
"""
|
"""
|
||||||
|
73
tests/functional/openlp_core_lib/test_file_dialog.py
Normal file
73
tests/functional/openlp_core_lib/test_file_dialog.py
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
"""
|
||||||
|
Package to test the openlp.core.lib.filedialog package.
|
||||||
|
"""
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from openlp.core.common import UiStrings
|
||||||
|
from openlp.core.lib.filedialog import FileDialog
|
||||||
|
from tests.functional import MagicMock, patch
|
||||||
|
|
||||||
|
class TestFileDialog(TestCase):
|
||||||
|
"""
|
||||||
|
Test the functions in the :mod:`filedialog` module.
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
self.os_patcher = patch('openlp.core.lib.filedialog.os')
|
||||||
|
self.qt_gui_patcher = patch('openlp.core.lib.filedialog.QtGui')
|
||||||
|
self.ui_strings_patcher = patch('openlp.core.lib.filedialog.UiStrings')
|
||||||
|
self.mocked_os = self.os_patcher.start()
|
||||||
|
self.mocked_qt_gui = self.qt_gui_patcher.start()
|
||||||
|
self.mocked_ui_strings = self.ui_strings_patcher.start()
|
||||||
|
self.mocked_parent = MagicMock()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.os_patcher.stop()
|
||||||
|
self.qt_gui_patcher.stop()
|
||||||
|
self.ui_strings_patcher.stop()
|
||||||
|
|
||||||
|
def get_open_file_names_canceled_test(self):
|
||||||
|
"""
|
||||||
|
Test that FileDialog.getOpenFileNames() returns and empty QStringList when QFileDialog is canceled
|
||||||
|
(returns an empty QStringList)
|
||||||
|
"""
|
||||||
|
self.mocked_os.reset()
|
||||||
|
|
||||||
|
# GIVEN: An empty QStringList as a return value from QFileDialog.getOpenFileNames
|
||||||
|
self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = []
|
||||||
|
|
||||||
|
# WHEN: FileDialog.getOpenFileNames is called
|
||||||
|
result = FileDialog.getOpenFileNames(self.mocked_parent)
|
||||||
|
|
||||||
|
# THEN: The returned value should be an empty QStringList and os.path.exists should not have been called
|
||||||
|
assert not self.mocked_os.path.exists.called
|
||||||
|
self.assertEqual(result, [],
|
||||||
|
'FileDialog.getOpenFileNames should return and empty list when QFileDialog.getOpenFileNames is canceled')
|
||||||
|
|
||||||
|
def returned_file_list_test(self):
|
||||||
|
"""
|
||||||
|
Test that FileDialog.getOpenFileNames handles a list of files properly when QFileList.getOpenFileNames
|
||||||
|
returns a good file name, a urlencoded file name and a non-existing file
|
||||||
|
"""
|
||||||
|
self.mocked_os.rest()
|
||||||
|
self.mocked_qt_gui.reset()
|
||||||
|
|
||||||
|
# GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid
|
||||||
|
# file names.
|
||||||
|
self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [
|
||||||
|
'/Valid File', '/url%20encoded%20file%20%231', '/non-existing']
|
||||||
|
self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [
|
||||||
|
'/Valid File', '/url encoded file #1']
|
||||||
|
|
||||||
|
# WHEN: FileDialog.getOpenFileNames is called
|
||||||
|
result = FileDialog.getOpenFileNames(self.mocked_parent)
|
||||||
|
|
||||||
|
# THEN: os.path.exists should have been called with known args. QmessageBox.information should have been
|
||||||
|
# called. The returned result should corrilate with the input.
|
||||||
|
self.mocked_os.path.exists.assert_callde_with('/Valid File')
|
||||||
|
self.mocked_os.path.exists.assert_callde_with('/url%20encoded%20file%20%231')
|
||||||
|
self.mocked_os.path.exists.assert_callde_with('/url encoded file #1')
|
||||||
|
self.mocked_os.path.exists.assert_callde_with('/non-existing')
|
||||||
|
self.mocked_os.path.exists.assert_callde_with('/non-existing')
|
||||||
|
self.mocked_qt_gui.QmessageBox.information.called_with(self.mocked_parent, UiStrings().FileNotFound,
|
||||||
|
UiStrings().FileNotFoundMessage % '/non-existing')
|
||||||
|
self.assertEqual(result, ['/Valid File', '/url encoded file #1'], 'The returned file list is incorrect')
|
@ -3,13 +3,13 @@ Package to test the openlp.core.lib.htmlbuilder module.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from mock import MagicMock, patch
|
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
|
|
||||||
from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
|
from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
|
||||||
build_lyrics_format_css, build_footer_css
|
build_lyrics_format_css, build_footer_css
|
||||||
from openlp.core.lib.theme import HorizontalType, VerticalType
|
from openlp.core.lib.theme import HorizontalType, VerticalType
|
||||||
|
from tests.functional import MagicMock, patch
|
||||||
|
|
||||||
|
|
||||||
HTML = """
|
HTML = """
|
||||||
@ -216,9 +216,9 @@ class Htmbuilder(TestCase):
|
|||||||
is_live = False
|
is_live = False
|
||||||
background = None
|
background = None
|
||||||
plugin = MagicMock()
|
plugin = MagicMock()
|
||||||
plugin.get_display_css = MagicMock(return_value='plugin CSS')
|
plugin.get_display_css.return_value = 'plugin CSS'
|
||||||
plugin.get_display_javascript = MagicMock(return_value='plugin JS')
|
plugin.get_display_javascript.return_value = 'plugin JS'
|
||||||
plugin.get_display_html = MagicMock(return_value='plugin HTML')
|
plugin.get_display_html.return_value = 'plugin HTML'
|
||||||
plugins = [plugin]
|
plugins = [plugin]
|
||||||
|
|
||||||
# WHEN: Create the html.
|
# WHEN: Create the html.
|
||||||
|
180
tests/functional/openlp_plugins/bibles/test_http.py
Normal file
180
tests/functional/openlp_plugins/bibles/test_http.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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 #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the http module of the Bibles plugin.
|
||||||
|
"""
|
||||||
|
from unittest import TestCase
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
from tests.functional import patch, MagicMock
|
||||||
|
from openlp.plugins.bibles.lib.http import BSExtract
|
||||||
|
|
||||||
|
#TODO: Items left to test
|
||||||
|
# BGExtract
|
||||||
|
# __init__
|
||||||
|
# _remove_elements
|
||||||
|
# _extract_verse
|
||||||
|
# _clean_soup
|
||||||
|
# _extract_verses
|
||||||
|
# _extract_verses_old
|
||||||
|
# get_bible_chapter
|
||||||
|
# get_books_from_http
|
||||||
|
# _get_application
|
||||||
|
# CWExtract
|
||||||
|
# __init__
|
||||||
|
# get_bible_chapter
|
||||||
|
# get_books_from_http
|
||||||
|
# _get_application
|
||||||
|
# HTTPBible
|
||||||
|
# __init__
|
||||||
|
# do_import
|
||||||
|
# get_verses
|
||||||
|
# get_chapter
|
||||||
|
# get_books
|
||||||
|
# get_chapter_count
|
||||||
|
# get_verse_count
|
||||||
|
# _get_application
|
||||||
|
# get_soup_for_bible_ref
|
||||||
|
# send_error_message
|
||||||
|
|
||||||
|
class TestBSExtract(TestCase):
|
||||||
|
"""
|
||||||
|
Test the BSExtractClass
|
||||||
|
"""
|
||||||
|
#TODO: Items left to test
|
||||||
|
# BSExtract
|
||||||
|
# __init__
|
||||||
|
# get_bible_chapter
|
||||||
|
# get_books_from_http
|
||||||
|
# _get_application
|
||||||
|
def setUp(self):
|
||||||
|
self.get_soup_for_bible_ref_patcher = patch('openlp.plugins.bibles.lib.http.get_soup_for_bible_ref')
|
||||||
|
self.log_patcher = patch('openlp.plugins.bibles.lib.http.log')
|
||||||
|
self.send_error_message_patcher = patch('openlp.plugins.bibles.lib.http.send_error_message')
|
||||||
|
self.socket_patcher = patch('openlp.plugins.bibles.lib.http.socket')
|
||||||
|
self.urllib_patcher = patch('openlp.plugins.bibles.lib.http.urllib')
|
||||||
|
|
||||||
|
self.mock_get_soup_for_bible_ref = self.get_soup_for_bible_ref_patcher.start()
|
||||||
|
self.mock_log = self.log_patcher.start()
|
||||||
|
self.mock_send_error_message = self.send_error_message_patcher.start()
|
||||||
|
self.mock_socket = self.socket_patcher.start()
|
||||||
|
self.mock_soup = MagicMock()
|
||||||
|
self.mock_urllib = self.urllib_patcher.start()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self.get_soup_for_bible_ref_patcher.stop()
|
||||||
|
self.log_patcher.stop()
|
||||||
|
self.send_error_message_patcher.stop()
|
||||||
|
self.socket_patcher.stop()
|
||||||
|
self.urllib_patcher.stop()
|
||||||
|
|
||||||
|
def get_books_from_http_no_soup_test(self):
|
||||||
|
"""
|
||||||
|
Test the get_books_from_http method when get_soup_for_bible_ref returns a falsey value
|
||||||
|
"""
|
||||||
|
# GIVEN: An instance of BSExtract, and reset log, urllib & get_soup_for_bible_ref mocks
|
||||||
|
instance = BSExtract()
|
||||||
|
self.mock_log.debug.reset_mock()
|
||||||
|
self.mock_urllib.reset_mock()
|
||||||
|
self.mock_get_soup_for_bible_ref.reset_mock()
|
||||||
|
|
||||||
|
# WHEN: get_books_from_http is called with 'NIV' and get_soup_for_bible_ref returns a None value
|
||||||
|
self.mock_urllib.parse.quote.return_value = 'NIV'
|
||||||
|
self.mock_get_soup_for_bible_ref.return_value = None
|
||||||
|
result = instance.get_books_from_http('NIV')
|
||||||
|
|
||||||
|
# THEN: The rest mocks should be called with known values and get_books_from_http should return None
|
||||||
|
self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV')
|
||||||
|
self.mock_urllib.parse.quote.assert_called_once_with(b'NIV')
|
||||||
|
self.mock_get_soup_for_bible_ref.assert_called_once_with(
|
||||||
|
'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
|
||||||
|
self.assertIsNone(result,
|
||||||
|
'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value')
|
||||||
|
|
||||||
|
def get_books_from_http_no_content_test(self):
|
||||||
|
"""
|
||||||
|
Test the get_books_from_http method when the specified element cannot be found in the tag object returned from
|
||||||
|
get_soup_for_bible_ref
|
||||||
|
"""
|
||||||
|
# GIVEN: An instance of BSExtract, and reset log, urllib, get_soup_for_bible_ref & soup mocks
|
||||||
|
instance = BSExtract()
|
||||||
|
self.mock_log.reset_mock()
|
||||||
|
self.mock_urllib.reset_mock()
|
||||||
|
self.mock_get_soup_for_bible_ref.reset_mock()
|
||||||
|
self.mock_soup.reset_mock()
|
||||||
|
|
||||||
|
# WHEN: get_books_from_http is called with 'NIV', get_soup_for_bible_ref returns a mocked_soup object and
|
||||||
|
# mocked_soup.find returns None
|
||||||
|
self.mock_urllib.parse.quote.return_value = 'NIV'
|
||||||
|
self.mock_soup.find.return_value = None
|
||||||
|
self.mock_get_soup_for_bible_ref.return_value = self.mock_soup
|
||||||
|
result = instance.get_books_from_http('NIV')
|
||||||
|
|
||||||
|
# THEN: The rest mocks should be called with known values and get_books_from_http should return None
|
||||||
|
self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV')
|
||||||
|
self.mock_urllib.parse.quote.assert_called_once_with(b'NIV')
|
||||||
|
self.mock_get_soup_for_bible_ref.assert_called_once_with(
|
||||||
|
'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
|
||||||
|
self.mock_soup.find.assert_called_once_with('ul')
|
||||||
|
self.mock_log.error.assert_called_once_with('No books found in the Bibleserver response.')
|
||||||
|
self.mock_send_error_message.assert_called_once_with('parse')
|
||||||
|
self.assertIsNone(result,
|
||||||
|
'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value')
|
||||||
|
|
||||||
|
def get_books_from_http_content_test(self):
|
||||||
|
"""
|
||||||
|
Test the get_books_from_http method with sample HTML
|
||||||
|
Also a regression test for bug #1184869. (The anchor tag in the second list item is empty)
|
||||||
|
"""
|
||||||
|
# GIVEN: An instance of BSExtract, and reset log, urllib & get_soup_for_bible_ref mocks and sample HTML data
|
||||||
|
self.test_html = '<ul><li><a href="/overlay/selectChapter?tocBook=1">Genesis</a></li>' \
|
||||||
|
'<li><a href="/overlay/selectChapter?tocBook=2"></a></li>' \
|
||||||
|
'<li><a href="/overlay/selectChapter?tocBook=3">Leviticus</a></li></ul>'
|
||||||
|
self.test_soup = BeautifulSoup(self.test_html)
|
||||||
|
instance = BSExtract()
|
||||||
|
self.mock_log.reset_mock()
|
||||||
|
self.mock_urllib.reset_mock()
|
||||||
|
self.mock_get_soup_for_bible_ref.reset_mock()
|
||||||
|
self.mock_send_error_message.reset_mock()
|
||||||
|
|
||||||
|
# WHEN: get_books_from_http is called with 'NIV' and get_soup_for_bible_ref returns tag object based on the
|
||||||
|
# supplied test data.
|
||||||
|
self.mock_urllib.parse.quote.return_value = 'NIV'
|
||||||
|
self.mock_get_soup_for_bible_ref.return_value = self.test_soup
|
||||||
|
result = instance.get_books_from_http('NIV')
|
||||||
|
|
||||||
|
# THEN: The rest mocks should be called with known values and get_books_from_http should return the two books
|
||||||
|
# in the test data
|
||||||
|
self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV')
|
||||||
|
self.mock_urllib.parse.quote.assert_called_once_with(b'NIV')
|
||||||
|
self.mock_get_soup_for_bible_ref.assert_called_once_with(
|
||||||
|
'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
|
||||||
|
self.assertFalse(self.mock_log.error.called, 'log.error should not have been called')
|
||||||
|
self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called')
|
||||||
|
self.assertEquals(result, ['Genesis', 'Leviticus'])
|
@ -62,7 +62,7 @@ class TestRemoteTab(TestCase):
|
|||||||
"""
|
"""
|
||||||
Create the UI
|
Create the UI
|
||||||
"""
|
"""
|
||||||
fd, self.ini_file = mkstemp('.ini')
|
self.fd, self.ini_file = mkstemp('.ini')
|
||||||
Settings().set_filename(self.ini_file)
|
Settings().set_filename(self.ini_file)
|
||||||
self.application = QtGui.QApplication.instance()
|
self.application = QtGui.QApplication.instance()
|
||||||
Settings().extend_default_settings(__default_settings__)
|
Settings().extend_default_settings(__default_settings__)
|
||||||
@ -76,6 +76,7 @@ class TestRemoteTab(TestCase):
|
|||||||
del self.application
|
del self.application
|
||||||
del self.parent
|
del self.parent
|
||||||
del self.form
|
del self.form
|
||||||
|
os.close(self.fd)
|
||||||
os.unlink(self.ini_file)
|
os.unlink(self.ini_file)
|
||||||
|
|
||||||
def get_ip_address_default_test(self):
|
def get_ip_address_default_test(self):
|
||||||
|
@ -37,7 +37,8 @@ from PyQt4 import QtGui
|
|||||||
|
|
||||||
from openlp.core.common import Settings
|
from openlp.core.common import Settings
|
||||||
from openlp.plugins.remotes.lib.httpserver import HttpRouter
|
from openlp.plugins.remotes.lib.httpserver import HttpRouter
|
||||||
from tests.functional import MagicMock
|
from tests.functional import MagicMock, patch
|
||||||
|
from mock import mock_open
|
||||||
|
|
||||||
__default_settings__ = {
|
__default_settings__ = {
|
||||||
'remotes/twelve hour': True,
|
'remotes/twelve hour': True,
|
||||||
@ -50,6 +51,7 @@ __default_settings__ = {
|
|||||||
'remotes/ip address': '0.0.0.0'
|
'remotes/ip address': '0.0.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_PATH = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
class TestRouter(TestCase):
|
class TestRouter(TestCase):
|
||||||
"""
|
"""
|
||||||
@ -59,7 +61,7 @@ class TestRouter(TestCase):
|
|||||||
"""
|
"""
|
||||||
Create the UI
|
Create the UI
|
||||||
"""
|
"""
|
||||||
fd, self.ini_file = mkstemp('.ini')
|
self.fd, self.ini_file = mkstemp('.ini')
|
||||||
Settings().set_filename(self.ini_file)
|
Settings().set_filename(self.ini_file)
|
||||||
self.application = QtGui.QApplication.instance()
|
self.application = QtGui.QApplication.instance()
|
||||||
Settings().extend_default_settings(__default_settings__)
|
Settings().extend_default_settings(__default_settings__)
|
||||||
@ -70,6 +72,7 @@ class TestRouter(TestCase):
|
|||||||
Delete all the C++ objects at the end so that we don't have a segfault
|
Delete all the C++ objects at the end so that we don't have a segfault
|
||||||
"""
|
"""
|
||||||
del self.application
|
del self.application
|
||||||
|
os.close(self.fd)
|
||||||
os.unlink(self.ini_file)
|
os.unlink(self.ini_file)
|
||||||
|
|
||||||
def password_encrypter_test(self):
|
def password_encrypter_test(self):
|
||||||
@ -109,4 +112,63 @@ class TestRouter(TestCase):
|
|||||||
assert function['function'] == mocked_function, \
|
assert function['function'] == mocked_function, \
|
||||||
'The mocked function should match defined value.'
|
'The mocked function should match defined value.'
|
||||||
assert function['secure'] == False, \
|
assert function['secure'] == False, \
|
||||||
'The mocked function should not require any security.'
|
'The mocked function should not require any security.'
|
||||||
|
|
||||||
|
def get_content_type_test(self):
|
||||||
|
"""
|
||||||
|
Test the get_content_type logic
|
||||||
|
"""
|
||||||
|
# GIVEN: a set of files and their corresponding types
|
||||||
|
headers = [ ['test.html', 'text/html'], ['test.css', 'text/css'],
|
||||||
|
['test.js', 'application/javascript'], ['test.jpg', 'image/jpeg'],
|
||||||
|
['test.gif', 'image/gif'], ['test.ico', 'image/x-icon'],
|
||||||
|
['test.png', 'image/png'], ['test.whatever', 'text/plain'],
|
||||||
|
['test', 'text/plain'], ['', 'text/plain'],
|
||||||
|
[os.path.join(TEST_PATH,'test.html'), 'text/html']]
|
||||||
|
# WHEN: calling each file type
|
||||||
|
for header in headers:
|
||||||
|
ext, content_type = self.router.get_content_type(header[0])
|
||||||
|
# THEN: all types should match
|
||||||
|
self.assertEqual(content_type, header[1], 'Mismatch of content type')
|
||||||
|
|
||||||
|
def serve_file_without_params_test(self):
|
||||||
|
"""
|
||||||
|
Test the serve_file method without params
|
||||||
|
"""
|
||||||
|
# GIVEN: mocked environment
|
||||||
|
self.router.send_response = MagicMock()
|
||||||
|
self.router.send_header = MagicMock()
|
||||||
|
self.router.end_headers = MagicMock()
|
||||||
|
self.router.wfile = MagicMock()
|
||||||
|
self.router.html_dir = os.path.normpath('test/dir')
|
||||||
|
self.router.template_vars = MagicMock()
|
||||||
|
# WHEN: call serve_file with no file_name
|
||||||
|
self.router.serve_file()
|
||||||
|
# THEN: it should return a 404
|
||||||
|
self.router.send_response.assert_called_once_with(404)
|
||||||
|
self.router.send_header.assert_called_once_with('Content-type','text/html')
|
||||||
|
self.assertEqual(self.router.end_headers.call_count, 1,
|
||||||
|
'end_headers called once')
|
||||||
|
|
||||||
|
def serve_file_with_valid_params_test(self):
|
||||||
|
"""
|
||||||
|
Test the serve_file method with an existing file
|
||||||
|
"""
|
||||||
|
# GIVEN: mocked environment
|
||||||
|
self.router.send_response = MagicMock()
|
||||||
|
self.router.send_header = MagicMock()
|
||||||
|
self.router.end_headers = MagicMock()
|
||||||
|
self.router.wfile = MagicMock()
|
||||||
|
self.router.html_dir = os.path.normpath('test/dir')
|
||||||
|
self.router.template_vars = MagicMock()
|
||||||
|
with patch('openlp.core.lib.os.path.exists') as mocked_exists, \
|
||||||
|
patch('builtins.open', mock_open(read_data='123')):
|
||||||
|
mocked_exists.return_value = True
|
||||||
|
# WHEN: call serve_file with an existing html file
|
||||||
|
self.router.serve_file(os.path.normpath('test/dir/test.html'))
|
||||||
|
# THEN: it should return a 200 and the file
|
||||||
|
self.router.send_response.assert_called_once_with(200)
|
||||||
|
self.router.send_header.assert_called_once_with(
|
||||||
|
'Content-type','text/html')
|
||||||
|
self.assertEqual(self.router.end_headers.call_count, 1,
|
||||||
|
'end_headers called once')
|
||||||
|
155
tests/functional/openlp_plugins/songs/test_songbeamerimport.py
Normal file
155
tests/functional/openlp_plugins/songs/test_songbeamerimport.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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 #
|
||||||
|
###############################################################################
|
||||||
|
"""
|
||||||
|
This module contains tests for the Songbeamer song importer.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from tests.functional import MagicMock, patch
|
||||||
|
from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport
|
||||||
|
|
||||||
|
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
|
'..', '..', '..', 'resources', 'songbeamersongs'))
|
||||||
|
SONG_TEST_DATA = {'Lobsinget dem Herrn.sng':
|
||||||
|
{'title': 'GL 1 - Lobsinget dem Herrn',
|
||||||
|
'verses':
|
||||||
|
[('1. Lobsinget dem Herrn,\no preiset Ihn gern!\n'
|
||||||
|
'Anbetung und Lob Ihm gebühret.\n', 'v'),
|
||||||
|
('2. Lobsingt Seiner Lieb´,\ndie einzig ihn trieb,\n'
|
||||||
|
'zu sterben für unsere Sünden!\n', 'v'),
|
||||||
|
('3. Lobsingt Seiner Macht!\nSein Werk ist vollbracht:\n'
|
||||||
|
'Er sitzet zur Rechten des Vaters.\n', 'v'),
|
||||||
|
('4. Lobsingt seiner Treu´,\ndie immerdar neu,\n'
|
||||||
|
'bis Er uns zur Herrlichket führet!\n\n', 'v')],
|
||||||
|
'song_book_name': 'Glaubenslieder I',
|
||||||
|
'song_number': "1"}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestSongBeamerImport(TestCase):
|
||||||
|
"""
|
||||||
|
Test the functions in the :mod:`songbeamerimport` module.
|
||||||
|
"""
|
||||||
|
def create_importer_test(self):
|
||||||
|
"""
|
||||||
|
Test creating an instance of the SongBeamer file importer
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||||
|
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: An importer object is created
|
||||||
|
importer = SongBeamerImport(mocked_manager)
|
||||||
|
|
||||||
|
# THEN: The importer object should not be None
|
||||||
|
self.assertIsNotNone(importer, 'Import should not be none')
|
||||||
|
|
||||||
|
def invalid_import_source_test(self):
|
||||||
|
"""
|
||||||
|
Test SongBeamerImport.doImport handles different invalid import_source values
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||||
|
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
mocked_import_wizard = MagicMock()
|
||||||
|
importer = SongBeamerImport(mocked_manager)
|
||||||
|
importer.import_wizard = mocked_import_wizard
|
||||||
|
importer.stop_import_flag = True
|
||||||
|
|
||||||
|
# WHEN: Import source is not a list
|
||||||
|
for source in ['not a list', 0]:
|
||||||
|
importer.import_source = source
|
||||||
|
|
||||||
|
# THEN: doImport should return none and the progress bar maximum should not be set.
|
||||||
|
self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list')
|
||||||
|
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
|
||||||
|
'setMaxium on import_wizard.progress_bar should not have been called')
|
||||||
|
|
||||||
|
def valid_import_source_test(self):
|
||||||
|
"""
|
||||||
|
Test SongBeamerImport.doImport handles different invalid import_source values
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
||||||
|
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
mocked_import_wizard = MagicMock()
|
||||||
|
importer = SongBeamerImport(mocked_manager)
|
||||||
|
importer.import_wizard = mocked_import_wizard
|
||||||
|
importer.stop_import_flag = True
|
||||||
|
|
||||||
|
# WHEN: Import source is a list
|
||||||
|
importer.import_source = ['List', 'of', 'files']
|
||||||
|
|
||||||
|
# THEN: doImport should return none and the progress bar setMaximum should be called with the length of
|
||||||
|
# import_source.
|
||||||
|
self.assertIsNone(importer.doImport(),
|
||||||
|
'doImport should return None when import_source is a list and stop_import_flag is True')
|
||||||
|
mocked_import_wizard.progress_bar.setMaximum.assert_called_with(len(importer.import_source))
|
||||||
|
|
||||||
|
def file_import_test(self):
|
||||||
|
"""
|
||||||
|
Test the actual import of real song files and check that the imported data is correct.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard",
|
||||||
|
# and mocked out "author", "add_copyright", "add_verse", "finish" methods.
|
||||||
|
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
|
||||||
|
for song_file in SONG_TEST_DATA:
|
||||||
|
mocked_manager = MagicMock()
|
||||||
|
mocked_import_wizard = MagicMock()
|
||||||
|
mocked_add_verse = MagicMock()
|
||||||
|
mocked_finish = MagicMock()
|
||||||
|
mocked_finish.return_value = True
|
||||||
|
importer = SongBeamerImport(mocked_manager)
|
||||||
|
importer.import_wizard = mocked_import_wizard
|
||||||
|
importer.stop_import_flag = False
|
||||||
|
importer.addVerse = mocked_add_verse
|
||||||
|
importer.finish = mocked_finish
|
||||||
|
|
||||||
|
# WHEN: Importing each file
|
||||||
|
importer.import_source = [os.path.join(TEST_PATH, song_file)]
|
||||||
|
title = SONG_TEST_DATA[song_file]['title']
|
||||||
|
add_verse_calls = SONG_TEST_DATA[song_file]['verses']
|
||||||
|
song_book_name = SONG_TEST_DATA[song_file]['song_book_name']
|
||||||
|
song_number = SONG_TEST_DATA[song_file]['song_number']
|
||||||
|
|
||||||
|
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
||||||
|
# called.
|
||||||
|
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
||||||
|
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
|
||||||
|
for verse_text, verse_tag in add_verse_calls:
|
||||||
|
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
|
if song_book_name:
|
||||||
|
self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"'
|
||||||
|
% (song_file, song_book_name))
|
||||||
|
if song_number:
|
||||||
|
self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s'
|
||||||
|
% (song_file, song_number))
|
||||||
|
mocked_finish.assert_called_with()
|
@ -33,51 +33,25 @@ This module contains tests for the SongShow Plus song importer.
|
|||||||
import os
|
import os
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from tests.helpers.songfileimport import SongImportTestHelper
|
||||||
from openlp.plugins.songs.lib import VerseType
|
from openlp.plugins.songs.lib import VerseType
|
||||||
from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport
|
from openlp.plugins.songs.lib.songshowplusimport import SongShowPlusImport
|
||||||
from tests.functional import patch, MagicMock
|
from tests.functional import patch, MagicMock
|
||||||
|
|
||||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../resources/songshowplussongs'))
|
TEST_PATH = os.path.abspath(
|
||||||
SONG_TEST_DATA = {'Amazing Grace.sbsong':
|
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'songshowplussongs'))
|
||||||
{'title': 'Amazing Grace (Demonstration)',
|
|
||||||
'authors': ['John Newton', 'Edwin Excell', 'John P. Rees'],
|
class TestSongShowPlusFileImport(SongImportTestHelper):
|
||||||
'copyright': 'Public Domain ',
|
def __init__(self, *args, **kwargs):
|
||||||
'ccli_number': 22025,
|
self.importer_class_name = 'SongShowPlusImport'
|
||||||
'verses':
|
self.importer_module_name = 'songshowplusimport'
|
||||||
[('Amazing grace! How sweet the sound!\r\nThat saved a wretch like me!\r\n'
|
super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs)
|
||||||
'I once was lost, but now am found;\r\nWas blind, but now I see.', 'v1'),
|
|
||||||
('\'Twas grace that taught my heart to fear,\r\nAnd grace my fears relieved.\r\n'
|
def test_song_import(self):
|
||||||
'How precious did that grace appear,\r\nThe hour I first believed.', 'v2'),
|
test_import = self.file_import(os.path.join(TEST_PATH, 'Amazing Grace.sbsong'),
|
||||||
('The Lord has promised good to me,\r\nHis Word my hope secures.\r\n'
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
|
||||||
'He will my shield and portion be\r\nAs long as life endures.', 'v3'),
|
test_import = self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'),
|
||||||
('Thro\' many dangers, toils and snares\r\nI have already come.\r\n'
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json')))
|
||||||
'\'Tis grace that brought me safe thus far,\r\nAnd grace will lead me home.', 'v4'),
|
|
||||||
('When we\'ve been there ten thousand years,\r\nBright shining as the sun,\r\n'
|
|
||||||
'We\'ve no less days to sing God\'s praise,\r\nThan when we first begun.', 'v5')],
|
|
||||||
'topics': ['Assurance', 'Grace', 'Praise', 'Salvation'],
|
|
||||||
'comments': '\n\n\n',
|
|
||||||
'song_book_name': 'Demonstration Songs',
|
|
||||||
'song_number': 0,
|
|
||||||
'verse_order_list': []},
|
|
||||||
'Beautiful Garden Of Prayer.sbsong':
|
|
||||||
{'title': 'Beautiful Garden Of Prayer (Demonstration)',
|
|
||||||
'authors': ['Eleanor Allen Schroll', 'James H. Fillmore'],
|
|
||||||
'copyright': 'Public Domain ',
|
|
||||||
'ccli_number': 60252,
|
|
||||||
'verses':
|
|
||||||
[('There\'s a garden where Jesus is waiting,\r\nThere\'s a place that is wondrously fair.\r\n'
|
|
||||||
'For it glows with the light of His presence,\r\n\'Tis the beautiful garden of prayer.', 'v1'),
|
|
||||||
('There\'s a garden where Jesus is waiting,\r\nAnd I go with my burden and care.\r\n'
|
|
||||||
'Just to learn from His lips, words of comfort,\r\nIn the beautiful garden of prayer.', 'v2'),
|
|
||||||
('There\'s a garden where Jesus is waiting,\r\nAnd He bids you to come meet Him there,\r\n'
|
|
||||||
'Just to bow and receive a new blessing,\r\nIn the beautiful garden of prayer.', 'v3'),
|
|
||||||
('O the beautiful garden, the garden of prayer,\r\nO the beautiful garden of prayer.\r\n'
|
|
||||||
'There my Savior awaits, and He opens the gates\r\nTo the beautiful garden of prayer.', 'c1')],
|
|
||||||
'topics': ['Devotion', 'Prayer'],
|
|
||||||
'comments': '',
|
|
||||||
'song_book_name': '',
|
|
||||||
'song_number': 0,
|
|
||||||
'verse_order_list': []}}
|
|
||||||
|
|
||||||
|
|
||||||
class TestSongShowPlusImport(TestCase):
|
class TestSongShowPlusImport(TestCase):
|
||||||
@ -117,7 +91,7 @@ class TestSongShowPlusImport(TestCase):
|
|||||||
# THEN: doImport should return none and the progress bar maximum should not be set.
|
# THEN: doImport should return none and the progress bar maximum should not be set.
|
||||||
self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list')
|
self.assertIsNone(importer.doImport(), 'doImport should return None when import_source is not a list')
|
||||||
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
|
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
|
||||||
'setMaxium on import_wizard.progress_bar should not have been called')
|
'setMaximum on import_wizard.progress_bar should not have been called')
|
||||||
|
|
||||||
def valid_import_source_test(self):
|
def valid_import_source_test(self):
|
||||||
"""
|
"""
|
||||||
@ -194,70 +168,3 @@ class TestSongShowPlusImport(TestCase):
|
|||||||
self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag,
|
self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag,
|
||||||
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"'
|
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"'
|
||||||
% (openlp_tag, original_tag))
|
% (openlp_tag, original_tag))
|
||||||
|
|
||||||
def file_import_test(self):
|
|
||||||
"""
|
|
||||||
Test the actual import of real song files and check that the imported data is correct.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# GIVEN: Test files with a mocked out SongImport class, a mocked out "manager", a mocked out "import_wizard",
|
|
||||||
# and mocked out "author", "add_copyright", "add_verse", "finish" methods.
|
|
||||||
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
|
|
||||||
for song_file in SONG_TEST_DATA:
|
|
||||||
mocked_manager = MagicMock()
|
|
||||||
mocked_import_wizard = MagicMock()
|
|
||||||
mocked_parse_author = MagicMock()
|
|
||||||
mocked_add_copyright = MagicMock()
|
|
||||||
mocked_add_verse = MagicMock()
|
|
||||||
mocked_finish = MagicMock()
|
|
||||||
mocked_finish.return_value = True
|
|
||||||
importer = SongShowPlusImport(mocked_manager)
|
|
||||||
importer.import_wizard = mocked_import_wizard
|
|
||||||
importer.stop_import_flag = False
|
|
||||||
importer.parse_author = mocked_parse_author
|
|
||||||
importer.addCopyright = mocked_add_copyright
|
|
||||||
importer.addVerse = mocked_add_verse
|
|
||||||
importer.finish = mocked_finish
|
|
||||||
importer.topics = []
|
|
||||||
|
|
||||||
# WHEN: Importing each file
|
|
||||||
importer.import_source = [os.path.join(TEST_PATH, song_file)]
|
|
||||||
title = SONG_TEST_DATA[song_file]['title']
|
|
||||||
author_calls = SONG_TEST_DATA[song_file]['authors']
|
|
||||||
song_copyright = SONG_TEST_DATA[song_file]['copyright']
|
|
||||||
ccli_number = SONG_TEST_DATA[song_file]['ccli_number']
|
|
||||||
add_verse_calls = SONG_TEST_DATA[song_file]['verses']
|
|
||||||
topics = SONG_TEST_DATA[song_file]['topics']
|
|
||||||
comments = SONG_TEST_DATA[song_file]['comments']
|
|
||||||
song_book_name = SONG_TEST_DATA[song_file]['song_book_name']
|
|
||||||
song_number = SONG_TEST_DATA[song_file]['song_number']
|
|
||||||
verse_order_list = SONG_TEST_DATA[song_file]['verse_order_list']
|
|
||||||
|
|
||||||
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
|
||||||
# called.
|
|
||||||
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
|
||||||
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
|
|
||||||
for author in author_calls:
|
|
||||||
mocked_parse_author.assert_any_call(author)
|
|
||||||
if song_copyright:
|
|
||||||
mocked_add_copyright.assert_called_with(song_copyright)
|
|
||||||
if ccli_number:
|
|
||||||
self.assertEquals(importer.ccliNumber, ccli_number, 'ccliNumber for %s should be %s'
|
|
||||||
% (song_file, ccli_number))
|
|
||||||
for verse_text, verse_tag in add_verse_calls:
|
|
||||||
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
|
||||||
if topics:
|
|
||||||
self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (song_file, topics))
|
|
||||||
if comments:
|
|
||||||
self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"'
|
|
||||||
% (song_file, comments))
|
|
||||||
if song_book_name:
|
|
||||||
self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"'
|
|
||||||
% (song_file, song_book_name))
|
|
||||||
if song_number:
|
|
||||||
self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s'
|
|
||||||
% (song_file, song_number))
|
|
||||||
if verse_order_list:
|
|
||||||
self.assertEquals(importer.verseOrderList, [], 'verseOrderList for %s should be %s'
|
|
||||||
% (song_file, verse_order_list))
|
|
||||||
mocked_finish.assert_called_with()
|
|
||||||
|
142
tests/helpers/songfileimport.py
Normal file
142
tests/helpers/songfileimport.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# OpenLP - Open Source Lyrics Projection #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||||
|
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||||
|
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||||
|
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||||
|
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||||
|
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||||
|
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||||
|
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||||
|
# --------------------------------------------------------------------------- #
|
||||||
|
# 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:`songfileimporthelper` modules provides a helper class and methods to easily enable testing the import of
|
||||||
|
song files from third party applications.
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from tests.functional import patch, MagicMock
|
||||||
|
|
||||||
|
class SongImportTestHelper(TestCase):
|
||||||
|
"""
|
||||||
|
This class is designed to be a helper class to reduce repition when testing the import of song files.
|
||||||
|
"""
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(SongImportTestHelper, self).__init__(*args, **kwargs)
|
||||||
|
self.importer_module = __import__(
|
||||||
|
'openlp.plugins.songs.lib.%s' % self.importer_module_name, fromlist=[self.importer_class_name])
|
||||||
|
self.importer_class = getattr(self.importer_module, self.importer_class_name)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
Patch and set up the mocks required.
|
||||||
|
"""
|
||||||
|
self.add_copyright_patcher = patch(
|
||||||
|
'openlp.plugins.songs.lib.%s.%s.addCopyright' % (self.importer_module_name, self.importer_class_name))
|
||||||
|
self.add_verse_patcher = patch(
|
||||||
|
'openlp.plugins.songs.lib.%s.%s.addVerse' % (self.importer_module_name, self.importer_class_name))
|
||||||
|
self.finish_patcher = patch(
|
||||||
|
'openlp.plugins.songs.lib.%s.%s.finish' % (self.importer_module_name, self.importer_class_name))
|
||||||
|
self.parse_author_patcher = patch(
|
||||||
|
'openlp.plugins.songs.lib.%s.%s.parse_author' % (self.importer_module_name, self.importer_class_name))
|
||||||
|
self.song_import_patcher = patch('openlp.plugins.songs.lib.%s.SongImport' % self.importer_module_name)
|
||||||
|
self.mocked_add_copyright = self.add_copyright_patcher.start()
|
||||||
|
self.mocked_add_verse = self.add_verse_patcher.start()
|
||||||
|
self.mocked_finish = self.finish_patcher.start()
|
||||||
|
self.mocked_parse_author = self.parse_author_patcher.start()
|
||||||
|
self.mocked_song_importer = self.song_import_patcher.start()
|
||||||
|
self.mocked_manager = MagicMock()
|
||||||
|
self.mocked_import_wizard = MagicMock()
|
||||||
|
self.mocked_finish.return_value = True
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
Clean up
|
||||||
|
"""
|
||||||
|
self.add_copyright_patcher.stop()
|
||||||
|
self.add_verse_patcher.stop()
|
||||||
|
self.finish_patcher.stop()
|
||||||
|
self.parse_author_patcher.stop()
|
||||||
|
self.song_import_patcher.stop()
|
||||||
|
|
||||||
|
def load_external_result_data(self, file_name):
|
||||||
|
"""
|
||||||
|
A method to load and return an object containing the song data from an external file.
|
||||||
|
"""
|
||||||
|
result_file = open(file_name, 'rb')
|
||||||
|
return json.loads(result_file.read().decode())
|
||||||
|
|
||||||
|
def file_import(self, source_file_name, result_data):
|
||||||
|
"""
|
||||||
|
Import the given file and check that it has imported correctly
|
||||||
|
"""
|
||||||
|
importer = self.importer_class(self.mocked_manager)
|
||||||
|
importer.import_wizard = self.mocked_import_wizard
|
||||||
|
importer.stop_import_flag = False
|
||||||
|
importer.topics = []
|
||||||
|
|
||||||
|
# WHEN: Importing the source file
|
||||||
|
importer.import_source = [source_file_name]
|
||||||
|
add_verse_calls = self._get_data(result_data, 'verses')
|
||||||
|
author_calls = self._get_data(result_data, 'authors')
|
||||||
|
ccli_number = self._get_data(result_data, 'ccli_number')
|
||||||
|
comments = self._get_data(result_data, 'comments')
|
||||||
|
song_book_name = self._get_data(result_data, 'song_book_name')
|
||||||
|
song_copyright = self._get_data(result_data, 'copyright')
|
||||||
|
song_number = self._get_data(result_data, 'song_number')
|
||||||
|
title = self._get_data(result_data, 'title')
|
||||||
|
topics = self._get_data(result_data, 'topics')
|
||||||
|
verse_order_list = self._get_data(result_data, 'verse_order_list')
|
||||||
|
|
||||||
|
# THEN: doImport should return none, the song data should be as expected, and finish should have been
|
||||||
|
# called.
|
||||||
|
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
|
||||||
|
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
|
||||||
|
for author in author_calls:
|
||||||
|
self.mocked_parse_author.assert_any_call(author)
|
||||||
|
if song_copyright:
|
||||||
|
self.mocked_add_copyright.assert_called_with(song_copyright)
|
||||||
|
if ccli_number:
|
||||||
|
self.assertEquals(importer.ccliNumber, ccli_number, 'ccliNumber for %s should be %s'
|
||||||
|
% (source_file_name, ccli_number))
|
||||||
|
for verse_text, verse_tag in add_verse_calls:
|
||||||
|
self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
|
if topics:
|
||||||
|
self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics))
|
||||||
|
if comments:
|
||||||
|
self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"'
|
||||||
|
% (source_file_name, comments))
|
||||||
|
if song_book_name:
|
||||||
|
self.assertEquals(importer.songBookName, song_book_name, 'songBookName for %s should be "%s"'
|
||||||
|
% (source_file_name, song_book_name))
|
||||||
|
if song_number:
|
||||||
|
self.assertEquals(importer.songNumber, song_number, 'songNumber for %s should be %s'
|
||||||
|
% (source_file_name, song_number))
|
||||||
|
if verse_order_list:
|
||||||
|
self.assertEquals(importer.verseOrderList, [], 'verseOrderList for %s should be %s'
|
||||||
|
% (source_file_name, verse_order_list))
|
||||||
|
self.mocked_finish.assert_called_with()
|
||||||
|
|
||||||
|
def _get_data(self, data, key):
|
||||||
|
if key in data:
|
||||||
|
return data[key]
|
||||||
|
return ''
|
||||||
|
|
@ -55,9 +55,9 @@ class TestEditSongForm(TestCase):
|
|||||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
||||||
# Mock out the verse.
|
# Mock out the verse.
|
||||||
first_verse = MagicMock()
|
first_verse = MagicMock()
|
||||||
first_verse.data = MagicMock(return_value='V1')
|
first_verse.data.return_value = 'V1'
|
||||||
second_verse = MagicMock()
|
second_verse = MagicMock()
|
||||||
second_verse.data = MagicMock(return_value= 'V2')
|
second_verse.data.return_value = 'V2'
|
||||||
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
||||||
self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split())
|
self.form._extract_verse_order = MagicMock(return_value=given_verse_order.split())
|
||||||
|
|
||||||
@ -76,9 +76,9 @@ class TestEditSongForm(TestCase):
|
|||||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
self.form.verse_list_widget.rowCount = MagicMock(return_value=2)
|
||||||
# Mock out the verse.
|
# Mock out the verse.
|
||||||
first_verse = MagicMock()
|
first_verse = MagicMock()
|
||||||
first_verse.data = MagicMock(return_value='V1')
|
first_verse.data.return_value = 'V1'
|
||||||
second_verse = MagicMock()
|
second_verse = MagicMock()
|
||||||
second_verse.data = MagicMock(return_value= 'V2')
|
second_verse.data.return_value = 'V2'
|
||||||
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
self.form.verse_list_widget.item = MagicMock(side_effect=[first_verse, second_verse])
|
||||||
self.form._extract_verse_order = MagicMock(return_value=[given_verse_order])
|
self.form._extract_verse_order = MagicMock(return_value=[given_verse_order])
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ class TestEditSongForm(TestCase):
|
|||||||
self.form.verse_list_widget.rowCount = MagicMock(return_value=1)
|
self.form.verse_list_widget.rowCount = MagicMock(return_value=1)
|
||||||
# Mock out the verse. (We want a verse type to be returned).
|
# Mock out the verse. (We want a verse type to be returned).
|
||||||
mocked_verse = MagicMock()
|
mocked_verse = MagicMock()
|
||||||
mocked_verse.data = MagicMock(return_value='V1')
|
mocked_verse.data.return_value = 'V1'
|
||||||
self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse)
|
self.form.verse_list_widget.item = MagicMock(return_value=mocked_verse)
|
||||||
self.form._extract_verse_order = MagicMock(return_value=[])
|
self.form._extract_verse_order = MagicMock(return_value=[])
|
||||||
self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order)
|
self.form.verse_order_edit.text = MagicMock(return_value=given_verse_order)
|
||||||
|
25
tests/resources/songbeamersongs/Lobsinget dem Herrn.sng
Normal file
25
tests/resources/songbeamersongs/Lobsinget dem Herrn.sng
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#LangCount=1
|
||||||
|
#Title=GL 1 - Lobsinget dem Herrn
|
||||||
|
#Editor=SongBeamer 4.20
|
||||||
|
#Version=3
|
||||||
|
#Format=F/K//
|
||||||
|
#TitleFormat=U
|
||||||
|
#ChurchSongID=0001
|
||||||
|
#Songbook=Glaubenslieder I / 1
|
||||||
|
---
|
||||||
|
1. Lobsinget dem Herrn,
|
||||||
|
o preiset Ihn gern!
|
||||||
|
Anbetung und Lob Ihm gebühret.
|
||||||
|
---
|
||||||
|
2. Lobsingt Seiner Lieb´,
|
||||||
|
die einzig ihn trieb,
|
||||||
|
zu sterben für unsere Sünden!
|
||||||
|
---
|
||||||
|
3. Lobsingt Seiner Macht!
|
||||||
|
Sein Werk ist vollbracht:
|
||||||
|
Er sitzet zur Rechten des Vaters.
|
||||||
|
---
|
||||||
|
4. Lobsingt seiner Treu´,
|
||||||
|
die immerdar neu,
|
||||||
|
bis Er uns zur Herrlichket führet!
|
||||||
|
|
42
tests/resources/songshowplussongs/Amazing Grace.json
Normal file
42
tests/resources/songshowplussongs/Amazing Grace.json
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"John Newton",
|
||||||
|
"Edwin Excell",
|
||||||
|
"John P. Rees"
|
||||||
|
],
|
||||||
|
"ccli_number": 22025,
|
||||||
|
"comments": "\n\n\n",
|
||||||
|
"copyright": "Public Domain ",
|
||||||
|
"song_book_name": "Demonstration Songs",
|
||||||
|
"song_number": 0,
|
||||||
|
"title": "Amazing Grace (Demonstration)",
|
||||||
|
"topics": [
|
||||||
|
"Assurance",
|
||||||
|
"Grace",
|
||||||
|
"Praise",
|
||||||
|
"Salvation"
|
||||||
|
],
|
||||||
|
"verse_order_list": [],
|
||||||
|
"verses": [
|
||||||
|
[
|
||||||
|
"Amazing grace! How sweet the sound!\r\nThat saved a wretch like me!\r\nI once was lost, but now am found;\r\nWas blind, but now I see.",
|
||||||
|
"v1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"'Twas grace that taught my heart to fear,\r\nAnd grace my fears relieved.\r\nHow precious did that grace appear,\r\nThe hour I first believed.",
|
||||||
|
"v2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"The Lord has promised good to me,\r\nHis Word my hope secures.\r\nHe will my shield and portion be\r\nAs long as life endures.",
|
||||||
|
"v3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Thro' many dangers, toils and snares\r\nI have already come.\r\n'Tis grace that brought me safe thus far,\r\nAnd grace will lead me home.",
|
||||||
|
"v4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"When we've been there ten thousand years,\r\nBright shining as the sun,\r\nWe've no less days to sing God's praise,\r\nThan when we first begun.",
|
||||||
|
"v5"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"authors": [
|
||||||
|
"Eleanor Allen Schroll",
|
||||||
|
"James H. Fillmore"
|
||||||
|
],
|
||||||
|
"ccli_number": 60252,
|
||||||
|
"comments": "",
|
||||||
|
"copyright": "Public Domain ",
|
||||||
|
"song_book_name": "",
|
||||||
|
"song_number": 0,
|
||||||
|
"title": "Beautiful Garden Of Prayer (Demonstration)",
|
||||||
|
"topics": [
|
||||||
|
"Devotion",
|
||||||
|
"Prayer"
|
||||||
|
],
|
||||||
|
"verse_order_list": [],
|
||||||
|
"verses": [
|
||||||
|
[
|
||||||
|
"There's a garden where Jesus is waiting,\r\nThere's a place that is wondrously fair.\r\nFor it glows with the light of His presence,\r\n'Tis the beautiful garden of prayer.",
|
||||||
|
"v1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"There's a garden where Jesus is waiting,\r\nAnd I go with my burden and care.\r\nJust to learn from His lips, words of comfort,\r\nIn the beautiful garden of prayer.",
|
||||||
|
"v2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"There's a garden where Jesus is waiting,\r\nAnd He bids you to come meet Him there,\r\nJust to bow and receive a new blessing,\r\nIn the beautiful garden of prayer.",
|
||||||
|
"v3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"O the beautiful garden, the garden of prayer,\r\nO the beautiful garden of prayer.\r\nThere my Savior awaits, and He opens the gates\r\nTo the beautiful garden of prayer.",
|
||||||
|
"c1"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user