mirror of https://gitlab.com/openlp/openlp.git
Merge branch 'master' into list-view
This commit is contained in:
commit
fba1e54f05
|
@ -13,11 +13,13 @@ environment:
|
|||
CHOCO_VLC_ARG:
|
||||
FORCE_PACKAGING: 0
|
||||
FORCE_PACKAGING_MANUAL: 0
|
||||
PYICU_PACK: PyICU-2.9-cp38-cp38-win_amd64.whl
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
|
||||
PY_DIR: C:\\Python38
|
||||
CHOCO_VLC_ARG: --forcex86
|
||||
FORCE_PACKAGING: 0
|
||||
FORCE_PACKAGING_MANUAL: 0
|
||||
PYICU_PACK: PyICU-2.9-cp38-cp38-win32.whl
|
||||
- APPVEYOR_BUILD_WORKER_IMAGE: macos-catalina
|
||||
QT_QPA_PLATFORM: offscreen
|
||||
FORCE_PACKAGING: 0
|
||||
|
@ -36,9 +38,13 @@ install:
|
|||
# Install Windows only dependencies
|
||||
- cmd: python -m pip install pyodbc pypiwin32
|
||||
- cmd: choco install vlc %CHOCO_VLC_ARG% --no-progress --limit-output
|
||||
# Download and install pyicu for windows (originally from http://www.lfd.uci.edu/~gohlke/pythonlibs/)
|
||||
- cmd: python -m pip install https://get.openlp.org/win-sdk/%PYICU_PACK%
|
||||
# Mac only dependencies
|
||||
- sh: python -m pip install Pyro4 'pyobjc-core<8.2' 'pyobjc-framework-Cocoa<8.2' py-applescript
|
||||
- sh: brew install --cask vlc
|
||||
- sh: brew install pkg-config icu4c
|
||||
- sh: PATH="/usr/local/opt/icu4c/bin:/usr/local/opt/icu4c/sbin:$PATH" PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/opt/icu4c/lib/pkgconfig" python -m pip install pyicu
|
||||
- sh: python -m pip install Pyro4 'pyobjc-core<8.2' 'pyobjc-framework-Cocoa<8.2' py-applescript
|
||||
|
||||
build: off
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
3.0.0
|
||||
3.0.0.dev6+dbea8bf56
|
|
@ -22,7 +22,7 @@
|
|||
from openlp.core.api.versions.v2.controller import controller_views
|
||||
from openlp.core.api.versions.v2.core import core
|
||||
from openlp.core.api.versions.v2.service import service_views
|
||||
from openlp.core.api.versions.v2.plugins import plugins
|
||||
from openlp.core.api.versions.v2.plugins import plugins, alert_1_views, alert_2_views
|
||||
|
||||
|
||||
def register_blueprints(app):
|
||||
|
@ -30,3 +30,5 @@ def register_blueprints(app):
|
|||
app.register_blueprint(core, url_prefix='/api/v2/core/')
|
||||
app.register_blueprint(service_views, url_prefix='/api/v2/service/')
|
||||
app.register_blueprint(plugins, url_prefix='/api/v2/plugins/')
|
||||
app.register_blueprint(alert_2_views, url_prefix='/api/v2/plugins/alerts')
|
||||
app.register_blueprint(alert_1_views, url_prefix='/api/alert')
|
||||
|
|
|
@ -24,7 +24,7 @@ import logging
|
|||
import re
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api.lib import login_required
|
||||
from openlp.core.api.lib import login_required, extract_request, old_success_response, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.plugins.songs.lib import transpose_lyrics
|
||||
|
@ -33,6 +33,8 @@ log = logging.getLogger(__name__)
|
|||
|
||||
|
||||
plugins = Blueprint('v2-plugins', __name__)
|
||||
alert_1_views = Blueprint('v1-alert-plugin', __name__)
|
||||
alert_2_views = Blueprint('v2-alert-plugin', __name__)
|
||||
|
||||
|
||||
def search(plugin_name, text):
|
||||
|
@ -172,3 +174,27 @@ def transpose(transpose_value):
|
|||
chord_slides.append({'chords': verse_list[i + 1].strip(), 'verse': verse_list[i]})
|
||||
return jsonify(chord_slides), 200
|
||||
abort(400)
|
||||
|
||||
|
||||
@alert_2_views.route('', methods=['POST'])
|
||||
@login_required
|
||||
def alert():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
alert = data.get('text', '')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return '', 204
|
||||
abort(400)
|
||||
|
||||
|
||||
@alert_1_views.route('')
|
||||
@old_auth
|
||||
def old_alert():
|
||||
alert = extract_request(request.args.get('data', ''), 'text')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return old_success_response()
|
||||
|
|
|
@ -100,6 +100,25 @@ def trace_error_handler(logger):
|
|||
logger.error(log_string)
|
||||
|
||||
|
||||
def path_to_module(path):
|
||||
"""
|
||||
Convert a path to a module name (i.e openlp.core.common)
|
||||
|
||||
:param pathlib.Path path: The path to convert to a module name.
|
||||
:return: The module name.
|
||||
:rtype: str
|
||||
"""
|
||||
module_path = path.with_suffix('')
|
||||
return 'openlp.' + '.'.join(module_path.parts)
|
||||
|
||||
|
||||
def import_openlp_module(module_name):
|
||||
"""
|
||||
Refactor module import out for testability. In Python 3.11, mock.patch and import_module do not play along nicely.
|
||||
"""
|
||||
importlib.import_module(module_name)
|
||||
|
||||
|
||||
def extension_loader(glob_pattern, excluded_files=None):
|
||||
"""
|
||||
A utility function to find and load OpenLP extensions, such as plugins, presentation and media controllers and
|
||||
|
@ -119,25 +138,13 @@ def extension_loader(glob_pattern, excluded_files=None):
|
|||
log.debug('Attempting to import %s', extension_path)
|
||||
module_name = path_to_module(extension_path)
|
||||
try:
|
||||
importlib.import_module(module_name)
|
||||
import_openlp_module(module_name)
|
||||
except (ImportError, OSError):
|
||||
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
|
||||
log.exception('Failed to import {module_name} on path {extension_path}'
|
||||
.format(module_name=module_name, extension_path=extension_path))
|
||||
|
||||
|
||||
def path_to_module(path):
|
||||
"""
|
||||
Convert a path to a module name (i.e openlp.core.common)
|
||||
|
||||
:param pathlib.Path path: The path to convert to a module name.
|
||||
:return: The module name.
|
||||
:rtype: str
|
||||
"""
|
||||
module_path = path.with_suffix('')
|
||||
return 'openlp.' + '.'.join(module_path.parts)
|
||||
|
||||
|
||||
def get_frozen_path(frozen_option, non_frozen_option):
|
||||
"""
|
||||
Return a path based on the system status.
|
||||
|
|
|
@ -112,6 +112,15 @@ class ServiceItemType(IntEnum):
|
|||
Image = 2
|
||||
Command = 3
|
||||
|
||||
@staticmethod
|
||||
def parse(value):
|
||||
if value in [1, '1', 'Text', 'ServiceItemType.Text']:
|
||||
return ServiceItemType.Text
|
||||
elif value in [2, '2', 'Image', 'ServiceItemType.Image']:
|
||||
return ServiceItemType.Image
|
||||
elif value in [3, '3', 'Command', 'ServiceItemType.Command']:
|
||||
return ServiceItemType.Command
|
||||
|
||||
|
||||
@unique
|
||||
class PluginStatus(IntEnum):
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
The :mod:`languages` module provides a list of language names with utility functions.
|
||||
"""
|
||||
import itertools
|
||||
import locale
|
||||
import logging
|
||||
import re
|
||||
from collections import namedtuple
|
||||
|
@ -51,7 +52,8 @@ def translate(context, text, comment=None, qt_translate=QtCore.QCoreApplication.
|
|||
|
||||
|
||||
Language = namedtuple('Language', ['id', 'name', 'code'])
|
||||
COLLATOR = None
|
||||
ICU_COLLATOR = None
|
||||
DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+')
|
||||
LANGUAGES = sorted([
|
||||
Language(1, translate('common.languages', '(Afan) Oromo', 'Language code: om'), 'om'),
|
||||
Language(2, translate('common.languages', 'Abkhazian', 'Language code: ab'), 'ab'),
|
||||
|
@ -503,19 +505,25 @@ def format_time(text, local_time):
|
|||
return re.sub(r'%[a-zA-Z]', match_formatting, text)
|
||||
|
||||
|
||||
def get_locale_key(string, numeric=False):
|
||||
def get_locale_key(string):
|
||||
"""
|
||||
Creates a key for case insensitive, locale aware string sorting.
|
||||
|
||||
:param string: The corresponding string.
|
||||
"""
|
||||
string = string.lower()
|
||||
global COLLATOR
|
||||
if COLLATOR is None:
|
||||
language = LanguageManager.get_language()
|
||||
COLLATOR = QtCore.QCollator(QtCore.QLocale(language))
|
||||
COLLATOR.setNumericMode(numeric)
|
||||
return COLLATOR.sortKey(string)
|
||||
# ICU is the prefered way to handle locale sort key, we fallback to locale.strxfrm which will work in most cases.
|
||||
global ICU_COLLATOR
|
||||
try:
|
||||
if ICU_COLLATOR is None:
|
||||
import icu
|
||||
language = LanguageManager.get_language()
|
||||
icu_locale = icu.Locale(language)
|
||||
ICU_COLLATOR = icu.Collator.createInstance(icu_locale)
|
||||
return ICU_COLLATOR.getSortKey(string)
|
||||
except Exception:
|
||||
log.warning('ICU not found! Fallback to strxfrm')
|
||||
return locale.strxfrm(string).encode()
|
||||
|
||||
|
||||
def get_natural_key(string):
|
||||
|
@ -525,7 +533,13 @@ def get_natural_key(string):
|
|||
:param string: string to be sorted by
|
||||
Returns a list of string compare keys and integers.
|
||||
"""
|
||||
return get_locale_key(string, True)
|
||||
key = DIGITS_OR_NONDIGITS.findall(string)
|
||||
key = [int(part) if part.isdigit() else get_locale_key(part) for part in key]
|
||||
# Python 3 does not support comparison of different types anymore. So make sure, that we do not compare str
|
||||
# and int.
|
||||
if string and string[0].isdigit():
|
||||
return [b''] + key
|
||||
return key
|
||||
|
||||
|
||||
def get_language(name):
|
||||
|
|
|
@ -291,6 +291,8 @@ class Settings(QtCore.QSettings):
|
|||
'media/vlc arguments': '',
|
||||
'media/live volume': 50,
|
||||
'media/preview volume': 0,
|
||||
'media/live loop': False,
|
||||
'media/preview loop': False,
|
||||
'media/db type': 'sqlite',
|
||||
'media/db username': '',
|
||||
'media/db password': '',
|
||||
|
|
|
@ -463,7 +463,7 @@ class ServiceItem(RegistryProperties):
|
|||
header = service_item['serviceitem']['header']
|
||||
self.title = header['title']
|
||||
self.name = header['name']
|
||||
self.service_item_type = header['type']
|
||||
self.service_item_type = ServiceItemType.parse(header['type'])
|
||||
self.theme = header['theme']
|
||||
self.add_icon()
|
||||
self.raw_footer = header['footer']
|
||||
|
@ -891,7 +891,7 @@ class ServiceItem(RegistryProperties):
|
|||
data_dict = {
|
||||
'title': self.title,
|
||||
'name': self.name,
|
||||
'type': str(self.service_item_type),
|
||||
'type': self.service_item_type,
|
||||
'theme': self.theme,
|
||||
'footer': self.raw_footer,
|
||||
'audit': self.audit,
|
||||
|
|
|
@ -23,6 +23,8 @@ The :mod:`~openlp.core.ui.media` module contains classes and objects for media p
|
|||
"""
|
||||
import logging
|
||||
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__ + '.__init__')
|
||||
|
||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||
|
@ -83,9 +85,7 @@ class ItemMediaInfo(object):
|
|||
This class hold the media related info
|
||||
"""
|
||||
file_info = None
|
||||
volume = 100
|
||||
is_background = False
|
||||
is_looping_playback = False
|
||||
length = 0
|
||||
start_time = 0
|
||||
end_time = 0
|
||||
|
@ -97,6 +97,57 @@ class ItemMediaInfo(object):
|
|||
media_type = MediaType()
|
||||
|
||||
|
||||
def get_volume(controller) -> int:
|
||||
"""
|
||||
The volume needs to be retrieved
|
||||
|
||||
:param controller: the controller in use
|
||||
:return: Are we looping
|
||||
"""
|
||||
if controller.is_live:
|
||||
return Registry().get('settings').value('media/live volume')
|
||||
else:
|
||||
return Registry().get('settings').value('media/preview volume')
|
||||
|
||||
|
||||
def save_volume(controller, volume: int) -> None:
|
||||
"""
|
||||
The volume needs to be saved
|
||||
|
||||
:param controller: the controller in use
|
||||
:param volume: The volume to use and save
|
||||
:return: Are we looping
|
||||
"""
|
||||
if controller.is_live:
|
||||
return Registry().get('settings').setValue('media/live volume', volume)
|
||||
else:
|
||||
return Registry().get('settings').setValue('media/preview volume', volume)
|
||||
|
||||
|
||||
def is_looping_playback(controller) -> bool:
|
||||
"""
|
||||
:param controller: the controller in use
|
||||
:return: Are we looping
|
||||
"""
|
||||
if controller.is_live:
|
||||
return Registry().get('settings').value('media/live loop')
|
||||
else:
|
||||
return Registry().get('settings').value('media/preview loop')
|
||||
|
||||
|
||||
def toggle_looping_playback(controller) -> None:
|
||||
"""
|
||||
|
||||
:param controller: the controller in use
|
||||
:return: None
|
||||
"""
|
||||
if controller.is_live:
|
||||
Registry().get('settings').setValue('media/live loop', not Registry().get('settings').value('media/live loop'))
|
||||
else:
|
||||
Registry().get('settings').setValue('media/preview loop',
|
||||
not Registry().get('settings').value('media/preview loop'))
|
||||
|
||||
|
||||
def parse_optical_path(input_string):
|
||||
"""
|
||||
Split the optical path info.
|
||||
|
|
|
@ -42,7 +42,8 @@ from openlp.core.lib.serviceitem import ItemCapabilities
|
|||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.state import State
|
||||
from openlp.core.ui import DisplayControllerType, HideMode
|
||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_stream_path
|
||||
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_stream_path, \
|
||||
get_volume, toggle_looping_playback, is_looping_playback, save_volume
|
||||
from openlp.core.ui.media.remote import register_views
|
||||
from openlp.core.ui.media.vlcplayer import VlcPlayer, get_vlc
|
||||
|
||||
|
@ -236,10 +237,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
self.media_reset(controller)
|
||||
controller.media_info = ItemMediaInfo()
|
||||
controller.media_info.media_type = MediaType.Video
|
||||
if controller.is_live:
|
||||
controller.media_info.volume = self.settings.value('media/live volume')
|
||||
else:
|
||||
controller.media_info.volume = self.settings.value('media/preview volume')
|
||||
# background will always loop video.
|
||||
if service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||
controller.media_info.file_info = [file_path for (file_path, file_hash) in service_item.background_audio]
|
||||
|
@ -255,7 +252,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
elif service_item.is_capable(ItemCapabilities.HasBackgroundVideo):
|
||||
controller.media_info.file_info = [service_item.video_file_name]
|
||||
service_item.media_length = self.media_length(service_item.video_file_name)
|
||||
controller.media_info.is_looping_playback = True
|
||||
controller.media_info.is_background = True
|
||||
else:
|
||||
controller.media_info.file_info = [service_item.get_frame_path()]
|
||||
|
@ -457,13 +453,13 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
controller.seek_slider.blockSignals(False)
|
||||
controller.volume_slider.blockSignals(False)
|
||||
return False
|
||||
self.media_volume(controller, controller.media_info.volume)
|
||||
self.media_volume(controller, get_volume(controller))
|
||||
if not start_hidden:
|
||||
self._media_set_visibility(controller, True)
|
||||
controller.mediabar.actions['playbackPlay'].setVisible(False)
|
||||
controller.mediabar.actions['playbackPause'].setVisible(True)
|
||||
controller.mediabar.actions['playbackStop'].setDisabled(False)
|
||||
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.is_looping_playback)
|
||||
controller.mediabar.actions['playbackLoop'].setChecked(is_looping_playback(controller))
|
||||
controller.mediabar.actions['playbackStop'].setVisible(not controller.media_info.is_background or
|
||||
controller.media_info.media_type is MediaType.Audio)
|
||||
controller.mediabar.actions['playbackLoop'].setVisible((not controller.media_info.is_background and
|
||||
|
@ -501,7 +497,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
if controller.media_info.is_playing and controller.media_info.length > 0:
|
||||
controller.media_info.timer += TICK_TIME
|
||||
if controller.media_info.timer >= controller.media_info.start_time + controller.media_info.length:
|
||||
if controller.media_info.is_looping_playback:
|
||||
if is_looping_playback(controller):
|
||||
start_again = True
|
||||
else:
|
||||
self.media_stop(controller)
|
||||
|
@ -522,8 +518,11 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
end_seconds = controller.media_info.end_time // 1000
|
||||
end_minutes = end_seconds // 60
|
||||
end_seconds %= 60
|
||||
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
||||
(minutes, seconds, end_minutes, end_seconds))
|
||||
if end_minutes == 0 and end_seconds == 0:
|
||||
controller.position_label.setText('')
|
||||
else:
|
||||
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
||||
(minutes, seconds, end_minutes, end_seconds))
|
||||
|
||||
def media_pause_msg(self, msg):
|
||||
"""
|
||||
|
@ -573,8 +572,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
|
||||
:param controller: The controller that needs to be stopped
|
||||
"""
|
||||
controller.media_info.is_looping_playback = not controller.media_info.is_looping_playback
|
||||
controller.mediabar.actions['playbackLoop'].setChecked(controller.media_info.is_looping_playback)
|
||||
toggle_looping_playback(controller)
|
||||
controller.mediabar.actions['playbackLoop'].setChecked(is_looping_playback(controller))
|
||||
|
||||
def media_stop_msg(self, msg):
|
||||
"""
|
||||
|
@ -638,11 +637,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||
:param volume: The volume to be set
|
||||
"""
|
||||
self.log_debug(f'media_volume {volume}')
|
||||
if controller.is_live:
|
||||
self.settings.setValue('media/live volume', volume)
|
||||
else:
|
||||
self.settings.setValue('media/preview volume', volume)
|
||||
controller.media_info.volume = volume
|
||||
save_volume(controller, volume)
|
||||
self.current_media_players[controller.controller_type].volume(controller, volume)
|
||||
controller.volume_slider.setValue(volume)
|
||||
|
||||
|
|
|
@ -35,13 +35,11 @@ from openlp.core.common.i18n import translate
|
|||
from openlp.core.common.platform import is_linux, is_macosx, is_win
|
||||
from openlp.core.display.screens import ScreenList
|
||||
from openlp.core.lib.ui import critical_error_message_box
|
||||
from openlp.core.ui.media import MediaState, MediaType, VlCState
|
||||
from openlp.core.ui.media import MediaState, MediaType, VlCState, get_volume
|
||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
# Audio and video extensions copied from 'include/vlc_interface.h' from vlc 2.2.0 source
|
||||
|
||||
|
||||
STATE_WAIT_TIME = 60
|
||||
|
||||
|
@ -229,7 +227,7 @@ class VlcPlayer(MediaPlayer):
|
|||
controller.vlc_media.parse()
|
||||
controller.seek_slider.setMinimum(controller.media_info.start_time)
|
||||
controller.seek_slider.setMaximum(controller.media_info.end_time)
|
||||
self.volume(controller, controller.media_info.volume)
|
||||
self.volume(controller, get_volume(controller))
|
||||
return True
|
||||
|
||||
def media_state_wait(self, controller, media_state):
|
||||
|
@ -275,7 +273,7 @@ class VlcPlayer(MediaPlayer):
|
|||
threading.Thread(target=controller.vlc_media_player.play).start()
|
||||
if not self.media_state_wait(controller, VlCState.Playing):
|
||||
return False
|
||||
self.volume(controller, controller.media_info.volume)
|
||||
self.volume(controller, get_volume(controller))
|
||||
self.set_state(MediaState.Playing, controller)
|
||||
return True
|
||||
|
||||
|
|
|
@ -971,6 +971,8 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
|||
Registry().execute(
|
||||
'{text}_start'.format(text=self.service_item.name.lower()),
|
||||
[self.service_item, self.is_live, self._current_hide_mode, slide_no])
|
||||
if self.service_item.is_capable(ItemCapabilities.ProvidesOwnTheme):
|
||||
self._set_theme(self.service_item)
|
||||
else:
|
||||
self._set_theme(self.service_item)
|
||||
self.info_label.setText(self.service_item.title)
|
||||
|
|
|
@ -29,7 +29,6 @@ from openlp.core.lib.plugin import Plugin, StringContent
|
|||
from openlp.core.lib.theme import VerticalType
|
||||
from openlp.core.lib.ui import create_action
|
||||
from openlp.core.ui.icons import UiIcons
|
||||
from openlp.plugins.alerts.remote import register_views
|
||||
from openlp.plugins.alerts.forms.alertform import AlertForm
|
||||
from openlp.plugins.alerts.lib.alertsmanager import AlertsManager
|
||||
from openlp.plugins.alerts.lib.alertstab import AlertsTab
|
||||
|
@ -139,7 +138,6 @@ class AlertsPlugin(Plugin):
|
|||
self.tools_alert_item.setVisible(True)
|
||||
action_list = ActionList.get_instance()
|
||||
action_list.add_action(self.tools_alert_item, UiStrings().Tools)
|
||||
register_views()
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
from flask import Blueprint, request, abort
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_success_response, old_auth
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
|
||||
|
||||
v1_views = Blueprint('v1-alert-plugin', __name__)
|
||||
v2_views = Blueprint('v2-alert-plugin', __name__)
|
||||
|
||||
|
||||
@v2_views.route('', methods=['POST'])
|
||||
@login_required
|
||||
def alert():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
alert = data.get('text', '')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return '', 204
|
||||
abort(400)
|
||||
|
||||
|
||||
@v1_views.route('')
|
||||
@old_auth
|
||||
def old_alert():
|
||||
alert = extract_request(request.args.get('data', ''), 'text')
|
||||
if alert:
|
||||
if Registry().get('plugin_manager').get_plugin_by_name('alerts').status == PluginStatus.Active:
|
||||
Registry().get('alerts_manager').alerts_text.emit([alert])
|
||||
return old_success_response()
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_views, url_prefix='/api/v2/plugins/alerts')
|
||||
app.register_blueprint(v1_views, url_prefix='/api/alert')
|
|
@ -1,116 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
##########################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2023 OpenLP Developers #
|
||||
# ---------------------------------------------------------------------- #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
|
||||
##########################################################################
|
||||
import logging
|
||||
|
||||
from flask import abort, request, Blueprint, jsonify
|
||||
|
||||
from openlp.core.api import app
|
||||
from openlp.core.api.lib import login_required, extract_request, old_auth
|
||||
from openlp.core.lib.plugin import PluginStatus
|
||||
from openlp.core.common.registry import Registry
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
v1_media = Blueprint('v1-media-plugin', __name__)
|
||||
v2_media = Blueprint('v2-media-plugin', __name__)
|
||||
|
||||
|
||||
def search(text):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
return results
|
||||
return None
|
||||
|
||||
|
||||
def live(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.media_go_live.emit([id, True])
|
||||
|
||||
|
||||
def add(id):
|
||||
plugin = Registry().get('plugin_manager').get_plugin_by_name('media')
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.media_add_to_service.emit([item_id, True])
|
||||
|
||||
|
||||
@v2_media.route('/search')
|
||||
@login_required
|
||||
def search_view():
|
||||
text = request.args.get('text', '')
|
||||
result = search(text)
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
@v2_media.route('/add', methods=['POST'])
|
||||
@login_required
|
||||
def add_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v2_media.route('/live', methods=['POST'])
|
||||
@login_required
|
||||
def live_view():
|
||||
data = request.json
|
||||
if not data:
|
||||
abort(400)
|
||||
id = data.get('id', -1)
|
||||
live(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
# ----------------- DEPRECATED --------------
|
||||
@v1_media.route('/search')
|
||||
@old_auth
|
||||
def old_search():
|
||||
text = extract_request(request.args.get('data', ''), 'text')
|
||||
return jsonify({'results': {'items': search(text)}})
|
||||
|
||||
|
||||
@v1_media.route('/add')
|
||||
@old_auth
|
||||
def old_add():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
add(id)
|
||||
return '', 204
|
||||
|
||||
|
||||
@v1_media.route('/live')
|
||||
@old_auth
|
||||
def old_live():
|
||||
id = extract_request(request.args.get('data', ''), 'id')
|
||||
live(id)
|
||||
return '', 204
|
||||
# ---------------- END DEPRECATED ----------------
|
||||
|
||||
|
||||
def register_views():
|
||||
app.register_blueprint(v2_media, url_prefix='/api/v2/plugins/media/')
|
||||
app.register_blueprint(v1_media, url_prefix='/api/media/')
|
|
@ -24,7 +24,7 @@ presentations from a variety of document formats.
|
|||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from openlp.core.common import extension_loader, sha256_file_hash
|
||||
from openlp.core.common.i18n import translate
|
||||
|
@ -84,7 +84,9 @@ class PresentationPlugin(Plugin):
|
|||
has_old_scheme = True
|
||||
# Migrate each file
|
||||
presentation_paths = self.settings.value('presentations/presentations files') or []
|
||||
for path in presentation_paths:
|
||||
for presentation_path in presentation_paths:
|
||||
# Typecast to Path object
|
||||
path = Path(presentation_path)
|
||||
# check to see if the file exists before trying to process it.
|
||||
if not path.exists():
|
||||
continue
|
||||
|
|
|
@ -52,7 +52,7 @@ def report_song_list():
|
|||
report_file_path.with_suffix('.csv')
|
||||
Registry().get('application').set_busy_cursor()
|
||||
try:
|
||||
with report_file_path.open('wt') as export_file:
|
||||
with report_file_path.open('wt', encoding='utf8') as export_file:
|
||||
fieldnames = ('Title', 'Alternative Title', 'Copyright', 'Author(s)', 'Song Book', 'Topic')
|
||||
writer = csv.DictWriter(export_file, fieldnames=fieldnames, quoting=csv.QUOTE_ALL)
|
||||
headers = dict((n, n) for n in fieldnames)
|
||||
|
|
|
@ -52,6 +52,7 @@ WIN32_MODULES = [
|
|||
'win32com',
|
||||
'win32ui',
|
||||
'pywintypes',
|
||||
'icu',
|
||||
]
|
||||
|
||||
LINUX_MODULES = [
|
||||
|
|
1
setup.py
1
setup.py
|
@ -107,6 +107,7 @@ using a computer and a display/projector.""",
|
|||
'lxml',
|
||||
'Mako',
|
||||
"pillow",
|
||||
'PyICU',
|
||||
'pymediainfo >= 2.2',
|
||||
'pyobjc; platform_system=="Darwin"',
|
||||
'pyobjc-framework-Cocoa; platform_system=="Darwin"',
|
||||
|
|
|
@ -35,15 +35,38 @@ from openlp.core.common.registry import Registry
|
|||
ZERO_URL = '0.0.0.0'
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def mocked_get_installed_version():
|
||||
setup_patcher = patch('openlp.core.api.tab.get_installed_version')
|
||||
mocked_setup_patcher = setup_patcher.start()
|
||||
mocked_setup_patcher.return_value = None
|
||||
yield mocked_setup_patcher
|
||||
setup_patcher.stop()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_tab(settings):
|
||||
Registry().set_flag('website_version', '00-00-0000')
|
||||
Registry().set_flag('no_web_server', False)
|
||||
def api_tab_instantiate(mocked_get_installed_version, settings):
|
||||
forms = []
|
||||
parent = QtWidgets.QMainWindow()
|
||||
form = ApiTab(parent)
|
||||
yield form
|
||||
|
||||
def _create_api_tab():
|
||||
nonlocal forms, parent
|
||||
Registry().set_flag('website_version', '00-00-0000')
|
||||
Registry().set_flag('no_web_server', False)
|
||||
form = ApiTab(parent)
|
||||
forms.append(form)
|
||||
return form
|
||||
|
||||
yield _create_api_tab
|
||||
del parent
|
||||
del form
|
||||
for form in forms:
|
||||
del form
|
||||
mocked_get_installed_version.return_value = None
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_tab(api_tab_instantiate):
|
||||
yield api_tab_instantiate()
|
||||
|
||||
|
||||
def test_get_ip_address_default(api_tab):
|
||||
|
@ -183,9 +206,12 @@ def test_available_version_property_set_none(api_tab):
|
|||
assert api_tab.available_version_value.text() == '(unknown)'
|
||||
|
||||
|
||||
def test_installed_version_property_get_none(api_tab):
|
||||
def test_installed_version_property_get_none(mocked_get_installed_version, api_tab_instantiate, settings):
|
||||
"""Test that the installed version property is None on init"""
|
||||
# GIVEN: An uninitialised API tab
|
||||
mocked_get_installed_version.return_value = None
|
||||
settings.setValue('api/download_version', None)
|
||||
api_tab = api_tab_instantiate()
|
||||
|
||||
# WHEN: the installed version is GET'ed
|
||||
result = api_tab.installed_version
|
||||
|
|
|
@ -77,7 +77,7 @@ def test_extension_loader_files_found():
|
|||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file2.py'),
|
||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file3.py'),
|
||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file4.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module') as mocked_import_module:
|
||||
patch('openlp.core.common.import_openlp_module') as mocked_import_module:
|
||||
|
||||
# WHEN: Calling `extension_loader` with a list of files to exclude
|
||||
extension_loader('glob', ['file2.py', 'file3.py'])
|
||||
|
@ -97,7 +97,7 @@ def test_extension_loader_import_error():
|
|||
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
||||
patch.object(Path, 'glob', return_value=[
|
||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module', side_effect=ImportError()), \
|
||||
patch('openlp.core.common.import_openlp_module', side_effect=ImportError()), \
|
||||
patch('openlp.core.common.log') as mocked_logger:
|
||||
|
||||
# WHEN: Calling `extension_loader`
|
||||
|
@ -116,7 +116,7 @@ def test_extension_loader_os_error():
|
|||
return_value=Path('/', 'app', 'dir', 'openlp')), \
|
||||
patch.object(Path, 'glob', return_value=[
|
||||
Path('/', 'app', 'dir', 'openlp', 'import_dir', 'file1.py')]), \
|
||||
patch('openlp.core.common.importlib.import_module', side_effect=OSError()), \
|
||||
patch('openlp.core.common.import_openlp_module', side_effect=OSError()), \
|
||||
patch('openlp.core.common.log') as mocked_logger:
|
||||
|
||||
# WHEN: Calling `extension_loader`
|
||||
|
|
|
@ -273,7 +273,7 @@ def test_image_to_byte_base_64():
|
|||
assert 'byte_array base64ified' == result, 'The result should be the return value of the mocked base64 method'
|
||||
|
||||
|
||||
def test_create_thumb_with_size():
|
||||
def test_create_thumb_with_size(registry):
|
||||
"""
|
||||
Test the create_thumb() function with a given size.
|
||||
"""
|
||||
|
@ -308,7 +308,7 @@ def test_create_thumb_with_size():
|
|||
pass
|
||||
|
||||
|
||||
def test_create_thumb_no_size():
|
||||
def test_create_thumb_no_size(registry):
|
||||
"""
|
||||
Test the create_thumb() function with no size specified.
|
||||
"""
|
||||
|
@ -343,7 +343,7 @@ def test_create_thumb_no_size():
|
|||
pass
|
||||
|
||||
|
||||
def test_create_thumb_invalid_size():
|
||||
def test_create_thumb_invalid_size(registry):
|
||||
"""
|
||||
Test the create_thumb() function with invalid size specified.
|
||||
"""
|
||||
|
@ -379,7 +379,7 @@ def test_create_thumb_invalid_size():
|
|||
pass
|
||||
|
||||
|
||||
def test_create_thumb_width_only():
|
||||
def test_create_thumb_width_only(registry):
|
||||
"""
|
||||
Test the create_thumb() function with a size of only width specified.
|
||||
"""
|
||||
|
@ -415,7 +415,7 @@ def test_create_thumb_width_only():
|
|||
pass
|
||||
|
||||
|
||||
def test_create_thumb_height_only():
|
||||
def test_create_thumb_height_only(registry):
|
||||
"""
|
||||
Test the create_thumb() function with a size of only height specified.
|
||||
"""
|
||||
|
@ -451,7 +451,7 @@ def test_create_thumb_height_only():
|
|||
pass
|
||||
|
||||
|
||||
def test_create_thumb_empty_img():
|
||||
def test_create_thumb_empty_img(registry):
|
||||
"""
|
||||
Test the create_thumb() function with a size of only height specified.
|
||||
"""
|
||||
|
@ -502,7 +502,7 @@ def test_create_thumb_empty_img():
|
|||
|
||||
@patch('openlp.core.lib.QtGui.QImageReader')
|
||||
@patch('openlp.core.lib.build_icon')
|
||||
def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader):
|
||||
def test_create_thumb_path_fails(mocked_build_icon, MockQImageReader, registry):
|
||||
"""
|
||||
Test that build_icon() is run against the image_path when the thumbnail fails to be created
|
||||
"""
|
||||
|
@ -537,7 +537,7 @@ def test_check_item_selected_true():
|
|||
assert result is True, 'The result should be True'
|
||||
|
||||
|
||||
def test_check_item_selected_false():
|
||||
def test_check_item_selected_false(registry):
|
||||
"""
|
||||
Test that the check_item_selected() function returns False when there are no selected indexes.
|
||||
"""
|
||||
|
@ -608,7 +608,7 @@ def test_validate_thumb_file_exists_and_older():
|
|||
assert result is False, 'The result should be False'
|
||||
|
||||
|
||||
def test_resize_thumb():
|
||||
def test_resize_thumb(registry):
|
||||
"""
|
||||
Test the resize_thumb() function
|
||||
"""
|
||||
|
@ -630,7 +630,7 @@ def test_resize_thumb():
|
|||
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
||||
|
||||
|
||||
def test_resize_thumb_ignoring_aspect_ratio():
|
||||
def test_resize_thumb_ignoring_aspect_ratio(registry):
|
||||
"""
|
||||
Test the resize_thumb() function ignoring aspect ratio
|
||||
"""
|
||||
|
@ -652,7 +652,7 @@ def test_resize_thumb_ignoring_aspect_ratio():
|
|||
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
|
||||
|
||||
|
||||
def test_resize_thumb_width_aspect_ratio():
|
||||
def test_resize_thumb_width_aspect_ratio(registry):
|
||||
"""
|
||||
Test the resize_thumb() function using the image's width as the reference
|
||||
"""
|
||||
|
@ -670,7 +670,7 @@ def test_resize_thumb_width_aspect_ratio():
|
|||
assert wanted_width == result_size.width(), 'The image should have the requested width.'
|
||||
|
||||
|
||||
def test_resize_thumb_same_aspect_ratio():
|
||||
def test_resize_thumb_same_aspect_ratio(registry):
|
||||
"""
|
||||
Test the resize_thumb() function when the image and the wanted aspect ratio are the same
|
||||
"""
|
||||
|
|
|
@ -862,7 +862,7 @@ def test_to_dict_text_item(mocked_sha256_file_hash, state_media, settings, servi
|
|||
],
|
||||
'theme': None,
|
||||
'title': 'Amazing Grace',
|
||||
'type': 'ServiceItemType.Text',
|
||||
'type': ServiceItemType.Text,
|
||||
'data': {'authors': 'John Newton', 'title': 'amazing grace@'}
|
||||
}
|
||||
assert result == expected_dict
|
||||
|
@ -905,7 +905,7 @@ def test_to_dict_image_item(state_media, settings, service_item_env):
|
|||
],
|
||||
'theme': -1,
|
||||
'title': 'Images',
|
||||
'type': 'ServiceItemType.Image',
|
||||
'type': ServiceItemType.Image,
|
||||
'data': {}
|
||||
}
|
||||
assert result == expected_dict
|
||||
|
@ -961,7 +961,7 @@ def test_to_dict_presentation_item(mocked_image_uri, mocked_get_data_path, state
|
|||
],
|
||||
'theme': None,
|
||||
'title': '',
|
||||
'type': 'ServiceItemType.Command',
|
||||
'type': ServiceItemType.Command,
|
||||
'data': {}
|
||||
}
|
||||
assert result == expected_dict
|
||||
|
|
|
@ -22,14 +22,18 @@
|
|||
Package to test the openlp.core.ui.media package.
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
from unittest import skipUnless
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.common.platform import is_linux, is_macosx
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.ui import DisplayControllerType, HideMode
|
||||
from openlp.core.ui.media.mediacontroller import MediaController
|
||||
from openlp.core.ui.media import ItemMediaInfo, MediaState
|
||||
from openlp.core.ui.media import ItemMediaInfo, MediaState, MediaType
|
||||
|
||||
from tests.utils.constants import RESOURCE_PATH
|
||||
|
||||
|
@ -46,6 +50,216 @@ def media_env(registry):
|
|||
yield media_controller
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.mediacontroller.register_views')
|
||||
def test_setup(mocked_register_views, media_env):
|
||||
"""
|
||||
Test that the setup method is called correctly
|
||||
"""
|
||||
# GIVEN: A media controller, and function list
|
||||
expected_functions_list = ['bootstrap_initialise', 'bootstrap_post_set_up', 'bootstrap_completion',
|
||||
'playbackPlay', 'playbackPause', 'playbackStop', 'playbackLoop', 'seek_slider',
|
||||
'volume_slider', 'media_hide', 'media_blank', 'media_unblank', 'songs_hide',
|
||||
'songs_blank', 'songs_unblank']
|
||||
# WHEN: Setup is called
|
||||
media_env.setup()
|
||||
# THEN: the functions should be defined along with the api blueprint
|
||||
assert list(Registry().functions_list.keys()) == expected_functions_list, \
|
||||
f'The function list should have been {(Registry().functions_list.keys())}'
|
||||
mocked_register_views.called_once(), 'The media blueprint has not been registered'
|
||||
|
||||
|
||||
def test_initialise_good(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly
|
||||
"""
|
||||
# GIVEN: a mocked setup
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup:
|
||||
# THEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
|
||||
|
||||
def test_initialise_missing_vlc(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly with no VLC
|
||||
"""
|
||||
# GIVEN: a mocked setup and no VLC
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False):
|
||||
# THEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
text = State().get_text()
|
||||
if not is_macosx():
|
||||
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.mediacontroller.pymediainfo_available', False)
|
||||
def test_initialise_missing_pymedia(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly with no pymediainfo
|
||||
"""
|
||||
# GIVEN: a mocked setup and no pymedia
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup:
|
||||
# THEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
text = State().get_text()
|
||||
if not is_macosx():
|
||||
assert text.find("python3-pymediainfo") > 0, "PyMedia should not be missing"
|
||||
|
||||
|
||||
@skipUnless(is_linux(), "Linux only")
|
||||
def test_initialise_missing_pymedia_fedora(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly with no VLC
|
||||
"""
|
||||
# GIVEN: a mocked setup and no VLC
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||
patch('openlp.core.ui.media.mediacontroller.is_linux', return_value=True):
|
||||
# WHEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
text = State().get_text()
|
||||
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||
assert text.find("rpmfusion") > 0, "RPMFusion should provide the modules"
|
||||
|
||||
|
||||
@skipUnless(is_linux(), "Linux only")
|
||||
def test_initialise_missing_pymedia_not_fedora(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly with no VLC
|
||||
"""
|
||||
# GIVEN: a mocked setup and no VLC
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||
patch('openlp.core.ui.media.mediacontroller.is_linux', return_value=False):
|
||||
# WHEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
text = State().get_text()
|
||||
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||
assert text.find("python3-vlc") > 0, "VLC should not be missing"
|
||||
assert text.find("rpmfusion") == -1, "RPMFusion should not provide the modules"
|
||||
|
||||
|
||||
def test_initialise_missing_pymedia_mac_os(media_env, state_media):
|
||||
"""
|
||||
Test that the bootstrap initialise method is called correctly with no VLC
|
||||
"""
|
||||
# GIVEN: a mocked setup and no VLC
|
||||
with patch.object(media_env.media_controller, u'setup') as mocked_setup, \
|
||||
patch('openlp.core.ui.media.mediacontroller.get_vlc', return_value=False), \
|
||||
patch('openlp.core.ui.media.mediacontroller.is_macosx', return_value=True):
|
||||
# WHEN: The underlying method is called
|
||||
media_env.media_controller.bootstrap_initialise()
|
||||
# THEN: The following should have happened
|
||||
mocked_setup.called_once(), 'The setup function has been called'
|
||||
text = State().get_text()
|
||||
assert text.find("python3-pymediainfo") == -1, "PyMedia should be missing"
|
||||
assert text.find("python3-vlc") == -1, "PyMedia should be missing"
|
||||
assert text.find("videolan") > 0, "VideoLAN should provide the modules"
|
||||
|
||||
|
||||
def test_post_set_up_good(media_env, state_media):
|
||||
"""
|
||||
Test the Bootstrap post set up assuming all functions are good
|
||||
"""
|
||||
# GIVEN: A working environment
|
||||
media_env.vlc_live_media_stop = MagicMock()
|
||||
media_env.vlc_preview_media_stop = MagicMock()
|
||||
media_env.vlc_live_media_tick = MagicMock()
|
||||
media_env.vlc_preview_media_tick = MagicMock()
|
||||
State().add_service("mediacontroller", 0)
|
||||
State().update_pre_conditions("mediacontroller", True)
|
||||
# WHEN: I call the function
|
||||
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||
media_env.bootstrap_post_set_up()
|
||||
# THEN: the environment is set up correctly
|
||||
assert mocked_display.call_count == 2, "Should have been called twice"
|
||||
text = State().get_text()
|
||||
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||
assert mocked_display.has_calls(None, False) # Live Controller
|
||||
assert mocked_display.has_calls(None, True) # Preview Controller
|
||||
|
||||
|
||||
def test_media_state_live(media_env, state_media):
|
||||
"""
|
||||
Test the Bootstrap post set up assuming all functions are good
|
||||
"""
|
||||
# GIVEN: A working environment
|
||||
media_env.vlc_live_media_stop = MagicMock()
|
||||
media_env.vlc_preview_media_stop = MagicMock()
|
||||
media_env.vlc_preview_media_tick = MagicMock()
|
||||
mocked_live_controller = MagicMock()
|
||||
mocked_live_controller.is_live = True
|
||||
mocked_live_controller.media_info.media_type = MediaType.Audio
|
||||
Registry().register('live_controller', mocked_live_controller)
|
||||
media_env.media_controller.vlc_player = MagicMock()
|
||||
media_env.media_controller._display_controllers = MagicMock(return_value=mocked_live_controller)
|
||||
# WHEN: I call the function
|
||||
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||
media_env.bootstrap_post_set_up()
|
||||
# THEN: the environment is set up correctly
|
||||
text = State().get_text()
|
||||
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||
assert mocked_display.has_calls(None, False) # Live Controller
|
||||
assert mocked_display.has_calls(None, True) # Preview Controller
|
||||
|
||||
|
||||
def test_post_set_up_no_controller(media_env, state_media):
|
||||
"""
|
||||
Test the Bootstrap post set up assuming all functions are good
|
||||
"""
|
||||
# GIVEN: A working environment
|
||||
media_env.vlc_live_media_stop = MagicMock()
|
||||
media_env.vlc_preview_media_stop = MagicMock()
|
||||
media_env.vlc_live_media_tick = MagicMock()
|
||||
media_env.vlc_preview_media_tick = MagicMock()
|
||||
State().add_service("mediacontroller", 0)
|
||||
State().update_pre_conditions("mediacontroller", False)
|
||||
# WHEN: I call the function
|
||||
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||
media_env.bootstrap_post_set_up()
|
||||
# THEN: the environment is set up correctly
|
||||
assert mocked_display.call_count == 0, "Should have not have been called twice"
|
||||
text = State().get_text()
|
||||
assert text.find("No Displays") == -1, "No Displays have been disable"
|
||||
|
||||
|
||||
def test_post_set_up_controller_exception(media_env, state_media):
|
||||
"""
|
||||
Test the Bootstrap post set up assuming all functions are good
|
||||
"""
|
||||
# GIVEN: A working environment
|
||||
media_env.vlc_live_media_stop = MagicMock()
|
||||
media_env.vlc_preview_media_stop = MagicMock()
|
||||
media_env.vlc_live_media_tick = MagicMock()
|
||||
media_env.vlc_preview_media_tick = MagicMock()
|
||||
State().add_service("mediacontroller", 0)
|
||||
State().update_pre_conditions("mediacontroller", True)
|
||||
State().add_service("media_live", 0)
|
||||
State().update_pre_conditions("media_live", True)
|
||||
# WHEN: I call the function
|
||||
with patch.object(media_env.media_controller, u'setup_display') as mocked_display:
|
||||
mocked_display.side_effect = AttributeError()
|
||||
try:
|
||||
media_env.bootstrap_post_set_up()
|
||||
except AttributeError:
|
||||
pass
|
||||
# THEN: the environment is set up correctly
|
||||
text = State().get_text()
|
||||
assert text.find("Displays") > 0, "Displays have been disable"
|
||||
assert mocked_display.call_count == 2, "Should have been called twice"
|
||||
|
||||
|
||||
def test_resize(media_env):
|
||||
"""
|
||||
Test that the resize method is called correctly
|
||||
|
@ -86,7 +300,6 @@ def test_load_video(media_env, settings):
|
|||
# The video should have autoplayed
|
||||
# The controls should have been made visible
|
||||
media_env.media_controller.media_reset.assert_called_once_with(mocked_slide_controller)
|
||||
assert mocked_slide_controller.media_info.volume == 1
|
||||
media_env.media_controller.media_play.assert_called_once_with(mocked_slide_controller, False)
|
||||
media_env.media_controller.set_controls_visible.assert_called_once_with(mocked_slide_controller, True)
|
||||
|
||||
|
|
|
@ -283,7 +283,7 @@ def test_check_not_available(mocked_get_vlc):
|
|||
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||
def test_load(mocked_normcase, mocked_get_vlc):
|
||||
def test_load(mocked_normcase, mocked_get_vlc, settings):
|
||||
"""
|
||||
Test loading a video into VLC
|
||||
"""
|
||||
|
@ -294,7 +294,6 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
|||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.media_info.media_type = MediaType.Video
|
||||
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
||||
mocked_vlc_media = MagicMock()
|
||||
|
@ -305,8 +304,7 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: A video is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume:
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
mocked_normcase.assert_called_with(media_path)
|
||||
|
@ -314,14 +312,13 @@ def test_load(mocked_normcase, mocked_get_vlc):
|
|||
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||
mocked_vlc_media.parse.assert_called_with()
|
||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
||||
assert result is True
|
||||
|
||||
|
||||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||
def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||
def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||
"""
|
||||
Test loading an audio CD into VLC
|
||||
"""
|
||||
|
@ -333,7 +330,6 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.media_info.media_type = MediaType.CD
|
||||
mocked_controller.media_info.title_track = 1
|
||||
mocked_vlc_media = MagicMock()
|
||||
|
@ -351,8 +347,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -361,7 +356,6 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||
mocked_vlc_media.parse.assert_called_with()
|
||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
||||
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
||||
|
@ -371,7 +365,7 @@ def test_load_audio_cd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||
def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||
def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||
"""
|
||||
Test loading an audio CD into VLC on Windows
|
||||
"""
|
||||
|
@ -383,7 +377,6 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
|||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.media_info.media_type = MediaType.CD
|
||||
mocked_controller.media_info.file_info.absoluteFilePath.return_value = media_path
|
||||
mocked_controller.media_info.title_track = 1
|
||||
|
@ -400,8 +393,7 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -410,7 +402,6 @@ def test_load_audio_cd_on_windows(mocked_normcase, mocked_get_vlc, mocked_is_win
|
|||
assert mocked_vlc_media == mocked_controller.vlc_media
|
||||
mocked_controller.vlc_media_player.set_media.assert_called_with(mocked_vlc_media)
|
||||
mocked_vlc_media.parse.assert_called_with()
|
||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||
assert result is True
|
||||
|
||||
|
@ -449,7 +440,7 @@ def test_load_audio_cd_no_tracks(mocked_normcase, mocked_get_vlc, mocked_is_win)
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: An audio CD is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume'), patch.object(vlc_player, 'media_state_wait'):
|
||||
with patch.object(vlc_player, 'media_state_wait'):
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -465,7 +456,7 @@ def test_load_audio_cd_no_tracks(mocked_normcase, mocked_get_vlc, mocked_is_win)
|
|||
@patch('openlp.core.ui.media.vlcplayer.is_win')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
@patch('openlp.core.ui.media.vlcplayer.os.path.normcase')
|
||||
def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
||||
def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win, settings):
|
||||
"""
|
||||
Test loading a DVD into VLC
|
||||
"""
|
||||
|
@ -477,14 +468,12 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
mocked_get_vlc.return_value = mocked_vlc
|
||||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.media_info.media_type = MediaType.DVD
|
||||
mocked_controller.media_info.title_track = '2'
|
||||
mocked_controller.media_info.audio_track = 2
|
||||
mocked_controller.media_info.subtitle_track = 4
|
||||
mocked_vlc_media = MagicMock()
|
||||
mocked_media = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.media_info.start_time = 20000
|
||||
mocked_controller.media_info.end_time = 30000
|
||||
mocked_controller.media_info.length = 10000
|
||||
|
@ -493,8 +482,7 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
vlc_player = VlcPlayer(None)
|
||||
|
||||
# WHEN: A DVD clip is loaded into VLC
|
||||
with patch.object(vlc_player, 'volume') as mocked_volume, \
|
||||
patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
result = vlc_player.load(mocked_controller, mocked_display, media_path)
|
||||
|
||||
# THEN: The video should be loaded
|
||||
|
@ -505,7 +493,6 @@ def test_load_dvd(mocked_normcase, mocked_get_vlc, mocked_is_win):
|
|||
mocked_controller.vlc_media_player.audio_set_track.assert_called_with(2)
|
||||
mocked_controller.vlc_media_player.video_set_spu.assert_called_with(4)
|
||||
mocked_vlc_media.parse.assert_called_with()
|
||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
||||
mocked_media_state_wait.assert_called_with(mocked_controller, ANY)
|
||||
mocked_controller.seek_slider.setMinimum.assert_called_with(20000)
|
||||
mocked_controller.seek_slider.setMaximum.assert_called_with(30000)
|
||||
|
@ -606,7 +593,7 @@ def test_resize():
|
|||
|
||||
@patch('openlp.core.ui.media.vlcplayer.threading')
|
||||
@patch('openlp.core.ui.media.vlcplayer.get_vlc')
|
||||
def test_play(mocked_get_vlc, mocked_threading):
|
||||
def test_play(mocked_get_vlc, mocked_threading, settings):
|
||||
"""
|
||||
Test the play() method
|
||||
"""
|
||||
|
@ -618,21 +605,17 @@ def test_play(mocked_get_vlc, mocked_threading):
|
|||
mocked_display = MagicMock()
|
||||
mocked_controller = MagicMock()
|
||||
mocked_media = MagicMock()
|
||||
mocked_controller.media_info.volume = 100
|
||||
mocked_controller.vlc_media_player.get_media.return_value = mocked_media
|
||||
vlc_player = VlcPlayer(None)
|
||||
vlc_player.set_state(MediaState.Paused, mocked_controller)
|
||||
|
||||
# WHEN: play() is called
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait, \
|
||||
patch.object(vlc_player, 'volume') as mocked_volume:
|
||||
with patch.object(vlc_player, 'media_state_wait') as mocked_media_state_wait:
|
||||
mocked_media_state_wait.return_value = True
|
||||
result = vlc_player.play(mocked_controller, mocked_display)
|
||||
|
||||
# THEN: A bunch of things should happen to play the media
|
||||
mocked_thread.start.assert_called_with()
|
||||
mocked_volume.assert_called_with(mocked_controller, 100)
|
||||
|
||||
assert MediaState.Playing == vlc_player.get_live_state()
|
||||
assert result is True, 'The value returned from play() should be True'
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ exceptionform.MIGRATE_VERSION = 'Migrate Test'
|
|||
exceptionform.CHARDET_VERSION = 'CHARDET Test'
|
||||
exceptionform.ENCHANT_VERSION = 'Enchant Test'
|
||||
exceptionform.MAKO_VERSION = 'Mako Test'
|
||||
exceptionform.ICU_VERSION = 'ICU Test'
|
||||
exceptionform.VLC_VERSION = 'VLC Test'
|
||||
|
||||
MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n'
|
||||
|
|
|
@ -26,10 +26,11 @@ import datetime
|
|||
from unittest.mock import MagicMock, patch, sentinel
|
||||
|
||||
from PyQt5 import QtCore, QtGui
|
||||
from openlp.core.lib.serviceitem import ServiceItem
|
||||
|
||||
from openlp.core.state import State
|
||||
from openlp.core.common.registry import Registry
|
||||
from openlp.core.lib import ServiceItemAction
|
||||
from openlp.core.lib import ItemCapabilities, ServiceItemAction
|
||||
from openlp.core.ui import HideMode
|
||||
from openlp.core.ui.slidecontroller import NON_TEXT_MENU, WIDE_MENU, NARROW_MENU, InfoLabel, LiveController, \
|
||||
PreviewController, SlideController
|
||||
|
@ -1141,6 +1142,54 @@ def test_process_item_is_reloading_wont_change_display_hide_mode(mocked_execute,
|
|||
slide_controller.set_hide_mode.assert_not_called()
|
||||
|
||||
|
||||
@patch.object(Registry, 'execute')
|
||||
def test_process_item_provides_own_theme(mocked_execute, registry, state_media):
|
||||
"""
|
||||
Test that media theme is set when media item is flagged with ProvidesOwnTheme
|
||||
"""
|
||||
# GIVEN: A mocked presentation service item that provides it's own theme, a mocked Registry.execute
|
||||
# and a slide controller with many mocks.
|
||||
mocked_pres_item = MagicMock()
|
||||
mocked_pres_item.name = 'mocked_presentation_item'
|
||||
mocked_pres_item.is_command.return_value = True
|
||||
mocked_pres_item.is_media.return_value = False
|
||||
mocked_pres_item.requires_media.return_value = False
|
||||
mocked_pres_item.is_image.return_value = False
|
||||
mocked_pres_item.is_text.return_value = False
|
||||
# Needed to perform the capability checks
|
||||
mocked_pres_item.is_capable = lambda param: ServiceItem.is_capable(mocked_pres_item, param)
|
||||
mocked_pres_item.from_service = False
|
||||
mocked_pres_item.capabilities = [ItemCapabilities.ProvidesOwnTheme]
|
||||
mocked_pres_item.get_frames.return_value = []
|
||||
mocked_settings = MagicMock()
|
||||
mocked_settings.value.return_value = True
|
||||
mocked_main_window = MagicMock()
|
||||
Registry().register('settings', mocked_settings)
|
||||
Registry().register('main_window', mocked_main_window)
|
||||
Registry().register('media_controller', MagicMock())
|
||||
Registry().register('application', MagicMock())
|
||||
slide_controller = SlideController(None)
|
||||
slide_controller.service_item = mocked_pres_item
|
||||
slide_controller.is_live = False
|
||||
slide_controller.preview_widget = MagicMock()
|
||||
slide_controller.preview_display = MagicMock()
|
||||
slide_controller.enable_tool_bar = MagicMock()
|
||||
slide_controller.slide_selected = MagicMock()
|
||||
slide_controller.on_stop_loop = MagicMock()
|
||||
slide_controller.info_label = MagicMock()
|
||||
slide_controller._set_theme = MagicMock()
|
||||
slide_controller.displays = [MagicMock()]
|
||||
slide_controller.split = 0
|
||||
slide_controller.type_prefix = 'test'
|
||||
slide_controller._current_hide_mode = None
|
||||
|
||||
# WHEN: _process_item is called
|
||||
slide_controller._process_item(mocked_pres_item, 0)
|
||||
|
||||
# THEN: _set_theme should be called once
|
||||
slide_controller._set_theme.assert_called_once()
|
||||
|
||||
|
||||
def test_live_stolen_focus_shortcuts(settings):
|
||||
"""
|
||||
Test that all the needed shortcuts are available in scenarios where Live has stolen focus.
|
||||
|
|
|
@ -32,8 +32,7 @@ from openlp.plugins.alerts.alertsplugin import AlertsPlugin
|
|||
def plugin_env(mocked_manager, settings, state, registry):
|
||||
"""An instance of the AlertsPlugin"""
|
||||
mocked_manager.return_value = MagicMock()
|
||||
with patch('openlp.plugins.alerts.alertsplugin.register_views'):
|
||||
return AlertsPlugin(), settings
|
||||
return AlertsPlugin(), settings
|
||||
|
||||
|
||||
def test_plugin_about():
|
||||
|
@ -77,12 +76,10 @@ def test_alerts_initialise(plugin_env):
|
|||
plugin = plugin_env[0]
|
||||
plugin.tools_alert_item = MagicMock()
|
||||
# WHEN: I request the form
|
||||
with patch('openlp.core.common.actions.ActionList') as mocked_actionlist, \
|
||||
patch('openlp.plugins.alerts.alertsplugin.register_views') as mocked_register_views:
|
||||
with patch('openlp.core.common.actions.ActionList') as mocked_actionlist:
|
||||
plugin.initialise()
|
||||
# THEN: the form is loaded
|
||||
mocked_actionlist.instance.add_action.assert_called_once()
|
||||
mocked_register_views.assert_called_once_with()
|
||||
plugin.tools_alert_item.setVisible.assert_called_once_with(True)
|
||||
|
||||
|
||||
|
|
|
@ -21,7 +21,14 @@
|
|||
"""
|
||||
This module contains tests for the plugin class Presentation plugin.
|
||||
"""
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from openlp.plugins.presentations.presentationplugin import PresentationPlugin
|
||||
from openlp.plugins.presentations.lib.presentationtab import PresentationTab
|
||||
|
||||
|
||||
TEST_RESOURCES_PATH = Path(__file__) / '..' / '..' / '..' / 'resources'
|
||||
|
||||
|
||||
def test_plugin_about():
|
||||
|
@ -34,3 +41,33 @@ def test_plugin_about():
|
|||
'programs. The choice of available presentation programs is '
|
||||
'available to the user in a drop down box.'
|
||||
)
|
||||
|
||||
|
||||
def test_creaste_settings_tab(qapp, state, registry, settings):
|
||||
"""Test creating the settings tab"""
|
||||
# GIVEN: A Presentations plugin
|
||||
presentations_plugin = PresentationPlugin()
|
||||
|
||||
# WHEN: create_settings_tab is run
|
||||
presentations_plugin.create_settings_tab(None)
|
||||
|
||||
# THEN: A settings tab should have been created
|
||||
assert isinstance(presentations_plugin.settings_tab, PresentationTab)
|
||||
|
||||
|
||||
@patch('openlp.plugins.presentations.presentationplugin.Manager')
|
||||
def test_initialise(MockedManager, state, registry, mock_settings):
|
||||
"""Test that initialising the plugin works correctly"""
|
||||
# GIVEN: Some initial values needed for intialisation and a presentations plugin
|
||||
mock_settings.setValue.side_effect = [None, [str(TEST_RESOURCES_PATH / 'presentations' / 'test.ppt')]]
|
||||
mocked_main_window = MagicMock()
|
||||
registry.register('main_window', mocked_main_window)
|
||||
presentations_plugin = PresentationPlugin()
|
||||
presentations_plugin.media_item = MagicMock()
|
||||
|
||||
# WHEN: initialise() is called
|
||||
presentations_plugin.initialise()
|
||||
|
||||
# THEN: Nothing should break, and everything should be called
|
||||
mock_settings.setValue.assert_called_with('presentations/thumbnail_scheme', 'sha256file')
|
||||
mock_settings.remove.assert_called_once_with('presentations/presentations files')
|
||||
|
|
|
@ -95,7 +95,9 @@ def test_report_song_list_error_reading(mock_file_dialog, mock_log, registry):
|
|||
Test that report song list sends an exception if the selected file location is not writable
|
||||
"""
|
||||
# GIVEN: A mocked file that returns a os error on open
|
||||
def raise_os_error(x):
|
||||
def raise_os_error(mode, encoding):
|
||||
assert mode == 'wt'
|
||||
assert encoding == 'utf8'
|
||||
raise OSError
|
||||
mock_file = MagicMock()
|
||||
mock_file.open.side_effect = raise_os_error
|
||||
|
|
Loading…
Reference in New Issue