This commit is contained in:
Tim Bentley 2016-08-14 10:23:12 +01:00
commit c6c7931323
7 changed files with 668 additions and 87 deletions

View File

@ -46,3 +46,4 @@ cover
coverage coverage
tags tags
output output
htmlcov

View File

@ -83,17 +83,17 @@ class SystemPlayer(MediaPlayer):
elif mime_type.startswith('video/'): elif mime_type.startswith('video/'):
self._add_to_list(self.video_extensions_list, mime_type) self._add_to_list(self.video_extensions_list, mime_type)
def _add_to_list(self, mime_type_list, mimetype): def _add_to_list(self, mime_type_list, mime_type):
""" """
Add mimetypes to the provided list Add mimetypes to the provided list
""" """
# Add all extensions which mimetypes provides us for supported types. # Add all extensions which mimetypes provides us for supported types.
extensions = mimetypes.guess_all_extensions(str(mimetype)) extensions = mimetypes.guess_all_extensions(mime_type)
for extension in extensions: for extension in extensions:
ext = '*%s' % extension ext = '*%s' % extension
if ext not in mime_type_list: if ext not in mime_type_list:
mime_type_list.append(ext) mime_type_list.append(ext)
log.info('MediaPlugin: %s extensions: %s' % (mimetype, ' '.join(extensions))) log.info('MediaPlugin: %s extensions: %s', mime_type, ' '.join(extensions))
def setup(self, display): def setup(self, display):
""" """
@ -284,25 +284,25 @@ class SystemPlayer(MediaPlayer):
:return: True if file can be played otherwise False :return: True if file can be played otherwise False
""" """
thread = QtCore.QThread() thread = QtCore.QThread()
check_media_player = CheckMedia(path) check_media_worker = CheckMediaWorker(path)
check_media_player.setVolume(0) check_media_worker.setVolume(0)
check_media_player.moveToThread(thread) check_media_worker.moveToThread(thread)
check_media_player.finished.connect(thread.quit) check_media_worker.finished.connect(thread.quit)
thread.started.connect(check_media_player.play) thread.started.connect(check_media_worker.play)
thread.start() thread.start()
while thread.isRunning(): while thread.isRunning():
self.application.processEvents() self.application.processEvents()
return check_media_player.result return check_media_worker.result
class CheckMedia(QtMultimedia.QMediaPlayer): class CheckMediaWorker(QtMultimedia.QMediaPlayer):
""" """
Class used to check if a media file is playable Class used to check if a media file is playable
""" """
finished = QtCore.pyqtSignal() finished = QtCore.pyqtSignal()
def __init__(self, path): def __init__(self, path):
super(CheckMedia, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface) super(CheckMediaWorker, self).__init__(None, QtMultimedia.QMediaPlayer.VideoSurface)
self.result = None self.result = None
self.error.connect(functools.partial(self.signals, 'error')) self.error.connect(functools.partial(self.signals, 'error'))

View File

@ -31,9 +31,8 @@ from PyQt5 import QtWidgets
from openlp.core.common import AppLocation, CONTROL_CHARS from openlp.core.common import AppLocation, CONTROL_CHARS
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.plugins.songs.lib.db import MediaFile, Song from openlp.plugins.songs.lib.db import Author, MediaFile, Song, Topic
from .db import Author from openlp.plugins.songs.lib.ui import SongStrings
from .ui import SongStrings
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -315,8 +314,8 @@ def retrieve_windows_encoding(recommendation=None):
] ]
recommended_index = -1 recommended_index = -1
if recommendation: if recommendation:
for index in range(len(encodings)): for index, encoding in enumerate(encodings):
if recommendation == encodings[index][0]: if recommendation == encoding[0]:
recommended_index = index recommended_index = index
break break
if recommended_index > -1: if recommended_index > -1:
@ -442,7 +441,7 @@ def strip_rtf(text, default_encoding=None):
# Encoded buffer. # Encoded buffer.
ebytes = bytearray() ebytes = bytearray()
for match in PATTERN.finditer(text): for match in PATTERN.finditer(text):
iinu, word, arg, hex, char, brace, tchar = match.groups() iinu, word, arg, hex_, char, brace, tchar = match.groups()
# \x (non-alpha character) # \x (non-alpha character)
if char: if char:
if char in '\\{}': if char in '\\{}':
@ -450,7 +449,7 @@ def strip_rtf(text, default_encoding=None):
else: else:
word = char word = char
# Flush encoded buffer to output buffer # Flush encoded buffer to output buffer
if ebytes and not hex and not tchar: if ebytes and not hex_ and not tchar:
failed = False failed = False
while True: while True:
try: try:
@ -507,11 +506,11 @@ def strip_rtf(text, default_encoding=None):
elif iinu: elif iinu:
ignorable = True ignorable = True
# \'xx # \'xx
elif hex: elif hex_:
if curskip > 0: if curskip > 0:
curskip -= 1 curskip -= 1
elif not ignorable: elif not ignorable:
ebytes.append(int(hex, 16)) ebytes.append(int(hex_, 16))
elif tchar: elif tchar:
if curskip > 0: if curskip > 0:
curskip -= 1 curskip -= 1

View File

@ -23,7 +23,8 @@
The :mod:`~openlp.plugins.songs.lib.songselect` module contains the SongSelect importer itself. The :mod:`~openlp.plugins.songs.lib.songselect` module contains the SongSelect importer itself.
""" """
import logging import logging
import sys import random
import re
from http.cookiejar import CookieJar from http.cookiejar import CookieJar
from urllib.parse import urlencode from urllib.parse import urlencode
from urllib.request import HTTPCookieProcessor, URLError, build_opener from urllib.request import HTTPCookieProcessor, URLError, build_opener
@ -32,14 +33,21 @@ from html import unescape
from bs4 import BeautifulSoup, NavigableString from bs4 import BeautifulSoup, NavigableString
from openlp.plugins.songs.lib import Song, VerseType, clean_song, Author from openlp.plugins.songs.lib import Song, Author, Topic, VerseType, clean_song
from openlp.plugins.songs.lib.openlyricsxml import SongXML from openlp.plugins.songs.lib.openlyricsxml import SongXML
USER_AGENT = 'Mozilla/5.0 (Linux; U; Android 4.0.3; en-us; GT-I9000 ' \ USER_AGENTS = [
'Build/IML74K) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 ' \ 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) '
'Mobile Safari/534.30' 'Chrome/52.0.2743.116 Safari/537.36',
BASE_URL = 'https://mobile.songselect.com' 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36',
LOGIN_URL = BASE_URL + '/account/login' 'Mozilla/5.0 (X11; Linux x86_64; rv:47.0) Gecko/20100101 Firefox/47.0',
'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:46.0) Gecko/20100101 Firefox/46.0',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:47.0) Gecko/20100101 Firefox/47.0'
]
BASE_URL = 'https://songselect.ccli.com'
LOGIN_PAGE = 'https://profile.ccli.com/account/signin?appContext=SongSelect&returnUrl='\
'https%3a%2f%2fsongselect.ccli.com%2f'
LOGIN_URL = 'https://profile.ccli.com/'
LOGOUT_URL = BASE_URL + '/account/logout' LOGOUT_URL = BASE_URL + '/account/logout'
SEARCH_URL = BASE_URL + '/search/results' SEARCH_URL = BASE_URL + '/search/results'
@ -60,7 +68,7 @@ class SongSelectImport(object):
self.db_manager = db_manager self.db_manager = db_manager
self.html_parser = HTMLParser() self.html_parser = HTMLParser()
self.opener = build_opener(HTTPCookieProcessor(CookieJar())) self.opener = build_opener(HTTPCookieProcessor(CookieJar()))
self.opener.addheaders = [('User-Agent', USER_AGENT)] self.opener.addheaders = [('User-Agent', random.choice(USER_AGENTS))]
self.run_search = True self.run_search = True
def login(self, username, password, callback=None): def login(self, username, password, callback=None):
@ -76,27 +84,27 @@ class SongSelectImport(object):
if callback: if callback:
callback() callback()
try: try:
login_page = BeautifulSoup(self.opener.open(LOGIN_URL).read(), 'lxml') login_page = BeautifulSoup(self.opener.open(LOGIN_PAGE).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as error:
log.exception('Could not login to SongSelect, {error}'.format(error=e)) log.exception('Could not login to SongSelect, {error}'.format(error=error))
return False return False
if callback: if callback:
callback() callback()
token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'}) token_input = login_page.find('input', attrs={'name': '__RequestVerificationToken'})
data = urlencode({ data = urlencode({
'__RequestVerificationToken': token_input['value'], '__RequestVerificationToken': token_input['value'],
'UserName': username, 'emailAddress': username,
'Password': password, 'password': password,
'RememberMe': 'false' 'RememberMe': 'false'
}) })
try: try:
posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml') posted_page = BeautifulSoup(self.opener.open(LOGIN_URL, data.encode('utf-8')).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as error:
log.exception('Could not login to SongSelect, {error}'.format(error=e)) log.exception('Could not login to SongSelect, {error}'.format(error=error))
return False return False
if callback: if callback:
callback() callback()
return not posted_page.find('input', attrs={'name': '__RequestVerificationToken'}) return posted_page.find('input', id='SearchText') is not None
def logout(self): def logout(self):
""" """
@ -104,8 +112,8 @@ class SongSelectImport(object):
""" """
try: try:
self.opener.open(LOGOUT_URL) self.opener.open(LOGOUT_URL)
except (TypeError, URLError) as e: except (TypeError, URLError) as error:
log.exception('Could not log of SongSelect, {error}'.format(error=e)) log.exception('Could not log of SongSelect, {error}'.format(error=error))
def search(self, search_text, max_results, callback=None): def search(self, search_text, max_results, callback=None):
""" """
@ -117,7 +125,15 @@ class SongSelectImport(object):
:return: List of songs :return: List of songs
""" """
self.run_search = True self.run_search = True
params = {'allowredirect': 'false', 'SearchTerm': search_text} params = {
'SongContent': '',
'PrimaryLanguage': '',
'Keys': '',
'Themes': '',
'List': '',
'Sort': '',
'SearchText': search_text
}
current_page = 1 current_page = 1
songs = [] songs = []
while self.run_search: while self.run_search:
@ -125,17 +141,17 @@ class SongSelectImport(object):
params['page'] = current_page params['page'] = current_page
try: try:
results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml') results_page = BeautifulSoup(self.opener.open(SEARCH_URL + '?' + urlencode(params)).read(), 'lxml')
search_results = results_page.find_all('li', 'result pane') search_results = results_page.find_all('div', 'song-result')
except (TypeError, URLError) as e: except (TypeError, URLError) as error:
log.exception('Could not search SongSelect, {error}'.format(error=e)) log.exception('Could not search SongSelect, {error}'.format(error=error))
search_results = None search_results = None
if not search_results: if not search_results:
break break
for result in search_results: for result in search_results:
song = { song = {
'title': unescape(result.find('h3').string), 'title': unescape(result.find('p', 'song-result-title').find('a').string).strip(),
'authors': [unescape(author.string) for author in result.find_all('li')], 'authors': unescape(result.find('p', 'song-result-subtitle').string).strip().split(', '),
'link': BASE_URL + result.find('a')['href'] 'link': BASE_URL + result.find('p', 'song-result-title').find('a')['href']
} }
if callback: if callback:
callback(song) callback(song)
@ -157,33 +173,43 @@ class SongSelectImport(object):
callback() callback()
try: try:
song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml') song_page = BeautifulSoup(self.opener.open(song['link']).read(), 'lxml')
except (TypeError, URLError) as e: except (TypeError, URLError) as error:
log.exception('Could not get song from SongSelect, {error}'.format(error=e)) log.exception('Could not get song from SongSelect, {error}'.format(error=error))
return None return None
if callback: if callback:
callback() callback()
try: try:
lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/lyrics').read(), 'lxml') lyrics_page = BeautifulSoup(self.opener.open(song['link'] + '/viewlyrics').read(), 'lxml')
except (TypeError, URLError): except (TypeError, URLError):
log.exception('Could not get lyrics from SongSelect') log.exception('Could not get lyrics from SongSelect')
return None return None
if callback: if callback:
callback() callback()
song['copyright'] = '/'.join([li.string for li in song_page.find('ul', 'copyright').find_all('li')]) copyright_elements = []
song['copyright'] = unescape(song['copyright']) theme_elements = []
song['ccli_number'] = song_page.find('ul', 'info').find('li').string.split(':')[1].strip() copyrights_regex = re.compile(r'\bCopyrights\b')
themes_regex = re.compile(r'\bThemes\b')
for ul in song_page.find_all('ul', 'song-meta-list'):
if ul.find('li', string=copyrights_regex):
copyright_elements.extend(ul.find_all('li')[1:])
if ul.find('li', string=themes_regex):
theme_elements.extend(ul.find_all('li')[1:])
song['copyright'] = '/'.join([unescape(li.string).strip() for li in copyright_elements])
song['topics'] = [unescape(li.string).strip() for li in theme_elements]
song['ccli_number'] = song_page.find('div', 'song-content-data').find('ul').find('li')\
.find('strong').string.strip()
song['verses'] = [] song['verses'] = []
verses = lyrics_page.find('section', 'lyrics').find_all('p') verses = lyrics_page.find('div', 'song-viewer lyrics').find_all('p')
verse_labels = lyrics_page.find('section', 'lyrics').find_all('h3') verse_labels = lyrics_page.find('div', 'song-viewer lyrics').find_all('h3')
for counter in range(len(verses)): for verse, label in zip(verses, verse_labels):
verse = {'label': verse_labels[counter].string, 'lyrics': ''} song_verse = {'label': unescape(label.string).strip(), 'lyrics': ''}
for v in verses[counter].contents: for v in verse.contents:
if isinstance(v, NavigableString): if isinstance(v, NavigableString):
verse['lyrics'] = verse['lyrics'] + v.string song_verse['lyrics'] += unescape(v.string).strip()
else: else:
verse['lyrics'] += '\n' song_verse['lyrics'] += '\n'
verse['lyrics'] = verse['lyrics'].strip(' \n\r\t') song_verse['lyrics'] = song_verse['lyrics'].strip(' \n\r\t')
song['verses'].append(unescape(verse)) song['verses'].append(song_verse)
for counter, author in enumerate(song['authors']): for counter, author in enumerate(song['authors']):
song['authors'][counter] = unescape(author) song['authors'][counter] = unescape(author)
return song return song
@ -199,7 +225,11 @@ class SongSelectImport(object):
song_xml = SongXML() song_xml = SongXML()
verse_order = [] verse_order = []
for verse in song['verses']: for verse in song['verses']:
verse_type, verse_number = verse['label'].split(' ')[:2] if ' ' in verse['label']:
verse_type, verse_number = verse['label'].split(' ', 1)
else:
verse_type = verse['label']
verse_number = 1
verse_type = VerseType.from_loose_input(verse_type) verse_type = VerseType.from_loose_input(verse_type)
verse_number = int(verse_number) verse_number = int(verse_number)
song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics']) song_xml.add_verse_to_lyrics(VerseType.tags[verse_type], verse_number, verse['lyrics'])
@ -220,6 +250,11 @@ class SongSelectImport(object):
last_name = name_parts[1] last_name = name_parts[1]
author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name) author = Author.populate(first_name=first_name, last_name=last_name, display_name=author_name)
db_song.add_author(author) db_song.add_author(author)
for topic_name in song.get('topics', []):
topic = self.db_manager.get_object_filtered(Topic, Topic.name == topic_name)
if not topic:
topic = Topic.populate(name=topic_name)
db_song.topics.append(topic)
self.db_manager.save_object(db_song) self.db_manager.save_object(db_song)
return db_song return db_song

View File

@ -243,7 +243,7 @@ class TestSlideController(TestCase):
mocked_service_item = MagicMock() mocked_service_item = MagicMock()
mocked_service_item.from_service = False mocked_service_item.from_service = False
mocked_preview_widget.current_slide_number.return_value = 1 mocked_preview_widget.current_slide_number.return_value = 1
mocked_preview_widget.slide_count.return_value = 2 mocked_preview_widget.slide_count = MagicMock(return_value=2)
mocked_live_controller.preview_widget = MagicMock() mocked_live_controller.preview_widget = MagicMock()
Registry.create() Registry.create()
Registry().register('live_controller', mocked_live_controller) Registry().register('live_controller', mocked_live_controller)
@ -273,7 +273,7 @@ class TestSlideController(TestCase):
mocked_service_item.from_service = True mocked_service_item.from_service = True
mocked_service_item.unique_identifier = 42 mocked_service_item.unique_identifier = 42
mocked_preview_widget.current_slide_number.return_value = 1 mocked_preview_widget.current_slide_number.return_value = 1
mocked_preview_widget.slide_count.return_value = 2 mocked_preview_widget.slide_count = MagicMock(return_value=2)
mocked_live_controller.preview_widget = MagicMock() mocked_live_controller.preview_widget = MagicMock()
Registry.create() Registry.create()
Registry().register('live_controller', mocked_live_controller) Registry().register('live_controller', mocked_live_controller)

View File

@ -0,0 +1,529 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2016 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.ui.media.systemplayer package.
"""
from unittest import TestCase
from PyQt5 import QtCore, QtMultimedia
from openlp.core.common import Registry
from openlp.core.ui.media import MediaState
from openlp.core.ui.media.systemplayer import SystemPlayer, CheckMediaWorker, ADDITIONAL_EXT
from tests.functional import MagicMock, call, patch
class TestSystemPlayer(TestCase):
"""
Test the system media player
"""
@patch('openlp.core.ui.media.systemplayer.mimetypes')
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
def test_constructor(self, MockQMediaPlayer, mocked_mimetypes):
"""
Test the SystemPlayer constructor
"""
# GIVEN: The SystemPlayer class and a mockedQMediaPlayer
mocked_media_player = MagicMock()
mocked_media_player.supportedMimeTypes.return_value = [
'application/postscript',
'audio/aiff',
'audio/x-aiff',
'text/html',
'video/animaflex',
'video/x-ms-asf'
]
mocked_mimetypes.guess_all_extensions.side_effect = [
['.aiff'],
['.aiff'],
['.afl'],
['.asf']
]
MockQMediaPlayer.return_value = mocked_media_player
# WHEN: An object is created from it
player = SystemPlayer(self)
# THEN: The correct initial values should be set up
self.assertEqual('system', player.name)
self.assertEqual('System', player.original_name)
self.assertEqual('&System', player.display_name)
self.assertEqual(self, player.parent)
self.assertEqual(ADDITIONAL_EXT, player.additional_extensions)
MockQMediaPlayer.assert_called_once_with(None, QtMultimedia.QMediaPlayer.VideoSurface)
mocked_mimetypes.init.assert_called_once_with()
mocked_media_player.service.assert_called_once_with()
mocked_media_player.supportedMimeTypes.assert_called_once_with()
self.assertEqual(['*.aiff'], player.audio_extensions_list)
self.assertEqual(['*.afl', '*.asf'], player.video_extensions_list)
@patch('openlp.core.ui.media.systemplayer.QtMultimediaWidgets.QVideoWidget')
@patch('openlp.core.ui.media.systemplayer.QtMultimedia.QMediaPlayer')
def test_setup(self, MockQMediaPlayer, MockQVideoWidget):
"""
Test the setup() method of SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mock display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.size.return_value = [1, 2, 3, 4]
mocked_video_widget = MagicMock()
mocked_media_player = MagicMock()
MockQVideoWidget.return_value = mocked_video_widget
MockQMediaPlayer.return_value = mocked_media_player
# WHEN: setup() is run
player.setup(mocked_display)
# THEN: The player should have a display widget
MockQVideoWidget.assert_called_once_with(mocked_display)
self.assertEqual(mocked_video_widget, mocked_display.video_widget)
mocked_display.size.assert_called_once_with()
mocked_video_widget.resize.assert_called_once_with([1, 2, 3, 4])
MockQMediaPlayer.assert_called_with(mocked_display)
self.assertEqual(mocked_media_player, mocked_display.media_player)
mocked_media_player.setVideoOutput.assert_called_once_with(mocked_video_widget)
mocked_video_widget.raise_.assert_called_once_with()
mocked_video_widget.hide.assert_called_once_with()
self.assertTrue(player.has_own_widget)
def test_check_available(self):
"""
Test the check_available() method on SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
# WHEN: check_available is run
result = player.check_available()
# THEN: it should be available
self.assertTrue(result)
def test_load_valid_media(self):
"""
Test the load() method of SystemPlayer with a valid media file
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.media_info.volume = 1
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
# WHEN: The load() method is run
with patch.object(player, 'check_media') as mocked_check_media, \
patch.object(player, 'volume') as mocked_volume:
mocked_check_media.return_value = True
result = player.load(mocked_display)
# THEN: the file is sent to the video widget
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
mocked_check_media.assert_called_once_with('/path/to/file')
mocked_display.media_player.setMedia.assert_called_once_with(
QtMultimedia.QMediaContent(QtCore.QUrl.fromLocalFile('/path/to/file')))
mocked_volume.assert_called_once_with(mocked_display, 1)
self.assertTrue(result)
def test_load_invalid_media(self):
"""
Test the load() method of SystemPlayer with an invalid media file
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.media_info.volume = 1
mocked_display.controller.media_info.file_info.absoluteFilePath.return_value = '/path/to/file'
# WHEN: The load() method is run
with patch.object(player, 'check_media') as mocked_check_media, \
patch.object(player, 'volume') as mocked_volume:
mocked_check_media.return_value = False
result = player.load(mocked_display)
# THEN: stuff
mocked_display.controller.media_info.file_info.absoluteFilePath.assert_called_once_with()
mocked_check_media.assert_called_once_with('/path/to/file')
self.assertFalse(result)
def test_resize(self):
"""
Test the resize() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.size.return_value = [1, 2, 3, 4]
# WHEN: The resize() method is called
player.resize(mocked_display)
# THEN: The player is resized
mocked_display.size.assert_called_once_with()
mocked_display.video_widget.resize.assert_called_once_with([1, 2, 3, 4])
@patch('openlp.core.ui.media.systemplayer.functools')
def test_play_is_live(self, mocked_functools):
"""
Test the play() method of the SystemPlayer on the live display
"""
# GIVEN: A SystemPlayer instance and a mocked display
mocked_functools.partial.return_value = 'function'
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = True
mocked_display.controller.media_info.start_time = 1
mocked_display.controller.media_info.volume = 1
# WHEN: play() is called
with patch.object(player, 'get_live_state') as mocked_get_live_state, \
patch.object(player, 'seek') as mocked_seek, \
patch.object(player, 'volume') as mocked_volume, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
result = player.play(mocked_display)
# THEN: the media file is played
mocked_get_live_state.assert_called_once_with()
mocked_display.media_player.play.assert_called_once_with()
mocked_seek.assert_called_once_with(mocked_display, 1000)
mocked_volume.assert_called_once_with(mocked_display, 1)
mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
mocked_display.video_widget.raise_.assert_called_once_with()
self.assertTrue(result)
@patch('openlp.core.ui.media.systemplayer.functools')
def test_play_is_preview(self, mocked_functools):
"""
Test the play() method of the SystemPlayer on the preview display
"""
# GIVEN: A SystemPlayer instance and a mocked display
mocked_functools.partial.return_value = 'function'
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = False
mocked_display.controller.media_info.start_time = 1
mocked_display.controller.media_info.volume = 1
# WHEN: play() is called
with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
patch.object(player, 'seek') as mocked_seek, \
patch.object(player, 'volume') as mocked_volume, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PlayingState
result = player.play(mocked_display)
# THEN: the media file is played
mocked_get_preview_state.assert_called_once_with()
mocked_display.media_player.play.assert_called_once_with()
mocked_seek.assert_called_once_with(mocked_display, 1000)
mocked_volume.assert_called_once_with(mocked_display, 1)
mocked_display.media_player.durationChanged.connect.assert_called_once_with('function')
mocked_set_state.assert_called_once_with(MediaState.Playing, mocked_display)
mocked_display.video_widget.raise_.assert_called_once_with()
self.assertTrue(result)
def test_pause_is_live(self):
"""
Test the pause() method of the SystemPlayer on the live display
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = True
# WHEN: The pause method is called
with patch.object(player, 'get_live_state') as mocked_get_live_state, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_live_state.return_value = QtMultimedia.QMediaPlayer.PausedState
player.pause(mocked_display)
# THEN: The video is paused
mocked_display.media_player.pause.assert_called_once_with()
mocked_get_live_state.assert_called_once_with()
mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
def test_pause_is_preview(self):
"""
Test the pause() method of the SystemPlayer on the preview display
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.controller.is_live = False
# WHEN: The pause method is called
with patch.object(player, 'get_preview_state') as mocked_get_preview_state, \
patch.object(player, 'set_state') as mocked_set_state:
mocked_get_preview_state.return_value = QtMultimedia.QMediaPlayer.PausedState
player.pause(mocked_display)
# THEN: The video is paused
mocked_display.media_player.pause.assert_called_once_with()
mocked_get_preview_state.assert_called_once_with()
mocked_set_state.assert_called_once_with(MediaState.Paused, mocked_display)
def test_stop(self):
"""
Test the stop() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: The stop method is called
with patch.object(player, 'set_visible') as mocked_set_visible, \
patch.object(player, 'set_state') as mocked_set_state:
player.stop(mocked_display)
# THEN: The video is stopped
mocked_display.media_player.stop.assert_called_once_with()
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_set_state.assert_called_once_with(MediaState.Stopped, mocked_display)
def test_volume(self):
"""
Test the volume() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
mocked_display.has_audio = True
# WHEN: The stop method is called
player.volume(mocked_display, 2)
# THEN: The video is stopped
mocked_display.media_player.setVolume.assert_called_once_with(2)
def test_seek(self):
"""
Test the seek() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: The stop method is called
player.seek(mocked_display, 2)
# THEN: The video is stopped
mocked_display.media_player.setPosition.assert_called_once_with(2)
def test_reset(self):
"""
Test the reset() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
mocked_display = MagicMock()
# WHEN: reset() is called
with patch.object(player, 'set_state') as mocked_set_state, \
patch.object(player, 'set_visible') as mocked_set_visible:
player.reset(mocked_display)
# THEN: The media player is reset
mocked_display.media_player.stop()
mocked_display.media_player.setMedia.assert_called_once_with(QtMultimedia.QMediaContent())
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_display.video_widget.setVisible.assert_called_once_with(False)
mocked_set_state.assert_called_once_with(MediaState.Off, mocked_display)
def test_set_visible(self):
"""
Test the set_visible() method on the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked display
player = SystemPlayer(self)
player.has_own_widget = True
mocked_display = MagicMock()
# WHEN: set_visible() is called
player.set_visible(mocked_display, True)
# THEN: The widget should be visible
mocked_display.video_widget.setVisible.assert_called_once_with(True)
def test_set_duration(self):
"""
Test the set_duration() method of the SystemPlayer
"""
# GIVEN: a mocked controller
mocked_controller = MagicMock()
mocked_controller.media_info.length = 5
# WHEN: The set_duration() is called. NB: the 10 here is ignored by the code
SystemPlayer.set_duration(mocked_controller, 10)
# THEN: The maximum length of the slider should be set
mocked_controller.seek_slider.setMaximum.assert_called_once_with(5)
def test_update_ui(self):
"""
Test the update_ui() method on the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
player.state = MediaState.Playing
mocked_display = MagicMock()
mocked_display.media_player.state.return_value = QtMultimedia.QMediaPlayer.PausedState
mocked_display.controller.media_info.end_time = 1
mocked_display.media_player.position.return_value = 2
mocked_display.controller.seek_slider.isSliderDown.return_value = False
# WHEN: update_ui() is called
with patch.object(player, 'stop') as mocked_stop, \
patch.object(player, 'set_visible') as mocked_set_visible:
player.update_ui(mocked_display)
# THEN: The UI is updated
expected_stop_calls = [call(mocked_display), call(mocked_display)]
expected_position_calls = [call(), call()]
expected_block_signals_calls = [call(True), call(False)]
mocked_display.media_player.state.assert_called_once_with()
self.assertEqual(2, mocked_stop.call_count)
self.assertEqual(expected_stop_calls, mocked_stop.call_args_list)
self.assertEqual(2, mocked_display.media_player.position.call_count)
self.assertEqual(expected_position_calls, mocked_display.media_player.position.call_args_list)
mocked_set_visible.assert_called_once_with(mocked_display, False)
mocked_display.controller.seek_slider.isSliderDown.assert_called_once_with()
self.assertEqual(expected_block_signals_calls,
mocked_display.controller.seek_slider.blockSignals.call_args_list)
mocked_display.controller.seek_slider.setSliderPosition.assert_called_once_with(2)
def test_get_media_display_css(self):
"""
Test the get_media_display_css() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
# WHEN: get_media_display_css() is called
result = player.get_media_display_css()
# THEN: The css should be empty
self.assertEqual('', result)
def test_get_info(self):
"""
Test the get_info() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance
player = SystemPlayer(self)
# WHEN: get_info() is called
result = player.get_info()
# THEN: The info should be correct
expected_info = 'This media player uses your operating system to provide media capabilities.<br/> ' \
'<strong>Audio</strong><br/>[]<br/><strong>Video</strong><br/>[]<br/>'
self.assertEqual(expected_info, result)
@patch('openlp.core.ui.media.systemplayer.CheckMediaWorker')
@patch('openlp.core.ui.media.systemplayer.QtCore.QThread')
def test_check_media(self, MockQThread, MockCheckMediaWorker):
"""
Test the check_media() method of the SystemPlayer
"""
# GIVEN: A SystemPlayer instance and a mocked thread
valid_file = '/path/to/video.ogv'
mocked_application = MagicMock()
Registry().create()
Registry().register('application', mocked_application)
player = SystemPlayer(self)
mocked_thread = MagicMock()
mocked_thread.isRunning.side_effect = [True, False]
mocked_thread.quit = 'quit' # actually supposed to be a slot, but it's all mocked out anyway
MockQThread.return_value = mocked_thread
mocked_check_media_worker = MagicMock()
mocked_check_media_worker.play = 'play'
mocked_check_media_worker.result = True
MockCheckMediaWorker.return_value = mocked_check_media_worker
# WHEN: check_media() is called with a valid media file
result = player.check_media(valid_file)
# THEN: It should return True
MockQThread.assert_called_once_with()
MockCheckMediaWorker.assert_called_once_with(valid_file)
mocked_check_media_worker.setVolume.assert_called_once_with(0)
mocked_check_media_worker.moveToThread.assert_called_once_with(mocked_thread)
mocked_check_media_worker.finished.connect.assert_called_once_with('quit')
mocked_thread.started.connect.assert_called_once_with('play')
mocked_thread.start.assert_called_once_with()
self.assertEqual(2, mocked_thread.isRunning.call_count)
mocked_application.processEvents.assert_called_once_with()
self.assertTrue(result)
class TestCheckMediaWorker(TestCase):
"""
Test the CheckMediaWorker class
"""
def test_constructor(self):
"""
Test the constructor of the CheckMediaWorker class
"""
# GIVEN: A file path
path = 'file.ogv'
# WHEN: The CheckMediaWorker object is instantiated
worker = CheckMediaWorker(path)
# THEN: The correct values should be set up
self.assertIsNotNone(worker)
def test_signals_media(self):
"""
Test the signals() signal of the CheckMediaWorker class with a "media" origin
"""
# GIVEN: A CheckMediaWorker instance
worker = CheckMediaWorker('file.ogv')
# WHEN: signals() is called with media and BufferedMedia
with patch.object(worker, 'stop') as mocked_stop, \
patch.object(worker, 'finished') as mocked_finished:
worker.signals('media', worker.BufferedMedia)
# THEN: The worker should exit and the result should be True
mocked_stop.assert_called_once_with()
mocked_finished.emit.assert_called_once_with()
self.assertTrue(worker.result)
def test_signals_error(self):
"""
Test the signals() signal of the CheckMediaWorker class with a "error" origin
"""
# GIVEN: A CheckMediaWorker instance
worker = CheckMediaWorker('file.ogv')
# WHEN: signals() is called with error and BufferedMedia
with patch.object(worker, 'stop') as mocked_stop, \
patch.object(worker, 'finished') as mocked_finished:
worker.signals('error', None)
# THEN: The worker should exit and the result should be True
mocked_stop.assert_called_once_with()
mocked_finished.emit.assert_called_once_with()
self.assertFalse(worker.result)

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 # vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
# pylint: disable=protected-access
############################################################################### ###############################################################################
# OpenLP - Open Source Lyrics Projection # # OpenLP - Open Source Lyrics Projection #
@ -28,14 +29,13 @@ from urllib.error import URLError
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
from tests.helpers.songfileimport import SongImportTestHelper
from openlp.core import Registry from openlp.core import Registry
from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker from openlp.plugins.songs.forms.songselectform import SongSelectForm, SearchWorker
from openlp.plugins.songs.lib import Song from openlp.plugins.songs.lib import Song
from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL from openlp.plugins.songs.lib.songselect import SongSelectImport, LOGOUT_URL, BASE_URL
from openlp.plugins.songs.lib.importers.cclifile import CCLIFileImport
from tests.functional import MagicMock, patch, call from tests.functional import MagicMock, patch, call
from tests.helpers.songfileimport import SongImportTestHelper
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
TEST_PATH = os.path.abspath( TEST_PATH = os.path.abspath(
@ -71,7 +71,7 @@ class TestSongSelectImport(TestCase, TestMixin):
mocked_opener = MagicMock() mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener mocked_build_opener.return_value = mocked_opener
mocked_login_page = MagicMock() mocked_login_page = MagicMock()
mocked_login_page.find.return_value = {'value': 'blah'} mocked_login_page.find.side_effect = [{'value': 'blah'}, None]
MockedBeautifulSoup.return_value = mocked_login_page MockedBeautifulSoup.return_value = mocked_login_page
mock_callback = MagicMock() mock_callback = MagicMock()
importer = SongSelectImport(None) importer = SongSelectImport(None)
@ -112,7 +112,7 @@ class TestSongSelectImport(TestCase, TestMixin):
mocked_opener = MagicMock() mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener mocked_build_opener.return_value = mocked_opener
mocked_login_page = MagicMock() mocked_login_page = MagicMock()
mocked_login_page.find.side_effect = [{'value': 'blah'}, None] mocked_login_page.find.side_effect = [{'value': 'blah'}, MagicMock()]
MockedBeautifulSoup.return_value = mocked_login_page MockedBeautifulSoup.return_value = mocked_login_page
mock_callback = MagicMock() mock_callback = MagicMock()
importer = SongSelectImport(None) importer = SongSelectImport(None)
@ -165,7 +165,7 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(0, mock_callback.call_count, 'callback should not have been called') self.assertEqual(0, mock_callback.call_count, 'callback should not have been called')
self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once') self.assertEqual(1, mocked_opener.open.call_count, 'open should have been called once')
self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once') self.assertEqual(1, mocked_results_page.find_all.call_count, 'find_all should have been called once')
mocked_results_page.find_all.assert_called_with('li', 'result pane') mocked_results_page.find_all.assert_called_with('div', 'song-result')
self.assertEqual([], results, 'The search method should have returned an empty list') self.assertEqual([], results, 'The search method should have returned an empty list')
@patch('openlp.plugins.songs.lib.songselect.build_opener') @patch('openlp.plugins.songs.lib.songselect.build_opener')
@ -177,12 +177,18 @@ class TestSongSelectImport(TestCase, TestMixin):
# GIVEN: A bunch of mocked out stuff and an importer object # GIVEN: A bunch of mocked out stuff and an importer object
# first search result # first search result
mocked_result1 = MagicMock() mocked_result1 = MagicMock()
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] mocked_result1.find.side_effect = [
mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
MagicMock(string='James, John'),
MagicMock(find=MagicMock(return_value={'href': '/url1'}))
]
# second search result # second search result
mocked_result2 = MagicMock() mocked_result2 = MagicMock()
mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] mocked_result2.find.side_effect = [
mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
MagicMock(string='Philip'),
MagicMock(find=MagicMock(return_value={'href': '/url2'}))
]
# rest of the stuff # rest of the stuff
mocked_opener = MagicMock() mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener mocked_build_opener.return_value = mocked_opener
@ -199,10 +205,10 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
mocked_results_page.find_all.assert_called_with('li', 'result pane') mocked_results_page.find_all.assert_called_with('div', 'song-result')
expected_list = [ expected_list = [
{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, {'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'} {'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}
] ]
self.assertListEqual(expected_list, results, 'The search method should have returned two songs') self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
@ -215,16 +221,25 @@ class TestSongSelectImport(TestCase, TestMixin):
# GIVEN: A bunch of mocked out stuff and an importer object # GIVEN: A bunch of mocked out stuff and an importer object
# first search result # first search result
mocked_result1 = MagicMock() mocked_result1 = MagicMock()
mocked_result1.find.side_effect = [MagicMock(string='Title 1'), {'href': '/url1'}] mocked_result1.find.side_effect = [
mocked_result1.find_all.return_value = [MagicMock(string='Author 1-1'), MagicMock(string='Author 1-2')] MagicMock(find=MagicMock(return_value=MagicMock(string='Title 1'))),
MagicMock(string='James, John'),
MagicMock(find=MagicMock(return_value={'href': '/url1'}))
]
# second search result # second search result
mocked_result2 = MagicMock() mocked_result2 = MagicMock()
mocked_result2.find.side_effect = [MagicMock(string='Title 2'), {'href': '/url2'}] mocked_result2.find.side_effect = [
mocked_result2.find_all.return_value = [MagicMock(string='Author 2-1'), MagicMock(string='Author 2-2')] MagicMock(find=MagicMock(return_value=MagicMock(string='Title 2'))),
MagicMock(string='Philip'),
MagicMock(find=MagicMock(return_value={'href': '/url2'}))
]
# third search result # third search result
mocked_result3 = MagicMock() mocked_result3 = MagicMock()
mocked_result3.find.side_effect = [MagicMock(string='Title 3'), {'href': '/url3'}] mocked_result3.find.side_effect = [
mocked_result3.find_all.return_value = [MagicMock(string='Author 3-1'), MagicMock(string='Author 3-2')] MagicMock(find=MagicMock(return_value=MagicMock(string='Title 3'))),
MagicMock(string='Luke, Matthew'),
MagicMock(find=MagicMock(return_value={'href': '/url3'}))
]
# rest of the stuff # rest of the stuff
mocked_opener = MagicMock() mocked_opener = MagicMock()
mocked_build_opener.return_value = mocked_opener mocked_build_opener.return_value = mocked_opener
@ -241,9 +256,9 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice') self.assertEqual(2, mock_callback.call_count, 'callback should have been called twice')
self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice') self.assertEqual(2, mocked_opener.open.call_count, 'open should have been called twice')
self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice') self.assertEqual(2, mocked_results_page.find_all.call_count, 'find_all should have been called twice')
mocked_results_page.find_all.assert_called_with('li', 'result pane') mocked_results_page.find_all.assert_called_with('div', 'song-result')
expected_list = [{'title': 'Title 1', 'authors': ['Author 1-1', 'Author 1-2'], 'link': BASE_URL + '/url1'}, expected_list = [{'title': 'Title 1', 'authors': ['James', 'John'], 'link': BASE_URL + '/url1'},
{'title': 'Title 2', 'authors': ['Author 2-1', 'Author 2-2'], 'link': BASE_URL + '/url2'}] {'title': 'Title 2', 'authors': ['Philip'], 'link': BASE_URL + '/url2'}]
self.assertListEqual(expected_list, results, 'The search method should have returned two songs') self.assertListEqual(expected_list, results, 'The search method should have returned two songs')
@patch('openlp.plugins.songs.lib.songselect.build_opener') @patch('openlp.plugins.songs.lib.songselect.build_opener')
@ -337,7 +352,7 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary') self.assertIsNotNone(result, 'The get_song() method should have returned a song dictionary')
self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice') self.assertEqual(2, mocked_lyrics_page.find.call_count, 'The find() method should have been called twice')
self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice') self.assertEqual(2, mocked_find_all.call_count, 'The find_all() method should have been called twice')
self.assertEqual([call('section', 'lyrics'), call('section', 'lyrics')], self.assertEqual([call('div', 'song-viewer lyrics'), call('div', 'song-viewer lyrics')],
mocked_lyrics_page.find.call_args_list, mocked_lyrics_page.find.call_args_list,
'The find() method should have been called with the right arguments') 'The find() method should have been called with the right arguments')
self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list, self.assertEqual([call('p'), call('h3')], mocked_find_all.call_args_list,
@ -348,8 +363,9 @@ class TestSongSelectImport(TestCase, TestMixin):
self.assertEqual(3, len(result['verses']), 'Three verses should have been returned') self.assertEqual(3, len(result['verses']), 'Three verses should have been returned')
@patch('openlp.plugins.songs.lib.songselect.clean_song') @patch('openlp.plugins.songs.lib.songselect.clean_song')
@patch('openlp.plugins.songs.lib.songselect.Topic')
@patch('openlp.plugins.songs.lib.songselect.Author') @patch('openlp.plugins.songs.lib.songselect.Author')
def test_save_song_new_author(self, MockedAuthor, mocked_clean_song): def test_save_song_new_author(self, MockedAuthor, MockedTopic, mocked_clean_song):
""" """
Test that saving a song with a new author performs the correct actions Test that saving a song with a new author performs the correct actions
""" """
@ -366,6 +382,7 @@ class TestSongSelectImport(TestCase, TestMixin):
'ccli_number': '123456' 'ccli_number': '123456'
} }
MockedAuthor.display_name.__eq__.return_value = False MockedAuthor.display_name.__eq__.return_value = False
MockedTopic.name.__eq__.return_value = False
mocked_db_manager = MagicMock() mocked_db_manager = MagicMock()
mocked_db_manager.get_object_filtered.return_value = None mocked_db_manager.get_object_filtered.return_value = None
importer = SongSelectImport(mocked_db_manager) importer = SongSelectImport(mocked_db_manager)
@ -848,7 +865,7 @@ class TestSearchWorker(TestCase, TestMixin):
# WHEN: The start() method is called # WHEN: The start() method is called
with patch.object(worker, 'found_song') as mocked_found_song: with patch.object(worker, 'found_song') as mocked_found_song:
worker._found_song_callback(song) worker._found_song_callback(song) # pylint: disable=protected-access
# THEN: The "found_song" signal should have been emitted # THEN: The "found_song" signal should have been emitted
mocked_found_song.emit.assert_called_with(song) mocked_found_song.emit.assert_called_with(song)