2014-04-10 07:50:30 +00:00
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
2019-04-13 13:00:22 +00:00
|
|
|
|
##########################################################################
|
|
|
|
|
# OpenLP - Open Source Lyrics Projection #
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
2020-12-30 21:42:49 +00:00
|
|
|
|
# Copyright (c) 2008-2021 OpenLP Developers #
|
2019-04-13 13:00:22 +00:00
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
|
|
|
# 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, either version 3 of the License, or #
|
|
|
|
|
# (at your option) any later version. #
|
|
|
|
|
# #
|
|
|
|
|
# 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, see <https://www.gnu.org/licenses/>. #
|
|
|
|
|
##########################################################################
|
2014-04-10 07:50:30 +00:00
|
|
|
|
"""
|
|
|
|
|
This module contains tests for the OpenLyrics song importer.
|
|
|
|
|
"""
|
2014-09-25 15:01:11 +00:00
|
|
|
|
import json
|
2014-04-10 07:50:30 +00:00
|
|
|
|
from unittest import TestCase
|
2017-04-24 05:17:55 +00:00
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
2014-09-25 15:01:11 +00:00
|
|
|
|
from lxml import etree, objectify
|
2014-04-10 07:50:30 +00:00
|
|
|
|
|
2017-10-07 07:05:07 +00:00
|
|
|
|
from openlp.core.common.registry import Registry
|
|
|
|
|
from openlp.core.common.settings import Settings
|
2014-07-04 09:31:06 +00:00
|
|
|
|
from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport
|
|
|
|
|
from openlp.plugins.songs.lib.importers.songimport import SongImport
|
2014-09-25 15:01:11 +00:00
|
|
|
|
from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
|
2020-07-13 08:48:33 +00:00
|
|
|
|
from openlp.plugins.songs.lib.ui import SongStrings
|
2017-04-24 05:17:55 +00:00
|
|
|
|
from tests.helpers.testmixin import TestMixin
|
2017-12-22 21:20:49 +00:00
|
|
|
|
from tests.utils.constants import RESOURCE_PATH
|
2014-04-10 07:50:30 +00:00
|
|
|
|
|
2018-10-02 04:39:42 +00:00
|
|
|
|
|
2017-12-24 08:42:46 +00:00
|
|
|
|
TEST_PATH = RESOURCE_PATH / 'songs' / 'openlyrics'
|
2014-04-15 22:23:29 +00:00
|
|
|
|
SONG_TEST_DATA = {
|
2014-04-16 21:01:57 +00:00
|
|
|
|
'What a friend we have in Jesus.xml': {
|
|
|
|
|
'title': 'What A Friend We Have In Jesus',
|
|
|
|
|
'verses': [
|
|
|
|
|
('What a friend we have in Jesus, All ours sins and griefs to bear;\n\
|
|
|
|
|
What a privilege to carry, Everything to God in prayer!\n\
|
|
|
|
|
O what peace we often forfeit, O what needless pain we bear;\n\
|
|
|
|
|
All because we do not carry, Everything to God in prayer!', 'v1'),
|
|
|
|
|
('Have we trials and temptations? Is there trouble anywhere?\n\
|
|
|
|
|
We should never be discouraged, Take it to the Lord in prayer.\n\
|
|
|
|
|
Can we find a friend so faithful? Who will all our sorrows share?\n\
|
|
|
|
|
Jesus knows our every weakness; Take it to the Lord in prayer.', 'v2'),
|
|
|
|
|
('Are we weak and heavy laden, Cumbered with a load of care?\n\
|
|
|
|
|
Precious Saviour still our refuge; Take it to the Lord in prayer.\n\
|
|
|
|
|
Do thy friends despise forsake thee? Take it to the Lord in prayer!\n\
|
|
|
|
|
In His arms He’ll take and shield thee; Thou wilt find a solace there.', 'v3')
|
2014-04-15 22:23:29 +00:00
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-25 20:31:31 +00:00
|
|
|
|
start_tags = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "</strong>", "temporary": False,
|
|
|
|
|
"end tag": "{/z}", "start html": "strong>"}]
|
|
|
|
|
result_tags = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>",
|
|
|
|
|
"end html": "</strong>", "end tag": "{/z}"},
|
|
|
|
|
{"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}",
|
|
|
|
|
"start html": "<span class=\"chord\" style=\"display:none\"><strong>", "end html": "</strong></span>",
|
|
|
|
|
"protected": False}]
|
2014-09-25 15:01:11 +00:00
|
|
|
|
|
2015-01-18 20:38:03 +00:00
|
|
|
|
author_xml = '<properties>\
|
|
|
|
|
<authors>\
|
|
|
|
|
<author type="words">Test Author1</author>\
|
|
|
|
|
<author type="music">Test Author1</author>\
|
|
|
|
|
<author type="words">Test Author2</author>\
|
|
|
|
|
</authors>\
|
|
|
|
|
</properties>'
|
|
|
|
|
|
2016-01-30 14:13:35 +00:00
|
|
|
|
songbook_xml = '<properties>\
|
|
|
|
|
<songbooks>\
|
|
|
|
|
<songbook name="Collection 1" entry="48"/>\
|
|
|
|
|
<songbook name="Collection 2" entry="445 A"/>\
|
|
|
|
|
</songbooks>\
|
|
|
|
|
</properties>'
|
|
|
|
|
|
2014-04-10 07:50:30 +00:00
|
|
|
|
|
2014-09-25 15:01:11 +00:00
|
|
|
|
class TestOpenLyricsImport(TestCase, TestMixin):
|
2014-04-10 07:50:30 +00:00
|
|
|
|
"""
|
|
|
|
|
Test the functions in the :mod:`openlyricsimport` module.
|
|
|
|
|
"""
|
2014-09-25 15:01:11 +00:00
|
|
|
|
def setUp(self):
|
|
|
|
|
"""
|
|
|
|
|
Create the registry
|
|
|
|
|
"""
|
2014-10-22 20:47:47 +00:00
|
|
|
|
self.setup_application()
|
2014-09-25 15:01:11 +00:00
|
|
|
|
Registry.create()
|
|
|
|
|
self.build_settings()
|
2020-02-01 21:12:07 +00:00
|
|
|
|
Registry().register('settings', Settings())
|
2014-09-25 15:01:11 +00:00
|
|
|
|
|
|
|
|
|
def tearDown(self):
|
|
|
|
|
"""
|
|
|
|
|
Cleanup
|
|
|
|
|
"""
|
|
|
|
|
self.destroy_settings()
|
|
|
|
|
|
2016-05-31 21:40:13 +00:00
|
|
|
|
def test_create_importer(self):
|
2014-04-10 07:50:30 +00:00
|
|
|
|
"""
|
|
|
|
|
Test creating an instance of the OpenLyrics file importer
|
|
|
|
|
"""
|
|
|
|
|
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
|
2014-07-04 09:31:06 +00:00
|
|
|
|
with patch('openlp.plugins.songs.lib.importers.openlyrics.SongImport'):
|
2014-04-10 07:50:30 +00:00
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
|
|
|
|
|
# WHEN: An importer object is created
|
2017-09-30 20:16:30 +00:00
|
|
|
|
importer = OpenLyricsImport(mocked_manager, file_paths=[])
|
2014-04-10 07:50:30 +00:00
|
|
|
|
|
|
|
|
|
# THEN: The importer should be an instance of SongImport
|
2017-12-22 16:53:40 +00:00
|
|
|
|
assert isinstance(importer, SongImport)
|
2014-04-15 22:23:29 +00:00
|
|
|
|
|
2016-05-31 21:40:13 +00:00
|
|
|
|
def test_file_import(self):
|
2014-04-15 22:23:29 +00:00
|
|
|
|
"""
|
2014-04-16 20:42:44 +00:00
|
|
|
|
Test the actual import of real song files
|
2014-04-15 22:23:29 +00:00
|
|
|
|
"""
|
|
|
|
|
# GIVEN: Test files with a mocked out "manager" and a mocked out "import_wizard"
|
|
|
|
|
for song_file in SONG_TEST_DATA:
|
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
mocked_import_wizard = MagicMock()
|
2017-09-30 20:16:30 +00:00
|
|
|
|
importer = OpenLyricsImport(mocked_manager, file_paths=[])
|
2014-04-15 22:23:29 +00:00
|
|
|
|
importer.import_wizard = mocked_import_wizard
|
2014-04-16 20:33:06 +00:00
|
|
|
|
importer.open_lyrics = MagicMock()
|
|
|
|
|
importer.open_lyrics.xml_to_song = MagicMock()
|
2014-04-15 22:23:29 +00:00
|
|
|
|
|
|
|
|
|
# WHEN: Importing each file
|
2017-12-22 21:20:49 +00:00
|
|
|
|
importer.import_source = [TEST_PATH / song_file]
|
2014-04-16 20:33:06 +00:00
|
|
|
|
importer.do_import()
|
2014-04-15 22:23:29 +00:00
|
|
|
|
|
2014-04-16 20:33:06 +00:00
|
|
|
|
# THEN: The xml_to_song() method should have been called
|
2017-12-22 22:20:04 +00:00
|
|
|
|
assert importer.open_lyrics.xml_to_song.called is True
|
2014-09-25 15:01:11 +00:00
|
|
|
|
|
2020-07-13 08:48:33 +00:00
|
|
|
|
def test_can_parse_file_having_a_processing_instruction(self):
|
|
|
|
|
"""
|
|
|
|
|
Test files having a processing instruction can be parsed
|
|
|
|
|
"""
|
|
|
|
|
# GIVEN: A OpenLyrics XML containing a processing instruction, an OpenLyrics importer with a mocked out
|
|
|
|
|
# manager, import wizard and 'log_error' method, and a mocked out logger
|
|
|
|
|
with patch('openlp.plugins.songs.lib.importers.openlyrics.log', autospec=True) as mocked_logger:
|
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
mocked_import_wizard = MagicMock()
|
|
|
|
|
importer = OpenLyricsImport(mocked_manager, file_paths=[])
|
|
|
|
|
importer.import_wizard = mocked_import_wizard
|
|
|
|
|
importer.log_error = MagicMock()
|
|
|
|
|
|
|
|
|
|
# WHEN: Importing a file which contains a processing instruction
|
|
|
|
|
importer.import_source = [TEST_PATH / 'Amazing Grace.xml']
|
|
|
|
|
try:
|
|
|
|
|
importer.do_import()
|
|
|
|
|
except Exception as ex:
|
|
|
|
|
# THEN: no uncaught exception escaped from importer.do_import() is etree.XMLSyntaxError
|
|
|
|
|
assert ex is not etree.XMLSyntaxError
|
|
|
|
|
# otherwise we don't care about it now (but should in other tests...)
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# THEN: the importer's log_error method was never called with SongStrings.XMLSyntaxError as its second
|
|
|
|
|
# positional argument
|
|
|
|
|
if importer.log_error.called:
|
|
|
|
|
for call_args in importer.log_error.call_args_list:
|
|
|
|
|
args = call_args[0]
|
|
|
|
|
# there are at least two positional arguments
|
|
|
|
|
if len(args) > 1:
|
|
|
|
|
assert args[1] is not SongStrings.XMLSyntaxError
|
|
|
|
|
|
|
|
|
|
# THEN: the logger's 'exception' method was never called with a first positional argument
|
|
|
|
|
# which is a string and starts with 'XML syntax error in file'
|
|
|
|
|
if mocked_logger.exception.called:
|
|
|
|
|
for call_args in mocked_logger.exception.call_args_list:
|
|
|
|
|
args = call_args[0]
|
|
|
|
|
# there is at least one positional argument and it is a string
|
|
|
|
|
if args and isinstance(args[0], str):
|
|
|
|
|
error_message = args[0]
|
|
|
|
|
assert not error_message.startswith('XML syntax error in file')
|
|
|
|
|
|
2016-05-31 21:40:13 +00:00
|
|
|
|
def test_process_formatting_tags(self):
|
2014-09-25 15:01:11 +00:00
|
|
|
|
"""
|
|
|
|
|
Test that _process_formatting_tags works
|
|
|
|
|
"""
|
2014-09-25 20:31:31 +00:00
|
|
|
|
# GIVEN: A OpenLyric XML with formatting tags and a mocked out manager
|
2014-09-25 15:01:11 +00:00
|
|
|
|
mocked_manager = MagicMock()
|
2014-09-25 20:31:31 +00:00
|
|
|
|
Settings().setValue('formattingTags/html_tags', json.dumps(start_tags))
|
2014-09-25 15:01:11 +00:00
|
|
|
|
ol = OpenLyrics(mocked_manager)
|
|
|
|
|
parser = etree.XMLParser(remove_blank_text=True)
|
2017-12-22 21:20:49 +00:00
|
|
|
|
parsed_file = etree.parse((TEST_PATH / 'duchu-tags.xml').open('rb'), parser)
|
2014-09-25 15:01:11 +00:00
|
|
|
|
xml = etree.tostring(parsed_file).decode()
|
|
|
|
|
song_xml = objectify.fromstring(xml)
|
|
|
|
|
|
|
|
|
|
# WHEN: processing the formatting tags
|
|
|
|
|
ol._process_formatting_tags(song_xml, False)
|
|
|
|
|
|
|
|
|
|
# THEN: New tags should have been saved
|
2017-12-23 09:22:53 +00:00
|
|
|
|
assert json.loads(json.dumps(result_tags)) == json.loads(str(Settings().value('formattingTags/html_tags'))), \
|
|
|
|
|
'The formatting tags should contain both the old and the new'
|
2015-01-18 20:38:03 +00:00
|
|
|
|
|
2016-05-31 21:40:13 +00:00
|
|
|
|
def test_process_author(self):
|
2015-01-18 20:38:03 +00:00
|
|
|
|
"""
|
|
|
|
|
Test that _process_authors works
|
|
|
|
|
"""
|
|
|
|
|
# GIVEN: A OpenLyric XML with authors and a mocked out manager
|
2017-10-07 07:05:07 +00:00
|
|
|
|
with patch('openlp.plugins.songs.lib.openlyricsxml.Author'):
|
2015-01-18 20:38:03 +00:00
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
mocked_manager.get_object_filtered.return_value = None
|
|
|
|
|
ol = OpenLyrics(mocked_manager)
|
|
|
|
|
properties_xml = objectify.fromstring(author_xml)
|
|
|
|
|
mocked_song = MagicMock()
|
|
|
|
|
|
|
|
|
|
# WHEN: processing the author xml
|
|
|
|
|
ol._process_authors(properties_xml, mocked_song)
|
|
|
|
|
|
|
|
|
|
# THEN: add_author should have been called twice
|
2017-12-22 22:20:04 +00:00
|
|
|
|
assert mocked_song.method_calls[0][1][1] == 'words+music'
|
|
|
|
|
assert mocked_song.method_calls[1][1][1] == 'words'
|
2016-01-30 14:13:35 +00:00
|
|
|
|
|
2016-05-31 21:40:13 +00:00
|
|
|
|
def test_process_songbooks(self):
|
2016-01-30 14:13:35 +00:00
|
|
|
|
"""
|
|
|
|
|
Test that _process_songbooks works
|
|
|
|
|
"""
|
|
|
|
|
# GIVEN: A OpenLyric XML with songbooks and a mocked out manager
|
|
|
|
|
with patch('openlp.plugins.songs.lib.openlyricsxml.Book'):
|
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
mocked_manager.get_object_filtered.return_value = None
|
|
|
|
|
ol = OpenLyrics(mocked_manager)
|
|
|
|
|
properties_xml = objectify.fromstring(songbook_xml)
|
|
|
|
|
mocked_song = MagicMock()
|
|
|
|
|
|
|
|
|
|
# WHEN: processing the songbook xml
|
|
|
|
|
ol._process_songbooks(properties_xml, mocked_song)
|
|
|
|
|
|
|
|
|
|
# THEN: add_songbook_entry should have been called twice
|
2017-12-22 22:20:04 +00:00
|
|
|
|
assert mocked_song.method_calls[0][1][1] == '48'
|
|
|
|
|
assert mocked_song.method_calls[1][1][1] == '445 A'
|
2020-08-13 19:40:14 +00:00
|
|
|
|
|
|
|
|
|
def test_leading_and_trailing_whitespaces_inside_lines_tags_are_removed(self):
|
|
|
|
|
"""
|
|
|
|
|
Test that leading and trailing whitespace inside <lines> tags and its descendants are removed
|
|
|
|
|
"""
|
|
|
|
|
# GIVEN: One OpenLyrics XML with extra whitespaces in <lines> tag (Amazing_Grace_1.xml)
|
|
|
|
|
# and a copy which only difference is that it lacks those whitespaces (Amazing_Grace_2.xml)
|
|
|
|
|
mocked_manager = MagicMock()
|
|
|
|
|
mocked_import_wizard = MagicMock()
|
|
|
|
|
importer = OpenLyricsImport(mocked_manager, file_paths=[])
|
|
|
|
|
importer.import_wizard = mocked_import_wizard
|
|
|
|
|
importer.open_lyrics = MagicMock()
|
|
|
|
|
importer.open_lyrics.xml_to_song = MagicMock()
|
|
|
|
|
|
|
|
|
|
# WHEN: Importing the file not having those whitespaces...
|
|
|
|
|
importer.import_source = [TEST_PATH / 'Amazing_Grace_2.xml']
|
|
|
|
|
importer.do_import()
|
|
|
|
|
|
|
|
|
|
# keep the parsed XML which is assumed to be the first positional argument of the xml_to_song() method
|
|
|
|
|
importer.open_lyrics.xml_to_song.assert_called()
|
|
|
|
|
no_whitespaces_xml = importer.open_lyrics.xml_to_song.call_args[0][0]
|
|
|
|
|
|
|
|
|
|
# ... and importing the file having those whitespaces
|
|
|
|
|
importer.import_source = [TEST_PATH / 'Amazing_Grace_1.xml']
|
|
|
|
|
importer.do_import()
|
|
|
|
|
|
|
|
|
|
# THEN: The last call of the xml_to_song() method should have got the same XML content as its first call
|
|
|
|
|
importer.open_lyrics.xml_to_song.assert_called_with(no_whitespaces_xml)
|