diff --git a/openlp/plugins/songs/lib/openlyricsxml.py b/openlp/plugins/songs/lib/openlyricsxml.py
index 6a5e0506f..d3388b236 100644
--- a/openlp/plugins/songs/lib/openlyricsxml.py
+++ b/openlp/plugins/songs/lib/openlyricsxml.py
@@ -129,12 +129,13 @@ class SongXML(object):
self.song_xml = objectify.fromstring(xml)
except etree.XMLSyntaxError:
log.exception('Invalid xml {text}'.format(text=xml))
- xml_iter = self.song_xml.getiterator()
- for element in xml_iter:
- if element.tag == 'verse':
- if element.text is None:
- element.text = ''
- verse_list.append([element.attrib, str(element.text)])
+ if self.song_xml is not None:
+ xml_iter = self.song_xml.getiterator()
+ for element in xml_iter:
+ if element.tag == 'verse':
+ if element.text is None:
+ element.text = ''
+ verse_list.append([element.attrib, str(element.text)])
return verse_list
def dump_xml(self):
diff --git a/tests/openlp_plugins/songs/test_openlyricsexport.py b/tests/openlp_plugins/songs/test_openlyricsexport.py
index 744a76d4c..e03b8b97d 100644
--- a/tests/openlp_plugins/songs/test_openlyricsexport.py
+++ b/tests/openlp_plugins/songs/test_openlyricsexport.py
@@ -28,7 +28,6 @@ from unittest.mock import MagicMock, patch
import pytest
-# from openlp.core.common.registry import Registry
from openlp.plugins.songs.lib.openlyricsexport import OpenLyricsExport
diff --git a/tests/openlp_plugins/songs/test_openlyricsimport.py b/tests/openlp_plugins/songs/test_openlyricsimport.py
old mode 100755
new mode 100644
index dc7fee1d2..7af3681d3
--- a/tests/openlp_plugins/songs/test_openlyricsimport.py
+++ b/tests/openlp_plugins/songs/test_openlyricsimport.py
@@ -21,17 +21,15 @@
"""
This module contains tests for the OpenLyrics song importer.
"""
-import json
from unittest import TestCase
from unittest.mock import MagicMock, patch
-from lxml import etree, objectify
+from lxml import etree
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.plugins.songs.lib.importers.openlyrics import OpenLyricsImport
from openlp.plugins.songs.lib.importers.songimport import SongImport
-from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics
from openlp.plugins.songs.lib.ui import SongStrings
from tests.helpers.testmixin import TestMixin
from tests.utils.constants import RESOURCE_PATH
@@ -58,29 +56,6 @@ SONG_TEST_DATA = {
}
}
-start_tags = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "", "temporary": False,
- "end tag": "{/z}", "start html": "strong>"}]
-result_tags = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>",
- "end html": "", "end tag": "{/z}"},
- {"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}",
- "start html": "", "end html": "",
- "protected": False}]
-
-author_xml = '\
- \
- Test Author1\
- Test Author1\
- Test Author2\
- \
- '
-
-songbook_xml = '\
- \
- \
- \
- \
- '
-
class TestOpenLyricsImport(TestCase, TestMixin):
"""
@@ -177,64 +152,6 @@ class TestOpenLyricsImport(TestCase, TestMixin):
error_message = args[0]
assert not error_message.startswith('XML syntax error in file')
- def test_process_formatting_tags(self):
- """
- Test that _process_formatting_tags works
- """
- # GIVEN: A OpenLyric XML with formatting tags and a mocked out manager
- mocked_manager = MagicMock()
- Settings().setValue('formattingTags/html_tags', json.dumps(start_tags))
- ol = OpenLyrics(mocked_manager)
- parser = etree.XMLParser(remove_blank_text=True)
- parsed_file = etree.parse((TEST_PATH / 'duchu-tags.xml').open('rb'), parser)
- 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
- 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'
-
- def test_process_author(self):
- """
- Test that _process_authors works
- """
- # GIVEN: A OpenLyric XML with authors and a mocked out manager
- with patch('openlp.plugins.songs.lib.openlyricsxml.Author'):
- 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
- assert mocked_song.method_calls[0][1][1] == 'words+music'
- assert mocked_song.method_calls[1][1][1] == 'words'
-
- def test_process_songbooks(self):
- """
- 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
- assert mocked_song.method_calls[0][1][1] == '48'
- assert mocked_song.method_calls[1][1][1] == '445 A'
-
def test_leading_and_trailing_whitespaces_inside_lines_tags_are_removed(self):
"""
Test that leading and trailing whitespace inside tags and its descendants are removed
diff --git a/tests/openlp_plugins/songs/test_openlyricsxml.py b/tests/openlp_plugins/songs/test_openlyricsxml.py
new file mode 100644
index 000000000..0efab5226
--- /dev/null
+++ b/tests/openlp_plugins/songs/test_openlyricsxml.py
@@ -0,0 +1,126 @@
+# -*- coding: utf-8 -*-
+
+##########################################################################
+# OpenLP - Open Source Lyrics Projection #
+# ---------------------------------------------------------------------- #
+# Copyright (c) 2008-2022 OpenLP Developers #
+# ---------------------------------------------------------------------- #
+# 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 . #
+##########################################################################
+"""
+This module contains tests for the OpenLyrics song importer.
+"""
+import json
+from unittest.mock import MagicMock, patch
+
+from lxml import etree, objectify
+
+from openlp.plugins.songs.lib.openlyricsxml import OpenLyrics, SongXML
+from tests.utils.constants import RESOURCE_PATH
+
+TEST_PATH = RESOURCE_PATH / 'songs' / 'openlyrics'
+START_TAGS = [{"protected": False, "desc": "z", "start tag": "{z}", "end html": "", "temporary": False,
+ "end tag": "{/z}", "start html": "strong>"}]
+RESULT_TAGS = [{"temporary": False, "protected": False, "desc": "z", "start tag": "{z}", "start html": "strong>",
+ "end html": "", "end tag": "{/z}"},
+ {"temporary": False, "end tag": "{/c}", "desc": "c", "start tag": "{c}",
+ "start html": "", "end html": "",
+ "protected": False}]
+AUTHOR_XML = '\
+ \
+ Test Author1\
+ Test Author1\
+ Test Author2\
+ \
+ '
+SONGBOOK_XML = '\
+ \
+ \
+ \
+ \
+ '
+
+
+def test_songxml_get_verses_invalid_xml():
+ """Test that invalid XML for a song is ignored"""
+ # GIVEN: Invalid XML and a SongXML object
+ invalid_xml = 'this is not xml'
+ song_xml = SongXML()
+
+ # WHEN: get_verses is called with invalid XML
+ result = song_xml.get_verses(invalid_xml)
+
+ # THEN: An empty list is returned
+ assert result == []
+
+
+def test_process_formatting_tags(settings):
+ """
+ Test that _process_formatting_tags works
+ """
+ # GIVEN: A OpenLyric XML with formatting tags and a mocked out manager
+ mocked_manager = MagicMock()
+ settings.setValue('formattingTags/html_tags', json.dumps(START_TAGS))
+ ol = OpenLyrics(mocked_manager)
+ parser = etree.XMLParser(remove_blank_text=True)
+ parsed_file = etree.parse((TEST_PATH / 'duchu-tags.xml').open('rb'), parser)
+ 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
+ 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'
+
+
+def test_process_author(registry, settings):
+ """
+ Test that _process_authors works
+ """
+ # GIVEN: A OpenLyric XML with authors and a mocked out manager
+ with patch('openlp.plugins.songs.lib.openlyricsxml.Author'):
+ 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
+ assert mocked_song.method_calls[0][1][1] == 'words+music'
+ assert mocked_song.method_calls[1][1][1] == 'words'
+
+
+def test_process_songbooks(registry, settings):
+ """
+ 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
+ assert mocked_song.method_calls[0][1][1] == '48'
+ assert mocked_song.method_calls[1][1][1] == '445 A'