From 60f11d91734205f3a9481d643ab72ced72313c13 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Fri, 4 Jul 2014 13:18:14 +0200 Subject: [PATCH 01/10] Always display copyright symbol in footer --- openlp/plugins/songs/forms/editsongdialog.py | 8 ++++---- openlp/plugins/songs/forms/editsongform.py | 13 ------------- openlp/plugins/songs/lib/mediaitem.py | 3 ++- openlp/plugins/songs/lib/upgrade.py | 19 ++++++++++++++++--- .../openlp_plugins/songs/test_mediaitem.py | 12 ++++++------ 5 files changed, 28 insertions(+), 27 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index a9ca71946..6abdf5f4f 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -218,12 +218,12 @@ class Ui_EditSongDialog(object): self.rights_layout.setObjectName('rights_layout') self.copyright_layout = QtGui.QHBoxLayout() self.copyright_layout.setObjectName('copyright_layout') + self.copyright_label = QtGui.QLabel(self.rights_group_box) + self.copyright_label.setObjectName('copyright_label') + self.copyright_layout.addWidget(self.copyright_label) self.copyright_edit = QtGui.QLineEdit(self.rights_group_box) self.copyright_edit.setObjectName('copyright_edit') self.copyright_layout.addWidget(self.copyright_edit) - self.copyright_insert_button = QtGui.QToolButton(self.rights_group_box) - self.copyright_insert_button.setObjectName('copyright_insert_button') - self.copyright_layout.addWidget(self.copyright_insert_button) self.rights_layout.addLayout(self.copyright_layout) self.ccli_layout = QtGui.QHBoxLayout() self.ccli_layout.setObjectName('ccli_layout') @@ -318,7 +318,7 @@ class Ui_EditSongDialog(object): self.theme_group_box.setTitle(UiStrings().Theme) self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme')) self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information')) - self.copyright_insert_button.setText(SongStrings.CopyrightSymbol) + self.copyright_label.setText(translate('SongsPlugin.EditSongForm', 'Copyright:')) self.ccli_label.setText(UiStrings().CCLINumberLabel) self.comments_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Comments')) self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.theme_tab), diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 2125922fe..3af7522d2 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -75,7 +75,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked) self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked) self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked) - self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered) self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked) self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked) self.verse_edit_button.clicked.connect(self.on_verse_edit_button_clicked) @@ -796,18 +795,6 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): label_text = self.not_all_verses_used_warning self.warning_label.setText(label_text) - def on_copyright_insert_button_triggered(self): - """ - Copyright insert button pressed - """ - text = self.copyright_edit.text() - pos = self.copyright_edit.cursorPosition() - sign = SongStrings.CopyrightSymbol - text = text[:pos] + sign + text[pos:] - self.copyright_edit.setText(text) - self.copyright_edit.setFocus() - self.copyright_edit.setCursorPosition(pos + len(sign)) - def on_maintenance_button_clicked(self): """ Maintenance button pressed diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index d57c8fbcc..c4eaa73e8 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -506,7 +506,8 @@ class SongMediaItem(MediaManagerItem): if authors_translation: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - item.raw_footer.append(song.copyright) + if song.copyright: + item.raw_footer.append('%s %s' % (SongStrings.CopyrightSymbol, 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'): diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 580ae767d..df841b39c 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -27,8 +27,7 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`upgrade` module provides a way for the database and schema that is the -backend for the Songs plugin +The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Songs plugin """ import logging @@ -39,7 +38,7 @@ from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op log = logging.getLogger(__name__) -__version__ = 4 +__version__ = 5 def upgrade_1(session, metadata): @@ -119,3 +118,17 @@ def upgrade_4(session, metadata): op.rename_table('authors_songs_tmp', 'authors_songs') except OperationalError: log.info('Upgrade 4 has already been run') + + +def upgrade_5(session, metadata): + """ + Version 5 upgrade + + This upgrade removes all hard-coded copyright symbols from the copyright field in the songs. + The copyright symbol is now being added directly in the footer. + """ + try: + op = get_upgrade_op(session) + op.execute('UPDATE songs SET copyright=TRIM(REPLACE(copyright, "©", ""))') + except OperationalError: + log.info('Upgrade 5 has already been run') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index bc22a4577..da6ae4b71 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -58,7 +58,7 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', 'My copyright'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', '© My copyright'], 'The array should be returned correctly with a song, one author and copyright') self.assertEqual(author_list, ['my author'], 'The author list should be returned correctly with one author') @@ -97,7 +97,7 @@ class TestMediaItem(TestCase, TestMixin): # THEN: I get the following Array returned self.assertEqual(service_item.raw_footer, ['My Song', 'Words: another author', 'Music: my author', - 'Translation: translator', 'My copyright'], + 'Translation: translator', '© My copyright'], 'The array should be returned correctly with a song, two authors and copyright') self.assertEqual(author_list, ['another author', 'my author', 'translator'], 'The author list should be returned correctly with two authors') @@ -117,7 +117,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 1234'], + self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright', 'CCLI License: 1234'], 'The array should be returned correctly with a song, an author, copyright and ccli') # WHEN: I amend the CCLI value @@ -125,7 +125,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I would get an amended footer string - self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 4321'], + 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): @@ -145,14 +145,14 @@ class TestMediaItem(TestCase, TestMixin): 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']) + 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']) + self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright', 'My songbook #12']) def authors_match_test(self): """ From c1df88693dca0e7bc3fc8e0848b6fcf5a9e6cae1 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Jul 2014 14:19:32 +0200 Subject: [PATCH 02/10] Revert changes --- openlp/plugins/songs/forms/editsongdialog.py | 8 ++++---- openlp/plugins/songs/forms/editsongform.py | 13 +++++++++++++ openlp/plugins/songs/lib/mediaitem.py | 3 +-- openlp/plugins/songs/lib/upgrade.py | 19 +++---------------- .../openlp_plugins/songs/test_mediaitem.py | 12 ++++++------ 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongdialog.py b/openlp/plugins/songs/forms/editsongdialog.py index 6abdf5f4f..a9ca71946 100644 --- a/openlp/plugins/songs/forms/editsongdialog.py +++ b/openlp/plugins/songs/forms/editsongdialog.py @@ -218,12 +218,12 @@ class Ui_EditSongDialog(object): self.rights_layout.setObjectName('rights_layout') self.copyright_layout = QtGui.QHBoxLayout() self.copyright_layout.setObjectName('copyright_layout') - self.copyright_label = QtGui.QLabel(self.rights_group_box) - self.copyright_label.setObjectName('copyright_label') - self.copyright_layout.addWidget(self.copyright_label) self.copyright_edit = QtGui.QLineEdit(self.rights_group_box) self.copyright_edit.setObjectName('copyright_edit') self.copyright_layout.addWidget(self.copyright_edit) + self.copyright_insert_button = QtGui.QToolButton(self.rights_group_box) + self.copyright_insert_button.setObjectName('copyright_insert_button') + self.copyright_layout.addWidget(self.copyright_insert_button) self.rights_layout.addLayout(self.copyright_layout) self.ccli_layout = QtGui.QHBoxLayout() self.ccli_layout.setObjectName('ccli_layout') @@ -318,7 +318,7 @@ class Ui_EditSongDialog(object): self.theme_group_box.setTitle(UiStrings().Theme) self.theme_add_button.setText(translate('SongsPlugin.EditSongForm', 'New &Theme')) self.rights_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Copyright Information')) - self.copyright_label.setText(translate('SongsPlugin.EditSongForm', 'Copyright:')) + self.copyright_insert_button.setText(SongStrings.CopyrightSymbol) self.ccli_label.setText(UiStrings().CCLINumberLabel) self.comments_group_box.setTitle(translate('SongsPlugin.EditSongForm', 'Comments')) self.song_tab_widget.setTabText(self.song_tab_widget.indexOf(self.theme_tab), diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 3af7522d2..2125922fe 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -75,6 +75,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked) self.topic_remove_button.clicked.connect(self.on_topic_remove_button_clicked) self.topics_list_view.itemClicked.connect(self.on_topic_list_view_clicked) + self.copyright_insert_button.clicked.connect(self.on_copyright_insert_button_triggered) self.verse_add_button.clicked.connect(self.on_verse_add_button_clicked) self.verse_list_widget.doubleClicked.connect(self.on_verse_edit_button_clicked) self.verse_edit_button.clicked.connect(self.on_verse_edit_button_clicked) @@ -795,6 +796,18 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties): label_text = self.not_all_verses_used_warning self.warning_label.setText(label_text) + def on_copyright_insert_button_triggered(self): + """ + Copyright insert button pressed + """ + text = self.copyright_edit.text() + pos = self.copyright_edit.cursorPosition() + sign = SongStrings.CopyrightSymbol + text = text[:pos] + sign + text[pos:] + self.copyright_edit.setText(text) + self.copyright_edit.setFocus() + self.copyright_edit.setCursorPosition(pos + len(sign)) + def on_maintenance_button_clicked(self): """ Maintenance button pressed diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index c4eaa73e8..d57c8fbcc 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -506,8 +506,7 @@ class SongMediaItem(MediaManagerItem): if authors_translation: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - if song.copyright: - item.raw_footer.append('%s %s' % (SongStrings.CopyrightSymbol, song.copyright)) + 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'): diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index df841b39c..580ae767d 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -27,7 +27,8 @@ # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### """ -The :mod:`upgrade` module provides a way for the database and schema that is the backend for the Songs plugin +The :mod:`upgrade` module provides a way for the database and schema that is the +backend for the Songs plugin """ import logging @@ -38,7 +39,7 @@ from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op log = logging.getLogger(__name__) -__version__ = 5 +__version__ = 4 def upgrade_1(session, metadata): @@ -118,17 +119,3 @@ def upgrade_4(session, metadata): op.rename_table('authors_songs_tmp', 'authors_songs') except OperationalError: log.info('Upgrade 4 has already been run') - - -def upgrade_5(session, metadata): - """ - Version 5 upgrade - - This upgrade removes all hard-coded copyright symbols from the copyright field in the songs. - The copyright symbol is now being added directly in the footer. - """ - try: - op = get_upgrade_op(session) - op.execute('UPDATE songs SET copyright=TRIM(REPLACE(copyright, "©", ""))') - except OperationalError: - log.info('Upgrade 5 has already been run') diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index da6ae4b71..bc22a4577 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -58,7 +58,7 @@ class TestMediaItem(TestCase, TestMixin): author_list = self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', '© My copyright'], + self.assertEqual(service_item.raw_footer, ['My Song', 'Written by: my author', 'My copyright'], 'The array should be returned correctly with a song, one author and copyright') self.assertEqual(author_list, ['my author'], 'The author list should be returned correctly with one author') @@ -97,7 +97,7 @@ class TestMediaItem(TestCase, TestMixin): # THEN: I get the following Array returned self.assertEqual(service_item.raw_footer, ['My Song', 'Words: another author', 'Music: my author', - 'Translation: translator', '© My copyright'], + 'Translation: translator', 'My copyright'], 'The array should be returned correctly with a song, two authors and copyright') self.assertEqual(author_list, ['another author', 'my author', 'translator'], 'The author list should be returned correctly with two authors') @@ -117,7 +117,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I get the following Array returned - self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright', 'CCLI License: 1234'], + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'CCLI License: 1234'], 'The array should be returned correctly with a song, an author, copyright and ccli') # WHEN: I amend the CCLI value @@ -125,7 +125,7 @@ class TestMediaItem(TestCase, TestMixin): self.media_item.generate_footer(service_item, mock_song) # THEN: I would get an amended footer string - self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright', 'CCLI License: 4321'], + 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): @@ -145,14 +145,14 @@ class TestMediaItem(TestCase, TestMixin): 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']) + 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']) + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) def authors_match_test(self): """ From 383f7ef6e2128f74fb3d9a060e80dada09ebeb45 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Jul 2014 14:33:26 +0200 Subject: [PATCH 03/10] Option for displaying copyright symbol --- openlp/plugins/songs/lib/mediaitem.py | 11 +++++++++-- openlp/plugins/songs/lib/songstab.py | 14 ++++++++++++++ openlp/plugins/songs/songsplugin.py | 1 + 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index fde103c5f..0d4fe5908 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -36,7 +36,7 @@ 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, ServiceItem, ServiceItemContext, \ +from openlp.core.lib import MediaManagerItem, ItemCapabilities, PluginStatus, ServiceItemContext, \ check_item_selected, create_separated_list from openlp.core.lib.ui import create_widget_action from openlp.plugins.songs.forms.editsongform import EditSongForm @@ -126,6 +126,7 @@ class SongMediaItem(MediaManagerItem): 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.display_songbook = Settings().value(self.settings_section + '/display songbook') + self.display_copyright_symbol = Settings().value(self.settings_section + '/display copyright symbol') def retranslateUi(self): self.search_text_label.setText('%s:' % UiStrings().Search) @@ -506,7 +507,13 @@ class SongMediaItem(MediaManagerItem): if authors_translation: item.raw_footer.append("%s: %s" % (AuthorType.Types[AuthorType.Translation], create_separated_list(authors_translation))) - item.raw_footer.append(song.copyright) + if song.copyright: + if self.display_copyright_symbol: + print("copyright") + item.raw_footer.append("%s %s" % (SongStrings.CopyrightSymbol, song.copyright)) + else: + print("no copyright") + 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'): diff --git a/openlp/plugins/songs/lib/songstab.py b/openlp/plugins/songs/lib/songstab.py index 1cf06d047..09d409c4a 100644 --- a/openlp/plugins/songs/lib/songstab.py +++ b/openlp/plugins/songs/lib/songstab.py @@ -31,6 +31,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import Settings, translate from openlp.core.lib import SettingsTab +from openlp.plugins.songs.lib.ui import SongStrings class SongsTab(SettingsTab): @@ -62,6 +63,9 @@ class SongsTab(SettingsTab): 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.display_copyright_check_box = QtGui.QCheckBox(self.mode_group_box) + self.display_copyright_check_box.setObjectName('copyright_check_box') + self.mode_layout.addWidget(self.display_copyright_check_box) self.left_layout.addWidget(self.mode_group_box) self.left_layout.addStretch() self.right_layout.addStretch() @@ -70,6 +74,7 @@ class SongsTab(SettingsTab): 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) + self.display_copyright_check_box.stateChanged.connect(self.on_copyright_check_box_changed) def retranslateUi(self): self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode')) @@ -80,6 +85,9 @@ class SongsTab(SettingsTab): 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')) + self.display_copyright_check_box.setText(translate('SongsPlugin.SongsTab', + 'Display "%s" symbol before copyright info' % + SongStrings.CopyrightSymbol)) def on_search_as_type_check_box_changed(self, check_state): self.song_search = (check_state == QtCore.Qt.Checked) @@ -96,6 +104,9 @@ class SongsTab(SettingsTab): def on_songbook_check_box_changed(self, check_state): self.display_songbook = (check_state == QtCore.Qt.Checked) + def on_copyright_check_box_changed(self, check_state): + self.display_copyright_symbol = (check_state == QtCore.Qt.Checked) + def load(self): settings = Settings() settings.beginGroup(self.settings_section) @@ -104,11 +115,13 @@ class SongsTab(SettingsTab): 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.display_copyright_symbol = settings.value('display copyright symbol') 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) + self.display_copyright_check_box.setChecked(self.display_copyright_symbol) settings.endGroup() def save(self): @@ -119,6 +132,7 @@ class SongsTab(SettingsTab): 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.setValue('display copyright symbol', self.display_copyright_symbol) 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 5cbedc994..56834a6eb 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -64,6 +64,7 @@ __default_settings__ = { 'songs/add song from service': True, 'songs/display songbar': True, 'songs/display songbook': False, + 'songs/display copyright symbol': False, 'songs/last directory import': '', 'songs/last directory export': '', 'songs/songselect username': '', From 79009a1a7fce07d8fef5600ac9374696fb1373c7 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Jul 2014 14:41:55 +0200 Subject: [PATCH 04/10] Add test --- .../openlp_plugins/songs/test_mediaitem.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index bc22a4577..f3ae511ea 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -28,6 +28,7 @@ class TestMediaItem(TestCase, TestMixin): patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'): self.media_item = SongMediaItem(None, MagicMock()) self.media_item.display_songbook = False + self.media_item.display_copyright_symbol = False self.get_application() self.build_settings() QtCore.QLocale.setDefault(QtCore.QLocale('en_GB')) @@ -154,6 +155,23 @@ class TestMediaItem(TestCase, TestMixin): # THEN: The songbook should be in the footer self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) + def build_song_footer_copyright_test(self): + """ + Test building song footer with displaying the copyright symbol + """ + # GIVEN: A Song and a Service Item; displaying the copyright symbol is enabled + self.media_item.display_copyright_symbol = True + mock_song = MagicMock() + mock_song.title = 'My Song' + mock_song.copyright = 'My copyright' + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The copyright symbol should be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright']) + def authors_match_test(self): """ Test the author matching when importing a song from a service From 6d5e6f0fafb0e290123ecf4429ff572422c60e15 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 9 Jul 2014 14:45:54 +0200 Subject: [PATCH 05/10] Remove debug output --- openlp/plugins/songs/lib/mediaitem.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openlp/plugins/songs/lib/mediaitem.py b/openlp/plugins/songs/lib/mediaitem.py index 0d4fe5908..33c4f2e53 100644 --- a/openlp/plugins/songs/lib/mediaitem.py +++ b/openlp/plugins/songs/lib/mediaitem.py @@ -509,10 +509,8 @@ class SongMediaItem(MediaManagerItem): create_separated_list(authors_translation))) if song.copyright: if self.display_copyright_symbol: - print("copyright") item.raw_footer.append("%s %s" % (SongStrings.CopyrightSymbol, song.copyright)) else: - print("no copyright") 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)) From 79c5d61d9483f7cf0b63be3aeadc3afca1a72862 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sat, 12 Jul 2014 22:00:58 +0200 Subject: [PATCH 06/10] Negative test --- .../openlp_plugins/songs/test_mediaitem.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/functional/openlp_plugins/songs/test_mediaitem.py b/tests/functional/openlp_plugins/songs/test_mediaitem.py index f3ae511ea..a473f8569 100644 --- a/tests/functional/openlp_plugins/songs/test_mediaitem.py +++ b/tests/functional/openlp_plugins/songs/test_mediaitem.py @@ -155,7 +155,7 @@ class TestMediaItem(TestCase, TestMixin): # THEN: The songbook should be in the footer self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright', 'My songbook #12']) - def build_song_footer_copyright_test(self): + def build_song_footer_copyright_enabled_test(self): """ Test building song footer with displaying the copyright symbol """ @@ -172,6 +172,22 @@ class TestMediaItem(TestCase, TestMixin): # THEN: The copyright symbol should be in the footer self.assertEqual(service_item.raw_footer, ['My Song', '© My copyright']) + def build_song_footer_copyright_disabled_test(self): + """ + Test building song footer without displaying the copyright symbol + """ + # GIVEN: A Song and a Service Item; displaying the copyright symbol should be disabled by default + mock_song = MagicMock() + mock_song.title = 'My Song' + mock_song.copyright = 'My copyright' + service_item = ServiceItem(None) + + # WHEN: I generate the Footer with default settings + self.media_item.generate_footer(service_item, mock_song) + + # THEN: The copyright symbol should not be in the footer + self.assertEqual(service_item.raw_footer, ['My Song', 'My copyright']) + def authors_match_test(self): """ Test the author matching when importing a song from a service From f2d3bbee475d43e4f8c72499b7c887743cdb52bf Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 13 Jul 2014 01:47:53 +0200 Subject: [PATCH 07/10] Added the name of the current item to the service controller, and added some more tests --- openlp/core/ui/slidecontroller.py | 24 +- .../openlp_core_ui/test_slidecontroller.py | 504 +++++++++++++++++- 2 files changed, 512 insertions(+), 16 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index bf92e9d76..44c28deb6 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -96,6 +96,8 @@ class DisplayController(QtGui.QWidget): """ This is the generic function to send signal for control widgets, created from within other plugins This function is needed to catch the current controller + + :param args: Arguments to send to the plugins """ sender = self.sender().objectName() if self.sender().objectName() else self.sender().text() controller = self @@ -143,11 +145,19 @@ class SlideController(DisplayController, RegistryProperties): self.panel_layout = QtGui.QVBoxLayout(self.panel) self.panel_layout.setSpacing(0) self.panel_layout.setMargin(0) - # Type label for the top of the slide controller + # Type label at the top of the slide controller self.type_label = QtGui.QLabel(self.panel) self.type_label.setStyleSheet('font-weight: bold; font-size: 12pt;') self.type_label.setAlignment(QtCore.Qt.AlignCenter) + if self.is_live: + self.type_label.setText(UiStrings().Live) + else: + self.type_label.setText(UiStrings().Preview) self.panel_layout.addWidget(self.type_label) + # Info label for the title of the current item, at the top of the slide controller + self.info_label = QtGui.QLabel(self.panel) + self.info_label.setAlignment(QtCore.Qt.AlignCenter) + self.panel_layout.addWidget(self.info_label) # Splitter self.splitter = QtGui.QSplitter(self.panel) self.splitter.setOrientation(QtCore.Qt.Vertical) @@ -402,12 +412,17 @@ class SlideController(DisplayController, RegistryProperties): """ try: from openlp.plugins.songs.lib import VerseType - SONGS_PLUGIN_AVAILABLE = True + is_songs_plugin_available = True except ImportError: - SONGS_PLUGIN_AVAILABLE = False + class VerseType(object): + """ + This empty class is mostly just to satisfy Python, PEP8 and PyCharm + """ + pass + is_songs_plugin_available = False sender_name = self.sender().objectName() verse_type = sender_name[15:] if sender_name[:15] == 'shortcutAction_' else '' - if SONGS_PLUGIN_AVAILABLE: + if is_songs_plugin_available: if verse_type == 'V': self.current_shortcut = VerseType.translated_tags[VerseType.Verse] elif verse_type == 'C': @@ -777,6 +792,7 @@ class SlideController(DisplayController, RegistryProperties): if service_item.is_command(): Registry().execute( '%s_start' % service_item.name.lower(), [self.service_item, self.is_live, self.hide_mode(), slide_no]) + self.info_label.setText(self.service_item.title) self.slide_list = {} if self.is_live: self.song_menu.menu().clear() diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 104c83750..3a1e754f8 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -30,10 +30,13 @@ Package to test the openlp.core.ui.slidecontroller package. """ from unittest import TestCase +from openlp.core import Registry +from openlp.core.lib import ServiceItemAction from openlp.core.ui import SlideController +from openlp.core.ui.slidecontroller import WIDE_MENU, NON_TEXT_MENU -from tests.interfaces import MagicMock +from tests.interfaces import MagicMock, patch class TestSlideController(TestCase): @@ -42,37 +45,514 @@ class TestSlideController(TestCase): """ Test the initial slide controller state . """ - # GIVEN: A new slideController instance. + # GIVEN: A new SlideController instance. slide_controller = SlideController(None) + # WHEN: the default controller is built. # THEN: The controller should not be a live controller. self.assertEqual(slide_controller.is_live, False, 'The base slide controller should not be a live controller') - def toggle_blank_test(self): + def text_service_item_blank_test(self): """ - Test the setting of the display blank icons by display type. + Test that loading a text-based service item into the slide controller sets the correct blank menu """ - # GIVEN: A new slideController instance. + # GIVEN: A new SlideController instance. slide_controller = SlideController(None) service_item = MagicMock() toolbar = MagicMock() - toolbar.set_widget_visible = self.dummy_widget_visible + toolbar.set_widget_visible = MagicMock() slide_controller.toolbar = toolbar slide_controller.service_item = service_item - # WHEN a text based service item is used + # WHEN: a text based service item is used slide_controller.service_item.is_text = MagicMock(return_value=True) slide_controller.set_blank_menu() - # THEN: then call set up the toolbar to blank the display screen. - self.assertEqual(len(self.test_widget), 3, 'There should be three icons to display on the screen') + # THEN: the call to set the visible items on the toolbar should be correct + toolbar.set_widget_visible.assert_called_with(WIDE_MENU, True) + + def non_text_service_item_blank_test(self): + """ + Test that loading a non-text service item into the slide controller sets the correct blank menu + """ + # GIVEN: A new SlideController instance. + slide_controller = SlideController(None) + service_item = MagicMock() + toolbar = MagicMock() + toolbar.set_widget_visible = MagicMock() + slide_controller.toolbar = toolbar + slide_controller.service_item = service_item # WHEN a non text based service item is used slide_controller.service_item.is_text = MagicMock(return_value=False) slide_controller.set_blank_menu() # THEN: then call set up the toolbar to blank the display screen. - self.assertEqual(len(self.test_widget), 2, 'There should be only two icons to display on the screen') + toolbar.set_widget_visible.assert_called_with(NON_TEXT_MENU, True) - def dummy_widget_visible(self, widget, visible=True): - self.test_widget = widget + def receive_spin_delay_test(self): + """ + Test that the spin box is updated accordingly after a call to receive_spin_delay() + """ + with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings: + # GIVEN: A new SlideController instance. + mocked_value = MagicMock(return_value=1) + MockedSettings.return_value = MagicMock(value=mocked_value) + mocked_delay_spin_box = MagicMock() + slide_controller = SlideController(None) + slide_controller.delay_spin_box = mocked_delay_spin_box + + # WHEN: The receive_spin_delay() method is called + slide_controller.receive_spin_delay() + + # THEN: The Settings()value() and delay_spin_box.setValue() methods should have been called correctly + mocked_value.assert_called_with('core/loop delay') + mocked_delay_spin_box.setValue.assert_called_with(1) + + def toggle_display_blank_test(self): + """ + Check that the toggle_display('blank') method calls the on_blank_display() method + """ + # GIVEN: A new SlideController instance. + mocked_on_blank_display = MagicMock() + mocked_on_theme_display = MagicMock() + mocked_on_hide_display = MagicMock() + slide_controller = SlideController(None) + slide_controller.on_blank_display = mocked_on_blank_display + slide_controller.on_theme_display = mocked_on_theme_display + slide_controller.on_hide_display = mocked_on_hide_display + + # WHEN: toggle_display() is called with an argument of "blank" + slide_controller.toggle_display('blank') + + # THEN: Only on_blank_display() should have been called with an argument of True + mocked_on_blank_display.assert_called_once_with(True) + self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called') + self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called') + + def toggle_display_hide_test(self): + """ + Check that the toggle_display('hide') method calls the on_blank_display() method + """ + # GIVEN: A new SlideController instance. + mocked_on_blank_display = MagicMock() + mocked_on_theme_display = MagicMock() + mocked_on_hide_display = MagicMock() + slide_controller = SlideController(None) + slide_controller.on_blank_display = mocked_on_blank_display + slide_controller.on_theme_display = mocked_on_theme_display + slide_controller.on_hide_display = mocked_on_hide_display + + # WHEN: toggle_display() is called with an argument of "hide" + slide_controller.toggle_display('hide') + + # THEN: Only on_blank_display() should have been called with an argument of True + mocked_on_blank_display.assert_called_once_with(True) + self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called') + self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called') + + def toggle_display_theme_test(self): + """ + Check that the toggle_display('theme') method calls the on_theme_display() method + """ + # GIVEN: A new SlideController instance. + mocked_on_blank_display = MagicMock() + mocked_on_theme_display = MagicMock() + mocked_on_hide_display = MagicMock() + slide_controller = SlideController(None) + slide_controller.on_blank_display = mocked_on_blank_display + slide_controller.on_theme_display = mocked_on_theme_display + slide_controller.on_hide_display = mocked_on_hide_display + + # WHEN: toggle_display() is called with an argument of "theme" + slide_controller.toggle_display('theme') + + # THEN: Only on_theme_display() should have been called with an argument of True + mocked_on_theme_display.assert_called_once_with(True) + self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called') + self.assertEqual(0, mocked_on_hide_display.call_count, 'on_hide_display should not have been called') + + def toggle_display_desktop_test(self): + """ + Check that the toggle_display('desktop') method calls the on_hide_display() method + """ + # GIVEN: A new SlideController instance. + mocked_on_blank_display = MagicMock() + mocked_on_theme_display = MagicMock() + mocked_on_hide_display = MagicMock() + slide_controller = SlideController(None) + slide_controller.on_blank_display = mocked_on_blank_display + slide_controller.on_theme_display = mocked_on_theme_display + slide_controller.on_hide_display = mocked_on_hide_display + + # WHEN: toggle_display() is called with an argument of "desktop" + slide_controller.toggle_display('desktop') + + # THEN: Only on_hide_display() should have been called with an argument of True + mocked_on_hide_display.assert_called_once_with(True) + self.assertEqual(0, mocked_on_blank_display.call_count, 'on_blank_display should not have been called') + self.assertEqual(0, mocked_on_theme_display.call_count, 'on_theme_display should not have been called') + + def toggle_display_show_test(self): + """ + Check that the toggle_display('show') method calls all the on_X_display() methods + """ + # GIVEN: A new SlideController instance. + mocked_on_blank_display = MagicMock() + mocked_on_theme_display = MagicMock() + mocked_on_hide_display = MagicMock() + slide_controller = SlideController(None) + slide_controller.on_blank_display = mocked_on_blank_display + slide_controller.on_theme_display = mocked_on_theme_display + slide_controller.on_hide_display = mocked_on_hide_display + + # WHEN: toggle_display() is called with an argument of "show" + slide_controller.toggle_display('show') + + # THEN: All the on_X_display() methods should have been called with an argument of False + mocked_on_blank_display.assert_called_once_with(False) + mocked_on_theme_display.assert_called_once_with(False) + mocked_on_hide_display.assert_called_once_with(False) + + def live_escape_test(self): + """ + Test that when the live_escape() method is called, the display is set to invisible and any media is stopped + """ + # GIVEN: A new SlideController instance and mocked out display and media_controller + mocked_display = MagicMock() + mocked_media_controller = MagicMock() + Registry.create() + Registry().register('media_controller', mocked_media_controller) + slide_controller = SlideController(None) + slide_controller.display = mocked_display + + # WHEN: live_escape() is called + slide_controller.live_escape() + + # THEN: the display should be set to invisible and the media controller stopped + mocked_display.setVisible.assert_called_once_with(False) + mocked_media_controller.media_stop.assert_called_once_with(slide_controller) + + def service_previous_test(self): + """ + Check that calling the service_previous() method adds the previous key to the queue and processes the queue + """ + # GIVEN: A new SlideController instance. + mocked_keypress_queue = MagicMock() + mocked_process_queue = MagicMock() + slide_controller = SlideController(None) + slide_controller.keypress_queue = mocked_keypress_queue + slide_controller._process_queue = mocked_process_queue + + # WHEN: The service_previous() method is called + slide_controller.service_previous() + + # THEN: The keypress is added to the queue and the queue is processed + mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Previous) + mocked_process_queue.assert_called_once_with() + + def service_next_test(self): + """ + Check that calling the service_next() method adds the next key to the queue and processes the queue + """ + # GIVEN: A new SlideController instance and mocked out methods + mocked_keypress_queue = MagicMock() + mocked_process_queue = MagicMock() + slide_controller = SlideController(None) + slide_controller.keypress_queue = mocked_keypress_queue + slide_controller._process_queue = mocked_process_queue + + # WHEN: The service_next() method is called + slide_controller.service_next() + + # THEN: The keypress is added to the queue and the queue is processed + mocked_keypress_queue.append.assert_called_once_with(ServiceItemAction.Next) + mocked_process_queue.assert_called_once_with() + + def update_slide_limits_test(self): + """ + Test that calling the update_slide_limits() method updates the slide limits + """ + # GIVEN: A mocked out Settings object, a new SlideController and a mocked out main_window + with patch('openlp.core.ui.slidecontroller.Settings') as MockedSettings: + mocked_value = MagicMock(return_value=10) + MockedSettings.return_value = MagicMock(value=mocked_value) + mocked_main_window = MagicMock(advanced_settings_section='advanced') + Registry.create() + Registry().register('main_window', mocked_main_window) + slide_controller = SlideController(None) + + # WHEN: update_slide_limits() is called + slide_controller.update_slide_limits() + + # THEN: The value of slide_limits should be 10 + mocked_value.assert_called_once_with('advanced/slide limits') + self.assertEqual(10, slide_controller.slide_limits, 'Slide limits should have been updated to 10') + + def enable_tool_bar_live_test(self): + """ + Check that when enable_tool_bar on a live slide controller is called, enable_live_tool_bar is called + """ + # GIVEN: Mocked out enable methods and a real slide controller which is set to live + mocked_enable_live_tool_bar = MagicMock() + mocked_enable_preview_tool_bar = MagicMock() + slide_controller = SlideController(None) + slide_controller.is_live = True + slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar + slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar + mocked_service_item = MagicMock() + + # WHEN: enable_tool_bar() is called + slide_controller.enable_tool_bar(mocked_service_item) + + # THEN: The enable_live_tool_bar() method is called, not enable_preview_tool_bar() + mocked_enable_live_tool_bar.assert_called_once_with(mocked_service_item) + self.assertEqual(0, mocked_enable_preview_tool_bar.call_count, 'The preview method should not have been called') + + def enable_tool_bar_preview_test(self): + """ + Check that when enable_tool_bar on a preview slide controller is called, enable_preview_tool_bar is called + """ + # GIVEN: Mocked out enable methods and a real slide controller which is set to live + mocked_enable_live_tool_bar = MagicMock() + mocked_enable_preview_tool_bar = MagicMock() + slide_controller = SlideController(None) + slide_controller.is_live = False + slide_controller.enable_live_tool_bar = mocked_enable_live_tool_bar + slide_controller.enable_preview_tool_bar = mocked_enable_preview_tool_bar + mocked_service_item = MagicMock() + + # WHEN: enable_tool_bar() is called + slide_controller.enable_tool_bar(mocked_service_item) + + # THEN: The enable_preview_tool_bar() method is called, not enable_live_tool_bar() + mocked_enable_preview_tool_bar.assert_called_once_with(mocked_service_item) + self.assertEqual(0, mocked_enable_live_tool_bar.call_count, 'The live method should not have been called') + + def refresh_service_item_text_test(self): + """ + Test that the refresh_service_item() method refreshes a text service item + """ + # GIVEN: A mock service item and a fresh slide controller + mocked_service_item = MagicMock() + mocked_service_item.is_text.return_value = True + mocked_service_item.is_image.return_value = False + mocked_process_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.service_item = mocked_service_item + slide_controller._process_item = mocked_process_item + slide_controller.selected_row = 5 + + # WHEN: The refresh_service_item method() is called + slide_controller.refresh_service_item() + + # THEN: The item should be re-processed + mocked_service_item.is_text.assert_called_once_with() + self.assertEqual(0, mocked_service_item.is_image.call_count, 'is_image should not have been called') + mocked_service_item.render.assert_called_once_with() + mocked_process_item.assert_called_once_with(mocked_service_item, 5) + + def refresh_service_item_image_test(self): + """ + Test that the refresh_service_item() method refreshes a image service item + """ + # GIVEN: A mock service item and a fresh slide controller + mocked_service_item = MagicMock() + mocked_service_item.is_text.return_value = False + mocked_service_item.is_image.return_value = True + mocked_process_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.service_item = mocked_service_item + slide_controller._process_item = mocked_process_item + slide_controller.selected_row = 5 + + # WHEN: The refresh_service_item method() is called + slide_controller.refresh_service_item() + + # THEN: The item should be re-processed + mocked_service_item.is_text.assert_called_once_with() + mocked_service_item.is_image.assert_called_once_with() + mocked_service_item.render.assert_called_once_with() + mocked_process_item.assert_called_once_with(mocked_service_item, 5) + + def refresh_service_item_not_image_or_text_test(self): + """ + Test that the refresh_service_item() method does not refresh a service item if it's neither text or an image + """ + # GIVEN: A mock service item and a fresh slide controller + mocked_service_item = MagicMock() + mocked_service_item.is_text.return_value = False + mocked_service_item.is_image.return_value = False + mocked_process_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.service_item = mocked_service_item + slide_controller._process_item = mocked_process_item + slide_controller.selected_row = 5 + + # WHEN: The refresh_service_item method() is called + slide_controller.refresh_service_item() + + # THEN: The item should be re-processed + mocked_service_item.is_text.assert_called_once_with() + mocked_service_item.is_image.assert_called_once_with() + self.assertEqual(0, mocked_service_item.render.call_count, 'The render() method should not have been called') + self.assertEqual(0, mocked_process_item.call_count, + 'The mocked_process_item() method should not have been called') + + def add_service_item_with_song_edit_test(self): + """ + Test the add_service_item() method when song_edit is True + """ + # GIVEN: A slide controller and a new item to add + mocked_item = MagicMock() + mocked_process_item =MagicMock() + slide_controller = SlideController(None) + slide_controller._process_item = mocked_process_item + slide_controller.song_edit = True + slide_controller.selected_row = 2 + + # WHEN: The item is added to the service + slide_controller.add_service_item(mocked_item) + + # THEN: The item is processed, the slide number is correct, and the song is not editable (or something) + mocked_item.render.assert_called_once_with() + self.assertFalse(slide_controller.song_edit, 'song_edit should be False') + mocked_process_item.assert_called_once_with(mocked_item, 2) + + def add_service_item_without_song_edit_test(self): + """ + Test the add_service_item() method when song_edit is False + """ + # GIVEN: A slide controller and a new item to add + mocked_item = MagicMock() + mocked_process_item =MagicMock() + slide_controller = SlideController(None) + slide_controller._process_item = mocked_process_item + slide_controller.song_edit = False + slide_controller.selected_row = 2 + + # WHEN: The item is added to the service + slide_controller.add_service_item(mocked_item) + + # THEN: The item is processed, the slide number is correct, and the song is not editable (or something) + mocked_item.render.assert_called_once_with() + self.assertFalse(slide_controller.song_edit, 'song_edit should be False') + mocked_process_item.assert_called_once_with(mocked_item, 0) + + def replace_service_manager_item_different_items_test(self): + """ + Test that when the service items are not the same, nothing happens + """ + # GIVEN: A slide controller and a new item to add + mocked_item = MagicMock() + mocked_preview_widget = MagicMock() + mocked_process_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.preview_widget = mocked_preview_widget + slide_controller._process_item = mocked_process_item + slide_controller.service_item = None + + # WHEN: The service item is replaced + slide_controller.replace_service_manager_item(mocked_item) + + # THEN: The service item should not be processed + self.assertEqual(0, mocked_process_item.call_count, 'The _process_item() method should not have been called') + self.assertEqual(0, mocked_preview_widget.current_slide_number.call_count, + 'The preview_widgetcurrent_slide_number.() method should not have been called') + + def replace_service_manager_item_same_item_test(self): + """ + Test that when the service item is the same, the service item is reprocessed + """ + # GIVEN: A slide controller and a new item to add + mocked_item = MagicMock() + mocked_preview_widget = MagicMock() + mocked_preview_widget.current_slide_number.return_value = 7 + mocked_process_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.preview_widget = mocked_preview_widget + slide_controller._process_item = mocked_process_item + slide_controller.service_item = mocked_item + + # WHEN: The service item is replaced + slide_controller.replace_service_manager_item(mocked_item) + + # THEN: The service item should not be processed + mocked_preview_widget.current_slide_number.assert_called_with() + mocked_process_item.assert_called_once_with(mocked_item, 7) + + def on_slide_selected_index_no_service_item_test(self): + """ + Test that when there is no service item, the on_slide_selected_index() method returns immediately + """ + # GIVEN: A mocked service item and a slide controller without a service item + mocked_item = MagicMock() + slide_controller = SlideController(None) + slide_controller.service_item = None + + # WHEN: The method is called + slide_controller.on_slide_selected_index([10]) + + # THEN: It should have exited early + self.assertEqual(0, mocked_item.is_command.call_count, 'The service item should have not been called') + + def on_slide_selected_index_service_item_command_test(self): + """ + Test that when there is a command service item, the command is executed + """ + # GIVEN: A mocked service item and a slide controller with a service item + 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 + + # 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() + mocked_execute.assert_called_once_with('mocked item_slide', [mocked_item, True, 9]) + mocked_update_preview.assert_called_once_with() + self.assertEqual(0, mocked_preview_widget.change_slide.call_count, 'Change slide should not have been called') + self.assertEqual(0, mocked_slide_selected.call_count, 'slide_selected should not have been called') + + def on_slide_selected_index_service_item_not_command_test(self): + """ + Test that when there is a service item but it's not a command, the preview widget is updated + """ + # GIVEN: A mocked service item and a slide controller with a service item + 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 + + # 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() + self.assertEqual(0, mocked_execute.call_count, 'Execute should not have been called') + self.assertEqual(0, mocked_update_preview.call_count, 'Update preview should not have been called') + mocked_preview_widget.change_slide.assert_called_once_with(7) + mocked_slide_selected.assert_called_once_with() From ba9ff89354eea616ea6e82c4744fc7ee8d25c9c0 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 13 Jul 2014 13:04:37 +0200 Subject: [PATCH 08/10] [fix] PEP8 error: no whitespace --- tests/functional/openlp_core_ui/test_slidecontroller.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 3a1e754f8..ed237d424 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -405,7 +405,7 @@ class TestSlideController(TestCase): """ # GIVEN: A slide controller and a new item to add mocked_item = MagicMock() - mocked_process_item =MagicMock() + mocked_process_item = MagicMock() slide_controller = SlideController(None) slide_controller._process_item = mocked_process_item slide_controller.song_edit = True @@ -425,7 +425,7 @@ class TestSlideController(TestCase): """ # GIVEN: A slide controller and a new item to add mocked_item = MagicMock() - mocked_process_item =MagicMock() + mocked_process_item = MagicMock() slide_controller = SlideController(None) slide_controller._process_item = mocked_process_item slide_controller.song_edit = False From 63350ab6eb9dc91743441b106fa58f2278d03e74 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 13 Jul 2014 15:10:58 +0200 Subject: [PATCH 09/10] Try to ignore the 2.0.x line of tags --- tests/utils/test_bzr_tags.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index acadbd8c4..bc5f4ade0 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -30,7 +30,7 @@ Package to test for proper bzr tags. """ import os - +import re from unittest import TestCase from subprocess import Popen, PIPE @@ -52,6 +52,7 @@ TAGS = [ ['2.0', '2118'], ['2.1.0', '2119'] ] +TAG_SEARCH = re.compile('2\.0\.\d') class TestBzrTags(TestCase): @@ -65,8 +66,9 @@ class TestBzrTags(TestCase): # WHEN getting the branches tags bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE) - stdout = bzr.communicate()[0] - tags = [line.decode('utf-8').split() for line in stdout.splitlines()] + std_out = bzr.communicate()[0] + tags = [line.decode('utf-8').split() for line in std_out.splitlines()] + tags = [t_r for t_r in tags if t_r[1] != '?' or not (t_r[1] == '?' and TAG_SEARCH.search(t_r[0]))] # THEN the tags should match the accepted tags self.assertEqual(TAGS, tags, 'List of tags should match') From 563fb794b5dab9907375cd450e68bb083fc83f33 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sun, 13 Jul 2014 21:21:50 +0200 Subject: [PATCH 10/10] Added an explanatory comment --- tests/utils/test_bzr_tags.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index bc5f4ade0..14da67bc7 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -52,6 +52,9 @@ TAGS = [ ['2.0', '2118'], ['2.1.0', '2119'] ] +# Depending on the repository, we sometimes have the 2.0.x tags in the repo too. They come up with a revision number of +# "?", which I suspect is due to the fact that we're using shared repositories. This regular expression matches all +# 2.0.x tags. TAG_SEARCH = re.compile('2\.0\.\d')