Head 2289

This commit is contained in:
Tim Bentley 2013-08-31 12:43:21 +01:00
commit 7b82de9206
22 changed files with 407 additions and 1802 deletions

View File

@ -28,8 +28,8 @@
############################################################################### ###############################################################################
""" """
The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are protected and included each time loaded. The :mod:`formattingtagform` provides an Tag Edit facility. The Base set are protected and included each time loaded.
Custom tags can be defined and saved. The Custom Tag arrays are saved in a pickle so QSettings works on them. Base Tags Custom tags can be defined and saved. The Custom Tag arrays are saved in a json string so QSettings works on them.
cannot be changed. Base Tags cannot be changed.
""" """
import cgi import cgi

View File

@ -1196,6 +1196,9 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
self.live_controller.splitter.restoreState(settings.value(u'live splitter geometry')) self.live_controller.splitter.restoreState(settings.value(u'live splitter geometry'))
self.preview_controller.splitter.restoreState(settings.value(u'preview splitter geometry')) self.preview_controller.splitter.restoreState(settings.value(u'preview splitter geometry'))
self.control_splitter.restoreState(settings.value(u'main window splitter geometry')) self.control_splitter.restoreState(settings.value(u'main window splitter geometry'))
#This needs to be called after restoreState(), because saveState() also saves the "Collapsible" property
#which was True (by default) < OpenLP 2.1.
self.control_splitter.setChildrenCollapsible(False)
settings.endGroup() settings.endGroup()
def save_settings(self): def save_settings(self):

View File

@ -30,7 +30,6 @@
The service manager sets up, loads, saves and manages services. The service manager sets up, loads, saves and manages services.
""" """
import cgi import cgi
import cPickle
import logging import logging
import os import os
import shutil import shutil
@ -706,7 +705,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
if p_file.endswith(u'osj'): if p_file.endswith(u'osj'):
items = json.load(file_to) items = json.load(file_to)
else: else:
items = cPickle.load(file_to) critical_error_message_box(message=translate('OpenLP.ServiceManager',
'The service file you are trying to open is in an old format.\n '
'Please save it using OpenLP 2.0.2 or greater.'))
return
file_to.close() file_to.close()
self.new_file() self.new_file()
self.set_file_name(file_name) self.set_file_name(file_name)

View File

@ -71,6 +71,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.gradientStartButton.clicked.connect(self.onGradientStartButtonClicked) self.gradientStartButton.clicked.connect(self.onGradientStartButtonClicked)
self.gradientEndButton.clicked.connect(self.onGradientEndButtonClicked) self.gradientEndButton.clicked.connect(self.onGradientEndButtonClicked)
self.imageBrowseButton.clicked.connect(self.onImageBrowseButtonClicked) self.imageBrowseButton.clicked.connect(self.onImageBrowseButtonClicked)
self.imageFileEdit.editingFinished.connect(self.onImageFileEditEditingFinished)
self.mainColorButton.clicked.connect(self.onMainColorButtonClicked) self.mainColorButton.clicked.connect(self.onMainColorButtonClicked)
self.outlineColorButton.clicked.connect(self.onOutlineColorButtonClicked) self.outlineColorButton.clicked.connect(self.onOutlineColorButtonClicked)
self.shadowColorButton.clicked.connect(self.onShadowColorButtonClicked) self.shadowColorButton.clicked.connect(self.onShadowColorButtonClicked)
@ -178,7 +179,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
""" """
background_image = BackgroundType.to_string(BackgroundType.Image) background_image = BackgroundType.to_string(BackgroundType.Image)
if self.page(self.currentId()) == self.backgroundPage and \ if self.page(self.currentId()) == self.backgroundPage and \
self.theme.background_type == background_image and is_not_image_file(self.imageFileEdit.text()): self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'), QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
translate('OpenLP.ThemeWizard', 'You have not selected a ' translate('OpenLP.ThemeWizard', 'You have not selected a '
'background image. Please select one before continuing.')) 'background image. Please select one before continuing.'))
@ -441,6 +442,12 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
self.theme.background_filename = unicode(filename) self.theme.background_filename = unicode(filename)
self.setBackgroundPageValues() self.setBackgroundPageValues()
def onImageFileEditEditingFinished(self):
"""
Background image path edited
"""
self.theme.background_filename = unicode(self.imageFileEdit.text())
def onMainColorButtonClicked(self): def onMainColorButtonClicked(self):
""" """
Set the main colour value Set the main colour value

View File

@ -221,6 +221,7 @@ def update_reference_separators():
u'(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' \ u'(?P<ranges>(?:%(range_regex)s(?:%(sep_l)s(?!\s*$)|(?=\s*$)))+)\s*$' \
% dict(REFERENCE_SEPARATORS.items() + [(u'range_regex', range_regex)]), re.UNICODE) % dict(REFERENCE_SEPARATORS.items() + [(u'range_regex', range_regex)]), re.UNICODE)
def get_reference_separator(separator_type): def get_reference_separator(separator_type):
""" """
Provides separators for parsing and formatting scripture references. Provides separators for parsing and formatting scripture references.
@ -232,6 +233,7 @@ def get_reference_separator(separator_type):
update_reference_separators() update_reference_separators()
return REFERENCE_SEPARATORS[separator_type] return REFERENCE_SEPARATORS[separator_type]
def get_reference_match(match_type): def get_reference_match(match_type):
""" """
Provides matches for parsing scripture references strings. Provides matches for parsing scripture references strings.
@ -243,6 +245,7 @@ def get_reference_match(match_type):
update_reference_separators() update_reference_separators()
return REFERENCE_MATCHES[match_type] return REFERENCE_MATCHES[match_type]
def parse_reference(reference, bible, language_selection, book_ref_id=False): def parse_reference(reference, bible, language_selection, book_ref_id=False):
""" """
This is the next generation über-awesome function that takes a person's typed in string and converts it to a list This is the next generation über-awesome function that takes a person's typed in string and converts it to a list
@ -402,7 +405,7 @@ class SearchResults(object):
""" """
Encapsulate a set of search results. This is Bible-type independent. Encapsulate a set of search results. This is Bible-type independent.
""" """
def __init__(self, book, chapter, verselist): def __init__(self, book, chapter, verse_list):
""" """
Create the search result object. Create the search result object.
@ -412,19 +415,19 @@ class SearchResults(object):
``chapter`` ``chapter``
The chapter of the book. The chapter of the book.
``verselist`` ``verse_list``
The list of verses for this reading. The list of verses for this reading.
""" """
self.book = book self.book = book
self.chapter = chapter self.chapter = chapter
self.verselist = verselist self.verse_list = verse_list
def has_verselist(self): def has_verse_list(self):
""" """
Returns whether or not the verse list contains verses. Returns whether or not the verse list contains verses.
""" """
return len(self.verselist) > 0 return len(self.verse_list) > 0
from versereferencelist import VerseReferenceList from versereferencelist import VerseReferenceList

View File

@ -618,7 +618,7 @@ class HTTPBible(BibleDB):
if BibleDB.get_verse_count(self, book_id, reference[1]) == 0: if BibleDB.get_verse_count(self, book_id, reference[1]) == 0:
self.application.set_busy_cursor() self.application.set_busy_cursor()
search_results = self.get_chapter(book, reference[1]) search_results = self.get_chapter(book, reference[1])
if search_results and search_results.has_verselist(): if search_results and search_results.has_verse_list():
## We have found a book of the bible lets check to see ## We have found a book of the bible lets check to see
## if it was there. By reusing the returned book name ## if it was there. By reusing the returned book name
## we get a correct book. For example it is possible ## we get a correct book. For example it is possible
@ -627,7 +627,7 @@ class HTTPBible(BibleDB):
self.application.process_events() self.application.process_events()
# Check to see if book/chapter exists. # Check to see if book/chapter exists.
db_book = self.get_book(book_name) db_book = self.get_book(book_name)
self.create_chapter(db_book.id, search_results.chapter, search_results.verselist) self.create_chapter(db_book.id, search_results.chapter, search_results.verse_list)
self.application.process_events() self.application.process_events()
self.application.set_normal_cursor() self.application.set_normal_cursor()
self.application.process_events() self.application.process_events()

View File

@ -171,13 +171,15 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if invalid_verses: if invalid_verses:
valid = create_separated_list(verse_names) valid = create_separated_list(verse_names)
if len(invalid_verses) > 1: if len(invalid_verses) > 1:
critical_error_message_box(message=translate('SongsPlugin.EditSongForm', msg = translate('SongsPlugin.EditSongForm', 'There are no verses corresponding to "%(invalid)s".'
'The verse order is invalid. There are no verses corresponding to %s. Valid entries are %s.') % 'Valid entries are %(valid)s.\nPlease enter the verses seperated by spaces.') \
(u', '.join(invalid_verses), valid)) % {'invalid' : u', '.join(invalid_verses), 'valid' : valid}
else: else:
critical_error_message_box(message=translate('SongsPlugin.EditSongForm', msg = translate('SongsPlugin.EditSongForm', 'There is no verse corresponding to "%(invalid)s".'
'The verse order is invalid. There is no verse corresponding to %s. Valid entries are %s.') % 'Valid entries are %(valid)s.\nPlease enter the verses seperated by spaces.') \
(invalid_verses[0], valid)) % {'invalid' : invalid_verses[0], 'valid' : valid}
critical_error_message_box(title=translate('SongsPlugin.EditSongForm', 'Invalid Verse Order'),
message=msg)
return len(invalid_verses) == 0 return len(invalid_verses) == 0
def _validate_song(self): def _validate_song(self):
@ -348,6 +350,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
self.load_topics() self.load_topics()
self.load_books() self.load_books()
self.load_media_files() self.load_media_files()
self.theme_combo_box.setEditText(u'')
self.theme_combo_box.setCurrentIndex(0) self.theme_combo_box.setCurrentIndex(0)
# it's a new song to preview is not possible # it's a new song to preview is not possible
self.preview_button.setVisible(False) self.preview_button.setVisible(False)
@ -376,8 +379,15 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog):
if self.song.song_book_id != 0: if self.song.song_book_id != 0:
book_name = self.manager.get_object(Book, self.song.song_book_id) book_name = self.manager.get_object(Book, self.song.song_book_id)
find_and_set_in_combo_box(self.song_book_combo_box, unicode(book_name.name)) find_and_set_in_combo_box(self.song_book_combo_box, unicode(book_name.name))
else:
self.song_book_combo_box.setEditText(u'')
self.song_book_combo_box.setCurrentIndex(0)
if self.song.theme_name: if self.song.theme_name:
find_and_set_in_combo_box(self.theme_combo_box, unicode(self.song.theme_name)) find_and_set_in_combo_box(self.theme_combo_box, unicode(self.song.theme_name))
else:
# Clear the theme combo box in case it was previously set (bug #1212801)
self.theme_combo_box.setEditText(u'')
self.theme_combo_box.setCurrentIndex(0)
self.copyright_edit.setText(self.song.copyright if self.song.copyright else u'') self.copyright_edit.setText(self.song.copyright if self.song.copyright else u'')
self.comments_edit.setPlainText(self.song.comments if self.song.comments else u'') self.comments_edit.setPlainText(self.song.comments if self.song.comments else u'')
self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else u'') self.ccli_number_edit.setText(self.song.ccli_number if self.song.ccli_number else u'')

View File

@ -96,6 +96,7 @@ import os
from lxml import etree, objectify from lxml import etree, objectify
from openlp.core.lib import translate
from openlp.core.ui.wizard import WizardStrings from openlp.core.ui.wizard import WizardStrings
from openlp.plugins.songs.lib import clean_song, VerseType from openlp.plugins.songs.lib import clean_song, VerseType
from openlp.plugins.songs.lib.songimport import SongImport from openlp.plugins.songs.lib.songimport import SongImport
@ -115,7 +116,7 @@ class FoilPresenterImport(SongImport):
""" """
log.debug(u'initialise FoilPresenterImport') log.debug(u'initialise FoilPresenterImport')
SongImport.__init__(self, manager, **kwargs) SongImport.__init__(self, manager, **kwargs)
self.FoilPresenter = FoilPresenter(self.manager) self.FoilPresenter = FoilPresenter(self.manager, self)
def doImport(self): def doImport(self):
""" """
@ -202,8 +203,9 @@ class FoilPresenter(object):
<copyright> tag. <copyright> tag.
""" """
def __init__(self, manager): def __init__(self, manager, importer):
self.manager = manager self.manager = manager
self.importer = importer
def xml_to_song(self, xml): def xml_to_song(self, xml):
""" """
@ -222,22 +224,23 @@ class FoilPresenter(object):
song.search_lyrics = u'' song.search_lyrics = u''
song.verse_order = u'' song.verse_order = u''
song.search_title = u'' song.search_title = u''
# Because "text" seems to be an reserverd word, we have to recompile it. self.save_song = True
# Because "text" seems to be an reserved word, we have to recompile it.
xml = re.compile(u'<text>').sub(u'<text_>', xml) xml = re.compile(u'<text>').sub(u'<text_>', xml)
xml = re.compile(u'</text>').sub(u'</text_>', xml) xml = re.compile(u'</text>').sub(u'</text_>', xml)
song_xml = objectify.fromstring(xml) song_xml = objectify.fromstring(xml)
foilpresenterfolie = song_xml self._process_copyright(song_xml, song)
self._process_copyright(foilpresenterfolie, song) self._process_cclinumber(song_xml, song)
self._process_cclinumber(foilpresenterfolie, song) self._process_titles(song_xml, song)
self._process_titles(foilpresenterfolie, song)
# The verse order is processed with the lyrics! # The verse order is processed with the lyrics!
self._process_lyrics(foilpresenterfolie, song) self._process_lyrics(song_xml, song)
self._process_comments(foilpresenterfolie, song) self._process_comments(song_xml, song)
self._process_authors(foilpresenterfolie, song) self._process_authors(song_xml, song)
self._process_songbooks(foilpresenterfolie, song) self._process_songbooks(song_xml, song)
self._process_topics(foilpresenterfolie, song) self._process_topics(song_xml, song)
clean_song(self.manager, song) if self.save_song:
self.manager.save_object(song) clean_song(self.manager, song)
self.manager.save_object(song)
def _child(self, element): def _child(self, element):
""" """
@ -420,6 +423,12 @@ class FoilPresenter(object):
VerseType.tags[VerseType.Intro]: 1, VerseType.tags[VerseType.Intro]: 1,
VerseType.tags[VerseType.PreChorus]: 1 VerseType.tags[VerseType.PreChorus]: 1
} }
if not hasattr(foilpresenterfolie.strophen, u'strophe'):
self.importer.logError(self._child(foilpresenterfolie.titel),
unicode(translate('SongsPlugin.FoilPresenterSongImport',
'Invalid Foilpresenter song file. No verses found.')))
self.save_song = False
return
for strophe in foilpresenterfolie.strophen.strophe: for strophe in foilpresenterfolie.strophen.strophe:
text = self._child(strophe.text_) if hasattr(strophe, u'text_') else u'' text = self._child(strophe.text_) if hasattr(strophe, u'text_') else u''
verse_name = self._child(strophe.key) verse_name = self._child(strophe.key)

View File

@ -45,13 +45,14 @@ class OpenSongImport(SongImport):
Import songs exported from OpenSong Import songs exported from OpenSong
The format is described loosly on the `OpenSong File Format Specification The format is described loosly on the `OpenSong File Format Specification
<http://www.opensong.org/d/manual/song_file_format_specification>`_ page on <http://www.opensong.org/d/manual/song_file_format_specification>`_ page on the OpenSong web site. However, it
the OpenSong web site. However, it doesn't describe the <lyrics> section, doesn't describe the <lyrics> section, so here's an attempt:
so here's an attempt:
Verses can be expressed in one of 2 ways, either in complete verses, or by If the first charachter of a line is a space, then the rest of that line is lyrics. If it is not a space the
line grouping, i.e. grouping all line 1's of a verse together, all line 2's following applies.
of a verse together, and so on.
Verses can be expressed in one of 2 ways, either in complete verses, or by line grouping, i.e. grouping all line 1's
of a verse together, all line 2's of a verse together, and so on.
An example of complete verses:: An example of complete verses::
@ -78,9 +79,8 @@ class OpenSongImport(SongImport):
2etc... 2etc...
</lyrics> </lyrics>
Either or both forms can be used in one song. The number does not Either or both forms can be used in one song. The number does not necessarily appear at the start of the line.
necessarily appear at the start of the line. Additionally, the [v1] labels Additionally, the [v1] labels can have either upper or lower case Vs.
can have either upper or lower case Vs.
Other labels can be used also: Other labels can be used also:
@ -92,18 +92,16 @@ class OpenSongImport(SongImport):
All verses are imported and tagged appropriately. All verses are imported and tagged appropriately.
Guitar chords can be provided "above" the lyrics (the line is preceeded by Guitar chords can be provided "above" the lyrics (the line is preceeded by a period "."), and one or more "_" can
a period "."), and one or more "_" can be used to signify long-drawn-out be used to signify long-drawn-out words. Chords and "_" are removed by this importer. For example::
words. Chords and "_" are removed by this importer. For example::
. A7 Bm . A7 Bm
1 Some____ Words 1 Some____ Words
The <presentation> tag is used to populate the OpenLP verse display order The <presentation> tag is used to populate the OpenLP verse display order field. The Author and Copyright tags are
field. The Author and Copyright tags are also imported to the appropriate also imported to the appropriate places.
places.
""" """
def __init__(self, manager, **kwargs): def __init__(self, manager, **kwargs):
""" """
Initialise the class. Initialise the class.
@ -169,13 +167,11 @@ class OpenSongImport(SongImport):
else: else:
lyrics = u'' lyrics = u''
for this_line in lyrics.split(u'\n'): for this_line in lyrics.split(u'\n'):
# remove comments
semicolon = this_line.find(u';')
if semicolon >= 0:
this_line = this_line[:semicolon]
this_line = this_line.strip()
if not this_line: if not this_line:
continue continue
# skip this line if it is a comment
if this_line.startswith(u';'):
continue
# skip guitar chords and page and column breaks # skip guitar chords and page and column breaks
if this_line.startswith(u'.') or this_line.startswith(u'---') or this_line.startswith(u'-!!'): if this_line.startswith(u'.') or this_line.startswith(u'---') or this_line.startswith(u'-!!'):
continue continue
@ -204,7 +200,6 @@ class OpenSongImport(SongImport):
if this_line[0].isdigit(): if this_line[0].isdigit():
verse_num = this_line[0] verse_num = this_line[0]
this_line = this_line[1:].strip() this_line = this_line[1:].strip()
our_verse_order.append([verse_tag, verse_num, inst])
verses.setdefault(verse_tag, {}) verses.setdefault(verse_tag, {})
verses[verse_tag].setdefault(verse_num, {}) verses[verse_tag].setdefault(verse_num, {})
if inst not in verses[verse_tag][verse_num]: if inst not in verses[verse_tag][verse_num]:
@ -214,6 +209,7 @@ class OpenSongImport(SongImport):
this_line = self.tidyText(this_line) this_line = self.tidyText(this_line)
this_line = this_line.replace(u'_', u'') this_line = this_line.replace(u'_', u'')
this_line = this_line.replace(u'|', u'\n') this_line = this_line.replace(u'|', u'\n')
this_line = this_line.strip()
verses[verse_tag][verse_num][inst].append(this_line) verses[verse_tag][verse_num][inst].append(this_line)
# done parsing # done parsing
# add verses in original order # add verses in original order

View File

@ -1,14 +1,16 @@
# -*- coding: utf-8 -*-
""" """
Package to test the openlp.core.lib package. Package to test the openlp.core.lib package.
""" """
import os import os
import json
import tempfile
from unittest import TestCase from unittest import TestCase
from mock import MagicMock, patch from mock import MagicMock, patch
from openlp.core.lib import ItemCapabilities, ServiceItem, Registry from openlp.core.lib import ItemCapabilities, ServiceItem, Registry
from tests.utils.osdinteraction import read_service_from_file from lxml import objectify, etree
from tests.utils.constants import TEST_RESOURCES_PATH
VERSE = u'The Lord said to {r}Noah{/r}: \n'\ VERSE = u'The Lord said to {r}Noah{/r}: \n'\
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\ 'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\
@ -19,6 +21,8 @@ VERSE = u'The Lord said to {r}Noah{/r}: \n'\
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n' 'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456'] FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
class TestServiceItem(TestCase): class TestServiceItem(TestCase):
@ -45,136 +49,17 @@ class TestServiceItem(TestCase):
assert service_item.is_valid is True, u'The new service item should be valid' assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.missing_frames() is True, u'There should not be any frames in the service item' assert service_item.missing_frames() is True, u'There should not be any frames in the service item'
def serviceitem_add_text_test(self):
"""
Test the Service Item - add text test
"""
# GIVEN: A new service item
service_item = ServiceItem(None)
# WHEN: adding text to a service item
service_item.add_from_text(VERSE)
service_item.raw_footer = FOOTER
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.missing_frames() is False, u'check frames loaded '
# WHEN: Render called
assert len(service_item._display_frames) == 0, u'A blank Service Item with no display frames'
service_item.render(True)
# THEN: We should have a page of output.
assert len(service_item._display_frames) == 1, u'A valid rendered Service Item has 1 display frame'
assert service_item.get_rendered_frame(0) == VERSE.split(u'\n')[0], u'A output has rendered correctly.'
def serviceitem_add_image_test(self):
"""
Test the Service Item - add image test
"""
# GIVEN: A new service item and a mocked renderer
service_item = ServiceItem(None)
service_item.name = u'test'
# WHEN: adding image to a service item
test_image = os.path.join(TEST_RESOURCES_PATH, u'church.jpg')
service_item.add_from_image(test_image, u'Image Title')
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert len(service_item._display_frames) == 0, u'The service item has no display frames'
# THEN: We should have a page of output.
assert len(service_item._raw_frames) == 1, u'A valid rendered Service Item has display frames'
assert service_item.get_rendered_frame(0) == test_image
# WHEN: adding a second image to a service item
service_item.add_from_image(test_image, u'Image1 Title')
# THEN: We should have an increased page of output.
assert len(service_item._raw_frames) == 2, u'A valid rendered Service Item has display frames'
assert service_item.get_rendered_frame(0) == test_image
assert service_item.get_rendered_frame(0) == service_item.get_rendered_frame(1)
# WHEN requesting a saved service item
service = service_item.get_service_repr(True)
# THEN: We should have two parts of the service.
assert len(service) == 2, u'A saved service should have two parts'
assert service[u'header'][u'name'] == u'test', u'A test plugin should have been returned'
assert service[u'data'][0][u'title'] == u'Image Title', u'"Image Title" should be returned as the title'
assert service[u'data'][0][u'path'] == test_image, u'The returned path should match the inputted path'
assert service[u'data'][0][u'title'] != service[u'data'][1][u'title'], \
u'The individual slide titles should not match'
assert service[u'data'][0][u'path'] == service[u'data'][1][u'path'], u'The file paths should match'
# WHEN validating a service item
service_item.validate_item([u'jpg'])
# THEN the service item should be valid
assert service_item.is_valid is True, u'The new service item should be valid'
# WHEN: adding a second image to a service item
service_item.add_from_image(u'resources/church1.jpg', u'Image1 Title')
# WHEN validating a service item
service_item.validate_item([u'jpg'])
# THEN the service item should be valid
assert service_item.is_valid is False, u'The service item should not be valid due to validation changes'
def serviceitem_add_command_test(self):
"""
Test the Service Item - add command test
"""
# GIVEN: A new service item and a mocked renderer
service_item = ServiceItem(None)
service_item.name = u'test'
# WHEN: adding image to a service item
test_file = os.path.join(TEST_RESOURCES_PATH, u'church.jpg')
service_item.add_from_command(TEST_RESOURCES_PATH, u'church.jpg', test_file)
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert len(service_item._display_frames) == 0, u'The service item should have no display frames '
# THEN: We should have a page of output.
assert len(service_item._raw_frames) == 1, u'A valid rendered Service Item should have one raw frame'
assert service_item.get_rendered_frame(0) == test_file, u'The image should match the input'
# WHEN requesting a saved service item
service = service_item.get_service_repr(True)
# THEN: We should have two parts of the service.
assert len(service) == 2, u'The saved service should have two parts'
assert service[u'header'][u'name'] == u'test', u'A test plugin should be returned'
assert service[u'data'][0][u'title'] == u'church.jpg', u'The first title name should be "church,jpg"'
assert service[u'data'][0][u'path'] == TEST_RESOURCES_PATH, u'The path should match the input path'
assert service[u'data'][0][u'image'] == test_file, u'The image should match the full path to image'
# WHEN validating a service item
service_item.validate_item([u'jpg'])
# THEN the service item should be valid
assert service_item.is_valid is True, u'The service item should be valid'
# WHEN validating a service item with a different suffix
service_item.validate_item([u'png'])
# THEN the service item should not be valid
assert service_item.is_valid is False, u'The service item should not be valid'
def serviceitem_load_custom_from_service_test(self): def serviceitem_load_custom_from_service_test(self):
""" """
Test the Service Item - adding a custom slide from a saved service Test the Service Item - adding a custom slide from a saved service
""" """
# GIVEN: A new service item # GIVEN: A new service item and a mocked add icon function
service_item = ServiceItem(None) service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
# WHEN: adding a custom from a saved Service # WHEN: adding a custom from a saved Service
service = read_service_from_file(u'serviceitem_custom_1.osd') line = self.convert_file_service_item(u'serviceitem_custom_1.osj')
service_item.set_from_service(service[0]) service_item.set_from_service(line)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid' assert service_item.is_valid is True, u'The new service item should be valid'
@ -193,17 +78,19 @@ class TestServiceItem(TestCase):
""" """
Test the Service Item - adding an image from a saved service Test the Service Item - adding an image from a saved service
""" """
# GIVEN: A new service item # GIVEN: A new service item and a mocked add icon function
image_name = u'image_1.jpg' image_name = u'image_1.jpg'
test_file = os.path.join(TEST_RESOURCES_PATH, image_name) test_file = os.path.join(TEST_PATH, image_name)
frame_array = {u'path': test_file, u'title': image_name} frame_array = {u'path': test_file, u'title': image_name}
service_item = ServiceItem(None) service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
# WHEN: adding an image from a saved Service and mocked exists # WHEN: adding an image from a saved Service and mocked exists
service = read_service_from_file(u'serviceitem_image_1.osd') line = self.convert_file_service_item(u'serviceitem_image_1.osj')
with patch('os.path.exists'): with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
service_item.set_from_service(service[0], TEST_RESOURCES_PATH) mocked_exists.return_value = True
service_item.set_from_service(line, TEST_PATH)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid' assert service_item.is_valid is True, u'The new service item should be valid'
@ -226,7 +113,7 @@ class TestServiceItem(TestCase):
""" """
Test the Service Item - adding an image from a saved local service Test the Service Item - adding an image from a saved local service
""" """
# GIVEN: A new service item # GIVEN: A new service item and a mocked add icon function
image_name1 = u'image_1.jpg' image_name1 = u'image_1.jpg'
image_name2 = u'image_2.jpg' image_name2 = u'image_2.jpg'
test_file1 = os.path.join(u'/home/openlp', image_name1) test_file1 = os.path.join(u'/home/openlp', image_name1)
@ -235,23 +122,36 @@ class TestServiceItem(TestCase):
frame_array2 = {u'path': test_file2, u'title': image_name2} frame_array2 = {u'path': test_file2, u'title': image_name2}
service_item = ServiceItem(None) service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
service_item2 = ServiceItem(None)
service_item2.add_icon = MagicMock()
# WHEN: adding an image from a saved Service and mocked exists # WHEN: adding an image from a saved Service and mocked exists
service = read_service_from_file(u'serviceitem_image_2.osd') line = self.convert_file_service_item(u'serviceitem_image_2.osj')
with patch('os.path.exists'): line2 = self.convert_file_service_item(u'serviceitem_image_2.osj', 1)
service_item.set_from_service(service[0])
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
mocked_exists.return_value = True
service_item2.set_from_service(line2)
service_item.set_from_service(line)
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
# This test is copied from service_item.py, but is changed since to conform to
# new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now.
assert service_item.is_valid is True, u'The first service item should be valid'
assert service_item2.is_valid is True, u'The second service item should be valid'
assert service_item.get_rendered_frame(0) == test_file1, u'The first frame should match the path to the image' assert service_item.get_rendered_frame(0) == test_file1, u'The first frame should match the path to the image'
assert service_item.get_rendered_frame(1) == test_file2, u'The Second frame should match the path to the image' assert service_item2.get_rendered_frame(0) == test_file2, u'The Second frame should match the path to the image'
assert service_item.get_frames()[0] == frame_array1, u'The return should match the frame array1' assert service_item.get_frames()[0] == frame_array1, u'The return should match the frame array1'
assert service_item.get_frames()[1] == frame_array2, u'The return should match the frame array2' assert service_item2.get_frames()[0] == frame_array2, u'The return should match the frame array2'
assert service_item.get_frame_path(0) == test_file1, u'The frame path should match the full path to the image' assert service_item.get_frame_path(0) == test_file1, u'The frame path should match the full path to the image'
assert service_item.get_frame_path(1) == test_file2, u'The frame path should match the full path to the image' assert service_item2.get_frame_path(0) == test_file2, u'The frame path should match the full path to the image'
assert service_item.get_frame_title(0) == image_name1, u'The 1st frame title should match the image name' assert service_item.get_frame_title(0) == image_name1, u'The 1st frame title should match the image name'
assert service_item.get_frame_title(1) == image_name2, u'The 2nd frame title should match the image name' assert service_item2.get_frame_title(0) == image_name2, u'The 2nd frame title should match the image name'
assert service_item.get_display_title().lower() == service_item.name, \ assert service_item.title.lower() == service_item.name, \
u'The plugin name should match the display title, as there are > 1 Images' u'The plugin name should match the display title, as there are > 1 Images'
assert service_item.is_image() is True, u'This service item should be of an "image" type' assert service_item.is_image() is True, u'This service item should be of an "image" type'
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
@ -263,22 +163,15 @@ class TestServiceItem(TestCase):
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
u'This service item should be able to have new items added to it' u'This service item should be able to have new items added to it'
def serviceitem_migrate_test_20_22(self): def convert_file_service_item(self, name, row=0):
""" service_file = os.path.join(TEST_PATH, name)
Test the Service Item - migrating a media only service item from 2.0 to 2.2 format try:
""" open_file = open(service_file, u'r')
# GIVEN: A new service item and a mocked add icon function items = json.load(open_file)
service_item = ServiceItem(None) first_line = items[row]
service_item.add_icon = MagicMock() except IOError:
first_line = u''
finally:
open_file.close()
return first_line
# WHEN: adding an media from a saved Service and mocked exists
line = read_service_from_file(u'migrate_video_20_22.osd')
with patch('os.path.exists'):
service_item.set_from_service(line[0], TEST_RESOURCES_PATH)
# THEN: We should get back a converted service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.processor == u'VLC', u'The Processor should have been set'
assert service_item.title is not None, u'The title should be set to a value'
assert service_item.is_capable(ItemCapabilities.HasDetailedTitleDisplay) is False, \
u'The Capability should have been removed'

View File

@ -1,261 +0,0 @@
# -*- coding: utf-8 -*-
"""
Package to test the openlp.core.lib package.
"""
import os
import io
import cPickle
import json
import tempfile
from unittest import TestCase
from mock import MagicMock, patch
from openlp.core.lib import ItemCapabilities, ServiceItem, Registry
from lxml import objectify, etree
VERSE = u'The Lord said to {r}Noah{/r}: \n'\
'There\'s gonna be a {su}floody{/su}, {sb}floody{/sb}\n'\
'The Lord said to {g}Noah{/g}:\n'\
'There\'s gonna be a {st}floody{/st}, {it}floody{/it}\n'\
'Get those children out of the muddy, muddy \n'\
'{r}C{/r}{b}h{/b}{bl}i{/bl}{y}l{/y}{g}d{/g}{pk}'\
'r{/pk}{o}e{/o}{pp}n{/pp} of the Lord\n'
FOOTER = [u'Arky Arky (Unknown)', u'Public Domain', u'CCLI 123456']
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
class TestServiceItem(TestCase):
def setUp(self):
"""
Set up the Registry
"""
Registry.create()
mocked_renderer = MagicMock()
mocked_renderer.format_slide.return_value = [VERSE]
Registry().register(u'renderer', mocked_renderer)
Registry().register(u'image_manager', MagicMock())
def serviceitem_basic_test(self):
"""
Test the Service Item - basic test
"""
# GIVEN: A new service item
# WHEN: A service item is created (without a plugin)
service_item = ServiceItem(None)
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.missing_frames() is True, u'There should not be any frames in the service item'
def serviceitem_load_custom_from_service_test(self):
"""
Test the Service Item - adding a custom slide from a saved service
"""
# GIVEN: A new service item and a mocked add icon function
service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
# WHEN: adding a custom from a saved Service
line = self.convert_file_service_item(u'serviceitem_custom_1.osj')
service_item.set_from_service(line)
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert len(service_item._display_frames) == 0, u'The service item should have no display frames'
assert len(service_item.capabilities) == 5, u'There should be 5 default custom item capabilities'
service_item.render(True)
assert service_item.get_display_title() == u'Test Custom', u'The title should be "Test Custom"'
assert service_item.get_frames()[0][u'text'] == VERSE[:-1], \
u'The returned text matches the input, except the last line feed'
assert service_item.get_rendered_frame(1) == VERSE.split(u'\n', 1)[0], u'The first line has been returned'
assert service_item.get_frame_title(0) == u'Slide 1', u'"Slide 1" has been returned as the title'
assert service_item.get_frame_title(1) == u'Slide 2', u'"Slide 2" has been returned as the title'
assert service_item.get_frame_title(2) == u'', u'Blank has been returned as the title of slide 3'
def serviceitem_load_image_from_service_test(self):
"""
Test the Service Item - adding an image from a saved service
"""
# GIVEN: A new service item and a mocked add icon function
image_name = u'image_1.jpg'
test_file = os.path.join(TEST_PATH, image_name)
frame_array = {u'path': test_file, u'title': image_name}
service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
# WHEN: adding an image from a saved Service and mocked exists
line = self.convert_file_service_item(u'serviceitem_image_1.osj')
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
mocked_exists.return_value = True
service_item.set_from_service(line, TEST_PATH)
# THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image'
assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1'
assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image'
assert service_item.get_frame_title(0) == image_name, u'The frame title should match the image name'
assert service_item.get_display_title() == image_name, u'The display title should match the first image name'
assert service_item.is_image() is True, u'This service item should be of an "image" type'
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
u'This service item should be able to be Maintained'
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
u'This service item should be able to be be Previewed'
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
u'This service item should be able to be run in a can be made to Loop'
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
u'This service item should be able to have new items added to it'
def serviceitem_load_image_from_local_service_test(self):
"""
Test the Service Item - adding an image from a saved local service
"""
# GIVEN: A new service item and a mocked add icon function
image_name1 = u'image_1.jpg'
image_name2 = u'image_2.jpg'
test_file1 = os.path.join(u'/home/openlp', image_name1)
test_file2 = os.path.join(u'/home/openlp', image_name2)
frame_array1 = {u'path': test_file1, u'title': image_name1}
frame_array2 = {u'path': test_file2, u'title': image_name2}
service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
service_item2 = ServiceItem(None)
service_item2.add_icon = MagicMock()
# WHEN: adding an image from a saved Service and mocked exists
line = self.convert_file_service_item(u'serviceitem_image_2.osj')
line2 = self.convert_file_service_item(u'serviceitem_image_2.osj', 1)
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
mocked_exists.return_value = True
service_item2.set_from_service(line2)
service_item.set_from_service(line)
# THEN: We should get back a valid service item
# This test is copied from service_item.py, but is changed since to conform to
# new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now.
assert service_item.is_valid is True, u'The first service item should be valid'
assert service_item2.is_valid is True, u'The second service item should be valid'
assert service_item.get_rendered_frame(0) == test_file1, u'The first frame should match the path to the image'
assert service_item2.get_rendered_frame(0) == test_file2, u'The Second frame should match the path to the image'
assert service_item.get_frames()[0] == frame_array1, u'The return should match the frame array1'
assert service_item2.get_frames()[0] == frame_array2, u'The return should match the frame array2'
assert service_item.get_frame_path(0) == test_file1, u'The frame path should match the full path to the image'
assert service_item2.get_frame_path(0) == test_file2, u'The frame path should match the full path to the image'
assert service_item.get_frame_title(0) == image_name1, u'The 1st frame title should match the image name'
assert service_item2.get_frame_title(0) == image_name2, u'The 2nd frame title should match the image name'
assert service_item.title.lower() == service_item.name, \
u'The plugin name should match the display title, as there are > 1 Images'
assert service_item.is_image() is True, u'This service item should be of an "image" type'
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
u'This service item should be able to be Maintained'
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
u'This service item should be able to be be Previewed'
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
u'This service item should be able to be run in a can be made to Loop'
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
u'This service item should be able to have new items added to it'
def serviceitem_convert_osd2osj_test(self):
"""
Test the Service Item - load a osd to service_item, convert to json,
load again to service_item and compare the old and new service_item.
"""
# GIVEN: A valid osd (python pickle format) service in file
service_file = os.path.join(TEST_PATH, u'serviceitem_osd2osj.osd')
osd_service_items = []
try:
open_file = open(service_file, u'r')
osd_service_items = cPickle.load(open_file)
except IOError:
osd_service_items = []
finally:
open_file.close()
# WHEN: Dumping loaded osd service to json format, and save to file and reloading to service
json_service_content = json.dumps(osd_service_items)
open_file = None
open_filename = u''
try:
(open_filep, open_filename) = tempfile.mkstemp()
open_file = open(open_filename, u'w')
open_file.write(json_service_content)
open_file.close()
open_file = open(open_filename, u'r')
json_service_content = open_file.read()
except IOError:
json_service_content = u''
finally:
open_file.close()
os.remove(open_filename)
osj_service_items = json.loads(json_service_content)
# THEN: The service loaded from osj (json format) should be the same as the service loaded from the original osd (python pickle format)
# Loop over every item and compare the osj with osd version
for osd_item, osj_item in zip(osd_service_items, osj_service_items):
# Create service item objects
service_item_osd = ServiceItem()
service_item_osd.add_icon = MagicMock()
service_item_osj = ServiceItem()
service_item_osj.add_icon = MagicMock()
with patch(u'openlp.core.ui.servicemanager.os.path.exists') as mocked_exists:
mocked_exists.return_value = True
service_item_osd.set_from_service(osd_item, TEST_PATH)
service_item_osj.set_from_service(osj_item, TEST_PATH)
# Check that the exported/imported attributes are the same
assert service_item_osj.is_valid is True, u'The osj service item should be valid'
assert service_item_osd.is_valid is True, u'The osd service item should be valid'
assert service_item_osj.name == service_item_osd.name , u'The osd and the osj attribute name should be the same!'
assert service_item_osj.theme == service_item_osd.theme , u'The osd and the osj attribute theme should be the same!'
assert service_item_osj.title == service_item_osd.title , u'The osd and the osj attribute title should be the same!'
assert service_item_osj.icon == service_item_osd.icon , u'The osd and the osj attribute icon should be the same!'
assert service_item_osj.raw_footer == service_item_osd.raw_footer , u'The osd and the osj attribute raw_footer should be the same!'
assert service_item_osj.service_item_type == service_item_osd.service_item_type , u'The osd and the osj attribute service_item_type should be the same!'
assert service_item_osj.audit == service_item_osd.audit , u'The osd and the osj attribute audit should be the same!'
assert service_item_osj.notes == service_item_osd.notes , u'The osd and the osj attribute notes should be the same!'
assert service_item_osj.from_plugin == service_item_osd.from_plugin , u'The osd and the osj attribute from_plugin should be the same!'
assert service_item_osj.capabilities == service_item_osd.capabilities , u'The osd and the osj attribute capabilities should be the same!'
assert service_item_osj.search_string == service_item_osd.search_string , u'The osd and the osj attribute search_string should be the same!'
assert service_item_osj.data_string == service_item_osd.data_string , u'The osd and the osj attribute data_string should be the same!'
# Notice that xml_version from osd needs to be utf-8 decoded, since unicode-characters
# is written as byte-codes by pickle, while json can escape unicode-characters
if service_item_osd.xml_version:
assert service_item_osj.xml_version == service_item_osd.xml_version.decode(u'utf-8') , u'The osd and the osj attribute xml_version should be the same!'
assert service_item_osj.auto_play_slides_once == service_item_osd.auto_play_slides_once , u'The osd and the osj attribute auto_play_slides_once should be the same!'
assert service_item_osj.auto_play_slides_loop == service_item_osd.auto_play_slides_loop , u'The osd and the osj attribute auto_play_slides_loop should be the same!'
assert service_item_osj.timed_slide_interval == service_item_osd.timed_slide_interval , u'The osd and the osj attribute timed_slide_interval should be the same!'
assert service_item_osj.start_time == service_item_osd.start_time , u'The osd and the osj attribute start_time should be the same!'
assert service_item_osj.end_time == service_item_osd.end_time , u'The osd and the osj attribute end_time should be the same!'
assert service_item_osj.media_length == service_item_osd.media_length , u'The osd and the osj attribute media_length should be the same!'
assert service_item_osj.background_audio == service_item_osd.background_audio , u'The osd and the osj attribute background_audio should be the same!'
assert service_item_osj.theme_overwritten == service_item_osd.theme_overwritten , u'The osd and the osj attribute theme_overwritten should be the same!'
assert service_item_osj.will_auto_start == service_item_osd.will_auto_start , u'The osd and the osj attribute will_auto_start should be the same!'
assert service_item_osj.processor == service_item_osd.processor , u'The osd and the osj attribute processor should be the same!'
def convert_file_service_item(self, name, row=0):
service_file = os.path.join(TEST_PATH, name)
try:
open_file = open(service_file, u'r')
items = json.load(open_file)
first_line = items[row]
except IOError:
first_line = u''
finally:
open_file.close()
return first_line

View File

@ -0,0 +1,3 @@
"""
Tests for the Bibles plugin
"""

View File

@ -0,0 +1,59 @@
"""
This module contains tests for the lib submodule of the Bibles plugin.
"""
from unittest import TestCase
from openlp.plugins.bibles.lib import SearchResults
class TestLib(TestCase):
"""
Test the functions in the :mod:`lib` module.
"""
def search_results_creation_test(self):
"""
Test the creation and construction of the SearchResults class
"""
# GIVEN: A book, chapter and a verse list
book = u'Genesis'
chapter = 1
verse_list = {
1: u'In the beginning God created the heavens and the earth.',
2: u'The earth was without form and void, and darkness was over the face of the deep. And the Spirit of '
u'God was hovering over the face of the waters.'
}
# WHEN: We create the search results object
search_results = SearchResults(book, chapter, verse_list)
# THEN: It should have a book, a chapter and a verse list
self.assertIsNotNone(search_results, u'The search_results object should not be None')
self.assertEqual(search_results.book, book, u'The book should be "Genesis"')
self.assertEqual(search_results.chapter, chapter, u'The chapter should be 1')
self.assertDictEqual(search_results.verse_list, verse_list, u'The verse lists should be identical')
def search_results_has_verse_list_test(self):
"""
Test that a SearchResults object with a valid verse list returns True when checking ``has_verse_list()``
"""
# GIVEN: A valid SearchResults object with a proper verse list
search_results = SearchResults(u'Genesis', 1, {1: u'In the beginning God created the heavens and the earth.'})
# WHEN: We check that the SearchResults object has a verse list
has_verse_list = search_results.has_verse_list()
# THEN: It should be True
self.assertTrue(has_verse_list, u'The SearchResults object should have a verse list')
def search_results_has_no_verse_list_test(self):
"""
Test that a SearchResults object with an empty verse list returns False when checking ``has_verse_list()``
"""
# GIVEN: A valid SearchResults object with an empty verse list
search_results = SearchResults(u'Genesis', 1, {})
# WHEN: We check that the SearchResults object has a verse list
has_verse_list = search_results.has_verse_list()
# THEN: It should be False
self.assertFalse(has_verse_list, u'The SearchResults object should have a verse list')

View File

@ -0,0 +1,3 @@
"""
Tests for the Songs plugin
"""

View File

@ -0,0 +1,195 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2013 Raoul Snyman #
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the SongShow Plus song importer.
"""
import os
from unittest import TestCase
from mock import patch, MagicMock
from openlp.plugins.songs.lib import VerseType
from openlp.plugins.songs.lib.foilpresenterimport import FoilPresenter
TEST_PATH = os.path.abspath(
os.path.join(os.path.dirname(__file__), u'..', u'..', u'..', u'/resources/foilpresentersongs'))
class TestFoilPresenter(TestCase):
"""
Test the functions in the :mod:`foilpresenterimport` module.
"""
#TODO: The following modules still need tests written for
# xml_to_song
# _child
# _process_authors
# _process_cclinumber
# _process_comments
# _process_copyright
# _process_lyrics
# _process_songbooks
# _process_titles
# _process_topics
def setUp(self):
self.child_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._child')
self.clean_song_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.clean_song')
self.objectify_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.objectify')
self.process_authors_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_authors')
self.process_cclinumber_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_cclinumber')
self.process_comments_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_comments')
self.process_lyrics_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_lyrics')
self.process_songbooks_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_songbooks')
self.process_titles_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_titles')
self.process_topics_patcher = \
patch(u'openlp.plugins.songs.lib.foilpresenterimport.FoilPresenter._process_topics')
self.re_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.re')
self.song_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.Song')
self.song_xml_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.SongXML')
self.translate_patcher = patch(u'openlp.plugins.songs.lib.foilpresenterimport.translate')
self.mocked_child = self.child_patcher.start()
self.mocked_clean_song = self.clean_song_patcher.start()
self.mocked_objectify = self.objectify_patcher.start()
self.mocked_process_authors = self.process_authors_patcher.start()
self.mocked_process_cclinumber = self.process_cclinumber_patcher.start()
self.mocked_process_comments = self.process_comments_patcher.start()
self.mocked_process_lyrics = self.process_lyrics_patcher.start()
self.mocked_process_songbooks = self.process_songbooks_patcher.start()
self.mocked_process_titles = self.process_titles_patcher.start()
self.mocked_process_topics = self.process_topics_patcher.start()
self.mocked_re = self.re_patcher.start()
self.mocked_song = self.song_patcher.start()
self.mocked_song_xml = self.song_xml_patcher.start()
self.mocked_translate = self.translate_patcher.start()
self.mocked_child.return_value = u'Element Text'
self.mocked_translate.return_value = u'Translated String'
self.mocked_manager = MagicMock()
self.mocked_song_import = MagicMock()
def tearDown(self):
self.child_patcher.stop()
self.clean_song_patcher.stop()
self.objectify_patcher.stop()
self.process_authors_patcher.stop()
self.process_cclinumber_patcher.stop()
self.process_comments_patcher.stop()
self.process_lyrics_patcher.stop()
self.process_songbooks_patcher.stop()
self.process_titles_patcher.stop()
self.process_topics_patcher.stop()
self.re_patcher.stop()
self.song_patcher.stop()
self.song_xml_patcher.stop()
self.translate_patcher.stop()
def create_foil_presenter_test(self):
"""
Test creating an instance of the FoilPresenter class
"""
# GIVEN: A mocked out "manager" and "SongImport" instance
mocked_manager = MagicMock()
mocked_song_import = MagicMock()
# WHEN: An FoilPresenter instance is created
foil_presenter_instance = FoilPresenter(mocked_manager, mocked_song_import)
# THEN: The instance should not be None
self.assertIsNotNone(foil_presenter_instance, u'FoilPresenter instance should not be none')
def no_xml_test(self):
"""
Test calling xml_to_song with out the xml argument
"""
# GIVEN: A mocked out "manager" and "SongImport" as well as an foil_presenter instance
mocked_manager = MagicMock()
mocked_song_import = MagicMock()
foil_presenter_instance = FoilPresenter(mocked_manager, mocked_song_import)
# WHEN: xml_to_song is called without valid an argument
for arg in [None, False, 0, u'']:
result = foil_presenter_instance.xml_to_song(arg)
# Then: xml_to_song should return False
self.assertEqual(result, None, u'xml_to_song should return None when called with %s' % arg)
def encoding_declaration_removal_test(self):
"""
Test that the encoding declaration is removed
"""
# GIVEN: A reset mocked out re and an instance of foil_presenter
self.mocked_re.reset()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: xml_to_song is called with a string with an xml encoding declaration
foil_presenter_instance.xml_to_song(u'<?xml version="1.0" encoding="UTF-8"?>\n<foilpresenterfolie>')
# THEN: the xml encoding declaration should have been stripped
self.mocked_re.compile.sub.called_with(u'\n<foilpresenterfolie>')
def no_encoding_declaration_test(self):
"""
Check that the xml sting is left intact when no encoding declaration is made
"""
# GIVEN: A reset mocked out re and an instance of foil_presenter
self.mocked_re.reset()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: xml_to_song is called with a string without an xml encoding declaration
foil_presenter_instance.xml_to_song(u'<foilpresenterfolie>')
# THEN: the string shiuld have been left intact
self.mocked_re.compile.sub.called_with(u'<foilpresenterfolie>')
def process_lyrics_no_verses_test(self):
"""
Test that _process_lyrics handles song files that have no verses.
"""
# GIVEN: A mocked foilpresenterfolie with no attribute strophe, a mocked song and a
# foil presenter instance
self.process_lyrics_patcher.stop()
self.mocked_song_xml.reset()
mock_foilpresenterfolie = MagicMock()
del mock_foilpresenterfolie.strophen.strophe
mocked_song = MagicMock()
foil_presenter_instance = FoilPresenter(self.mocked_manager, self.mocked_song_import)
# WHEN: _process_lyrics is called
result = foil_presenter_instance._process_lyrics(mock_foilpresenterfolie, mocked_song)
# THEN: _process_lyrics should return None and the song_import logError method should have been called once
self.assertIsNone(result)
self.mocked_song_import.logError.assert_called_once_with(u'Element Text', u'Translated String')
self.process_lyrics_patcher.start()

View File

@ -11,6 +11,7 @@ from openlp.core.lib import Registry, ServiceItem
from openlp.core.ui import listpreviewwidget from openlp.core.ui import listpreviewwidget
from tests.utils.osdinteraction import read_service_from_file from tests.utils.osdinteraction import read_service_from_file
class TestListPreviewWidget(TestCase): class TestListPreviewWidget(TestCase):
def setUp(self): def setUp(self):
@ -41,8 +42,7 @@ class TestListPreviewWidget(TestCase):
# GIVEN: A new ListPreviewWidget instance. # GIVEN: A new ListPreviewWidget instance.
# WHEN: No SlideItem has been added yet. # WHEN: No SlideItem has been added yet.
# THEN: The count of items should be zero. # THEN: The count of items should be zero.
self.assertEqual(self.preview_widget.slide_count(), 0, self.assertEqual(self.preview_widget.slide_count(), 0, u'The slide list should be empty.')
u'The slide list should be empty.')
def initial_slide_number_test(self): def initial_slide_number_test(self):
""" """
@ -51,8 +51,7 @@ class TestListPreviewWidget(TestCase):
# GIVEN: A new ListPreviewWidget instance. # GIVEN: A new ListPreviewWidget instance.
# WHEN: No SlideItem has been added yet. # WHEN: No SlideItem has been added yet.
# THEN: The number of the current item should be -1. # THEN: The number of the current item should be -1.
self.assertEqual(self.preview_widget.current_slide_number(), -1, self.assertEqual(self.preview_widget.current_slide_number(), -1, u'The slide number should be -1.')
u'The slide number should be -1.')
def replace_service_item_test(self): def replace_service_item_test(self):
""" """
@ -60,16 +59,14 @@ class TestListPreviewWidget(TestCase):
""" """
# GIVEN: A ServiceItem with two frames. # GIVEN: A ServiceItem with two frames.
service_item = ServiceItem(None) service_item = ServiceItem(None)
service = read_service_from_file(u'serviceitem_image_2.osd') service = read_service_from_file(u'serviceitem_image_3.osj')
with patch('os.path.exists'): with patch('os.path.exists'):
service_item.set_from_service(service[0]) service_item.set_from_service(service[0])
# WHEN: Added to the preview widget. # WHEN: Added to the preview widget.
self.preview_widget.replace_service_item(service_item, 1, 1) self.preview_widget.replace_service_item(service_item, 1, 1)
# THEN: The slide count and number should fit. # THEN: The slide count and number should fit.
self.assertEqual(self.preview_widget.slide_count(), 2, self.assertEqual(self.preview_widget.slide_count(), 2, u'The slide count should be 2.')
u'The slide count should be 2.') self.assertEqual(self.preview_widget.current_slide_number(), 1, u'The current slide number should be 1.')
self.assertEqual(self.preview_widget.current_slide_number(), 1,
u'The current slide number should be 1.')
def change_slide_test(self): def change_slide_test(self):
""" """
@ -77,12 +74,11 @@ class TestListPreviewWidget(TestCase):
""" """
# GIVEN: A ServiceItem with two frames content. # GIVEN: A ServiceItem with two frames content.
service_item = ServiceItem(None) service_item = ServiceItem(None)
service = read_service_from_file(u'serviceitem_image_2.osd') service = read_service_from_file(u'serviceitem_image_3.osj')
with patch('os.path.exists'): with patch('os.path.exists'):
service_item.set_from_service(service[0]) service_item.set_from_service(service[0])
# WHEN: Added to the preview widget and switched to the second frame. # WHEN: Added to the preview widget and switched to the second frame.
self.preview_widget.replace_service_item(service_item, 1, 0) self.preview_widget.replace_service_item(service_item, 1, 0)
self.preview_widget.change_slide(1) self.preview_widget.change_slide(1)
# THEN: The current_slide_number should reflect the change. # THEN: The current_slide_number should reflect the change.
self.assertEqual(self.preview_widget.current_slide_number(), 1, self.assertEqual(self.preview_widget.current_slide_number(), 1, u'The current slide number should be 1.')
u'The current slide number should be 1.')

View File

@ -1,96 +0,0 @@
(lp1
(dp2
Vserviceitem
p3
(dp4
Vheader
p5
(dp6
Vfooter
p7
(lp8
VTest Custom Credits
p9
asVaudit
p10
V
sVsearch
p11
V
sVwill_auto_start
p12
I00
sVname
p13
Vcustom
p14
sVplugin
p15
g14
sVdata
p16
V
sVnotes
p17
V
sVtitle
p18
VTest Custom
p19
sVfrom_plugin
p20
I00
sVcapabilities
p21
(lp22
I2
aI1
aI5
aI13
aI8
asVmedia_length
p23
I0
sVtheme_overwritten
p24
I00
sVtheme
p25
NsVxml_version
p26
NsVend_time
p27
I0
sVbackground_audio
p28
(lp29
sVtype
p30
I1
sVstart_time
p31
I0
sVicon
p32
V:/plugins/plugin_custom.png
p33
ssg16
(lp34
(dp35
VverseTag
p36
NsVraw_slide
p37
VSlide 1
p38
sVtitle
p39
g38
sa(dp40
g36
Nsg37
VSlide 2
p41
sg39
g41
sassa.

View File

@ -1,79 +0,0 @@
(lp1
(dp2
Vserviceitem
p3
(dp4
Vheader
p5
(dp6
Vfooter
p7
(lp8
sVaudit
p9
V
sVsearch
p10
V
sVwill_auto_start
p11
I00
sVname
p12
Vimages
p13
sVplugin
p14
g13
sVdata
p15
V
sVnotes
p16
V
sVtitle
p17
VImages
p18
sVfrom_plugin
p19
I00
sVcapabilities
p20
(lp21
I3
aI1
aI5
aI6
asVmedia_length
p22
I0
sVtheme_overwritten
p23
I00
sVtheme
p24
I-1
sVxml_version
p25
NsVend_time
p26
I0
sVbackground_audio
p27
(lp28
sVtype
p29
I2
sVstart_time
p30
I0
sVicon
p31
V:/plugins/plugin_images.png
p32
ssg15
(lp33
Vimage_1.jpg
p34
assa.

View File

@ -1,101 +0,0 @@
(lp1
(dp2
Vserviceitem
p3
(dp4
Vheader
p5
(dp6
Vxml_version
p7
NsVauto_play_slides_loop
p8
I00
sVauto_play_slides_once
p9
I00
sVwill_auto_start
p10
I00
sVtitle
p11
VImages
p12
sVcapabilities
p13
(lp14
I3
aI1
aI5
aI6
asVtheme
p15
I-1
sVbackground_audio
p16
(lp17
sVicon
p18
V:/plugins/plugin_images.png
p19
sVtype
p20
I2
sVstart_time
p21
I0
sVfrom_plugin
p22
I00
sVmedia_length
p23
I0
sVdata
p24
V
sVtimed_slide_interval
p25
I0
sVaudit
p26
V
sVsearch
p27
V
sVname
p28
Vimages
p29
sVfooter
p30
(lp31
sVnotes
p32
V
sVplugin
p33
g29
sVtheme_overwritten
p34
I00
sVend_time
p35
I0
ssg24
(lp36
(dp37
Vpath
p38
V/home/openlp/image_1.jpg
p39
sg11
Vimage_1.jpg
p40
sa(dp41
g38
V/home/openlp/image_2.jpg
p42
sg11
Vimage_2.jpg
p43
sassa.

View File

@ -0,0 +1 @@
[{"serviceitem": {"header": {"xml_version": null, "auto_play_slides_loop": false, "auto_play_slides_once": false, "will_auto_start": false, "title": "Images", "capabilities": [3, 1, 5, 6, 3, 1, 5, 6], "theme": -1, "background_audio": [], "icon": ":/plugins/plugin_images.png", "type": 2, "start_time": 0, "from_plugin": false, "media_length": 0, "data": "", "timed_slide_interval": 0, "audit": "", "search": "", "name": "images", "footer": [], "notes": "", "plugin": "images", "theme_overwritten": false, "end_time": 0, "processor": null}, "data": [{"path": "/home/tim/Pictures/Holiday_base/11 November/Holiday - Jon/IMG_7445.JPG", "title": "IMG_7445.JPG"}, {"path": "/home/tim/Pictures/Holiday_base/11 November/Holiday - Jon/IMG_7478.JPG", "title": "IMG_7478.JPG"}]}}]

File diff suppressed because it is too large Load Diff

View File

@ -30,9 +30,8 @@
The :mod:`osdinteraction` provides miscellaneous functions for interacting with The :mod:`osdinteraction` provides miscellaneous functions for interacting with
OSD files. OSD files.
""" """
import os import os
import cPickle import json
from tests.utils.constants import TEST_RESOURCES_PATH from tests.utils.constants import TEST_RESOURCES_PATH
@ -45,5 +44,5 @@ def read_service_from_file(file_name):
""" """
service_file = os.path.join(TEST_RESOURCES_PATH, file_name) service_file = os.path.join(TEST_RESOURCES_PATH, file_name)
with open(service_file, u'r') as open_file: with open(service_file, u'r') as open_file:
service = cPickle.load(open_file) service = json.load(open_file)
return service return service