This commit is contained in:
Philip Ridout 2019-03-15 20:56:32 +00:00
commit e1d2c67f33
26 changed files with 144 additions and 87 deletions

View File

@ -7,6 +7,7 @@ cover
.coverage
coverage
.directory
.vscode
dist
*.dll
documentation/build/doctrees

View File

@ -706,6 +706,7 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow):
else:
# The remaining elements do not fit, thus reset the indexes, create a new list and continue.
raw_list = raw_list[index + 1:]
log.debug(raw_list)
raw_list[0] = raw_tags + raw_list[0]
html_list = html_list[index + 1:]
html_list[0] = html_tags + html_list[0]

View File

@ -29,16 +29,14 @@ import copy
from PyQt5 import QtCore, QtWebChannel, QtWidgets
from openlp.core.common.path import Path, path_to_str
from openlp.core.common.path import path_to_str
from openlp.core.common.settings import Settings
from openlp.core.common.registry import Registry
from openlp.core.common.applocation import AppLocation
from openlp.core.ui import HideMode
from openlp.core.display.screens import ScreenList
log = logging.getLogger(__name__)
DISPLAY_PATH = Path(__file__).parent / 'html' / 'display.html'
CHECKERBOARD_PATH = Path(__file__).parent / 'html' / 'checkerboard.png'
OPENLP_SPLASH_SCREEN_PATH = Path(__file__).parent / 'html' / 'openlp-splash-screen.png'
class MediaWatcher(QtCore.QObject):
@ -126,7 +124,11 @@ class DisplayWindow(QtWidgets.QWidget):
self.webview.page().setBackgroundColor(QtCore.Qt.transparent)
self.layout.addWidget(self.webview)
self.webview.loadFinished.connect(self.after_loaded)
self.set_url(QtCore.QUrl.fromLocalFile(path_to_str(DISPLAY_PATH)))
display_base_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'display' / 'html'
self.display_path = display_base_path / 'display.html'
self.checkerboard_path = display_base_path / 'checkerboard.png'
self.openlp_splash_screen_path = display_base_path / 'openlp-splash-screen.png'
self.set_url(QtCore.QUrl.fromLocalFile(path_to_str(self.display_path)))
self.media_watcher = MediaWatcher(self)
self.channel = QtWebChannel.QWebChannel(self)
self.channel.registerObject('mediaWatcher', self.media_watcher)
@ -169,7 +171,7 @@ class DisplayWindow(QtWidgets.QWidget):
bg_color = Settings().value('core/logo background color')
image = Settings().value('core/logo file')
if path_to_str(image).startswith(':'):
image = OPENLP_SPLASH_SCREEN_PATH
image = self.openlp_splash_screen_path
image_uri = image.as_uri()
self.run_javascript('Display.setStartupSplashScreen("{bg_color}", "{image}");'.format(bg_color=bg_color,
image=image_uri))
@ -329,7 +331,7 @@ class DisplayWindow(QtWidgets.QWidget):
if theme.background_type == 'transparent' and not self.is_display:
theme_copy = copy.deepcopy(theme)
theme_copy.background_type = 'image'
theme_copy.background_filename = CHECKERBOARD_PATH
theme_copy.background_filename = self.checkerboard_path
exported_theme = theme_copy.export_theme()
else:
exported_theme = theme.export_theme()

View File

@ -417,11 +417,17 @@ class ProjectorDB(Manager):
value: (str) From ProjectorSource, Sources tables or PJLink default code list
"""
source_dict = {}
# Apparently, there was a change to the projector object. Test for which object has db id
if hasattr(projector, "id"):
chk = projector.id
elif hasattr(projector.entry, "id"):
chk = projector.entry.id
# Get default list first
for key in projector.source_available:
item = self.get_object_filtered(ProjectorSource,
and_(ProjectorSource.code == key,
ProjectorSource.projector_id == projector.id))
ProjectorSource.projector_id == chk))
if item is None:
source_dict[key] = PJLINK_DEFAULT_CODES[key]
else:

View File

@ -46,31 +46,10 @@ from openlp.core.projectors.sourceselectform import SourceSelectSingle, SourceSe
from openlp.core.ui.icons import UiIcons
from openlp.core.widgets.toolbar import OpenLPToolbar
log = logging.getLogger(__name__)
log.debug('projectormanager loaded')
# Dict for matching projector status to display icon
STATUS_ICONS = {
S_NOT_CONNECTED: ':/projector/projector_item_disconnect.png',
S_CONNECTING: ':/projector/projector_item_connect.png',
S_CONNECTED: ':/projector/projector_off.png',
S_OFF: ':/projector/projector_off.png',
S_INITIALIZE: ':/projector/projector_off.png',
S_STANDBY: ':/projector/projector_off.png',
S_WARMUP: ':/projector/projector_warmup.png',
S_ON: ':/projector/projector_on.png',
S_COOLDOWN: ':/projector/projector_cooldown.png',
E_ERROR: ':/projector/projector_error.png',
E_NETWORK: ':/projector/projector_not_connected_error.png',
E_SOCKET_TIMEOUT: ':/projector/projector_not_connected_error.png',
E_AUTHENTICATION: ':/projector/projector_not_connected_error.png',
E_UNKNOWN_SOCKET_ERROR: ':/projector/projector_not_connected_error.png',
E_NOT_CONNECTED: ':/projector/projector_not_connected_error.png'
}
class UiProjectorManager(object):
"""
UI part of the Projector Manager
@ -122,7 +101,7 @@ class UiProjectorManager(object):
self.one_toolbar.add_toolbar_action('connect_projector',
text=translate('OpenLP.ProjectorManager',
'Connect to selected projector.'),
icon=UiIcons().projector_connect,
icon=UiIcons().projector_select_connect,
tooltip=translate('OpenLP.ProjectorManager',
'Connect to selected projector.'),
triggers=self.on_connect_projector)
@ -136,7 +115,7 @@ class UiProjectorManager(object):
self.one_toolbar.add_toolbar_action('disconnect_projector',
text=translate('OpenLP.ProjectorManager',
'Disconnect from selected projectors'),
icon=UiIcons().projector_disconnect,
icon=UiIcons().projector_select_disconnect,
tooltip=translate('OpenLP.ProjectorManager',
'Disconnect from selected projector.'),
triggers=self.on_disconnect_projector)
@ -151,7 +130,7 @@ class UiProjectorManager(object):
self.one_toolbar.add_toolbar_action('poweron_projector',
text=translate('OpenLP.ProjectorManager',
'Power on selected projector'),
icon=UiIcons().projector_on,
icon=UiIcons().projector_power_on,
tooltip=translate('OpenLP.ProjectorManager',
'Power on selected projector.'),
triggers=self.on_poweron_projector)
@ -164,7 +143,7 @@ class UiProjectorManager(object):
triggers=self.on_poweron_projector)
self.one_toolbar.add_toolbar_action('poweroff_projector',
text=translate('OpenLP.ProjectorManager', 'Standby selected projector'),
icon=UiIcons().projector_off,
icon=UiIcons().projector_power_off,
tooltip=translate('OpenLP.ProjectorManager',
'Put selected projector in standby.'),
triggers=self.on_poweroff_projector)
@ -309,7 +288,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
S_INITIALIZE: UiIcons().projector_on,
S_STANDBY: UiIcons().projector_off,
S_WARMUP: UiIcons().projector_warmup,
S_ON: UiIcons().projector_off,
S_ON: UiIcons().projector_on,
S_COOLDOWN: UiIcons().projector_cooldown,
E_ERROR: UiIcons().projector_error,
E_NETWORK: UiIcons().error,
@ -880,6 +859,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
"""
Update the icons when the selected projectors change
"""
log.debug('update_icons(): Checking for selected projector items in list')
count = len(self.projector_list_widget.selectedItems())
projector = None
if count == 0:
@ -900,6 +880,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
self.get_toolbar_item('blank_projector_multiple', hidden=True)
self.get_toolbar_item('show_projector_multiple', hidden=True)
elif count == 1:
log.debug('update_icons(): Found one item selected')
projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole)
connected = QSOCKET_STATE[projector.link.state()] == S_CONNECTED
power = projector.link.power == S_ON
@ -910,12 +891,14 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
self.get_toolbar_item('blank_projector_multiple', hidden=True)
self.get_toolbar_item('show_projector_multiple', hidden=True)
if connected:
log.debug('update_icons(): Updating icons for connected state')
self.get_toolbar_item('view_projector', enabled=True)
self.get_toolbar_item('source_view_projector',
enabled=connected and power and projector.link.source_available is not None)
enabled=projector.link.source_available is not None and connected and power)
self.get_toolbar_item('edit_projector', hidden=True)
self.get_toolbar_item('delete_projector', hidden=True)
else:
log.debug('update_icons(): Updating for not connected state')
self.get_toolbar_item('view_projector', hidden=True)
self.get_toolbar_item('source_view_projector', hidden=True)
self.get_toolbar_item('edit_projector', enabled=True)
@ -931,6 +914,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
self.get_toolbar_item('blank_projector', enabled=False)
self.get_toolbar_item('show_projector', enabled=False)
else:
log.debug('update_icons(): Updating for multiple items selected')
self.get_toolbar_item('edit_projector', enabled=False)
self.get_toolbar_item('delete_projector', enabled=False)
self.get_toolbar_item('view_projector', hidden=True)

View File

@ -528,8 +528,9 @@ class PJLinkCommands(object):
sources.append(source)
sources.sort()
self.source_available = sources
log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.entry.name,
log.debug('({ip}) Setting projector source_available to "{data}"'.format(ip=self.entry.name,
data=self.source_available))
self.projectorUpdateIcons.emit()
return
def process_lamp(self, data):
@ -886,6 +887,9 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
elif status >= S_NOT_CONNECTED and status in QSOCKET_STATE:
# Socket connection status update
self.status_connect = status
# Check if we need to update error state as well
if self.error_status != S_OK and status != S_NOT_CONNECTED:
self.error_status = S_OK
elif status >= S_NOT_CONNECTED and status in PROJECTOR_STATE:
# Only affects the projector status
self.projector_status = status
@ -905,7 +909,14 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
message=status_message if msg is None else msg))
# Now that we logged extra information for debugging, broadcast the original change/message
(code, message) = self._get_status(status)
# Check for connection errors first
if self.error_status != S_OK:
log.debug('({ip}) Signalling error code'.format(ip=self.entry.name))
code, message = self._get_status(self.error_status)
status = self.error_status
else:
log.debug('({ip}) Signalling status code'.format(ip=self.entry.name))
code, message = self._get_status(status)
if msg is not None:
message = msg
elif message is None:

View File

@ -455,7 +455,7 @@ class AdvancedTab(SettingsTab):
Service Name options changed
"""
self.service_name_day.setEnabled(default_service_enabled)
time_enabled = default_service_enabled and self.service_name_day.currentIndex() is not 7
time_enabled = default_service_enabled and self.service_name_day.currentIndex() != 7
self.service_name_time.setEnabled(time_enabled)
self.service_name_edit.setEnabled(default_service_enabled)
self.service_name_revert_button.setEnabled(default_service_enabled)
@ -497,7 +497,7 @@ class AdvancedTab(SettingsTab):
"""
React to the day of the service name changing.
"""
self.service_name_time.setEnabled(service_day is not 7)
self.service_name_time.setEnabled(service_day != 7)
self.update_service_name_example(None)
def on_service_name_revert_button_clicked(self):

View File

@ -117,13 +117,17 @@ class UiIcons(object):
'presentation': {'icon': 'fa.bar-chart'},
'preview': {'icon': 'fa.laptop'},
'projector': {'icon': 'op.video'},
'projector_connect': {'icon': 'fa.plug'},
'projector_connect': {'icon': 'fa.plug'}, # Projector connect
'projector_cooldown': {'icon': 'fa.video-camera', 'attr': 'blue'},
'projector_disconnect': {'icon': 'fa.plug', 'attr': 'lightGray'},
'projector_disconnect': {'icon': 'fa.plug', 'attr': 'lightGray'}, # Projector disconnect
'projector_error': {'icon': 'fa.video-camera', 'attr': 'red'},
'projector_hdmi': {'icon': 'op.hdmi'},
'projector_off': {'icon': 'fa.video-camera', 'attr': 'black'},
'projector_on': {'icon': 'fa.video-camera', 'attr': 'green'},
'projector_power_off': {'icon': 'fa.video-camera', 'attr': 'red'}, # Toolbar power off
'projector_power_on': {'icon': 'fa.video-camera', 'attr': 'green'}, # Toolbar power on
'projector_off': {'icon': 'fa.video-camera', 'attr': 'black'}, # Projector off
'projector_on': {'icon': 'fa.video-camera', 'attr': 'green'}, # Projector on
'projector_select_connect': {'icon': 'fa.plug', 'attr': 'green'}, # Toolbar connect
'projector_select_disconnect': {'icon': 'fa.plug', 'attr': 'red'}, # Toolbar disconnect
'projector_warmup': {'icon': 'fa.video-camera', 'attr': 'yellow'},
'picture': {'icon': 'fa.picture-o'},
'print': {'icon': 'fa.print'},

View File

@ -206,7 +206,7 @@ class ThemesTab(SettingsTab):
find_and_set_in_combo_box(self.default_combo_box, self.global_theme)
# self.renderer.set_global_theme()
self.renderer.set_theme_level(self.theme_level)
if self.global_theme is not '':
if self.global_theme != '':
self._preview_global_theme()
def _preview_global_theme(self):

View File

@ -46,6 +46,7 @@ from openlp.plugins.presentations.lib.presentationcontroller import Presentation
if is_win():
from win32com.client import Dispatch
import pywintypes
uno_available = False
# Declare an empty exception to match the exception imported from UNO
class ErrorCodeIOException(Exception):

View File

@ -263,8 +263,9 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog, RegistryProperties)
self.login_progress_bar.setVisible(True)
self.application.process_events()
# Log the user in
if not self.song_select_importer.login(
self.username_edit.text(), self.password_edit.text(), self._update_login_progress):
subscription_level = self.song_select_importer.login(
self.username_edit.text(), self.password_edit.text(), self._update_login_progress)
if not subscription_level:
QtWidgets.QMessageBox.critical(
self,
translate('SongsPlugin.SongSelectForm', 'Error Logging In'),
@ -272,6 +273,14 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog, RegistryProperties)
'There was a problem logging in, perhaps your username or password is incorrect?')
)
else:
if subscription_level == 'Free':
QtWidgets.QMessageBox.information(
self,
translate('SongsPlugin.SongSelectForm', 'Free user'),
translate('SongsPlugin.SongSelectForm', 'You logged in with a free account, '
'the search will be limited to songs '
'in the public domain.')
)
if self.save_password_checkbox.isChecked():
Settings().setValue(self.plugin.settings_section + '/songselect username', self.username_edit.text())
Settings().setValue(self.plugin.settings_section + '/songselect password', self.password_edit.text())

View File

@ -81,7 +81,7 @@ class SongSelectImport(object):
:param username: SongSelect username
:param password: SongSelect password
:param callback: Method to notify of progress.
:return: True on success, False on failure.
:return: subscription level on success, None on failure.
"""
if callback:
callback()
@ -115,11 +115,31 @@ class SongSelectImport(object):
return False
if callback:
callback()
# Page if user is in an organization
if posted_page.find('input', id='SearchText') is not None:
return True
self.subscription_level = self.find_subscription_level(posted_page)
return self.subscription_level
# Page if user is not in an organization
elif posted_page.find('div', id="select-organization") is not None:
try:
home_page = BeautifulSoup(self.opener.open(BASE_URL).read(), 'lxml')
except (TypeError, URLError) as error:
log.exception('Could not reach SongSelect, {error}'.format(error=error))
self.subscription_level = self.find_subscription_level(home_page)
return self.subscription_level
else:
log.debug(posted_page)
return False
return None
def find_subscription_level(self, page):
scripts = page.find_all('script')
for tag in scripts:
if tag.string:
match = re.search("'Subscription': '(?P<subscription_level>[^']+)", tag.string)
if match:
return match.group('subscription_level')
log.error('Could not determine SongSelect subscription level')
return None
def logout(self):
"""
@ -128,7 +148,7 @@ class SongSelectImport(object):
try:
self.opener.open(LOGOUT_URL)
except (TypeError, URLError) as error:
log.exception('Could not log of SongSelect, {error}'.format(error=error))
log.exception('Could not log out of SongSelect, {error}'.format(error=error))
def search(self, search_text, max_results, callback=None):
"""
@ -145,7 +165,7 @@ class SongSelectImport(object):
'PrimaryLanguage': '',
'Keys': '',
'Themes': '',
'List': '',
'List': 'publicdomain' if self.subscription_level == 'Free' else '',
'Sort': '',
'SearchText': search_text
}

View File

@ -121,7 +121,7 @@ class SongsPlugin(Plugin):
self.song_export_item.setVisible(True)
self.song_tools_menu.menuAction().setVisible(True)
action_list = ActionList.get_instance()
action_list.add_action(self.song_import_item, UiStrings().Import)
#action_list.add_action(self.song_import_item, UiStrings().Import)
action_list.add_action(self.song_export_item, UiStrings().Export)
action_list.add_action(self.tools_reindex_item, UiStrings().Tools)
action_list.add_action(self.tools_find_duplicates, UiStrings().Tools)

View File

@ -1,5 +1,8 @@
version: OpenLP-win-ci-b{build}
image:
- Visual Studio 2017
clone_script:
- curl -L https://bazaar.launchpad.net/BRANCHPATH/tarball -o sourcecode.tar.gz
- 7z e sourcecode.tar.gz
@ -7,15 +10,17 @@ clone_script:
- mv BRANCHPATH openlp-branch
environment:
PYTHON: C:\\Python37-x64
matrix:
- PYTHON: C:\\Python37-x64
- PYTHON: C:\\Python37
install:
# Install dependencies from pypi
- "%PYTHON%\\python.exe -m pip install sqlalchemy alembic appdirs chardet beautifulsoup4 lxml Mako mysql-connector-python nose mock pyodbc psycopg2 pypiwin32 websockets asyncio waitress six webob requests QtAwesome PyQt5 pymediainfo"
- "%PYTHON%\\python.exe -m pip install sqlalchemy alembic appdirs chardet beautifulsoup4 lxml Mako mysql-connector-python pytest mock pyodbc psycopg2 pypiwin32 websockets asyncio waitress six webob requests QtAwesome PyQt5 PyQtWebEngine pymediainfo"
# Download and unpack mupdf
- appveyor DownloadFile https://mupdf.com/downloads/archive/mupdf-1.14.0-windows.zip
- 7z x mupdf-1.14.0-windows.zip
- cp mupdf-1.14.0-windows/mupdf.exe openlp-branch/mupdf.exe
- cp mupdf-1.14.0-windows/mutool.exe openlp-branch/mutool.exe
# Download and unpack mediainfo
- appveyor DownloadFile https://mediaarea.net/download/binary/mediainfo/18.08.1/MediaInfo_CLI_18.08.1_Windows_i386.zip
- mkdir MediaInfo
@ -27,7 +32,7 @@ build: off
test_script:
- cd openlp-branch
# Run the tests
- "%PYTHON%\\python.exe -m nose -v tests"
- "%PYTHON%\\python.exe -m pytest -v tests"
# Go back to the user root folder
- cd..

View File

@ -9,6 +9,7 @@ ignore = E402,E722,W503,W504
[flake8]
exclude=resources.py,vlc.py
max-line-length = 120
ignore = E402,W503,W504,D
[pycodestyle]

View File

@ -15,13 +15,12 @@
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
from tempfile import mkdtemp
# 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 #
###############################################################################
import os
from tempfile import mkdtemp
from unittest import TestCase
from unittest.mock import MagicMock, patch
@ -57,7 +56,8 @@ class TestRemoteDeploy(TestCase):
# GIVEN: A new downloaded zip file
mocked_zipfile = MagicMock()
MockZipFile.return_value = mocked_zipfile
root_path = Path('/tmp/remotes')
root_path_str = '{sep}tmp{sep}remotes'.format(sep=os.sep)
root_path = Path(root_path_str)
# WHEN: deploy_zipfile() is called
deploy_zipfile(root_path, 'site.zip')

View File

@ -322,7 +322,7 @@ class TestPath(TestCase):
obj = path.json_object(extra=1, args=2)
# THEN: A JSON decodable object should have been returned.
assert obj == {'__Path__': ('/', 'base', 'path', 'to', 'fi.le')}
assert obj == {'__Path__': (os.sep, 'base', 'path', 'to', 'fi.le')}
def test_path_json_object_base_path(self):
"""

View File

@ -178,7 +178,7 @@ class TestImageManager(TestCase, TestMixin):
# THEN a KeyError is thrown
with self.assertRaises(KeyError) as context:
self.image_manager.get_image(TEST_PATH, 'church1.jpg')
assert context.exception is not '', 'KeyError exception should have been thrown for missing image'
assert context.exception != '', 'KeyError exception should have been thrown for missing image'
@patch('openlp.core.lib.imagemanager.run_thread')
def test_different_dimension_image(self, mocked_run_thread):
@ -211,7 +211,7 @@ class TestImageManager(TestCase, TestMixin):
# WHEN: calling with correct image, but wrong dimensions
with self.assertRaises(KeyError) as context:
self.image_manager.get_image(full_path, 'church.jpg', 120, 120)
assert context.exception is not '', 'KeyError exception should have been thrown for missing dimension'
assert context.exception != '', 'KeyError exception should have been thrown for missing dimension'
@patch('openlp.core.lib.imagemanager.resize_image')
@patch('openlp.core.lib.imagemanager.image_to_byte')

View File

@ -154,13 +154,13 @@ class TestServiceItem(TestCase, TestMixin):
mocked_get_section_data_path:
mocked_exists.return_value = True
mocked_get_section_data_path.return_value = Path('/path/')
service_item.set_from_service(line, TEST_PATHb)
service_item.set_from_service(line, TEST_PATH)
# THEN: We should get back a valid service item
assert service_item.is_valid is True, 'The new service item should be valid'
assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image'
assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'
assert test_file == str(service_item.get_frame_path(0)), \
assert test_file == service_item.get_frame_path(0), \
'The frame path should match the full path to the image'
assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'
assert image_name == service_item.get_display_title(), 'The display title should match the first image name'
@ -328,7 +328,7 @@ class TestServiceItem(TestCase, TestMixin):
# WHEN: We add a custom from a saved service
line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj')
service_item.set_from_service(line, '/test/')
service_item.set_from_service(line, Path('/test/'))
# THEN: We should get back a valid service item
assert service_item.is_valid is True, 'The new service item should be valid'

View File

@ -57,4 +57,4 @@ class MediaPluginTest(TestCase, TestMixin):
# THEN: about() should return a string object
assert isinstance(MediaPlugin.about(), str)
# THEN: about() should return a non-empty string
assert len(MediaPlugin.about()) is not 0
assert len(MediaPlugin.about()) != 0

View File

@ -199,7 +199,7 @@ class TestLib(TestCase):
result = _remove_typos(diff)
# THEN: There should be no typos in the middle anymore. The remaining equals should have been merged.
assert len(result) is 1, 'The result should contain only one element.'
assert len(result) == 1, 'The result should contain only one element.'
assert result[0][0] == 'equal', 'The result should contain an equal element.'
assert result[0][1] == 0, 'The start indices should be kept.'
assert result[0][2] == 22, 'The stop indices should be kept.'

View File

@ -64,7 +64,7 @@ class TestSongSelectImport(TestCase, TestMixin):
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
def test_login_fails(self, MockedBeautifulSoup, mocked_build_opener):
"""
Test that when logging in to SongSelect fails, the login method returns False
Test that when logging in to SongSelect fails, the login method returns None
"""
# GIVEN: A bunch of mocked out stuff and an importer object
mocked_opener = MagicMock()
@ -80,12 +80,12 @@ class TestSongSelectImport(TestCase, TestMixin):
# WHEN: The login method is called after being rigged to fail
result = importer.login('username', 'password', mock_callback)
# THEN: callback was called 3 times, open was called twice, find was called twice, and False was returned
# THEN: callback was called 3 times, open was called twice, find was called twice, and None was returned
assert 3 == mock_callback.call_count, 'callback should have been called 3 times'
assert 2 == mocked_login_page.find.call_count, 'find should have been called twice'
assert 1 == mocked_posted_page.find.call_count, 'find should have been called once'
assert 2 == mocked_posted_page.find.call_count, 'find should have been called twice'
assert 2 == mocked_opener.open.call_count, 'opener should have been called twice'
assert result is False, 'The login method should have returned False'
assert result is None, 'The login method should have returned None'
@patch('openlp.plugins.songs.lib.songselect.build_opener')
def test_login_except(self, mocked_build_opener):
@ -129,7 +129,7 @@ class TestSongSelectImport(TestCase, TestMixin):
assert 2 == mocked_login_page.find.call_count, 'find should have been called twice on the login page'
assert 1 == mocked_posted_page.find.call_count, 'find should have been called once on the posted page'
assert 2 == mocked_opener.open.call_count, 'opener should have been called twice'
assert result is True, 'The login method should have returned True'
assert result is None, 'The login method should have returned the subscription level'
@patch('openlp.plugins.songs.lib.songselect.build_opener')
@patch('openlp.plugins.songs.lib.songselect.BeautifulSoup')
@ -146,7 +146,8 @@ class TestSongSelectImport(TestCase, TestMixin):
mocked_login_page.find.side_effect = [{'value': 'blah'}, mocked_form]
mocked_posted_page = MagicMock()
mocked_posted_page.find.return_value = MagicMock()
MockedBeautifulSoup.side_effect = [mocked_login_page, mocked_posted_page]
mocked_home_page = MagicMock()
MockedBeautifulSoup.side_effect = [mocked_login_page, mocked_posted_page, mocked_home_page]
mock_callback = MagicMock()
importer = SongSelectImport(None)
@ -158,7 +159,7 @@ class TestSongSelectImport(TestCase, TestMixin):
assert 2 == mocked_login_page.find.call_count, 'find should have been called twice on the login page'
assert 1 == mocked_posted_page.find.call_count, 'find should have been called once on the posted page'
assert 'https://profile.ccli.com/do/login', mocked_opener.open.call_args_list[1][0][0]
assert result is True, 'The login method should have returned True'
assert result is None, 'The login method should have returned the subscription level'
@patch('openlp.plugins.songs.lib.songselect.build_opener')
def test_logout(self, mocked_build_opener):
@ -191,6 +192,7 @@ class TestSongSelectImport(TestCase, TestMixin):
MockedBeautifulSoup.return_value = mocked_results_page
mock_callback = MagicMock()
importer = SongSelectImport(None)
importer.subscription_level = 'premium'
# WHEN: The login method is called after being rigged to fail
results = importer.search('text', 1000, mock_callback)
@ -231,6 +233,7 @@ class TestSongSelectImport(TestCase, TestMixin):
MockedBeautifulSoup.return_value = mocked_results_page
mock_callback = MagicMock()
importer = SongSelectImport(None)
importer.subscription_level = 'premium'
# WHEN: The search method is called
results = importer.search('text', 1000, mock_callback)
@ -282,6 +285,7 @@ class TestSongSelectImport(TestCase, TestMixin):
MockedBeautifulSoup.return_value = mocked_results_page
mock_callback = MagicMock()
importer = SongSelectImport(None)
importer.subscription_level = 'premium'
# WHEN: The search method is called
results = importer.search('text', 2, mock_callback)

View File

@ -45,8 +45,8 @@ class TestSongUsage(TestCase):
# THEN: about() should return a string object
assert isinstance(SongUsagePlugin.about(), str)
# THEN: about() should return a non-empty string
assert len(SongUsagePlugin.about()) is not 0
assert len(SongUsagePlugin.about()) is not 0
assert len(SongUsagePlugin.about()) != 0
assert len(SongUsagePlugin.about()) != 0
@patch('openlp.plugins.songusage.songusageplugin.Manager')
def test_song_usage_init(self, MockedManager):

View File

@ -134,6 +134,7 @@ class TestProjectorDB(TestCase, TestMixin):
"""
Set up anything necessary for all tests
"""
self.tmp_folder = mkdtemp(prefix='openlp_')
# Create a test app to keep from segfaulting
Registry.create()
self.registry = Registry()
@ -153,11 +154,11 @@ class TestProjectorDB(TestCase, TestMixin):
patch('openlp.core.ui.mainwindow.ThemeManager'), \
patch('openlp.core.ui.mainwindow.ProjectorManager'), \
patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \
patch('openlp.core.ui.mainwindow.server.HttpServer'):
patch('openlp.core.ui.mainwindow.server.HttpServer'), \
patch('openlp.core.state.State.list_plugins') as mock_plugins:
mock_plugins.return_value = []
self.main_window = MainWindow()
# Create a temporary database directory and database
self.tmp_folder = mkdtemp(prefix='openlp_')
tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB))
mocked_init_url.return_value = tmpdb_url
self.projector = ProjectorDB()

View File

@ -452,12 +452,16 @@ class TestPJLinkCommands(TestCase):
"""
Test saving video source available information
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.source_available = []
log_debug_calls = [call('({ip}) Setting projector sources_available to '
log_debug_calls = [call('({ip}) reset_information() connect status is '
'S_NOT_CONNECTED'.format(ip=pjlink.name)),
call('({ip}) Setting projector source_available to '
'"[\'11\', \'12\', \'21\', \'22\', \'31\', \'32\']"'.format(ip=pjlink.name))]
chk_data = '21 12 11 22 32 31' # Although they should already be sorted, use unsorted to verify method
chk_test = ['11', '12', '21', '22', '31', '32']

View File

@ -24,7 +24,7 @@ Package to test for proper bzr tags.
"""
import os
from subprocess import PIPE, Popen
from unittest import TestCase
from unittest import TestCase, SkipTest
TAGS1 = {'1.9.0', '1.9.1', '1.9.2', '1.9.3', '1.9.4', '1.9.5', '1.9.6', '1.9.7', '1.9.8', '1.9.9', '1.9.10',
@ -42,7 +42,10 @@ class TestBzrTags(TestCase):
path = os.path.dirname(__file__)
# WHEN getting the branches tags
try:
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
except Exception:
raise SkipTest('bzr is not installed')
std_out = bzr.communicate()[0]
count = len(TAGS1)
tags = [line.decode('utf-8').split()[0] for line in std_out.splitlines()]