diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py index 1b16ecab7..0ae62e0a4 100644 --- a/openlp/core/common/__init__.py +++ b/openlp/core/common/__init__.py @@ -190,31 +190,32 @@ def verify_ip_address(addr): return True if verify_ipv4(addr) else verify_ipv6(addr) -def md5_hash(salt, data): +def md5_hash(salt, data=None): """ Returns the hashed output of md5sum on salt,data using Python3 hashlib :param salt: Initial salt - :param data: Data to hash + :param data: OPTIONAL Data to hash :returns: str """ log.debug('md5_hash(salt="%s")' % salt) hash_obj = hashlib.new('md5') - hash_obj.update(salt.encode('ascii')) - hash_obj.update(data.encode('ascii')) + hash_obj.update(salt) + if data: + hash_obj.update(data) hash_value = hash_obj.hexdigest() log.debug('md5_hash() returning "%s"' % hash_value) return hash_value -def qmd5_hash(salt, data): +def qmd5_hash(salt, data=None): """ Returns the hashed output of MD5Sum on salt, data using PyQt4.QCryptographicHash. :param salt: Initial salt - :param data: Data to hash + :param data: OPTIONAL Data to hash :returns: str """ log.debug('qmd5_hash(salt="%s"' % salt) @@ -223,7 +224,7 @@ def qmd5_hash(salt, data): hash_obj.addData(data) hash_value = hash_obj.result().toHex() log.debug('qmd5_hash() returning "%s"' % hash_value) - return decode(hash_value.data(), 'ascii') + return hash_value.data() def clean_button_text(button_text): diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py index db2b66878..b7f8afe99 100644 --- a/openlp/core/lib/projector/constants.py +++ b/openlp/core/lib/projector/constants.py @@ -271,8 +271,8 @@ ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The proxy address set with setProxy() was not found'), E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants', - 'The connection negotiation with the proxy server because the response ' - 'from the proxy server could not be understood'), + 'The connection negotiation with the proxy server failed because the ' + 'response from the proxy server could not be understood'), E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'), S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'), S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'), diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py index 65a728b35..a82c600e6 100644 --- a/openlp/core/lib/projector/pjlink1.py +++ b/openlp/core/lib/projector/pjlink1.py @@ -343,7 +343,7 @@ class PJLink1(QTcpSocket): # Authenticated login with salt log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2])) log.debug('(%s) pin="%s"' % (self.ip, self.pin)) - salt = qmd5_hash(salt=data_check[2], data=self.pin) + salt = qmd5_hash(salt=data_check[2].endcode('ascii'), data=self.pin.encode('ascii')) else: salt = None # We're connected at this point, so go ahead and do regular I/O diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index 3f3403d19..d92866bc4 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -268,9 +268,11 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties): self.web = 'http://openlp.org/files/frw/' self.cancel_button.clicked.connect(self.on_cancel_button_clicked) self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked) + self.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked) self.currentIdChanged.connect(self.on_current_id_changed) Registry().register_function('config_screen_changed', self.update_screen_list_combo) self.no_internet_finish_button.setVisible(False) + self.no_internet_cancel_button.setVisible(False) # Check if this is a re-run of the wizard. self.has_run_wizard = Settings().value('core/has run wizard') check_directory_exists(os.path.join(gettempdir(), 'openlp')) @@ -327,6 +329,10 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties): self.next_button.setVisible(False) self.cancel_button.setVisible(False) self.no_internet_finish_button.setVisible(True) + if self.has_run_wizard: + self.no_internet_cancel_button.setVisible(False) + else: + self.no_internet_cancel_button.setVisible(True) elif page_id == FirstTimePage.Plugins: self.back_button.setVisible(False) elif page_id == FirstTimePage.Progress: @@ -372,6 +378,13 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties): Settings().setValue('core/has run wizard', True) self.close() + def on_no_internet_cancel_button_clicked(self): + """ + Process the triggering of the "Cancel" button on the No Internet page. + """ + self.was_cancelled = True + self.close() + def url_get_file(self, url, f_path): """" Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py index 63695a09a..041cfc050 100644 --- a/openlp/core/ui/firsttimewizard.py +++ b/openlp/core/ui/firsttimewizard.py @@ -59,7 +59,8 @@ class UiFirstTimeWizard(object): first_time_wizard.resize(550, 386) first_time_wizard.setModal(True) first_time_wizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage | - QtGui.QWizard.NoBackButtonOnLastPage | QtGui.QWizard.HaveCustomButton1) + QtGui.QWizard.NoBackButtonOnLastPage | QtGui.QWizard.HaveCustomButton1 | + QtGui.QWizard.HaveCustomButton2) if is_macosx(): first_time_wizard.setPixmap(QtGui.QWizard.BackgroundPixmap, QtGui.QPixmap(':/wizards/openlp-osx-wizard.png')) @@ -69,6 +70,7 @@ class UiFirstTimeWizard(object): self.finish_button = self.button(QtGui.QWizard.FinishButton) self.no_internet_finish_button = self.button(QtGui.QWizard.CustomButton1) self.cancel_button = self.button(QtGui.QWizard.CancelButton) + self.no_internet_cancel_button = self.button(QtGui.QWizard.CustomButton2) self.next_button = self.button(QtGui.QWizard.NextButton) self.back_button = self.button(QtGui.QWizard.BackButton) add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp') @@ -271,3 +273,4 @@ class UiFirstTimeWizard(object): 'and OpenLP is configured.')) self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...')) first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish')) + first_time_wizard.setButtonText(QtGui.QWizard.CustomButton2, translate('OpenLP.FirstTimeWizard', 'Cancel')) diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index 2feab127e..0e919871a 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -989,15 +989,21 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties): # Read the temp file and output the user's CONF file with blanks to # make it more readable. temp_conf = open(temp_file, 'r') - export_conf = open(export_file_name, 'w') - for file_record in temp_conf: - # Get rid of any invalid entries. - if file_record.find('@Invalid()') == -1: - file_record = file_record.replace('%20', ' ') - export_conf.write(file_record) - temp_conf.close() - export_conf.close() - os.remove(temp_file) + try: + export_conf = open(export_file_name, 'w') + for file_record in temp_conf: + # Get rid of any invalid entries. + if file_record.find('@Invalid()') == -1: + file_record = file_record.replace('%20', ' ') + export_conf.write(file_record) + temp_conf.close() + export_conf.close() + os.remove(temp_file) + except OSError as ose: + QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'), + translate('OpenLP.MainWindow', 'An error occurred while exporting the ' + 'settings: %s') % ose.strerror, + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) def on_mode_default_item_clicked(self): """ diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py index 8bdf9c383..0213fb02f 100644 --- a/openlp/core/ui/servicemanager.py +++ b/openlp/core/ui/servicemanager.py @@ -601,6 +601,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage shutil.copy(temp_file_name, path_file_name) except shutil.Error: return self.save_file_as() + except OSError as ose: + QtGui.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'), + translate('OpenLP.ServiceManager', 'An error occurred while writing the ' + 'service file: %s') % ose.strerror, + QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok)) + success = False self.main_window.add_recent_file(path_file_name) self.set_modified(False) delete_file(temp_file_name) diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 5ced57ea7..030516821 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -377,17 +377,11 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R self.application.set_busy_cursor() if path: Settings().setValue(self.settings_section + '/last directory export', path) - try: - self._export_theme(path, theme) + if self._export_theme(path, theme): QtGui.QMessageBox.information(self, translate('OpenLP.ThemeManager', 'Theme Exported'), translate('OpenLP.ThemeManager', 'Your theme has been successfully exported.')) - except (IOError, OSError): - self.log_exception('Export Theme Failed') - critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'), - translate('OpenLP.ThemeManager', - 'Your theme could not be exported due to an error.')) self.application.set_normal_cursor() def _export_theme(self, path, theme): @@ -397,19 +391,24 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R :param theme: The name of the theme to be exported """ theme_path = os.path.join(path, theme + '.otz') + theme_zip = None try: theme_zip = zipfile.ZipFile(theme_path, 'w') source = os.path.join(self.path, theme) for files in os.walk(source): for name in files[2]: theme_zip.write(os.path.join(source, name), os.path.join(theme, name)) - except (IOError, OSError): + theme_zip.close() + return True + except OSError as ose: + self.log_exception('Export Theme Failed') + critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'), + translate('OpenLP.ThemeManager', 'The theme export failed because this error ' + 'occurred: %s') % ose.strerror) if theme_zip: theme_zip.close() shutil.rmtree(theme_path, True) - raise - else: - theme_zip.close() + return False def on_import_theme(self, field=None): """ diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py index a8becc041..df72514fa 100644 --- a/openlp/plugins/bibles/lib/__init__.py +++ b/openlp/plugins/bibles/lib/__init__.py @@ -371,7 +371,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): from_chapter = from_verse from_verse = None if to_chapter: - if to_chapter < from_chapter: + if from_chapter and to_chapter < from_chapter: continue else: chapter = to_chapter @@ -387,7 +387,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False): from_verse = 1 if not to_verse: to_verse = -1 - if to_chapter > from_chapter: + if to_chapter and to_chapter > from_chapter: ref_list.append((book_ref_id, from_chapter, from_verse, -1)) for i in range(from_chapter + 1, to_chapter): ref_list.append((book_ref_id, i, 1, -1)) diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 2f46efebd..7334dd259 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -365,31 +365,20 @@ class CWExtract(RegistryProperties): if not soup: return None self.application.process_events() - html_verses = soup.find_all('span', 'versetext') - if not html_verses: + verses_div = soup.find_all('div', 'verse') + if not verses_div: log.error('No verses found in the CrossWalk response.') send_error_message('parse') return None verses = {} - for verse in html_verses: + for verse in verses_div: self.application.process_events() - verse_number = int(verse.contents[0].contents[0]) - verse_text = '' - for part in verse.contents: - self.application.process_events() - if isinstance(part, NavigableString): - verse_text += part - elif part and part.attrMap and \ - (part.attrMap['class'] == 'WordsOfChrist' or part.attrMap['class'] == 'strongs'): - for subpart in part.contents: - self.application.process_events() - if isinstance(subpart, NavigableString): - verse_text += subpart - elif subpart and subpart.attrMap and subpart.attrMap['class'] == 'strongs': - for subsub in subpart.contents: - self.application.process_events() - if isinstance(subsub, NavigableString): - verse_text += subsub + verse_number = int(verse.find('strong').contents[0]) + verse_span = verse.find('span') + tags_to_remove = verse_span.find_all(['a', 'sup']) + for tag in tags_to_remove: + tag.decompose() + verse_text = verse_span.get_text() self.application.process_events() # Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and . verse_text = verse_text.strip('\n\r\t ') @@ -409,16 +398,13 @@ class CWExtract(RegistryProperties): soup = get_soup_for_bible_ref(chapter_url) if not soup: return None - content = soup.find('div', {'class': 'Body'}) - content = content.find('ul', {'class': 'parent'}) + content = soup.find_all(('h4', {'class': 'small-header'})) if not content: log.error('No books found in the Crosswalk response.') send_error_message('parse') return None - content = content.find_all('li') books = [] for book in content: - book = book.find('a') books.append(book.contents[0]) return books diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py index 52e232ac9..07ec4ab23 100644 --- a/openlp/plugins/bibles/lib/osis.py +++ b/openlp/plugins/bibles/lib/osis.py @@ -58,7 +58,7 @@ class OSISBible(BibleDB): # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding # detection, and the two mechanisms together interfere with each other. import_file = open(self.filename, 'rb') - osis_bible_tree = etree.parse(import_file) + osis_bible_tree = etree.parse(import_file, parser=etree.XMLParser(recover=True)) namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'} # Find bible language language_id = None diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py index 355fb82cf..ad8f96648 100644 --- a/openlp/plugins/bibles/lib/zefania.py +++ b/openlp/plugins/bibles/lib/zefania.py @@ -57,7 +57,7 @@ class ZefaniaBible(BibleDB): # NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding # detection, and the two mechanisms together interfere with each other. import_file = open(self.filename, 'rb') - zefania_bible_tree = etree.parse(import_file) + zefania_bible_tree = etree.parse(import_file, parser=etree.XMLParser(recover=True)) # Find bible language language_id = None language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()") @@ -76,9 +76,19 @@ class ZefaniaBible(BibleDB): etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False) xmlbible = zefania_bible_tree.getroot() for BIBLEBOOK in xmlbible: - book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books) - if not book_ref_id: - book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname'))) + if self.stop_import_flag: + break + bname = BIBLEBOOK.get('bname') + bnumber = BIBLEBOOK.get('bnumber') + if not bname and not bnumber: + continue + if bname: + book_ref_id = self.get_book_ref_id_by_name(bname, num_books) + if not book_ref_id: + book_ref_id = self.get_book_ref_id_by_localised_name(bname) + else: + log.debug('Could not find a name, will use number, basically a guess.') + book_ref_id = int(bnumber) if not book_ref_id: log.error('Importing books from "%s" failed' % self.filename) return False @@ -94,7 +104,7 @@ class ZefaniaBible(BibleDB): self.wizard.increment_progress_bar( translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...' % {'bookname': db_book.name, 'chapter': chapter_number})) - self.session.commit() + self.session.commit() self.application.process_events() except Exception as e: critical_error_message_box( diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py index 04553c375..10b295235 100644 --- a/openlp/plugins/presentations/lib/mediaitem.py +++ b/openlp/plugins/presentations/lib/mediaitem.py @@ -225,10 +225,10 @@ class PresentationMediaItem(MediaManagerItem): self.clean_up_thumbnails(filepath) self.main_window.increment_progress_bar() self.main_window.finished_progress_bar() - self.application.set_busy_cursor() for row in row_list: self.list_view.takeItem(row) Settings().setValue(self.settings_section + '/presentations files', self.get_file_list()) + self.application.set_normal_cursor() def clean_up_thumbnails(self, filepath): """ diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 06d17694a..926971924 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -134,7 +134,7 @@ class PresentationDocument(object): """ # TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed if Settings().value('presentations/thumbnail_scheme') == 'md5': - folder = md5_hash('', self.file_path) + folder = md5_hash(self.file_path.encode('utf-8')) else: folder = self.get_file_name() return os.path.join(self.controller.thumbnail_folder, folder) @@ -145,7 +145,7 @@ class PresentationDocument(object): """ # TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed if Settings().value('presentations/thumbnail_scheme') == 'md5': - folder = md5_hash('', self.file_path) + folder = md5_hash(self.file_path.encode('utf-8')) else: folder = folder = self.get_file_name() return os.path.join(self.controller.temp_folder, folder) diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py index 9caf7a446..f96c46638 100644 --- a/openlp/plugins/songs/forms/songexportform.py +++ b/openlp/plugins/songs/forms/songexportform.py @@ -244,12 +244,16 @@ class SongExportForm(OpenLPWizard): for song in self._find_list_widget_items(self.selected_list_widget) ] exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text()) - if exporter.do_export(): - self.progress_label.setText( - translate('SongsPlugin.SongExportForm', - 'Finished export. To import these files use the OpenLyrics importer.')) - else: - self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.')) + try: + if exporter.do_export(): + self.progress_label.setText( + translate('SongsPlugin.SongExportForm', + 'Finished export. To import these files use the OpenLyrics importer.')) + else: + self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.')) + except OSError as ose: + self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed because this ' + 'error occurred: %s') % ose.strerror) def _find_list_widget_items(self, list_widget, text=''): """ diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py index f35b812a1..523bc5763 100644 --- a/openlp/plugins/songs/lib/importers/presentationmanager.py +++ b/openlp/plugins/songs/lib/importers/presentationmanager.py @@ -25,7 +25,9 @@ Presentationmanager song files into the current database. """ import os -from lxml import objectify +import re +import chardet +from lxml import objectify, etree from openlp.core.ui.wizard import WizardStrings from .songimport import SongImport @@ -42,7 +44,18 @@ class PresentationManagerImport(SongImport): if self.stop_import_flag: return self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path)) - root = objectify.parse(open(file_path, 'rb')).getroot() + try: + tree = etree.parse(file_path, parser=etree.XMLParser(recover=True)) + except etree.XMLSyntaxError: + # Try to detect encoding and use it + file = open(file_path, mode='rb') + encoding = chardet.detect(file.read())['encoding'] + file.close() + # Open file with detected encoding and remove encoding declaration + text = open(file_path, mode='r', encoding=encoding).read() + text = re.sub('.+\?>\n', '', text) + tree = etree.fromstring(text, parser=etree.XMLParser(recover=True)) + root = objectify.fromstring(etree.tostring(tree)) self.process_song(root) def process_song(self, root): diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py index ba65e21df..a4136e88b 100644 --- a/openlp/plugins/songusage/forms/songusagedetailform.py +++ b/openlp/plugins/songusage/forms/songusagedetailform.py @@ -27,6 +27,7 @@ from PyQt4 import QtGui from sqlalchemy.sql import and_ from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate +from openlp.core.lib.ui import critical_error_message_box from openlp.plugins.songusage.lib.db import SongUsageItem from .songusagedetaildialog import Ui_SongUsageDetailDialog @@ -104,8 +105,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog, RegistryPrope translate('SongUsagePlugin.SongUsageDetailForm', 'Report \n%s \nhas been successfully created. ') % report_file_name ) - except IOError: + except OSError as ose: log.exception('Failed to write out song usage records') + critical_error_message_box(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation Failed'), + translate('SongUsagePlugin.SongUsageDetailForm', + 'An error occurred while creating the report: %s') % ose.strerror) finally: if file_handle: file_handle.close() diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc index ba0f10e96..11c3482da 100644 --- a/resources/images/openlp-2.qrc +++ b/resources/images/openlp-2.qrc @@ -83,6 +83,7 @@ openlp-logo-64x64.png openlp-logo-128x128.png openlp-logo-256x256.png + openlp-logo.svg exception.png diff --git a/tests/functional/openlp_core_common/test_projector_utilities.py b/tests/functional/openlp_core_common/test_projector_utilities.py index fd9a2c57e..df06a3efd 100644 --- a/tests/functional/openlp_core_common/test_projector_utilities.py +++ b/tests/functional/openlp_core_common/test_projector_utilities.py @@ -23,6 +23,8 @@ Package to test the openlp.core.ui.projector.networkutils package. """ +import os + from unittest import TestCase from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash @@ -30,6 +32,8 @@ from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash salt = '498e4a67' pin = 'JBMIAProjectorLink' test_hash = '5d8409bc1c3fa39749434aa3a5c38682' +test_non_ascii_string = '이것은 한국어 시험 문자열' +test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201' ip4_loopback = '127.0.0.1' ip4_local = '192.168.1.1' @@ -120,7 +124,7 @@ class testProjectorUtilities(TestCase): Test MD5 hash from salt+data pass (python) """ # WHEN: Given a known salt+data - hash_ = md5_hash(salt=salt, data=pin) + hash_ = md5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii')) # THEN: Validate return has is same self.assertEquals(hash_, test_hash, 'MD5 should have returned a good hash') @@ -130,7 +134,7 @@ class testProjectorUtilities(TestCase): Test MD5 hash from salt+data fail (python) """ # WHEN: Given a different salt+hash - hash_ = md5_hash(salt=pin, data=salt) + hash_ = md5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii')) # THEN: return data is different self.assertNotEquals(hash_, test_hash, 'MD5 should have returned a bad hash') @@ -140,17 +144,37 @@ class testProjectorUtilities(TestCase): Test MD5 hash from salt+data pass (Qt) """ # WHEN: Given a known salt+data - hash_ = qmd5_hash(salt=salt, data=pin) + hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii')) # THEN: Validate return has is same - self.assertEquals(hash_, test_hash, 'Qt-MD5 should have returned a good hash') + self.assertEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a good hash') def test_qmd5_hash_bad(self): """ Test MD5 hash from salt+hash fail (Qt) """ # WHEN: Given a different salt+hash - hash_ = qmd5_hash(salt=pin, data=salt) + hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii')) # THEN: return data is different - self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash') + self.assertNotEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a bad hash') + + def test_md5_non_ascii_string(self): + """ + Test MD5 hash with non-ascii string - bug 1417809 + """ + # WHEN: Non-ascii string is hashed + hash_ = md5_hash(salt=test_non_ascii_string.encode('utf-8'), data=None) + + # THEN: Valid MD5 hash should be returned + self.assertEqual(hash_, test_non_ascii_hash, 'MD5 should have returned a valid hash') + + def test_qmd5_non_ascii_string(self): + """ + Test MD5 hash with non-ascii string - bug 1417809 + """ + # WHEN: Non-ascii string is hashed + hash_ = md5_hash(salt=test_non_ascii_string.encode('utf-8'), data=None) + + # THEN: Valid MD5 hash should be returned + self.assertEqual(hash_, test_non_ascii_hash, 'Qt-MD5 should have returned a valid hash') diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py index 0a234903c..63545a00c 100644 --- a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py +++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py @@ -77,7 +77,6 @@ class TestZefaniaImport(TestCase): mocked_import_wizard = MagicMock() importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') importer.wizard = mocked_import_wizard - importer.get_book_ref_id_by_name = MagicMock() importer.create_verse = MagicMock() importer.create_book = MagicMock() importer.session = MagicMock() @@ -92,3 +91,34 @@ class TestZefaniaImport(TestCase): self.assertTrue(importer.create_verse.called) for verse_tag, verse_text in test_data['verses']: importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text) + importer.create_book.assert_any_call('Genesis', 1, 1) + + def file_import_no_book_name_test(self): + """ + Test the import of Zefania Bible file without book names + """ + # GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions + # get_book_ref_id_by_name, create_verse, create_book, session and get_language. + result_file = open(os.path.join(TEST_PATH, 'rst.json'), 'rb') + test_data = json.loads(result_file.read().decode()) + bible_file = 'zefania-rst.xml' + with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application'): + mocked_manager = MagicMock() + mocked_import_wizard = MagicMock() + importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='') + importer.wizard = mocked_import_wizard + importer.create_verse = MagicMock() + importer.create_book = MagicMock() + importer.session = MagicMock() + importer.get_language = MagicMock() + importer.get_language.return_value = 'Russian' + + # WHEN: Importing bible file + importer.filename = os.path.join(TEST_PATH, bible_file) + importer.do_import() + + # THEN: The create_verse() method should have been called with each verse in the file. + self.assertTrue(importer.create_verse.called) + for verse_tag, verse_text in test_data['verses']: + importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text) + importer.create_book.assert_any_call('Exodus', 2, 1) diff --git a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py index 218596cf7..585aa34c5 100644 --- a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py +++ b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py @@ -46,3 +46,5 @@ class TestPresentationManagerFileImport(SongImportTestHelper): self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json'))) self.file_import([os.path.join(TEST_PATH, 'Agnus Dei.sng')], self.load_external_result_data(os.path.join(TEST_PATH, 'Agnus Dei.json'))) + self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')], + self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json'))) diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 5ad3a594c..700933465 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -506,7 +506,6 @@ class TestSongSelectFileImport(TestCase, TestMixin): # WHEN: We call the song importer song_import.do_import() - print(song_import.verses) # THEN: Song values should be equal to test values in setUp self.assertEquals(song_import.title, self.title, 'Song title should match') self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match') diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py index 4c0e0fe14..b4eec48af 100644 --- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py +++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py @@ -109,3 +109,13 @@ class TestBibleManager(TestCase, TestMixin): results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock()) # THEN a verse array should be returned self.assertEqual(False, results, "The bible Search should return False") + + def parse_reference_five_test(self): + """ + Test the parse_reference method with 1 Timothy 1:3-end + """ + # GIVEN given a bible in the bible manager + # WHEN asking to parse the bible reference + results = parse_reference('1 Timothy 1:3-end', self.manager.db_cache['tests'], MagicMock(), 54) + # THEN a verse array should be returned + self.assertEqual([(54, 1, 3, -1)], results, "The bible verses should matches the expected results") diff --git a/tests/resources/bibles/rst.json b/tests/resources/bibles/rst.json new file mode 100644 index 000000000..d8aca09ac --- /dev/null +++ b/tests/resources/bibles/rst.json @@ -0,0 +1,16 @@ +{ + "book": "Exodus", + "chapter": 1, + "verses": [ + [ "1", "Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:" ], + [ "2", "Рувим, Симеон, Левий и Иуда," ], + [ "3", "Иссахар, Завулон и Вениамин," ], + [ "4", "Дан и Неффалим, Гад и Асир." ], + [ "5", "Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте." ], + [ "6", "И умер Иосиф и все братья его и весь род их;" ], + [ "7", "а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та." ], + [ "8", "И восстал в Египте новый царь, который не знал Иосифа," ], + [ "9", "и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;" ], + [ "10", "перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]." ] + ] +} diff --git a/tests/resources/bibles/zefania-rst.xml b/tests/resources/bibles/zefania-rst.xml new file mode 100644 index 000000000..72d1230a4 --- /dev/null +++ b/tests/resources/bibles/zefania-rst.xml @@ -0,0 +1,48 @@ + + + + + + + + Russian Synodal Translation + Zefania XML Bible Markup Language + 2009-01-20 + Jens Grabner + http://www.agape-biblia.org + http://www.crosswire.org/sword/modules/ + RUS + + RST + + 1876 Russian Synodal Translation, 1956 Edition + The text was supplied by "Light in East Germany". + + + + "Light in East Germany" Tel +49 711 83 30 57 + Postfach 1340 Fax +49 711 83 13 51 + 7015 Korntal + Munchingen 1 + Germany + + + + + + + + Вторая книга Моисеева. Исход + Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим: + Рувим, Симеон, Левий и Иуда, + Иссахар, Завулон и Вениамин, + Дан и Неффалим, Гад и Асир. + Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте. + И умер Иосиф и все братья его и весь род их; + а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та. + И восстал в Египте новый царь, который не знал Иосифа, + и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас; + перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]. + + + diff --git a/tests/resources/presentationmanagersongs/Amazing Grace.json b/tests/resources/presentationmanagersongs/Amazing Grace.json new file mode 100644 index 000000000..fa2e182a9 --- /dev/null +++ b/tests/resources/presentationmanagersongs/Amazing Grace.json @@ -0,0 +1,29 @@ +{ + "title": "Amazing Grace", + "authors": [ + "John Newton" + ], + "verse_order_list": ["v1", "v2", "v3", "v4", "v5"], + "verses": [ + [ + "Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.", + "v1" + ], + [ + "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.", + "v2" + ], + [ + "The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.", + "v3" + ], + [ + "Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.", + "v4" + ], + [ + "When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.", + "v5" + ] + ] +} diff --git a/tests/resources/presentationmanagersongs/Amazing Grace.sng b/tests/resources/presentationmanagersongs/Amazing Grace.sng new file mode 100644 index 000000000..47b5ff3b3 Binary files /dev/null and b/tests/resources/presentationmanagersongs/Amazing Grace.sng differ