diff --git a/MANIFEST.in b/MANIFEST.in index 35e83e30f..be81efb23 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -5,6 +5,8 @@ recursive-include openlp *.html recursive-include openlp *.js recursive-include openlp *.css recursive-include openlp *.png +recursive-include openlp *.ps +recursive-include openlp *.json recursive-include documentation * recursive-include resources * recursive-include scripts * diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 5b5aa0781..664c1a596 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -320,14 +320,14 @@ class Ui_MainWindow(object): # i18n add Language Actions add_actions(self.settings_language_menu, (self.auto_language_item, None)) add_actions(self.settings_language_menu, self.language_group.actions()) - # Order things differently in OS X so that Preferences menu item in the - # app menu is correct (this gets picked up automatically by Qt). + # Qt on OS X looks for keywords in the menu items title to determine which menu items get added to the main + # menu. If we are running on Mac OS X the menu items whose title contains those keywords but don't belong in the + # main menu need to be marked as such with QAction.NoRole. if sys.platform == 'darwin': - add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), - None, self.settings_configure_item, self.settings_shortcuts_item, self.formatting_tag_item)) - else: - add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), - None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) + self.settings_shortcuts_item.setMenuRole(QtGui.QAction.NoRole) + self.formatting_tag_item.setMenuRole(QtGui.QAction.NoRole) + add_actions(self.settings_menu, (self.settings_plugin_list_item, self.settings_language_menu.menuAction(), + None, self.formatting_tag_item, self.settings_shortcuts_item, self.settings_configure_item)) add_actions(self.tools_menu, (self.tools_add_tool_item, None)) add_actions(self.tools_menu, (self.tools_open_data_folder, None)) add_actions(self.tools_menu, (self.tools_first_time_wizard, None)) diff --git a/openlp/core/ui/media/vlcplayer.py b/openlp/core/ui/media/vlcplayer.py index c80bb6218..d02526b0e 100644 --- a/openlp/core/ui/media/vlcplayer.py +++ b/openlp/core/ui/media/vlcplayer.py @@ -34,6 +34,7 @@ from distutils.version import LooseVersion import logging import os import sys +import threading from PyQt4 import QtGui @@ -207,7 +208,7 @@ class VlcPlayer(MediaPlayer): start_time = 0 if self.state != MediaState.Paused and controller.media_info.start_time > 0: start_time = controller.media_info.start_time - display.vlc_media_player.play() + threading.Thread(target=display.vlc_media_player.play).start() if not self.media_state_wait(display, vlc.State.Playing): return False self.volume(display, controller.media_info.volume) @@ -233,7 +234,7 @@ class VlcPlayer(MediaPlayer): """ Stop the current item """ - display.vlc_media_player.stop() + threading.Thread(target=display.vlc_media_player.stop).start() self.state = MediaState.Stopped def volume(self, display, vol): diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index c28f1e834..36df55dac 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -353,7 +353,7 @@ class ImageMediaItem(MediaManagerItem): icon = build_icon(thumb) else: icon = create_thumb(imageFile.filename, thumb) - item_name = QtGui.QTreeWidgetItem(filename) + item_name = QtGui.QTreeWidgetItem([filename]) item_name.setText(0, filename) item_name.setIcon(0, icon) item_name.setToolTip(0, imageFile.filename) diff --git a/openlp/plugins/media/lib/mediaitem.py b/openlp/plugins/media/lib/mediaitem.py index 92426334a..0780b175d 100644 --- a/openlp/plugins/media/lib/mediaitem.py +++ b/openlp/plugins/media/lib/mediaitem.py @@ -229,7 +229,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties): self.service_path = os.path.join(AppLocation.get_section_data_path(self.settings_section), 'thumbnails') check_directory_exists(self.service_path) self.load_list(Settings().value(self.settings_section + '/media files')) - self.populate_display_types() + self.rebuild_players() def rebuild_players(self): """ diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index faa4122c8..b08193672 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -74,6 +74,7 @@ class EasyWorshipSongImport(SongImport): """ def __init__(self, manager, **kwargs): super(EasyWorshipSongImport, self).__init__(manager, **kwargs) + self.entry_error_log = '' def do_import(self): """ @@ -183,7 +184,12 @@ class EasyWorshipSongImport(SongImport): self.set_song_import_object(authors, inflated_content) if self.stop_import_flag: break - if not self.finish(): + if self.entry_error_log: + self.log_error(self.import_source, + translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') + % (self.title, self.entry_error_log)) + self.entry_error_log = '' + elif not self.finish(): self.log_error(self.import_source) # Set file_pos for next entry file_pos += entry_length @@ -281,7 +287,7 @@ class EasyWorshipSongImport(SongImport): raw_record = db_file.read(record_size) self.fields = self.record_structure.unpack(raw_record) self.set_defaults() - self.title = self.get_field(fi_title).decode() + self.title = self.get_field(fi_title).decode('unicode-escape') # Get remaining fields. copy = self.get_field(fi_copy) admin = self.get_field(fi_admin) @@ -289,23 +295,28 @@ class EasyWorshipSongImport(SongImport): authors = self.get_field(fi_author) words = self.get_field(fi_words) if copy: - self.copyright = copy.decode() + self.copyright = copy.decode('unicode-escape') if admin: if copy: self.copyright += ', ' self.copyright += translate('SongsPlugin.EasyWorshipSongImport', - 'Administered by %s') % admin.decode() + 'Administered by %s') % admin.decode('unicode-escape') if ccli: - self.ccli_number = ccli.decode() + self.ccli_number = ccli.decode('unicode-escape') if authors: - authors = authors.decode() + authors = authors.decode('unicode-escape') else: authors = '' # Set the SongImport object members. self.set_song_import_object(authors, words) if self.stop_import_flag: break - if not self.finish(): + if self.entry_error_log: + self.log_error(self.import_source, + translate('SongsPlugin.EasyWorshipSongImport', '"%s" could not be imported. %s') + % (self.title, self.entry_error_log)) + self.entry_error_log = '' + elif not self.finish(): self.log_error(self.import_source) db_file.close() self.memo_file.close() @@ -328,8 +339,19 @@ class EasyWorshipSongImport(SongImport): self.add_author(author_name.strip()) if words: # Format the lyrics - result = strip_rtf(words.decode(), self.encoding) + result = None + decoded_words = None + try: + decoded_words = words.decode() + except UnicodeDecodeError: + # The unicode chars in the rtf was not escaped in the expected manor + self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport', + 'Unexpected data formatting.') + return + result = strip_rtf(decoded_words, self.encoding) if result is None: + self.entry_error_log = translate('SongsPlugin.EasyWorshipSongImport', + 'No song text found.') return words, self.encoding = result verse_type = VerseType.tags[VerseType.Verse] diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index e51743413..d57c8fbcc 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -36,8 +36,8 @@ from PyQt4 import QtCore, QtGui from sqlalchemy.sql import or_ from openlp.core.common import Registry, AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, check_item_selected, \ - create_separated_list +from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItem, ServiceItemContext, \ + check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action from openlp.plugins.songs.forms.editsongform import EditSongForm from openlp.plugins.songs.forms.songmaintenanceform import SongMaintenanceForm @@ -124,7 +124,8 @@ class SongMediaItem(MediaManagerItem): log.debug('config_updated') self.search_as_you_type = Settings().value(self.settings_section + '/search as type') self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit') - self.add_song_from_service = Settings().value(self.settings_section + '/add song from service',) + self.add_song_from_service = Settings().value(self.settings_section + '/add song from service') + self.display_songbook = Settings().value(self.settings_section + '/display songbook') def retranslateUi(self): self.search_text_label.setText('%s:' % UiStrings().Search) @@ -506,6 +507,8 @@ class SongMediaItem(MediaManagerItem): item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) item.raw_footer.append(song.copyright) + if self.display_songbook and song.book: + item.raw_footer.append("%s #%s" % (song.book.name, song.song_number)) if Settings().value('core/ccli number'): item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') + Settings().value('core/ccli number')) diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 0a8dc4650..aebded029 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -121,7 +121,7 @@ class SongShowPlusImport(SongImport): null, verse_no, = struct.unpack("BB", song_data.read(2)) elif block_key == CUSTOM_VERSE: null, verse_name_length, = struct.unpack("BB", song_data.read(2)) - verse_name = song_data.read(verse_name_length) + verse_name = self.decode(song_data.read(verse_name_length)) length_descriptor_size, = struct.unpack("B", song_data.read(1)) log.debug(length_descriptor_size) # Detect if/how long the length descriptor is @@ -147,7 +147,12 @@ class SongShowPlusImport(SongImport): elif block_key == COPYRIGHT: self.add_copyright(self.decode(data)) elif block_key == CCLI_NO: - self.ccli_number = int(data) + # Try to get the CCLI number even if the field contains additional text + match = re.search(r'\d+', self.decode(data)) + if match: + self.ccli_number = int(match.group()) + else: + log.warn("Can't parse CCLI Number from string: %s" % self.decode(data)) elif block_key == VERSE: self.add_verse(self.decode(data), "%s%s" % (VerseType.tags[VerseType.Verse], verse_no)) elif block_key == CHORUS: diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index bf74a4e7c..1cf06d047 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -59,6 +59,9 @@ class SongsTab(SettingsTab): self.add_from_service_check_box = QtGui.QCheckBox(self.mode_group_box) self.add_from_service_check_box.setObjectName('add_from_service_check_box') self.mode_layout.addWidget(self.add_from_service_check_box) + self.display_songbook_check_box = QtGui.QCheckBox(self.mode_group_box) + self.display_songbook_check_box.setObjectName('songbook_check_box') + self.mode_layout.addWidget(self.display_songbook_check_box) self.left_layout.addWidget(self.mode_group_box) self.left_layout.addStretch() self.right_layout.addStretch() @@ -66,6 +69,7 @@ class SongsTab(SettingsTab): self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed) self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed) self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed) + self.display_songbook_check_box.stateChanged.connect(self.on_songbook_check_box_changed) def retranslateUi(self): self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode')) @@ -75,6 +79,7 @@ class SongsTab(SettingsTab): self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit')) self.add_from_service_check_box.setText(translate('SongsPlugin.SongsTab', 'Import missing songs from service files')) + self.display_songbook_check_box.setText(translate('SongsPlugin.SongsTab', 'Display songbook in footer')) def on_search_as_type_check_box_changed(self, check_state): self.song_search = (check_state == QtCore.Qt.Checked) @@ -88,6 +93,9 @@ class SongsTab(SettingsTab): def on_add_from_service_check_box_changed(self, check_state): self.update_load = (check_state == QtCore.Qt.Checked) + def on_songbook_check_box_changed(self, check_state): + self.display_songbook = (check_state == QtCore.Qt.Checked) + def load(self): settings = Settings() settings.beginGroup(self.settings_section) @@ -95,10 +103,12 @@ class SongsTab(SettingsTab): self.tool_bar = settings.value('display songbar') self.update_edit = settings.value('update service on edit') self.update_load = settings.value('add song from service') + self.display_songbook = settings.value('display songbook') self.search_as_type_check_box.setChecked(self.song_search) self.tool_bar_active_check_box.setChecked(self.tool_bar) self.update_on_edit_check_box.setChecked(self.update_edit) self.add_from_service_check_box.setChecked(self.update_load) + self.display_songbook_check_box.setChecked(self.display_songbook) settings.endGroup() def save(self): @@ -108,6 +118,7 @@ class SongsTab(SettingsTab): settings.setValue('display songbar', self.tool_bar) settings.setValue('update service on edit', self.update_edit) settings.setValue('add song from service', self.update_load) + settings.setValue('display songbook', self.display_songbook) settings.endGroup() if self.tab_visited: self.settings_form.register_post_process('songs_config_updated') diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index b1ddaf412..79fc282a6 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -63,6 +63,7 @@ __default_settings__ = { 'songs/search as type': False, 'songs/add song from service': True, 'songs/display songbar': True, + 'songs/display songbook': False, 'songs/last directory import': '', 'songs/last directory export': '', 'songs/songselect username': '', diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index 37b6d6fdd..072978993 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -171,4 +171,4 @@ class TestImageManager(TestCase, TestMixin): self.lock.release() # The sleep time is adjusted in the test case. time.sleep(self.sleep_time) - return '' \ No newline at end of file + return '' diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core_ui/test_firsttimeform.py index 9fc6f5137..2e26c286a 100644 --- a/tests/functional/openlp_core_ui/test_firsttimeform.py +++ b/tests/functional/openlp_core_ui/test_firsttimeform.py @@ -31,12 +31,12 @@ Package to test the openlp.core.ui.firsttimeform package. """ from unittest import TestCase -from tests.functional import MagicMock - -from tests.helpers.testmixin import TestMixin from openlp.core.common import Registry from openlp.core.ui.firsttimeform import FirstTimeForm +from tests.functional import MagicMock +from tests.helpers.testmixin import TestMixin + class TestFirstTimeForm(TestCase, TestMixin): diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core_ui/test_mainwindow.py index 0b17828b9..b348f8f80 100644 --- a/tests/functional/openlp_core_ui/test_mainwindow.py +++ b/tests/functional/openlp_core_ui/test_mainwindow.py @@ -34,6 +34,7 @@ import os from unittest import TestCase from openlp.core.ui.mainwindow import MainWindow +from openlp.core.lib.ui import UiStrings from openlp.core.common.registry import Registry from tests.utils.constants import TEST_RESOURCES_PATH from tests.helpers.testmixin import TestMixin @@ -95,3 +96,41 @@ class TestMainWindow(TestCase, TestMixin): # THEN the file should not be opened assert not mocked_load_path.called, 'load_path should not have been called' + + def main_window_title_test(self): + """ + Test that running a new instance of OpenLP set the window title correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN no changes are made to the service + + # THEN the main window's title shoud be the same as the OLPV2x string in the UiStrings class + self.assertEqual(self.main_window.windowTitle(), UiStrings().OLPV2x, + 'The main window\'s title should be the same as the OLPV2x string in UiStrings class') + + def set_service_modifed_test(self): + """ + Test that when setting the service's title the main window's title is set correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN set_service_modified is called with with the modified flag set true and a file name + self.main_window.set_service_modified(True, 'test.osz') + + # THEN the main window's title should be set to the + self.assertEqual(self.main_window.windowTitle(), '%s - %s*' % (UiStrings().OLPV2x, 'test.osz'), + 'The main window\'s title should be set to " - test.osz*"') + + def set_service_unmodified_test(self): + """ + Test that when setting the service's title the main window's title is set correctly + """ + # GIVEN a newly opened OpenLP instance + + # WHEN set_service_modified is called with with the modified flag set False and a file name + self.main_window.set_service_modified(False, 'test.osz') + + # THEN the main window's title should be set to the + self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'), + 'The main window\'s title should be set to " - test.osz"') diff --git a/tests/functional/openlp_core_ui/test_media.py b/tests/functional/openlp_core_ui/test_media.py index d59690949..4c6fa7f86 100644 --- a/tests/functional/openlp_core_ui/test_media.py +++ b/tests/functional/openlp_core_ui/test_media.py @@ -125,4 +125,4 @@ class TestMedia(TestCase, TestMixin): # THEN: the used_players should be an empty list, and the overridden player should be an empty string self.assertEqual(['vlc', 'webkit', 'phonon'], used_players, 'Used players should be correct') - self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players') \ No newline at end of file + self.assertEqual('vlc,webkit,phonon', overridden_player, 'Overridden player should be a string of players') diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 56d87c511..104c83750 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -33,7 +33,7 @@ from unittest import TestCase from openlp.core.ui import SlideController -from tests.interfaces import MagicMock, patch +from tests.interfaces import MagicMock class TestSlideController(TestCase): diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 9e13a448b..037169b88 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -32,7 +32,7 @@ This module contains tests for the lib submodule of the Remotes plugin. import os from unittest import TestCase -from openlp.core.common import Settings +from openlp.core.common import Settings, Registry from openlp.plugins.remotes.lib.httpserver import HttpRouter from tests.functional import MagicMock, patch, mock_open from tests.helpers.testmixin import TestMixin @@ -92,15 +92,14 @@ class TestRouter(TestCase, TestMixin): Test the router control functionality """ # GIVEN: A testing set of Routes - router = HttpRouter() mocked_function = MagicMock() test_route = [ (r'^/stage/api/poll$', {'function': mocked_function, 'secure': False}), ] - router.routes = test_route + self.router.routes = test_route # WHEN: called with a poll route - function, args = router.process_http_request('/stage/api/poll', None) + function, args = self.router.process_http_request('/stage/api/poll', None) # THEN: the function should have been called only once self.assertEqual(mocked_function, function['function'], 'The mocked function should match defined value.') @@ -126,6 +125,25 @@ class TestRouter(TestCase, TestMixin): # THEN: all types should match self.assertEqual(content_type, header[1], 'Mismatch of content type') + def main_poll_test(self): + """ + Test the main poll logic + """ + # GIVEN: a defined router with two slides + Registry().register('live_controller', MagicMock) + router = HttpRouter() + router.send_response = MagicMock() + router.send_header = MagicMock() + router.end_headers = MagicMock() + router.live_controller.slide_count = 2 + + # WHEN: main poll called + results = router.main_poll() + + # THEN: the correct response should be returned + self.assertEqual(results.decode('utf-8'), '{"results": {"slide_count": 2}}', + 'The resulting json strings should match') + def serve_file_without_params_test(self): """ Test the serve_file method without params diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 49bc367cd..8d9302015 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -67,7 +67,21 @@ SONG_TEST_DATA = [ 'Just to learn from His lips, words of comfort,\nIn the beautiful garden of prayer.', 'v2'), ('There\'s a garden where Jesus is waiting,\nAnd He bids you to come meet Him there,\n' 'Just to bow and receive a new blessing,\nIn the beautiful garden of prayer.', 'v3')], - 'verse_order_list': []}] + 'verse_order_list': []}, + {'title': 'Vi pløjed og vi så\'de', + 'authors': ['Matthias Claudius'], + 'copyright': 'Public Domain', + 'ccli_number': 0, + 'verses': + [('Vi pløjed og vi så\'de\nvor sæd i sorten jord,\nså bad vi ham os hjælpe,\nsom højt i Himlen bor,\n' + 'og han lod snefald hegne\nmod frosten barsk og hård,\nhan lod det tø og regne\nog varme mildt i vår.', + 'v1'), + ('Alle gode gaver\nde kommer ovenned,\nså tak da Gud, ja, pris dog Gud\nfor al hans kærlighed!', 'c1'), + ('Han er jo den, hvis vilje\nopholder alle ting,\nhan klæder markens lilje\nog runder himlens ring,\n' + 'ham lyder vind og vove,\nham rører ravnes nød,\nhvi skulle ej hans småbørn\nda og få dagligt brød?', 'v2'), + ('Ja, tak, du kære Fader,\nså mild, så rig, så rund,\nfor korn i hæs og lader,\nfor godt i allen stund!\n' + 'Vi kan jo intet give,\nsom nogen ting er værd,\nmen tag vort stakkels hjerte,\nså ringe som det er!', 'v3')], + 'verse_order_list': []}] EWS_SONG_TEST_DATA =\ {'title': 'Vi pløjed og vi så\'de', @@ -139,6 +153,7 @@ 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. @@ -467,3 +482,22 @@ class TestEasyWorshipSongImport(TestCase): for verse_text, verse_tag in EWS_SONG_TEST_DATA['verses']: mocked_add_verse.assert_any_call(verse_text, verse_tag) mocked_finish.assert_called_with() + + def import_rtf_unescaped_unicode_test(self): + """ + Test import of rtf without the expected escaping of unicode + """ + + # GIVEN: A mocked out SongImport class, a mocked out "manager" and mocked out "author" method. + with patch('openlp.plugins.songs.lib.ewimport.SongImport'): + mocked_manager = MagicMock() + mocked_add_author = MagicMock() + importer = EasyWorshipSongImportLogger(mocked_manager) + importer.add_author = mocked_add_author + importer.encoding = 'cp1252' + + # WHEN: running set_song_import_object on a verse string without the needed escaping + importer.set_song_import_object('Test Author', b'Det som var fr\x86n begynnelsen') + + # THEN: The import should fail + self.assertEquals(importer.entry_error_log, 'Unexpected data formatting.', 'Import should fail') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index 359e27a0f..d21342926 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -1,8 +1,6 @@ """ This module contains tests for the lib submodule of the Songs plugin. """ -import os -from tempfile import mkstemp from unittest import TestCase from PyQt4 import QtCore, QtGui @@ -29,6 +27,7 @@ class TestMediaItem(TestCase, TestMixin): with patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup'), \ patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): self.media_item = SongMediaItem(None, MagicMock()) + self.media_item.display_songbook = False self.get_application() self.build_settings() QtCore.QLocale.setDefault(QtCore.QLocale('en_GB')) @@ -129,6 +128,32 @@ class TestMediaItem(TestCase, TestMixin): self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], 'The array should be returned correctly with a song, an author, copyright and amended ccli') + def build_song_footer_base_songbook_test(self): + """ + Test build songs footer with basic song and a songbook + """ + # GIVEN: A Song and a Service Item + mock_song = MagicMock() + mock_song.title = 'My Song' + mock_song.copyright = 'My copyright' + mock_song.book = MagicMock() + mock_song.book.name = "My songbook" + mock_song.song_number = 12 + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The songbook should not be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright']) + + # WHEN: I activate the "display songbook" option + self.media_item.display_songbook = True + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The songbook should be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) + def match_authors_test(self): """ Test the author matching when importing a song from a service diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 7292bb2b0..08400fdc5 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -57,6 +57,8 @@ class TestSongShowPlusFileImport(SongImportTestHelper): self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) self.file_import(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.sbsong'), self.load_external_result_data(os.path.join(TEST_PATH, 'Beautiful Garden Of Prayer.json'))) + self.file_import(os.path.join(TEST_PATH, 'a mighty fortress is our god.sbsong'), + self.load_external_result_data(os.path.join(TEST_PATH, 'a mighty fortress is our god.json'))) class TestSongShowPlusImport(TestCase): diff --git a/tests/interfaces/openlp_core_common/test_historycombobox.py b/tests/interfaces/openlp_core_common/test_historycombobox.py index 00a63f15f..c0131e46c 100644 --- a/tests/interfaces/openlp_core_common/test_historycombobox.py +++ b/tests/interfaces/openlp_core_common/test_historycombobox.py @@ -62,4 +62,4 @@ class TestHistoryComboBox(TestCase, TestMixin): self.combo.addItem('test2') # THEN: The list of items should contain both strings. - self.assertEqual(self.combo.getItems(), ['test1', 'test2']) \ No newline at end of file + self.assertEqual(self.combo.getItems(), ['test1', 'test2']) diff --git a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py index 472bce03f..29c365194 100644 --- a/tests/interfaces/openlp_core_ui/test_shortcutlistform.py +++ b/tests/interfaces/openlp_core_ui/test_shortcutlistform.py @@ -75,4 +75,4 @@ class TestShortcutform(TestCase, TestMixin): # THEN: The button should be changed. self.assertEqual(button.text(), text, "The text should match.") mocked_check_method.assert_called_once_with(True) - self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") \ No newline at end of file + self.assertEqual(button.isEnabled(), enabled, "The button should be disabled.") diff --git a/tests/resources/easyworshipsongs/Songs.DB b/tests/resources/easyworshipsongs/Songs.DB index 8c9679b86..347695057 100644 Binary files a/tests/resources/easyworshipsongs/Songs.DB and b/tests/resources/easyworshipsongs/Songs.DB differ diff --git a/tests/resources/easyworshipsongs/Songs.MB b/tests/resources/easyworshipsongs/Songs.MB index b46323005..949d7755c 100644 Binary files a/tests/resources/easyworshipsongs/Songs.MB and b/tests/resources/easyworshipsongs/Songs.MB differ diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.json b/tests/resources/songshowplussongs/a mighty fortress is our god.json new file mode 100644 index 000000000..2788ad05c --- /dev/null +++ b/tests/resources/songshowplussongs/a mighty fortress is our god.json @@ -0,0 +1,31 @@ +{ + "authors": [ + "Martin Luther" + ], + "ccli_number": 12456, + "comments": "", + "copyright": "Public Domain", + "song_number": 0, + "title": "A Mighty Fortress is our God", + "topics": [], + "verse_order_list": [], + "verses": [ + [ + "A mighty fortress is our God, a bulwark never failing;\r\nOur helper He, amid the flood of mortal ills prevailing:\r\nFor still our ancient foe doth seek to work us woe;\r\nHis craft and power are great, and, armed with cruel hate,\r\nOn earth is not his equal.\r\n", + "v1" + ], + [ + "Did we in our own strength confide, our striving would be losing;\r\nWere not the right Man on our side, the Man of God’s own choosing:\r\nDost ask who that may be? Christ Jesus, it is He;\r\nLord Sabaoth, His Name, from age to age the same,\r\nAnd He must win the battle.\r\n", + "v2" + ], + [ + "And though this world, with devils filled, should threaten to undo us,\r\nWe will not fear, for God hath willed His truth to triumph through us:\r\nThe Prince of Darkness grim, we tremble not for him;\r\nHis rage we can endure, for lo, his doom is sure,\r\nOne little word shall fell him.\r\n", + "v3" + ], + [ + "That word above all earthly powers, no thanks to them, abideth;\r\nThe Spirit and the gifts are ours through Him Who with us sideth:\r\nLet goods and kindred go, this mortal life also;\r\nThe body they may kill: God’s truth abideth still,\r\nHis kingdom is forever.\r\n", + "v4" + ] + ] +} + diff --git a/tests/resources/songshowplussongs/a mighty fortress is our god.sbsong b/tests/resources/songshowplussongs/a mighty fortress is our god.sbsong new file mode 100644 index 000000000..b66d52b2b Binary files /dev/null and b/tests/resources/songshowplussongs/a mighty fortress is our god.sbsong differ