From e959511021a22e5d06edd77b575a269618c8c431 Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 18 Jan 2015 15:42:59 -0800 Subject: [PATCH 01/18] Fix invalid call to QMessageBox --- openlp/core/ui/projector/sourceselectform.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/openlp/core/ui/projector/sourceselectform.py b/openlp/core/ui/projector/sourceselectform.py index ab85ba08b..879bffebf 100644 --- a/openlp/core/ui/projector/sourceselectform.py +++ b/openlp/core/ui/projector/sourceselectform.py @@ -339,8 +339,7 @@ class SourceSelectTabs(QDialog): msg = QtGui.QMessageBox() msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setInformativeText(translate('OpenLP.SourceSelectForm', - 'Are you sure you want to delete ALL user-defined '), - translate('OpenLP.SourceSelectForm', + 'Are you sure you want to delete ALL user-defined ' 'source input text for this projector?')) msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) @@ -478,8 +477,7 @@ class SourceSelectSingle(QDialog): msg = QtGui.QMessageBox() msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setInformativeText(translate('OpenLP.SourceSelectForm', - 'Are you sure you want to delete ALL user-defined '), - translate('OpenLP.SourceSelectForm', + 'Are you sure you want to delete ALL user-defined ' 'source input text for this projector?')) msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) From 44bf096bbbf2be35358df6e100834beeb9106bdc Mon Sep 17 00:00:00 2001 From: Ken Roberts Date: Sun, 18 Jan 2015 20:24:45 -0800 Subject: [PATCH 02/18] Source edit form button test --- .../test_projectorsourceform.py | 82 ++++++++++++++++++- tests/resources/projector/data.py | 3 + 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/tests/interfaces/openlp_core_ui/test_projectorsourceform.py b/tests/interfaces/openlp_core_ui/test_projectorsourceform.py index f8624c0f7..4ddf88768 100644 --- a/tests/interfaces/openlp_core_ui/test_projectorsourceform.py +++ b/tests/interfaces/openlp_core_ui/test_projectorsourceform.py @@ -36,11 +36,18 @@ log = logging.getLogger(__name__) log.debug('test_projectorsourceform loaded') from unittest import TestCase +from PyQt4 import QtGui +from PyQt4.QtGui import QDialog +from tests.functional import patch +from tests.functional.openlp_core_lib.test_projectordb import tmpfile from tests.helpers.testmixin import TestMixin -from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES +from tests.resources.projector.data import TEST_DB, TEST1_DATA -from openlp.core.ui.projector.sourceselectform import source_group +from openlp.core.common import Registry, Settings +from openlp.core.lib.projector.db import ProjectorDB +from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES +from openlp.core.ui.projector.sourceselectform import source_group, SourceSelectSingle def build_source_dict(): @@ -61,6 +68,37 @@ class ProjectorSourceFormTest(TestCase, TestMixin): """ Test class for the Projector Source Select form module """ + @patch('openlp.core.lib.projector.db.init_url') + def setUp(self, mocked_init_url): + """ + Set up anything necessary for all tests + """ + mocked_init_url.start() + mocked_init_url.return_value = 'sqlite:///{}'.format(tmpfile) + self.build_settings() + self.setup_application() + Registry.create() + # Do not try to recreate if we've already been created from a previous test + if not hasattr(self, 'projectordb'): + self.projectordb = ProjectorDB() + # Retrieve/create a database record + self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip) + if not self.projector: + self.projectordb.add_projector(projector=TEST1_DATA) + self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip) + self.projector.dbid = self.projector.id + self.projector.db_item = self.projector + + def tearDown(self): + """ + Close database session. + Delete all C++ objects at end so we don't segfault. + """ + self.projectordb.session.close() + del(self.projectordb) + del(self.projector) + self.destroy_settings() + def source_dict_test(self): """ Test that source list dict returned from sourceselectform module is a valid dict with proper entries @@ -77,3 +115,43 @@ class ProjectorSourceFormTest(TestCase, TestMixin): # THEN: return dictionary should match test dictionary self.assertEquals(check, build_source_dict(), "Source group dictionary should match test dictionary") + + @patch.object(QDialog, 'exec_') + def source_select_edit_button_test(self, mocked_qdialog): + """ + Test source select form edit has Ok, Cancel, Reset, and Revert buttons + """ + # GIVEN: Initial setup and mocks + self.projector.source_available = ['11', ] + self.projector.source = '11' + + # WHEN we create a source select widget and set edit=True + select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb) + select_form.edit = True + select_form.exec_(projector=self.projector) + projector = select_form.projector + + # THEN: Verify all 4 buttons are available + self.assertEquals(len(select_form.button_box.buttons()), 4, + 'SourceSelect dialog box should have "OK", "Cancel" ' + '"Rest", and "Revert" buttons available') + + @patch.object(QDialog, 'exec_') + def source_select_noedit_button_test(self, mocked_qdialog): + """ + Test source select form view has OK and Cancel buttons only + """ + # GIVEN: Initial setup and mocks + self.projector.source_available = ['11', ] + self.projector.source = '11' + + # WHEN we create a source select widget and set edit=False + select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb) + select_form.edit = False + select_form.exec_(projector=self.projector) + projector = select_form.projector + + # THEN: Verify only 2 buttons are available + self.assertEquals(len(select_form.button_box.buttons()), 2, + 'SourceSelect dialog box should only have "OK" ' + 'and "Cancel" buttons available') diff --git a/tests/resources/projector/data.py b/tests/resources/projector/data.py index 2525b8b8c..412369495 100644 --- a/tests/resources/projector/data.py +++ b/tests/resources/projector/data.py @@ -30,9 +30,12 @@ The :mod:`tests.resources.projector.data file contains test data """ +import os from openlp.core.lib.projector.db import Projector # Test data +TEST_DB = os.path.join('tmp', 'openlp-test-projectordb.sql') + TEST1_DATA = Projector(ip='111.111.111.111', port='1111', pin='1111', From 28222ed0f87b83b2893f4d5614102c9c6d1dc1b1 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Sun, 25 Jan 2015 22:00:14 +0000 Subject: [PATCH 03/18] fix check_available to work on feature detection --- openlp/core/ui/media/webkitplayer.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 9673240a6..d33aec7c0 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -22,11 +22,11 @@ """ The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player """ -from PyQt4 import QtGui +from PyQt4 import QtGui, QtWebKit import logging -from openlp.core.common import Settings, is_macosx +from openlp.core.common import Settings from openlp.core.lib import translate from openlp.core.ui.media import MediaState from openlp.core.ui.media.mediaplayer import MediaPlayer @@ -224,11 +224,9 @@ class WebkitPlayer(MediaPlayer): """ Check the availability of the media player """ - # At the moment we don't have support for webkitplayer on Mac OS X - if is_macosx(): - return False - else: - return True + web = QtWebKit.QWebPage() + return web.mainFrame().evaluateJavaScript( + "Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]' def load(self, display): """ From 34f8cbc09b56eb1eb8e8d6c08cbd491814890dbc Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Sun, 25 Jan 2015 22:12:33 +0000 Subject: [PATCH 04/18] Typos & tests --- tests/functional/test_init.py | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py index a5ec608ef..b681ef4b8 100644 --- a/tests/functional/test_init.py +++ b/tests/functional/test_init.py @@ -24,6 +24,7 @@ Package to test the openlp.core.__init__ package. """ from optparse import Values import os +import sys from unittest import TestCase from unittest.mock import MagicMock, patch @@ -122,8 +123,23 @@ class TestInit(TestCase, TestMixin): options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args'] # WHEN: Calling parse_options - resluts = parse_options(options) + results = parse_options(options) # THEN: A tuple should be returned with the parsed options and left over args - self.assertEqual(resluts, (Values({'no_error_form': True, 'dev_version': True, 'portable': True, + self.assertEqual(results, (Values({'no_error_form': True, 'dev_version': True, 'portable': True, + 'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args'])) + + def parse_options_valid_argv_short_options_test(self): + """ + Test that parse_options parses valid short options correctly when passed through sys.argv + """ + # GIVEN: A list of valid options + options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args'] + + # WHEN: Passing in the options through sys.argv and calling parse_args with None + with patch.object(sys, 'argv', options): + results = parse_options(None) + + # THEN: parse_args should return a tuple of valid options and of left over options that OpenLP does not use + self.assertEqual(results, (Values({'no_error_form': True, 'dev_version': True, 'portable': True, 'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args'])) From 3dc248e556a14e36e884db75665262acd72d8066 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Sun, 25 Jan 2015 22:13:35 +0000 Subject: [PATCH 05/18] Typo --- tests/functional/test_init.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py index b681ef4b8..d1f399c62 100644 --- a/tests/functional/test_init.py +++ b/tests/functional/test_init.py @@ -119,7 +119,7 @@ class TestInit(TestCase, TestMixin): """ Test that parse_options parses short options correctly """ - # GIVEN: A list of vaild short options + # GIVEN: A list of valid short options options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args'] # WHEN: Calling parse_options From 1712464e8c828403b32821fca2a1f42793fb78dd Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Mon, 26 Jan 2015 20:42:19 +0000 Subject: [PATCH 06/18] Fix up tests --- openlp/core/ui/media/webkitplayer.py | 8 ++++- .../openlp_core_ui_media/test_webkitplayer.py | 34 +++++++++++-------- tests/functional/test_init.py | 2 +- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index d33aec7c0..86c20e11d 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -222,9 +222,15 @@ class WebkitPlayer(MediaPlayer): def check_available(self): """ - Check the availability of the media player + Check the availability of the media player. + + :return: boolean. True if available """ web = QtWebKit.QWebPage() + # This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it + # should return '[object HTMLUnknownElement]' + mf = web.mainFrame() + return web.mainFrame().evaluateJavaScript( "Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]' diff --git a/tests/functional/openlp_core_ui_media/test_webkitplayer.py b/tests/functional/openlp_core_ui_media/test_webkitplayer.py index 89e79bb0e..9de430f04 100644 --- a/tests/functional/openlp_core_ui_media/test_webkitplayer.py +++ b/tests/functional/openlp_core_ui_media/test_webkitplayer.py @@ -23,7 +23,7 @@ Package to test the openlp.core.ui.media.webkitplayer package. """ from unittest import TestCase -from tests.functional import patch +from tests.functional import MagicMock, patch from openlp.core.ui.media.webkitplayer import WebkitPlayer @@ -33,32 +33,36 @@ class TestWebkitPlayer(TestCase): Test the functions in the :mod:`webkitplayer` module. """ - def check_available_mac_test(self): + def check_available_video_disabled_test(self): """ - Simple test of webkitplayer availability on Mac OS X + Test of webkit video unavailability """ - # GIVEN: A WebkitPlayer and a mocked is_macosx - with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx: - mocked_is_macosx.return_value = True + # GIVEN: A WebkitPlayer instance and a mocked QWebPage + mocked_qwebpage = MagicMock() + mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLUnknownElement]' + with patch('openlp.core.ui.media.webkitplayer.QtWebKit.QWebPage', **{'return_value': mocked_qwebpage}): webkit_player = WebkitPlayer(None) # WHEN: An checking if the player is available available = webkit_player.check_available() - # THEN: The player should not be available on Mac OS X - self.assertEqual(False, available, 'The WebkitPlayer should not be available on Mac OS X.') + # THEN: The player should not be available when '[object HTMLUnknownElement]' is returned + self.assertEqual(False, available, + 'The WebkitPlayer should not be available when video feature detection fails') - def check_available_non_mac_test(self): + def check_available_video_enabled_test(self): """ - Simple test of webkitplayer availability when not on Mac OS X + Test of webkit video availability """ - # GIVEN: A WebkitPlayer and a mocked is_macosx - with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx: - mocked_is_macosx.return_value = False + # GIVEN: A WebkitPlayer instance and a mocked QWebPage + mocked_qwebpage = MagicMock() + mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLUnknownElement]' + with patch('openlp.core.ui.media.webkitplayer.QtWebKit.QWebPage', **{'return_value': mocked_qwebpage}): webkit_player = WebkitPlayer(None) # WHEN: An checking if the player is available available = webkit_player.check_available() - # THEN: The player should be available when not on Mac OS X - self.assertEqual(True, available, 'The WebkitPlayer should be available when not on Mac OS X.') + # THEN: The player should be available when '[object HTMLVideoElement]' is returned + self.assertEqual(True, available, + 'The WebkitPlayer should be available when video feature detection passes') diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py index d1f399c62..62e25aab6 100644 --- a/tests/functional/test_init.py +++ b/tests/functional/test_init.py @@ -134,7 +134,7 @@ class TestInit(TestCase, TestMixin): Test that parse_options parses valid short options correctly when passed through sys.argv """ # GIVEN: A list of valid options - options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args'] + options = ['openlp.py', '-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args'] # WHEN: Passing in the options through sys.argv and calling parse_args with None with patch.object(sys, 'argv', options): From 091cb079e74e0a276c08c69e58d1c4c0465c221f Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Mon, 26 Jan 2015 20:43:53 +0000 Subject: [PATCH 07/18] Fix up tests --- tests/functional/openlp_core_ui_media/test_webkitplayer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/openlp_core_ui_media/test_webkitplayer.py b/tests/functional/openlp_core_ui_media/test_webkitplayer.py index 9de430f04..cea0aa4ad 100644 --- a/tests/functional/openlp_core_ui_media/test_webkitplayer.py +++ b/tests/functional/openlp_core_ui_media/test_webkitplayer.py @@ -56,7 +56,7 @@ class TestWebkitPlayer(TestCase): """ # GIVEN: A WebkitPlayer instance and a mocked QWebPage mocked_qwebpage = MagicMock() - mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLUnknownElement]' + mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLVideoElement]' with patch('openlp.core.ui.media.webkitplayer.QtWebKit.QWebPage', **{'return_value': mocked_qwebpage}): webkit_player = WebkitPlayer(None) From 2815bc46e3183a723e59f3666bdecc18089e9972 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Mon, 26 Jan 2015 21:04:29 +0000 Subject: [PATCH 08/18] typo --- openlp/plugins/bibles/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 1d7216ee5..a8becc041 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -178,7 +178,7 @@ def update_reference_separators(): default_separators = [ '|'.join([ translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'), - translate('BiblesPlugin', 'v','Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), + translate('BiblesPlugin', 'v', 'Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'V', 'Verse identifier e.g. Genesis 1 V 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verses', From bfe1824cb45ba1c6ee09fb5525050cfd82477458 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Mon, 26 Jan 2015 21:18:23 +0000 Subject: [PATCH 09/18] removed redundant code --- openlp/core/ui/media/webkitplayer.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/openlp/core/ui/media/webkitplayer.py b/openlp/core/ui/media/webkitplayer.py index 86c20e11d..338a1eae5 100644 --- a/openlp/core/ui/media/webkitplayer.py +++ b/openlp/core/ui/media/webkitplayer.py @@ -229,8 +229,6 @@ class WebkitPlayer(MediaPlayer): web = QtWebKit.QWebPage() # This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it # should return '[object HTMLUnknownElement]' - mf = web.mainFrame() - return web.mainFrame().evaluateJavaScript( "Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]' From 0cc2ae5998e02df32e6e0859dfc00d7a76c4bf76 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Tue, 27 Jan 2015 19:57:09 +0000 Subject: [PATCH 10/18] Fix exception raised on mac Fixes: https://launchpad.net/bugs/1414360 --- .../presentations/presentationplugin.py | 5 +- .../openlp_core_utils/test_utils.py | 56 ++++++++++++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/openlp/plugins/presentations/presentationplugin.py b/openlp/plugins/presentations/presentationplugin.py index cb92c0826..44470b578 100644 --- a/openlp/plugins/presentations/presentationplugin.py +++ b/openlp/plugins/presentations/presentationplugin.py @@ -143,7 +143,10 @@ class PresentationPlugin(Plugin): super().app_startup() files_from_config = Settings().value('presentations/presentations files') for file in files_from_config: - self.media_item.clean_up_thumbnails(file) + try: + self.media_item.clean_up_thumbnails(file) + except AttributeError: + pass self.media_item.list_view.clear() Settings().setValue('presentations/thumbnail_scheme', 'md5') self.media_item.validate_and_load(files_from_config) diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index ad7cf2831..893fb41ca 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -25,7 +25,7 @@ Functional tests to test the AppLocation class and related methods. import os from unittest import TestCase -from openlp.core.utils import clean_filename, get_filesystem_encoding, get_locale_key, \ +from openlp.core.utils import clean_filename, delete_file, get_filesystem_encoding, get_locale_key, \ get_natural_key, split_filename, _get_user_agent, get_web_page, get_uno_instance, add_actions from tests.functional import MagicMock, patch @@ -184,6 +184,60 @@ class TestUtils(TestCase): # THEN: The file name should be cleaned. self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.') + def delete_file_no_path_test(self): + """" + Test the delete_file function when called with out a valid path + """ + # GIVEN: A blank path + # WEHN: Calling delete_file + result = delete_file('') + + # THEN: delete_file should return False + self.assertFalse(result, "delete_file should return False when called with ''") + + def delete_file_path_success_test(self): + """" + Test the delete_file function when it successfully deletes a file + """ + # GIVEN: A mocked os which returns True when os.path.exists is called + with patch('openlp.core.utils.os', **{'path.exists.return_value': False}) as mocked_os: + + # WHEN: Calling delete_file with a file path + result = delete_file('path/file.ext') + + # THEN: delete_file should return True + self.assertTrue(result, 'delete_file should return True when it successfully deletes a file') + + def delete_file_path_no_file_exists_test(self): + """" + Test the delete_file function when the file to remove does not exist + """ + # GIVEN: A mocked os which returns False when os.path.exists is called + with patch('openlp.core.utils.os', **{'path.exists.return_value': False}) as mocked_os: + + # WHEN: Calling delete_file with a file path + result = delete_file('path/file.ext') + + # THEN: delete_file should return True + self.assertTrue(result, 'delete_file should return True when the file doesnt exist') + + def delete_file_path_exception_test(self): + """" + Test the delete_file function when os.remove raises an exception + """ + # GIVEN: A mocked os which returns True when os.path.exists is called and raises an OSError when os.remove is + # called. + with patch('openlp.core.utils.os', **{'path.exists.return_value': True, + 'path.exists.side_effect': OSError}) as mocked_os, \ + patch('openlp.core.utils.log') as mocked_log: + + # WHEN: Calling delete_file with a file path + result = delete_file('path/file.ext') + + # THEN: delete_file should log and exception and return False + self.assertEqual(mocked_log.exception.call_count, 1) + self.assertFalse(result, 'delete_file should return False when os.remove raises an OSError') + def get_locale_key_test(self): """ Test the get_locale_key(string) function From 3fcccd70e4568976c60ff2077a949e18833ba564 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Wed, 28 Jan 2015 20:41:42 +0000 Subject: [PATCH 11/18] Fixes thumbnails in stage view --- openlp/plugins/images/lib/mediaitem.py | 36 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index a3ec28b37..19d96b4b2 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -314,6 +314,16 @@ class ImageMediaItem(MediaManagerItem): return True return return_value + def generate_thumbnail_path(self, image): + """ + Generate a path to the thumbnail + + :param imageFile: An instance of fImageFileNames + :return: A path to the thumbnail of type str + """ + ext = os.path.splitext(image.filename)[1].lower() + return os.path.join(self.service_path, '{}{}'.format(str(image.id), ext)) + def load_full_list(self, images, initial_load=False, open_group=None): """ Replace the list of images and groups in the interface. @@ -338,8 +348,7 @@ class ImageMediaItem(MediaManagerItem): for imageFile in images: log.debug('Loading image: %s', imageFile.filename) filename = os.path.split(imageFile.filename)[1] - ext = os.path.splitext(imageFile.filename)[1].lower() - thumb = os.path.join(self.service_path, "%s%s" % (str(imageFile.id), ext)) + thumb = self.generate_thumbnail_path(imageFile) if not os.path.exists(imageFile.filename): icon = build_icon(':/general/general_delete.png') else: @@ -549,24 +558,24 @@ class ImageMediaItem(MediaManagerItem): # force a nonexistent theme service_item.theme = -1 missing_items_file_names = [] - images_file_names = [] + images = [] # Expand groups to images for bitem in items: if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None: for index in range(0, bitem.childCount()): if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames): - images_file_names.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename) + images.append(bitem.child(index).data(0, QtCore.Qt.UserRole)) elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames): - images_file_names.append(bitem.data(0, QtCore.Qt.UserRole).filename) + images.append(bitem.data(0, QtCore.Qt.UserRole)) # Don't try to display empty groups - if not images_file_names: + if not images: return False # Find missing files - for filename in images_file_names: - if not os.path.exists(filename): - missing_items_file_names.append(filename) + for image in images: + if not os.path.exists(image.filename): + missing_items_file_names.append(image.filename) # We cannot continue, as all images do not exist. - if not images_file_names: + if not images: if not remote: critical_error_message_box( translate('ImagePlugin.MediaItem', 'Missing Image(s)'), @@ -582,9 +591,10 @@ class ImageMediaItem(MediaManagerItem): QtGui.QMessageBox.No: return False # Continue with the existing images. - for filename in images_file_names: - name = os.path.split(filename)[1] - service_item.add_from_image(filename, name, background, os.path.join(self.service_path, name)) + for image in images: + name = os.path.split(image.filename)[1] + thumbnail = self.generate_thumbnail_path(image) + service_item.add_from_image(image.filename, name, background, thumbnail) return True def check_group_exists(self, new_group): From 52a1033505a5dc9228039442f03ef7f4ac4de91e Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Thu, 29 Jan 2015 18:26:00 +0000 Subject: [PATCH 12/18] test tidies --- tests/functional/openlp_core_utils/test_utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index 893fb41ca..cca5f6825 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -200,7 +200,7 @@ class TestUtils(TestCase): Test the delete_file function when it successfully deletes a file """ # GIVEN: A mocked os which returns True when os.path.exists is called - with patch('openlp.core.utils.os', **{'path.exists.return_value': False}) as mocked_os: + with patch('openlp.core.utils.os', **{'path.exists.return_value': False}): # WHEN: Calling delete_file with a file path result = delete_file('path/file.ext') @@ -213,7 +213,7 @@ class TestUtils(TestCase): Test the delete_file function when the file to remove does not exist """ # GIVEN: A mocked os which returns False when os.path.exists is called - with patch('openlp.core.utils.os', **{'path.exists.return_value': False}) as mocked_os: + with patch('openlp.core.utils.os', **{'path.exists.return_value': False}): # WHEN: Calling delete_file with a file path result = delete_file('path/file.ext') @@ -227,8 +227,7 @@ class TestUtils(TestCase): """ # GIVEN: A mocked os which returns True when os.path.exists is called and raises an OSError when os.remove is # called. - with patch('openlp.core.utils.os', **{'path.exists.return_value': True, - 'path.exists.side_effect': OSError}) as mocked_os, \ + with patch('openlp.core.utils.os', **{'path.exists.return_value': True, 'path.exists.side_effect': OSError}), \ patch('openlp.core.utils.log') as mocked_log: # WHEN: Calling delete_file with a file path From 84dfb24674098b7da7e38ce8992808e382ee37fc Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Thu, 29 Jan 2015 18:28:00 +0000 Subject: [PATCH 13/18] pep fix --- openlp/plugins/bibles/lib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 1d7216ee5..a8becc041 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -178,7 +178,7 @@ def update_reference_separators(): default_separators = [ '|'.join([ translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'), - translate('BiblesPlugin', 'v','Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), + translate('BiblesPlugin', 'v', 'Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'V', 'Verse identifier e.g. Genesis 1 V 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verses', From b33fe701df6bb19abeae545eaef0b080aef45b51 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Thu, 29 Jan 2015 20:57:02 +0000 Subject: [PATCH 14/18] Make Phonon optional Fixes: https://launchpad.net/bugs/1224404 --- openlp/core/ui/maindisplay.py | 10 ++++- openlp/core/ui/slidecontroller.py | 42 ++++++++++--------- .../openlp_core_ui/test_slidecontroller.py | 28 +++++++++++++ 3 files changed, 58 insertions(+), 22 deletions(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index 2a9b3141e..aee76e005 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -33,7 +33,13 @@ import cgi import logging from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL -from PyQt4.phonon import Phonon + +PHONON_AVAILABLE = True +try: + from PyQt4.phonon import Phonon +except ImportError: + PHONON_AVAILABLE = False +PHONON_AVAILABLE = False from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte @@ -139,7 +145,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): self.override = {} self.retranslateUi() self.media_object = None - if self.is_live: + if self.is_live and PHONON_AVAILABLE: self.audio_player = AudioPlayer(self) else: self.audio_player = None diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index e88342373..170f73325 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -580,6 +580,7 @@ class SlideController(DisplayController, RegistryProperties): self.display.setup() if self.is_live: self.__add_actions_to_widget(self.display) + if self.display.audio_player: self.display.audio_player.connectSlot(QtCore.SIGNAL('tick(qint64)'), self.on_audio_time_remaining) # The SlidePreview's ratio. try: @@ -834,26 +835,27 @@ class SlideController(DisplayController, RegistryProperties): self.slide_list = {} if self.is_live: self.song_menu.menu().clear() - self.display.audio_player.reset() - self.set_audio_items_visibility(False) - self.audio_pause_item.setChecked(False) - # If the current item has background audio - if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio): - self.log_debug('Starting to play...') - self.display.audio_player.add_to_playlist(self.service_item.background_audio) - self.track_menu.clear() - for counter in range(len(self.service_item.background_audio)): - action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter])) - action.setData(counter) - action.triggered.connect(self.on_track_triggered) - self.display.audio_player.repeat = \ - Settings().value(self.main_window.general_settings_section + '/audio repeat list') - if Settings().value(self.main_window.general_settings_section + '/audio start paused'): - self.audio_pause_item.setChecked(True) - self.display.audio_player.pause() - else: - self.display.audio_player.play() - self.set_audio_items_visibility(True) + if self.display.audio_player: + self.display.audio_player.reset() + self.set_audio_items_visibility(False) + self.audio_pause_item.setChecked(False) + # If the current item has background audio + if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio): + self.log_debug('Starting to play...') + self.display.audio_player.add_to_playlist(self.service_item.background_audio) + self.track_menu.clear() + for counter in range(len(self.service_item.background_audio)): + action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter])) + action.setData(counter) + action.triggered.connect(self.on_track_triggered) + self.display.audio_player.repeat = \ + Settings().value(self.main_window.general_settings_section + '/audio repeat list') + if Settings().value(self.main_window.general_settings_section + '/audio start paused'): + self.audio_pause_item.setChecked(True) + self.display.audio_player.pause() + else: + self.display.audio_player.play() + self.set_audio_items_visibility(True) row = 0 width = self.main_window.control_splitter.sizes()[self.split] for frame_number, frame in enumerate(self.service_item.get_frames()): diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 3e7697a2c..e2f3dec6a 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -479,6 +479,34 @@ class TestSlideController(TestCase): mocked_preview_widget.current_slide_number.assert_called_with() mocked_process_item.assert_called_once_with(mocked_item, 7) + def on_slide_blank_test(self): + """ + Test on_slide_blank + """ + # GIVEN: An instance of SlideController and a mocked on_blank_display + slide_controller = SlideController(None) + slide_controller.on_blank_display = MagicMock() + + # WHEN: Calling on_slide_blank + slide_controller.on_slide_blank() + + # THEN: on_blank_display should have been called with True + slide_controller.on_blank_display.assert_called_once_with(True) + + def on_slide_unblank_test(self): + """ + Test on_slide_unblank + """ + # GIVEN: An instance of SlideController and a mocked on_blank_display + slide_controller = SlideController(None) + slide_controller.on_blank_display = MagicMock() + + # WHEN: Calling on_slide_unblank + slide_controller.on_slide_unblank() + + # THEN: on_blank_display should have been called with False + slide_controller.on_blank_display.assert_called_once_with(False) + 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 From 0e79c0fe40eec6494de8485dc9d9932b341487ba Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Thu, 29 Jan 2015 20:58:14 +0000 Subject: [PATCH 15/18] Remove testing code --- openlp/core/ui/maindisplay.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index aee76e005..7085626ef 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -39,7 +39,6 @@ try: from PyQt4.phonon import Phonon except ImportError: PHONON_AVAILABLE = False -PHONON_AVAILABLE = False from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte From 59088887fcc30919901ad727179830078f16e8c6 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Thu, 29 Jan 2015 21:21:03 +0000 Subject: [PATCH 16/18] PEP Fixes --- openlp/core/ui/slidecontroller.py | 3 ++- openlp/plugins/bibles/lib/__init__.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 170f73325..5696296e8 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -845,7 +845,8 @@ class SlideController(DisplayController, RegistryProperties): self.display.audio_player.add_to_playlist(self.service_item.background_audio) self.track_menu.clear() for counter in range(len(self.service_item.background_audio)): - action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter])) + action = self.track_menu.addAction( + os.path.basename(self.service_item.background_audio[counter])) action.setData(counter) action.triggered.connect(self.on_track_triggered) self.display.audio_player.repeat = \ diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index 1d7216ee5..a8becc041 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -178,7 +178,7 @@ def update_reference_separators(): default_separators = [ '|'.join([ translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'), - translate('BiblesPlugin', 'v','Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), + translate('BiblesPlugin', 'v', 'Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'V', 'Verse identifier e.g. Genesis 1 V 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'), translate('BiblesPlugin', 'verses', From cdafe89624a50aa886bb3c0d2b5487ed20173866 Mon Sep 17 00:00:00 2001 From: Phill Ridout Date: Fri, 30 Jan 2015 21:03:53 +0000 Subject: [PATCH 17/18] Change fileName to file_name --- openlp/plugins/images/lib/mediaitem.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 19d96b4b2..a52b25d8a 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -318,7 +318,7 @@ class ImageMediaItem(MediaManagerItem): """ Generate a path to the thumbnail - :param imageFile: An instance of fImageFileNames + :param image: An instance of ImageFileNames :return: A path to the thumbnail of type str """ ext = os.path.splitext(image.filename)[1].lower() @@ -345,26 +345,26 @@ class ImageMediaItem(MediaManagerItem): # Sort the images by its filename considering language specific. # characters. images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1])) - for imageFile in images: - log.debug('Loading image: %s', imageFile.filename) - filename = os.path.split(imageFile.filename)[1] - thumb = self.generate_thumbnail_path(imageFile) - if not os.path.exists(imageFile.filename): + for image_file in images: + log.debug('Loading image: %s', image_file.filename) + filename = os.path.split(image_file.filename)[1] + thumb = self.generate_thumbnail_path(image_file) + if not os.path.exists(image_file.filename): icon = build_icon(':/general/general_delete.png') else: - if validate_thumb(imageFile.filename, thumb): + if validate_thumb(image_file.filename, thumb): icon = build_icon(thumb) else: - icon = create_thumb(imageFile.filename, thumb) + icon = create_thumb(image_file.filename, thumb) item_name = QtGui.QTreeWidgetItem([filename]) item_name.setText(0, filename) item_name.setIcon(0, icon) - item_name.setToolTip(0, imageFile.filename) - item_name.setData(0, QtCore.Qt.UserRole, imageFile) - if imageFile.group_id == 0: + item_name.setToolTip(0, image_file.filename) + item_name.setData(0, QtCore.Qt.UserRole, image_file) + if image_file.group_id == 0: self.list_view.addTopLevelItem(item_name) else: - group_items[imageFile.group_id].addChild(item_name) + group_items[image_file.group_id].addChild(item_name) if not initial_load: self.main_window.increment_progress_bar() if not initial_load: From 6f76c164e0bef0950f142a846751179661739681 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Fri, 30 Jan 2015 21:15:03 +0000 Subject: [PATCH 18/18] fix delete --- openlp/core/lib/mediamanageritem.py | 14 +++++++------- .../openlp_core_ui/test_mainwindow.py | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index abdf4af6a..155e484ee 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -213,13 +213,6 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties): icon=':/general/general_edit.png', triggers=self.on_edit_click) create_widget_action(self.list_view, separator=True) - if self.has_delete_icon: - create_widget_action(self.list_view, - 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()), - text=self.plugin.get_string(StringContent.Delete)['title'], - icon=':/general/general_delete.png', - can_shortcuts=True, triggers=self.on_delete_click) - create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()), text=self.plugin.get_string(StringContent.Preview)['title'], @@ -238,6 +231,13 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties): text=self.plugin.get_string(StringContent.Service)['title'], icon=':/general/general_add.png', triggers=self.on_add_click) + if self.has_delete_icon: + create_widget_action(self.list_view, separator=True) + create_widget_action(self.list_view, + 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()), + text=self.plugin.get_string(StringContent.Delete)['title'], + icon=':/general/general_delete.png', + can_shortcuts=True, triggers=self.on_delete_click) if self.add_to_service_item: create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, diff --git a/tests/functional/openlp_core_ui/test_mainwindow.py b/tests/functional/openlp_core_ui/test_mainwindow.py index 02062c06b..b912d488a 100644 --- a/tests/functional/openlp_core_ui/test_mainwindow.py +++ b/tests/functional/openlp_core_ui/test_mainwindow.py @@ -128,3 +128,21 @@ class TestMainWindow(TestCase, TestMixin): # 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 mainwindow_configuration_test(self): + """ + Check that the Main Window initialises the Registry Correctly + """ + # GIVEN: A built main window + + # WHEN: you check the started functions + + # THEN: the following registry functions should have been registered + self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.') + self.assertEqual(len(self.registry.functions_list), 16, 'The registry should have 16 functions') + self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.') + self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.') + self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been ' + 'registered.') + self.assertTrue('plugin_manager' in self.registry.service_list, + 'The plugin_manager should have been registered.')