forked from openlp/openlp
fixes
This commit is contained in:
commit
f48a3346db
@ -2,6 +2,7 @@
|
|||||||
*.*~
|
*.*~
|
||||||
\#*\#
|
\#*\#
|
||||||
*.eric4project
|
*.eric4project
|
||||||
|
*.eric5project
|
||||||
*.ropeproject
|
*.ropeproject
|
||||||
*.e4*
|
*.e4*
|
||||||
.eric4project
|
.eric4project
|
||||||
|
@ -173,7 +173,7 @@ def create_button(parent, name, **kwargs):
|
|||||||
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
|
kwargs.setdefault('tooltip', translate('OpenLP.Ui', 'Move selection down one position.'))
|
||||||
else:
|
else:
|
||||||
log.warn('The role "%s" is not defined in create_push_button().', role)
|
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)
|
button = QtGui.QToolButton(parent)
|
||||||
else:
|
else:
|
||||||
button = QtGui.QPushButton(parent)
|
button = QtGui.QPushButton(parent)
|
||||||
|
@ -67,7 +67,7 @@ class ThemeScreenshotThread(QtCore.QThread):
|
|||||||
title = config.get('theme_%s' % theme, 'title')
|
title = config.get('theme_%s' % theme, 'title')
|
||||||
filename = config.get('theme_%s' % theme, 'filename')
|
filename = config.get('theme_%s' % theme, 'filename')
|
||||||
screenshot = config.get('theme_%s' % theme, 'screenshot')
|
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))
|
os.path.join(gettempdir(), 'openlp', screenshot))
|
||||||
item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget)
|
item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget)
|
||||||
item.setData(QtCore.Qt.UserRole, filename)
|
item.setData(QtCore.Qt.UserRole, filename)
|
||||||
@ -96,6 +96,10 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
|
|||||||
if self.web_access:
|
if self.web_access:
|
||||||
files = self.web_access.read()
|
files = self.web_access.read()
|
||||||
self.config.read_string(files.decode())
|
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.update_screen_list_combo()
|
||||||
self.was_download_cancelled = False
|
self.was_download_cancelled = False
|
||||||
self.theme_screenshot_thread = None
|
self.theme_screenshot_thread = None
|
||||||
@ -341,7 +345,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
|
|||||||
item = self.songs_list_widget.item(i)
|
item = self.songs_list_widget.item(i)
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
filename = item.data(QtCore.Qt.UserRole)
|
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
|
self.max_progress += size
|
||||||
# Loop through the Bibles list and increase for each selected item
|
# Loop through the Bibles list and increase for each selected item
|
||||||
iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
|
iterator = QtGui.QTreeWidgetItemIterator(self.bibles_tree_widget)
|
||||||
@ -350,7 +354,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
|
|||||||
item = iterator.value()
|
item = iterator.value()
|
||||||
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
if item.parent() and item.checkState(0) == QtCore.Qt.Checked:
|
||||||
filename = item.data(0, QtCore.Qt.UserRole)
|
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
|
self.max_progress += size
|
||||||
iterator += 1
|
iterator += 1
|
||||||
# Loop through the themes list and increase for each selected item
|
# 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)
|
item = self.themes_list_widget.item(i)
|
||||||
if item.checkState() == QtCore.Qt.Checked:
|
if item.checkState() == QtCore.Qt.Checked:
|
||||||
filename = item.data(QtCore.Qt.UserRole)
|
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
|
self.max_progress += size
|
||||||
if self.max_progress:
|
if self.max_progress:
|
||||||
# Add on 2 for plugins status setting plus a "finished" point.
|
# 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._increment_progress_bar(self.downloading % filename, 0)
|
||||||
self.previous_size = 0
|
self.previous_size = 0
|
||||||
destination = os.path.join(songs_destination, str(filename))
|
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
|
# Download Bibles
|
||||||
bibles_iterator = QtGui.QTreeWidgetItemIterator(
|
bibles_iterator = QtGui.QTreeWidgetItemIterator(
|
||||||
self.bibles_tree_widget)
|
self.bibles_tree_widget)
|
||||||
@ -445,7 +449,7 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard, RegistryProperties):
|
|||||||
bible = item.data(0, QtCore.Qt.UserRole)
|
bible = item.data(0, QtCore.Qt.UserRole)
|
||||||
self._increment_progress_bar(self.downloading % bible, 0)
|
self._increment_progress_bar(self.downloading % bible, 0)
|
||||||
self.previous_size = 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
|
bibles_iterator += 1
|
||||||
# Download themes
|
# Download themes
|
||||||
for i in range(self.themes_list_widget.count()):
|
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)
|
theme = item.data(QtCore.Qt.UserRole)
|
||||||
self._increment_progress_bar(self.downloading % theme, 0)
|
self._increment_progress_bar(self.downloading % theme, 0)
|
||||||
self.previous_size = 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
|
# Set Default Display
|
||||||
if self.display_combo_box.currentIndex() != -1:
|
if self.display_combo_box.currentIndex() != -1:
|
||||||
Settings().setValue('core/monitor', self.display_combo_box.currentIndex())
|
Settings().setValue('core/monitor', self.display_combo_box.currentIndex())
|
||||||
|
@ -168,8 +168,10 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
if enabled:
|
if enabled:
|
||||||
self.setAutoFillBackground(False)
|
self.setAutoFillBackground(False)
|
||||||
|
self.setStyleSheet("QGraphicsView {background: transparent; border: 0px;}")
|
||||||
else:
|
else:
|
||||||
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
|
self.setAttribute(QtCore.Qt.WA_NoSystemBackground, False)
|
||||||
|
self.setStyleSheet("QGraphicsView {}")
|
||||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
|
self.setAttribute(QtCore.Qt.WA_TranslucentBackground, enabled)
|
||||||
self.repaint()
|
self.repaint()
|
||||||
|
|
||||||
@ -350,7 +352,6 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
self.hide_display(self.hide_mode)
|
self.hide_display(self.hide_mode)
|
||||||
# Only continue if the visibility wasn't changed during method call.
|
# Only continue if the visibility wasn't changed during method call.
|
||||||
elif was_visible == self.isVisible():
|
elif was_visible == self.isVisible():
|
||||||
|
|
||||||
# Single screen active
|
# Single screen active
|
||||||
if self.screens.display_count == 1:
|
if self.screens.display_count == 1:
|
||||||
# Only make visible if setting enabled.
|
# Only make visible if setting enabled.
|
||||||
|
@ -506,7 +506,8 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
|||||||
else:
|
else:
|
||||||
self.media_volume(controller, controller.media_info.volume)
|
self.media_volume(controller, controller.media_info.volume)
|
||||||
if status:
|
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)
|
self.current_media_players[controller.controller_type].set_visible(display, True)
|
||||||
# Flash needs to be played and will not AutoPlay
|
# Flash needs to be played and will not AutoPlay
|
||||||
if controller.media_info.is_flash:
|
if controller.media_info.is_flash:
|
||||||
@ -517,7 +518,7 @@ class MediaController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
|||||||
controller.mediabar.actions['playbackPause'].setVisible(True)
|
controller.mediabar.actions['playbackPause'].setVisible(True)
|
||||||
controller.mediabar.actions['playbackStop'].setVisible(True)
|
controller.mediabar.actions['playbackStop'].setVisible(True)
|
||||||
if controller.is_live:
|
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()
|
controller.hide_menu.defaultAction().trigger()
|
||||||
# Start Timer for ui updates
|
# Start Timer for ui updates
|
||||||
if not self.timer.isActive():
|
if not self.timer.isActive():
|
||||||
|
@ -1489,9 +1489,11 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
|
|||||||
if new_item:
|
if new_item:
|
||||||
self.add_service_item(new_item, replace=True)
|
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.
|
Opens a dialog to rename the service item.
|
||||||
|
|
||||||
|
:param field: Not used, but PyQt needs this.
|
||||||
"""
|
"""
|
||||||
item = self.find_service_item()[0]
|
item = self.find_service_item()[0]
|
||||||
if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle):
|
if not self.service_items[item]['service_item'].is_capable(ItemCapabilities.CanEditTitle):
|
||||||
|
@ -495,14 +495,14 @@ class SlideController(DisplayController, RegistryProperties):
|
|||||||
self.on_theme_display(False)
|
self.on_theme_display(False)
|
||||||
self.on_hide_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.
|
Live event to select the previous service item from the service manager.
|
||||||
"""
|
"""
|
||||||
self.keypress_queue.append(ServiceItemAction.Previous)
|
self.keypress_queue.append(ServiceItemAction.Previous)
|
||||||
self._process_queue()
|
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.
|
Live event to select the next service item from the service manager.
|
||||||
"""
|
"""
|
||||||
|
@ -32,9 +32,11 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import time
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PyQt4 import QtCore
|
||||||
from sqlalchemy import Column, ForeignKey, Table, or_, types, func
|
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 import class_mapper, mapper, relation
|
||||||
from sqlalchemy.orm.exc import UnmappedClassError
|
from sqlalchemy.orm.exc import UnmappedClassError
|
||||||
|
|
||||||
@ -235,7 +237,12 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
|
|||||||
text=verse_text
|
text=verse_text
|
||||||
)
|
)
|
||||||
self.session.add(verse)
|
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):
|
def create_verse(self, book_id, chapter, verse, text):
|
||||||
"""
|
"""
|
||||||
|
@ -204,19 +204,19 @@ class PdfDocument(PresentationDocument):
|
|||||||
log.debug(' '.join(e.cmd))
|
log.debug(' '.join(e.cmd))
|
||||||
log.debug(e.output)
|
log.debug(e.output)
|
||||||
# Extract the pdf resolution from output, the format is " Size: x: <width>, y: <height>"
|
# Extract the pdf resolution from output, the format is " Size: x: <width>, y: <height>"
|
||||||
width = 0
|
width = 0.0
|
||||||
height = 0
|
height = 0.0
|
||||||
for line in runlog.splitlines():
|
for line in runlog.splitlines():
|
||||||
try:
|
try:
|
||||||
width = int(re.search('.*Size: x: (\d+\.?\d*), y: \d+.*', line.decode()).group(1))
|
width = float(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))
|
height = float(re.search('.*Size: x: \d+\.?\d*, y: (\d+\.?\d*).*', line.decode()).group(1))
|
||||||
break
|
break
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
continue
|
||||||
# Calculate the ratio from pdf to screen
|
# Calculate the ratio from pdf to screen
|
||||||
if width > 0 and height > 0:
|
if width > 0 and height > 0:
|
||||||
width_ratio = size.right() / float(width)
|
width_ratio = size.right() / width
|
||||||
height_ratio = size.bottom() / float(height)
|
height_ratio = size.bottom() / height
|
||||||
# return the resolution that should be used. 72 is default.
|
# return the resolution that should be used. 72 is default.
|
||||||
if width_ratio > height_ratio:
|
if width_ratio > height_ratio:
|
||||||
return int(height_ratio * 72)
|
return int(height_ratio * 72)
|
||||||
|
@ -122,8 +122,10 @@ class PresentationDocument(object):
|
|||||||
a file, e.g. thumbnails
|
a file, e.g. thumbnails
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(self.get_thumbnail_folder())
|
if os.path.exists(self.get_thumbnail_folder()):
|
||||||
shutil.rmtree(self.get_temp_folder())
|
shutil.rmtree(self.get_thumbnail_folder())
|
||||||
|
if os.path.exists(self.get_temp_folder()):
|
||||||
|
shutil.rmtree(self.get_temp_folder())
|
||||||
except OSError:
|
except OSError:
|
||||||
log.exception('Failed to delete presentation controller files')
|
log.exception('Failed to delete presentation controller files')
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ The duplicate song removal logic for OpenLP.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui
|
||||||
@ -45,6 +46,18 @@ from openlp.plugins.songs.lib.songcompare import songs_probably_equal
|
|||||||
log = logging.getLogger(__name__)
|
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):
|
class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
|
||||||
"""
|
"""
|
||||||
This is the Duplicate Song Removal Wizard. It provides functionality to search for and remove duplicate songs
|
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
|
max_progress_count = max_songs * (max_songs - 1) // 2
|
||||||
self.duplicate_search_progress_bar.setMaximum(max_progress_count)
|
self.duplicate_search_progress_bar.setMaximum(max_progress_count)
|
||||||
songs = self.plugin.manager.get_all_objects(Song)
|
songs = self.plugin.manager.get_all_objects(Song)
|
||||||
for outer_song_counter in range(max_songs - 1):
|
# Create a worker/process pool to check the songs.
|
||||||
for inner_song_counter in range(outer_song_counter + 1, max_songs):
|
process_number = max(1, multiprocessing.cpu_count() - 1)
|
||||||
if songs_probably_equal(songs[outer_song_counter], songs[inner_song_counter]):
|
pool = multiprocessing.Pool(process_number)
|
||||||
duplicate_added = self.add_duplicates_to_song_list(
|
result = pool.imap_unordered(songs_probably_equal, song_generator(songs), 30)
|
||||||
songs[outer_song_counter], songs[inner_song_counter])
|
# Do not accept any further tasks. Also this closes the processes if all tasks are done.
|
||||||
if duplicate_added:
|
pool.close()
|
||||||
self.found_duplicates_edit.appendPlainText(
|
# While the processes are still working, start to look at the results.
|
||||||
songs[outer_song_counter].title + " = " + songs[inner_song_counter].title)
|
for song_tuple in result:
|
||||||
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
|
self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
|
||||||
# The call to process_events() will keep the GUI responsive.
|
# The call to process_events() will keep the GUI responsive.
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
if self.break_search:
|
if self.break_search:
|
||||||
return
|
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)
|
self.review_total_count = len(self.duplicate_song_list)
|
||||||
if self.review_total_count == 0:
|
if self.duplicate_song_list:
|
||||||
self.notify_no_duplicates()
|
|
||||||
else:
|
|
||||||
self.button(QtGui.QWizard.NextButton).show()
|
self.button(QtGui.QWizard.NextButton).show()
|
||||||
|
else:
|
||||||
|
self.notify_no_duplicates()
|
||||||
finally:
|
finally:
|
||||||
self.application.set_normal_cursor()
|
self.application.set_normal_cursor()
|
||||||
elif page_id == self.review_page_id:
|
elif page_id == self.review_page_id:
|
||||||
|
@ -56,11 +56,15 @@ class SongBeamerTypes(object):
|
|||||||
'Zwischenspiel': VerseType.tags[VerseType.Bridge],
|
'Zwischenspiel': VerseType.tags[VerseType.Bridge],
|
||||||
'Pre-Chorus': VerseType.tags[VerseType.PreChorus],
|
'Pre-Chorus': VerseType.tags[VerseType.PreChorus],
|
||||||
'Pre-Refrain': VerseType.tags[VerseType.PreChorus],
|
'Pre-Refrain': VerseType.tags[VerseType.PreChorus],
|
||||||
|
'Misc': VerseType.tags[VerseType.Other],
|
||||||
'Pre-Bridge': VerseType.tags[VerseType.Other],
|
'Pre-Bridge': VerseType.tags[VerseType.Other],
|
||||||
'Pre-Coda': 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],
|
'Unbekannt': VerseType.tags[VerseType.Other],
|
||||||
'Unknown': 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()
|
line = str(line).strip()
|
||||||
if line.startswith('#') and not read_verses:
|
if line.startswith('#') and not read_verses:
|
||||||
self.parseTags(line)
|
self.parseTags(line)
|
||||||
elif line.startswith('---'):
|
elif line.startswith('--'):
|
||||||
|
# --- and -- allowed for page-breaks (difference in Songbeamer only in printout)
|
||||||
if self.current_verse:
|
if self.current_verse:
|
||||||
self.replace_html_tags()
|
self.replace_html_tags()
|
||||||
self.add_verse(self.current_verse, self.current_verse_type)
|
self.add_verse(self.current_verse, self.current_verse_type)
|
||||||
@ -282,4 +287,7 @@ class SongBeamerImport(SongImport):
|
|||||||
if marks[1].isdigit():
|
if marks[1].isdigit():
|
||||||
self.current_verse_type += marks[1]
|
self.current_verse_type += marks[1]
|
||||||
return True
|
return True
|
||||||
|
elif marks[0].startswith('$$M='): # this verse-mark cannot be numbered
|
||||||
|
self.current_verse_type = SongBeamerTypes.MarkTypes['$$M=']
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -52,13 +52,13 @@ MIN_BLOCK_SIZE = 70
|
|||||||
MAX_TYPO_SIZE = 3
|
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.
|
Calculate and return whether two songs are probably equal.
|
||||||
|
|
||||||
:param song1: The first song to compare.
|
:param song_tupel: A tuple of two songs to compare.
|
||||||
:param song2: The second song to compare.
|
|
||||||
"""
|
"""
|
||||||
|
song1, song2 = song_tupel
|
||||||
if len(song1.search_lyrics) < len(song2.search_lyrics):
|
if len(song1.search_lyrics) < len(song2.search_lyrics):
|
||||||
small = song1.search_lyrics
|
small = song1.search_lyrics
|
||||||
large = song2.search_lyrics
|
large = song2.search_lyrics
|
||||||
@ -75,18 +75,19 @@ def songs_probably_equal(song1, song2):
|
|||||||
for element in diff_no_typos:
|
for element in diff_no_typos:
|
||||||
if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE:
|
if element[0] == "equal" and _op_length(element) >= MIN_BLOCK_SIZE:
|
||||||
length_of_equal_blocks += _op_length(element)
|
length_of_equal_blocks += _op_length(element)
|
||||||
|
|
||||||
if length_of_equal_blocks >= MIN_BLOCK_SIZE:
|
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.
|
# 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.
|
# Calculate the length of the largest equal block of the diff set.
|
||||||
length_of_longest_equal_block = 0
|
length_of_longest_equal_block = 0
|
||||||
for element in diff_no_typos:
|
for element in diff_no_typos:
|
||||||
if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
|
if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
|
||||||
length_of_longest_equal_block = _op_length(element)
|
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:
|
if length_of_longest_equal_block > len(small) * 2 // 3:
|
||||||
return True
|
return song1, song2
|
||||||
# Both checks failed. We assume the songs are not equal.
|
# Both checks failed. We assume the songs are not equal.
|
||||||
return False
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _op_length(opcode):
|
def _op_length(opcode):
|
||||||
|
@ -675,6 +675,7 @@ class OpenLyrics(object):
|
|||||||
sxml = SongXML()
|
sxml = SongXML()
|
||||||
verses = {}
|
verses = {}
|
||||||
verse_def_list = []
|
verse_def_list = []
|
||||||
|
verse_order = self._text(properties.verseOrder).split(' ') if hasattr(properties, 'verseOrder') else []
|
||||||
try:
|
try:
|
||||||
lyrics = song_xml.lyrics
|
lyrics = song_xml.lyrics
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
@ -717,13 +718,17 @@ class OpenLyrics(object):
|
|||||||
else:
|
else:
|
||||||
verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
|
verses[(verse_tag, verse_number, lang, translit, verse_part)] = text
|
||||||
verse_def_list.append((verse_tag, verse_number, lang, translit, verse_part))
|
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.
|
# We have to use a list to keep the order, as dicts are not sorted.
|
||||||
for verse in verse_def_list:
|
for verse in verse_def_list:
|
||||||
sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2])
|
sxml.add_verse_to_lyrics(verse[0], verse[1], verses[verse], verse[2])
|
||||||
song_obj.lyrics = str(sxml.extract_xml(), 'utf-8')
|
song_obj.lyrics = str(sxml.extract_xml(), 'utf-8')
|
||||||
# Process verse order
|
# Process verse order
|
||||||
if hasattr(properties, 'verseOrder'):
|
song_obj.verse_order = ' '.join(verse_order)
|
||||||
song_obj.verse_order = self._text(properties.verseOrder)
|
|
||||||
|
|
||||||
def _process_songbooks(self, properties, song):
|
def _process_songbooks(self, properties, song):
|
||||||
"""
|
"""
|
||||||
|
@ -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
|
from optparse import OptionParser
|
||||||
|
import re
|
||||||
from requests.exceptions import HTTPError
|
from requests.exceptions import HTTPError
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
import sys
|
import sys
|
||||||
@ -49,6 +50,9 @@ from jenkins import Jenkins
|
|||||||
|
|
||||||
|
|
||||||
JENKINS_URL = 'http://ci.openlp.org/'
|
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):
|
class OpenLPJobs(object):
|
||||||
@ -59,9 +63,20 @@ class OpenLPJobs(object):
|
|||||||
Branch_Functional = 'Branch-02-Functional-Tests'
|
Branch_Functional = 'Branch-02-Functional-Tests'
|
||||||
Branch_Interface = 'Branch-03-Interface-Tests'
|
Branch_Interface = 'Branch-03-Interface-Tests'
|
||||||
Branch_Windows = 'Branch-04-Windows_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):
|
class JenkinsTrigger(object):
|
||||||
@ -79,14 +94,25 @@ class JenkinsTrigger(object):
|
|||||||
"""
|
"""
|
||||||
Ask our jenkins server to build the "Branch-01-Pull" job.
|
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):
|
def print_output(self):
|
||||||
"""
|
"""
|
||||||
Print the status information of the build tirggered.
|
Print the status information of the build tirggered.
|
||||||
"""
|
"""
|
||||||
print("Add this to your merge proposal:")
|
print('Add this to your merge proposal:')
|
||||||
print("--------------------------------")
|
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:
|
for job in OpenLPJobs.Jobs:
|
||||||
self.__print_build_info(job)
|
self.__print_build_info(job)
|
||||||
|
|
||||||
@ -107,17 +133,17 @@ class JenkinsTrigger(object):
|
|||||||
"""
|
"""
|
||||||
job = self.jenkins_instance.job(job_name)
|
job = self.jenkins_instance.job(job_name)
|
||||||
while job.info['inQueue']:
|
while job.info['inQueue']:
|
||||||
# Give other processes the possibility to take over. Like Thread.yield().
|
time.sleep(1)
|
||||||
time.sleep(0)
|
|
||||||
build = job.last_build
|
build = job.last_build
|
||||||
build.wait()
|
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']
|
url = build.info['url']
|
||||||
print('[%s] %s' % (result_string, 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():
|
def get_repo_name():
|
||||||
@ -139,46 +165,41 @@ def get_repo_name():
|
|||||||
for line in output_list:
|
for line in output_list:
|
||||||
# Check if it is remote branch.
|
# Check if it is remote branch.
|
||||||
if 'push branch' in line:
|
if 'push branch' in line:
|
||||||
repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
|
match = re.match(REPO_REGEX, line)
|
||||||
break
|
if match:
|
||||||
|
repo_name = 'lp:%s' % match.group(2)
|
||||||
|
break
|
||||||
elif 'checkout of branch' in line:
|
elif 'checkout of branch' in line:
|
||||||
repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
|
match = re.match(REPO_REGEX, line)
|
||||||
break
|
if match:
|
||||||
repo_name = repo_name.strip('/')
|
repo_name = 'lp:%s' % match.group(2)
|
||||||
|
break
|
||||||
# Did we find the branch name?
|
return repo_name.strip('/')
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
usage = 'Usage: python %prog TOKEN [options]'
|
usage = 'Usage: python %prog TOKEN [options]'
|
||||||
|
|
||||||
parser = OptionParser(usage=usage)
|
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.')
|
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.')
|
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)
|
options, args = parser.parse_args(sys.argv)
|
||||||
|
|
||||||
if len(args) == 2:
|
if len(args) == 2:
|
||||||
if not get_repo_name():
|
if not get_repo_name():
|
||||||
|
print('Not a branch. Have you pushed it to launchpad? Did you cd to the branch?')
|
||||||
return
|
return
|
||||||
token = args[-1]
|
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)
|
jenkins_trigger = JenkinsTrigger(token)
|
||||||
try:
|
try:
|
||||||
jenkins_trigger.trigger_build()
|
jenkins_trigger.trigger_build()
|
||||||
except HTTPError:
|
except HTTPError:
|
||||||
print("Wrong token.")
|
print('Wrong token.')
|
||||||
return
|
return
|
||||||
# Open the browser before printing the output.
|
# Open the browser before printing the output.
|
||||||
if options.open_browser:
|
if options.open_browser:
|
||||||
|
@ -52,7 +52,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
|
|||||||
# GIVEN an Empty Registry
|
# GIVEN an Empty Registry
|
||||||
# WHEN there is no Application
|
# WHEN there is no Application
|
||||||
# THEN the application should be none
|
# 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):
|
def application_test(self):
|
||||||
"""
|
"""
|
||||||
@ -63,4 +63,4 @@ class TestRegistryProperties(TestCase, RegistryProperties):
|
|||||||
# WHEN the application is registered
|
# WHEN the application is registered
|
||||||
Registry().register('application', application)
|
Registry().register('application', application)
|
||||||
# THEN the application should be none
|
# 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')
|
||||||
|
@ -30,12 +30,16 @@
|
|||||||
Package to test the openlp.core.ui package.
|
Package to test the openlp.core.ui package.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
|
import time
|
||||||
|
from threading import Lock
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui
|
||||||
|
|
||||||
from openlp.core.common import Registry
|
from openlp.core.common import Registry
|
||||||
from openlp.core.lib import ImageManager, ScreenList
|
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
|
from tests.helpers.testmixin import TestMixin
|
||||||
|
|
||||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
|
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'resources'))
|
||||||
@ -51,6 +55,8 @@ class TestImageManager(TestCase, TestMixin):
|
|||||||
self.get_application()
|
self.get_application()
|
||||||
ScreenList.create(self.app.desktop())
|
ScreenList.create(self.app.desktop())
|
||||||
self.image_manager = ImageManager()
|
self.image_manager = ImageManager()
|
||||||
|
self.lock = Lock()
|
||||||
|
self.sleep_time = 0.1
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
"""
|
"""
|
||||||
@ -82,3 +88,87 @@ class TestImageManager(TestCase, TestMixin):
|
|||||||
with self.assertRaises(KeyError) as context:
|
with self.assertRaises(KeyError) as context:
|
||||||
self.image_manager.get_image(TEST_PATH, 'church1.jpg')
|
self.image_manager.get_image(TEST_PATH, 'church1.jpg')
|
||||||
self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image')
|
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 ''
|
@ -49,7 +49,7 @@ class TestRenderer(TestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
"""
|
"""
|
||||||
Set up the components need for all tests.
|
Set up the components need for all tests
|
||||||
"""
|
"""
|
||||||
# Mocked out desktop object
|
# Mocked out desktop object
|
||||||
self.desktop = MagicMock()
|
self.desktop = MagicMock()
|
||||||
@ -67,7 +67,7 @@ class TestRenderer(TestCase):
|
|||||||
|
|
||||||
def initial_renderer_test(self):
|
def initial_renderer_test(self):
|
||||||
"""
|
"""
|
||||||
Test the initial renderer state .
|
Test the initial renderer state
|
||||||
"""
|
"""
|
||||||
# GIVEN: A new renderer instance.
|
# GIVEN: A new renderer instance.
|
||||||
renderer = Renderer()
|
renderer = Renderer()
|
||||||
@ -77,7 +77,7 @@ class TestRenderer(TestCase):
|
|||||||
|
|
||||||
def default_screen_layout_test(self):
|
def default_screen_layout_test(self):
|
||||||
"""
|
"""
|
||||||
Test the default layout calculations.
|
Test the default layout calculations
|
||||||
"""
|
"""
|
||||||
# GIVEN: A new renderer instance.
|
# GIVEN: A new renderer instance.
|
||||||
renderer = Renderer()
|
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.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.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')
|
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)
|
||||||
|
@ -82,6 +82,38 @@ class TestUi(TestCase):
|
|||||||
self.assertEqual(1, len(btnbox.buttons()))
|
self.assertEqual(1, len(btnbox.buttons()))
|
||||||
self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]))
|
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):
|
def test_create_valign_selection_widgets(self):
|
||||||
"""
|
"""
|
||||||
Test creating a combo box for valign selection
|
Test creating a combo box for valign selection
|
||||||
@ -98,3 +130,43 @@ class TestUi(TestCase):
|
|||||||
self.assertEqual(combo, label.buddy())
|
self.assertEqual(combo, label.buddy())
|
||||||
for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]:
|
for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]:
|
||||||
self.assertTrue(combo.findText(text) >= 0)
|
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())
|
||||||
|
72
tests/functional/openlp_core_ui/test_firsttimeform.py
Normal file
72
tests/functional/openlp_core_ui/test_firsttimeform.py
Normal file
@ -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')
|
@ -79,3 +79,31 @@ class TestMainDisplay(TestCase):
|
|||||||
|
|
||||||
# THEN: The controller should not be a live controller.
|
# THEN: The controller should not be a live controller.
|
||||||
self.assertEqual(main_display.is_live, True, 'The main display should 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')
|
||||||
|
@ -180,4 +180,4 @@ class TestBSExtract(TestCase):
|
|||||||
'http://m.bibleserver.com/overlay/selectBook?translation=NIV')
|
'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_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.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'])
|
||||||
|
@ -139,10 +139,10 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
|
|
||||||
# THEN:
|
# THEN:
|
||||||
self.assertIsNotNone(field_desc_entry, 'Import should not be none')
|
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.assertEqual(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.field_type, field_type,
|
||||||
'FieldDescEntry.type should be the same as the type argument')
|
'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):
|
def create_importer_test(self):
|
||||||
"""
|
"""
|
||||||
@ -174,7 +174,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
for field_name in existing_fields:
|
for field_name in existing_fields:
|
||||||
|
|
||||||
# THEN: The item corresponding the index returned should have the same name attribute
|
# 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):
|
def find_non_existing_field_test(self):
|
||||||
"""
|
"""
|
||||||
@ -230,7 +230,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
return_value = importer.get_field(field_index)
|
return_value = importer.get_field(field_index)
|
||||||
|
|
||||||
# THEN: get_field should return the known results
|
# 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"' %
|
'get_field should return "%s" when called with "%s"' %
|
||||||
(result, TEST_FIELDS[field_index]))
|
(result, TEST_FIELDS[field_index]))
|
||||||
|
|
||||||
@ -257,7 +257,7 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
get_field_seek_calls = test_results[2]['seek']
|
get_field_seek_calls = test_results[2]['seek']
|
||||||
|
|
||||||
# THEN: get_field should return the appropriate value with the appropriate mocked objects being called
|
# 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:
|
for call in get_field_read_calls:
|
||||||
mocked_memo_file.read.assert_any_call(call)
|
mocked_memo_file.read.assert_any_call(call)
|
||||||
for call in get_field_seek_calls:
|
for call in get_field_seek_calls:
|
||||||
@ -403,11 +403,11 @@ class TestEasyWorshipSongImport(TestCase):
|
|||||||
if song_copyright:
|
if song_copyright:
|
||||||
self.assertEqual(importer.copyright, song_copyright)
|
self.assertEqual(importer.copyright, song_copyright)
|
||||||
if ccli_number:
|
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))
|
% (title, ccli_number))
|
||||||
for verse_text, verse_tag in add_verse_calls:
|
for verse_text, verse_tag in add_verse_calls:
|
||||||
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
if verse_order_list:
|
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))
|
'verse_order_list for %s should be %s' % (title, verse_order_list))
|
||||||
mocked_finish.assert_called_with()
|
mocked_finish.assert_called_with()
|
||||||
|
@ -96,10 +96,10 @@ class TestLib(TestCase):
|
|||||||
self.song2.search_lyrics = self.full_lyrics
|
self.song2.search_lyrics = self.full_lyrics
|
||||||
|
|
||||||
# WHEN: We compare those songs for equality.
|
# 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.
|
# THEN: The result should be a tuple..
|
||||||
assert result is True, 'The result should be True'
|
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
|
||||||
|
|
||||||
def songs_probably_equal_short_song_test(self):
|
def songs_probably_equal_short_song_test(self):
|
||||||
"""
|
"""
|
||||||
@ -110,10 +110,10 @@ class TestLib(TestCase):
|
|||||||
self.song2.search_lyrics = self.short_lyrics
|
self.song2.search_lyrics = self.short_lyrics
|
||||||
|
|
||||||
# WHEN: We compare those songs for equality.
|
# 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.
|
# THEN: The result should be a tuple..
|
||||||
assert result is True, 'The result should be True'
|
assert result == (self.song1, self.song2), 'The result should be the tuble of songs'
|
||||||
|
|
||||||
def songs_probably_equal_error_song_test(self):
|
def songs_probably_equal_error_song_test(self):
|
||||||
"""
|
"""
|
||||||
@ -124,10 +124,11 @@ class TestLib(TestCase):
|
|||||||
self.song2.search_lyrics = self.error_lyrics
|
self.song2.search_lyrics = self.error_lyrics
|
||||||
|
|
||||||
# WHEN: We compare those songs for equality.
|
# 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):
|
def songs_probably_equal_different_song_test(self):
|
||||||
"""
|
"""
|
||||||
@ -138,10 +139,10 @@ class TestLib(TestCase):
|
|||||||
self.song2.search_lyrics = self.different_lyrics
|
self.song2.search_lyrics = self.different_lyrics
|
||||||
|
|
||||||
# WHEN: We compare those songs for equality.
|
# 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.
|
# THEN: The result should be None.
|
||||||
assert result is False, 'The result should be False'
|
assert result is None, 'The result should be None'
|
||||||
|
|
||||||
def remove_typos_beginning_test(self):
|
def remove_typos_beginning_test(self):
|
||||||
"""
|
"""
|
||||||
|
@ -35,6 +35,7 @@ from unittest import TestCase
|
|||||||
|
|
||||||
from tests.functional import MagicMock, patch
|
from tests.functional import MagicMock, patch
|
||||||
from openlp.plugins.songs.lib.songbeamerimport import SongBeamerImport
|
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__),
|
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__),
|
||||||
'..', '..', '..', 'resources', 'songbeamersongs'))
|
'..', '..', '..', 'resources', 'songbeamersongs'))
|
||||||
@ -89,7 +90,7 @@ class TestSongBeamerImport(TestCase):
|
|||||||
|
|
||||||
# THEN: do_import should return none and the progress bar maximum should not be set.
|
# 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.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')
|
'setMaxium on import_wizard.progress_bar should not have been called')
|
||||||
|
|
||||||
def valid_import_source_test(self):
|
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
|
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||||
# called.
|
# called.
|
||||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
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:
|
for verse_text, verse_tag in add_verse_calls:
|
||||||
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
if song_book_name:
|
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))
|
(song_file, song_book_name))
|
||||||
if song_number:
|
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))
|
(song_file, song_number))
|
||||||
mocked_finish.assert_called_with()
|
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')
|
||||||
|
@ -95,7 +95,7 @@ class TestSongShowPlusImport(TestCase):
|
|||||||
|
|
||||||
# THEN: do_import should return none and the progress bar maximum should not be set.
|
# 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.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')
|
'setMaximum on import_wizard.progress_bar should not have been called')
|
||||||
|
|
||||||
def valid_import_source_test(self):
|
def valid_import_source_test(self):
|
||||||
@ -143,7 +143,7 @@ class TestSongShowPlusImport(TestCase):
|
|||||||
|
|
||||||
# THEN: The returned value should should correlate with the input arguments
|
# THEN: The returned value should should correlate with the input arguments
|
||||||
for original_tag, openlp_tag in test_values:
|
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"' %
|
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
|
||||||
(openlp_tag, original_tag))
|
(openlp_tag, original_tag))
|
||||||
|
|
||||||
@ -172,6 +172,6 @@ class TestSongShowPlusImport(TestCase):
|
|||||||
|
|
||||||
# THEN: The returned value should should correlate with the input arguments
|
# THEN: The returned value should should correlate with the input arguments
|
||||||
for original_tag, openlp_tag in test_values:
|
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"' %
|
'SongShowPlusImport.to_openlp_verse_tag should return "%s" when called with "%s"' %
|
||||||
(openlp_tag, original_tag))
|
(openlp_tag, original_tag))
|
||||||
|
56
tests/functional/openlp_plugins/songs/test_zionworximport.py
Normal file
56
tests/functional/openlp_plugins/songs/test_zionworximport.py
Normal file
@ -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)
|
@ -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
|
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||||
# called.
|
# called.
|
||||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
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:
|
for author in author_calls:
|
||||||
self.mocked_parse_author.assert_any_call(author)
|
self.mocked_parse_author.assert_any_call(author)
|
||||||
if song_copyright:
|
if song_copyright:
|
||||||
self.mocked_add_copyright.assert_called_with(song_copyright)
|
self.mocked_add_copyright.assert_called_with(song_copyright)
|
||||||
if ccli_number:
|
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))
|
(source_file_name, ccli_number))
|
||||||
for verse_text, verse_tag in add_verse_calls:
|
for verse_text, verse_tag in add_verse_calls:
|
||||||
self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
self.mocked_add_verse.assert_any_call(verse_text, verse_tag)
|
||||||
if topics:
|
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:
|
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))
|
(source_file_name, comments))
|
||||||
if song_book_name:
|
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))
|
(source_file_name, song_book_name))
|
||||||
if song_number:
|
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))
|
(source_file_name, song_number))
|
||||||
if verse_order_list:
|
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))
|
(source_file_name, verse_order_list))
|
||||||
self.mocked_finish.assert_called_with()
|
self.mocked_finish.assert_called_with()
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ class TestBibleManager(TestCase, TestMixin):
|
|||||||
# WHEN asking to parse the bible reference
|
# WHEN asking to parse the bible reference
|
||||||
results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54)
|
results = parse_reference('1 Timothy 1', self.manager.db_cache['tests'], MagicMock(), 54)
|
||||||
# THEN a verse array should be returned
|
# 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):
|
def parse_reference_two_test(self):
|
||||||
"""
|
"""
|
||||||
@ -93,7 +93,7 @@ class TestBibleManager(TestCase, TestMixin):
|
|||||||
# WHEN asking to parse the bible reference
|
# WHEN asking to parse the bible reference
|
||||||
results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54)
|
results = parse_reference('1 Timothy 1:1-2', self.manager.db_cache['tests'], MagicMock(), 54)
|
||||||
# THEN a verse array should be returned
|
# 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):
|
def parse_reference_three_test(self):
|
||||||
"""
|
"""
|
||||||
@ -103,5 +103,5 @@ class TestBibleManager(TestCase, TestMixin):
|
|||||||
# WHEN asking to parse the bible reference
|
# WHEN asking to parse the bible reference
|
||||||
results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54)
|
results = parse_reference('1 Timothy 1:1-2:1', self.manager.db_cache['tests'], MagicMock(), 54)
|
||||||
# THEN a verse array should be returned
|
# 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")
|
"results")
|
||||||
|
BIN
tests/resources/church2.jpg
Normal file
BIN
tests/resources/church2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 213 KiB |
BIN
tests/resources/church3.jpg
Normal file
BIN
tests/resources/church3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 213 KiB |
BIN
tests/resources/church4.jpg
Normal file
BIN
tests/resources/church4.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 213 KiB |
@ -29,6 +29,7 @@
|
|||||||
"""
|
"""
|
||||||
Package to test for proper bzr tags.
|
Package to test for proper bzr tags.
|
||||||
"""
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
@ -63,9 +64,10 @@ class TestBzrTags(TestCase):
|
|||||||
Test for proper bzr tags
|
Test for proper bzr tags
|
||||||
"""
|
"""
|
||||||
# GIVEN: A bzr branch
|
# GIVEN: A bzr branch
|
||||||
|
path = os.path.dirname(__file__)
|
||||||
|
|
||||||
# WHEN getting the branches tags
|
# WHEN getting the branches tags
|
||||||
bzr = Popen(('bzr', 'tags'), stdout=PIPE)
|
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
|
||||||
stdout = bzr.communicate()[0]
|
stdout = bzr.communicate()[0]
|
||||||
tags = [line.decode('utf-8').split() for line in stdout.splitlines()]
|
tags = [line.decode('utf-8').split() for line in stdout.splitlines()]
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user