merge trunk

This commit is contained in:
Jonathan Springer 2014-04-16 04:00:19 -04:00
commit cebec89556
29 changed files with 485 additions and 119 deletions

View File

@ -2,6 +2,7 @@
*.*~
\#*\#
*.eric4project
*.eric5project
*.ropeproject
*.e4*
.eric4project

View File

@ -173,7 +173,7 @@ def create_button(parent, name, **kwargs):
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
else:
log.warn('The role "%s" is not defined in create_push_button().', role)
if kwargs.pop('class', '') == 'toolbutton':
if kwargs.pop('btn_class', '') == 'toolbutton':
button = QtGui.QToolButton(parent)
else:
button = QtGui.QPushButton(parent)

View File

@ -168,8 +168,10 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
"""
if enabled:
self.setAutoFillBackground(False)
self.setStyleSheet("QGraphicsView {background: transparent; border: 0px;}")
else:
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
self.setStyleSheet("QGraphicsView {}")
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
self.repaint()

View File

@ -506,7 +506,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
else:
self.media_volume(controller, controller.media_info.volume)
if status:
display.frame.evaluateJavaScript('show_blank("desktop");')
if not controller.media_info.is_background:
display.frame.evaluateJavaScript('show_blank("desktop");')
self.current_media_players[controller.controller_type].set_visible(display, True)
# Flash needs to be played and will not AutoPlay
if controller.media_info.is_flash:
@ -517,7 +518,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
controller.mediabar.actions['playbackPause'].setVisible(True)
controller.mediabar.actions['playbackStop'].setVisible(True)
if controller.is_live:
if controller.hide_menu.defaultAction().isChecked():
if controller.hide_menu.defaultAction().isChecked() and not controller.media_info.is_background:
controller.hide_menu.defaultAction().trigger()
# Start Timer for ui updates
if not self.timer.isActive():

View File

@ -1489,9 +1489,11 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
if new_item:
self.add_service_item(new_item, replace=True)
def on_service_item_rename(self):
def on_service_item_rename(self, field=None):
"""
Opens a dialog to rename the service item.
:param field: Not used, but PyQt needs this.
"""
item = self.find_service_item()[0]
if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle):

View File

@ -495,14 +495,14 @@ class SlideController(DisplayController, RegistryProperties):
self.on_theme_display(False)
self.on_hide_display(False)
def service_previous(self):
def service_previous(self, field=None):
"""
Live event to select the previous service item from the service manager.
"""
self.keypress_queue.append(ServiceItemAction.Previous)
self._process_queue()
def service_next(self):
def service_next(self, field=None):
"""
Live event to select the next service item from the service manager.
"""

View File

@ -204,19 +204,19 @@ class PdfDocument(PresentationDocument):
log.debug(' '.join(e.cmd))
log.debug(e.output)
# Extract the pdf resolution from output, the format is " Size: x: <width>, y: <height>"
width = 0
height = 0
width = 0.0
height = 0.0
for line in runlog.splitlines():
try:
width = int(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1))
height = int(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1))
width = float(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1))
height = float(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1))
break
except AttributeError:
pass
continue
# Calculate the ratio from pdf to screen
if width > 0 and height > 0:
width_ratio = size.right() / float(width)
height_ratio = size.bottom() / float(height)
width_ratio = size.right() / width
height_ratio = size.bottom() / height
# return the resolution that should be used. 72 is default.
if width_ratio > height_ratio:
return int(height_ratio * 72)

View File

@ -122,8 +122,10 @@ class PresentationDocument(object):
a file, e.g. thumbnails
"""
try:
shutil.rmtree(self.get_thumbnail_folder())
shutil.rmtree(self.get_temp_folder())
if os.path.exists(self.get_thumbnail_folder()):
shutil.rmtree(self.get_thumbnail_folder())
if os.path.exists(self.get_temp_folder()):
shutil.rmtree(self.get_temp_folder())
except OSError:
log.exception('Failed to delete presentation controller files')

View File

@ -31,6 +31,7 @@ The duplicate song removal logic for OpenLP.
"""
import logging
import multiprocessing
import os
from PyQt4 import QtCore, QtGui
@ -45,6 +46,18 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal
log = logging.getLogger(__name__)
def song_generator(songs):
"""
This is a generator function to return tuples of two songs. When completed then all songs have once been returned
combined with any other songs.
:param songs: All songs in the database.
"""
for outer_song_counter in range(len(songs) - 1):
for inner_song_counter in range(outer_song_counter + 1, len(songs)):
yield (songs[outer_song_counter], songs[inner_song_counter])
class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
"""
This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs
@ -167,24 +180,31 @@ class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
max_progress_count = max_songs * (max_songs - 1) // 2
self.duplicate_search_progress_bar.setMaximum(max_progress_count)
songs = self.plugin.manager.get_all_objects(Song)
for outer_song_counter in range(max_songs - 1):
for inner_song_counter in range(outer_song_counter + 1, max_songs):
if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]):
duplicate_added = self.add_duplicates_to_song_list(
songs[outer_song_counter], songs[inner_song_counter])
if duplicate_added:
self.found_duplicates_edit.appendPlainText(
songs[outer_song_counter].title + " = " + songs[inner_song_counter].title)
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
# The call to process_events() will keep the GUI responsive.
self.application.process_events()
if self.break_search:
return
# Create a worker/process pool to check the songs.
process_number = max(1, multiprocessing.cpu_count() - 1)
pool = multiprocessing.Pool(process_number)
result = pool.imap_unordered(songs_probably_equal, song_generator(songs), 30)
# Do not accept any further tasks. Also this closes the processes if all tasks are done.
pool.close()
# While the processes are still working, start to look at the results.
for song_tuple in result:
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
# The call to process_events() will keep the GUI responsive.
self.application.process_events()
if self.break_search:
pool.terminate()
return
if song_tuple is None:
continue
song1, song2 = song_tuple
duplicate_added = self.add_duplicates_to_song_list(song1, song2)
if duplicate_added:
self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title)
self.review_total_count = len(self.duplicate_song_list)
if self.review_total_count == 0:
self.notify_no_duplicates()
else:
if self.duplicate_song_list:
self.button(QtGui.QWizard.NextButton).show()
else:
self.notify_no_duplicates()
finally:
self.application.set_normal_cursor()
elif page_id == self.review_page_id:

View File

@ -56,11 +56,15 @@ class SongBeamerTypes(object):
'Zwischenspiel': VerseType.tags[VerseType.Bridge],
'Pre-Chorus': VerseType.tags[VerseType.PreChorus],
'Pre-Refrain': VerseType.tags[VerseType.PreChorus],
'Misc': VerseType.tags[VerseType.Other],
'Pre-Bridge': VerseType.tags[VerseType.Other],
'Pre-Coda': VerseType.tags[VerseType.Other],
'Part': VerseType.tags[VerseType.Other],
'Teil': VerseType.tags[VerseType.Other],
'Unbekannt': VerseType.tags[VerseType.Other],
'Unknown': VerseType.tags[VerseType.Other],
'Unbenannt': VerseType.tags[VerseType.Other]
'Unbenannt': VerseType.tags[VerseType.Other],
'$$M=': VerseType.tags[VerseType.Other]
}
@ -132,7 +136,8 @@ class SongBeamerImport(SongImport):
line = str(line).strip()
if line.startswith('#') and not read_verses:
self.parseTags(line)
elif line.startswith('---'):
elif line.startswith('--'):
# --- and -- allowed for page-breaks (difference in Songbeamer only in printout)
if self.current_verse:
self.replace_html_tags()
self.add_verse(self.current_verse, self.current_verse_type)
@ -282,4 +287,7 @@ class SongBeamerImport(SongImport):
if marks[1].isdigit():
self.current_verse_type += marks[1]
return True
elif marks[0].startswith('$$M='): # this verse-mark cannot be numbered
self.current_verse_type = SongBeamerTypes.MarkTypes['$$M=']
return True
return False

View File

@ -52,13 +52,13 @@ MIN_BLOCK_SIZE = 70
MAX_TYPO_SIZE = 3
def songs_probably_equal(song1, song2):
def songs_probably_equal(song_tupel):
"""
Calculate and return whether two songs are probably equal.
:param song1: The first song to compare.
:param song2: The second song to compare.
:param song_tupel: A tuple of two songs to compare.
"""
song1, song2 = song_tupel
if len(song1.search_lyrics) < len(song2.search_lyrics):
small = song1.search_lyrics
large = song2.search_lyrics
@ -75,18 +75,19 @@ def songs_probably_equal(song1, song2):
for element in diff_no_typos:
if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE:
length_of_equal_blocks += _op_length(element)
if length_of_equal_blocks >= MIN_BLOCK_SIZE:
return True
return song1, song2
# Check 2: Similarity based on the relative length of the longest equal block.
# Calculate the length of the largest equal block of the diff set.
length_of_longest_equal_block = 0
for element in diff_no_typos:
if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
length_of_longest_equal_block = _op_length(element)
if length_of_equal_blocks >= MIN_BLOCK_SIZE or length_of_longest_equal_block > len(small) * 2 // 3:
return True
if length_of_longest_equal_block > len(small) * 2 // 3:
return song1, song2
# Both checks failed. We assume the songs are not equal.
return False
return None
def _op_length(opcode):

View File

@ -675,6 +675,7 @@ class OpenLyrics(object):
sxml = SongXML()
verses = {}
verse_def_list = []
verse_order = self._text(properties.verseOrder).split(' ') if hasattr(properties, 'verseOrder') else []
try:
lyrics = song_xml.lyrics
except AttributeError:
@ -717,13 +718,17 @@ class OpenLyrics(object):
else:
verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
# Update verse order when the verse name has changed
if verse_def != verse_tag + verse_number + verse_part:
for i in range(len(verse_order)):
if verse_order[i] == verse_def:
verse_order[i] = verse_tag + verse_number + verse_part
# We have to use a list to keep the order, as dicts are not sorted.
for verse in verse_def_list:
sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2])
song_obj.lyrics = str(sxml.extract_xml(), 'utf-8')
# Process verse order
if hasattr(properties, 'verseOrder'):
song_obj.verse_order = self._text(properties.verseOrder)
song_obj.verse_order = ' '.join(verse_order)
def _process_songbooks(self, properties, song):
"""

View File

@ -40,6 +40,7 @@ You can look up the token in the Branch-01-Pull job configuration or ask in IRC.
"""
from optparse import OptionParser
import re
from requests.exceptions import HTTPError
from subprocess import Popen, PIPE
import sys
@ -49,6 +50,9 @@ from jenkins import Jenkins
JENKINS_URL = 'http://ci.openlp.org/'
REPO_REGEX = r'(.*/+)(~.*)'
# Allows us to black list token. So when we change the token, we can display a proper message to the user.
OLD_TOKENS = []
class OpenLPJobs(object):
@ -59,9 +63,20 @@ class OpenLPJobs(object):
Branch_Functional = 'Branch-02-Functional-Tests'
Branch_Interface = 'Branch-03-Interface-Tests'
Branch_Windows = 'Branch-04-Windows_Tests'
Branch_PEP = 'Branch-05-Code-Analysis'
Branch_PEP = 'Branch-05a-Code_Analysis'
Branch_Coverage = 'Branch-05b-Test_Coverage'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP]
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP, Branch_Coverage]
class Colour(object):
"""
This class holds values which can be used to print coloured text.
"""
RED_START = '\033[1;31m'
RED_END = '\033[1;m'
GREEN_START = '\033[1;32m'
GREEN_END = '\033[1;m'
class JenkinsTrigger(object):
@ -79,14 +94,25 @@ class JenkinsTrigger(object):
"""
Ask our jenkins server to build the "Branch-01-Pull" job.
"""
self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token)
bzr = Popen(('bzr', 'whoami'), stdout=PIPE, stderr=PIPE)
raw_output, error = bzr.communicate()
# We just want the name (not the email).
name = ' '.join(raw_output.decode().split()[:-1])
cause = 'Build triggered by %s (%s)' % (name, self.repo_name)
self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build(
{'BRANCH_NAME': self.repo_name, 'cause': cause}, token=self.token)
def print_output(self):
"""
Print the status information of the build tirggered.
"""
print("Add this to your merge proposal:")
print("--------------------------------")
print('Add this to your merge proposal:')
print('--------------------------------')
bzr = Popen(('bzr', 'revno'), stdout=PIPE, stderr=PIPE)
raw_output, error = bzr.communicate()
revno = raw_output.decode().strip()
print('%s (revision %s)' % (get_repo_name(), revno))
for job in OpenLPJobs.Jobs:
self.__print_build_info(job)
@ -107,17 +133,17 @@ class JenkinsTrigger(object):
"""
job = self.jenkins_instance.job(job_name)
while job.info['inQueue']:
# Give other processes the possibility to take over. Like Thread.yield().
time.sleep(0)
time.sleep(1)
build = job.last_build
build.wait()
result_string = build.info['result']
if build.info['result'] == 'SUCCESS':
# Make 'SUCCESS' green.
result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
else:
# Make 'FAILURE' red.
result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
url = build.info['url']
print('[%s] %s' % (result_string, url))
# On failure open the browser.
#if result_string == "FAILURE":
# url += 'console'
# Popen(('xdg-open', url), stderr=PIPE)
def get_repo_name():
@ -139,46 +165,41 @@ def get_repo_name():
for line in output_list:
# Check if it is remote branch.
if 'push branch' in line:
repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
break
match = re.match(REPO_REGEX, line)
if match:
repo_name = 'lp:%s' % match.group(2)
break
elif 'checkout of branch' in line:
repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
break
repo_name = repo_name.strip('/')
# Did we find the branch name?
if not repo_name:
for line in output_list:
# Check if the branch was pushed.
if 'Shared repository with trees (format: 2a)' in line:
print('Not a branch. cd to a branch.')
return
print('Not a branch. Have you pushed it to launchpad?')
return
return repo_name
match = re.match(REPO_REGEX, line)
if match:
repo_name = 'lp:%s' % match.group(2)
break
return repo_name.strip('/')
def main():
usage = 'Usage: python %prog TOKEN [options]'
parser = OptionParser(usage=usage)
parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True,
parser.add_option('-d', '--disable-output', dest='enable_output', action='store_false', default=True,
help='Disable output.')
parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False,
parser.add_option('-b', '--open-browser', dest='open_browser', action='store_true', default=False,
help='Opens the jenkins page in your browser.')
#parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true",
# default=False, help='Opens the jenkins page in your browser in case a test fails.')
options, args = parser.parse_args(sys.argv)
if len(args) == 2:
if not get_repo_name():
print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
return
token = args[-1]
if token in OLD_TOKENS:
print('Your token is not valid anymore. Get the most recent one.')
return
jenkins_trigger = JenkinsTrigger(token)
try:
jenkins_trigger.trigger_build()
except HTTPError as e:
print("Wrong token.")
print('Wrong token.')
return
# Open the browser before printing the output.
if options.open_browser:

View File

@ -52,7 +52,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# GIVEN an Empty Registry
# WHEN there is no Application
# THEN the application should be none
self.assertEquals(self.application, None, 'The application value should be None')
self.assertEqual(self.application, None, 'The application value should be None')
def application_test(self):
"""
@ -63,4 +63,4 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# WHEN the application is registered
Registry().register('application', application)
# THEN the application should be none
self.assertEquals(self.application, application, 'The application value should match')
self.assertEqual(self.application, application, 'The application value should match')

View File

@ -30,12 +30,16 @@
Package to test the openlp.core.ui package.
"""
import os
import time
from threading import Lock
from unittest import TestCase
from PyQt4 import QtGui
from openlp.core.common import Registry
from openlp.core.lib import ImageManager, ScreenList
from openlp.core.lib.imagemanager import Priority
from tests.functional import patch
from tests.helpers.testmixin import TestMixin
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
@ -51,6 +55,8 @@ class TestImageManager(TestCase, TestMixin):
self.get_application()
ScreenList.create(self.app.desktop())
self.image_manager = ImageManager()
self.lock = Lock()
self.sleep_time = 0.1
def tearDown(self):
"""
@ -82,3 +88,87 @@ class TestImageManager(TestCase, TestMixin):
with self.assertRaises(KeyError) as context:
self.image_manager.get_image(TEST_PATH, 'church1.jpg')
self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image')
def process_cache_test(self):
"""
Test the process_cache method
"""
with patch('openlp.core.lib.imagemanager.resize_image') as mocked_resize_image, \
patch('openlp.core.lib.imagemanager.image_to_byte') as mocked_image_to_byte:
# GIVEN: Mocked functions
mocked_resize_image.side_effect = self.mocked_resize_image
mocked_image_to_byte.side_effect = self.mocked_image_to_byte
image1 = 'church.jpg'
image2 = 'church2.jpg'
image3 = 'church3.jpg'
image4 = 'church4.jpg'
# WHEN: Add the images. Then get the lock (=queue can not be processed).
self.lock.acquire()
self.image_manager.add_image(TEST_PATH, image1, None)
self.image_manager.add_image(TEST_PATH, image2, None)
# THEN: All images have been added to the queue, and only the first image is not be in the list anymore, but
# is being processed (see mocked methods/functions).
# Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the #
# priority is adjusted to Priority.Lowest).
self.assertEqual(self.get_image_priority(image1), Priority.Normal,
"image1's priority should be 'Priority.Normal'")
self.assertEqual(self.get_image_priority(image2), Priority.Normal,
"image2's priority should be 'Priority.Normal'")
# WHEN: Add more images.
self.image_manager.add_image(TEST_PATH, image3, None)
self.image_manager.add_image(TEST_PATH, image4, None)
# Allow the queue to process.
self.lock.release()
# Request some "data".
image_bytes = self.image_manager.get_image_bytes(TEST_PATH, image4)
image_object = self.image_manager.get_image(TEST_PATH, image3)
# Now the mocked methods/functions do not have to sleep anymore.
self.sleep_time = 0
# Wait for the queue to finish.
while not self.image_manager._conversion_queue.empty():
time.sleep(0.1)
# Because empty() is not reliable, wait a litte; just to make sure.
time.sleep(0.1)
# THEN: The images' priority reflect how they were processed.
self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.")
self.assertEqual(self.get_image_priority(image1), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image2), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image3), Priority.Low,
"Only the QImage should have been requested (=Low).")
self.assertEqual(self.get_image_priority(image4), Priority.Urgent,
"The image bytes should have been requested (=Urgent).")
def get_image_priority(self, image):
"""
This is a help method to get the priority of the given image out of the image_manager's cache.
NOTE: This requires, that the image has been added to the image manager using the *TEST_PATH*.
:param image: The name of the image. E. g. ``image1``
"""
return self.image_manager._cache[(TEST_PATH, image)].priority
def mocked_resize_image(self, *args):
"""
This is a mocked method, so that we can control the work flow of the image manager.
"""
self.lock.acquire()
self.lock.release()
# The sleep time is adjusted in the test case.
time.sleep(self.sleep_time)
return QtGui.QImage()
def mocked_image_to_byte(self, *args):
"""
This is a mocked method, so that we can control the work flow of the image manager.
"""
self.lock.acquire()
self.lock.release()
# The sleep time is adjusted in the test case.
time.sleep(self.sleep_time)
return ''

View File

@ -49,7 +49,7 @@ class TestRenderer(TestCase):
def setUp(self):
"""
Set up the components need for all tests.
Set up the components need for all tests
"""
# Mocked out desktop object
self.desktop = MagicMock()
@ -67,7 +67,7 @@ class TestRenderer(TestCase):
def initial_renderer_test(self):
"""
Test the initial renderer state .
Test the initial renderer state
"""
# GIVEN: A new renderer instance.
renderer = Renderer()
@ -77,7 +77,7 @@ class TestRenderer(TestCase):
def default_screen_layout_test(self):
"""
Test the default layout calculations.
Test the default layout calculations
"""
# GIVEN: A new renderer instance.
renderer = Renderer()
@ -87,3 +87,35 @@ class TestRenderer(TestCase):
self.assertEqual(renderer.height, 768, 'The base renderer should be a live controller')
self.assertEqual(renderer.screen_ratio, 0.75, 'The base renderer should be a live controller')
self.assertEqual(renderer.footer_start, 691, 'The base renderer should be a live controller')
def _get_start_tags_test(self):
"""
Test the _get_start_tags() method
"""
# GIVEN: A new renderer instance. Broken raw_text (missing closing tags).
renderer = Renderer()
given_raw_text = '{st}{r}Text text text'
expected_tuple = ('{st}{r}Text text text{/r}{/st}', '{st}{r}',
'<strong><span style="-webkit-text-fill-color:red">')
# WHEN:
result = renderer._get_start_tags(given_raw_text)
# THEN: Check if the correct tuple is returned.
self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \
'tags, the opening tags, and the opening html tags.'
def _word_split_test(self):
"""
Test the _word_split() method
"""
# GIVEN: A line of text
renderer = Renderer()
given_line = 'beginning asdf \n end asdf'
expected_words = ['beginning', 'asdf', 'end', 'asdf']
# WHEN: Split the line
result_words = renderer._words_split(given_line)
# THEN: The word lists should be the same.
self.assertListEqual(result_words, expected_words)

View File

@ -82,6 +82,38 @@ class TestUi(TestCase):
self.assertEqual(1, len(btnbox.buttons()))
self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]))
def test_create_button(self):
"""
Test creating a button
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the button
btn = create_button(dialog, 'my_btn')
# THEN: We should get a button with a name
self.assertIsInstance(btn, QtGui.QPushButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
# WHEN: We create a button with some attributes
btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False)
# THEN: We should get a button with those attributes
self.assertIsInstance(btn, QtGui.QPushButton)
self.assertEqual('Hello', btn.text())
self.assertEqual('How are you?', btn.toolTip())
self.assertFalse(btn.isEnabled())
# WHEN: We create a toolbutton
btn = create_button(dialog, 'my_btn', btn_class='toolbutton')
# THEN: We should get a toolbutton
self.assertIsInstance(btn, QtGui.QToolButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
def test_create_valign_selection_widgets(self):
"""
Test creating a combo box for valign selection
@ -98,3 +130,43 @@ class TestUi(TestCase):
self.assertEqual(combo, label.buddy())
for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]:
self.assertTrue(combo.findText(text) >= 0)
def test_create_horizontal_adjusting_combo_box(self):
"""
Test creating a horizontal adjusting combo box
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the combobox
combo = create_horizontal_adjusting_combo_box(dialog, 'combo1')
# THEN: We should get a ComboBox
self.assertIsInstance(combo, QtGui.QComboBox)
self.assertEqual('combo1', combo.objectName())
self.assertEqual(QtGui.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy())
def test_create_action(self):
"""
Test creating an action
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create an action
action = create_action(dialog, 'my_action')
# THEN: We should get a QAction
self.assertIsInstance(action, QtGui.QAction)
self.assertEqual('my_action', action.objectName())
# WHEN: We create an action with some properties
action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp',
tooltip='my tooltip', statustip='my statustip')
# THEN: These properties should be set
self.assertIsInstance(action, QtGui.QAction)
self.assertEqual('my text', action.text())
self.assertIsInstance(action.icon(), QtGui.QIcon)
self.assertEqual('my tooltip', action.toolTip())
self.assertEqual('my statustip', action.statusTip())

View File

@ -79,3 +79,31 @@ class TestMainDisplay(TestCase):
# THEN: The controller should not be a live controller.
self.assertEqual(main_display.is_live, True, 'The main display should be a live controller')
def set_transparency_test(self):
"""
Test creating an instance of the MainDisplay class
"""
# GIVEN: get an instance of MainDisplay
display = MagicMock()
main_display = MainDisplay(display)
# WHEN: We enable transparency
main_display.set_transparency(True)
# THEN: There should be a Stylesheet
self.assertEqual('QGraphicsView {background: transparent; border: 0px;}', main_display.styleSheet(),
'MainDisplay instance should be transparent')
self.assertFalse(main_display.autoFillBackground(),
'MainDisplay instance should be without background auto fill')
self.assertTrue(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground),
'MainDisplay hasnt translucent background')
# WHEN: We disable transparency
main_display.set_transparency(False)
# THEN: The Stylesheet should be empty
self.assertEqual('QGraphicsView {}', main_display.styleSheet(),
'MainDisplay instance should not be transparent')
self.assertFalse(main_display.testAttribute(QtCore.Qt.WA_TranslucentBackground),
'MainDisplay hasnt translucent background')

View File

@ -180,4 +180,4 @@ class TestBSExtract(TestCase):
'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
self.assertFalse(self.mock_log.error.called, 'log.error should not have been called')
self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called')
self.assertEquals(result, ['Genesis', 'Leviticus'])
self.assertEqual(result, ['Genesis', 'Leviticus'])

View File

@ -139,10 +139,10 @@ class TestEasyWorshipSongImport(TestCase):
# THEN:
self.assertIsNotNone(field_desc_entry, 'Import should not be none')
self.assertEquals(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument')
self.assertEquals(field_desc_entry.field_type, field_type,
self.assertEqual(field_desc_entry.name, name, 'FieldDescEntry.name should be the same as the name argument')
self.assertEqual(field_desc_entry.field_type, field_type,
'FieldDescEntry.type should be the same as the type argument')
self.assertEquals(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument')
self.assertEqual(field_desc_entry.size, size, 'FieldDescEntry.size should be the same as the size argument')
def create_importer_test(self):
"""
@ -174,7 +174,7 @@ class TestEasyWorshipSongImport(TestCase):
for field_name in existing_fields:
# THEN: The item corresponding the index returned should have the same name attribute
self.assertEquals(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
self.assertEqual(importer.field_descriptions[importer.find_field(field_name)].name, field_name)
def find_non_existing_field_test(self):
"""
@ -230,7 +230,7 @@ class TestEasyWorshipSongImport(TestCase):
return_value = importer.get_field(field_index)
# THEN: get_field should return the known results
self.assertEquals(return_value, result,
self.assertEqual(return_value, result,
'get_field should return "%s" when called with "%s"' %
(result, TEST_FIELDS[field_index]))
@ -257,7 +257,7 @@ class TestEasyWorshipSongImport(TestCase):
get_field_seek_calls = test_results[2]['seek']
# THEN: get_field should return the appropriate value with the appropriate mocked objects being called
self.assertEquals(importer.get_field(field_index), get_field_result)
self.assertEqual(importer.get_field(field_index), get_field_result)
for call in get_field_read_calls:
mocked_memo_file.read.assert_any_call(call)
for call in get_field_seek_calls:
@ -403,11 +403,11 @@ class TestEasyWorshipSongImport(TestCase):
if song_copyright:
self.assertEqual(importer.copyright, song_copyright)
if ccli_number:
self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s'
self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s'
% (title, ccli_number))
for verse_text, verse_tag in add_verse_calls:
mocked_add_verse.assert_any_call(verse_text, verse_tag)
if verse_order_list:
self.assertEquals(importer.verse_order_list, verse_order_list,
self.assertEqual(importer.verse_order_list, verse_order_list,
'verse_order_list for %s should be %s' % (title, verse_order_list))
mocked_finish.assert_called_with()

View File

@ -96,10 +96,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.full_lyrics
# WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2)
result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be True.
assert result is True, 'The result should be True'
# THEN: The result should be a tuple..
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
def songs_probably_equal_short_song_test(self):
"""
@ -110,10 +110,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.short_lyrics
# WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2)
result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be True.
assert result is True, 'The result should be True'
# THEN: The result should be a tuple..
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
def songs_probably_equal_error_song_test(self):
"""
@ -124,10 +124,11 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.error_lyrics
# WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2)
result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be a tuple of songs..
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
# THEN: The result should be True.
assert result is True, 'The result should be True'
def songs_probably_equal_different_song_test(self):
"""
@ -138,10 +139,10 @@ class TestLib(TestCase):
self.song2.search_lyrics = self.different_lyrics
# WHEN: We compare those songs for equality.
result = songs_probably_equal(self.song1, self.song2)
result = songs_probably_equal((self.song1, self.song2))
# THEN: The result should be False.
assert result is False, 'The result should be False'
# THEN: The result should be None.
assert result is None, 'The result should be None'
def remove_typos_beginning_test(self):
"""

View File

@ -35,6 +35,7 @@ from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport
from openlp.plugins.songs.lib import VerseType
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
'..', '..', '..', 'resources', 'songbeamersongs'))
@ -89,7 +90,7 @@ class TestSongBeamerImport(TestCase):
# THEN: do_import should return none and the progress bar maximum should not be set.
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaxium on import_wizard.progress_bar should not have been called')
def valid_import_source_test(self):
@ -143,13 +144,90 @@ class TestSongBeamerImport(TestCase):
# THEN: do_import should return none, the song data should be as expected, and finish should have been
# called.
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (song_file, title))
for verse_text, verse_tag in add_verse_calls:
mocked_add_verse.assert_any_call(verse_text, verse_tag)
if song_book_name:
self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
(song_file, song_book_name))
if song_number:
self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' %
self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' %
(song_file, song_number))
mocked_finish.assert_called_with()
def check_verse_marks_test(self):
"""
Tests different lines to see if a verse mark is detected or not
"""
# GIVEN: line with unnumbered verse-type
line = 'Refrain'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and c as self.current_verse_type
self.assertTrue(result, 'Versemark for <Refrain> should be found, value true')
self.assertEqual(self.current_verse_type, 'c', '<Refrain> should be interpreted as <c>')
# GIVEN: line with unnumbered verse-type and trailing space
line = 'Refrain '
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and c as self.current_verse_type
self.assertTrue(result, 'Versemark for <Refrain > should be found, value true')
self.assertEqual(self.current_verse_type, 'c', '<Refrain > should be interpreted as <c>')
# GIVEN: line with numbered verse-type
line = 'Verse 1'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and v1 as self.current_verse_type
self.assertTrue(result, 'Versemark for <Verse 1> should be found, value true')
self.assertEqual(self.current_verse_type, 'v1', u'<Verse 1> should be interpreted as <v1>')
# GIVEN: line with special unnumbered verse-mark (used in Songbeamer to allow usage of non-supported tags)
line = '$$M=special'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back true and o as self.current_verse_type
self.assertTrue(result, 'Versemark for <$$M=special> should be found, value true')
self.assertEqual(self.current_verse_type, 'o', u'<$$M=special> should be interpreted as <o>')
# GIVEN: line with song-text with 3 words
line = 'Jesus my saviour'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <Jesus my saviour> should be found, value false')
self.assertIsNone(self.current_verse_type, '<Jesus my saviour> should be interpreted as none versemark')
# GIVEN: line with song-text with 2 words
line = 'Praise him'
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <Praise him> should be found, value false')
self.assertIsNone(self.current_verse_type, '<Praise him> should be interpreted as none versemark')
# GIVEN: line with only a space (could occur, nothing regular)
line = ' '
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for < > should be found, value false')
self.assertIsNone(self.current_verse_type, '< > should be interpreted as none versemark')
# GIVEN: blank line (could occur, nothing regular)
line = ''
self.current_verse_type = None
# WHEN: line is being checked for verse marks
result = SongBeamerImport.check_verse_marks(self, line)
# THEN: we should get back false and none as self.current_verse_type
self.assertFalse(result, 'No versemark for <> should be found, value false')
self.assertIsNone(self.current_verse_type, '<> should be interpreted as none versemark')

View File

@ -95,7 +95,7 @@ class TestSongShowPlusImport(TestCase):
# THEN: do_import should return none and the progress bar maximum should not be set.
self.assertIsNone(importer.do_import(), 'do_import should return None when import_source is not a list')
self.assertEquals(mocked_import_wizard.progress_bar.setMaximum.called, False,
self.assertEqual(mocked_import_wizard.progress_bar.setMaximum.called, False,
'setMaximum on import_wizard.progress_bar should not have been called')
def valid_import_source_test(self):
@ -143,7 +143,7 @@ class TestSongShowPlusImport(TestCase):
# THEN: The returned value should should correlate with the input arguments
for original_tag, openlp_tag in test_values:
self.assertEquals(importer.to_openlp_verse_tag(original_tag), openlp_tag,
self.assertEqual(importer.to_openlp_verse_tag(original_tag), openlp_tag,
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
(openlp_tag, original_tag))
@ -172,6 +172,6 @@ class TestSongShowPlusImport(TestCase):
# THEN: The returned value should should correlate with the input arguments
for original_tag, openlp_tag in test_values:
self.assertEquals(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag,
self.assertEqual(importer.to_openlp_verse_tag(original_tag, ignore_unique=True), openlp_tag,
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
(openlp_tag, original_tag))

View File

@ -110,29 +110,29 @@ class SongImportTestHelper(TestCase):
# THEN: do_import should return none, the song data should be as expected, and finish should have been
# called.
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
self.assertEquals(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
for author in author_calls:
self.mocked_parse_author.assert_any_call(author)
if song_copyright:
self.mocked_add_copyright.assert_called_with(song_copyright)
if ccli_number:
self.assertEquals(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' %
self.assertEqual(importer.ccli_number, ccli_number, 'ccli_number for %s should be %s' %
(source_file_name, ccli_number))
for verse_text, verse_tag in add_verse_calls:
self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
if topics:
self.assertEquals(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics))
self.assertEqual(importer.topics, topics, 'topics for %s should be %s' % (source_file_name, topics))
if comments:
self.assertEquals(importer.comments, comments, 'comments for %s should be "%s"' %
self.assertEqual(importer.comments, comments, 'comments for %s should be "%s"' %
(source_file_name, comments))
if song_book_name:
self.assertEquals(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
self.assertEqual(importer.song_book_name, song_book_name, 'song_book_name for %s should be "%s"' %
(source_file_name, song_book_name))
if song_number:
self.assertEquals(importer.song_number, song_number, 'song_number for %s should be %s' %
self.assertEqual(importer.song_number, song_number, 'song_number for %s should be %s' %
(source_file_name, song_number))
if verse_order_list:
self.assertEquals(importer.verse_order_list, [], 'verse_order_list for %s should be %s' %
self.assertEqual(importer.verse_order_list, [], 'verse_order_list for %s should be %s' %
(source_file_name, verse_order_list))
self.mocked_finish.assert_called_with()

View File

@ -83,7 +83,7 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned
self.assertEquals([(54, 1, 1, -1)], results, "The bible verses should matches the expected results")
self.assertEqual([(54, 1, 1, -1)], results, "The bible verses should matches the expected results")
def parse_reference_two_test(self):
"""
@ -93,7 +93,7 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned
self.assertEquals([(54, 1, 1, 2)], results, "The bible verses should matches the expected results")
self.assertEqual([(54, 1, 1, 2)], results, "The bible verses should matches the expected results")
def parse_reference_three_test(self):
"""
@ -103,5 +103,5 @@ class TestBibleManager(TestCase, TestMixin):
# WHEN asking to parse the bible reference
results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54)
# THEN a verse array should be returned
self.assertEquals([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected "
self.assertEqual([(54, 1, 1, -1), (54, 2, 1, 1)], results, "The bible verses should matches the expected "
"results")

BIN
tests/resources/church2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
tests/resources/church3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

BIN
tests/resources/church4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

View File

@ -29,6 +29,7 @@
"""
Package to test for proper bzr tags.
"""
import os
from unittest import TestCase
@ -63,9 +64,10 @@ class TestBzrTags(TestCase):
Test for proper bzr tags
"""
# GIVEN: A bzr branch
path = os.path.dirname(__file__)
# WHEN getting the branches tags
bzr = Popen(('bzr', 'tags'), stdout=PIPE)
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
stdout = bzr.communicate()[0]
tags = [line.decode('utf-8').split() for line in stdout.splitlines()]