This commit is contained in:
Tim Bentley 2013-09-26 22:06:57 +01:00
commit 8f47af1732
19 changed files with 354 additions and 127 deletions

View File

@ -27,3 +27,4 @@ openlp.pro
tests.kdev4
*.nja
*.orig
__pycache__

View File

@ -88,9 +88,9 @@ class StartTimeForm(QtGui.QDialog, Ui_StartTimeDialog):
"""
Split time up into hours minutes and seconds from secongs
"""
hours = seconds / 3600
hours = seconds // 3600
seconds -= 3600 * hours
minutes = seconds / 60
minutes = seconds // 60
seconds -= 60 * minutes
return hours, minutes, seconds

View File

@ -256,7 +256,7 @@ def is_not_image_file(file_name):
if not file_name:
return True
else:
formats = [str(fmt).lower() for fmt in QtGui.QImageReader.supportedImageFormats()]
formats = [bytes(fmt).decode().lower() for fmt in QtGui.QImageReader.supportedImageFormats()]
file_part, file_extension = os.path.splitext(str(file_name))
if file_extension[1:].lower() in formats and os.path.exists(file_name):
return False

View File

@ -48,6 +48,7 @@ __default_settings__ = {
'bibles/verse layout style': LayoutStyle.VersePerSlide,
'bibles/book name language': LanguageSelection.Bible,
'bibles/display brackets': DisplayStyle.NoBrackets,
'bibles/is verse number visible': True,
'bibles/display new chapter': False,
'bibles/second bibles': True,
'bibles/advanced bible': '',

View File

@ -58,6 +58,9 @@ class BiblesTab(SettingsTab):
self.verse_display_group_box.setObjectName('verse_display_group_box')
self.verse_display_layout = QtGui.QFormLayout(self.verse_display_group_box)
self.verse_display_layout.setObjectName('verse_display_layout')
self.is_verse_number_visible_check_box = QtGui.QCheckBox(self.verse_display_group_box)
self.is_verse_number_visible_check_box.setObjectName('is_verse_number_visible_check_box')
self.verse_display_layout.addRow(self.is_verse_number_visible_check_box)
self.new_chapters_check_box = QtGui.QCheckBox(self.verse_display_group_box)
self.new_chapters_check_box.setObjectName('new_chapters_check_box')
self.verse_display_layout.addRow(self.new_chapters_check_box)
@ -134,6 +137,7 @@ class BiblesTab(SettingsTab):
self.left_layout.addStretch()
self.right_layout.addStretch()
# Signals and slots
self.is_verse_number_visible_check_box.stateChanged.connect(self.on_is_verse_number_visible_check_box_changed)
self.new_chapters_check_box.stateChanged.connect(self.on_new_chapters_check_box_changed)
self.display_style_combo_box.activated.connect(self.on_display_style_combo_box_changed)
self.bible_theme_combo_box.activated.connect(self.on_bible_theme_combo_box_changed)
@ -156,6 +160,7 @@ class BiblesTab(SettingsTab):
def retranslateUi(self):
self.verse_display_group_box.setTitle(translate('BiblesPlugin.BiblesTab', 'Verse Display'))
self.is_verse_number_visible_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Show verse numbers'))
self.new_chapters_check_box.setText(translate('BiblesPlugin.BiblesTab', 'Only show new chapter numbers'))
self.layout_style_label.setText(UiStrings().LayoutStyle)
self.display_style_label.setText(UiStrings().DisplayStyle)
@ -208,6 +213,13 @@ class BiblesTab(SettingsTab):
def on_language_selection_combo_box_changed(self):
self.language_selection = self.language_selection_combo_box.currentIndex()
def on_is_verse_number_visible_check_box_changed(self, check_state):
"""
Event handler for the 'verse number visible' check box
"""
self.is_verse_number_visible = (check_state == QtCore.Qt.Checked)
self.check_is_verse_number_visible()
def on_new_chapters_check_box_changed(self, check_state):
self.show_new_chapters = False
# We have a set value convert to True/False.
@ -299,11 +311,14 @@ class BiblesTab(SettingsTab):
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
self.is_verse_number_visible = settings.value('is verse number visible')
self.show_new_chapters = settings.value('display new chapter')
self.display_style = settings.value('display brackets')
self.layout_style = settings.value('verse layout style')
self.bible_theme = settings.value('bible theme')
self.second_bibles = settings.value('second bibles')
self.is_verse_number_visible_check_box.setChecked(self.is_verse_number_visible)
self.check_is_verse_number_visible()
self.new_chapters_check_box.setChecked(self.show_new_chapters)
self.display_style_combo_box.setCurrentIndex(self.display_style)
self.layout_style_combo_box.setCurrentIndex(self.layout_style)
@ -351,6 +366,7 @@ class BiblesTab(SettingsTab):
def save(self):
settings = Settings()
settings.beginGroup(self.settings_section)
settings.setValue('is verse number visible', self.is_verse_number_visible)
settings.setValue('display new chapter', self.show_new_chapters)
settings.setValue('display brackets', self.display_style)
settings.setValue('verse layout style', self.layout_style)
@ -405,3 +421,12 @@ class BiblesTab(SettingsTab):
color.setAlpha(128)
palette.setColor(QtGui.QPalette.Active, QtGui.QPalette.Text, color)
return palette
def check_is_verse_number_visible(self):
"""
Enables / Disables verse settings dependent on is_verse_number_visible
"""
self.new_chapters_check_box.setEnabled(self.is_verse_number_visible)
self.display_style_label.setEnabled(self.is_verse_number_visible)
self.display_style_combo_box.setEnabled(self.is_verse_number_visible)

View File

@ -717,7 +717,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None, pre
return None
page_source = page.read()
if pre_parse_regex and pre_parse_substitute is not None:
page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source)
page_source = re.sub(pre_parse_regex, pre_parse_substitute, page_source.decode())
soup = None
try:
soup = BeautifulSoup(page_source)

View File

@ -803,20 +803,20 @@ class BibleMediaItem(MediaManagerItem):
verses.add(book, chapter, verse, version, copyright, permissions)
verse_text = self.formatVerse(old_chapter, chapter, verse)
if second_bible:
bible_text = '%s %s\n\n%s %s' % (verse_text, text, verse_text, second_text)
bible_text = '%s%s\n\n%s %s' % (verse_text, text, verse_text, second_text)
raw_slides.append(bible_text.rstrip())
bible_text = ''
# If we are 'Verse Per Slide' then create a new slide.
elif self.settings.layout_style == LayoutStyle.VersePerSlide:
bible_text = '%s %s' % (verse_text, text)
bible_text = '%s%s' % (verse_text, text)
raw_slides.append(bible_text.rstrip())
bible_text = ''
# If we are 'Verse Per Line' then force a new line.
elif self.settings.layout_style == LayoutStyle.VersePerLine:
bible_text = '%s%s %s\n' % (bible_text, verse_text, text)
bible_text = '%s%s%s\n' % (bible_text, verse_text, text)
# We have to be 'Continuous'.
else:
bible_text = '%s %s %s\n' % (bible_text, verse_text, text)
bible_text = '%s %s%s\n' % (bible_text, verse_text, text)
bible_text = bible_text.strip(' ')
if not old_item:
start_item = bitem
@ -943,17 +943,19 @@ class BibleMediaItem(MediaManagerItem):
The verse number (int).
"""
verse_separator = get_reference_separator('sep_v_display')
if not self.settings.is_verse_number_visible:
return ''
if not self.settings.show_new_chapters or old_chapter != chapter:
verse_text = str(chapter) + verse_separator + str(verse)
else:
verse_text = str(verse)
if self.settings.display_style == DisplayStyle.Round:
return '{su}(%s){/su}' % verse_text
return '{su}(%s){/su} ' % verse_text
if self.settings.display_style == DisplayStyle.Curly:
return '{su}{%s}{/su}' % verse_text
return '{su}{%s}{/su} ' % verse_text
if self.settings.display_style == DisplayStyle.Square:
return '{su}[%s]{/su}' % verse_text
return '{su}%s{/su}' % verse_text
return '{su}[%s]{/su} ' % verse_text
return '{su}%s{/su} ' % verse_text
def search(self, string, showError):
"""

View File

@ -198,21 +198,7 @@ class RemoteTab(SettingsTab):
"""
Update the display based on the data input on the screen
"""
ip_address = 'localhost'
if self.address_edit.text() == ZERO_URL:
interfaces = QtNetwork.QNetworkInterface.allInterfaces()
for interface in interfaces:
if not interface.isValid():
continue
if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)):
continue
for address in interface.addressEntries():
ip = address.ip()
if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost:
ip_address = ip
break
else:
ip_address = self.address_edit.text()
ip_address = self.get_ip_address(self.address_edit.text())
http_url = 'http://%s:%s/' % (ip_address, self.port_spin_box.value())
https_url = 'https://%s:%s/' % (ip_address, self.https_port_spin_box.value())
self.remote_url.setText('<a href="%s">%s</a>' % (http_url, http_url))
@ -226,6 +212,25 @@ class RemoteTab(SettingsTab):
self.live_url.setText('<a href="%s">%s</a>' % (http_url_temp, http_url_temp))
self.live_https_url.setText('<a href="%s">%s</a>' % (https_url_temp, https_url_temp))
def get_ip_address(self, ip_address):
"""
returns the IP address in dependency of the passed address
ip_address == 0.0.0.0: return the IP address of the first valid interface
else: return ip_address
"""
if ip_address == ZERO_URL:
interfaces = QtNetwork.QNetworkInterface.allInterfaces()
for interface in interfaces:
if not interface.isValid():
continue
if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)):
continue
for address in interface.addressEntries():
ip = address.ip()
if ip.protocol() == QtNetwork.QAbstractSocket.IPv4Protocol and ip != QtNetwork.QHostAddress.LocalHost:
return ip.toString()
return ip_address
def load(self):
"""
Load the configuration and update the server configuration if necessary

View File

@ -46,7 +46,13 @@ log = logging.getLogger(__name__)
WHITESPACE = re.compile(r'[\W_]+', re.UNICODE)
APOSTROPHE = re.compile('[\'`ʻ]', re.UNICODE)
PATTERN = re.compile(r"\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z])|([{}])|[\r\n]+|(.)", re.I)
# PATTERN will look for the next occurence of one of these symbols:
# \controlword - optionally preceded by \*, optionally followed by a number
# \'## - where ## is a pair of hex digits, representing a single character
# \# - where # is a single non-alpha character, representing a special symbol
# { or } - marking the beginning/end of a group
# a run of characters without any \ { } or end-of-line
PATTERN = re.compile(r"(\\\*)?\\([a-z]{1,32})(-?\d{1,10})?[ ]?|\\'([0-9a-f]{2})|\\([^a-z*])|([{}])|[\r\n]+|([^\\{}\r\n]+)", re.I)
# RTF control words which specify a "destination" to be ignored.
DESTINATIONS = frozenset((
'aftncn', 'aftnsep', 'aftnsepc', 'annotation', 'atnauthor',
@ -57,8 +63,8 @@ DESTINATIONS = frozenset((
'datafield', 'datastore', 'defchp', 'defpap', 'do', 'doccomm',
'docvar', 'dptxbxtext', 'ebcend', 'ebcstart', 'factoidname',
'falt', 'fchars', 'ffdeftext', 'ffentrymcr', 'ffexitmcr',
'ffformat', 'ffhelptext', 'ffl', 'ffname', 'ffstattext', 'field',
'file', 'filetbl', 'fldinst', 'fldrslt', 'fldtype', 'fname',
'ffformat', 'ffhelptext', 'ffl', 'ffname', 'ffstattext',
'file', 'filetbl', 'fldinst', 'fldtype', 'fname',
'fontemb', 'fontfile', 'footer', 'footerf', 'footerl', 'footerr',
'footnote', 'formfield', 'ftncn', 'ftnsep', 'ftnsepc', 'g',
'generator', 'gridtbl', 'header', 'headerf', 'headerl',
@ -106,6 +112,11 @@ DESTINATIONS = frozenset((
'xmlclose', 'xmlname', 'xmlnstbl', 'xmlopen'))
# Translation of some special characters.
SPECIAL_CHARS = {
'\n': '\n',
'\r': '\n',
'~': '\u00A0',
'-': '\u00AD',
'_': '\u2011',
'par': '\n',
'sect': '\n\n',
# Required page and column break.
@ -132,16 +143,19 @@ SPECIAL_CHARS = {
'zwj': '\u200D',
'zwnj': '\u200C'}
CHARSET_MAPPING = {
'fcharset0': 'cp1252',
'fcharset161': 'cp1253',
'fcharset162': 'cp1254',
'fcharset163': 'cp1258',
'fcharset177': 'cp1255',
'fcharset178': 'cp1256',
'fcharset186': 'cp1257',
'fcharset204': 'cp1251',
'fcharset222': 'cp874',
'fcharset238': 'cp1250'}
'0': 'cp1252',
'128': 'cp932',
'129': 'cp949',
'134': 'cp936',
'161': 'cp1253',
'162': 'cp1254',
'163': 'cp1258',
'177': 'cp1255',
'178': 'cp1256',
'186': 'cp1257',
'204': 'cp1251',
'222': 'cp874',
'238': 'cp1250'}
class VerseType(object):
@ -351,7 +365,7 @@ def retrieve_windows_encoding(recommendation=None):
if recommendation == encodings[index][0]:
recommended_index = index
break
if recommended_index > 0:
if recommended_index > -1:
choice = QtGui.QInputDialog.getItem(None,
translate('SongsPlugin', 'Character Encoding'),
translate('SongsPlugin', 'The codepage setting is responsible\n'
@ -365,7 +379,7 @@ def retrieve_windows_encoding(recommendation=None):
[pair[1] for pair in encodings], 0, False)
if not choice[1]:
return None
return filter(lambda item: item[1] == choice[0], encodings)[0][0]
return next(filter(lambda item: item[1] == choice[0], encodings))[0]
def clean_string(string):
@ -521,43 +535,59 @@ def strip_rtf(text, default_encoding=None):
curskip = 0
# Output buffer.
out = []
# Encoded buffer.
ebytes = bytearray()
for match in PATTERN.finditer(text):
word, arg, hex, char, brace, tchar = match.groups()
iinu, word, arg, hex, char, brace, tchar = match.groups()
# \x (non-alpha character)
if char:
if char in '\\{}':
tchar = char
else:
word = char
# Flush encoded buffer to output buffer
if ebytes and not hex and not tchar:
failed = False
while True:
try:
encoding, default_encoding = get_encoding(font, font_table, default_encoding, failed=failed)
if not encoding:
return None
dbytes = ebytes.decode(encoding)
# Code 5C is a peculiar case with Windows Codepage 932
if encoding == 'cp932' and '\\' in dbytes:
dbytes = dbytes.replace('\\', '\u00A5')
out.append(dbytes)
ebytes.clear()
except UnicodeDecodeError:
failed = True
else:
break
# {}
if brace:
curskip = 0
if brace == '{':
# Push state
stack.append((ucskip, ignorable, font))
elif brace == '}':
elif brace == '}' and len(stack) > 0:
# Pop state
ucskip, ignorable, font = stack.pop()
# \x (not a letter)
elif char:
curskip = 0
if char == '~' and not ignorable:
out.append('\xA0')
elif char in '{}\\' and not ignorable:
out.append(char)
elif char == '-' and not ignorable:
out.append('\u00AD')
elif char == '_' and not ignorable:
out.append('\u2011')
elif char == '*':
ignorable = True
# \command
elif word:
curskip = 0
if word in DESTINATIONS:
ignorable = True
elif word in SPECIAL_CHARS:
out.append(SPECIAL_CHARS[word])
if not ignorable:
out.append(SPECIAL_CHARS[word])
elif word == 'uc':
ucskip = int(arg)
elif word == ' ':
elif word == 'u':
c = int(arg)
if c < 0:
c += 0x10000
out.append(chr(c))
if not ignorable:
out.append(chr(c))
curskip = ucskip
elif word == 'fonttbl':
ignorable = True
@ -565,31 +595,24 @@ def strip_rtf(text, default_encoding=None):
font = arg
elif word == 'ansicpg':
font_table[font] = 'cp' + arg
elif word == 'fcharset' and font not in font_table and word + arg in CHARSET_MAPPING:
# \ansicpg overrides \fcharset, if present.
font_table[font] = CHARSET_MAPPING[word + arg]
elif word == 'fcharset' and font not in font_table and arg in CHARSET_MAPPING:
font_table[font] = CHARSET_MAPPING[arg]
elif word == 'fldrslt':
pass
# \* 'Ignore if not understood' marker
elif iinu:
ignorable = True
# \'xx
elif hex:
if curskip > 0:
curskip -= 1
elif not ignorable:
charcode = int(hex, 16)
failed = False
while True:
try:
encoding, default_encoding = get_encoding(font, font_table, default_encoding, failed=failed)
if not encoding:
return None
out.append(chr(charcode).decode(encoding))
except UnicodeDecodeError:
failed = True
else:
break
ebytes.append(int(hex, 16))
elif tchar:
if curskip > 0:
curskip -= 1
elif not ignorable:
out.append(tchar)
ebytes += tchar.encode()
text = ''.join(out)
return text, default_encoding

View File

@ -122,7 +122,7 @@ class EasyWorshipSongImport(SongImport):
db_file.seek(120)
field_info = db_file.read(num_fields * 2)
db_file.seek(4 + (num_fields * 4) + 261, os.SEEK_CUR)
field_names = db_file.read(header_size - db_file.tell()).split('\0', num_fields)
field_names = db_file.read(header_size - db_file.tell()).split(b'\0', num_fields)
field_names.pop()
field_descs = []
for i, field_name in enumerate(field_names):
@ -132,12 +132,12 @@ class EasyWorshipSongImport(SongImport):
# Pick out the field description indexes we will need
try:
success = True
fi_title = self.findField('Title')
fi_author = self.findField('Author')
fi_copy = self.findField('Copyright')
fi_admin = self.findField('Administrator')
fi_words = self.findField('Words')
fi_ccli = self.findField('Song Number')
fi_title = self.findField(b'Title')
fi_author = self.findField(b'Author')
fi_copy = self.findField(b'Copyright')
fi_admin = self.findField(b'Administrator')
fi_words = self.findField(b'Words')
fi_ccli = self.findField(b'Song Number')
except IndexError:
# This is the wrong table
success = False
@ -150,7 +150,7 @@ class EasyWorshipSongImport(SongImport):
cur_block_pos = header_size + ((cur_block - 1) * 1024 * block_size)
db_file.seek(cur_block_pos)
cur_block, rec_count = struct.unpack('<h2xh', db_file.read(6))
rec_count = (rec_count + record_size) / record_size
rec_count = (rec_count + record_size) // record_size
block_list.append((cur_block_pos, rec_count))
total_count += rec_count
self.import_wizard.progress_bar.setMaximum(total_count)
@ -164,7 +164,7 @@ class EasyWorshipSongImport(SongImport):
raw_record = db_file.read(record_size)
self.fields = self.recordStruct.unpack(raw_record)
self.setDefaults()
self.title = self.getField(fi_title)
self.title = self.getField(fi_title).decode()
# Get remaining fields.
copy = self.getField(fi_copy)
admin = self.getField(fi_admin)
@ -173,25 +173,26 @@ class EasyWorshipSongImport(SongImport):
words = self.getField(fi_words)
# Set the SongImport object members.
if copy:
self.copyright = copy
self.copyright = copy.decode()
if admin:
if copy:
self.copyright += ', '
self.copyright += translate('SongsPlugin.EasyWorshipSongImport', 'Administered by %s') % admin
self.copyright += translate('SongsPlugin.EasyWorshipSongImport',
'Administered by %s') % admin.decode()
if ccli:
self.ccliNumber = ccli
self.ccliNumber = ccli.decode()
if authors:
# Split up the authors
author_list = authors.split('/')
author_list = authors.split(b'/')
if len(author_list) < 2:
author_list = authors.split(';')
author_list = authors.split(b';')
if len(author_list) < 2:
author_list = authors.split(',')
author_list = authors.split(b',')
for author_name in author_list:
self.addAuthor(author_name.strip())
self.addAuthor(author_name.decode().strip())
if words:
# Format the lyrics
result = strip_rtf(words, self.encoding)
result = strip_rtf(words.decode(), self.encoding)
if result is None:
return
words, self.encoding = result
@ -267,14 +268,14 @@ class EasyWorshipSongImport(SongImport):
field = self.fields[field_desc_index]
field_desc = self.fieldDescs[field_desc_index]
# Return None in case of 'blank' entries
if isinstance(field, str):
if not field.rstrip('\0'):
if isinstance(field, bytes):
if not field.rstrip(b'\0'):
return None
elif field == 0:
return None
# Format the field depending on the field type
if field_desc.field_type == FieldType.String:
return field.rstrip('\0')
return field.rstrip(b'\0')
elif field_desc.field_type == FieldType.Int16:
return field ^ 0x8000
elif field_desc.field_type == FieldType.Int32:
@ -291,12 +292,12 @@ class EasyWorshipSongImport(SongImport):
self.memoFile.seek(8, os.SEEK_CUR)
elif memo_block_type == 3:
if sub_block > 63:
return ''
return b''
self.memoFile.seek(11 + (5 * sub_block), os.SEEK_CUR)
sub_block_start, = struct.unpack('B', self.memoFile.read(1))
self.memoFile.seek(block_start + (sub_block_start * 16))
else:
return ''
return b''
return self.memoFile.read(blob_size)
else:
return 0

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
@ -50,7 +50,7 @@ IS_WIN = sys.platform.startswith('win')
VERS = {
'Python': '2.6',
'Python': '3.0',
'PyQt4': '4.6',
'Qt4': '4.6',
'sqlalchemy': '0.5',
@ -64,6 +64,7 @@ WIN32_MODULES = [
'win32ui',
'pywintypes',
'pyodbc',
'icu',
]
MODULES = [
@ -77,6 +78,7 @@ MODULES = [
'PyQt4.QtWebKit',
'PyQt4.phonon',
'sqlalchemy',
'alembic',
'sqlite3',
'lxml',
'chardet',
@ -84,8 +86,6 @@ MODULES = [
'bs4',
'mako',
'uno',
'icu',
'bs4',
]

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
@ -102,7 +102,7 @@ class CommandStack(object):
def __iter__(self):
return self
def next(self):
def __next__(self):
if self.current_index == len(self.data):
raise StopIteration
else:
@ -145,9 +145,9 @@ def print_quiet(text, linefeed=True):
global quiet_mode
if not quiet_mode:
if linefeed:
print text
print(text)
else:
print text,
print(text, end=' ')
def print_verbose(text):
"""

View File

@ -1,4 +1,4 @@
#!/usr/bin/env python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4

View File

@ -187,7 +187,7 @@ class TestLib(TestCase):
"""
Test the get_text_file_string() method when a read error happens
"""
with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('builtins.open') as mocked_open:
with patch('openlp.core.lib.os.path.isfile') as mocked_isfile, patch('openlp.core.lib.open', create=True) as mocked_open:
# GIVEN: A mocked-out open() which raises an exception and isfile returns True
filename = 'testfile.txt'
mocked_isfile.return_value = True
@ -252,7 +252,7 @@ class TestLib(TestCase):
# GIVEN: A set of mocked-out Qt classes
mocked_byte_array = MagicMock()
MockedQtCore.QByteArray.return_value = mocked_byte_array
mocked_byte_array.toBase64.return_value = 'base64mock'
mocked_byte_array.toBase64.return_value = QtCore.QByteArray('base64mock')
mocked_buffer = MagicMock()
MockedQtCore.QBuffer.return_value = mocked_buffer
MockedQtCore.QIODevice.WriteOnly = 'writeonly'

View File

@ -0,0 +1,116 @@
"""
This module contains tests for the versereferencelist submodule of the Bibles plugin.
"""
from unittest import TestCase
from openlp.plugins.bibles.lib.versereferencelist import VerseReferenceList
class TestVerseReferenceList(TestCase):
def setUp(self):
"""
Initializes all we need
"""
def add_first_verse_test(self):
"""
Test the addition of a verse to the empty list
"""
# GIVEN: an empty list
reference_list = VerseReferenceList()
book = 'testBook'
chapter = 1
verse = 1
version = 'testVersion'
copyright = 'testCopyright'
permission = 'testPermision'
# WHEN: We add it to the verse list
reference_list.add(book, chapter, verse, version, copyright, permission)
# THEN: The entries should be in the first entry of the list
self.assertEqual(reference_list.current_index, 0, 'The current index should be 0')
self.assertEqual(reference_list.verse_list[0]['book'], book, 'The book in first entry should be %s' % book)
self.assertEqual(reference_list.verse_list[0]['chapter'], chapter, 'The chapter in first entry should be %u' % chapter)
self.assertEqual(reference_list.verse_list[0]['start'], verse, 'The start in first entry should be %u' % verse)
self.assertEqual(reference_list.verse_list[0]['version'], version, 'The version in first entry should be %s' % version)
self.assertEqual(reference_list.verse_list[0]['end'], verse, 'The end in first entry should be %u' % verse)
def add_next_verse_test(self):
"""
Test the addition of the following verse
"""
# GIVEN: 1 line in the list of verses
book = 'testBook'
chapter = 1
verse = 1
next_verse = 2
version = 'testVersion'
copyright = 'testCopyright'
permission = 'testPermision'
reference_list = VerseReferenceList()
reference_list.add(book, chapter, verse, version, copyright, permission)
# WHEN: We add the following verse to the verse list
reference_list.add(book, chapter, next_verse, version, copyright, permission)
# THEN: The current index should be 0 and the end pointer of the entry should be '2'
self.assertEqual(reference_list.current_index, 0, 'The current index should be 0')
self.assertEqual(reference_list.verse_list[0]['end'], next_verse, 'The end in first entry should be %u' % next_verse)
def add_another_verse_test(self):
"""
Test the addition of a verse in another book
"""
# GIVEN: 1 line in the list of verses
book = 'testBook'
chapter = 1
verse = 1
next_verse = 2
another_book = 'testBook2'
another_chapter = 2
another_verse = 5
version = 'testVersion'
copyright = 'testCopyright'
permission = 'testPermision'
reference_list = VerseReferenceList()
reference_list.add(book, chapter, verse, version, copyright, permission)
# WHEN: We add a verse of another book to the verse list
reference_list.add(another_book, another_chapter, another_verse, version, copyright, permission)
# THEN: the current index should be 1
self.assertEqual(reference_list.current_index, 1, 'The current index should be 1')
def add_version_test(self):
"""
Test the addition of a version to the list
"""
# GIVEN: version, copyright and permission
reference_list = VerseReferenceList()
version = 'testVersion'
copyright = 'testCopyright'
permission = 'testPermision'
# WHEN: a not existing version will be added
reference_list.add_version(version, copyright, permission)
# THEN: the data will be appended to the list
self.assertEqual(len(reference_list.version_list), 1, 'The version data should be appended')
self.assertEqual(reference_list.version_list[0], {'version': version, 'copyright': copyright, 'permission': permission},
'The version data should be appended')
def add_existing_version_test(self):
"""
Test the addition of an existing version to the list
"""
# GIVEN: version, copyright and permission, added to the version list
reference_list = VerseReferenceList()
version = 'testVersion'
copyright = 'testCopyright'
permission = 'testPermision'
reference_list.add_version(version, copyright, permission)
# WHEN: an existing version will be added
reference_list.add_version(version, copyright, permission)
# THEN: the data will not be appended to the list
self.assertEqual(len(reference_list.version_list), 1, 'The version data should not be appended')

View File

@ -2,6 +2,7 @@
This module contains tests for the lib submodule of the Remotes plugin.
"""
import os
import re
from unittest import TestCase
from tempfile import mkstemp
@ -52,6 +53,27 @@ class TestRemoteTab(TestCase):
del self.form
os.unlink(self.ini_file)
def get_ip_address_default_test(self):
"""
Test the get_ip_address function with ZERO_URL
"""
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(ZERO_URL)
# THEN: the default ip address will be returned
self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), 'The return value should be a valid ip address')
def get_ip_address_with_ip_test(self):
"""
Test the get_ip_address function with given ip address
"""
# GIVEN: A mocked location
# GIVEN: An ip address
given_ip = '192.168.1.1'
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(given_ip)
# THEN: the default ip address will be returned
self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip)
def set_basic_urls_test(self):
"""
Test the set_urls function with standard defaults

View File

@ -74,18 +74,18 @@ TEST_FIELD_DESCS = [TestFieldDesc('Title', FieldType.String, 50),
TestFieldDesc('Default Background', FieldType.Logical, 1), TestFieldDesc('Words', FieldType.Memo, 250),
TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('BK Bitmap', FieldType.Blob, 10),
TestFieldDesc('Last Modified', FieldType.Timestamp, 10)]
TEST_FIELDS = ['A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750,
129, '{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;'
'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g<EFBFBD><EFBFBD>\7\0f\r\0\0\1\0',
'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255'
'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g><3E>\6\0<EFBFBD>\6\0\0\1\0', '\0\0\0\0\0\0\0\0\0\0', 0]
TEST_FIELDS = [b'A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750,
129, b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;'
b'\\red255\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\xBF\xBD\7\0f\r\0\0\1\0',
b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}'
b'{\\colortbl\\red0\\green0\\blue0;\\red255\\green0\\blue0;\\red0\\green128\\blue0;\\red0\\green0\\blue255;\\red255'
b'\\green255\\blue0;\\red255\\green0\\blue255;\\red128\\g\6\0\xEF\xBF\xBD\6\0\0\1\0', b'\0\0\0\0\0\0\0\0\0\0', 0]
GET_MEMO_FIELD_TEST_RESULTS = [
(4, '\2', {'return': '\2','read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}),
(4, '\3', {'return': '', 'read': (1, ), 'seek': (507136, )}),
(5, '\3', {'return': '\3', 'read': (1, 1725), 'seek': (3220111360, (41, os.SEEK_CUR), 3220111408)}),
(5, '\4', {'return': '', 'read': (), 'seek': ()})]
(4, b'\2', {'return': b'\2','read': (1, 3430), 'seek': (507136, (8, os.SEEK_CUR))}),
(4, b'\3', {'return': b'', 'read': (1, ), 'seek': (507136, )}),
(5, b'\3', {'return': b'\3', 'read': (1, 1725), 'seek': (3220111360, (41, os.SEEK_CUR), 3220111408)}),
(5, b'\4', {'return': b'', 'read': (), 'seek': ()})]
class TestEasyWorshipSongImport(TestCase):
"""
@ -189,7 +189,7 @@ class TestEasyWorshipSongImport(TestCase):
importer.encoding = TEST_DATA_ENCODING
importer.fields = TEST_FIELDS
importer.fieldDescs = TEST_FIELD_DESCS
field_results = [(0, 'A Heart Like Thine'), (1, 100), (2, 102), (3, True), (6, None), (7, None)]
field_results = [(0, b'A Heart Like Thine'), (1, 100), (2, 102), (3, True), (6, None), (7, None)]
# WHEN: Called with test data
for field_index, result in field_results:
@ -276,7 +276,7 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \
patch('__builtin__.open') as mocked_open, \
patch('builtins.open') as mocked_open, \
patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
@ -303,7 +303,7 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: A mocked out SongImport class, a mocked out "manager"
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path, \
patch('__builtin__.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \
patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \
patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
@ -354,7 +354,6 @@ class TestEasyWorshipSongImport(TestCase):
# called.
self.assertIsNone(importer.doImport(), 'doImport should return None when it has completed')
for song_data in SONG_TEST_DATA:
print mocked_title.mocked_calls()
title = song_data['title']
author_calls = song_data['authors']
song_copyright = song_data['copyright']

View File

@ -6,7 +6,7 @@ from unittest import TestCase
from mock import patch, MagicMock
from openlp.plugins.songs.lib import VerseType, clean_string, clean_title
from openlp.plugins.songs.lib import VerseType, clean_string, clean_title, strip_rtf
from openlp.plugins.songs.lib.songcompare import songs_probably_equal, _remove_typos, _op_length
@ -215,6 +215,38 @@ class TestLib(TestCase):
# THEN: The maximum length should be returned.
assert result == 10, 'The length should be 10.'
def strip_rtf_charsets_test(self):
"""
Test that the strip_rtf() method properly decodes the supported charsets.
"""
test_charset_table = [
('0', 'weor\\\'F0-myndum \\\'FEah\\par ', 'weorð-myndum þah\n'),
('128', '\\\'83C\\\'83G\\\'83X\\\'A5\\\'83L\\\'83\\\'8A\\\'83X\\\'83g\\\'A1 '
'\\\\ \\\'95\\\\ \\\'8E\\} \\\'8E\\{ \\\'A1\\par ', 'イエス・キリスト。 ¥ 表 枝 施 。\n'),
('129', '\\\'BF\\\'B9\\\'BC\\\'F6 \\\'B1\\\'D7\\\'B8\\\'AE\\\'BD\\\'BA\\\'B5\\\'B5\\par ', '예수 그리스도\n'),
('134', '\\\'D2\\\'AE\\\'F6\\\'D5\\\'BB\\\'F9\\\'B6\\\'BD\\\'CA\\\'C7\\\'D6\\\'F7\\par ', '耶稣基督是主\n'),
('161', '\\\'D7\\\'F1\\\'E9\\\'F3\\\'F4\\\'FC\\\'F2\\par ', 'Χριστός\n'),
('162', 'Hazreti \\\'DDsa\\par ', 'Hazreti İsa\n'),
('163', 'ph\\\'FD\\\'F5ng\\par ', 'phương\n'),
('177', '\\\'E1\\\'F8\\\'E0\\\'F9\\\'E9\\\'FA\\par ', 'בראשית\n'),
('178', '\\\'ED\\\'D3\\\'E6\\\'DA \\\'C7\\\'E1\\\'E3\\\'D3\\\'ED\\\'CD\\par ', 'يسوع المسيح\n'),
('186', 'J\\\'EBzus Kristus yra Vie\\\'F0pats\\par ', 'Jėzus Kristus yra Viešpats\n'),
('204', '\\\'D0\\\'EE\\\'F1\\\'F1\\\'E8\\\'FF\\par ', 'Россия\n'),
('222', '\\\'A4\\\'C3\\\'D4\\\'CA\\\'B5\\\'EC\\par ', 'คริสต์\n'),
('238', 'Z\\\'E1v\\\'ECre\\\'E8n\\\'E1 zkou\\\'9Aka\\par ', 'Závěrečná zkouška\n')
]
# GIVEN: For each character set and input
for charset, input, exp_result in test_charset_table:
# WHEN: We call strip_rtf on the input RTF
result, result_enc = strip_rtf(
'{\\rtf1 \\ansi \\ansicpg1252 {\\fonttbl \\f0 \\fswiss \\fcharset%s Helvetica;}' \
'{\\colortbl ;\\red0 \\green0 \\blue0 ;}\\pard \\f0 %s}' % (charset, input))
# THEN: The stripped text matches thed expected result
assert result == exp_result, 'The result should be %s' % exp_result
class TestVerseType(TestCase):
"""

View File

@ -43,7 +43,7 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('NIV', 'John', 3)
# THEN: We should get back a valid service item
assert len(results.verselist) == 36, 'The book of John should not have had any verses added or removed'
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'
def crosswalk_extract_books_test(self):
"""
@ -69,5 +69,5 @@ class TestBibleHTTP(TestCase):
results = handler.get_bible_chapter('niv', 'john', 3)
# THEN: We should get back a valid service item
assert len(results.verselist) == 36, 'The book of John should not have had any verses added or removed'
assert len(results.verse_list) == 36, 'The book of John should not have had any verses added or removed'