diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py
index 3b7b31ca1..634bc5ced 100644
--- a/openlp/core/common/settings.py
+++ b/openlp/core/common/settings.py
@@ -286,6 +286,7 @@ class Settings(QtCore.QSettings):
'themes/last directory export': '',
'themes/last directory import': '',
'themes/theme level': ThemeLevel.Song,
+ 'themes/wrap footer': False,
'user interface/live panel': True,
'user interface/live splitter geometry': QtCore.QByteArray(),
'user interface/lock panel': False,
diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py
index d67c05c42..8e9380241 100644
--- a/openlp/core/lib/db.py
+++ b/openlp/core/lib/db.py
@@ -96,9 +96,10 @@ def upgrade_db(url, upgrade):
mapper(Metadata, metadata_table)
version_meta = session.query(Metadata).get('version')
if version_meta is None:
- version_meta = Metadata.populate(key='version', value='0')
+ # Tables have just been created - fill the version field with the most recent version
+ version = upgrade.__version__
+ version_meta = Metadata.populate(key='version', value=version)
session.add(version_meta)
- version = 0
else:
version = int(version_meta.value)
if version > upgrade.__version__:
diff --git a/openlp/core/lib/htmlbuilder.py b/openlp/core/lib/htmlbuilder.py
index 473aa9d7d..058e5a2a1 100644
--- a/openlp/core/lib/htmlbuilder.py
+++ b/openlp/core/lib/htmlbuilder.py
@@ -398,6 +398,7 @@ import logging
from PyQt4 import QtWebKit
+from openlp.core.common import Settings
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
log = logging.getLogger(__name__)
@@ -750,12 +751,13 @@ def build_footer_css(item, height):
font-size: %spt;
color: %s;
text-align: left;
- white-space: nowrap;
+ white-space: %s;
"""
theme = item.theme_data
if not theme or not item.footer:
return ''
bottom = height - int(item.footer.y()) - int(item.footer.height())
+ whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
- theme.font_footer_name, theme.font_footer_size, theme.font_footer_color)
+ theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
return lyrics_html
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 654e27f6d..d0ac1560e 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -993,7 +993,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
service_item.auto_play_slides_once = False
self.set_modified()
- def on_auto_start(self):
+ def on_auto_start(self, field=None):
"""
Toggles to Auto Start Setting.
"""
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index fdd2ea592..b07c7fd2b 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -384,16 +384,8 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
self.application.set_busy_cursor()
if path:
Settings().setValue(self.settings_section + '/last directory export', path)
- theme_path = os.path.join(path, theme + '.otz')
- theme_zip = None
try:
- theme_zip = zipfile.ZipFile(theme_path, 'w')
- source = os.path.join(self.path, theme)
- for files in os.walk(source):
- for name in files[2]:
- theme_zip.write(
- os.path.join(source, name).encode('utf-8'), os.path.join(theme, name).encode('utf-8')
- )
+ self._export_theme(path, theme)
QtGui.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
@@ -403,11 +395,29 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
translate('OpenLP.ThemeManager',
'Your theme could not be exported due to an error.'))
- finally:
- if theme_zip:
- theme_zip.close()
self.application.set_normal_cursor()
+ def _export_theme(self, path, theme):
+ """
+ Create the zipfile with the theme contents.
+ :param path: Location where the zip file will be placed
+ :param theme: The name of the theme to be exported
+ """
+ theme_path = os.path.join(path, theme + '.otz')
+ try:
+ theme_zip = zipfile.ZipFile(theme_path, 'w')
+ source = os.path.join(self.path, theme)
+ for files in os.walk(source):
+ for name in files[2]:
+ theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
+ except (IOError, OSError):
+ if theme_zip:
+ theme_zip.close()
+ shutil.rmtree(theme_path, True)
+ raise
+ else:
+ theme_zip.close()
+
def on_import_theme(self, field=None):
"""
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
@@ -650,7 +660,7 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
finally:
if out_file:
out_file.close()
- if image_from and image_from != image_to:
+ if image_from and os.path.abspath(image_from) != os.path.abspath(image_to):
try:
encoding = get_filesystem_encoding()
shutil.copyfile(str(image_from).encode(encoding), str(image_to).encode(encoding))
diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py
index 0478f0ed0..4b3f8b6eb 100644
--- a/openlp/core/ui/themestab.py
+++ b/openlp/core/ui/themestab.py
@@ -69,6 +69,14 @@ class ThemesTab(SettingsTab):
self.default_list_view.setObjectName('default_list_view')
self.global_group_box_layout.addWidget(self.default_list_view)
self.left_layout.addWidget(self.global_group_box)
+ self.universal_group_box = QtGui.QGroupBox(self.left_column)
+ self.universal_group_box.setObjectName('universal_group_box')
+ self.universal_group_box_layout = QtGui.QVBoxLayout(self.universal_group_box)
+ self.universal_group_box_layout.setObjectName('universal_group_box_layout')
+ self.wrap_footer_check_box = QtGui.QCheckBox(self.universal_group_box)
+ self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
+ self.universal_group_box_layout.addWidget(self.wrap_footer_check_box)
+ self.left_layout.addWidget(self.universal_group_box)
self.left_layout.addStretch()
self.level_group_box = QtGui.QGroupBox(self.right_column)
self.level_group_box.setObjectName('level_group_box')
@@ -112,6 +120,8 @@ class ThemesTab(SettingsTab):
"""
self.tab_title_visible = UiStrings().Themes
self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
+ self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
+ self.wrap_footer_check_box.setText(translate('OpenLP.ThemesTab', '&Wrap footer text'))
self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
self.song_level_label.setText(
@@ -136,6 +146,7 @@ class ThemesTab(SettingsTab):
settings.beginGroup(self.settings_section)
self.theme_level = settings.value('theme level')
self.global_theme = settings.value('global theme')
+ self.wrap_footer_check_box.setChecked(settings.value('wrap footer'))
settings.endGroup()
if self.theme_level == ThemeLevel.Global:
self.global_level_radio_button.setChecked(True)
@@ -152,6 +163,7 @@ class ThemesTab(SettingsTab):
settings.beginGroup(self.settings_section)
settings.setValue('theme level', self.theme_level)
settings.setValue('global theme', self.global_theme)
+ settings.setValue('wrap footer', self.wrap_footer_check_box.isChecked())
settings.endGroup()
self.renderer.set_theme_level(self.theme_level)
if self.tab_visited:
diff --git a/openlp/plugins/songs/lib/__init__.py b/openlp/plugins/songs/lib/__init__.py
index d03bdefd6..999f51fad 100644
--- a/openlp/plugins/songs/lib/__init__.py
+++ b/openlp/plugins/songs/lib/__init__.py
@@ -374,7 +374,7 @@ def clean_song(manager, song):
:param manager: The song database manager object.
:param song: The song object.
"""
- from .xml import SongXML
+ from .openlyricsxml import SongXML
if song.title:
song.title = clean_title(song.title)
diff --git a/openlp/plugins/songs/lib/importer.py b/openlp/plugins/songs/lib/importer.py
index a8ac76971..e9ee2c2f3 100644
--- a/openlp/plugins/songs/lib/importer.py
+++ b/openlp/plugins/songs/lib/importer.py
@@ -52,12 +52,11 @@ from .importers.zionworx import ZionWorxImport
from .importers.propresenter import ProPresenterImport
from .importers.worshipassistant import WorshipAssistantImport
from .importers.powerpraise import PowerPraiseImport
-# Imports that might fail
-
+from .importers.presentationmanager import PresentationManagerImport
log = logging.getLogger(__name__)
-
+# Imports that might fail
try:
from .importers.songsoffellowship import SongsOfFellowshipImport
HAS_SOF = True
@@ -163,16 +162,17 @@ class SongFormat(object):
OpenSong = 10
PowerPraise = 11
PowerSong = 12
- ProPresenter = 13
- SongBeamer = 14
- SongPro = 15
- SongShowPlus = 16
- SongsOfFellowship = 17
- SundayPlus = 18
- WordsOfWorship = 19
- WorshipAssistant = 20
- WorshipCenterPro = 21
- ZionWorx = 22
+ PresentationManager = 13
+ ProPresenter = 14
+ SongBeamer = 15
+ SongPro = 16
+ SongShowPlus = 17
+ SongsOfFellowship = 18
+ SundayPlus = 19
+ WordsOfWorship = 20
+ WorshipAssistant = 21
+ WorshipCenterPro = 22
+ ZionWorx = 23
# Set optional attribute defaults
__defaults__ = {
@@ -282,11 +282,17 @@ class SongFormat(object):
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
'database folder.')
},
+ PresentationManager: {
+ 'class': PresentationManagerImport,
+ 'name': 'PresentationManager',
+ 'prefix': 'presentationManager',
+ 'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'PresentationManager Song Files')
+ },
ProPresenter: {
'class': ProPresenterImport,
- 'name': 'ProPresenter',
+ 'name': 'ProPresenter 4',
'prefix': 'proPresenter',
- 'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter Song Files')
+ 'filter': '%s (*.pro4)' % translate('SongsPlugin.ImportWizardForm', 'ProPresenter 4 Song Files')
},
SongBeamer: {
'class': SongBeamerImport,
@@ -384,6 +390,7 @@ class SongFormat(object):
SongFormat.OpenSong,
SongFormat.PowerPraise,
SongFormat.PowerSong,
+ SongFormat.PresentationManager,
SongFormat.ProPresenter,
SongFormat.SongBeamer,
SongFormat.SongPro,
diff --git a/openlp/plugins/songs/lib/importers/__init__.py b/openlp/plugins/songs/lib/importers/__init__.py
index da302572e..f86a3e95e 100644
--- a/openlp/plugins/songs/lib/importers/__init__.py
+++ b/openlp/plugins/songs/lib/importers/__init__.py
@@ -27,5 +27,5 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
-The :mod:`~openlp.plugins.songs.lib.import` module contains importers for the Songs plugin.
+The :mod:`~openlp.plugins.songs.lib.importers` module contains importers for the Songs plugin.
"""
diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py
new file mode 100644
index 000000000..52a047a30
--- /dev/null
+++ b/openlp/plugins/songs/lib/importers/presentationmanager.py
@@ -0,0 +1,93 @@
+# -*- 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:`presentationmanager` module provides the functionality for importing
+Presentationmanager song files into the current database.
+"""
+
+import os
+from lxml import objectify
+
+from openlp.core.ui.wizard import WizardStrings
+from .songimport import SongImport
+
+
+class PresentationManagerImport(SongImport):
+ """
+ The :class:`PresentationManagerImport` class provides OpenLP with the
+ ability to import Presentationmanager song files.
+ """
+ def do_import(self):
+ self.import_wizard.progress_bar.setMaximum(len(self.import_source))
+ for file_path in self.import_source:
+ if self.stop_import_flag:
+ return
+ self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
+ root = objectify.parse(open(file_path, 'rb')).getroot()
+ self.process_song(root)
+
+ def process_song(self, root):
+ self.set_defaults()
+ self.title = str(root.attributes.title)
+ self.add_author(str(root.attributes.author))
+ self.copyright = str(root.attributes.copyright)
+ self.ccli_number = str(root.attributes.ccli_number)
+ self.comments = str(root.attributes.comments)
+ verse_order_list = []
+ verse_count = {}
+ duplicates = []
+ for verse in root.verses.verse:
+ original_verse_def = verse.get('id')
+ # Presentation Manager stores duplicate verses instead of a verse order.
+ # We need to create the verse order from that.
+ is_duplicate = False
+ if original_verse_def in duplicates:
+ is_duplicate = True
+ else:
+ duplicates.append(original_verse_def)
+ if original_verse_def.startswith("Verse"):
+ verse_def = 'v'
+ elif original_verse_def.startswith("Chorus") or original_verse_def.startswith("Refrain"):
+ verse_def = 'c'
+ elif original_verse_def.startswith("Bridge"):
+ verse_def = 'b'
+ elif original_verse_def.startswith("End"):
+ verse_def = 'e'
+ else:
+ verse_def = 'o'
+ if not is_duplicate: # Only increment verse number if no duplicate
+ verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
+ verse_def = '%s%d' % (verse_def, verse_count[verse_def])
+ if not is_duplicate: # Only add verse if no duplicate
+ self.add_verse(str(verse).strip(), verse_def)
+ verse_order_list.append(verse_def)
+
+ self.verse_order_list = verse_order_list
+ if not self.finish():
+ self.log_error(self.import_source)
diff --git a/openlp/plugins/songs/lib/importers/propresenter.py b/openlp/plugins/songs/lib/importers/propresenter.py
index 3bf7f9cd8..b0509393b 100644
--- a/openlp/plugins/songs/lib/importers/propresenter.py
+++ b/openlp/plugins/songs/lib/importers/propresenter.py
@@ -33,17 +33,20 @@ ProPresenter song files into the current installation database.
import os
import base64
+import logging
from lxml import objectify
from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import strip_rtf
from .songimport import SongImport
+log = logging.getLogger(__name__)
+
class ProPresenterImport(SongImport):
"""
The :class:`ProPresenterImport` class provides OpenLP with the
- ability to import ProPresenter song files.
+ ability to import ProPresenter 4 song files.
"""
def do_import(self):
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
@@ -52,11 +55,11 @@ class ProPresenterImport(SongImport):
return
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
root = objectify.parse(open(file_path, 'rb')).getroot()
- self.process_song(root)
+ self.process_song(root, file_path)
- def process_song(self, root):
+ def process_song(self, root, filename):
self.set_defaults()
- self.title = root.get('CCLISongTitle')
+ self.title = os.path.basename(filename).rstrip('.pro4')
self.copyright = root.get('CCLICopyrightInfo')
self.comments = root.get('notes')
self.ccli_number = root.get('CCLILicenseNumber')
@@ -67,6 +70,9 @@ class ProPresenterImport(SongImport):
count = 0
for slide in root.slides.RVDisplaySlide:
count += 1
+ if not hasattr(slide.displayElements, 'RVTextElement'):
+ log.debug('No text found, may be an image slide')
+ continue
RTFData = slide.displayElements.RVTextElement.get('RTFData')
rtf = base64.standard_b64decode(RTFData)
words, encoding = strip_rtf(rtf.decode())
diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py
index 580ae767d..5b7255266 100644
--- a/openlp/plugins/songs/lib/upgrade.py
+++ b/openlp/plugins/songs/lib/upgrade.py
@@ -33,7 +33,6 @@ backend for the Songs plugin
import logging
from sqlalchemy import Column, ForeignKey, types
-from sqlalchemy.exc import OperationalError
from sqlalchemy.sql.expression import func, false, null, text
from openlp.core.lib.db import get_upgrade_op
@@ -57,16 +56,13 @@ def upgrade_1(session, metadata):
:param session:
:param metadata:
"""
- try:
- op = get_upgrade_op(session)
- op.drop_table('media_files_songs')
- op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
- op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
- if metadata.bind.url.get_dialect().name != 'sqlite':
- # SQLite doesn't support ALTER TABLE ADD CONSTRAINT
- op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
- except OperationalError:
- log.info('Upgrade 1 has already been run')
+ op = get_upgrade_op(session)
+ op.drop_table('media_files_songs')
+ op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
+ op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
+ if metadata.bind.url.get_dialect().name != 'sqlite':
+ # SQLite doesn't support ALTER TABLE ADD CONSTRAINT
+ op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
def upgrade_2(session, metadata):
@@ -75,12 +71,9 @@ def upgrade_2(session, metadata):
This upgrade adds a create_date and last_modified date to the songs table
"""
- try:
- op = get_upgrade_op(session)
- op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
- op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
- except OperationalError:
- log.info('Upgrade 2 has already been run')
+ op = get_upgrade_op(session)
+ op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
+ op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
def upgrade_3(session, metadata):
@@ -89,14 +82,11 @@ def upgrade_3(session, metadata):
This upgrade adds a temporary song flag to the songs table
"""
- try:
- op = get_upgrade_op(session)
- if metadata.bind.url.get_dialect().name == 'sqlite':
- op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
- else:
- op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
- except OperationalError:
- log.info('Upgrade 3 has already been run')
+ op = get_upgrade_op(session)
+ if metadata.bind.url.get_dialect().name == 'sqlite':
+ op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
+ else:
+ op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
def upgrade_4(session, metadata):
@@ -105,17 +95,14 @@ def upgrade_4(session, metadata):
This upgrade adds a column for author type to the authors_songs table
"""
- try:
- # Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
- # and copy the old values
- op = get_upgrade_op(session)
- op.create_table('authors_songs_tmp',
- Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
- Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
- Column('author_type', types.String(), primary_key=True,
- nullable=False, server_default=text('""')))
- op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
- op.drop_table('authors_songs')
- op.rename_table('authors_songs_tmp', 'authors_songs')
- except OperationalError:
- log.info('Upgrade 4 has already been run')
+ # Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
+ # and copy the old values
+ op = get_upgrade_op(session)
+ op.create_table('authors_songs_tmp',
+ Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
+ Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
+ Column('author_type', types.String(), primary_key=True,
+ nullable=False, server_default=text('""')))
+ op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
+ op.drop_table('authors_songs')
+ op.rename_table('authors_songs_tmp', 'authors_songs')
diff --git a/openlp/plugins/songusage/lib/upgrade.py b/openlp/plugins/songusage/lib/upgrade.py
index 24f264824..b0f0f52f0 100644
--- a/openlp/plugins/songusage/lib/upgrade.py
+++ b/openlp/plugins/songusage/lib/upgrade.py
@@ -32,7 +32,6 @@ backend for the SongsUsage plugin
"""
import logging
-from sqlalchemy.exc import OperationalError
from sqlalchemy import Column, types
from openlp.core.lib.db import get_upgrade_op
@@ -50,9 +49,6 @@ def upgrade_1(session, metadata):
:param session: SQLAlchemy Session object
:param metadata: SQLAlchemy MetaData object
"""
- try:
- op = get_upgrade_op(session)
- op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
- op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
- except OperationalError:
- log.info('Upgrade 1 has already taken place')
+ op = get_upgrade_op(session)
+ op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
+ op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py
index eeafbfe23..7abdbccd0 100755
--- a/scripts/jenkins_script.py
+++ b/scripts/jenkins_script.py
@@ -62,11 +62,13 @@ class OpenLPJobs(object):
Branch_Pull = 'Branch-01-Pull'
Branch_Functional = 'Branch-02-Functional-Tests'
Branch_Interface = 'Branch-03-Interface-Tests'
- Branch_Windows = 'Branch-04-Windows_Tests'
+ Branch_Windows_Functional = 'Branch-04a-Windows_Functional_Tests'
+ Branch_Windows_Interface = 'Branch-04b-Windows_Interface_Tests'
Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
- Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage]
+ Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows_Functional, Branch_Windows_Interface,
+ Branch_PEP, Branch_Coverage]
class Colour(object):
@@ -114,7 +116,9 @@ class JenkinsTrigger(object):
print('%s (revision %s)' % (get_repo_name(), revno))
for job in OpenLPJobs.Jobs:
- self.__print_build_info(job)
+ if not self.__print_build_info(job):
+ print('Stopping after failure')
+ break
def open_browser(self):
"""
@@ -131,6 +135,7 @@ class JenkinsTrigger(object):
:param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class
variables from the :class:`OpenLPJobs` class.
"""
+ is_success = False
job = self.jenkins_instance.job(job_name)
while job.info['inQueue']:
time.sleep(1)
@@ -139,11 +144,13 @@ class JenkinsTrigger(object):
if build.info['result'] == 'SUCCESS':
# Make 'SUCCESS' green.
result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
+ is_success = True
else:
# Make 'FAILURE' red.
result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
url = build.info['url']
print('[%s] %s' % (result_string, url))
+ return is_success
def get_repo_name():
diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py
index ef5ffdf43..7ba63a792 100644
--- a/tests/functional/openlp_core_lib/test_htmlbuilder.py
+++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py
@@ -6,11 +6,12 @@ from unittest import TestCase
from PyQt4 import QtCore
+from openlp.core.common import Settings
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
from openlp.core.lib.theme import HorizontalType, VerticalType
from tests.functional import MagicMock, patch
-
+from tests.helpers.testmixin import TestMixin
HTML = """
@@ -184,7 +185,7 @@ LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-c
LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \
'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \
'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; '
-FOOTER_CSS = """
+FOOTER_CSS_BASE = """
left: 10px;
bottom: 0px;
width: 1260px;
@@ -192,11 +193,28 @@ FOOTER_CSS = """
font-size: 12pt;
color: #FFFFFF;
text-align: left;
- white-space: nowrap;
+ white-space: %s;
"""
+FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
+FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
-class Htmbuilder(TestCase):
+class Htmbuilder(TestCase, TestMixin):
+ """
+ Test the functions in the Htmlbuilder module
+ """
+ def setUp(self):
+ """
+ Create the UI
+ """
+ self.build_settings()
+
+ def tearDown(self):
+ """
+ Delete all the C++ objects at the end so that we don't have a segfault
+ """
+ self.destroy_settings()
+
def build_html_test(self):
"""
Test the build_html() function
@@ -225,7 +243,7 @@ class Htmbuilder(TestCase):
html = build_html(item, screen, is_live, background, plugins=plugins)
# THEN: The returned html should match.
- assert html == HTML
+ self.assertEqual(html, HTML, 'The returned html should match')
def build_background_css_radial_test(self):
"""
@@ -241,7 +259,7 @@ class Htmbuilder(TestCase):
css = build_background_css(item, width)
# THEN: The returned css should match.
- assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
+ self.assertEqual(BACKGROUND_CSS_RADIAL, css, 'The background css should be equal.')
def build_lyrics_css_test(self):
"""
@@ -262,7 +280,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_css(item)
# THEN: The css should be equal.
- assert LYRICS_CSS == css, 'The lyrics css should be equal.'
+ self.assertEqual(LYRICS_CSS, css, 'The lyrics css should be equal.')
def build_lyrics_outline_css_test(self):
"""
@@ -279,7 +297,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_outline_css(theme_data)
# THEN: The css should be equal.
- assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
+ self.assertEqual(LYRICS_OUTLINE_CSS, css, 'The outline css should be equal.')
def build_lyrics_format_css_test(self):
"""
@@ -302,7 +320,7 @@ class Htmbuilder(TestCase):
css = build_lyrics_format_css(theme_data, width, height)
# THEN: They should be equal.
- assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
+ self.assertEqual(LYRICS_FORMAT_CSS, css, 'The lyrics format css should be equal.')
def build_footer_css_test(self):
"""
@@ -316,8 +334,27 @@ class Htmbuilder(TestCase):
item.theme_data.font_footer_color = '#FFFFFF'
height = 1024
- # WHEN: create the css.
+ # WHEN: create the css with default settings.
css = build_footer_css(item, height)
# THEN: THE css should be the same.
- assert FOOTER_CSS == css, 'The footer strings should be equal.'
+ self.assertEqual(FOOTER_CSS, css, 'The footer strings should be equal.')
+
+ def build_footer_css_wrap_test(self):
+ """
+ Test the build_footer_css() function
+ """
+ # GIVEN: Create a theme.
+ item = MagicMock()
+ item.footer = QtCore.QRect(10, 921, 1260, 103)
+ item.theme_data.font_footer_name = 'Arial'
+ item.theme_data.font_footer_size = 12
+ item.theme_data.font_footer_color = '#FFFFFF'
+ height = 1024
+
+ # WHEN: Settings say that footer should wrap
+ Settings().setValue('themes/wrap footer', True)
+ css = build_footer_css(item, height)
+
+ # THEN: Footer should wrap
+ self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')
diff --git a/tests/functional/openlp_core_ui/test_servicemanager.py b/tests/functional/openlp_core_ui/test_servicemanager.py
index 2c76ed965..d8d2be50e 100644
--- a/tests/functional/openlp_core_ui/test_servicemanager.py
+++ b/tests/functional/openlp_core_ui/test_servicemanager.py
@@ -31,7 +31,8 @@ Package to test the openlp.core.ui.slidecontroller package.
"""
from unittest import TestCase
-from openlp.core.common import Registry
+from openlp.core.common import Registry, ThemeLevel
+from openlp.core.lib import ServiceItem, ServiceItemType, ItemCapabilities
from openlp.core.ui import ServiceManager
from tests.interfaces import MagicMock, patch
@@ -89,3 +90,466 @@ class TestServiceManager(TestCase):
self.assertEqual('txt' in service_manager.suffixes, True, 'The suffix txt should be in the list')
self.assertEqual('ppt' in service_manager.suffixes, True, 'The suffix ppt should be in the list')
self.assertEqual('pptx' in service_manager.suffixes, True, 'The suffix pptx should be in the list')
+
+ def build_context_menu_test(self):
+ """
+ Test the creation of a context menu from a null service item.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have been called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have been called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
+ 'Should have been called once')
+
+ def build_song_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type text from Songs.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ mocked_renderer = MagicMock()
+ mocked_renderer.theme_level = ThemeLevel.Song
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', mocked_renderer)
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.CanEdit)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.OnLoadUpdate)
+ service_item.add_capability(ItemCapabilities.AddIfNewItem)
+ service_item.add_capability(ItemCapabilities.CanSoftBreak)
+ service_item.service_item_type = ServiceItemType.Text
+ service_item.edit_id = 1
+ service_item._display_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ # THEN we add a 2nd display frame
+ service_item._display_frames.append(MagicMock())
+ service_manager.context_menu(1)
+ # THEN the following additional calls should have occurred.
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
+
+ def build_bible_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type text from Bibles.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ mocked_renderer = MagicMock()
+ mocked_renderer.theme_level = ThemeLevel.Song
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', mocked_renderer)
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.NoLineBreaks)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanWordSplit)
+ service_item.add_capability(ItemCapabilities.CanEditTitle)
+ service_item.service_item_type = ServiceItemType.Text
+ service_item.edit_id = 1
+ service_item._display_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ # THEN we add a 2nd display frame
+ service_item._display_frames.append(MagicMock())
+ service_manager.context_menu(1)
+ # THEN the following additional calls should have occurred.
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
+
+ def build_custom_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type text from Custom.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ mocked_renderer = MagicMock()
+ mocked_renderer.theme_level = ThemeLevel.Song
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', mocked_renderer)
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.CanEdit)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanSoftBreak)
+ service_item.add_capability(ItemCapabilities.OnLoadUpdate)
+ service_item.service_item_type = ServiceItemType.Text
+ service_item.edit_id = 1
+ service_item._display_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ # THEN we add a 2nd display frame
+ service_item._display_frames.append(MagicMock())
+ service_manager.context_menu(1)
+ # THEN the following additional calls should have occurred.
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
+
+ def build_image_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type Image from Image.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', MagicMock())
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.CanMaintain)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanAppend)
+ service_item.add_capability(ItemCapabilities.CanEditTitle)
+ service_item.service_item_type = ServiceItemType.Image
+ service_item.edit_id = 1
+ service_item._raw_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ # THEN we add a 2nd display frame and regenerate the menu.
+ service_item._raw_frames.append(MagicMock())
+ service_manager.context_menu(1)
+ # THEN the following additional calls should have occurred.
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 2,
+ 'Should have be called twice')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 1, 'Should have be called once')
+
+ def build_media_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type Command from Media.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', MagicMock())
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
+ service_item.add_capability(ItemCapabilities.CanEditTitle)
+ service_item.add_capability(ItemCapabilities.RequiresMedia)
+ service_item.service_item_type = ServiceItemType.Command
+ service_item.edit_id = 1
+ service_item._raw_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ # THEN I change the length of the media and regenerate the menu.
+ service_item.set_media_length(5)
+ service_manager.context_menu(1)
+ # THEN the following additional calls should have occurred.
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 3, 'Should have be called three times')
+
+ def build_presentation_pdf_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type Command with PDF from Presentation.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', MagicMock())
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.CanMaintain)
+ service_item.add_capability(ItemCapabilities.CanPreview)
+ service_item.add_capability(ItemCapabilities.CanLoop)
+ service_item.add_capability(ItemCapabilities.CanAppend)
+ service_item.service_item_type = ServiceItemType.Command
+ service_item.edit_id = 1
+ service_item._raw_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 2, 'Should have be called twice')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+
+ def build_presentation_non_pdf_context_menu_test(self):
+ """
+ Test the creation of a context menu from service item of type Command with Impress from Presentation.
+ """
+ # GIVEN: A new service manager instance and a default service item.
+ Registry().register('plugin_manager', MagicMock())
+ Registry().register('renderer', MagicMock())
+ service_manager = ServiceManager(None)
+ item = MagicMock()
+ item.parent.return_value = False
+ item.data.return_value = 0
+ service_manager.service_manager_list = MagicMock()
+ service_manager.service_manager_list.itemAt.return_value = item
+ service_item = ServiceItem(None)
+ service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
+ service_item.service_item_type = ServiceItemType.Command
+ service_item.edit_id = 1
+ service_item._raw_frames.append(MagicMock())
+ service_manager.service_items.insert(1, {'service_item': service_item})
+ service_manager.edit_action = MagicMock()
+ service_manager.rename_action = MagicMock()
+ service_manager.create_custom_action = MagicMock()
+ service_manager.maintain_action = MagicMock()
+ service_manager.notes_action = MagicMock()
+ service_manager.time_action = MagicMock()
+ service_manager.auto_start_action = MagicMock()
+ service_manager.auto_play_slides_menu = MagicMock()
+ service_manager.auto_play_slides_once = MagicMock()
+ service_manager.auto_play_slides_loop = MagicMock()
+ service_manager.timed_slide_interval = MagicMock()
+ service_manager.theme_menu = MagicMock()
+ service_manager.menu = MagicMock()
+ # WHEN I define a context menu
+ service_manager.context_menu(1)
+ # THEN the following calls should have occurred.
+ self.assertEquals(service_manager.edit_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.rename_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.create_custom_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.maintain_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.notes_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.time_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_start_action.setVisible.call_count, 1, 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
+ self.assertEquals(service_manager.auto_play_slides_once.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.auto_play_slides_loop.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.timed_slide_interval.setChecked.call_count, 0, 'Should not be called')
+ self.assertEquals(service_manager.theme_menu.menuAction().setVisible.call_count, 1,
+ 'Should have be called once')
diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py
index ed237d424..1d241a317 100644
--- a/tests/functional/openlp_core_ui/test_slidecontroller.py
+++ b/tests/functional/openlp_core_ui/test_slidecontroller.py
@@ -504,21 +504,20 @@ class TestSlideController(TestCase):
mocked_item = MagicMock()
mocked_item.is_command.return_value = True
mocked_item.name = 'Mocked Item'
- mocked_execute = MagicMock()
mocked_update_preview = MagicMock()
mocked_preview_widget = MagicMock()
mocked_slide_selected = MagicMock()
- Registry.execute = mocked_execute
- Registry.create()
- slide_controller = SlideController(None)
- slide_controller.service_item = mocked_item
- slide_controller.update_preview = mocked_update_preview
- slide_controller.preview_widget = mocked_preview_widget
- slide_controller.slide_selected = mocked_slide_selected
- slide_controller.is_live = True
+ with patch.object(Registry, 'execute') as mocked_execute:
+ Registry.create()
+ slide_controller = SlideController(None)
+ slide_controller.service_item = mocked_item
+ slide_controller.update_preview = mocked_update_preview
+ slide_controller.preview_widget = mocked_preview_widget
+ slide_controller.slide_selected = mocked_slide_selected
+ slide_controller.is_live = True
- # WHEN: The method is called
- slide_controller.on_slide_selected_index([9])
+ # WHEN: The method is called
+ slide_controller.on_slide_selected_index([9])
# THEN: It should have sent a notification
mocked_item.is_command.assert_called_once_with()
@@ -535,20 +534,19 @@ class TestSlideController(TestCase):
mocked_item = MagicMock()
mocked_item.is_command.return_value = False
mocked_item.name = 'Mocked Item'
- mocked_execute = MagicMock()
mocked_update_preview = MagicMock()
mocked_preview_widget = MagicMock()
mocked_slide_selected = MagicMock()
- Registry.execute = mocked_execute
- Registry.create()
- slide_controller = SlideController(None)
- slide_controller.service_item = mocked_item
- slide_controller.update_preview = mocked_update_preview
- slide_controller.preview_widget = mocked_preview_widget
- slide_controller.slide_selected = mocked_slide_selected
+ with patch.object(Registry, 'execute') as mocked_execute:
+ Registry.create()
+ slide_controller = SlideController(None)
+ slide_controller.service_item = mocked_item
+ slide_controller.update_preview = mocked_update_preview
+ slide_controller.preview_widget = mocked_preview_widget
+ slide_controller.slide_selected = mocked_slide_selected
- # WHEN: The method is called
- slide_controller.on_slide_selected_index([7])
+ # WHEN: The method is called
+ slide_controller.on_slide_selected_index([7])
# THEN: It should have sent a notification
mocked_item.is_command.assert_called_once_with()
diff --git a/tests/functional/openlp_core_ui/test_thememanager.py b/tests/functional/openlp_core_ui/test_thememanager.py
new file mode 100644
index 000000000..0f3fa8ac4
--- /dev/null
+++ b/tests/functional/openlp_core_ui/test_thememanager.py
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2014 Raoul Snyman #
+# Portions copyright (c) 2008-2014 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 #
+###############################################################################
+"""
+Package to test the openlp.core.ui.thememanager package.
+"""
+import zipfile
+import os
+
+from unittest import TestCase
+from tests.interfaces import MagicMock
+
+from openlp.core.ui import ThemeManager
+from openlp.core.common import Registry
+
+from tests.utils.constants import TEST_RESOURCES_PATH
+from tests.interfaces import MagicMock, patch
+
+
+class TestThemeManager(TestCase):
+
+ def setUp(self):
+ """
+ Set up the tests
+ """
+ Registry.create()
+
+ def export_theme_test(self):
+ """
+ Test exporting a theme .
+ """
+ # GIVEN: A new ThemeManager instance.
+ theme_manager = ThemeManager()
+ theme_manager.path = os.path.join(TEST_RESOURCES_PATH, 'themes')
+ zipfile.ZipFile.__init__ = MagicMock()
+ zipfile.ZipFile.__init__.return_value = None
+ zipfile.ZipFile.write = MagicMock()
+
+ # WHEN: The theme is exported
+ theme_manager._export_theme(os.path.join('some', 'path'), 'Default')
+
+ # THEN: The zipfile should be created at the given path
+ zipfile.ZipFile.__init__.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
+ zipfile.ZipFile.write.assert_called_with(os.path.join(TEST_RESOURCES_PATH, 'themes', 'Default', 'Default.xml'),
+ os.path.join('Default', 'Default.xml'))
+
+ def initial_theme_manager_test(self):
+ """
+ Test the instantiation of theme manager.
+ """
+ # GIVEN: A new service manager instance.
+ ThemeManager(None)
+
+ # WHEN: the default theme manager is built.
+ # THEN: The the controller should be registered in the registry.
+ self.assertIsNotNone(Registry().get('theme_manager'), 'The base theme manager should be registered')
+
+ def write_theme_same_image_test(self):
+ """
+ Test that we don't try to overwrite a theme background image with itself
+ """
+ # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
+ # theme, check_directory_exists and thememanager-attributes.
+ with patch('builtins.open') as mocked_open, \
+ patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
+ patch('openlp.core.ui.thememanager.check_directory_exists') as mocked_check_directory_exists:
+ mocked_open.return_value = MagicMock()
+ theme_manager = ThemeManager(None)
+ theme_manager.old_background_image = None
+ theme_manager.generate_and_save_image = MagicMock()
+ theme_manager.path = ''
+ mocked_theme = MagicMock()
+ mocked_theme.theme_name = 'themename'
+ mocked_theme.extract_formatted_xml = MagicMock()
+ mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
+
+ # WHEN: Calling _write_theme with path to the same image, but the path written slightly different
+ file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
+ # Do replacement from end of string to avoid problems with path start
+ file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1]
+ theme_manager._write_theme(mocked_theme, file_name1, file_name2)
+
+ # THEN: The mocked_copyfile should not have been called
+ self.assertFalse(mocked_copyfile.called, 'shutil.copyfile should not be called')
+
+ def write_theme_diff_images_test(self):
+ """
+ Test that we do overwrite a theme background image when a new is submitted
+ """
+ # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
+ # theme, check_directory_exists and thememanager-attributes.
+ with patch('builtins.open') as mocked_open, \
+ patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
+ patch('openlp.core.ui.thememanager.check_directory_exists') as mocked_check_directory_exists:
+ mocked_open.return_value = MagicMock()
+ theme_manager = ThemeManager(None)
+ theme_manager.old_background_image = None
+ theme_manager.generate_and_save_image = MagicMock()
+ theme_manager.path = ''
+ mocked_theme = MagicMock()
+ mocked_theme.theme_name = 'themename'
+ mocked_theme.extract_formatted_xml = MagicMock()
+ mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
+
+ # WHEN: Calling _write_theme with path to different images
+ file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
+ file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg')
+ theme_manager._write_theme(mocked_theme, file_name1, file_name2)
+
+ # THEN: The mocked_copyfile should not have been called
+ self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called')
diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/functional/openlp_plugins/songs/test_db.py
index 53b98c045..e696ea94b 100644
--- a/tests/functional/openlp_plugins/songs/test_db.py
+++ b/tests/functional/openlp_plugins/songs/test_db.py
@@ -125,3 +125,31 @@ class TestDB(TestCase):
# THEN: The type should be correct
self.assertEqual(author_type, AuthorType.Words)
+
+ def test_author_get_display_name(self):
+ """
+ Test that the display name of an author is correct
+ """
+ # GIVEN: An author
+ author = Author()
+ author.display_name = "John Doe"
+
+ # WHEN: We call the get_display_name() function
+ display_name = author.get_display_name()
+
+ # THEN: It should return only the name
+ self.assertEqual("John Doe", display_name)
+
+ def test_author_get_display_name_with_type(self):
+ """
+ Test that the display name of an author with a type is correct
+ """
+ # GIVEN: An author
+ author = Author()
+ author.display_name = "John Doe"
+
+ # WHEN: We call the get_display_name() function
+ display_name = author.get_display_name(AuthorType.Words)
+
+ # THEN: It should return the name with the type in brackets
+ self.assertEqual("John Doe (Words)", display_name)
diff --git a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
index dbe834e1c..e6a2a5194 100644
--- a/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
+++ b/tests/functional/openlp_plugins/songs/test_powerpraiseimport.py
@@ -50,7 +50,7 @@ class TestPowerPraiseFileImport(SongImportTestHelper):
"""
Test that loading a PowerPraise file works correctly
"""
- self.file_import([os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.ppl')],
- self.load_external_result_data(os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.json')))
+ self.file_import([os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.ppl')],
+ self.load_external_result_data(os.path.join(TEST_PATH, 'Naher, mein Gott zu Dir.json')))
self.file_import([os.path.join(TEST_PATH, 'You are so faithful.ppl')],
self.load_external_result_data(os.path.join(TEST_PATH, 'You are so faithful.json')))
diff --git a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py
new file mode 100644
index 000000000..9d0f7dca4
--- /dev/null
+++ b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py
@@ -0,0 +1,53 @@
+# -*- coding: utf-8 -*-
+# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
+
+###############################################################################
+# OpenLP - Open Source Lyrics Projection #
+# --------------------------------------------------------------------------- #
+# Copyright (c) 2008-2014 Raoul Snyman #
+# Portions copyright (c) 2008-2014 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 PresentationManager song importer.
+"""
+
+import os
+
+from tests.helpers.songfileimport import SongImportTestHelper
+
+TEST_PATH = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'presentationmanagersongs'))
+
+
+class TestSongShowPlusFileImport(SongImportTestHelper):
+
+ def __init__(self, *args, **kwargs):
+ self.importer_class_name = 'PresentationManagerImport'
+ self.importer_module_name = 'presentationmanager'
+ super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs)
+
+ def test_song_import(self):
+ """
+ Test that loading a PresentationManager file works correctly
+ """
+ self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
+ self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
diff --git a/tests/functional/openlp_plugins/songs/test_propresenterimport.py b/tests/functional/openlp_plugins/songs/test_propresenterimport.py
index 3b79961f9..f3bdcb959 100644
--- a/tests/functional/openlp_plugins/songs/test_propresenterimport.py
+++ b/tests/functional/openlp_plugins/songs/test_propresenterimport.py
@@ -52,3 +52,5 @@ class TestProPresenterFileImport(SongImportTestHelper):
"""
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')],
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
+ self.file_import([os.path.join(TEST_PATH, 'Vaste Grond.pro4')],
+ self.load_external_result_data(os.path.join(TEST_PATH, 'Vaste Grond.json')))
diff --git a/tests/resources/powerpraisesongs/Näher, mein Gott zu Dir.json b/tests/resources/powerpraisesongs/Naher, mein Gott zu Dir.json
similarity index 100%
rename from tests/resources/powerpraisesongs/Näher, mein Gott zu Dir.json
rename to tests/resources/powerpraisesongs/Naher, mein Gott zu Dir.json
diff --git a/tests/resources/powerpraisesongs/Näher, mein Gott zu Dir.ppl b/tests/resources/powerpraisesongs/Naher, mein Gott zu Dir.ppl
similarity index 100%
rename from tests/resources/powerpraisesongs/Näher, mein Gott zu Dir.ppl
rename to tests/resources/powerpraisesongs/Naher, mein Gott zu Dir.ppl
diff --git a/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.json b/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.json
new file mode 100644
index 000000000..1e484b11b
--- /dev/null
+++ b/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.json
@@ -0,0 +1,25 @@
+{
+ "title": "Great Is Thy Faithfulness",
+ "authors": [
+ "Thomas O. Chisholm (1866-1960)"
+ ],
+ "verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1"],
+ "verses": [
+ [
+ "\"Great is Thy faithfulness\", O God my Father.\nThere is no shadow of turning with Thee;\nThou changest not, Thy compassions they fail not,\nAs Thou hast been Thou forever shall be.",
+ "v1"
+ ],
+ [
+ "Great is Thy faithfulness!\nGreat is Thy faithfulness!\nMorning by morning new mercies I see!\nAll I have needed Thy hand hath provided -\n\"Great is Thy faithfulness\", Lord, unto me!",
+ "c1"
+ ],
+ [
+ "Summer and winter, and springtime and harvest,\nSun, moon, and stars in their courses above,\nJoin with all nature in manifold witness,\nTo Thy great faithfulness, mercy and love.",
+ "v2"
+ ],
+ [
+ "Pardon for sin and a peace that endureth,\nThine own dear presence to cheer and to guide,\nStrength for today and bright hope for tomorrow,\nBlessings all mine, with ten thousand beside!",
+ "v3"
+ ]
+ ]
+}
diff --git a/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.sng b/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.sng
new file mode 100644
index 000000000..49b29c4c7
--- /dev/null
+++ b/tests/resources/presentationmanagersongs/Great Is Thy Faithfulness.sng
@@ -0,0 +1,51 @@
+
+
+
+Great Is Thy Faithfulness
+Thomas O. Chisholm (1866-1960)
+
+
+
+
+
+
+"Great is Thy faithfulness", O God my Father.
+There is no shadow of turning with Thee;
+Thou changest not, Thy compassions they fail not,
+As Thou hast been Thou forever shall be.
+
+
+Great is Thy faithfulness!
+Great is Thy faithfulness!
+Morning by morning new mercies I see!
+All I have needed Thy hand hath provided -
+"Great is Thy faithfulness", Lord, unto me!
+
+
+Summer and winter, and springtime and harvest,
+Sun, moon, and stars in their courses above,
+Join with all nature in manifold witness,
+To Thy great faithfulness, mercy and love.
+
+
+Great is Thy faithfulness!
+Great is Thy faithfulness!
+Morning by morning new mercies I see!
+All I have needed Thy hand hath provided -
+"Great is Thy faithfulness", Lord, unto me!
+
+
+Pardon for sin and a peace that endureth,
+Thine own dear presence to cheer and to guide,
+Strength for today and bright hope for tomorrow,
+Blessings all mine, with ten thousand beside!
+
+
+Great is Thy faithfulness!
+Great is Thy faithfulness!
+Morning by morning new mercies I see!
+All I have needed Thy hand hath provided -
+"Great is Thy faithfulness", Lord, unto me!
+
+
+
diff --git a/tests/resources/propresentersongs/Vaste Grond.json b/tests/resources/propresentersongs/Vaste Grond.json
new file mode 100644
index 000000000..75ffac7a2
--- /dev/null
+++ b/tests/resources/propresentersongs/Vaste Grond.json
@@ -0,0 +1,34 @@
+{
+ "title": "Vaste Grond",
+ "verse_order_list": [],
+ "verses": [
+ [
+ "God voor U is niets onmogelijk\nHoe ongelofelijk\nU heeft alles in de hand",
+ "v1"
+ ],
+ [
+ "U bent God en trekt Uw eigen plan\nU bent voor niemand bang\nVoor niets en niemand bang",
+ "v2"
+ ],
+ [
+ "U houd me vast en geeft me moed\nOm door te gaan als ik niet durf\nIk wil van U zijn",
+ "v3"
+ ],
+ [
+ "U geeft me kracht, en bent de vaste grond\nwaarop ik stevig sta\nik wil van U zijn, voor altijd van U zijn\nO God.",
+ "v4"
+ ],
+ [
+ "Grote God, U bent uitzonderlijk\nen ondoorgrondelijk\nU biedt Uw liefde aan",
+ "v5"
+ ],
+ [
+ "Wie ben ik, dat U mij ziet staan\nen met mij om wilt gaan?\nIk kan U niet weerstaan",
+ "v6"
+ ],
+ [
+ "Onweerstaanbaar,\nonweerstaanbare God",
+ "v7"
+ ]
+ ]
+}
diff --git a/tests/resources/propresentersongs/Vaste Grond.pro4 b/tests/resources/propresentersongs/Vaste Grond.pro4
new file mode 100644
index 000000000..7abfb593d
--- /dev/null
+++ b/tests/resources/propresentersongs/Vaste Grond.pro4
@@ -0,0 +1 @@
+<_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary"><_-RVRect3D-_position x="32.37209" y="29" z="0" width="1074.349" height="818.7442"><_-D-_serializedShadow containerClass="NSMutableDictionary">
\ No newline at end of file
diff --git a/tests/resources/themes/Default/Default.xml b/tests/resources/themes/Default/Default.xml
new file mode 100644
index 000000000..d77731005
--- /dev/null
+++ b/tests/resources/themes/Default/Default.xml
@@ -0,0 +1,34 @@
+
+
+ Default
+
+ #000000
+
+
+ Arial
+ #FFFFFF
+ 40
+ False
+ False
+ 0
+
+ True
+ False
+
+
+ Arial
+ #FFFFFF
+ 12
+ False
+ False
+ 0
+
+ True
+ False
+
+
+ 0
+ 0
+ False
+
+