Merge with trunk to resolve conflict.

This commit is contained in:
Tomas Groth 2013-08-06 17:33:05 +01:00
commit 180c16147f
44 changed files with 891 additions and 134 deletions

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -185,7 +185,7 @@ class OpenLP(QtGui.QApplication):
"""
log.exception(''.join(format_exception(exctype, value, traceback)))
if not hasattr(self, u'exception_form'):
self.exception_form = ExceptionForm(self.main_window)
self.exception_form = ExceptionForm()
self.exception_form.exception_text_edit.setPlainText(''.join(format_exception(exctype, value, traceback)))
self.set_normal_cursor()
self.exception_form.exec_()

View File

@ -308,12 +308,12 @@ class Plugin(QtCore.QObject):
Perform tasks on application startup
"""
# FIXME: Remove after 2.2 release.
# This is needed to load the list of images/media/presentation from the config saved
# before the settings rewrite.
# This is needed to load the list of media/presentation from the config saved before the settings rewrite.
if self.media_item_class is not None and self.name != u'images':
loaded_list = Settings().get_files_from_config(self)
# Now save the list to the config using our Settings class.
Settings().setValue(u'%s/%s files' % (self.settings_section, self.name), loaded_list)
if loaded_list:
Settings().setValue(u'%s/%s files' % (self.settings_section, self.name), loaded_list)
def uses_theme(self, theme):
"""

View File

@ -37,6 +37,9 @@ import platform
import bs4
import sqlalchemy
from lxml import etree
from openlp.core.lib import Registry
from PyQt4 import Qt, QtCore, QtGui, QtWebKit
try:
@ -77,19 +80,7 @@ try:
CHERRYPY_VERSION = cherrypy.__version__
except ImportError:
CHERRYPY_VERSION = u'-'
try:
import uno
arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue')
arg.Name = u'nodepath'
arg.Value = u'/org.openoffice.Setup/Product'
context = uno.getComponentContext()
provider = context.ServiceManager.createInstance(u'com.sun.star.configuration.ConfigurationProvider')
node = provider.createInstanceWithArguments(u'com.sun.star.configuration.ConfigurationAccess', (arg,))
UNO_VERSION = node.getByName(u'ooSetupVersion')
except ImportError:
UNO_VERSION = u'-'
except:
UNO_VERSION = u'- (Possible non-standard UNO installation)'
try:
WEBKIT_VERSION = QtWebKit.qWebKitVersion()
except AttributeError:
@ -100,7 +91,6 @@ try:
except ImportError:
VLC_VERSION = u'-'
from openlp.core.lib import UiStrings, Settings, translate
from openlp.core.utils import get_application_version
@ -113,11 +103,11 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
"""
The exception dialog
"""
def __init__(self, parent):
def __init__(self):
"""
Constructor.
"""
QtGui.QDialog.__init__(self, parent)
QtGui.QDialog.__init__(self, self.main_window)
self.setupUi(self)
self.settings_section = u'crashreport'
@ -152,7 +142,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
u'Mako: %s\n' % MAKO_VERSION + \
u'CherryPy: %s\n' % CHERRYPY_VERSION + \
u'pyICU: %s\n' % ICU_VERSION + \
u'pyUNO bridge: %s\n' % UNO_VERSION + \
u'pyUNO bridge: %s\n' % self._pyuno_import() + \
u'VLC: %s\n' % VLC_VERSION
if platform.system() == u'Linux':
if os.environ.get(u'KDE_FULL_SESSION') == u'true':
@ -256,3 +246,34 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
"""
self.save_report_button.setEnabled(state)
self.send_report_button.setEnabled(state)
def _pyuno_import(self):
"""
Added here to define only when the form is actioned. The uno interface spits out lots of exception messages
if the import is at a file level. If uno import is changed this could be reverted.
This happens in other classes but there it is localised here it is across the whole system and hides real
errors.
"""
try:
import uno
arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue')
arg.Name = u'nodepath'
arg.Value = u'/org.openoffice.Setup/Product'
context = uno.getComponentContext()
provider = context.ServiceManager.createInstance(u'com.sun.star.configuration.ConfigurationProvider')
node = provider.createInstanceWithArguments(u'com.sun.star.configuration.ConfigurationAccess', (arg,))
return node.getByName(u'ooSetupVersion')
except ImportError:
return u'-'
except:
return u'- (Possible non-standard UNO installation)'
def _get_main_window(self):
"""
Adds the main window to the class dynamically
"""
if not hasattr(self, u'_main_window'):
self._main_window = Registry().get(u'main_window')
return self._main_window
main_window = property(_get_main_window)

View File

@ -31,7 +31,7 @@ The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are pro
Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags
cannot be changed.
"""
from PyQt4 import QtCore, QtGui
from PyQt4 import QtGui
from openlp.core.lib import FormattingTags, translate
from openlp.core.lib.ui import critical_error_message_box

View File

@ -93,14 +93,14 @@ class GeneralTab(SettingsTab):
self.monitor_layout.addWidget(self.custom_width_label, 3, 3)
self.custom_width_value_edit = QtGui.QSpinBox(self.monitor_group_box)
self.custom_width_value_edit.setObjectName(u'custom_width_value_edit')
self.custom_width_value_edit.setMaximum(9999)
self.custom_width_value_edit.setRange(1, 9999)
self.monitor_layout.addWidget(self.custom_width_value_edit, 4, 3)
self.custom_height_label = QtGui.QLabel(self.monitor_group_box)
self.custom_height_label.setObjectName(u'custom_height_label')
self.monitor_layout.addWidget(self.custom_height_label, 3, 4)
self.custom_height_value_edit = QtGui.QSpinBox(self.monitor_group_box)
self.custom_height_value_edit.setObjectName(u'custom_height_value_edit')
self.custom_height_value_edit.setMaximum(9999)
self.custom_height_value_edit.setRange(1, 9999)
self.monitor_layout.addWidget(self.custom_height_value_edit, 4, 4)
self.display_on_monitor_check = QtGui.QCheckBox(self.monitor_group_box)
self.display_on_monitor_check.setObjectName(u'monitor_combo_box')

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -38,7 +38,7 @@ from openlp.core.lib import UiStrings, Registry, translate
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import ThemeLayoutForm
from openlp.core.utils import get_images_filter
from openlp.core.utils import get_images_filter, is_not_image_file
from themewizard import Ui_ThemeWizard
log = logging.getLogger(__name__)
@ -71,6 +71,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.gradientStartButton.clicked.connect(self.onGradientStartButtonClicked)
self.gradientEndButton.clicked.connect(self.onGradientEndButtonClicked)
self.imageBrowseButton.clicked.connect(self.onImageBrowseButtonClicked)
self.imageFileEdit.editingFinished.connect(self.onImageFileEditEditingFinished)
self.mainColorButton.clicked.connect(self.onMainColorButtonClicked)
self.outlineColorButton.clicked.connect(self.onOutlineColorButtonClicked)
self.shadowColorButton.clicked.connect(self.onShadowColorButtonClicked)
@ -178,7 +179,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
"""
background_image = BackgroundType.to_string(BackgroundType.Image)
if self.page(self.currentId()) == self.backgroundPage and \
self.theme.background_type == background_image and not self.imageFileEdit.text():
self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.'))
@ -441,6 +442,12 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.theme.background_filename = unicode(filename)
self.setBackgroundPageValues()
def onImageFileEditEditingFinished(self):
"""
Background image path edited
"""
self.theme.background_filename = unicode(self.imageFileEdit.text())
def onMainColorButtonClicked(self):
"""
Set the main colour value

View File

@ -246,6 +246,23 @@ def get_images_filter():
return IMAGES_FILTER
def is_not_image_file(file_name):
"""
Validate that the file is not an image file.
``file_name``
File name to be checked.
"""
if not file_name:
return True
else:
formats = [unicode(fmt).lower() for fmt in QtGui.QImageReader.supportedImageFormats()]
file_part, file_extension = os.path.splitext(unicode(file_name))
if file_extension[1:].lower() in formats and os.path.exists(file_name):
return False
return True
def split_filename(path):
"""
Return a list of the parts in a given path.

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -59,7 +59,7 @@ class PresentationMediaItem(MediaManagerItem):
self.controllers = controllers
self.icon_path = u'presentations/presentation'
self.Automatic = u''
MediaManagerItem.__init__(self, parent, plugin)
super(PresentationMediaItem, self).__init__(parent, plugin)
self.message_listener = MessageListener(self)
self.has_search = True
self.single_service_item = False
@ -80,15 +80,15 @@ class PresentationMediaItem(MediaManagerItem):
"""
Build the list of file extensions to be used in the Open file dialog.
"""
file_type_list = u''
file_type_string = u''
for controller in self.controllers:
if self.controllers[controller].enabled():
file_types = self.controllers[controller].supports + self.controllers[controller].also_supports
for file_type in file_types:
if file_type_list.find(file_type) == -1:
file_type_list += u'*.%s ' % file_type
if file_type not in file_type_string:
file_type_string += u'*.%s ' % file_type
self.service_manager.supported_suffixes(file_type)
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type_list
self.on_new_file_masks = translate('PresentationPlugin.MediaItem', 'Presentations (%s)') % file_type_string
def required_icons(self):
"""

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -30,10 +30,10 @@
<head>
<meta charset="utf-8" />
<title>${live_title}</title>
<link rel="stylesheet" href="/files/live.css" />
<link rel="stylesheet" href="/files/main.css" />
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
<script type="text/javascript" src="/files/jquery.js"></script>
<script type="text/javascript" src="/files/live.js"></script>
<script type="text/javascript" src="/files/main.js"></script>
</head>
<body>
<img id="image" class="size"/>

View File

@ -26,7 +26,7 @@
window.OpenLP = {
loadSlide: function (event) {
$.getJSON(
"/live/image",
"/main/image",
function (data, status) {
var img = document.getElementById('image');
img.src = data.results.slide_image;
@ -36,7 +36,7 @@ window.OpenLP = {
},
pollServer: function () {
$.getJSON(
"/live/poll",
"/main/poll",
function (data, status) {
if (OpenLP.slideCount != data.results.slide_count) {
OpenLP.slideCount = data.results.slide_count;

View File

@ -177,11 +177,11 @@ class HttpServer(object):
self.root = self.Public()
self.root.files = self.Files()
self.root.stage = self.Stage()
self.root.live = self.Live()
self.root.main = self.Main()
self.root.router = self.router
self.root.files.router = self.router
self.root.stage.router = self.router
self.root.live.router = self.router
self.root.main.router = self.router
cherrypy.tree.mount(self.root, '/', config=self.define_config())
# Turn off the flood of access messages cause by poll
cherrypy.log.access_log.propagate = False
@ -218,7 +218,7 @@ class HttpServer(object):
u'/stage': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.router.html_dir,
u'tools.basic_auth.on': False},
u'/live': {u'tools.staticdir.on': True,
u'/main': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.router.html_dir,
u'tools.basic_auth.on': False}}
return directory_config
@ -253,9 +253,9 @@ class HttpServer(object):
url = urlparse.urlparse(cherrypy.url())
return self.router.process_http_request(url.path, *args)
class Live(object):
class Main(object):
"""
Live view is read only so security is not relevant and would reduce it's usability
Main view is read only so security is not relevant and would reduce it's usability
"""
@cherrypy.expose
def default(self, *args, **kwargs):
@ -281,12 +281,12 @@ class HttpRouter(object):
self.routes = [
(u'^/$', self.serve_file),
(u'^/(stage)$', self.serve_file),
(u'^/(live)$', self.serve_file),
(u'^/(main)$', self.serve_file),
(r'^/files/(.*)$', self.serve_file),
(r'^/api/poll$', self.poll),
(r'^/stage/poll$', self.poll),
(r'^/live/poll$', self.live_poll),
(r'^/live/image$', self.live_image),
(r'^/main/poll$', self.main_poll),
(r'^/main/image$', self.main_image),
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service),
@ -378,7 +378,7 @@ class HttpRouter(object):
'slides': translate('RemotePlugin.Mobile', 'Slides')
}
def serve_file(self, filename=None):
def serve_file(self, file_name=None):
"""
Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
If subfolders requested return 404, easier for security for the present.
@ -386,17 +386,17 @@ class HttpRouter(object):
Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
where xx is the language, e.g. 'en'
"""
log.debug(u'serve file request %s' % filename)
if not filename:
filename = u'index.html'
elif filename == u'stage':
filename = u'stage.html'
elif filename == u'live':
filename = u'live.html'
path = os.path.normpath(os.path.join(self.html_dir, filename))
log.debug(u'serve file request %s' % file_name)
if not file_name:
file_name = u'index.html'
elif file_name == u'stage':
file_name = u'stage.html'
elif file_name == u'main':
file_name = u'main.html'
path = os.path.normpath(os.path.join(self.html_dir, file_name))
if not path.startswith(self.html_dir):
return self._http_not_found()
ext = os.path.splitext(filename)[1]
ext = os.path.splitext(file_name)[1]
html = None
if ext == u'.html':
mimetype = u'text/html'
@ -447,7 +447,7 @@ class HttpRouter(object):
cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': result})
def live_poll(self):
def main_poll(self):
"""
Poll OpenLP to determine the current slide count.
"""
@ -457,7 +457,7 @@ class HttpRouter(object):
cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': result})
def live_image(self):
def main_image(self):
"""
Return the latest display image as a byte stream.
"""

View File

@ -491,6 +491,16 @@ class SongImportForm(OpenLPWizard):
main_window = property(_get_main_window)
def _get_main_window(self):
"""
Adds the main window to the class dynamically
"""
if not hasattr(self, u'_main_window'):
self._main_window = Registry().get(u'main_window')
return self._main_window
main_window = property(_get_main_window)
class SongImportSourcePage(QtGui.QWizardPage):
"""

View File

@ -48,12 +48,25 @@ NOTE_REGEX = re.compile(r'\(.*?\)')
class FieldDescEntry:
def __init__(self, name, type, size):
def __init__(self, name, field_type, size):
self.name = name
self.type = type
self.field_type = field_type
self.size = size
class FieldType(object):
"""
An enumeration class for different field types that can be expected in an EasyWorship song file.
"""
String = 1
Int16 = 3
Int32 = 4
Logical = 9
Memo = 0x0c
Blob = 0x0d
Timestamp = 0x15
class EasyWorshipSongImport(SongImport):
"""
The :class:`EasyWorshipSongImport` class provides OpenLP with the
@ -65,9 +78,7 @@ class EasyWorshipSongImport(SongImport):
def doImport(self):
# Open the DB and MB files if they exist
import_source_mb = self.import_source.replace('.DB', '.MB')
if not os.path.isfile(self.import_source):
return
if not os.path.isfile(import_source_mb):
if not os.path.isfile(self.import_source) or not os.path.isfile(import_source_mb):
return
db_size = os.path.getsize(self.import_source)
if db_size < 0x800:
@ -107,10 +118,6 @@ class EasyWorshipSongImport(SongImport):
self.encoding = retrieve_windows_encoding(self.encoding)
if not self.encoding:
return
# There does not appear to be a _reliable_ way of getting the number
# of songs/records, so let's use file blocks for measuring progress.
total_blocks = (db_size - header_size) / (block_size * 1024)
self.import_wizard.progress_bar.setMaximum(total_blocks)
# Read the field description information
db_file.seek(120)
field_info = db_file.read(num_fields * 2)
@ -134,12 +141,22 @@ class EasyWorshipSongImport(SongImport):
except IndexError:
# This is the wrong table
success = False
# Loop through each block of the file
# There does not appear to be a _reliable_ way of getting the number of songs/records, so loop through the file
# blocks and total the number of records. Store the information in a list so we dont have to do all this again.
cur_block = first_block
total_count = 0
block_list = []
while cur_block != 0 and success:
db_file.seek(header_size + ((cur_block - 1) * 1024 * block_size))
cur_block_pos = header_size + ((cur_block - 1) * 1024 * block_size)
db_file.seek(cur_block_pos)
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
rec_count = (rec_count + record_size) / record_size
block_list.append((cur_block_pos, rec_count))
total_count += rec_count
self.import_wizard.progress_bar.setMaximum(total_count)
for block in block_list:
cur_block_pos, rec_count = block
db_file.seek(cur_block_pos + 6)
# Loop through each record within the current block
for i in range(rec_count):
if self.stop_import_flag:
@ -227,26 +244,19 @@ class EasyWorshipSongImport(SongImport):
# Begin with empty field struct list
fsl = ['>']
for field_desc in field_descs:
if field_desc.type == 1:
# string
if field_desc.field_type == FieldType.String:
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 3:
# 16-bit int
elif field_desc.field_type == FieldType.Int16:
fsl.append('H')
elif field_desc.type == 4:
# 32-bit int
elif field_desc.field_type == FieldType.Int32:
fsl.append('I')
elif field_desc.type == 9:
# Logical
elif field_desc.field_type == FieldType.Logical:
fsl.append('B')
elif field_desc.type == 0x0c:
# Memo
elif field_desc.field_type == FieldType.Memo:
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 0x0d:
# Blob
elif field_desc.field_type == FieldType.Blob:
fsl.append('%ds' % field_desc.size)
elif field_desc.type == 0x15:
# Timestamp
elif field_desc.field_type == FieldType.Timestamp:
fsl.append('Q')
else:
fsl.append('%ds' % field_desc.size)
@ -263,20 +273,15 @@ class EasyWorshipSongImport(SongImport):
elif field == 0:
return None
# Format the field depending on the field type
if field_desc.type == 1:
# string
if field_desc.field_type == FieldType.String:
return field.rstrip('\0').decode(self.encoding)
elif field_desc.type == 3:
# 16-bit int
elif field_desc.field_type == FieldType.Int16:
return field ^ 0x8000
elif field_desc.type == 4:
# 32-bit int
elif field_desc.field_type == FieldType.Int32:
return field ^ 0x80000000
elif field_desc.type == 9:
# Logical
elif field_desc.field_type == FieldType.Logical:
return (field ^ 0x80 == 1)
elif field_desc.type == 0x0c or field_desc.type == 0x0d:
# Memo or Blob
elif field_desc.field_type == FieldType.Memo or field_desc.field_type == FieldType.Blob:
block_start, blob_size = struct.unpack_from('<II', field, len(field)-10)
sub_block = block_start & 0xff
block_start &= ~0xff

View File

@ -96,6 +96,7 @@ import os
from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.songimport import SongImport
@ -115,7 +116,7 @@ class FoilPresenterImport(SongImport):
"""
log.debug(u'initialise FoilPresenterImport')
SongImport.__init__(self, manager, **kwargs)
self.FoilPresenter = FoilPresenter(self.manager)
self.FoilPresenter = FoilPresenter(self.manager, self)
def doImport(self):
"""
@ -202,8 +203,9 @@ class FoilPresenter(object):
<copyright> tag.
"""
def __init__(self, manager):
def __init__(self, manager, importer):
self.manager = manager
self.importer = importer
def xml_to_song(self, xml):
"""
@ -222,22 +224,23 @@ class FoilPresenter(object):
song.search_lyrics = u''
song.verse_order = u''
song.search_title = u''
# Because "text" seems to be an reserverd word, we have to recompile it.
self.save_song = True
# Because "text" seems to be an reserved word, we have to recompile it.
xml = re.compile(u'<text>').sub(u'<text_>', xml)
xml = re.compile(u'</text>').sub(u'</text_>', xml)
song_xml = objectify.fromstring(xml)
foilpresenterfolie = song_xml
self._process_copyright(foilpresenterfolie, song)
self._process_cclinumber(foilpresenterfolie, song)
self._process_titles(foilpresenterfolie, song)
self._process_copyright(song_xml, song)
self._process_cclinumber(song_xml, song)
self._process_titles(song_xml, song)
# The verse order is processed with the lyrics!
self._process_lyrics(foilpresenterfolie, song)
self._process_comments(foilpresenterfolie, song)
self._process_authors(foilpresenterfolie, song)
self._process_songbooks(foilpresenterfolie, song)
self._process_topics(foilpresenterfolie, song)
clean_song(self.manager, song)
self.manager.save_object(song)
self._process_lyrics(song_xml, song)
self._process_comments(song_xml, song)
self._process_authors(song_xml, song)
self._process_songbooks(song_xml, song)
self._process_topics(song_xml, song)
if self.save_song:
clean_song(self.manager, song)
self.manager.save_object(song)
def _child(self, element):
"""
@ -420,6 +423,12 @@ class FoilPresenter(object):
VerseType.tags[VerseType.Intro]: 1,
VerseType.tags[VerseType.PreChorus]: 1
}
if not hasattr(foilpresenterfolie.strophen, u'strophe'):
self.importer.logError(self._child(foilpresenterfolie.titel),
unicode(translate('SongsPlugin.FoilPresenterSongImport',
'Invalid Foilpresenter song file. No verses found.')))
self.save_song = False
return
for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_) if hasattr(strophe, u'text_') else u''
verse_name = self._child(strophe.key)

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,5 +1,5 @@
# -*- 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 #

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- 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 #
@ -83,7 +83,6 @@ MODULES = [
'bs4',
'mako',
'cherrypy',
'migrate',
'uno',
'icu',
'bs4',

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- 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 #

View File

@ -1,6 +1,6 @@
#!/usr/bin/env python
# -*- 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 #

View File

@ -0,0 +1,71 @@
"""
This module contains tests for the lib submodule of the Presentations plugin.
"""
import os
from tempfile import mkstemp
from unittest import TestCase
from mock import patch, MagicMock
from PyQt4 import QtGui
from openlp.core.lib import Registry
from openlp.plugins.presentations.lib.mediaitem import PresentationMediaItem
class TestMediaItem(TestCase):
"""
Test the mediaitem methods.
"""
def setUp(self):
"""
Set up the components need for all tests.
"""
Registry.create()
Registry().register(u'service_manager', MagicMock())
Registry().register(u'main_window', MagicMock())
with patch('openlp.plugins.presentations.lib.mediaitem.PresentationMediaItem.__init__') as mocked_init:
mocked_init.return_value = None
self.media_item = PresentationMediaItem(MagicMock(), MagicMock, MagicMock(), MagicMock())
self.application = QtGui.QApplication.instance()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.application
def build_file_mask_string_test(self):
"""
Test the build_file_mask_string() method
"""
# GIVEN: Different controllers.
impress_controller = MagicMock()
impress_controller.enabled.return_value = True
impress_controller.supports = [u'odp']
impress_controller.also_supports = [u'ppt']
presentation_controller = MagicMock()
presentation_controller.enabled.return_value = True
presentation_controller.supports = [u'ppt']
presentation_controller.also_supports = []
presentation_viewer_controller = MagicMock()
presentation_viewer_controller.enabled.return_value = False
# Mock the controllers.
self.media_item.controllers = {
u'Impress': impress_controller,
u'Powerpoint': presentation_controller,
u'Powerpoint Viewer': presentation_viewer_controller
}
# WHEN: Build the file mask.
with patch('openlp.plugins.presentations.lib.mediaitem.translate') as mocked_translate:
mocked_translate.side_effect = lambda module, string_to_translate: string_to_translate
self.media_item.build_file_mask_string()
# THEN: The file mask should be generated.
assert self.media_item.on_new_file_masks == u'Presentations (*.odp *.ppt )', \
u'The file mask should contain the odp and ppt extensions'

View File

@ -0,0 +1,377 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
"""
This module contains tests for the EasyWorship song importer.
"""
import os
from unittest import TestCase
from mock import patch, MagicMock
from openlp.plugins.songs.lib.ewimport import EasyWorshipSongImport, FieldDescEntry, FieldType
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), u'..', u'..', u'..', u'resources', u'easyworshipsongs'))
SONG_TEST_DATA = [
{u'title': u'Amazing Grace',
u'authors': [u'John Newton'],
u'copyright': u'Public Domain',
u'ccli_number': 0,
u'verses':
[(u'Amazing grace how sweet the sound,\nThat saved a wretch like me;\n'
u'I once was lost, but now am found\nWas blind, but now I see.', u'v1'),
(u'T\'was grace that taught my heart to fear,\nAnd grace my fears relieved;\n'
u'How precious did that grace appear\nThe hour I first believed.', u'v2'),
(u'Through many dangers, toil and snares,\nI have already come;\n'
u'\'Tis grace has brought me safe thus far,\nAnd grace will lead me home.', u'v3'),
(u'When we\'ve been there ten thousand years\nBright shining as the sun,\n'
u'We\'ve no less days to sing God\'s praise\nThan when we\'ve first begun.', u'v4')],
u'verse_order_list': []},
{u'title': u'Beautiful Garden Of Prayer',
u'authors': [u'Eleanor Allen Schroll James H. Fillmore'],
u'copyright': u'Public Domain',
u'ccli_number': 0,
u'verses':
[(u'O the beautiful garden, the garden of prayer,\nO the beautiful garden of prayer.\n'
u'There my Savior awaits, and He opens the gates\nTo the beautiful garden of prayer.', u'c1'),
(u'There\'s a garden where Jesus is waiting,\nThere\'s a place that is wondrously fair.\n'
u'For it glows with the light of His presence,\n\'Tis the beautiful garden of prayer.', u'v1'),
(u'There\'s a garden where Jesus is waiting,\nAnd I go with my burden and care.\n'
u'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', u'v2'),
(u'There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n'
u'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', u'v3')],
u'verse_order_list': []}]
class EasyWorshipSongImportLogger(EasyWorshipSongImport):
"""
This class logs changes in the title instance variable
"""
_title_assignment_list = []
def __init__(self, manager):
EasyWorshipSongImport.__init__(self, manager)
@property
def title(self):
return self._title_assignment_list[-1]
@title.setter
def title(self, title):
self._title_assignment_list.append(title)
class TestFieldDesc:
def __init__(self, name, field_type, size):
self.name = name
self.field_type = field_type
self.size = size
TEST_DATA_ENCODING = u'cp1252'
CODE_PAGE_MAPPINGS = [(852, u'cp1250'), (737, u'cp1253'), (775, u'cp1257'), (855, u'cp1251'), (857, u'cp1254'),
(866, u'cp1251'), (869, u'cp1253'), (862, u'cp1255'), (874, u'cp874')]
TEST_FIELD_DESCS = [TestFieldDesc(u'Title', FieldType.String, 50),
TestFieldDesc(u'Text Percentage Bottom', FieldType.Int16, 2), TestFieldDesc(u'RecID', FieldType.Int32, 4),
TestFieldDesc(u'Default Background', FieldType.Logical, 1), TestFieldDesc(u'Words', FieldType.Memo, 250),
TestFieldDesc(u'Words', FieldType.Memo, 250), TestFieldDesc(u'BK Bitmap', FieldType.Blob, 10),
TestFieldDesc(u'Last Modified', FieldType.Timestamp, 10)]
TEST_FIELDS = ['A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750,
129, '{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;'
'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g<EFBFBD><EFBFBD>\7\0f\r\0\0\1\0',
'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255'
'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g><3E>\6\0<EFBFBD>\6\0\0\1\0', '\0\0\0\0\0\0\0\0\0\0', 0]
GET_MEMO_FIELD_TEST_RESULTS = [
(4, u'\2', {u'return': u'\2',u'read': (1, 3430), u'seek': (507136, (8, os.SEEK_CUR))}),
(4, u'\3', {u'return': u'', u'read': (1, ), u'seek': (507136, )}),
(5, u'\3', {u'return': u'\3', u'read': (1, 1725), u'seek': (3220111360L, (41L, os.SEEK_CUR), 3220111408L)}),
(5, u'\4', {u'return': u'', u'read': (), u'seek': ()})]
class TestEasyWorshipSongImport(TestCase):
"""
Test the functions in the :mod:`ewimport` module.
"""
def create_field_desc_entry_test(self):
"""
Test creating an instance of the :class`FieldDescEntry` class.
"""
# GIVEN: Set arguments
name = u'Title'
field_type = FieldType.String
size = 50
# WHEN: A FieldDescEntry object is created.
field_desc_entry = FieldDescEntry(name, field_type, size)
# THEN:
self.assertIsNotNone(field_desc_entry, u'Import should not be none')
self.assertEquals(field_desc_entry.name, name, u'FieldDescEntry.name should be the same as the name argument')
self.assertEquals(field_desc_entry.field_type, field_type,
u'FieldDescEntry.type should be the same as the typeargument')
self.assertEquals(field_desc_entry.size, size, u'FieldDescEntry.size should be the same as the size argument')
def create_importer_test(self):
"""
Test creating an instance of the EasyWorship file importer
"""
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = EasyWorshipSongImport(mocked_manager)
# THEN: The importer object should not be None
self.assertIsNotNone(importer, u'Import should not be none')
def find_field_exists_test(self):
"""
Test finding an existing field in a given list using the :mod:`findField`
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer.fieldDescs = TEST_FIELD_DESCS
# WHEN: Called with a field name that exists
existing_fields = [u'Title', u'Text Percentage Bottom', u'RecID', u'Default Background', u'Words',
u'BK Bitmap', u'Last Modified']
for field_name in existing_fields:
# THEN: The item corresponding the index returned should have the same name attribute
self.assertEquals(importer.fieldDescs[importer.findField(field_name)].name, field_name)
def find_non_existing_field_test(self):
"""
Test finding an non-existing field in a given list using the :mod:`findField`
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer.fieldDescs = TEST_FIELD_DESCS
# WHEN: Called with a field name that does not exist
non_existing_fields = [u'BK Gradient Shading', u'BK Gradient Variant', u'Favorite', u'Copyright']
for field_name in non_existing_fields:
# THEN: The importer object should not be None
self.assertRaises(IndexError, importer.findField, field_name)
def set_record_struct_test(self):
"""
Test the :mod:`setRecordStruct` module
"""
# GIVEN: A mocked out SongImport class, a mocked out struct class, and a mocked out "manager" and a list of
# field descriptions
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
# WHEN: setRecordStruct is called with a list of field descriptions
return_value = importer.setRecordStruct(TEST_FIELD_DESCS)
# THEN: setRecordStruct should return None and Struct should be called with a value representing
# the list of field descriptions
self.assertIsNone(return_value, u'setRecordStruct should return None')
mocked_struct.Struct.assert_called_with('>50sHIB250s250s10sQ')
def get_field_test(self):
"""
Test the :mod:`getField` module
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer.encoding = TEST_DATA_ENCODING
importer.fields = TEST_FIELDS
importer.fieldDescs = TEST_FIELD_DESCS
field_results = [(0, 'A Heart Like Thine'), (1, 100), (2, 102L), (3, True), (6, None), (7, None)]
# WHEN: Called with test data
for field_index, result in field_results:
return_value = importer.getField(field_index)
# THEN: getField should return the known results
self.assertEquals(return_value, result,
u'getField should return "%s" when called with "%s"' % (result, TEST_FIELDS[field_index]))
def get_memo_field_test(self):
"""
Test the :mod:`getField` module
"""
for test_results in GET_MEMO_FIELD_TEST_RESULTS:
# GIVEN: A mocked out SongImport class, a mocked out "manager", a mocked out memo_file and an encoding
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
mocked_memo_file = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer.memoFile = mocked_memo_file
importer.encoding = TEST_DATA_ENCODING
# WHEN: Supplied with test fields and test field descriptions
importer.fields = TEST_FIELDS
importer.fieldDescs = TEST_FIELD_DESCS
field_index = test_results[0]
mocked_memo_file.read.return_value = test_results[1]
get_field_result = test_results[2][u'return']
get_field_read_calls = test_results[2][u'read']
get_field_seek_calls = test_results[2][u'seek']
# THEN: getField should return the appropriate value with the appropriate mocked objects being called
self.assertEquals(importer.getField(field_index), get_field_result)
for call in get_field_read_calls:
mocked_memo_file.read.assert_any_call(call)
for call in get_field_seek_calls:
if isinstance(call, (int, long)):
mocked_memo_file.seek.assert_any_call(call)
else:
mocked_memo_file.seek.assert_any_call(call[0], call[1])
def do_import_source_test(self):
"""
Test the :mod:`doImport` module opens the correct files
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
mocked_os_path.isfile.side_effect = [True, False]
# WHEN: Supplied with an import source
importer.import_source = u'Songs.DB'
# THEN: doImport should return None having called os.path.isfile
self.assertIsNone(importer.doImport(), u'doImport should return None')
mocked_os_path.isfile.assert_any_call(u'Songs.DB')
mocked_os_path.isfile.assert_any_call(u'Songs.MB')
def do_import_database_validity_test(self):
"""
Test the :mod:`doImport` module handles invalid database files correctly
"""
# GIVEN: A mocked out SongImport class, os.path and a mocked out "manager"
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
mocked_os_path.isfile.return_value = True
importer.import_source = u'Songs.DB'
# WHEN: DB file size is less than 0x800
mocked_os_path.getsize.return_value = 0x7FF
# THEN: doImport should return None having called os.path.isfile
self.assertIsNone(importer.doImport(), u'doImport should return None when db_size is less than 0x800')
mocked_os_path.getsize.assert_any_call(u'Songs.DB')
def do_import_memo_validty_test(self):
"""
Test the :mod:`doImport` module handles invalid memo files correctly
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \
patch(u'__builtin__.open') as mocked_open, \
patch(u'openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
mocked_os_path.isfile.return_value = True
mocked_os_path.getsize.return_value = 0x800
importer.import_source = u'Songs.DB'
# WHEN: Unpacking first 35 bytes of Memo file
struct_unpack_return_values = [(0, 0x700, 2, 0, 0), (0, 0x800, 0, 0, 0), (0, 0x800, 5, 0, 0)]
mocked_struct.unpack.side_effect = struct_unpack_return_values
# THEN: doImport should return None having called closed the open files db and memo files.
for effect in struct_unpack_return_values:
self.assertIsNone(importer.doImport(), u'doImport should return None when db_size is less than 0x800')
self.assertEqual(mocked_open().close.call_count, 2,
u'The open db and memo files should have been closed')
mocked_open().close.reset_mock()
self.assertIs(mocked_open().seek.called, False, u'db_file.seek should not have been called.')
def code_page_to_encoding_test(self):
"""
Test the :mod:`doImport` converts the code page to the encoding correctly
"""
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \
patch(u'__builtin__.open'), patch(u'openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \
patch(u'openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
mocked_os_path.isfile.return_value = True
mocked_os_path.getsize.return_value = 0x800
importer.import_source = u'Songs.DB'
# WHEN: Unpacking the code page
for code_page, encoding in CODE_PAGE_MAPPINGS:
struct_unpack_return_values = [(0, 0x800, 2, 0, 0), (code_page, )]
mocked_struct.unpack.side_effect = struct_unpack_return_values
mocked_retrieve_windows_encoding.return_value = False
# THEN: doImport should return None having called retrieve_windows_encoding with the correct encoding.
self.assertIsNone(importer.doImport(), u'doImport should return None when db_size is less than 0x800')
mocked_retrieve_windows_encoding.assert_call(encoding)
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(u'openlp.plugins.songs.lib.ewimport.SongImport'), \
patch(u'openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding:
mocked_retrieve_windows_encoding.return_value = u'cp1252'
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
mocked_add_author = MagicMock()
mocked_add_verse = MagicMock()
mocked_finish = MagicMock()
mocked_title = MagicMock()
mocked_finish.return_value = True
importer = EasyWorshipSongImportLogger(mocked_manager)
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = False
importer.addAuthor = mocked_add_author
importer.addVerse = mocked_add_verse
importer.title = mocked_title
importer.finish = mocked_finish
importer.topics = []
# WHEN: Importing each file
importer.import_source = os.path.join(TEST_PATH, u'Songs.DB')
# THEN: doImport should return none, the song data should be as expected, and finish should have been
# called.
self.assertIsNone(importer.doImport(), u'doImport should return None when it has completed')
for song_data in SONG_TEST_DATA:
print mocked_title.mocked_calls()
title = song_data[u'title']
author_calls = song_data[u'authors']
song_copyright = song_data[u'copyright']
ccli_number = song_data[u'ccli_number']
add_verse_calls = song_data[u'verses']
verse_order_list = song_data[u'verse_order_list']
self.assertIn(title, importer._title_assignment_list, u'title for %s should be "%s"' % (title, title))
for author in author_calls:
mocked_add_author.assert_any_call(author)
if song_copyright:
self.assertEqual(importer.copyright, song_copyright)
if ccli_number:
self.assertEquals(importer.ccliNumber, ccli_number, u'ccliNumber for %s should be %s'
% (title, ccli_number))
for verse_text, verse_tag in add_verse_calls:
mocked_add_verse.assert_any_call(verse_text, verse_tag)
if verse_order_list:
self.assertEquals(importer.verseOrderList, verse_order_list, u'verseOrderList for %s should be %s'
% (title, verse_order_list))
mocked_finish.assert_called_with()

View File

@ -0,0 +1,195 @@
# -*- 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 SongShow Plus song importer.
"""
import os
from unittest import TestCase
from mock import patch, MagicMock
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), u'..', u'..', u'..', u'/resources/foilpresentersongs'))
class TestFoilPresenter(TestCase):
"""
Test the functions in the :mod:`foilpresenterimport` module.
"""
#TODO: The following modules still need tests written for
# xml_to_song
# _child
# _process_authors
# _process_cclinumber
# _process_comments
# _process_copyright
# _process_lyrics
# _process_songbooks
# _process_titles
# _process_topics
def setUp(self):
self.child_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._child')
self.clean_song_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.clean_song')
self.objectify_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.objectify')
self.process_authors_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_authors')
self.process_cclinumber_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_cclinumber')
self.process_comments_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_comments')
self.process_lyrics_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_lyrics')
self.process_songbooks_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_songbooks')
self.process_titles_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_titles')
self.process_topics_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_topics')
self.re_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.re')
self.song_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.Song')
self.song_xml_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.SongXML')
self.translate_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.translate')
self.mocked_child = self.child_patcher.start()
self.mocked_clean_song = self.clean_song_patcher.start()
self.mocked_objectify = self.objectify_patcher.start()
self.mocked_process_authors = self.process_authors_patcher.start()
self.mocked_process_cclinumber = self.process_cclinumber_patcher.start()
self.mocked_process_comments = self.process_comments_patcher.start()
self.mocked_process_lyrics = self.process_lyrics_patcher.start()
self.mocked_process_songbooks = self.process_songbooks_patcher.start()
self.mocked_process_titles = self.process_titles_patcher.start()
self.mocked_process_topics = self.process_topics_patcher.start()
self.mocked_re = self.re_patcher.start()
self.mocked_song = self.song_patcher.start()
self.mocked_song_xml = self.song_xml_patcher.start()
self.mocked_translate = self.translate_patcher.start()
self.mocked_child.return_value = u'Element Text'
self.mocked_translate.return_value = u'Translated String'
self.mocked_manager = MagicMock()
self.mocked_song_import = MagicMock()
def tearDown(self):
self.child_patcher.stop()
self.clean_song_patcher.stop()
self.objectify_patcher.stop()
self.process_authors_patcher.stop()
self.process_cclinumber_patcher.stop()
self.process_comments_patcher.stop()
self.process_lyrics_patcher.stop()
self.process_songbooks_patcher.stop()
self.process_titles_patcher.stop()
self.process_topics_patcher.stop()
self.re_patcher.stop()
self.song_patcher.stop()
self.song_xml_patcher.stop()
self.translate_patcher.stop()
def create_foil_presenter_test(self):
"""
Test creating an instance of the FoilPresenter class
"""
# GIVEN: A mocked out "manager" and "SongImport" instance
mocked_manager = MagicMock()
mocked_song_import = MagicMock()
# WHEN: An FoilPresenter instance is created
foil_presenter_instance = FoilPresenter(mocked_manager, mocked_song_import)
# THEN: The instance should not be None
self.assertIsNotNone(foil_presenter_instance, u'FoilPresenter instance should not be none')
def no_xml_test(self):
"""
Test calling xml_to_song with out the xml argument
"""
# GIVEN: A mocked out "manager" and "SongImport" as well as an foil_presenter instance
mocked_manager = MagicMock()
mocked_song_import = MagicMock()
foil_presenter_instance = FoilPresenter(mocked_manager, mocked_song_import)
# WHEN: xml_to_song is called without valid an argument
for arg in [None, False, 0, u'']:
result = foil_presenter_instance.xml_to_song(arg)
# Then: xml_to_song should return False
self.assertEqual(result, None, u'xml_to_song should return None when called with %s' % arg)
def encoding_declaration_removal_test(self):
"""
Test that the encoding declaration is removed
"""
# GIVEN: A reset mocked out re and an instance of foil_presenter
self.mocked_re.reset()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: xml_to_song is called with a string with an xml encoding declaration
foil_presenter_instance.xml_to_song(u'<?xml version="1.0" encoding="UTF-8"?>\n<foilpresenterfolie>')
# THEN: the xml encoding declaration should have been stripped
self.mocked_re.compile.sub.called_with(u'\n<foilpresenterfolie>')
def no_encoding_declaration_test(self):
"""
Check that the xml sting is left intact when no encoding declaration is made
"""
# GIVEN: A reset mocked out re and an instance of foil_presenter
self.mocked_re.reset()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: xml_to_song is called with a string without an xml encoding declaration
foil_presenter_instance.xml_to_song(u'<foilpresenterfolie>')
# THEN: the string shiuld have been left intact
self.mocked_re.compile.sub.called_with(u'<foilpresenterfolie>')
def process_lyrics_no_verses_test(self):
"""
Test that _process_lyrics handles song files that have no verses.
"""
# GIVEN: A mocked foilpresenterfolie with no attribute strophe, a mocked song and a
# foil presenter instance
self.process_lyrics_patcher.stop()
self.mocked_song_xml.reset()
mock_foilpresenterfolie = MagicMock()
del mock_foilpresenterfolie.strophen.strophe
mocked_song = MagicMock()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: _process_lyrics is called
result = foil_presenter_instance._process_lyrics(mock_foilpresenterfolie, mocked_song)
# THEN: _process_lyrics should return None and the song_import logError method should have been called once
self.assertIsNone(result)
self.mocked_song_import.logError.assert_called_once_with(u'Element Text', u'Translated String')
self.process_lyrics_patcher.start()

View File

@ -26,7 +26,7 @@ class TestMediaItem(TestCase):
Registry().register(u'service_list', MagicMock())
Registry().register(u'main_window', MagicMock())
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
self.media_item = SongMediaItem(MagicMock(), MagicMock())
fd, self.ini_file = mkstemp(u'.ini')

View File

@ -0,0 +1,52 @@
"""
Functional tests to test the AppLocation class and related methods.
"""
import os
from unittest import TestCase
from openlp.core.utils import is_not_image_file
from tests.utils.constants import TEST_RESOURCES_PATH
class TestUtils(TestCase):
"""
A test suite to test out various methods around the Utils functions.
"""
def is_not_image_empty_test(self):
"""
Test the method handles an empty string
"""
# Given and empty string
file_name = ""
# WHEN testing for it
result = is_not_image_file(file_name)
# THEN the result is false
assert result is True, u'The missing file test should return True'
def is_not_image_with_image_file_test(self):
"""
Test the method handles an image file
"""
# Given and empty string
file_name = os.path.join(TEST_RESOURCES_PATH, u'church.jpg')
# WHEN testing for it
result = is_not_image_file(file_name)
# THEN the result is false
assert result is False, u'The file is present so the test should return False'
def is_not_image_with_none_image_file_test(self):
"""
Test the method handles a non image file
"""
# Given and empty string
file_name = os.path.join(TEST_RESOURCES_PATH, u'serviceitem_custom_1.osj')
# WHEN testing for it
result = is_not_image_file(file_name)
# THEN the result is false
assert result is True, u'The file is not an image file so the test should return True'

View File

@ -80,9 +80,7 @@ class TestEditCustomForm(TestCase):
# GIVEN: Mocked methods.
with patch(u'openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
mocked_critical_error_message_box:
mocked_displayText = MagicMock()
mocked_displayText.return_value = u''
self.form.title_edit.displayText = mocked_displayText
self.form.title_edit.displayText = MagicMock(return_value=u'')
mocked_setFocus = MagicMock()
self.form.title_edit.setFocus = mocked_setFocus
@ -101,12 +99,8 @@ class TestEditCustomForm(TestCase):
# GIVEN: Mocked methods.
with patch(u'openlp.plugins.custom.forms.editcustomform.critical_error_message_box') as \
mocked_critical_error_message_box:
mocked_displayText = MagicMock()
mocked_displayText.return_value = u'something'
self.form.title_edit.displayText = mocked_displayText
mocked_count = MagicMock()
mocked_count.return_value = 0
self.form.slide_list_view.count = mocked_count
self.form.title_edit.displayText = MagicMock(return_value=u'something')
self.form.slide_list_view.count = MagicMock(return_value=0)
# WHEN: Call the method.
result = self.form._validate()

Binary file not shown.

Binary file not shown.