diff --git a/.bzrignore b/.bzrignore index f60d6cfff..2b43ce13a 100644 --- a/.bzrignore +++ b/.bzrignore @@ -7,6 +7,7 @@ cover .coverage coverage .directory +.vscode dist *.dll documentation/build/doctrees diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index 35f144ee8..06ce89283 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -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] diff --git a/openlp/core/display/window.py b/openlp/core/display/window.py index d811de5b2..35442b5ed 100644 --- a/openlp/core/display/window.py +++ b/openlp/core/display/window.py @@ -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() diff --git a/openlp/core/projectors/db.py b/openlp/core/projectors/db.py index ef233999b..9249f0b60 100644 --- a/openlp/core/projectors/db.py +++ b/openlp/core/projectors/db.py @@ -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: diff --git a/openlp/core/projectors/manager.py b/openlp/core/projectors/manager.py index 966da08f8..c8c01401e 100644 --- a/openlp/core/projectors/manager.py +++ b/openlp/core/projectors/manager.py @@ -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) diff --git a/openlp/core/projectors/pjlink.py b/openlp/core/projectors/pjlink.py index 02d6c444b..24f1b04ed 100644 --- a/openlp/core/projectors/pjlink.py +++ b/openlp/core/projectors/pjlink.py @@ -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, - data=self.source_available)) + 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: @@ -953,7 +964,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): log.error('({ip}) Invalid initial packet received - closing socket'.format(ip=self.entry.name)) return self.disconnect_from_host() # Convert the initial login prompt with the expected PJLink normal command format for processing - log.debug('({ip}) check_login(): Formatting initial connection prompt' + log.debug('({ip}) check_login(): Formatting initial connection prompt ' 'to PJLink packet'.format(ip=self.entry.name)) return self.get_data('{start}{clss}{data}'.format(start=PJLINK_PREFIX, clss='1', diff --git a/openlp/core/ui/advancedtab.py b/openlp/core/ui/advancedtab.py index 4957b86d0..e4bc18703 100644 --- a/openlp/core/ui/advancedtab.py +++ b/openlp/core/ui/advancedtab.py @@ -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): diff --git a/openlp/core/ui/icons.py b/openlp/core/ui/icons.py index 5aa157e9f..0e83a01d8 100644 --- a/openlp/core/ui/icons.py +++ b/openlp/core/ui/icons.py @@ -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'}, diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py index d1998ccca..ee87b79d0 100644 --- a/openlp/core/ui/themestab.py +++ b/openlp/core/ui/themestab.py @@ -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): diff --git a/openlp/plugins/presentations/lib/impresscontroller.py b/openlp/plugins/presentations/lib/impresscontroller.py index d231acb05..1db6b9e68 100644 --- a/openlp/plugins/presentations/lib/impresscontroller.py +++ b/openlp/plugins/presentations/lib/impresscontroller.py @@ -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): diff --git a/openlp/plugins/songs/forms/songselectform.py b/openlp/plugins/songs/forms/songselectform.py index d8b4db300..68e393a82 100644 --- a/openlp/plugins/songs/forms/songselectform.py +++ b/openlp/plugins/songs/forms/songselectform.py @@ -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()) diff --git a/openlp/plugins/songs/lib/songselect.py b/openlp/plugins/songs/lib/songselect.py index bde9b7bf9..71e093cb1 100644 --- a/openlp/plugins/songs/lib/songselect.py +++ b/openlp/plugins/songs/lib/songselect.py @@ -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[^']+)", 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 } diff --git a/openlp/plugins/songs/songsplugin.py b/openlp/plugins/songs/songsplugin.py index d5c52edf0..963da89c7 100644 --- a/openlp/plugins/songs/songsplugin.py +++ b/openlp/plugins/songs/songsplugin.py @@ -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) diff --git a/scripts/appveyor.yml b/scripts/appveyor.yml index 43f14e69d..fd9ffac73 100644 --- a/scripts/appveyor.yml +++ b/scripts/appveyor.yml @@ -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.. diff --git a/setup.cfg b/setup.cfg index 71cd36460..5b443dcf7 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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] diff --git a/tests/functional/openlp_core/api/test_deploy.py b/tests/functional/openlp_core/api/test_deploy.py index 439dc2b8c..3a8225170 100644 --- a/tests/functional/openlp_core/api/test_deploy.py +++ b/tests/functional/openlp_core/api/test_deploy.py @@ -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') diff --git a/tests/functional/openlp_core/common/test_path.py b/tests/functional/openlp_core/common/test_path.py index d6668ea67..ad65d47c4 100644 --- a/tests/functional/openlp_core/common/test_path.py +++ b/tests/functional/openlp_core/common/test_path.py @@ -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): """ diff --git a/tests/functional/openlp_core/lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py index 33207f69b..9cb77ece9 100644 --- a/tests/functional/openlp_core/lib/test_image_manager.py +++ b/tests/functional/openlp_core/lib/test_image_manager.py @@ -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') diff --git a/tests/functional/openlp_core/lib/test_serviceitem.py b/tests/functional/openlp_core/lib/test_serviceitem.py index d2152bdec..9c3608fa4 100644 --- a/tests/functional/openlp_core/lib/test_serviceitem.py +++ b/tests/functional/openlp_core/lib/test_serviceitem.py @@ -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' diff --git a/tests/functional/openlp_plugins/media/test_mediaplugin.py b/tests/functional/openlp_plugins/media/test_mediaplugin.py index eabf35aa0..d5bfcac0b 100644 --- a/tests/functional/openlp_plugins/media/test_mediaplugin.py +++ b/tests/functional/openlp_plugins/media/test_mediaplugin.py @@ -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 diff --git a/tests/functional/openlp_plugins/songs/test_lib.py b/tests/functional/openlp_plugins/songs/test_lib.py index eedbe85c7..e9c657df3 100644 --- a/tests/functional/openlp_plugins/songs/test_lib.py +++ b/tests/functional/openlp_plugins/songs/test_lib.py @@ -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.' diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py index 1351faf51..1affd66c2 100644 --- a/tests/functional/openlp_plugins/songs/test_songselect.py +++ b/tests/functional/openlp_plugins/songs/test_songselect.py @@ -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) diff --git a/tests/functional/openlp_plugins/songusage/test_songusage.py b/tests/functional/openlp_plugins/songusage/test_songusage.py index 03847a601..e80c31c03 100644 --- a/tests/functional/openlp_plugins/songusage/test_songusage.py +++ b/tests/functional/openlp_plugins/songusage/test_songusage.py @@ -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): diff --git a/tests/openlp_core/projectors/test_projector_db.py b/tests/openlp_core/projectors/test_projector_db.py index ae25e011c..bc5899aeb 100644 --- a/tests/openlp_core/projectors/test_projector_db.py +++ b/tests/openlp_core/projectors/test_projector_db.py @@ -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() diff --git a/tests/openlp_core/projectors/test_projector_pjlink_commands_01.py b/tests/openlp_core/projectors/test_projector_pjlink_commands_01.py index eb85ac1f3..1218d998e 100644 --- a/tests/openlp_core/projectors/test_projector_pjlink_commands_01.py +++ b/tests/openlp_core/projectors/test_projector_pjlink_commands_01.py @@ -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'] diff --git a/tests/utils/test_bzr_tags.py b/tests/utils/test_bzr_tags.py index e6c00f707..66b159c96 100644 --- a/tests/utils/test_bzr_tags.py +++ b/tests/utils/test_bzr_tags.py @@ -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 - bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE) + 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()]