Merge lp:~tomasgroth/openlp/bugfixes4 into lp:openlp

Proposed by Tomas Groth
Status: Merged
Approved by: Tim Bentley
Approved revision: 2448
Merged at revision: 2440
Proposed branch: lp:~tomasgroth/openlp/bugfixes4
Merge into: lp:openlp
Diff against target: 440 lines (+179/-40)
12 files modified
openlp.py (+7/-1)
openlp/core/ui/projector/__init__.py (+1/-1)
openlp/plugins/media/forms/mediaclipselectorform.py (+7/-0)
openlp/plugins/songs/forms/duplicatesongremovalform.py (+8/-6)
openlp/plugins/songs/lib/importers/wordsofworship.py (+11/-5)
openlp/plugins/songs/lib/songcompare.py (+9/-7)
tests/functional/openlp_core_ui/test_settingsform.py (+1/-1)
tests/functional/openlp_plugins/images/test_imagetab.py (+1/-1)
tests/functional/openlp_plugins/songs/test_lib.py (+16/-18)
tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py (+56/-0)
tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json (+33/-0)
tests/resources/wordsofworshipsongs/When morning gilds the skies.json (+29/-0)
To merge this branch: bzr merge lp:~tomasgroth/openlp/bugfixes4
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Raoul Snyman Approve
Review via email: mp+240831@code.launchpad.net

Description of the change

Change duplicate check to pass int-string tuples to workers, to workaround bug #1388850, also added multiprocessing.freeze_support to __main__ to support multiprocessing in windows builds.
Try to fix DVD 0 track length by waiting. Fixes bug 1387293.
Fix for import of Words of Worship file, bug 1388768, added tests.

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) wrote :
Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp.py'
--- openlp.py 2014-09-04 20:10:34 +0000
+++ openlp.py 2014-11-06 09:51:19 +0000
@@ -28,7 +28,9 @@
28###############################################################################28###############################################################################
2929
30import sys30import sys
31import multiprocessing
3132
33from openlp.core.common import is_win, is_macosx
32from openlp.core import main34from openlp.core import main
3335
3436
@@ -36,9 +38,13 @@
36 """38 """
37 Instantiate and run the application.39 Instantiate and run the application.
38 """40 """
41 # Add support for using multiprocessing from frozen Windows executable (built using PyInstaller),
42 # see https://docs.python.org/3/library/multiprocessing.html#multiprocessing.freeze_support
43 if is_win():
44 multiprocessing.freeze_support()
39 # Mac OS X passes arguments like '-psn_XXXX' to the application. This argument is actually a process serial number.45 # Mac OS X passes arguments like '-psn_XXXX' to the application. This argument is actually a process serial number.
40 # However, this causes a conflict with other OpenLP arguments. Since we do not use this argument we can delete it46 # However, this causes a conflict with other OpenLP arguments. Since we do not use this argument we can delete it
41 # to avoid any potential conflicts.47 # to avoid any potential conflicts.
42 if sys.platform.startswith('darwin'):48 if is_macosx():
43 sys.argv = [x for x in sys.argv if not x.startswith('-psn')]49 sys.argv = [x for x in sys.argv if not x.startswith('-psn')]
44 main()50 main()
4551
=== modified file 'openlp/core/ui/projector/__init__.py'
--- openlp/core/ui/projector/__init__.py 2014-10-31 19:47:36 +0000
+++ openlp/core/ui/projector/__init__.py 2014-11-06 09:51:19 +0000
@@ -28,4 +28,4 @@
28###############################################################################28###############################################################################
29"""29"""
30The Projector driver module.30The Projector driver module.
31"""
32\ No newline at end of file31\ No newline at end of file
32"""
3333
=== modified file 'openlp/plugins/media/forms/mediaclipselectorform.py'
--- openlp/plugins/media/forms/mediaclipselectorform.py 2014-10-31 22:44:22 +0000
+++ openlp/plugins/media/forms/mediaclipselectorform.py 2014-11-06 09:51:19 +0000
@@ -446,6 +446,13 @@
446 # Set media length info446 # Set media length info
447 self.playback_length = self.vlc_media_player.get_length()447 self.playback_length = self.vlc_media_player.get_length()
448 log.debug('playback_length: %d ms' % self.playback_length)448 log.debug('playback_length: %d ms' % self.playback_length)
449 # if length is 0, wait a bit, maybe vlc will change its mind...
450 loop_count = 0
451 while self.playback_length == 0 and loop_count < 20:
452 sleep(0.1)
453 self.playback_length = self.vlc_media_player.get_length()
454 loop_count += 1
455 log.debug('in loop, playback_length: %d ms' % self.playback_length)
449 self.position_slider.setMaximum(self.playback_length)456 self.position_slider.setMaximum(self.playback_length)
450 # setup start and end time457 # setup start and end time
451 rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0)458 rounded_vlc_ms_length = int(round(self.playback_length / 100.0) * 100.0)
452459
=== modified file 'openlp/plugins/songs/forms/duplicatesongremovalform.py'
--- openlp/plugins/songs/forms/duplicatesongremovalform.py 2014-04-21 09:49:17 +0000
+++ openlp/plugins/songs/forms/duplicatesongremovalform.py 2014-11-06 09:51:19 +0000
@@ -48,14 +48,15 @@
4848
49def song_generator(songs):49def song_generator(songs):
50 """50 """
51 This is a generator function to return tuples of two songs. When completed then all songs have once been returned51 This is a generator function to return tuples of tuple with two songs and their position in the song array.
52 combined with any other songs.52 When completed then all songs have once been returned combined with any other songs.
5353
54 :param songs: All songs in the database.54 :param songs: All songs in the database.
55 """55 """
56 for outer_song_counter in range(len(songs) - 1):56 for outer_song_counter in range(len(songs) - 1):
57 for inner_song_counter in range(outer_song_counter + 1, len(songs)):57 for inner_song_counter in range(outer_song_counter + 1, len(songs)):
58 yield (songs[outer_song_counter], songs[inner_song_counter])58 yield ((outer_song_counter, songs[outer_song_counter].search_lyrics),
59 (inner_song_counter, songs[inner_song_counter].search_lyrics))
5960
6061
61class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):62class DuplicateSongRemovalForm(OpenLPWizard, RegistryProperties):
@@ -187,16 +188,17 @@
187 # Do not accept any further tasks. Also this closes the processes if all tasks are done.188 # Do not accept any further tasks. Also this closes the processes if all tasks are done.
188 pool.close()189 pool.close()
189 # While the processes are still working, start to look at the results.190 # While the processes are still working, start to look at the results.
190 for song_tuple in result:191 for pos_tuple in result:
191 self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)192 self.duplicate_search_progress_bar.setValue(self.duplicate_search_progress_bar.value() + 1)
192 # The call to process_events() will keep the GUI responsive.193 # The call to process_events() will keep the GUI responsive.
193 self.application.process_events()194 self.application.process_events()
194 if self.break_search:195 if self.break_search:
195 pool.terminate()196 pool.terminate()
196 return197 return
197 if song_tuple is None:198 if pos_tuple is None:
198 continue199 continue
199 song1, song2 = song_tuple200 song1 = songs[pos_tuple[0]]
201 song2 = songs[pos_tuple[1]]
200 duplicate_added = self.add_duplicates_to_song_list(song1, song2)202 duplicate_added = self.add_duplicates_to_song_list(song1, song2)
201 if duplicate_added:203 if duplicate_added:
202 self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title)204 self.found_duplicates_edit.appendPlainText(song1.title + " = " + song2.title)
203205
=== modified file 'openlp/plugins/songs/lib/importers/wordsofworship.py'
--- openlp/plugins/songs/lib/importers/wordsofworship.py 2014-07-04 09:35:10 +0000
+++ openlp/plugins/songs/lib/importers/wordsofworship.py 2014-11-06 09:51:19 +0000
@@ -99,7 +99,7 @@
99 """99 """
100 Initialise the Words of Worship importer.100 Initialise the Words of Worship importer.
101 """101 """
102 SongImport.__init__(self, manager, **kwargs)102 super(WordsOfWorshipImport, self).__init__(manager, **kwargs)
103103
104 def do_import(self):104 def do_import(self):
105 """105 """
@@ -112,17 +112,17 @@
112 return112 return
113 self.set_defaults()113 self.set_defaults()
114 song_data = open(source, 'rb')114 song_data = open(source, 'rb')
115 if song_data.read(19) != 'WoW File\nSong Words':115 if song_data.read(19).decode() != 'WoW File\nSong Words':
116 self.log_error(source,116 self.log_error(source,
117 str(translate('SongsPlugin.WordsofWorshipSongImport',117 str(translate('SongsPlugin.WordsofWorshipSongImport',
118 'Invalid Words of Worship song file. Missing "Wow File\\nSong '118 'Invalid Words of Worship song file. Missing "WoW File\\nSong '
119 'Words" header.')))119 'Words" header.')))
120 continue120 continue
121 # Seek to byte which stores number of blocks in the song121 # Seek to byte which stores number of blocks in the song
122 song_data.seek(56)122 song_data.seek(56)
123 no_of_blocks = ord(song_data.read(1))123 no_of_blocks = ord(song_data.read(1))
124 song_data.seek(66)124 song_data.seek(66)
125 if song_data.read(16) != 'CSongDoc::CBlock':125 if song_data.read(16).decode() != 'CSongDoc::CBlock':
126 self.log_error(source,126 self.log_error(source,
127 str(translate('SongsPlugin.WordsofWorshipSongImport',127 str(translate('SongsPlugin.WordsofWorshipSongImport',
128 'Invalid Words of Worship song file. Missing "CSongDoc::CBlock" '128 'Invalid Words of Worship song file. Missing "CSongDoc::CBlock" '
@@ -131,11 +131,17 @@
131 # Seek to the beginning of the first block131 # Seek to the beginning of the first block
132 song_data.seek(82)132 song_data.seek(82)
133 for block in range(no_of_blocks):133 for block in range(no_of_blocks):
134 skip_char_at_end = True
134 self.lines_to_read = ord(song_data.read(4)[:1])135 self.lines_to_read = ord(song_data.read(4)[:1])
135 block_text = ''136 block_text = ''
136 while self.lines_to_read:137 while self.lines_to_read:
137 self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252')138 self.line_text = str(song_data.read(ord(song_data.read(1))), 'cp1252')
138 song_data.seek(1, os.SEEK_CUR)139 if skip_char_at_end:
140 skip_char = ord(song_data.read(1))
141 # Check if we really should skip a char. In some wsg files we shouldn't
142 if skip_char != 0:
143 song_data.seek(-1, os.SEEK_CUR)
144 skip_char_at_end = False
139 if block_text:145 if block_text:
140 block_text += '\n'146 block_text += '\n'
141 block_text += self.line_text147 block_text += self.line_text
142148
=== modified file 'openlp/plugins/songs/lib/songcompare.py'
--- openlp/plugins/songs/lib/songcompare.py 2014-04-12 16:05:27 +0000
+++ openlp/plugins/songs/lib/songcompare.py 2014-11-06 09:51:19 +0000
@@ -59,12 +59,14 @@
59 :param song_tupel: A tuple of two songs to compare.59 :param song_tupel: A tuple of two songs to compare.
60 """60 """
61 song1, song2 = song_tupel61 song1, song2 = song_tupel
62 if len(song1.search_lyrics) < len(song2.search_lyrics):62 pos1, lyrics1 = song1
63 small = song1.search_lyrics63 pos2, lyrics2 = song2
64 large = song2.search_lyrics64 if len(lyrics1) < len(lyrics2):
65 small = lyrics1
66 large = lyrics2
65 else:67 else:
66 small = song2.search_lyrics68 small = lyrics2
67 large = song1.search_lyrics69 large = lyrics1
68 differ = difflib.SequenceMatcher(a=large, b=small)70 differ = difflib.SequenceMatcher(a=large, b=small)
69 diff_tuples = differ.get_opcodes()71 diff_tuples = differ.get_opcodes()
70 diff_no_typos = _remove_typos(diff_tuples)72 diff_no_typos = _remove_typos(diff_tuples)
@@ -77,7 +79,7 @@
77 length_of_equal_blocks += _op_length(element)79 length_of_equal_blocks += _op_length(element)
7880
79 if length_of_equal_blocks >= MIN_BLOCK_SIZE:81 if length_of_equal_blocks >= MIN_BLOCK_SIZE:
80 return song1, song282 return pos1, pos2
81 # Check 2: Similarity based on the relative length of the longest equal block.83 # Check 2: Similarity based on the relative length of the longest equal block.
82 # Calculate the length of the largest equal block of the diff set.84 # Calculate the length of the largest equal block of the diff set.
83 length_of_longest_equal_block = 085 length_of_longest_equal_block = 0
@@ -85,7 +87,7 @@
85 if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:87 if element[0] == "equal" and _op_length(element) > length_of_longest_equal_block:
86 length_of_longest_equal_block = _op_length(element)88 length_of_longest_equal_block = _op_length(element)
87 if length_of_longest_equal_block > len(small) * 2 // 3:89 if length_of_longest_equal_block > len(small) * 2 // 3:
88 return song1, song290 return pos1, pos2
89 # Both checks failed. We assume the songs are not equal.91 # Both checks failed. We assume the songs are not equal.
90 return None92 return None
9193
9294
=== modified file 'tests/functional/openlp_core_ui/test_settingsform.py'
--- tests/functional/openlp_core_ui/test_settingsform.py 2014-10-30 22:53:06 +0000
+++ tests/functional/openlp_core_ui/test_settingsform.py 2014-11-06 09:51:19 +0000
@@ -157,4 +157,4 @@
157157
158 # THEN: The general tab's cancel() method should have been called, but not the themes tab158 # THEN: The general tab's cancel() method should have been called, but not the themes tab
159 mocked_general_cancel.assert_called_with()159 mocked_general_cancel.assert_called_with()
160 self.assertEqual(0, mocked_theme_cancel.call_count, 'The Themes tab\'s cancel() should not have been called')
161\ No newline at end of file160\ No newline at end of file
161 self.assertEqual(0, mocked_theme_cancel.call_count, 'The Themes tab\'s cancel() should not have been called')
162162
=== modified file 'tests/functional/openlp_plugins/images/test_imagetab.py'
--- tests/functional/openlp_plugins/images/test_imagetab.py 2014-11-01 11:06:17 +0000
+++ tests/functional/openlp_plugins/images/test_imagetab.py 2014-11-06 09:51:19 +0000
@@ -95,4 +95,4 @@
95 self.form.save()95 self.form.save()
96 # THEN: the post process should be requested96 # THEN: the post process should be requested
97 self.assertEqual(1, self.form.settings_form.register_post_process.call_count,97 self.assertEqual(1, self.form.settings_form.register_post_process.call_count,
98 'Image Post processing should have been requested')
99\ No newline at end of file98\ No newline at end of file
99 'Image Post processing should have been requested')
100100
=== modified file 'tests/functional/openlp_plugins/songs/test_lib.py'
--- tests/functional/openlp_plugins/songs/test_lib.py 2014-05-07 23:52:51 +0000
+++ tests/functional/openlp_plugins/songs/test_lib.py 2014-11-06 09:51:19 +0000
@@ -58,8 +58,6 @@
58 i love that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the58 i love that old cross where the dearest and best for a world of lost sinners was slain so ill cherish the
59 old rugged cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it59 old rugged cross till my trophies at last i lay down i will cling to the old rugged cross and exchange it
60 some day for a crown'''60 some day for a crown'''
61 self.song1 = MagicMock()
62 self.song2 = MagicMock()
6361
64 def clean_string_test(self):62 def clean_string_test(self):
65 """63 """
@@ -92,53 +90,53 @@
92 Test the songs_probably_equal function with twice the same song.90 Test the songs_probably_equal function with twice the same song.
93 """91 """
94 # GIVEN: Two equal songs.92 # GIVEN: Two equal songs.
95 self.song1.search_lyrics = self.full_lyrics93 song_tuple1 = (2, self.full_lyrics)
96 self.song2.search_lyrics = self.full_lyrics94 song_tuple2 = (4, self.full_lyrics)
9795
98 # WHEN: We compare those songs for equality.96 # WHEN: We compare those songs for equality.
99 result = songs_probably_equal((self.song1, self.song2))97 result = songs_probably_equal((song_tuple1, song_tuple2))
10098
101 # THEN: The result should be a tuple..99 # THEN: The result should be a tuple..
102 assert result == (self.song1, self.song2), 'The result should be the tuble of songs'100 assert result == (2, 4), 'The result should be the tuble of song positions'
103101
104 def songs_probably_equal_short_song_test(self):102 def songs_probably_equal_short_song_test(self):
105 """103 """
106 Test the songs_probably_equal function with a song and a shorter version of the same song.104 Test the songs_probably_equal function with a song and a shorter version of the same song.
107 """105 """
108 # GIVEN: A song and a short version of the same song.106 # GIVEN: A song and a short version of the same song.
109 self.song1.search_lyrics = self.full_lyrics107 song_tuple1 = (1, self.full_lyrics)
110 self.song2.search_lyrics = self.short_lyrics108 song_tuple2 = (3, self.short_lyrics)
111109
112 # WHEN: We compare those songs for equality.110 # WHEN: We compare those songs for equality.
113 result = songs_probably_equal((self.song1, self.song2))111 result = songs_probably_equal((song_tuple1, song_tuple2))
114112
115 # THEN: The result should be a tuple..113 # THEN: The result should be a tuple..
116 assert result == (self.song1, self.song2), 'The result should be the tuble of songs'114 assert result == (1, 3), 'The result should be the tuble of song positions'
117115
118 def songs_probably_equal_error_song_test(self):116 def songs_probably_equal_error_song_test(self):
119 """117 """
120 Test the songs_probably_equal function with a song and a very erroneous version of the same song.118 Test the songs_probably_equal function with a song and a very erroneous version of the same song.
121 """119 """
122 # GIVEN: A song and the same song with lots of errors.120 # GIVEN: A song and the same song with lots of errors.
123 self.song1.search_lyrics = self.full_lyrics121 song_tuple1 = (4, self.full_lyrics)
124 self.song2.search_lyrics = self.error_lyrics122 song_tuple2 = (7, self.error_lyrics)
125123
126 # WHEN: We compare those songs for equality.124 # WHEN: We compare those songs for equality.
127 result = songs_probably_equal((self.song1, self.song2))125 result = songs_probably_equal((song_tuple1, song_tuple2))
128126
129 # THEN: The result should be a tuple of songs..127 # THEN: The result should be a tuple of song positions.
130 assert result == (self.song1, self.song2), 'The result should be the tuble of songs'128 assert result == (4, 7), 'The result should be the tuble of song positions'
131129
132 def songs_probably_equal_different_song_test(self):130 def songs_probably_equal_different_song_test(self):
133 """131 """
134 Test the songs_probably_equal function with two different songs.132 Test the songs_probably_equal function with two different songs.
135 """133 """
136 # GIVEN: Two different songs.134 # GIVEN: Two different songs.
137 self.song1.search_lyrics = self.full_lyrics135 song_tuple1 = (5, self.full_lyrics)
138 self.song2.search_lyrics = self.different_lyrics136 song_tuple2 = (8, self.different_lyrics)
139137
140 # WHEN: We compare those songs for equality.138 # WHEN: We compare those songs for equality.
141 result = songs_probably_equal((self.song1, self.song2))139 result = songs_probably_equal((song_tuple1, song_tuple2))
142140
143 # THEN: The result should be None.141 # THEN: The result should be None.
144 assert result is None, 'The result should be None'142 assert result is None, 'The result should be None'
145143
=== added file 'tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py'
--- tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py 1970-01-01 00:00:00 +0000
+++ tests/functional/openlp_plugins/songs/test_wordsofworshipimport.py 2014-11-06 09:51:19 +0000
@@ -0,0 +1,56 @@
1# -*- coding: utf-8 -*-
2# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
3
4###############################################################################
5# OpenLP - Open Source Lyrics Projection #
6# --------------------------------------------------------------------------- #
7# Copyright (c) 2008-2014 Raoul Snyman #
8# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
9# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
10# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
11# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
12# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
13# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
14# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
15# --------------------------------------------------------------------------- #
16# This program is free software; you can redistribute it and/or modify it #
17# under the terms of the GNU General Public License as published by the Free #
18# Software Foundation; version 2 of the License. #
19# #
20# This program is distributed in the hope that it will be useful, but WITHOUT #
21# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
22# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
23# more details. #
24# #
25# You should have received a copy of the GNU General Public License along #
26# with this program; if not, write to the Free Software Foundation, Inc., 59 #
27# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
28###############################################################################
29"""
30This module contains tests for the Words of Worship song importer.
31"""
32
33import os
34
35from tests.helpers.songfileimport import SongImportTestHelper
36from openlp.plugins.songs.lib.importers.wordsofworship import WordsOfWorshipImport
37
38TEST_PATH = os.path.abspath(
39 os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'wordsofworshipsongs'))
40
41
42class TestWordsOfWorshipFileImport(SongImportTestHelper):
43
44 def __init__(self, *args, **kwargs):
45 self.importer_class_name = 'WordsOfWorshipImport'
46 self.importer_module_name = 'wordsofworship'
47 super(TestWordsOfWorshipFileImport, self).__init__(*args, **kwargs)
48
49 def test_song_import(self):
50 """
51 Test that loading a Words of Worship file works correctly
52 """
53 self.file_import([os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).wow-song')],
54 self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace (6 Verses).json')))
55 self.file_import([os.path.join(TEST_PATH, 'When morning gilds the skies.wsg')],
56 self.load_external_result_data(os.path.join(TEST_PATH, 'When morning gilds the skies.json')))
057
=== added directory 'tests/resources/wordsofworshipsongs'
=== added file 'tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json'
--- tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json 1970-01-01 00:00:00 +0000
+++ tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).json 2014-11-06 09:51:19 +0000
@@ -0,0 +1,33 @@
1{
2 "authors": [
3 "John Newton (1725-1807)"
4 ],
5 "title": "Amazing Grace (6 Verses)",
6 "verse_order_list": [],
7 "verses": [
8 [
9 "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.",
10 "V"
11 ],
12 [
13 "'Twas grace that taught my heart to fear,\nAnd grace my fears relieved;\nHow precious did that grace appear,\nThe hour I first believed!",
14 "V"
15 ],
16 [
17 "Through many dangers, toils and snares\nI have already come;\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
18 "V"
19 ],
20 [
21 "The Lord has promised good to me,\nHis word my hope secures;\nHe will my shield and portion be\nAs long as life endures.",
22 "V"
23 ],
24 [
25 "Yes, when this heart and flesh shall fail,\nAnd mortal life shall cease,\nI shall possess within the veil\nA life of joy and peace.",
26 "V"
27 ],
28 [
29 "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.",
30 "V"
31 ]
32 ]
33}
034
=== added file 'tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song'
1Binary files tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song 1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song 2014-11-06 09:51:19 +0000 differ35Binary files tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song 1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/Amazing Grace (6 Verses).wow-song 2014-11-06 09:51:19 +0000 differ
=== added file 'tests/resources/wordsofworshipsongs/When morning gilds the skies.json'
--- tests/resources/wordsofworshipsongs/When morning gilds the skies.json 1970-01-01 00:00:00 +0000
+++ tests/resources/wordsofworshipsongs/When morning gilds the skies.json 2014-11-06 09:51:19 +0000
@@ -0,0 +1,29 @@
1{
2 "authors": [
3 "Author Unknown. Tr. Edward Caswall"
4 ],
5 "title": "When morning gilds the skies",
6 "verse_order_list": [],
7 "verses": [
8 [
9 "When morning gilds the skies\nMy heart awaking cries:\n'May Jesus Christ be prais'd!'\nAlike at work and prayer to Jesus I repair:\n'May Jesus Christ be prais'd!'",
10 "V"
11 ],
12 [
13 "Does sadness fill my mind?\nA solace here I find:\n'May Jesus Christ be praised!'\nWhen evil thoughts molest,\nWith this I shield my breast:\n'May Jesus Christ be prais'd!'",
14 "V"
15 ],
16 [
17 "To God, the Word, on high\nThe hosts of angels cry:\n'May Jesus Christ be prais'd!'\nLet mortals, too, upraise\nTheir voice in hymns of praise:\n'May Jesus Christ be prais'd!'",
18 "V"
19 ],
20 [
21 "Let earth's wide circle round\nIn joyful notes resound:\n'May Jesus Christ be prais'd!'\nLet air, and sea, and sky,\nFrom depth to height, reply:\n'May Jesus Christ be prais'd!'",
22 "V"
23 ],
24 [
25 "Be this while life is mine\nMy canticle divine\n'May Jesus Christ be prais'd!'\nBe this the eternal song,\nThrough all the ages long:\n'May Jesus Christ be prais'd!'",
26 "V"
27 ]
28 ]
29}
030
=== added file 'tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg'
1Binary files tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg 1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg 2014-11-06 09:51:19 +0000 differ31Binary files tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg 1970-01-01 00:00:00 +0000 and tests/resources/wordsofworshipsongs/When morning gilds the skies.wsg 2014-11-06 09:51:19 +0000 differ