diff --git a/.bzrignore b/.bzrignore index f149d97a7..b91d4fc93 100644 --- a/.bzrignore +++ b/.bzrignore @@ -2,6 +2,7 @@ *.*~ \#*\# *.eric4project +*.eric5project *.ropeproject *.e4* .eric4project diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 3126d1a56..965adb053 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -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) diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index aa89da6c0..f1b875e6b 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -67,7 +67,7 @@ class ThemeScreenshotThread(QtCore.QThread): title = config.get('theme_%s' % theme, 'title') filename = config.get('theme_%s' % theme, 'filename') screenshot = config.get('theme_%s' % theme, 'screenshot') - urllib.request.urlretrieve('%s%s' % (self.parent().web, screenshot), + urllib.request.urlretrieve('%s%s' % (self.parent().themes_url, screenshot), os.path.join(gettempdir(), 'openlp', screenshot)) item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget) item.setData(QtCore.Qt.UserRole, filename) @@ -96,6 +96,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): if self.web_access: files = self.web_access.read() self.config.read_string(files.decode()) + self.web = self.config.get('general', 'base url') + self.songs_url = self.web + self.config.get('songs', 'directory') + '/' + self.bibles_url = self.web + self.config.get('bibles', 'directory') + '/' + self.themes_url = self.web + self.config.get('themes', 'directory') + '/' self.update_screen_list_combo() self.was_download_cancelled = False self.theme_screenshot_thread = None @@ -341,7 +345,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = self.songs_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename = item.data(QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.songs_url, filename)) self.max_progress += size # Loop through the Bibles list and increase for each selected item iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget) @@ -350,7 +354,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = iterator.value() if item.parent() and item.checkState(0) == QtCore.Qt.Checked: filename = item.data(0, QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.bibles_url, filename)) self.max_progress += size iterator += 1 # Loop through the themes list and increase for each selected item @@ -359,7 +363,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): item = self.themes_list_widget.item(i) if item.checkState() == QtCore.Qt.Checked: filename = item.data(QtCore.Qt.UserRole) - size = self._get_file_size('%s%s' % (self.web, filename)) + size = self._get_file_size('%s%s' % (self.themes_url, filename)) self.max_progress += size if self.max_progress: # Add on 2 for plugins status setting plus a "finished" point. @@ -435,7 +439,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): self._increment_progress_bar(self.downloading % filename, 0) self.previous_size = 0 destination = os.path.join(songs_destination, str(filename)) - self.url_get_file('%s%s' % (self.web, filename), destination) + self.url_get_file('%s%s' % (self.songs_url, filename), destination) # Download Bibles bibles_iterator = QtGui.QTreeWidgetItemIterator( self.bibles_tree_widget) @@ -445,7 +449,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): bible = item.data(0, QtCore.Qt.UserRole) self._increment_progress_bar(self.downloading % bible, 0) self.previous_size = 0 - self.url_get_file('%s%s' % (self.web, bible), os.path.join(bibles_destination, bible)) + self.url_get_file('%s%s' % (self.bibles_url, bible), os.path.join(bibles_destination, bible)) bibles_iterator += 1 # Download themes for i in range(self.themes_list_widget.count()): @@ -454,7 +458,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties): theme = item.data(QtCore.Qt.UserRole) self._increment_progress_bar(self.downloading % theme, 0) self.previous_size = 0 - self.url_get_file('%s%s' % (self.web, theme), os.path.join(themes_destination, theme)) + self.url_get_file('%s%s' % (self.themes_url, theme), os.path.join(themes_destination, theme)) # Set Default Display if self.display_combo_box.currentIndex() != -1: Settings().setValue('core/monitor', self.display_combo_box.currentIndex()) diff --git a/openlp/core/ui/maindisplay.py b/openlp/core/ui/maindisplay.py index b9423046e..946299aca 100644 --- a/openlp/core/ui/maindisplay.py +++ b/openlp/core/ui/maindisplay.py @@ -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() @@ -350,7 +352,6 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties): self.hide_display(self.hide_mode) # Only continue if the visibility wasn't changed during method call. elif was_visible == self.isVisible(): - # Single screen active if self.screens.display_count == 1: # Only make visible if setting enabled. diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py index 0b065c131..596b618cb 100644 --- a/openlp/core/ui/media/mediacontroller.py +++ b/openlp/core/ui/media/mediacontroller.py @@ -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(): diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 1796ddc11..70cbd6141 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -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): diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index ed3ddaeda..bf92e9d76 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -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. """ diff --git a/openlp/plugins/bibles/lib/db.py b/openlp/plugins/bibles/lib/db.py index c94f6ec8c..203d86406 100644 --- a/openlp/plugins/bibles/lib/db.py +++ b/openlp/plugins/bibles/lib/db.py @@ -32,9 +32,11 @@ import logging import os import re import sqlite3 +import time from PyQt4 import QtCore from sqlalchemy import Column, ForeignKey, Table, or_, types, func +from sqlalchemy.exc import OperationalError from sqlalchemy.orm import class_mapper, mapper, relation from sqlalchemy.orm.exc import UnmappedClassError @@ -235,7 +237,12 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties): text=verse_text ) self.session.add(verse) - self.session.commit() + try: + self.session.commit() + except OperationalError: + # Wait 10ms and try again (lp#1154467) + time.sleep(0.01) + self.session.commit() def create_verse(self, book_id, chapter, verse, text): """ diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 1365e59a9..5ed11d2ab 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -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: , y: " - 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) diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index fd636b468..6c8d7fb8c 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -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') diff --git a/openlp/plugins/songs/forms/duplicatesongremovalform.py b/openlp/plugins/songs/forms/duplicatesongremovalform.py index d85197223..c99dee4a7 100644 --- a/openlp/plugins/songs/forms/duplicatesongremovalform.py +++ b/openlp/plugins/songs/forms/duplicatesongremovalform.py @@ -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: diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 0106ca687..5b86591e8 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -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 diff --git a/openlp/plugins/songs/lib/songcompare.py b/openlp/plugins/songs/lib/songcompare.py index 195923b46..8a6cc7130 100644 --- a/openlp/plugins/songs/lib/songcompare.py +++ b/openlp/plugins/songs/lib/songcompare.py @@ -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): diff --git a/openlp/plugins/songs/lib/xml.py b/openlp/plugins/songs/lib/xml.py index 667afebdd..d516b5e02 100644 --- a/openlp/plugins/songs/lib/xml.py +++ b/openlp/plugins/songs/lib/xml.py @@ -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): """ diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index cf438964d..eeafbfe23 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -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: - print("Wrong token.") + print('Wrong token.') return # Open the browser before printing the output. if options.open_browser: diff --git a/tests/functional/openlp_core_common/test_registryproperties.py b/tests/functional/openlp_core_common/test_registryproperties.py index fa8a2b540..c2e430a95 100644 --- a/tests/functional/openlp_core_common/test_registryproperties.py +++ b/tests/functional/openlp_core_common/test_registryproperties.py @@ -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') diff --git a/tests/functional/openlp_core_lib/test_image_manager.py b/tests/functional/openlp_core_lib/test_image_manager.py index e9968e1c7..37b6d6fdd 100644 --- a/tests/functional/openlp_core_lib/test_image_manager.py +++ b/tests/functional/openlp_core_lib/test_image_manager.py @@ -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 '' \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_renderer.py b/tests/functional/openlp_core_lib/test_renderer.py index 51e915e3f..8814a21a0 100644 --- a/tests/functional/openlp_core_lib/test_renderer.py +++ b/tests/functional/openlp_core_lib/test_renderer.py @@ -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}', + '') + + # 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) diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 03cdd5bb1..91d59ab5a 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -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()) diff --git a/tests/functional/openlp_core_ui/test_firsttimeform.py b/tests/functional/openlp_core_ui/test_firsttimeform.py new file mode 100644 index 000000000..9fc6f5137 --- /dev/null +++ b/tests/functional/openlp_core_ui/test_firsttimeform.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 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 # +############################################################################### +""" +Package to test the openlp.core.ui.firsttimeform package. +""" +from unittest import TestCase + +from tests.functional import MagicMock + +from tests.helpers.testmixin import TestMixin +from openlp.core.common import Registry +from openlp.core.ui.firsttimeform import FirstTimeForm + + +class TestFirstTimeForm(TestCase, TestMixin): + + def setUp(self): + screens = MagicMock() + self.get_application() + Registry.create() + Registry().register('application', self.app) + self.first_time_form = FirstTimeForm(screens) + + def test_access_to_config(self): + """ + Test if we can access the First Time Form's config file + """ + # GIVEN A new First Time Form instance. + + # WHEN The default First Time Form is built. + + # THEN The First Time Form web configuration file should be accessable. + self.assertTrue(self.first_time_form.web_access, + 'First Time Wizard\'s web configuration file should be available') + + def test_parsable_config(self): + """ + Test if the First Time Form's config file is parsable + """ + # GIVEN A new First Time Form instance. + + # WHEN The default First Time Form is built. + + # THEN The First Time Form web configuration file should be parsable + self.assertTrue(self.first_time_form.songs_url, + 'First Time Wizard\'s web configuration file should be parsable') diff --git a/tests/functional/openlp_core_ui/test_maindisplay.py b/tests/functional/openlp_core_ui/test_maindisplay.py index dffc6ec16..b1a4dc7f7 100644 --- a/tests/functional/openlp_core_ui/test_maindisplay.py +++ b/tests/functional/openlp_core_ui/test_maindisplay.py @@ -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') diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py index 92af51619..060c00d02 100644 --- a/tests/functional/openlp_plugins/bibles/test_http.py +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -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']) diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index 9e327517c..c1b9db52d 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -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() diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index f6e5d98b9..2ab808bc9 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -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): """ diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index e7bd891d3..f08cedec5 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -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 should be found, value true') + self.assertEqual(self.current_verse_type, 'c', ' should be interpreted as ') + + # 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 should be found, value true') + self.assertEqual(self.current_verse_type, 'c', ' should be interpreted as ') + + # 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 should be found, value true') + self.assertEqual(self.current_verse_type, 'v1', u' should be interpreted as ') + + # 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 ') + + # 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 should be found, value false') + self.assertIsNone(self.current_verse_type, ' 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 should be found, value false') + self.assertIsNone(self.current_verse_type, ' 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') diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 7876558e9..f2839c332 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -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)) diff --git a/tests/functional/openlp_plugins/songs/test_zionworximport.py b/tests/functional/openlp_plugins/songs/test_zionworximport.py new file mode 100644 index 000000000..2edc071c7 --- /dev/null +++ b/tests/functional/openlp_plugins/songs/test_zionworximport.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 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 ZionWorx song importer. +""" + +from unittest import TestCase + +from tests.functional import MagicMock, patch +from openlp.plugins.songs.lib.zionworximport import ZionWorxImport +from openlp.plugins.songs.lib.songimport import SongImport + + +class TestZionWorxImport(TestCase): + """ + Test the functions in the :mod:`zionworximport` module. + """ + def create_importer_test(self): + """ + Test creating an instance of the ZionWorx file importer + """ + # GIVEN: A mocked out SongImport class, and a mocked out "manager" + with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'): + mocked_manager = MagicMock() + + # WHEN: An importer object is created + importer = ZionWorxImport(mocked_manager, filenames=[]) + + # THEN: The importer should be an instance of SongImport + self.assertIsInstance(importer, SongImport) diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index cc67770c1..49a09528c 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -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() diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index b085bd1df..84f80e7ed 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -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") diff --git a/tests/resources/church2.jpg b/tests/resources/church2.jpg new file mode 100644 index 000000000..826180870 Binary files /dev/null and b/tests/resources/church2.jpg differ diff --git a/tests/resources/church3.jpg b/tests/resources/church3.jpg new file mode 100644 index 000000000..826180870 Binary files /dev/null and b/tests/resources/church3.jpg differ diff --git a/tests/resources/church4.jpg b/tests/resources/church4.jpg new file mode 100644 index 000000000..826180870 Binary files /dev/null and b/tests/resources/church4.jpg differ diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index 7bb38f4ab..95017e94f 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -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()]