This commit is contained in:
Phill Ridout 2015-02-01 21:40:59 +00:00
commit 4c36bba911
15 changed files with 308 additions and 87 deletions

View File

@ -213,13 +213,6 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties):
icon=':/general/general_edit.png', icon=':/general/general_edit.png',
triggers=self.on_edit_click) triggers=self.on_edit_click)
create_widget_action(self.list_view, separator=True) 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, create_widget_action(self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()), 'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
text=self.plugin.get_string(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'], text=self.plugin.get_string(StringContent.Service)['title'],
icon=':/general/general_add.png', icon=':/general/general_add.png',
triggers=self.on_add_click) 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: if self.add_to_service_item:
create_widget_action(self.list_view, separator=True) create_widget_action(self.list_view, separator=True)
create_widget_action(self.list_view, create_widget_action(self.list_view,

View File

@ -33,7 +33,12 @@ import cgi
import logging import logging
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL 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
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx 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 openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
@ -139,7 +144,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
self.override = {} self.override = {}
self.retranslateUi() self.retranslateUi()
self.media_object = None self.media_object = None
if self.is_live: if self.is_live and PHONON_AVAILABLE:
self.audio_player = AudioPlayer(self) self.audio_player = AudioPlayer(self)
else: else:
self.audio_player = None self.audio_player = None

View File

@ -22,11 +22,11 @@
""" """
The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player
""" """
from PyQt4 import QtGui from PyQt4 import QtGui, QtWebKit
import logging 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.lib import translate
from openlp.core.ui.media import MediaState from openlp.core.ui.media import MediaState
from openlp.core.ui.media.mediaplayer import MediaPlayer from openlp.core.ui.media.mediaplayer import MediaPlayer
@ -222,13 +222,15 @@ class WebkitPlayer(MediaPlayer):
def check_available(self): def check_available(self):
""" """
Check the availability of the media player Check the availability of the media player.
:return: boolean. True if available
""" """
# At the moment we don't have support for webkitplayer on Mac OS X web = QtWebKit.QWebPage()
if is_macosx(): # This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it
return False # should return '[object HTMLUnknownElement]'
else: return web.mainFrame().evaluateJavaScript(
return True "Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]'
def load(self, display): def load(self, display):
""" """

View File

@ -332,8 +332,7 @@ class SourceSelectTabs(QDialog):
msg = QtGui.QMessageBox() msg = QtGui.QMessageBox()
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
msg.setInformativeText(translate('OpenLP.SourceSelectForm', msg.setInformativeText(translate('OpenLP.SourceSelectForm',
'Are you sure you want to delete ALL user-defined '), 'Are you sure you want to delete ALL user-defined '
translate('OpenLP.SourceSelectForm',
'source input text for this projector?')) 'source input text for this projector?'))
msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setStandardButtons(msg.Cancel | msg.Ok)
msg.setDefaultButton(msg.Cancel) msg.setDefaultButton(msg.Cancel)
@ -471,8 +470,7 @@ class SourceSelectSingle(QDialog):
msg = QtGui.QMessageBox() msg = QtGui.QMessageBox()
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector')) msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
msg.setInformativeText(translate('OpenLP.SourceSelectForm', msg.setInformativeText(translate('OpenLP.SourceSelectForm',
'Are you sure you want to delete ALL user-defined '), 'Are you sure you want to delete ALL user-defined '
translate('OpenLP.SourceSelectForm',
'source input text for this projector?')) 'source input text for this projector?'))
msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setStandardButtons(msg.Cancel | msg.Ok)
msg.setDefaultButton(msg.Cancel) msg.setDefaultButton(msg.Cancel)

View File

@ -580,6 +580,7 @@ class SlideController(DisplayController, RegistryProperties):
self.display.setup() self.display.setup()
if self.is_live: if self.is_live:
self.__add_actions_to_widget(self.display) 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) self.display.audio_player.connectSlot(QtCore.SIGNAL('tick(qint64)'), self.on_audio_time_remaining)
# The SlidePreview's ratio. # The SlidePreview's ratio.
try: try:
@ -834,26 +835,28 @@ class SlideController(DisplayController, RegistryProperties):
self.slide_list = {} self.slide_list = {}
if self.is_live: if self.is_live:
self.song_menu.menu().clear() self.song_menu.menu().clear()
self.display.audio_player.reset() if self.display.audio_player:
self.set_audio_items_visibility(False) self.display.audio_player.reset()
self.audio_pause_item.setChecked(False) self.set_audio_items_visibility(False)
# If the current item has background audio self.audio_pause_item.setChecked(False)
if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio): # If the current item has background audio
self.log_debug('Starting to play...') if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
self.display.audio_player.add_to_playlist(self.service_item.background_audio) self.log_debug('Starting to play...')
self.track_menu.clear() self.display.audio_player.add_to_playlist(self.service_item.background_audio)
for counter in range(len(self.service_item.background_audio)): self.track_menu.clear()
action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter])) for counter in range(len(self.service_item.background_audio)):
action.setData(counter) action = self.track_menu.addAction(
action.triggered.connect(self.on_track_triggered) os.path.basename(self.service_item.background_audio[counter]))
self.display.audio_player.repeat = \ action.setData(counter)
Settings().value(self.main_window.general_settings_section + '/audio repeat list') action.triggered.connect(self.on_track_triggered)
if Settings().value(self.main_window.general_settings_section + '/audio start paused'): self.display.audio_player.repeat = \
self.audio_pause_item.setChecked(True) Settings().value(self.main_window.general_settings_section + '/audio repeat list')
self.display.audio_player.pause() if Settings().value(self.main_window.general_settings_section + '/audio start paused'):
else: self.audio_pause_item.setChecked(True)
self.display.audio_player.play() self.display.audio_player.pause()
self.set_audio_items_visibility(True) else:
self.display.audio_player.play()
self.set_audio_items_visibility(True)
row = 0 row = 0
width = self.main_window.control_splitter.sizes()[self.split] width = self.main_window.control_splitter.sizes()[self.split]
for frame_number, frame in enumerate(self.service_item.get_frames()): for frame_number, frame in enumerate(self.service_item.get_frames()):

View File

@ -178,7 +178,7 @@ def update_reference_separators():
default_separators = [ default_separators = [
'|'.join([ '|'.join([
translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'), 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', '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', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'),
translate('BiblesPlugin', 'verses', translate('BiblesPlugin', 'verses',

View File

@ -314,6 +314,16 @@ class ImageMediaItem(MediaManagerItem):
return True return True
return return_value return return_value
def generate_thumbnail_path(self, image):
"""
Generate a path to the thumbnail
:param image: An instance of ImageFileNames
: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): def load_full_list(self, images, initial_load=False, open_group=None):
""" """
Replace the list of images and groups in the interface. Replace the list of images and groups in the interface.
@ -335,27 +345,26 @@ class ImageMediaItem(MediaManagerItem):
# Sort the images by its filename considering language specific. # Sort the images by its filename considering language specific.
# characters. # characters.
images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1])) images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1]))
for imageFile in images: for image_file in images:
log.debug('Loading image: %s', imageFile.filename) log.debug('Loading image: %s', image_file.filename)
filename = os.path.split(imageFile.filename)[1] filename = os.path.split(image_file.filename)[1]
ext = os.path.splitext(imageFile.filename)[1].lower() thumb = self.generate_thumbnail_path(image_file)
thumb = os.path.join(self.service_path, "%s%s" % (str(imageFile.id), ext)) if not os.path.exists(image_file.filename):
if not os.path.exists(imageFile.filename):
icon = build_icon(':/general/general_delete.png') icon = build_icon(':/general/general_delete.png')
else: else:
if validate_thumb(imageFile.filename, thumb): if validate_thumb(image_file.filename, thumb):
icon = build_icon(thumb) icon = build_icon(thumb)
else: else:
icon = create_thumb(imageFile.filename, thumb) icon = create_thumb(image_file.filename, thumb)
item_name = QtGui.QTreeWidgetItem([filename]) item_name = QtGui.QTreeWidgetItem([filename])
item_name.setText(0, filename) item_name.setText(0, filename)
item_name.setIcon(0, icon) item_name.setIcon(0, icon)
item_name.setToolTip(0, imageFile.filename) item_name.setToolTip(0, image_file.filename)
item_name.setData(0, QtCore.Qt.UserRole, imageFile) item_name.setData(0, QtCore.Qt.UserRole, image_file)
if imageFile.group_id == 0: if image_file.group_id == 0:
self.list_view.addTopLevelItem(item_name) self.list_view.addTopLevelItem(item_name)
else: else:
group_items[imageFile.group_id].addChild(item_name) group_items[image_file.group_id].addChild(item_name)
if not initial_load: if not initial_load:
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
if not initial_load: if not initial_load:
@ -549,24 +558,24 @@ class ImageMediaItem(MediaManagerItem):
# force a nonexistent theme # force a nonexistent theme
service_item.theme = -1 service_item.theme = -1
missing_items_file_names = [] missing_items_file_names = []
images_file_names = [] images = []
# Expand groups to images # Expand groups to images
for bitem in items: for bitem in items:
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None: 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()): for index in range(0, bitem.childCount()):
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames): 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): 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 # Don't try to display empty groups
if not images_file_names: if not images:
return False return False
# Find missing files # Find missing files
for filename in images_file_names: for image in images:
if not os.path.exists(filename): if not os.path.exists(image.filename):
missing_items_file_names.append(filename) missing_items_file_names.append(image.filename)
# We cannot continue, as all images do not exist. # We cannot continue, as all images do not exist.
if not images_file_names: if not images:
if not remote: if not remote:
critical_error_message_box( critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
@ -582,9 +591,10 @@ class ImageMediaItem(MediaManagerItem):
QtGui.QMessageBox.No: QtGui.QMessageBox.No:
return False return False
# Continue with the existing images. # Continue with the existing images.
for filename in images_file_names: for image in images:
name = os.path.split(filename)[1] name = os.path.split(image.filename)[1]
service_item.add_from_image(filename, name, background, os.path.join(self.service_path, name)) thumbnail = self.generate_thumbnail_path(image)
service_item.add_from_image(image.filename, name, background, thumbnail)
return True return True
def check_group_exists(self, new_group): def check_group_exists(self, new_group):

View File

@ -143,7 +143,10 @@ class PresentationPlugin(Plugin):
super().app_startup() super().app_startup()
files_from_config = Settings().value('presentations/presentations files') files_from_config = Settings().value('presentations/presentations files')
for file in files_from_config: 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() self.media_item.list_view.clear()
Settings().setValue('presentations/thumbnail_scheme', 'md5') Settings().setValue('presentations/thumbnail_scheme', 'md5')
self.media_item.validate_and_load(files_from_config) self.media_item.validate_and_load(files_from_config)

View File

@ -128,3 +128,21 @@ class TestMainWindow(TestCase, TestMixin):
# THEN the main window's title should be set to the # THEN the main window's title should be set to the
self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'), self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'),
'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - test.osz"') 'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - 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.')

View File

@ -479,6 +479,34 @@ class TestSlideController(TestCase):
mocked_preview_widget.current_slide_number.assert_called_with() mocked_preview_widget.current_slide_number.assert_called_with()
mocked_process_item.assert_called_once_with(mocked_item, 7) 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): 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 Test that when there is no service item, the on_slide_selected_index() method returns immediately

View File

@ -23,7 +23,7 @@
Package to test the openlp.core.ui.media.webkitplayer package. Package to test the openlp.core.ui.media.webkitplayer package.
""" """
from unittest import TestCase from unittest import TestCase
from tests.functional import patch from tests.functional import MagicMock, patch
from openlp.core.ui.media.webkitplayer import WebkitPlayer from openlp.core.ui.media.webkitplayer import WebkitPlayer
@ -33,32 +33,36 @@ class TestWebkitPlayer(TestCase):
Test the functions in the :mod:`webkitplayer` module. 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 # GIVEN: A WebkitPlayer instance and a mocked QWebPage
with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx: mocked_qwebpage = MagicMock()
mocked_is_macosx.return_value = True 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) webkit_player = WebkitPlayer(None)
# WHEN: An checking if the player is available # WHEN: An checking if the player is available
available = webkit_player.check_available() available = webkit_player.check_available()
# THEN: The player 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 on Mac OS X.') 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 # GIVEN: A WebkitPlayer instance and a mocked QWebPage
with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx: mocked_qwebpage = MagicMock()
mocked_is_macosx.return_value = False 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) webkit_player = WebkitPlayer(None)
# WHEN: An checking if the player is available # WHEN: An checking if the player is available
available = webkit_player.check_available() available = webkit_player.check_available()
# THEN: The player 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 not on Mac OS X.') self.assertEqual(True, available,
'The WebkitPlayer should be available when video feature detection passes')

View File

@ -25,7 +25,7 @@ Functional tests to test the AppLocation class and related methods.
import os import os
from unittest import TestCase 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 get_natural_key, split_filename, _get_user_agent, get_web_page, get_uno_instance, add_actions
from tests.functional import MagicMock, patch from tests.functional import MagicMock, patch
@ -184,6 +184,59 @@ class TestUtils(TestCase):
# THEN: The file name should be cleaned. # THEN: The file name should be cleaned.
self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.') 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}):
# 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}):
# 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}), \
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): def get_locale_key_test(self):
""" """
Test the get_locale_key(string) function Test the get_locale_key(string) function

View File

@ -24,6 +24,7 @@ Package to test the openlp.core.__init__ package.
""" """
from optparse import Values from optparse import Values
import os import os
import sys
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
@ -118,12 +119,27 @@ class TestInit(TestCase, TestMixin):
""" """
Test that parse_options parses short options correctly 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'] options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args']
# WHEN: Calling parse_options # 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 # 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 = ['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):
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'])) 'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args']))

View File

@ -29,11 +29,18 @@ log = logging.getLogger(__name__)
log.debug('test_projectorsourceform loaded') log.debug('test_projectorsourceform loaded')
from unittest import TestCase 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 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(): def build_source_dict():
@ -54,6 +61,37 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
""" """
Test class for the Projector Source Select form module 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): def source_dict_test(self):
""" """
Test that source list dict returned from sourceselectform module is a valid dict with proper entries Test that source list dict returned from sourceselectform module is a valid dict with proper entries
@ -70,3 +108,43 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
# THEN: return dictionary should match test dictionary # THEN: return dictionary should match test dictionary
self.assertEquals(check, build_source_dict(), self.assertEquals(check, build_source_dict(),
"Source group dictionary should match test dictionary") "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')

View File

@ -23,9 +23,12 @@
The :mod:`tests.resources.projector.data file contains test data The :mod:`tests.resources.projector.data file contains test data
""" """
import os
from openlp.core.lib.projector.db import Projector from openlp.core.lib.projector.db import Projector
# Test data # Test data
TEST_DB = os.path.join('tmp', 'openlp-test-projectordb.sql')
TEST1_DATA = Projector(ip='111.111.111.111', TEST1_DATA = Projector(ip='111.111.111.111',
port='1111', port='1111',
pin='1111', pin='1111',