This commit is contained in:
Raoul Snyman 2019-05-22 09:47:20 -07:00
commit ca5e46eb49
53 changed files with 1940 additions and 10573 deletions

View File

@ -134,8 +134,8 @@ def extension_loader(glob_pattern, excluded_files=[]):
importlib.import_module(module_name) importlib.import_module(module_name)
except (ImportError, OSError): except (ImportError, OSError):
# On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X) # On some platforms importing vlc.py might cause OSError exceptions. (e.g. Mac OS X)
log.warning('Failed to import {module_name} on path {extension_path}' log.exception('Failed to import {module_name} on path {extension_path}'
.format(module_name=module_name, extension_path=extension_path)) .format(module_name=module_name, extension_path=extension_path))
def path_to_module(path): def path_to_module(path):
@ -463,8 +463,8 @@ def get_file_encoding(file_path):
Utility function to incrementally detect the file encoding. Utility function to incrementally detect the file encoding.
:param openlp.core.common.path.Path file_path: Filename for the file to determine the encoding for. :param openlp.core.common.path.Path file_path: Filename for the file to determine the encoding for.
:return: A dict with the keys 'encoding' and 'confidence' :return: The name of the encoding detected
:rtype: dict[str, float] :rtype: str
""" """
detector = UniversalDetector() detector = UniversalDetector()
try: try:
@ -477,7 +477,7 @@ def get_file_encoding(file_path):
except OSError: except OSError:
log.exception('Error detecting file encoding') log.exception('Error detecting file encoding')
finally: finally:
return detector.close() return detector.close()['encoding']
def normalize_str(irregular_string): def normalize_str(irregular_string):

View File

@ -180,15 +180,13 @@ class Settings(QtCore.QSettings):
'api/thumbnails': True, 'api/thumbnails': True,
'crashreport/last directory': None, 'crashreport/last directory': None,
'formattingTags/html_tags': '', 'formattingTags/html_tags': '',
'core/audio repeat list': False,
'core/auto open': False, 'core/auto open': False,
'core/auto preview': False, 'core/auto preview': False,
'core/audio start paused': True,
'core/auto unblank': False, 'core/auto unblank': False,
'core/click live slide to unblank': False, 'core/click live slide to unblank': False,
'core/blank warning': False, 'core/blank warning': False,
'core/ccli number': '', 'core/ccli number': '',
'core/experimental': False, 'advanced/experimental': False,
'core/has run wizard': False, 'core/has run wizard': False,
'core/language': '[en]', 'core/language': '[en]',
'core/last version test': '', 'core/last version test': '',
@ -213,6 +211,7 @@ class Settings(QtCore.QSettings):
'images/background color': '#000000', 'images/background color': '#000000',
'media/media auto start': QtCore.Qt.Unchecked, 'media/media auto start': QtCore.Qt.Unchecked,
'media/stream command': '', 'media/stream command': '',
'media/vlc arguments': '',
'remotes/download version': '0.0', 'remotes/download version': '0.0',
'players/background color': '#000000', 'players/background color': '#000000',
'servicemanager/last directory': None, 'servicemanager/last directory': None,
@ -317,7 +316,9 @@ class Settings(QtCore.QSettings):
('bibles/proxy username', '', []), ('bibles/proxy username', '', []),
('bibles/proxy password', '', []), ('bibles/proxy password', '', []),
('media/players', '', []), ('media/players', '', []),
('media/override player', '', []) ('media/override player', '', []),
('core/audio start paused', '', []),
('core/audio repeat list', '', [])
] ]
@staticmethod @staticmethod

View File

@ -180,7 +180,7 @@ class DisplayWindow(QtWidgets.QWidget):
""" """
Set the URL of the webview Set the URL of the webview
:param str url: The URL to set :param QtCore.QUrl | str url: The URL to set
""" """
if not isinstance(url, QtCore.QUrl): if not isinstance(url, QtCore.QUrl):
url = QtCore.QUrl(url) url = QtCore.QUrl(url)

View File

@ -29,7 +29,6 @@ from PyQt5 import QtCore, QtWidgets
from openlp.core.common.i18n import UiStrings, translate from openlp.core.common.i18n import UiStrings, translate
from openlp.core.common.mixins import RegistryProperties from openlp.core.common.mixins import RegistryProperties
from openlp.core.common.path import path_to_str, str_to_path
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import ServiceItemContext from openlp.core.lib import ServiceItemContext
@ -333,7 +332,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
self.validate_and_load(file_paths) self.validate_and_load(file_paths)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def load_file(self, data): def handle_mime_data(self, data):
""" """
Turn file from Drag and Drop into an array so the Validate code can run it. Turn file from Drag and Drop into an array so the Validate code can run it.
@ -379,11 +378,11 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
duplicates_found = False duplicates_found = False
files_added = False files_added = False
for file_path in file_paths: for file_path in file_paths:
if path_to_str(file_path) in full_list: if file_path in full_list:
duplicates_found = True duplicates_found = True
else: else:
files_added = True files_added = True
full_list.append(path_to_str(file_path)) full_list.append(file_path)
if full_list and files_added: if full_list and files_added:
if target_group is None: if target_group is None:
self.list_view.clear() self.list_view.clear()
@ -416,8 +415,8 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
file_paths = [] file_paths = []
for index in range(self.list_view.count()): for index in range(self.list_view.count()):
list_item = self.list_view.item(index) list_item = self.list_view.item(index)
filename = list_item.data(QtCore.Qt.UserRole) file_path = list_item.data(QtCore.Qt.UserRole)
file_paths.append(str_to_path(filename)) file_paths.append(file_path)
return file_paths return file_paths
def load_list(self, load_list, target_group): def load_list(self, load_list, target_group):

View File

@ -333,7 +333,7 @@ class Theme(object):
else: else:
# make string value unicode # make string value unicode
if not isinstance(value, str): if not isinstance(value, str):
value = str(str(value), 'utf-8') value = str(value, 'utf-8')
# None means an empty string so lets have one. # None means an empty string so lets have one.
if value == 'None': if value == 'None':
value = '' value = ''

View File

@ -39,6 +39,7 @@ PJLINK_PREFIX = '%'
PJLINK_PORT = 4352 PJLINK_PORT = 4352
PJLINK_SUFFIX = CR PJLINK_SUFFIX = CR
PJLINK_TIMEOUT = 30.0 PJLINK_TIMEOUT = 30.0
PJLINK_TOKEN_SIZE = 8 # PJLINK 1 <token> : where <token> is 8 characters
# Error and status codes # Error and status codes
S_OK = E_OK = 0 # E_OK included since I sometimes forget S_OK = E_OK = 0 # E_OK included since I sometimes forget

View File

@ -281,6 +281,7 @@ class PJLink(QtNetwork.QTcpSocket):
self.pjlink_class = copy(PJLINK_CLASS) self.pjlink_class = copy(PJLINK_CLASS)
self.pjlink_name = None # NAME self.pjlink_name = None # NAME
self.power = S_OFF # POWR self.power = S_OFF # POWR
self.projector_errors = {} # Full ERST errors
self.serial_no = None # SNUM self.serial_no = None # SNUM
self.serial_no_received = None self.serial_no_received = None
self.sw_version = None # SVER self.sw_version = None # SVER
@ -520,14 +521,23 @@ class PJLink(QtNetwork.QTcpSocket):
self.send_busy = False self.send_busy = False
return return
# Although we have a packet length limit, go ahead and use a larger buffer # Although we have a packet length limit, go ahead and use a larger buffer
read = self.readLine(1024) self.socket_timer.start()
log.debug('({ip}) get_socket(): "{buff}"'.format(ip=self.entry.name, buff=read)) while self.bytesAvailable() >= 1:
if read == -1: data = self.readLine(1024)
data = data.strip()
if not data:
log.warning('({ip}) get_socket(): Ignoring empty packet'.format(ip=self.entry.name))
if self.bytesAvailable() < 1:
break
self.socket_timer.stop()
if data:
log.debug('({ip}) get_socket(): "{buff}"'.format(ip=self.entry.name, buff=data))
if data == -1:
# No data available # No data available
log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.entry.name)) log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.entry.name))
return self.receive_data_signal() return
self.socket_timer.stop() return self.get_data(buff=data)
return self.get_data(buff=read)
def get_data(self, buff, *args, **kwargs): def get_data(self, buff, *args, **kwargs):
""" """
@ -540,21 +550,22 @@ class PJLink(QtNetwork.QTcpSocket):
# NOTE: Class2 has changed to some values being UTF-8 # NOTE: Class2 has changed to some values being UTF-8
data_in = decode(buff, 'utf-8') if isinstance(buff, bytes) else buff data_in = decode(buff, 'utf-8') if isinstance(buff, bytes) else buff
data = data_in.strip() data = data_in.strip()
self.receive_data_signal()
# Initial packet checks # Initial packet checks
if (len(data) < 7): if (len(data) < 7):
self._trash_buffer(msg='get_data(): Invalid packet - length') self._trash_buffer(msg='get_data(): Invalid packet - length')
return self.receive_data_signal() return
elif len(data) > self.max_size: elif len(data) > self.max_size:
self._trash_buffer(msg='get_data(): Invalid packet - too long ({length} bytes)'.format(length=len(data))) self._trash_buffer(msg='get_data(): Invalid packet - too long ({length} bytes)'.format(length=len(data)))
return self.receive_data_signal() return
elif not data.startswith(PJLINK_PREFIX): elif not data.startswith(PJLINK_PREFIX):
self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing') self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
return self.receive_data_signal() return
elif data[6] != '=' and data[8] != '=': elif data[6] != '=' and data[8] != '=':
# data[6] = standard command packet # data[6] = standard command packet
# data[8] = initial PJLink connection (after mangling) # data[8] = initial PJLink connection (after mangling)
self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="') self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
return self.receive_data_signal() return
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data)) log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data))
header, data = data.split('=') header, data = data.split('=')
log.debug('({ip}) get_data() header="{header}" data="{data}"'.format(ip=self.entry.name, log.debug('({ip}) get_data() header="{header}" data="{data}"'.format(ip=self.entry.name,
@ -572,20 +583,20 @@ class PJLink(QtNetwork.QTcpSocket):
data=data)) data=data))
if cmd not in PJLINK_VALID_CMD: if cmd not in PJLINK_VALID_CMD:
self._trash_buffer('get_data(): Invalid packet - unknown command "{data}"'.format(data=cmd)) self._trash_buffer('get_data(): Invalid packet - unknown command "{data}"'.format(data=cmd))
return self.receive_data_signal() return
elif version not in PJLINK_VALID_CMD[cmd]['version']: elif version not in PJLINK_VALID_CMD[cmd]['version']:
self._trash_buffer(msg='get_data() Command reply version does not match a valid command version') self._trash_buffer(msg='get_data() Command reply version does not match a valid command version')
return self.receive_data_signal() return
elif int(self.pjlink_class) < int(version): elif int(self.pjlink_class) < int(version):
if not ignore_class: if not ignore_class:
log.warning('({ip}) get_data(): Projector returned class reply higher ' log.warning('({ip}) get_data(): Projector returned class reply higher '
'than projector stated class'.format(ip=self.entry.name)) 'than projector stated class'.format(ip=self.entry.name))
return self.receive_data_signal() return
chk = process_command(self, cmd, data) chk = process_command(self, cmd, data)
if chk is None: if chk is None:
# Command processed normally and not initial connection, so skip other checks # Command processed normally and not initial connection, so skip other checks
return self.receive_data_signal() return
# PJLink initial connection checks # PJLink initial connection checks
elif chk == S_DATA_OK: elif chk == S_DATA_OK:
# Previous command returned OK # Previous command returned OK
@ -612,7 +623,7 @@ class PJLink(QtNetwork.QTcpSocket):
self.projectorAuthentication.emit(self.entry.name) self.projectorAuthentication.emit(self.entry.name)
self.change_status(status=E_AUTHENTICATION) self.change_status(status=E_AUTHENTICATION)
return self.receive_data_signal() return
@QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError)
def get_error(self, err): def get_error(self, err):
@ -728,17 +739,17 @@ class PJLink(QtNetwork.QTcpSocket):
log.debug('({ip}) _send_command(): Normal queue = {data}'.format(ip=self.entry.name, data=self.send_queue)) log.debug('({ip}) _send_command(): Normal queue = {data}'.format(ip=self.entry.name, data=self.send_queue))
return return
if len(self.priority_queue) != 0: if not self.priority_queue and not self.send_queue:
out = self.priority_queue.pop(0)
log.debug('({ip}) _send_command(): Getting priority queued packet'.format(ip=self.entry.name))
elif len(self.send_queue) != 0:
out = self.send_queue.pop(0)
log.debug('({ip}) _send_command(): Getting normal queued packet'.format(ip=self.entry.name))
else:
# No data to send # No data to send
log.warning('({ip}) _send_command(): No data to send'.format(ip=self.entry.name)) log.warning('({ip}) _send_command(): No data to send'.format(ip=self.entry.name))
self.send_busy = False self.send_busy = False
return return
elif self.priority_queue:
out = self.priority_queue.pop(0)
log.debug('({ip}) _send_command(): Getting priority queued packet'.format(ip=self.entry.name))
elif self.send_queue:
out = self.send_queue.pop(0)
log.debug('({ip}) _send_command(): Getting normal queued packet'.format(ip=self.entry.name))
self.send_busy = True self.send_busy = True
log.debug('({ip}) _send_command(): Sending "{data}"'.format(ip=self.entry.name, data=out.strip())) log.debug('({ip}) _send_command(): Sending "{data}"'.format(ip=self.entry.name, data=out.strip()))
self.socket_timer.start() self.socket_timer.start()
@ -861,6 +872,24 @@ class PJLink(QtNetwork.QTcpSocket):
log.debug('({ip}) Sending POWR command'.format(ip=self.entry.name)) log.debug('({ip}) Sending POWR command'.format(ip=self.entry.name))
return self.send_command(cmd='POWR', priority=priority) return self.send_command(cmd='POWR', priority=priority)
def set_audio_mute(self, priority=False):
"""
Send command to set audio to muted
"""
log.debug('({ip}) Setting AVMT to 21 (audio mute)'.format(ip=self.entry.name))
self.send_command(cmd='AVMT', opts='21', priority=True)
self.status_timer_add(cmd='AVMT', callback=self.get_av_mute_status)
self.poll_loop()
def set_audio_normal(self, priority=False):
"""
Send command to set audio to normal
"""
log.debug('({ip}) Setting AVMT to 20 (audio normal)'.format(ip=self.entry.name))
self.send_command(cmd='AVMT', opts='20', priority=True)
self.status_timer_add(cmd='AVMT', callback=self.get_av_mute_status)
self.poll_loop()
def set_input_source(self, src=None): def set_input_source(self, src=None):
""" """
Verify input source available as listed in 'INST' command, Verify input source available as listed in 'INST' command,
@ -924,9 +953,9 @@ class PJLink(QtNetwork.QTcpSocket):
log.warning('({ip}) "{cmd}" already in checks - returning'.format(ip=self.entry.name, cmd=cmd)) log.warning('({ip}) "{cmd}" already in checks - returning'.format(ip=self.entry.name, cmd=cmd))
return return
log.debug('({ip}) Adding "{cmd}" callback for status timer'.format(ip=self.entry.name, cmd=cmd)) log.debug('({ip}) Adding "{cmd}" callback for status timer'.format(ip=self.entry.name, cmd=cmd))
self.status_timer_checks[cmd] = callback
if not self.status_timer.isActive(): if not self.status_timer.isActive():
self.status_timer.start() self.status_timer.start()
self.status_timer_checks[cmd] = callback
def status_timer_delete(self, cmd): def status_timer_delete(self, cmd):
""" """

View File

@ -29,13 +29,13 @@ NOTE: PJLink Class (version) checks are handled in the respective PJLink/PJLinkU
import logging import logging
import re import re
import string
from openlp.core.common.i18n import translate
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.projectors.constants import E_AUTHENTICATION, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \ from openlp.core.projectors.constants import E_AUTHENTICATION, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \
PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_POWR_STATUS, S_AUTHENTICATE, S_CONNECT, S_DATA_OK, S_OFF, S_OK, S_ON, \ PJLINK_ERST_DATA, PJLINK_ERST_LIST, PJLINK_ERST_STATUS, PJLINK_POWR_STATUS, PJLINK_TOKEN_SIZE, \
S_STANDBY, STATUS_MSG E_NO_AUTHENTICATION, S_AUTHENTICATE, S_CONNECT, S_DATA_OK, S_OFF, S_OK, S_ON, S_STANDBY, STATUS_MSG
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.debug('Loading pjlinkcommands') log.debug('Loading pjlinkcommands')
@ -113,15 +113,21 @@ def process_avmt(projector, data):
'31': {'shutter': True, 'mute': True} '31': {'shutter': True, 'mute': True}
} }
if data not in settings: if data not in settings:
log.warning('({ip}) Invalid shutter response: {data}'.format(ip=projector.entry.name, data=data)) log.warning('({ip}) Invalid av mute response: {data}'.format(ip=projector.entry.name, data=data))
return return
shutter = settings[data]['shutter'] shutter = settings[data]['shutter']
mute = settings[data]['mute'] mute = settings[data]['mute']
# Check if we need to update the icons # Check if we need to update the icons
update_icons = (shutter != projector.shutter) or (mute != projector.mute) update_icons = (shutter != projector.shutter) or (mute != projector.mute)
projector.shutter = shutter
projector.mute = mute
if update_icons: if update_icons:
if projector.shutter != shutter:
projector.shutter = shutter
log.debug('({ip}) Setting shutter to {chk}'.format(ip=projector.entry.name,
chk='closed' if shutter else 'open'))
if projector.mute != mute:
projector.mute = mute
log.debug('({ip}) Setting speaker to {chk}'.format(ip=projector.entry.name,
chk='muted' if shutter else 'normal'))
if 'AVMT' in projector.status_timer_checks: if 'AVMT' in projector.status_timer_checks:
projector.status_timer_delete('AVMT') projector.status_timer_delete('AVMT')
projector.projectorUpdateIcons.emit() projector.projectorUpdateIcons.emit()
@ -147,14 +153,14 @@ def process_clss(projector, data):
# fix the class reply is to just remove all non-digit characters. # fix the class reply is to just remove all non-digit characters.
chk = re.findall(r'\d', data) chk = re.findall(r'\d', data)
if len(chk) < 1: if len(chk) < 1:
log.error('({ip}) No numbers found in class version reply "{data}" - ' log.warning('({ip}) No numbers found in class version reply "{data}" - '
'defaulting to class "1"'.format(ip=projector.entry.name, data=data)) 'defaulting to class "1"'.format(ip=projector.entry.name, data=data))
clss = '1' clss = '1'
else: else:
clss = chk[0] # Should only be the first match clss = chk[0] # Should only be the first match
elif not data.isdigit(): elif not data.isdigit():
log.error('({ip}) NAN CLSS version reply "{data}" - ' log.warning('({ip}) NAN CLSS version reply "{data}" - '
'defaulting to class "1"'.format(ip=projector.entry.name, data=data)) 'defaulting to class "1"'.format(ip=projector.entry.name, data=data))
clss = '1' clss = '1'
else: else:
clss = data clss = data
@ -189,8 +195,7 @@ def process_erst(projector, data):
# Bad data - ignore # Bad data - ignore
log.warning('({ip}) Invalid error status response "{data}"'.format(ip=projector.entry.name, data=data)) log.warning('({ip}) Invalid error status response "{data}"'.format(ip=projector.entry.name, data=data))
return return
datacheck = int(data) if int(data) == 0:
if datacheck == 0:
projector.projector_errors = None projector.projector_errors = None
# No errors # No errors
return return
@ -203,23 +208,17 @@ def process_erst(projector, data):
data[PJLINK_ERST_DATA['FILTER']], data[PJLINK_ERST_DATA['FILTER']],
data[PJLINK_ERST_DATA['OTHER']]) data[PJLINK_ERST_DATA['OTHER']])
if fan != PJLINK_ERST_STATUS[S_OK]: if fan != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \ projector.projector_errors[PJLINK_ERST_LIST['FAN']] = PJLINK_ERST_STATUS[fan]
PJLINK_ERST_STATUS[fan]
if lamp != PJLINK_ERST_STATUS[S_OK]: if lamp != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \ projector.projector_errors[PJLINK_ERST_LIST['LAMP']] = PJLINK_ERST_STATUS[lamp]
PJLINK_ERST_STATUS[lamp]
if temp != PJLINK_ERST_STATUS[S_OK]: if temp != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \ projector.projector_errors[PJLINK_ERST_LIST['TEMP']] = PJLINK_ERST_STATUS[temp]
PJLINK_ERST_STATUS[temp]
if cover != PJLINK_ERST_STATUS[S_OK]: if cover != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \ projector.projector_errors[PJLINK_ERST_LIST['COVER']] = PJLINK_ERST_STATUS[cover]
PJLINK_ERST_STATUS[cover]
if filt != PJLINK_ERST_STATUS[S_OK]: if filt != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \ projector.projector_errors[PJLINK_ERST_LIST['FILTER']] = PJLINK_ERST_STATUS[filt]
PJLINK_ERST_STATUS[filt]
if other != PJLINK_ERST_STATUS[S_OK]: if other != PJLINK_ERST_STATUS[S_OK]:
projector.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \ projector.projector_errors[PJLINK_ERST_LIST['OTHER']] = PJLINK_ERST_STATUS[other]
PJLINK_ERST_STATUS[other]
return return
@ -276,12 +275,13 @@ def process_inpt(projector, data):
if projector.source_available is not None: if projector.source_available is not None:
# We have available inputs, so verify it's in the list # We have available inputs, so verify it's in the list
if data not in projector.source_available: if data not in projector.source_available:
log.warn('({ip}) Input source not listed in available sources - ignoring'.format(ip=projector.entry.name)) log.warning('({ip}) Input source not listed in available sources - '
'ignoring'.format(ip=projector.entry.name))
return return
elif data not in PJLINK_DEFAULT_CODES: elif data not in PJLINK_DEFAULT_CODES:
# Hmm - no sources available yet, so check with PJLink defaults # Hmm - no sources available yet, so check with PJLink defaults
log.warn('({ip}) Input source not listed as a PJLink available source ' log.warning('({ip}) Input source not listed as a PJLink valid source '
'- ignoring'.format(ip=projector.entry.name)) '- ignoring'.format(ip=projector.entry.name))
return return
projector.source = data projector.source = data
log.debug('({ip}) Setting current source to "{data}"'.format(ip=projector.entry.name, data=projector.source)) log.debug('({ip}) Setting current source to "{data}"'.format(ip=projector.entry.name, data=projector.source))
@ -320,7 +320,10 @@ def process_lamp(projector, data):
lamps = [] lamps = []
lamp_list = data.split() lamp_list = data.split()
if len(lamp_list) < 2: if len(lamp_list) < 2:
lamps.append({'Hours': int(lamp_list[0]), 'On': None}) # Invalid data - not enough information
log.warning('({ip}) process_lamp(): Invalid data "{data}" - '
'Missing data'.format(ip=projector.entry.name, data=data))
return
else: else:
while lamp_list: while lamp_list:
if not lamp_list[0].isnumeric() or not lamp_list[1].isnumeric(): if not lamp_list[0].isnumeric() or not lamp_list[1].isnumeric():
@ -379,20 +382,29 @@ def process_pjlink(projector, data):
if len(chk) > 1: if len(chk) > 1:
# Invalid data - there should be nothing after a normal authentication scheme # Invalid data - there should be nothing after a normal authentication scheme
log.error('({ip}) Normal connection with extra information - aborting'.format(ip=projector.entry.name)) log.error('({ip}) Normal connection with extra information - aborting'.format(ip=projector.entry.name))
return E_AUTHENTICATION return E_NO_AUTHENTICATION
elif projector.pin: elif projector.pin:
log.error('({ip}) Normal connection but PIN set - aborting'.format(ip=projector.entry.name)) log.error('({ip}) Normal connection but PIN set - aborting'.format(ip=projector.entry.name))
return E_AUTHENTICATION return E_NO_AUTHENTICATION
log.debug('({ip}) PJLINK: Returning S_CONNECT'.format(ip=projector.entry.name)) log.debug('({ip}) PJLINK: Returning S_CONNECT'.format(ip=projector.entry.name))
return S_CONNECT return S_CONNECT
elif chk[0] == '1': elif chk[0] == '1':
if len(chk) < 2: if len(chk) < 2:
# Not enough information for authenticated connection # Not enough information for authenticated connection
log.error('({ip}) Authenticated connection but not enough info - aborting'.format(ip=projector.entry.name)) log.error('({ip}) Authenticated connection but not enough info - aborting'.format(ip=projector.entry.name))
return E_AUTHENTICATION return E_NO_AUTHENTICATION
elif len(chk[-1]) != PJLINK_TOKEN_SIZE:
# Bad token - incorrect size
log.error('({ip}) Authentication token invalid (size) - aborting'.format(ip=projector.entry.name))
return E_NO_AUTHENTICATION
elif not all(c in string.hexdigits for c in chk[-1]):
# Bad token - not hexadecimal
log.error('({ip}) Authentication token invalid (not a hexadecimal number) '
'- aborting'.format(ip=projector.entry.name))
return E_NO_AUTHENTICATION
elif not projector.pin: elif not projector.pin:
log.error('({ip}) Authenticate connection but no PIN - aborting'.format(ip=projector.entry.name)) log.error('({ip}) Authenticate connection but no PIN - aborting'.format(ip=projector.entry.name))
return E_AUTHENTICATION return E_NO_AUTHENTICATION
log.debug('({ip}) PJLINK: Returning S_AUTHENTICATE'.format(ip=projector.entry.name)) log.debug('({ip}) PJLINK: Returning S_AUTHENTICATE'.format(ip=projector.entry.name))
return S_AUTHENTICATE return S_AUTHENTICATE
@ -405,20 +417,22 @@ def process_powr(projector, data):
:param projector: Projector instance :param projector: Projector instance
:param data: Power status :param data: Power status
""" """
log.debug('({ip}: Processing POWR command'.format(ip=projector.entry.name)) log.debug('({ip}) Processing POWR command'.format(ip=projector.entry.name))
if data in PJLINK_POWR_STATUS: if data not in PJLINK_POWR_STATUS:
power = PJLINK_POWR_STATUS[data]
update_icons = projector.power != power
projector.power = power
projector.change_status(PJLINK_POWR_STATUS[data])
if update_icons:
projector.projectorUpdateIcons.emit()
# Update the input sources available
if power == S_ON:
projector.send_command('INST')
else:
# Log unknown status response # Log unknown status response
log.warning('({ip}) Unknown power response: "{data}"'.format(ip=projector.entry.name, data=data)) log.warning('({ip}) Unknown power response: "{data}"'.format(ip=projector.entry.name, data=data))
return
power = PJLINK_POWR_STATUS[data]
update_icons = projector.power != power
if update_icons:
projector.power = power
projector.change_status(PJLINK_POWR_STATUS[data])
projector.projectorUpdateIcons.emit()
if power == S_ON:
# Input sources list should only be available after power on, so update here
projector.send_command('INST')
if projector.power in [S_ON, S_STANDBY, S_OFF] and 'POWR' in projector.status_timer_checks: if projector.power in [S_ON, S_STANDBY, S_OFF] and 'POWR' in projector.status_timer_checks:
projector.status_timer_delete(cmd='POWR') projector.status_timer_delete(cmd='POWR')
return return

View File

@ -22,6 +22,7 @@
from PyQt5 import QtCore, QtNetwork from PyQt5 import QtCore, QtNetwork
from openlp.core.common.mixins import LogMixin from openlp.core.common.mixins import LogMixin
from openlp.core.common.path import Path
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
@ -97,7 +98,7 @@ class Server(QtCore.QObject, LogMixin):
msg = self.in_stream.readLine() msg = self.in_stream.readLine()
if msg: if msg:
self.log_debug("socket msg = " + msg) self.log_debug("socket msg = " + msg)
Registry().get('service_manager').on_load_service_clicked(msg) Registry().get('service_manager').load_service(Path(msg))
def close_server(self): def close_server(self):
""" """

View File

@ -93,4 +93,4 @@ class SingleColumnTableWidget(QtWidgets.QTableWidget):
self.resizeRowsToContents() self.resizeRowsToContents()
__all__ = ['SingleColumnTableWidget'] __all__ = ['SingleColumnTableWidget', 'DisplayControllerType']

View File

@ -117,6 +117,9 @@ class AdvancedTab(SettingsTab):
self.enable_auto_close_check_box = QtWidgets.QCheckBox(self.ui_group_box) self.enable_auto_close_check_box = QtWidgets.QCheckBox(self.ui_group_box)
self.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box') self.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box')
self.ui_layout.addRow(self.enable_auto_close_check_box) self.ui_layout.addRow(self.enable_auto_close_check_box)
self.experimental_check_box = QtWidgets.QCheckBox(self.ui_group_box)
self.experimental_check_box.setObjectName('experimental_check_box')
self.ui_layout.addRow(self.experimental_check_box)
self.left_layout.addWidget(self.ui_group_box) self.left_layout.addWidget(self.ui_group_box)
if HAS_DARK_STYLE: if HAS_DARK_STYLE:
self.use_dark_style_checkbox = QtWidgets.QCheckBox(self.ui_group_box) self.use_dark_style_checkbox = QtWidgets.QCheckBox(self.ui_group_box)
@ -291,6 +294,8 @@ class AdvancedTab(SettingsTab):
'Auto-scroll the next slide to bottom')) 'Auto-scroll the next slide to bottom'))
self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab', self.enable_auto_close_check_box.setText(translate('OpenLP.AdvancedTab',
'Enable application exit confirmation')) 'Enable application exit confirmation'))
self.experimental_check_box.setText(translate('OpenLP.GeneralTab',
'Experimental features (use at your own risk)'))
if HAS_DARK_STYLE: if HAS_DARK_STYLE:
self.use_dark_style_checkbox.setText(translate('OpenLP.AdvancedTab', 'Use dark style (needs restart)')) self.use_dark_style_checkbox.setText(translate('OpenLP.AdvancedTab', 'Use dark style (needs restart)'))
self.service_name_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Default Service Name')) self.service_name_group_box.setTitle(translate('OpenLP.AdvancedTab', 'Default Service Name'))
@ -360,6 +365,7 @@ class AdvancedTab(SettingsTab):
if self.autoscroll_map[i] == autoscroll_value and i < self.autoscroll_combo_box.count(): if self.autoscroll_map[i] == autoscroll_value and i < self.autoscroll_combo_box.count():
self.autoscroll_combo_box.setCurrentIndex(i) self.autoscroll_combo_box.setCurrentIndex(i)
self.enable_auto_close_check_box.setChecked(settings.value('enable exit confirmation')) self.enable_auto_close_check_box.setChecked(settings.value('enable exit confirmation'))
self.experimental_check_box.setChecked(settings.value('experimental'))
if HAS_DARK_STYLE: if HAS_DARK_STYLE:
self.use_dark_style_checkbox.setChecked(settings.value('use_dark_style')) self.use_dark_style_checkbox.setChecked(settings.value('use_dark_style'))
self.hide_mouse_check_box.setChecked(settings.value('hide mouse')) self.hide_mouse_check_box.setChecked(settings.value('hide mouse'))
@ -423,6 +429,7 @@ class AdvancedTab(SettingsTab):
slide_max_height_value = self.slide_max_height_combo_box.itemData(slide_max_height_index) slide_max_height_value = self.slide_max_height_combo_box.itemData(slide_max_height_index)
settings.setValue('slide max height', slide_max_height_value) settings.setValue('slide max height', slide_max_height_value)
settings.setValue('autoscrolling', self.autoscroll_map[self.autoscroll_combo_box.currentIndex()]) settings.setValue('autoscrolling', self.autoscroll_map[self.autoscroll_combo_box.currentIndex()])
settings.setValue('experimental', self.experimental_check_box.isChecked())
settings.setValue('enable exit confirmation', self.enable_auto_close_check_box.isChecked()) settings.setValue('enable exit confirmation', self.enable_auto_close_check_box.isChecked())
settings.setValue('hide mouse', self.hide_mouse_check_box.isChecked()) settings.setValue('hide mouse', self.hide_mouse_check_box.isChecked())
settings.setValue('alternate rows', self.alternate_rows_check_box.isChecked()) settings.setValue('alternate rows', self.alternate_rows_check_box.isChecked())

View File

@ -78,7 +78,7 @@ class ThemeListWidget(QtWidgets.QListWidget):
""" """
nominal_width = 141 # Icon width of 133 + 4 each side nominal_width = 141 # Icon width of 133 + 4 each side
max_items_per_row = self.viewport().width() // nominal_width or 1 # or 1 to avoid divide by 0 errors max_items_per_row = self.viewport().width() // nominal_width or 1 # or 1 to avoid divide by 0 errors
col_size = (self.viewport().width() - 1) / max_items_per_row col_size = (self.viewport().width() - 1) // max_items_per_row
self.setGridSize(QtCore.QSize(col_size, 140)) self.setGridSize(QtCore.QSize(col_size, 140))

View File

@ -83,18 +83,6 @@ class GeneralTab(SettingsTab):
self.password_edit.setObjectName('password_edit') self.password_edit.setObjectName('password_edit')
self.ccli_layout.addRow(self.password_label, self.password_edit) self.ccli_layout.addRow(self.password_label, self.password_edit)
self.left_layout.addWidget(self.ccli_group_box) self.left_layout.addWidget(self.ccli_group_box)
# Background audio
self.audio_group_box = QtWidgets.QGroupBox(self.left_column)
self.audio_group_box.setObjectName('audio_group_box')
self.audio_layout = QtWidgets.QVBoxLayout(self.audio_group_box)
self.audio_layout.setObjectName('audio_layout')
self.start_paused_check_box = QtWidgets.QCheckBox(self.audio_group_box)
self.start_paused_check_box.setObjectName('start_paused_check_box')
self.audio_layout.addWidget(self.start_paused_check_box)
self.repeat_list_check_box = QtWidgets.QCheckBox(self.audio_group_box)
self.repeat_list_check_box.setObjectName('repeat_list_check_box')
self.audio_layout.addWidget(self.repeat_list_check_box)
self.left_layout.addWidget(self.audio_group_box)
self.left_layout.addStretch() self.left_layout.addStretch()
# Application Startup # Application Startup
self.startup_group_box = QtWidgets.QGroupBox(self.right_column) self.startup_group_box = QtWidgets.QGroupBox(self.right_column)
@ -195,9 +183,7 @@ class GeneralTab(SettingsTab):
self.number_label.setText(UiStrings().CCLINumberLabel) self.number_label.setText(UiStrings().CCLINumberLabel)
self.username_label.setText(translate('OpenLP.GeneralTab', 'SongSelect username:')) self.username_label.setText(translate('OpenLP.GeneralTab', 'SongSelect username:'))
self.password_label.setText(translate('OpenLP.GeneralTab', 'SongSelect password:')) self.password_label.setText(translate('OpenLP.GeneralTab', 'SongSelect password:'))
self.audio_group_box.setTitle(translate('OpenLP.GeneralTab', 'Background Audio')) self.logo_file_path_edit.dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File')
self.start_paused_check_box.setText(translate('OpenLP.GeneralTab', 'Start background audio paused'))
self.repeat_list_check_box.setText(translate('OpenLP.GeneralTab', 'Repeat track list'))
self.logo_file_path_edit.dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File') self.logo_file_path_edit.dialog_caption = translate('OpenLP.AdvancedTab', 'Select Logo File')
self.logo_file_path_edit.filters = '{text};;{names} (*)'.format( self.logo_file_path_edit.filters = '{text};;{names} (*)'.format(
text=get_images_filter(), names=UiStrings().AllFiles) text=get_images_filter(), names=UiStrings().AllFiles)
@ -224,8 +210,6 @@ class GeneralTab(SettingsTab):
self.check_for_updates_check_box.setChecked(settings.value('update check')) self.check_for_updates_check_box.setChecked(settings.value('update check'))
self.auto_preview_check_box.setChecked(settings.value('auto preview')) self.auto_preview_check_box.setChecked(settings.value('auto preview'))
self.timeout_spin_box.setValue(settings.value('loop delay')) self.timeout_spin_box.setValue(settings.value('loop delay'))
self.start_paused_check_box.setChecked(settings.value('audio start paused'))
self.repeat_list_check_box.setChecked(settings.value('audio repeat list'))
settings.endGroup() settings.endGroup()
def save(self): def save(self):
@ -249,8 +233,6 @@ class GeneralTab(SettingsTab):
settings.setValue('ccli number', self.number_edit.displayText()) settings.setValue('ccli number', self.number_edit.displayText())
settings.setValue('songselect username', self.username_edit.displayText()) settings.setValue('songselect username', self.username_edit.displayText())
settings.setValue('songselect password', self.password_edit.displayText()) settings.setValue('songselect password', self.password_edit.displayText())
settings.setValue('audio start paused', self.start_paused_check_box.isChecked())
settings.setValue('audio repeat list', self.repeat_list_check_box.isChecked())
settings.endGroup() settings.endGroup()
self.post_set_up() self.post_set_up()

View File

@ -1338,7 +1338,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.application.set_normal_cursor() self.application.set_normal_cursor()
self.log_exception('Data copy failed {err}'.format(err=str(why))) self.log_exception('Data copy failed {err}'.format(err=str(why)))
err_text = translate('OpenLP.MainWindow', err_text = translate('OpenLP.MainWindow',
'OpenLP Data directory copy failed\n\n{err}').format(err=str(why)), 'OpenLP Data directory copy failed\n\n{err}').format(err=str(why))
QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'), QtWidgets.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'New Data Directory Error'),
err_text, err_text,
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok)) QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Ok))
@ -1356,6 +1356,8 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
def open_cmd_line_files(self, args): def open_cmd_line_files(self, args):
""" """
Open files passed in through command line arguments Open files passed in through command line arguments
:param list[str] args: List of remaining positionall arguments
""" """
for arg in args: for arg in args:
file_name = os.path.expanduser(arg) file_name = os.path.expanduser(arg)

View File

@ -20,8 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # # along with this program. If not, see <https://www.gnu.org/licenses/>. #
########################################################################## ##########################################################################
""" """
The :mod:`~openlp.core.ui.media.mediacontroller` module contains a base class for media components and other widgets The :mod:`~openlp.core.ui.media.mediacontroller` module is the control module for all media playing.
related to playing media, such as sliders.
""" """
import logging import logging
@ -124,14 +123,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
self.setup() self.setup()
self.vlc_player = VlcPlayer(self) self.vlc_player = VlcPlayer(self)
State().add_service("mediacontroller", 0) State().add_service('mediacontroller', 0)
State().add_service("media_live", 0, requires="mediacontroller") State().add_service('media_live', 0)
if get_vlc() and pymediainfo_available: if get_vlc() and pymediainfo_available:
State().update_pre_conditions("mediacontroller", True) State().update_pre_conditions('mediacontroller', True)
State().update_pre_conditions('media_live', True) State().update_pre_conditions('media_live', True)
else: else:
State().missing_text("mediacontroller", translate('OpenLP.SlideController', State().missing_text('media_live', translate('OpenLP.SlideController',
"VLC or pymediainfo are missing, so you are unable to play any media")) 'VLC or pymediainfo are missing, so you are unable to play any media'))
self._generate_extensions_lists() self._generate_extensions_lists()
return True return True
@ -140,11 +139,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
Set up the controllers. Set up the controllers.
:return: :return:
""" """
try: if State().check_preconditions('mediacontroller'):
self.setup_display(self.live_controller.display, False) try:
except AttributeError: self.setup_display(self.live_controller.display, False)
State().update_pre_conditions('media_live', False) except AttributeError:
self.setup_display(self.preview_controller.preview_display, True) State().update_pre_conditions('media_live', False)
State().missing_text('media_live', translate('OpenLP.SlideController',
'No Displays configure so Live Media has been disabled'))
self.setup_display(self.preview_controller.preview_display, True)
def display_controllers(self, controller_type): def display_controllers(self, controller_type):
""" """
@ -215,9 +217,6 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
""" """
# Generic controls # Generic controls
controller.mediabar.setVisible(value) controller.mediabar.setVisible(value)
# if controller.is_live and controller.display:
# if self.current_media_players and value:
# controller.display.set_transparency(False)
@staticmethod @staticmethod
def resize(display, player): def resize(display, player):
@ -563,8 +562,7 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
total_seconds = controller.media_info.length // 1000 total_seconds = controller.media_info.length // 1000
total_minutes = total_seconds // 60 total_minutes = total_seconds // 60
total_seconds %= 60 total_seconds %= 60
controller.position_label.setText(' %02d:%02d / %02d:%02d' % controller.position_label.setText(' %02d:%02d / %02d:%02d' % (0, 0, total_minutes, total_seconds))
(0, 0, total_minutes, total_seconds))
controller.mediabar.actions['playbackPlay'].setVisible(True) controller.mediabar.actions['playbackPlay'].setVisible(True)
controller.mediabar.actions['playbackStop'].setDisabled(True) controller.mediabar.actions['playbackStop'].setDisabled(True)
controller.mediabar.actions['playbackPause'].setVisible(False) controller.mediabar.actions['playbackPause'].setVisible(False)

View File

@ -139,11 +139,12 @@ class MediaPlayer(RegistryProperties):
""" """
pass pass
def update_ui(self, display): def update_ui(self, controller, output_display):
""" """
Do some ui related stuff (e.g. update the seek slider) Do some ui related stuff (e.g. update the seek slider)
:param display: The display to be updated. :param controller: Which Controller is running the show.
:param output_display: The display where the media is
""" """
pass pass

View File

@ -20,11 +20,12 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # # along with this program. If not, see <https://www.gnu.org/licenses/>. #
########################################################################## ##########################################################################
""" """
The :mod:`~openlp.core.ui.media.playertab` module holds the configuration tab for the media stuff. The :mod:`~openlp.core.ui.media.mediatab` module holds the configuration tab for the media stuff.
""" """
import logging
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
# from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio from PyQt5.QtMultimedia import QCameraInfo, QAudioDeviceInfo, QAudio
from openlp.core.common import is_linux, is_win from openlp.core.common import is_linux, is_win
from openlp.core.common.i18n import translate from openlp.core.common.i18n import translate
@ -32,8 +33,11 @@ from openlp.core.common.settings import Settings
from openlp.core.lib.settingstab import SettingsTab from openlp.core.lib.settingstab import SettingsTab
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
LINUX_STREAM = 'v4l2:///dev/video0' LINUX_STREAM = 'v4l2://{video} :v4l2-standard= :input-slave={audio} :live-caching=300'
WIN_STREAM = 'dshow:// :dshow-vdev=' WIN_STREAM = 'dshow://:dshow-vdev={video} :dshow-adev={audio} :live-caching=300'
OSX_STREAM = 'avcapture://{video} :qtsound://{audio} :live-caching=300'
log = logging.getLogger(__name__)
class MediaTab(SettingsTab): class MediaTab(SettingsTab):
@ -44,8 +48,6 @@ class MediaTab(SettingsTab):
""" """
Constructor Constructor
""" """
# self.media_players = Registry().get('media_controller').media_players
# self.saved_used_players = None
self.icon_path = UiIcons().video self.icon_path = UiIcons().video
player_translated = translate('OpenLP.MediaTab', 'Media') player_translated = translate('OpenLP.MediaTab', 'Media')
super(MediaTab, self).__init__(parent, 'Media', player_translated) super(MediaTab, self).__init__(parent, 'Media', player_translated)
@ -67,39 +69,52 @@ class MediaTab(SettingsTab):
self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column) self.stream_media_group_box = QtWidgets.QGroupBox(self.left_column)
self.stream_media_group_box.setObjectName('stream_media_group_box') self.stream_media_group_box.setObjectName('stream_media_group_box')
self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box) self.stream_media_layout = QtWidgets.QHBoxLayout(self.stream_media_group_box)
self.stream_media_layout.setObjectName('live_media_layout') self.stream_media_layout.setObjectName('stream_media_layout')
self.stream_media_layout.setContentsMargins(0, 0, 0, 0) self.stream_media_layout.setContentsMargins(0, 0, 0, 0)
self.stream_edit = QtWidgets.QPlainTextEdit(self) self.stream_edit = QtWidgets.QLabel(self)
self.stream_media_layout.addWidget(self.stream_edit) self.stream_media_layout.addWidget(self.stream_edit)
self.browse_button = QtWidgets.QToolButton(self)
self.browse_button.setIcon(UiIcons().undo)
self.stream_media_layout.addWidget(self.browse_button)
self.left_layout.addWidget(self.stream_media_group_box)
self.left_layout.addWidget(self.stream_media_group_box) self.left_layout.addWidget(self.stream_media_group_box)
self.vlc_arguments_group_box = QtWidgets.QGroupBox(self.left_column)
self.vlc_arguments_group_box.setObjectName('vlc_arguments_group_box')
self.vlc_arguments_layout = QtWidgets.QHBoxLayout(self.vlc_arguments_group_box)
self.vlc_arguments_layout.setObjectName('vlc_arguments_layout')
self.vlc_arguments_layout.setContentsMargins(0, 0, 0, 0)
self.vlc_arguments_edit = QtWidgets.QPlainTextEdit(self)
self.vlc_arguments_layout.addWidget(self.vlc_arguments_edit)
self.left_layout.addWidget(self.vlc_arguments_group_box)
self.left_layout.addStretch() self.left_layout.addStretch()
self.right_layout.addStretch() self.right_layout.addStretch()
# # Signals and slots # # Signals and slots
self.browse_button.clicked.connect(self.on_revert)
def retranslateUi(self): def retranslate_ui(self):
""" """
Translate the UI on the fly Translate the UI on the fly
""" """
self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media')) self.live_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Live Media'))
self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command')) self.stream_media_group_box.setTitle(translate('MediaPlugin.MediaTab', 'Stream Media Command'))
self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start automatically')) self.vlc_arguments_group_box.setTitle(translate('MediaPlugin.MediaTab', 'VLC arguments'))
self.auto_start_check_box.setText(translate('MediaPlugin.MediaTab', 'Start Live items automatically'))
def load(self): def load(self):
""" """
Load the settings Load the settings
""" """
self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start')) self.auto_start_check_box.setChecked(Settings().value(self.settings_section + '/media auto start'))
self.stream_edit.setPlainText(Settings().value(self.settings_section + '/stream command')) self.stream_edit.setText(Settings().value(self.settings_section + '/stream command'))
if not self.stream_edit.toPlainText(): if not self.stream_edit.text():
if is_linux: if is_linux:
self.stream_edit.setPlainText(LINUX_STREAM) self.stream_edit.setText(LINUX_STREAM)
elif is_win: elif is_win:
self.stream_edit.setPlainText(WIN_STREAM) self.stream_edit.setText(WIN_STREAM)
else:
self.stream_edit.setText(OSX_STREAM)
self.vlc_arguments_edit.setPlainText(Settings().value(self.settings_section + '/vlc arguments'))
if Settings().value('advanced/experimental'):
for cam in QCameraInfo.availableCameras():
log.debug(cam.deviceName())
log.debug(cam.description())
for au in QAudioDeviceInfo.availableDevices(QAudio.AudioInput):
log.debug(au.deviceName())
def save(self): def save(self):
""" """
@ -108,17 +123,8 @@ class MediaTab(SettingsTab):
setting_key = self.settings_section + '/media auto start' setting_key = self.settings_section + '/media auto start'
if Settings().value(setting_key) != self.auto_start_check_box.checkState(): if Settings().value(setting_key) != self.auto_start_check_box.checkState():
Settings().setValue(setting_key, self.auto_start_check_box.checkState()) Settings().setValue(setting_key, self.auto_start_check_box.checkState())
# settings = Settings() Settings().setValue(self.settings_section + '/stream command', self.stream_edit.text())
# settings.beginGroup(self.settings_section) Settings().setValue(self.settings_section + '/vlc arguments', self.vlc_arguments_edit.toPlainText())
# settings.setValue('background color', self.background_color)
# settings.endGroup()
# old_players, override_player = get_media_players()
# if self.used_players != old_players:
# # clean old Media stuff
# set_media_players(self.used_players, override_player)
# self.settings_form.register_post_process('mediaitem_suffix_reset')
# self.settings_form.register_post_process('mediaitem_media_rebuild')
# self.settings_form.register_post_process('config_screen_changed')
def post_set_up(self, post_update=False): def post_set_up(self, post_update=False):
""" """
@ -127,22 +133,6 @@ class MediaTab(SettingsTab):
:param post_update: Indicates if called before or after updates. :param post_update: Indicates if called before or after updates.
""" """
pass pass
# for key, player in self.media_players.items():
# player = self.media_players[key]
# checkbox = MediaQCheckBox(self.media_player_group_box)
# checkbox.setEnabled(player.available)
# checkbox.setObjectName(player.name + '_check_box')
# checkbox.setToolTip(player.get_info())
# checkbox.set_player_name(player.name)
# self.player_check_boxes[player.name] = checkbox
# checkbox.stateChanged.connect(self.on_player_check_box_changed)
# self.media_player_layout.addWidget(checkbox)
# if player.available and player.name in self.used_players:
# checkbox.setChecked(True)
# else:
# checkbox.setChecked(False)
# self.update_player_list()
# self.retranslate_players()
def on_revert(self): def on_revert(self):
pass pass

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
The :mod:`~openlp.core.ui.media.vendor` module contains any scripts or libraries
from 3rd party vendors which are required to make certain media modules work.
"""

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,7 @@ import os
import sys import sys
import threading import threading
from datetime import datetime from datetime import datetime
from distutils.version import LooseVersion import vlc
from PyQt5 import QtWidgets from PyQt5 import QtWidgets
@ -65,59 +65,19 @@ def get_vlc():
:return: The "vlc" module, or None :return: The "vlc" module, or None
""" """
if 'openlp.core.ui.media.vendor.vlc' in sys.modules: if 'vlc' in sys.modules:
# If VLC has already been imported, no need to do all the stuff below again # If VLC has already been imported, no need to do all the stuff below again
is_vlc_available = False is_vlc_available = False
try: try:
is_vlc_available = bool(sys.modules['openlp.core.ui.media.vendor.vlc'].get_default_instance()) is_vlc_available = bool(sys.modules['vlc'].get_default_instance())
except Exception: except Exception:
pass pass
if is_vlc_available: if is_vlc_available:
return sys.modules['openlp.core.ui.media.vendor.vlc'] return sys.modules['vlc']
else: else:
return None return None
is_vlc_available = False
try:
if is_macosx():
# Newer versions of VLC on OS X need this. See https://forum.videolan.org/viewtopic.php?t=124521
os.environ['VLC_PLUGIN_PATH'] = '/Applications/VLC.app/Contents/MacOS/plugins'
# On Windows when frozen in PyInstaller, we need to blank SetDllDirectoryW to allow loading of the VLC dll.
# This is due to limitations (by design) in PyInstaller. SetDllDirectoryW original value is restored once
# VLC has been imported.
if is_win():
buffer_size = 1024
dll_directory = ctypes.create_unicode_buffer(buffer_size)
new_buffer_size = ctypes.windll.kernel32.GetDllDirectoryW(buffer_size, dll_directory)
dll_directory = ''.join(dll_directory[:new_buffer_size]).replace('\0', '')
log.debug('Original DllDirectory: %s' % dll_directory)
ctypes.windll.kernel32.SetDllDirectoryW(None)
from openlp.core.ui.media.vendor import vlc
if is_win():
ctypes.windll.kernel32.SetDllDirectoryW(dll_directory)
is_vlc_available = bool(vlc.get_default_instance())
except (ImportError, NameError, NotImplementedError):
pass
except OSError as e:
# this will get raised the first time
if is_win():
if not isinstance(e, WindowsError) and e.winerror != 126:
raise
else:
pass
if is_vlc_available:
try:
VERSION = vlc.libvlc_get_version().decode('UTF-8')
except Exception:
VERSION = '0.0.0'
# LooseVersion does not work when a string contains letter and digits (e. g. 2.0.5 Twoflower).
# http://bugs.python.org/issue14894
if LooseVersion(VERSION.split()[0]) < LooseVersion('1.1.0'):
is_vlc_available = False
log.debug('VLC could not be loaded, because the vlc version is too old: %s' % VERSION)
if is_vlc_available:
return vlc
else: else:
return None return vlc
# On linux we need to initialise X threads, but not when running tests. # On linux we need to initialise X threads, but not when running tests.
@ -161,35 +121,35 @@ class VlcPlayer(MediaPlayer):
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
# Temporary workaround output_display.vlc_widget = QtWidgets.QFrame(output_display)
if vlc: output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame)
output_display.vlc_widget = QtWidgets.QFrame(output_display) # creating a basic vlc instance
output_display.vlc_widget.setFrameStyle(QtWidgets.QFrame.NoFrame) command_line_options = '--no-video-title-show '
# creating a basic vlc instance if Settings().value('advanced/hide mouse') and live_display:
command_line_options = '--no-video-title-show' command_line_options += '--mouse-hide-timeout=0 '
if Settings().value('advanced/hide mouse') and live_display: if Settings().value('media/vlc arguments'):
command_line_options += ' --mouse-hide-timeout=0' command_line_options += Settings().value('media/vlc arguments')
output_display.vlc_instance = vlc.Instance(command_line_options) output_display.vlc_instance = vlc.Instance(command_line_options)
# creating an empty vlc media player # creating an empty vlc media player
output_display.vlc_media_player = output_display.vlc_instance.media_player_new() output_display.vlc_media_player = output_display.vlc_instance.media_player_new()
output_display.vlc_widget.resize(output_display.size()) output_display.vlc_widget.resize(output_display.size())
output_display.vlc_widget.raise_() output_display.vlc_widget.raise_()
output_display.vlc_widget.hide() output_display.vlc_widget.hide()
# The media player has to be 'connected' to the QFrame. # The media player has to be 'connected' to the QFrame.
# (otherwise a video would be displayed in it's own window) # (otherwise a video would be displayed in it's own window)
# This is platform specific! # This is platform specific!
# You have to give the id of the QFrame (or similar object) # You have to give the id of the QFrame (or similar object)
# to vlc, different platforms have different functions for this. # to vlc, different platforms have different functions for this.
win_id = int(output_display.vlc_widget.winId()) win_id = int(output_display.vlc_widget.winId())
if is_win(): if is_win():
output_display.vlc_media_player.set_hwnd(win_id) output_display.vlc_media_player.set_hwnd(win_id)
elif is_macosx(): elif is_macosx():
# We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa # We have to use 'set_nsobject' since Qt5 on OSX uses Cocoa
# framework and not the old Carbon. # framework and not the old Carbon.
output_display.vlc_media_player.set_nsobject(win_id) output_display.vlc_media_player.set_nsobject(win_id)
else: else:
# for Linux/*BSD using the X Server # for Linux/*BSD using the X Server
output_display.vlc_media_player.set_xwindow(win_id) output_display.vlc_media_player.set_xwindow(win_id)
self.has_own_widget = True self.has_own_widget = True
def check_available(self): def check_available(self):
@ -226,7 +186,8 @@ class VlcPlayer(MediaPlayer):
return False return False
output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track) output_display.vlc_media = audio_cd_tracks.item_at_index(controller.media_info.title_track)
elif controller.media_info.media_type == MediaType.Stream: elif controller.media_info.media_type == MediaType.Stream:
output_display.vlc_media = output_display.vlc_instance.media_new_location('ZZZZZZ') stream_cmd = Settings().value('media/stream command')
output_display.vlc_media = output_display.vlc_instance.media_new_location(stream_cmd)
else: else:
output_display.vlc_media = output_display.vlc_instance.media_new_path(path) output_display.vlc_media = output_display.vlc_instance.media_new_path(path)
# put the media in the media player # put the media in the media player
@ -242,7 +203,7 @@ class VlcPlayer(MediaPlayer):
Wait no longer than 60 seconds. (loading an iso file needs a long time) Wait no longer than 60 seconds. (loading an iso file needs a long time)
:param media_state: The state of the playing media :param media_state: The state of the playing media
:param display: The display where the media is :param output_display: The display where the media is
:return: :return:
""" """
vlc = get_vlc() vlc = get_vlc()
@ -316,7 +277,7 @@ class VlcPlayer(MediaPlayer):
self.volume(output_display, output_display.media_info.volume) self.volume(output_display, output_display.media_info.volume)
if start_time > 0 and output_display.vlc_media_player.is_seekable(): if start_time > 0 and output_display.vlc_media_player.is_seekable():
output_display.vlc_media_player.set_time(int(start_time)) output_display.vlc_media_player.set_time(int(start_time))
controller.seek_slider.setMaximum(output_display.media_info.length) controller.seek_slider.setMaximum(controller.media_info.length)
self.set_state(MediaState.Playing, output_display) self.set_state(MediaState.Playing, output_display)
output_display.vlc_widget.raise_() output_display.vlc_widget.raise_()
return True return True
@ -363,9 +324,9 @@ class VlcPlayer(MediaPlayer):
:param seek_value: The position of where a seek goes to :param seek_value: The position of where a seek goes to
:param output_display: The display where the media is :param output_display: The display where the media is
""" """
if output_display.controller.media_info.media_type == MediaType.CD \ if output_display.media_info.media_type == MediaType.CD \
or output_display.controller.media_info.media_type == MediaType.DVD: or output_display.media_info.media_type == MediaType.DVD:
seek_value += int(output_display.controller.media_info.start_time) seek_value += int(output_display.media_info.start_time)
if output_display.vlc_media_player.is_seekable(): if output_display.vlc_media_player.is_seekable():
output_display.vlc_media_player.set_time(seek_value) output_display.vlc_media_player.set_time(seek_value)

View File

@ -38,7 +38,7 @@ from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, format_time, translate from openlp.core.common.i18n import UiStrings, format_time, translate
from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import Path, str_to_path from openlp.core.common.path import Path
from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
@ -433,8 +433,8 @@ class ServiceManager(QtWidgets.QWidget, RegistryBase, Ui_ServiceManager, LogMixi
def on_load_service_clicked(self, checked): def on_load_service_clicked(self, checked):
""" """
Handle the `fileOpenItem` action Handle the `fileOpenItem` action
:param bool checked: Not used. :param bool checked: Not used.
:rtype: None :rtype: None
""" """
self.load_service() self.load_service()

View File

@ -30,7 +30,6 @@ from openlp.core.state import State
from openlp.core.api.tab import ApiTab from openlp.core.api.tab import ApiTab
from openlp.core.common.mixins import RegistryProperties from openlp.core.common.mixins import RegistryProperties
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib import build_icon from openlp.core.lib import build_icon
from openlp.core.projectors.tab import ProjectorTab from openlp.core.projectors.tab import ProjectorTab
from openlp.core.ui.advancedtab import AdvancedTab from openlp.core.ui.advancedtab import AdvancedTab
@ -61,6 +60,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.setting_list_widget.currentRowChanged.connect(self.list_item_changed) self.setting_list_widget.currentRowChanged.connect(self.list_item_changed)
self.general_tab = None self.general_tab = None
self.themes_tab = None self.themes_tab = None
self.player_tab = None
self.projector_tab = None self.projector_tab = None
self.advanced_tab = None self.advanced_tab = None
self.api_tab = None self.api_tab = None
@ -79,8 +79,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.insert_tab(self.advanced_tab) self.insert_tab(self.advanced_tab)
self.insert_tab(self.screens_tab) self.insert_tab(self.screens_tab)
self.insert_tab(self.themes_tab) self.insert_tab(self.themes_tab)
if Settings().value('core/experimental'): self.insert_tab(self.player_tab)
self.insert_tab(self.player_tab)
self.insert_tab(self.projector_tab) self.insert_tab(self.projector_tab)
self.insert_tab(self.api_tab) self.insert_tab(self.api_tab)
for plugin in State().list_plugins(): for plugin in State().list_plugins():
@ -161,8 +160,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.themes_tab = ThemesTab(self) self.themes_tab = ThemesTab(self)
self.projector_tab = ProjectorTab(self) self.projector_tab = ProjectorTab(self)
self.advanced_tab = AdvancedTab(self) self.advanced_tab = AdvancedTab(self)
if Settings().value('core/experimental'): self.player_tab = MediaTab(self)
self.player_tab = MediaTab(self)
self.api_tab = ApiTab(self) self.api_tab = ApiTab(self)
self.screens_tab = ScreensTab(self) self.screens_tab = ScreensTab(self)
except Exception as e: except Exception as e:
@ -170,8 +168,7 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
self.general_tab.post_set_up() self.general_tab.post_set_up()
self.themes_tab.post_set_up() self.themes_tab.post_set_up()
self.advanced_tab.post_set_up() self.advanced_tab.post_set_up()
if Settings().value('core/experimental'): self.player_tab.post_set_up()
self.player_tab.post_set_up()
self.api_tab.post_set_up() self.api_tab.post_set_up()
for plugin in State().list_plugins(): for plugin in State().list_plugins():
if plugin.settings_tab: if plugin.settings_tab:

View File

@ -112,7 +112,7 @@ class MediaSlider(QtWidgets.QSlider):
class InfoLabel(QtWidgets.QLabel): class InfoLabel(QtWidgets.QLabel):
""" """
InfoLabel is a subclassed QLabel. Created to provide the ablilty to add a ellipsis if the text is cut off. Original InfoLabel is a subclassed QLabel. Created to provide the ability to add a ellipsis if the text is cut off. Original
source: https://stackoverflow.com/questions/11446478/pyside-pyqt-truncate-text-in-qlabel-based-on-minimumsize source: https://stackoverflow.com/questions/11446478/pyside-pyqt-truncate-text-in-qlabel-based-on-minimumsize
""" """
@ -177,7 +177,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
if screen.is_display: if screen.is_display:
display = DisplayWindow(self, screen) display = DisplayWindow(self, screen)
self.displays.append(display) self.displays.append(display)
# display.media_watcher.progress.connect(self.on_audio_time_remaining)
@property @property
def display(self): def display(self):
@ -381,7 +380,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.position_label.setMinimumSize(90, 0) self.position_label.setMinimumSize(90, 0)
self.position_label.setObjectName('position_label') self.position_label.setObjectName('position_label')
self.mediabar.add_toolbar_widget(self.position_label) self.mediabar.add_toolbar_widget(self.position_label)
# Build the seek_slider. # Build the media seek_slider.
self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self) self.seek_slider = MediaSlider(QtCore.Qt.Horizontal, self, self)
self.seek_slider.setMaximum(1000) self.seek_slider.setMaximum(1000)
self.seek_slider.setTracking(True) self.seek_slider.setTracking(True)
@ -398,7 +397,6 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.volume_slider.setMaximum(100) self.volume_slider.setMaximum(100)
self.volume_slider.setTracking(True) self.volume_slider.setTracking(True)
self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.')) self.volume_slider.setToolTip(translate('OpenLP.SlideController', 'Audio Volume.'))
# self.volume_slider.setValue(self.media_info.volume)
self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24)) self.volume_slider.setGeometry(QtCore.QRect(90, 160, 221, 24))
self.volume_slider.setObjectName('volume_slider') self.volume_slider.setObjectName('volume_slider')
self.mediabar.add_toolbar_widget(self.volume_slider) self.mediabar.add_toolbar_widget(self.volume_slider)

View File

@ -26,19 +26,18 @@ import os
import zipfile import zipfile
from xml.etree.ElementTree import XML, ElementTree from xml.etree.ElementTree import XML, ElementTree
from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import delete_file from openlp.core.common import delete_file
from openlp.core.common.applocation import AppLocation from openlp.core.common.applocation import AppLocation
from openlp.core.common.i18n import UiStrings, get_locale_key, translate from openlp.core.common.i18n import UiStrings, get_locale_key, translate
from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.mixins import LogMixin, RegistryProperties
from openlp.core.common.path import Path, copyfile, create_paths, path_to_str from openlp.core.common.path import Path, copyfile, create_paths
from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import ImageSource, build_icon, check_item_selected, create_thumb, get_text_file_string, \ from openlp.core.lib import build_icon, check_item_selected, create_thumb, get_text_file_string, validate_thumb
validate_thumb
from openlp.core.lib.exceptions import ValidationError from openlp.core.lib.exceptions import ValidationError
from openlp.core.lib.theme import BackgroundType, Theme from openlp.core.lib.theme import Theme
from openlp.core.lib.ui import create_widget_action, critical_error_message_box from openlp.core.lib.ui import create_widget_action, critical_error_message_box
from openlp.core.ui.filerenameform import FileRenameForm from openlp.core.ui.filerenameform import FileRenameForm
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
@ -648,11 +647,6 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
:rtype: None :rtype: None
""" """
self._write_theme(theme, image_source_path, image_destination_path) self._write_theme(theme, image_source_path, image_destination_path)
if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
self.image_manager.update_image_border(path_to_str(theme.background_filename),
ImageSource.Theme,
QtGui.QColor(theme.background_border_color))
self.image_manager.process_updates()
def _write_theme(self, theme, image_source_path=None, image_destination_path=None): def _write_theme(self, theme, image_source_path=None, image_destination_path=None):
""" """

View File

@ -35,7 +35,7 @@ class FileDialog(QtWidgets.QFileDialog):
:type caption: str :type caption: str
:type directory: openlp.core.common.path.Path :type directory: openlp.core.common.path.Path
:type options: QtWidgets.QFileDialog.Options :type options: QtWidgets.QFileDialog.Options
:rtype: tuple[openlp.core.common.path.Path, str] :rtype: openlp.core.common.path.Path
""" """
args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),)) args, kwargs = replace_params(args, kwargs, ((2, 'directory', path_to_str),))

View File

@ -37,7 +37,7 @@ class AspectRatioLayout(QtWidgets.QLayout):
""" """
Create a layout. Create a layout.
:param PyQt5.QtWidgets.QWidget parent: The parent widget, can be None. :param QtWidgets.QWidget | None parent: The parent widget
:param float aspect_ratio: The aspect ratio as a float (e.g. 16.0/9.0) :param float aspect_ratio: The aspect ratio as a float (e.g. 16.0/9.0)
""" """
super().__init__(parent) super().__init__(parent)

View File

@ -39,7 +39,7 @@ def handle_mime_data_urls(mime_data):
""" """
Process the data from a drag and drop operation. Process the data from a drag and drop operation.
:param PyQt5.QtCore.QMimeData mime_data: The mime data from the drag and drop opperation. :param QtCore.QMimeData mime_data: The mime data from the drag and drop opperation.
:return: A list of file paths that were dropped :return: A list of file paths that were dropped
:rtype: list[openlp.core.common.path.Path] :rtype: list[openlp.core.common.path.Path]
""" """
@ -297,7 +297,7 @@ class ListWidgetWithDnD(QtWidgets.QListWidget):
""" """
self.setAcceptDrops(True) self.setAcceptDrops(True)
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file) Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().handle_mime_data)
def clear(self, search_while_typing=False): def clear(self, search_while_typing=False):
""" """
@ -412,7 +412,7 @@ class TreeWidgetWithDnD(QtWidgets.QTreeWidget):
""" """
self.setAcceptDrops(True) self.setAcceptDrops(True)
self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop) self.setDragDropMode(QtWidgets.QAbstractItemView.DragDrop)
Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().load_file) Registry().register_function(('%s_dnd' % self.mime_data_text), self.parent().handle_mime_data)
Registry().register_function(('%s_dnd_internal' % self.mime_data_text), self.parent().dnd_move_internal) Registry().register_function(('%s_dnd_internal' % self.mime_data_text), self.parent().dnd_move_internal)
def mouseMoveEvent(self, event): def mouseMoveEvent(self, event):

View File

@ -102,7 +102,7 @@ class CSVBible(BibleImport):
:rtype: list[namedtuple] :rtype: list[namedtuple]
""" """
try: try:
encoding = get_file_encoding(file_path)['encoding'] encoding = get_file_encoding(file_path)
with file_path.open('r', encoding=encoding, newline='') as csv_file: with file_path.open('r', encoding=encoding, newline='') as csv_file:
csv_reader = csv.reader(csv_file, delimiter=',', quotechar='"') csv_reader = csv.reader(csv_file, delimiter=',', quotechar='"')
return [results_tuple(*line) for line in csv_reader] return [results_tuple(*line) for line in csv_reader]

View File

@ -401,10 +401,9 @@ class ImageMediaItem(MediaManagerItem):
Process a list for files either from the File Dialog or from Drag and Drop. Process a list for files either from the File Dialog or from Drag and Drop.
This method is overloaded from MediaManagerItem. This method is overloaded from MediaManagerItem.
:param files: A List of strings containing the filenames of the files to be loaded :param list[openlp.core.common.path.Path] file_paths: A List of paths to be loaded
:param target_group: The QTreeWidgetItem of the group that will be the parent of the added files :param target_group: The QTreeWidgetItem of the group that will be the parent of the added files
""" """
file_paths = [Path(file) for file in file_paths]
self.application.set_normal_cursor() self.application.set_normal_cursor()
self.load_list(file_paths, target_group) self.load_list(file_paths, target_group)
last_dir = file_paths[0].parent last_dir = file_paths[0].parent

View File

@ -93,16 +93,6 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
to another language. to another language.
""" """
self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media') self.on_new_prompt = translate('MediaPlugin.MediaItem', 'Select Media')
# self.replace_action.setText(UiStrings().ReplaceBG)
# self.replace_action_context.setText(UiStrings().ReplaceBG)
# self.replace_action.setToolTip(UiStrings().ReplaceLiveBGDisabled)
# self.replace_action_context.setToolTip(UiStrings().ReplaceLiveBGDisabled)
# self.reset_action.setText(UiStrings().ResetBG)
# self.reset_action.setToolTip(UiStrings().ResetLiveBG)
# self.reset_action_context.setText(UiStrings().ResetBG)
# self.reset_action_context.setToolTip(UiStrings().ResetLiveBG)
# self.automatic = UiStrings().Automatic
# self.display_type_label.setText(translate('MediaPlugin.MediaItem', 'Use Player:'))
def required_icons(self): def required_icons(self):
""" """
@ -117,7 +107,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
self.can_make_live = False self.can_make_live = False
self.can_add_to_service = False self.can_add_to_service = False
if State().check_preconditions('media_live'): if State().check_preconditions('media_live'):
self.can_make_live = False self.can_make_live = True
def add_list_view_to_toolbar(self): def add_list_view_to_toolbar(self):
""" """

View File

@ -24,7 +24,7 @@ import logging
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common.i18n import UiStrings, get_natural_key, translate from openlp.core.common.i18n import UiStrings, get_natural_key, translate
from openlp.core.common.path import path_to_str, str_to_path from openlp.core.common.path import path_to_str
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings from openlp.core.common.settings import Settings
from openlp.core.lib import ServiceItemContext, build_icon, check_item_selected, create_thumb, validate_thumb from openlp.core.lib import ServiceItemContext, build_icon, check_item_selected, create_thumb, validate_thumb
@ -127,7 +127,7 @@ class PresentationMediaItem(MediaManagerItem):
""" """
self.list_view.setIconSize(QtCore.QSize(88, 50)) self.list_view.setIconSize(QtCore.QSize(88, 50))
file_paths = Settings().value(self.settings_section + '/presentations files') file_paths = Settings().value(self.settings_section + '/presentations files')
self.load_list([path_to_str(path) for path in file_paths], initial_load=True) self.load_list(file_paths, initial_load=True)
self.populate_display_types() self.populate_display_types()
def populate_display_types(self): def populate_display_types(self):
@ -158,7 +158,6 @@ class PresentationMediaItem(MediaManagerItem):
:param list[openlp.core.common.path.Path] file_paths: List of file paths to add to the media manager. :param list[openlp.core.common.path.Path] file_paths: List of file paths to add to the media manager.
""" """
file_paths = [str_to_path(filename) for filename in file_paths]
current_paths = self.get_file_list() current_paths = self.get_file_list()
titles = [file_path.name for file_path in current_paths] titles = [file_path.name for file_path in current_paths]
self.application.set_busy_cursor() self.application.set_busy_cursor()
@ -175,7 +174,7 @@ class PresentationMediaItem(MediaManagerItem):
if not file_path.exists(): if not file_path.exists():
item_name = QtWidgets.QListWidgetItem(file_name) item_name = QtWidgets.QListWidgetItem(file_name)
item_name.setIcon(UiIcons().delete) item_name.setIcon(UiIcons().delete)
item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path)) item_name.setData(QtCore.Qt.UserRole, file_path)
item_name.setToolTip(str(file_path)) item_name.setToolTip(str(file_path))
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
else: else:
@ -211,7 +210,7 @@ class PresentationMediaItem(MediaManagerItem):
'This type of presentation is not supported.')) 'This type of presentation is not supported.'))
continue continue
item_name = QtWidgets.QListWidgetItem(file_name) item_name = QtWidgets.QListWidgetItem(file_name)
item_name.setData(QtCore.Qt.UserRole, path_to_str(file_path)) item_name.setData(QtCore.Qt.UserRole, file_path)
item_name.setIcon(icon) item_name.setIcon(icon)
item_name.setToolTip(str(file_path)) item_name.setToolTip(str(file_path))
self.list_view.addItem(item_name) self.list_view.addItem(item_name)
@ -230,8 +229,7 @@ class PresentationMediaItem(MediaManagerItem):
self.application.set_busy_cursor() self.application.set_busy_cursor()
self.main_window.display_progress_bar(len(row_list)) self.main_window.display_progress_bar(len(row_list))
for item in items: for item in items:
file_path = str_to_path(item.data(QtCore.Qt.UserRole)) self.clean_up_thumbnails(item.data(QtCore.Qt.UserRole))
self.clean_up_thumbnails(file_path)
self.main_window.increment_progress_bar() self.main_window.increment_progress_bar()
self.main_window.finished_progress_bar() self.main_window.finished_progress_bar()
for row in row_list: for row in row_list:
@ -278,7 +276,7 @@ class PresentationMediaItem(MediaManagerItem):
if len(items) > 1: if len(items) > 1:
return False return False
if file_path is None: if file_path is None:
file_path = str_to_path(items[0].data(QtCore.Qt.UserRole)) file_path = items[0].data(QtCore.Qt.UserRole)
file_type = file_path.suffix.lower()[1:] file_type = file_path.suffix.lower()[1:]
if not self.display_type_combo_box.currentText(): if not self.display_type_combo_box.currentText():
return False return False
@ -293,7 +291,7 @@ class PresentationMediaItem(MediaManagerItem):
service_item.theme = -1 service_item.theme = -1
for bitem in items: for bitem in items:
if file_path is None: if file_path is None:
file_path = str_to_path(bitem.data(QtCore.Qt.UserRole)) file_path = bitem.data(QtCore.Qt.UserRole)
path, file_name = file_path.parent, file_path.name path, file_name = file_path.parent, file_path.name
service_item.title = file_name service_item.title = file_name
if file_path.exists(): if file_path.exists():
@ -329,7 +327,7 @@ class PresentationMediaItem(MediaManagerItem):
service_item.processor = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
for bitem in items: for bitem in items:
file_path = str_to_path(bitem.data(QtCore.Qt.UserRole)) file_path = bitem.data(QtCore.Qt.UserRole)
path, file_name = file_path.parent, file_path.name path, file_name = file_path.parent, file_path.name
service_item.title = file_name service_item.title = file_name
if file_path.exists(): if file_path.exists():

View File

@ -48,7 +48,7 @@ class PresentationManagerImport(SongImport):
tree = etree.parse(str(file_path), parser=etree.XMLParser(recover=True)) tree = etree.parse(str(file_path), parser=etree.XMLParser(recover=True))
except etree.XMLSyntaxError: except etree.XMLSyntaxError:
# Try to detect encoding and use it # Try to detect encoding and use it
encoding = get_file_encoding(file_path)['encoding'] encoding = get_file_encoding(file_path)
# Open file with detected encoding and remove encoding declaration # Open file with detected encoding and remove encoding declaration
text = file_path.read_text(encoding=encoding) text = file_path.read_text(encoding=encoding)
text = re.sub(r'.+\?>\n', '', text) text = re.sub(r'.+\?>\n', '', text)

View File

@ -124,7 +124,7 @@ class SongBeamerImport(SongImport):
self.chord_table = None self.chord_table = None
if file_path.is_file(): if file_path.is_file():
# Detect the encoding # Detect the encoding
self.input_file_encoding = get_file_encoding(file_path)['encoding'] self.input_file_encoding = get_file_encoding(file_path)
# The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode. # The encoding should only be ANSI (cp1252), UTF-8, Unicode, Big-Endian-Unicode.
# So if it doesn't start with 'u' we default to cp1252. See: # So if it doesn't start with 'u' we default to cp1252. See:
# https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2 # https://forum.songbeamer.com/viewtopic.php?p=419&sid=ca4814924e37c11e4438b7272a98b6f2

View File

@ -82,7 +82,7 @@ class WorshipAssistantImport(SongImport):
Receive a CSV file to import. Receive a CSV file to import.
""" """
# Get encoding # Get encoding
encoding = get_file_encoding(self.import_source)['encoding'] encoding = get_file_encoding(self.import_source)
with self.import_source.open('r', encoding=encoding) as songs_file: with self.import_source.open('r', encoding=encoding) as songs_file:
songs_reader = csv.DictReader(songs_file, escapechar='\\') songs_reader = csv.DictReader(songs_file, escapechar='\\')
try: try:

View File

@ -89,7 +89,8 @@ MODULES = [
'webob', 'webob',
'requests', 'requests',
'qtawesome', 'qtawesome',
'pymediainfo' 'pymediainfo',
'vlc'
] ]

View File

@ -88,7 +88,7 @@ class TestCommonFunctions(TestCase):
extension_loader('glob') extension_loader('glob')
# THEN: The `ImportError` should be caught and logged # THEN: The `ImportError` should be caught and logged
assert mocked_logger.warning.called assert mocked_logger.exception.called
def test_extension_loader_os_error(self): def test_extension_loader_os_error(self):
""" """
@ -106,7 +106,7 @@ class TestCommonFunctions(TestCase):
extension_loader('glob') extension_loader('glob')
# THEN: The `OSError` should be caught and logged # THEN: The `OSError` should be caught and logged
assert mocked_logger.warning.called assert mocked_logger.exception.called
def test_de_hump_conversion(self): def test_de_hump_conversion(self):
""" """

View File

@ -322,7 +322,7 @@ class TestInit(TestCase, TestMixin):
mocked_open.assert_called_once_with('rb') mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)] assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256)]
mocked_universal_detector_inst.close.assert_called_once_with() mocked_universal_detector_inst.close.assert_called_once_with()
assert result == encoding_result assert result == 'UTF-8'
def test_get_file_encoding_eof(self): def test_get_file_encoding_eof(self):
""" """
@ -344,7 +344,7 @@ class TestInit(TestCase, TestMixin):
mocked_open.assert_called_once_with('rb') mocked_open.assert_called_once_with('rb')
assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)] assert mocked_universal_detector_inst.feed.mock_calls == [call(b'data' * 256), call(b'data' * 4)]
mocked_universal_detector_inst.close.assert_called_once_with() mocked_universal_detector_inst.close.assert_called_once_with()
assert result == encoding_result assert result == 'UTF-8'
def test_get_file_encoding_oserror(self): def test_get_file_encoding_oserror(self):
""" """
@ -367,4 +367,4 @@ class TestInit(TestCase, TestMixin):
mocked_log.exception.assert_called_once_with('Error detecting file encoding') mocked_log.exception.assert_called_once_with('Error detecting file encoding')
mocked_universal_detector_inst.feed.assert_not_called() mocked_universal_detector_inst.feed.assert_not_called()
mocked_universal_detector_inst.close.assert_called_once_with() mocked_universal_detector_inst.close.assert_called_once_with()
assert result == encoding_result assert result == 'UTF-8'

View File

@ -138,7 +138,7 @@ def test_parse_options_file():
assert args.loglevel == 'warning', 'The log level should be set to warning' assert args.loglevel == 'warning', 'The log level should be set to warning'
assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.no_error_form is False, 'The no_error_form should be set to False'
assert args.portable is False, 'The portable flag should be set to false' assert args.portable is False, 'The portable flag should be set to false'
assert args.rargs == 'dummy_temp', 'The service file should not be blank' assert args.rargs == ['dummy_temp'], 'The service file should not be blank'
def test_parse_options_file_and_debug(): def test_parse_options_file_and_debug():
@ -155,7 +155,7 @@ def test_parse_options_file_and_debug():
assert args.loglevel == ' debug', 'The log level should be set to debug' assert args.loglevel == ' debug', 'The log level should be set to debug'
assert args.no_error_form is False, 'The no_error_form should be set to False' assert args.no_error_form is False, 'The no_error_form should be set to False'
assert args.portable is False, 'The portable flag should be set to false' assert args.portable is False, 'The portable flag should be set to false'
assert args.rargs == 'dummy_temp', 'The service file should not be blank' assert args.rargs == ['dummy_temp'], 'The service file should not be blank'
@skip('Figure out why this is causing a segfault') @skip('Figure out why this is causing a segfault')

View File

@ -22,6 +22,7 @@
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, patch from unittest.mock import MagicMock, patch
from openlp.core.common.path import Path
from openlp.core.common.registry import Registry from openlp.core.common.registry import Registry
from openlp.core.server import Server from openlp.core.server import Server
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
@ -83,8 +84,8 @@ class TestServer(TestCase, TestMixin):
self.server._on_ready_read() self.server._on_ready_read()
# THEN: the service will be loaded # THEN: the service will be loaded
assert service_manager.on_load_service_clicked.call_count == 1 assert service_manager.load_service.call_count == 1
service_manager.on_load_service_clicked.assert_called_once_with(file_name) service_manager.load_service.assert_called_once_with(Path(file_name))
@patch("PyQt5.QtCore.QTextStream") @patch("PyQt5.QtCore.QTextStream")
def test_post_to_server(self, mocked_stream): def test_post_to_server(self, mocked_stream):

View File

@ -65,21 +65,6 @@ class TestVLCPlayer(TestCase, TestMixin):
# THEN: The extra environment variable should be there # THEN: The extra environment variable should be there
assert 'openlp.core.ui.media.vendor.vlc' not in sys.modules assert 'openlp.core.ui.media.vendor.vlc' not in sys.modules
@patch('openlp.core.ui.media.vlcplayer.is_macosx')
def test_fix_vlc_22_plugin_path(self, mocked_is_macosx):
"""
Test that on OS X we set the VLC plugin path to fix a bug in the VLC module
"""
# GIVEN: We're on OS X and we don't have the VLC plugin path set
mocked_is_macosx.return_value = True
# WHEN: An checking if the player is available
get_vlc()
# THEN: The extra environment variable should be there
assert 'VLC_PLUGIN_PATH' in os.environ, 'The plugin path should be in the environment variables'
assert '/Applications/VLC.app/Contents/MacOS/plugins' == os.environ['VLC_PLUGIN_PATH']
@patch.dict(os.environ) @patch.dict(os.environ)
@patch('openlp.core.ui.media.vlcplayer.is_macosx') @patch('openlp.core.ui.media.vlcplayer.is_macosx')
def test_not_osx_fix_vlc_22_plugin_path(self, mocked_is_macosx): def test_not_osx_fix_vlc_22_plugin_path(self, mocked_is_macosx):
@ -126,7 +111,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_is_macosx.return_value = False mocked_is_macosx.return_value = False
mocked_is_win.return_value = False mocked_is_win.return_value = False
mocked_settings = MagicMock() mocked_settings = MagicMock()
mocked_settings.value.return_value = True mocked_settings.value.return_value = ''
MockedSettings.return_value = mocked_settings MockedSettings.return_value = mocked_settings
mocked_qframe = MagicMock() mocked_qframe = MagicMock()
mocked_qframe.winId.return_value = 2 mocked_qframe.winId.return_value = 2
@ -150,8 +135,9 @@ class TestVLCPlayer(TestCase, TestMixin):
# THEN: The VLC widget should be set up correctly # THEN: The VLC widget should be set up correctly
assert mocked_output_display.vlc_widget == mocked_qframe assert mocked_output_display.vlc_widget == mocked_qframe
mocked_qframe.setFrameStyle.assert_called_with(1) mocked_qframe.setFrameStyle.assert_called_with(1)
mocked_settings.value.assert_called_with('advanced/hide mouse') mocked_settings.value.assert_any_call('advanced/hide mouse')
mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0') mocked_settings.value.assert_any_call('media/vlc arguments')
mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
assert mocked_output_display.vlc_instance == mocked_instance assert mocked_output_display.vlc_instance == mocked_instance
mocked_instance.media_player_new.assert_called_with() mocked_instance.media_player_new.assert_called_with()
assert mocked_output_display.vlc_media_player == mocked_media_player_new assert mocked_output_display.vlc_media_player == mocked_media_player_new
@ -175,7 +161,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_is_macosx.return_value = False mocked_is_macosx.return_value = False
mocked_is_win.return_value = False mocked_is_win.return_value = False
mocked_settings = MagicMock() mocked_settings = MagicMock()
mocked_settings.value.return_value = True mocked_settings.value.return_value = ''
MockedSettings.return_value = mocked_settings MockedSettings.return_value = mocked_settings
mocked_qframe = MagicMock() mocked_qframe = MagicMock()
mocked_qframe.winId.return_value = 2 mocked_qframe.winId.return_value = 2
@ -197,7 +183,7 @@ class TestVLCPlayer(TestCase, TestMixin):
vlc_player.setup(mocked_output_display, mocked_controller) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: The VLC instance should be created with the correct options # THEN: The VLC instance should be created with the correct options
mocked_vlc.Instance.assert_called_with('--no-video-title-show --mouse-hide-timeout=0') mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@patch('openlp.core.ui.media.vlcplayer.is_macosx') @patch('openlp.core.ui.media.vlcplayer.is_macosx')
@ -213,7 +199,7 @@ class TestVLCPlayer(TestCase, TestMixin):
mocked_is_macosx.return_value = False mocked_is_macosx.return_value = False
mocked_is_win.return_value = False mocked_is_win.return_value = False
mocked_settings = MagicMock() mocked_settings = MagicMock()
mocked_settings.value.return_value = False mocked_settings.value.return_value = ''
MockedSettings.return_value = mocked_settings MockedSettings.return_value = mocked_settings
mocked_qframe = MagicMock() mocked_qframe = MagicMock()
mocked_qframe.winId.return_value = 2 mocked_qframe.winId.return_value = 2
@ -235,7 +221,7 @@ class TestVLCPlayer(TestCase, TestMixin):
vlc_player.setup(mocked_output_display, mocked_controller) vlc_player.setup(mocked_output_display, mocked_controller)
# THEN: The VLC instance should be created with the correct options # THEN: The VLC instance should be created with the correct options
mocked_vlc.Instance.assert_called_with('--no-video-title-show') mocked_vlc.Instance.assert_called_with('--no-video-title-show ')
@patch('openlp.core.ui.media.vlcplayer.is_win') @patch('openlp.core.ui.media.vlcplayer.is_win')
@patch('openlp.core.ui.media.vlcplayer.is_macosx') @patch('openlp.core.ui.media.vlcplayer.is_macosx')
@ -863,7 +849,7 @@ class TestVLCPlayer(TestCase, TestMixin):
# THEN: nothing should happen # THEN: nothing should happen
mocked_display.vlc_media_player.is_seekable.assert_called_with() mocked_display.vlc_media_player.is_seekable.assert_called_with()
mocked_display.vlc_media_player.set_time.assert_called_with(5000) mocked_display.vlc_media_player.set_time.assert_called_with(2000)
def test_reset(self): def test_reset(self):
""" """

View File

@ -96,7 +96,7 @@ class TestMainWindow(TestCase, TestMixin):
# WHEN the argument is processed # WHEN the argument is processed
with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file: with patch.object(self.main_window.service_manager, 'load_file') as mocked_load_file:
self.main_window.open_cmd_line_files(service) self.main_window.open_cmd_line_files([service])
# THEN the service from the arguments is loaded # THEN the service from the arguments is loaded
mocked_load_file.assert_called_with(Path(service)) mocked_load_file.assert_called_with(Path(service))

View File

@ -143,7 +143,7 @@ class TestThemeManager(TestCase):
mocked_theme.export_theme.return_value = "{}" mocked_theme.export_theme.return_value = "{}"
# WHEN: Calling _write_theme with a theme with a name with special characters in it # WHEN: Calling _write_theme with a theme with a name with special characters in it
theme_manager._write_theme(mocked_theme, None, None) theme_manager._write_theme(mocked_theme)
# THEN: It should have been created # THEN: It should have been created
assert os.path.exists(os.path.join(self.temp_folder, 'theme 愛 name', 'theme 愛 name.json')) is True, \ assert os.path.exists(os.path.join(self.temp_folder, 'theme 愛 name', 'theme 愛 name.json')) is True, \
@ -224,7 +224,7 @@ class TestThemeManager(TestCase):
theme_manager = ThemeManager(None) theme_manager = ThemeManager(None)
# WHEN: unzip_theme is called # WHEN: unzip_theme is called
theme_manager.unzip_theme('theme.file', 'folder') theme_manager.unzip_theme(Path('theme.file'), Path('folder'))
# THEN: The critical_error_message_box should have been called # THEN: The critical_error_message_box should have been called
assert mocked_critical_error_message_box.call_count == 1, 'Should have been called once' assert mocked_critical_error_message_box.call_count == 1, 'Should have been called once'

View File

@ -136,8 +136,7 @@ class TestCSVImport(TestCase):
mocked_enter_file = MagicMock() mocked_enter_file = MagicMock()
mocked_csv_file.open.return_value.__enter__.return_value = mocked_enter_file mocked_csv_file.open.return_value.__enter__.return_value = mocked_enter_file
with patch('openlp.plugins.bibles.lib.importers.csvbible.get_file_encoding', with patch('openlp.plugins.bibles.lib.importers.csvbible.get_file_encoding', return_value='utf-8'), \
return_value={'encoding': 'utf-8', 'confidence': 0.99}), \
patch('openlp.plugins.bibles.lib.importers.csvbible.csv.reader', patch('openlp.plugins.bibles.lib.importers.csvbible.csv.reader',
return_value=iter(test_data)) as mocked_reader: return_value=iter(test_data)) as mocked_reader:

View File

@ -28,7 +28,8 @@ from unittest.mock import call, patch
import openlp.core.projectors.pjlink import openlp.core.projectors.pjlink
from openlp.core.projectors.pjlinkcommands import process_command from openlp.core.projectors.pjlinkcommands import process_command
from openlp.core.projectors.constants import E_UNDEFINED, S_DATA_OK from openlp.core.projectors.constants import E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED, \
S_DATA_OK, PJLINK_ERRORS, STATUS_MSG
from openlp.core.projectors.db import Projector from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA from tests.resources.projector.data import TEST1_DATA
@ -114,19 +115,111 @@ class TestPJLinkRouting(TestCase):
assert (chk == S_DATA_OK), 'Should have returned S_DATA_OK' assert (chk == S_DATA_OK), 'Should have returned S_DATA_OK'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log') @patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_routing_pjink_errors(self, mock_log): def test_routing_pjink_err1(self, mock_log):
""" """
Test rouing when PJLink error received (err1, err2, err3, err4, erra) Test rouing when PJLink ERR1 received
""" """
# GIVEN: Test setup # GIVEN: Test setup
log_error_text = [call('({ip}) CLSS: PJLink returned "ERR1: Undefined Command"'.format(ip=self.pjlink.name))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "ERR1"'.format(ip=self.pjlink.name))]
err_code = E_UNDEFINED err_code = E_UNDEFINED
err_msg = STATUS_MSG[err_code]
err_str = PJLINK_ERRORS[err_code]
log_error_text = [call('({ip}) CLSS: {err}'.format(ip=self.pjlink.name, err=err_msg))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "{err}"'.format(ip=self.pjlink.name,
err=err_str))]
# WHEN: routing called # WHEN: routing called
chk = process_command(projector=self.pjlink, cmd='CLSS', data='ERR1') chk = process_command(projector=self.pjlink, cmd='CLSS', data=err_str)
# THEN: Appropriate log entries should have been made and methods called/not called # THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text) mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text) mock_log.debug.assert_has_calls(log_debug_text)
assert (chk == err_code), 'Should have returned E_UNDEFINED' assert (chk == err_code), 'Should have returned {err}'.format(err=PJLINK_ERRORS[err_code])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_routing_pjink_err2(self, mock_log):
"""
Test rouing when PJLink ERR2 received
"""
# GIVEN: Test setup
err_code = E_PARAMETER
err_msg = STATUS_MSG[err_code]
err_str = PJLINK_ERRORS[err_code]
log_error_text = [call('({ip}) CLSS: {err}'.format(ip=self.pjlink.name, err=err_msg))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "{err}"'.format(ip=self.pjlink.name,
err=err_str))]
# WHEN: routing called
chk = process_command(projector=self.pjlink, cmd='CLSS', data=err_str)
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
assert (chk == err_code), 'Should have returned {err}'.format(err=PJLINK_ERRORS[err_code])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_routing_pjink_err3(self, mock_log):
"""
Test rouing when PJLink ERR3 received
"""
# GIVEN: Test setup
err_code = E_UNAVAILABLE
err_msg = STATUS_MSG[err_code]
err_str = PJLINK_ERRORS[err_code]
log_error_text = [call('({ip}) CLSS: {err}'.format(ip=self.pjlink.name, err=err_msg))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "{err}"'.format(ip=self.pjlink.name,
err=err_str))]
# WHEN: routing called
chk = process_command(projector=self.pjlink, cmd='CLSS', data=err_str)
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
assert (chk == err_code), 'Should have returned {err}'.format(err=PJLINK_ERRORS[err_code])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_routing_pjink_err4(self, mock_log):
"""
Test rouing when PJLink ERR4 received
"""
# GIVEN: Test setup
err_code = E_PROJECTOR
err_msg = STATUS_MSG[err_code]
err_str = PJLINK_ERRORS[err_code]
log_error_text = [call('({ip}) CLSS: {err}'.format(ip=self.pjlink.name, err=err_msg))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "{err}"'.format(ip=self.pjlink.name,
err=err_str))]
# WHEN: routing called
chk = process_command(projector=self.pjlink, cmd='CLSS', data=err_str)
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
assert (chk == err_code), 'Should have returned {err}'.format(err=PJLINK_ERRORS[err_code])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_routing_pjink_errA(self, mock_log):
"""
Test rouing when PJLink ERRA received
"""
# GIVEN: Test setup
err_code = E_AUTHENTICATION
err_msg = STATUS_MSG[err_code]
err_str = PJLINK_ERRORS[err_code]
log_error_text = [call('({ip}) CLSS: {err}'.format(ip=self.pjlink.name, err=err_msg))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "{err}"'.format(ip=self.pjlink.name,
err=err_str))]
# WHEN: routing called
chk = process_command(projector=self.pjlink, cmd='CLSS', data=err_str)
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
assert (chk == err_code), 'Should have returned {err}'.format(err=PJLINK_ERRORS[err_code])

View File

@ -0,0 +1,549 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.pjlinkcommands import process_command
from openlp.core.projectors.constants import E_ERROR, E_WARN, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, S_OK
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkCommands(TestCase):
"""
Tests PJLink commands part 1
"""
def setUp(self):
"""
Initial test setup
"""
self.pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
"""
Test reset
"""
del(self.pjlink)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_audio_muted(self, mock_log, mock_UpdateIcons):
"""
Test avmt status shutter unchanged and mute on
"""
# GIVEN: Test setup
log_warning_text = []
log_debug_text = [call('({ip}) Processing command "AVMT" with data "21"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name)),
call('({ip}) Setting speaker to muted'.format(ip=self.pjlink.name))]
self.pjlink.shutter = True
self.pjlink.mute = False
# WHEN: Called with setting shutter closed and mute on
process_command(projector=self.pjlink, cmd='AVMT', data='21')
# THEN: Shutter should be closed and mute should be True
assert self.pjlink.shutter, 'Shutter should not have changed'
assert self.pjlink.mute, 'Audio should be off'
assert mock_UpdateIcons.emit.called, 'Update icons should have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_bad_data(self, mock_log, mock_UpdateIcons):
"""
Test avmt bad data fail
"""
# GIVEN: Test object
log_warning_text = [call('({ip}) Invalid av mute response: 36'.format(ip=self.pjlink.name))]
log_debug_text = [call('({ip}) Processing command "AVMT" with data "36"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name))]
self.pjlink.shutter = True
self.pjlink.mute = True
# WHEN: Called with an invalid setting
process_command(projector=self.pjlink, cmd='AVMT', data='36')
# THEN: Shutter should be closed and mute should be True
assert self.pjlink.shutter, 'Shutter should changed'
assert self.pjlink.mute, 'Audio should not have changed'
assert not mock_UpdateIcons.emit.called, 'Update icons should NOT have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_closed_muted(self, mock_log, mock_UpdateIcons):
"""
Test avmt status shutter closed and mute off
"""
# GIVEN: Test object
log_warning_text = []
log_debug_text = [call('({ip}) Processing command "AVMT" with data "31"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name)),
call('({ip}) Setting shutter to closed'.format(ip=self.pjlink.name)),
call('({ip}) Setting speaker to muted'.format(ip=self.pjlink.name))]
self.pjlink.shutter = False
self.pjlink.mute = False
# WHEN: Called with setting shutter to closed and mute on
process_command(projector=self.pjlink, cmd='AVMT', data='31')
# THEN: Shutter should be closed and mute should be True
assert self.pjlink.shutter, 'Shutter should have been set to closed'
assert self.pjlink.mute, 'Audio should be muted'
assert mock_UpdateIcons.emit.called, 'Update icons should have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_open_unmuted(self, mock_log, mock_UpdateIcons):
"""
Test avmt status shutter open and mute off
"""
# GIVEN: Test object
log_warning_text = []
log_debug_text = [call('({ip}) Processing command "AVMT" with data "30"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name)),
call('({ip}) Setting shutter to open'.format(ip=self.pjlink.name)),
call('({ip}) Setting speaker to normal'.format(ip=self.pjlink.name))]
self.pjlink.shutter = True
self.pjlink.mute = True
# WHEN: Called with setting shutter to closed and mute on
process_command(projector=self.pjlink, cmd='AVMT', data='30')
# THEN: Shutter should be closed and mute should be True
assert not self.pjlink.shutter, 'Shutter should have been set to off'
assert not self.pjlink.mute, 'Audio should be on'
assert mock_UpdateIcons.emit.called, 'Update icons should have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_shutter_closed(self, mock_log, mock_UpdateIcons):
"""
Test avmt status shutter closed and audio unchanged
"""
# GIVEN: Test object
log_warning_text = []
log_debug_text = [call('({ip}) Processing command "AVMT" with data "11"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name)),
call('({ip}) Setting shutter to closed'.format(ip=self.pjlink.name))]
self.pjlink.shutter = False
self.pjlink.mute = True
# WHEN: Called with setting shutter closed and mute off
process_command(projector=self.pjlink, cmd='AVMT', data='11')
# THEN: Shutter should be True and mute should be False
assert self.pjlink.shutter, 'Shutter should have been set to closed'
assert self.pjlink.mute, 'Audio should not have changed'
assert mock_UpdateIcons.emit.called, 'Update icons should have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_avmt_status_timer_check_delete(self, mock_log, mock_UpdateIcons):
"""
Test avmt deletes callback in projector.status_timer_check
"""
# GIVEN: Test object
log_warning_text = []
log_debug_text = [call('({ip}) Processing command "AVMT" with data "11"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for AVMT'.format(ip=self.pjlink.name)),
call('({ip}) Setting shutter to closed'.format(ip=self.pjlink.name))]
self.pjlink.shutter = False
self.pjlink.mute = True
self.pjlink.status_timer_checks = {'AVMT': self.pjlink.get_av_mute_status}
# WHEN: Called with setting shutter closed and mute off
with patch.object(self.pjlink, 'status_timer') as mock_status_timer:
process_command(projector=self.pjlink, cmd='AVMT', data='11')
# THEN: Shutter should be True and mute should be False
assert self.pjlink.shutter, 'Shutter should have been set to closed'
assert self.pjlink.mute, 'Audio should not have changed'
assert mock_UpdateIcons.emit.called, 'Update icons should have been called'
assert 'AVMT' not in self.pjlink.status_timer_checks, 'Status timer list should not have AVMT callback'
assert mock_status_timer.stop.called, 'Projector status_timer.stop() should have been called'
mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_1(self, mock_log):
"""
Test CLSS request returns non-standard reply 1
"""
# GIVEN: Test object
log_error_calls = []
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=self.pjlink.name))]
# WHEN: Process non-standard reply
process_command(projector=self.pjlink, cmd='CLSS', data='1')
# THEN: Projector class should be set with proper value
assert '1' == self.pjlink.pjlink_class, 'Should have set class=1'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_2(self, mock_log):
"""
Test CLSS request returns non-standard reply 1
"""
# GIVEN: Test object
log_error_calls = []
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "2"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "2"'.format(ip=self.pjlink.name))]
# WHEN: Process non-standard reply
process_command(projector=self.pjlink, cmd='CLSS', data='2')
# THEN: Projector class should be set with proper value
assert '2' == self.pjlink.pjlink_class, 'Should have set class=2'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_invalid_nan(self, mock_log):
"""
Test CLSS reply has no class number
"""
# GIVEN: Test setup
log_warning_calls = [call('({ip}) NAN CLSS version reply "Z" - '
'defaulting to class "1"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "Z"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=self.pjlink.name))]
# WHEN: Process invalid reply
process_command(projector=self.pjlink, cmd='CLSS', data='Z')
# THEN: Projector class should be set with default value
assert self.pjlink.pjlink_class == '1', 'Invalid NaN class reply should have set class=1'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_invalid_no_version(self, mock_log):
"""
Test CLSS reply has no class number
"""
# GIVEN: Test object
log_warning_calls = [call('({ip}) No numbers found in class version reply "Invalid" '
'- defaulting to class "1"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "Invalid"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=self.pjlink.name))]
# WHEN: Process invalid reply
process_command(projector=self.pjlink, cmd='CLSS', data='Invalid')
# THEN: Projector class should be set with default value
assert self.pjlink.pjlink_class == '1', 'Invalid class reply should have set class=1'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_nonstandard_reply_1(self, mock_log):
"""
Test CLSS request returns non-standard reply 1
"""
# GIVEN: Test object
log_error_calls = []
log_warning_calls = [call('({ip}) Non-standard CLSS reply: "Class 1"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "Class 1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=self.pjlink.name))]
# WHEN: Process non-standard reply
process_command(projector=self.pjlink, cmd='CLSS', data='Class 1')
# THEN: Projector class should be set with proper value
assert '1' == self.pjlink.pjlink_class, 'Non-standard class reply should have set class=1'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_clss_nonstandard_reply_2(self, mock_log):
"""
Test CLSS request returns non-standard reply 1
"""
# GIVEN: Test object
log_warning_calls = [call('({ip}) Non-standard CLSS reply: "Version2"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "CLSS" with data "Version2"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name)),
call('({ip}) Setting pjlink_class for this projector to "2"'.format(ip=self.pjlink.name))]
# WHEN: Process non-standard reply
process_command(projector=self.pjlink, cmd='CLSS', data='Version2')
# THEN: Projector class should be set with proper value
assert '2' == self.pjlink.pjlink_class, 'Non-standard class reply should have set class=1'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_all_error(self, mock_log):
"""
Test test_projector_process_erst_all_error
"""
# GIVEN: Test object
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_ERROR],
lamp=PJLINK_ERST_STATUS[E_ERROR],
temp=PJLINK_ERST_STATUS[E_ERROR],
cover=PJLINK_ERST_STATUS[E_ERROR],
filt=PJLINK_ERST_STATUS[E_ERROR],
other=PJLINK_ERST_STATUS[E_ERROR])
chk_test = {'Fan': E_ERROR,
'Lamp': E_ERROR,
'Temperature': E_ERROR,
'Cover': E_ERROR,
'Filter': E_ERROR,
'Other': E_ERROR}
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{chk}"'.format(ip=self.pjlink.name,
chk=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
self.pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
process_command(projector=self.pjlink, cmd='ERST', data=chk_data)
# THEN: PJLink instance errors should match chk_value
assert self.pjlink.projector_errors == chk_test, 'Projector errors should be all E_ERROR'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_all_ok(self, mock_log):
"""
Test to verify pjlink.projector_errors is set to None when no errors
"""
# GIVEN: Test object
chk_data = '0' * PJLINK_ERST_DATA['DATA_LENGTH']
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{chk}"'.format(ip=self.pjlink.name,
chk=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
# WHEN: process_erst with no errors
process_command(projector=self.pjlink, cmd='ERST', data=chk_data)
# THEN: PJLink instance errors should be None
assert self.pjlink.projector_errors is None, 'projector_errors should have been set to None'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_all_warn(self, mock_log):
"""
Test test_projector_process_erst_all_error
"""
# GIVEN: Test object
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_WARN],
lamp=PJLINK_ERST_STATUS[E_WARN],
temp=PJLINK_ERST_STATUS[E_WARN],
cover=PJLINK_ERST_STATUS[E_WARN],
filt=PJLINK_ERST_STATUS[E_WARN],
other=PJLINK_ERST_STATUS[E_WARN])
chk_test = {'Fan': E_WARN,
'Lamp': E_WARN,
'Temperature': E_WARN,
'Cover': E_WARN,
'Filter': E_WARN,
'Other': E_WARN}
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{chk}"'.format(ip=self.pjlink.name,
chk=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
self.pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
process_command(projector=self.pjlink, cmd='ERST', data=chk_data)
# THEN: PJLink instance errors should match chk_value
assert self.pjlink.projector_errors == chk_test, 'Projector errors should be all E_WARN'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_data_invalid_length(self, mock_log):
"""
Test test_projector_process_erst_data_invalid_length
"""
# GIVEN: Test object
chk_data = '0' * (PJLINK_ERST_DATA['DATA_LENGTH'] + 1)
log_warn_calls = [call('({ip}) Invalid error status response "{data}": '
'length != {chk}'.format(ip=self.pjlink.name,
data=chk_data, chk=PJLINK_ERST_DATA['DATA_LENGTH']))]
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
self.pjlink.projector_errors = None
# WHEN: process_erst called with invalid data (too many values
process_command(self.pjlink, cmd='ERST', data=chk_data)
# THEN: pjlink.projector_errors should be empty and warning logged
assert not self.pjlink.projector_errors, 'There should be no errors'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_data_invalid_nan(self, mock_log):
"""
Test ERST called with invalid data
"""
# GIVEN: Test object
chk_data = 'Z' + ('0' * (PJLINK_ERST_DATA['DATA_LENGTH'] - 1))
log_warn_calls = [call('({ip}) Invalid error status response "{data}"'.format(ip=self.pjlink.name,
data=chk_data))]
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
self.pjlink.projector_errors = None
# WHEN: process_erst called with invalid data (too many values
process_command(self.pjlink, cmd='ERST', data=chk_data)
# THEN: pjlink.projector_errors should be empty and warning logged
assert not self.pjlink.projector_errors, 'There should be no errors'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_erst_warn_cover_only(self, mock_log):
"""
Test test_projector_process_erst_warn_cover_only
"""
# GIVEN: Test object
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[S_OK],
lamp=PJLINK_ERST_STATUS[S_OK],
temp=PJLINK_ERST_STATUS[S_OK],
cover=PJLINK_ERST_STATUS[E_WARN],
filt=PJLINK_ERST_STATUS[S_OK],
other=PJLINK_ERST_STATUS[S_OK])
chk_test = {'Cover': E_WARN}
log_warn_calls = []
log_debug_calls = [call('({ip}) Processing command "ERST" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for ERST'.format(ip=self.pjlink.name))]
self.pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
process_command(projector=self.pjlink, cmd='ERST', data=chk_data)
# THEN: PJLink instance errors should match only cover warning
assert 1 == len(self.pjlink.projector_errors), 'There should only be 1 error listed in projector_errors'
assert 'Cover' in self.pjlink.projector_errors, '"Cover" should be the only error listed'
assert self.pjlink.projector_errors['Cover'] == E_WARN, '"Cover" should have E_WARN listed as error'
assert chk_test == self.pjlink.projector_errors, 'projector_errors should match test errors'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inf1(self, mock_log):
"""
Test saving INF1 data (manufacturer)
"""
# GIVEN: Test object
chk_data = 'TEst INformation MultiCase'
log_warn_calls = []
log_debug_calls = [call('({ip}) Processing command "INF1" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for INF1'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector manufacturer data to '
'"{data}"'.format(ip=self.pjlink.name, data=chk_data))]
self.pjlink.manufacturer = None
# WHEN: process_inf called with test data
process_command(projector=self.pjlink, cmd='INF1', data=chk_data)
# THEN: Data should be saved
assert self.pjlink.manufacturer == chk_data, 'Test data should have been saved'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inf2(self, mock_log):
"""
Test saving INF2 data (model)
"""
# GIVEN: Test object
chk_data = 'TEst moDEl MultiCase'
log_warn_calls = []
log_debug_calls = [call('({ip}) Processing command "INF2" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for INF2'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector model to "{data}"'.format(ip=self.pjlink.name,
data=chk_data))]
self.pjlink.model = None
# WHEN: process_inf called with test data
process_command(projector=self.pjlink, cmd='INF2', data=chk_data)
# THEN: Data should be saved
assert self.pjlink.model == chk_data, 'Test data should have been saved'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_info(self, mock_log):
"""
Test saving INF2 data (model)
"""
# GIVEN: Test object
chk_data = 'TEst ExtrANEous MultiCase INformatoin that MFGR might Set'
log_warn_calls = []
log_debug_calls = [call('({ip}) Processing command "INFO" with data "{data}"'.format(ip=self.pjlink.name,
data=chk_data)),
call('({ip}) Calling function for INFO'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector other_info to "{data}"'.format(ip=self.pjlink.name,
data=chk_data))]
self.pjlink.other_info = None
# WHEN: process_inf called with test data
process_command(projector=self.pjlink, cmd='INFO', data=chk_data)
# THEN: Data should be saved
assert self.pjlink.other_info == chk_data, 'Test data should have been saved'
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)

View File

@ -0,0 +1,550 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.pjlinkcommands import process_command
from openlp.core.projectors.constants import PJLINK_POWR_STATUS, S_ON, S_STANDBY
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkCommands(TestCase):
"""
Tests PJLink commands part 2
"""
def setUp(self):
"""
Initial test setup
"""
self.pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
"""
Test reset
"""
del(self.pjlink)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inpt_good(self, mock_log):
"""
Test input source status shows current input
"""
# GIVEN: Test object
self.pjlink.source = '11'
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "INPT" with data "21"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for INPT'.format(ip=self.pjlink.name)),
call('({ip}) Setting current source to "21"'.format(ip=self.pjlink.name))]
chk_source_available = ['11', '12', '21', '22', '31', '32']
self.pjlink.source_available = chk_source_available
# WHEN: Called with input source
process_command(projector=self.pjlink, cmd='INPT', data='21')
# THEN: Input selected should reflect current input
assert '21' == self.pjlink.source, 'Input source should be set to "21"'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inpt_invalid(self, mock_log):
"""
Test input source returned not valid according to standard
"""
# GIVEN: Test object
log_warning_calls = [call('({ip}) Input source not listed as a PJLink valid source - '
'ignoring'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "INPT" with data "91"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for INPT'.format(ip=self.pjlink.name))]
self.pjlink.source = None
self.pjlink.source_available = None
# WHEN: Called with input source
process_command(projector=self.pjlink, cmd='INPT', data='91')
# THEN: Input selected should reflect current input
assert not self.pjlink.source, 'Input source should not have changed'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inpt_not_in_list(self, mock_log):
"""
Test input source not listed in available sources
"""
# GIVEN: Test object
log_warning_calls = [call('({ip}) Input source not listed in available sources - '
'ignoring'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "INPT" with data "25"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for INPT'.format(ip=self.pjlink.name))]
self.pjlink.source = '11'
chk_source_available = ['11', '12', '21', '22', '31', '32']
self.pjlink.source_available = chk_source_available
# WHEN: Called with input source
process_command(projector=self.pjlink, cmd='INPT', data='25')
# THEN: Input selected should reflect current input
assert '11' == self.pjlink.source, 'Input source should not have changed'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_inst_class_1(self, mock_log):
"""
Test saving video source available information
"""
# GIVEN: Test object
self.pjlink.source_available = []
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "INST" with data '
'"21 12 11 22 32 31"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for INST'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector source_available to '
'"[\'11\', \'12\', \'21\', \'22\', \'31\', \'32\']"'.format(ip=self.pjlink.name))]
chk_data = '21 12 11 22 32 31' # Although they should already be sorted, use unsorted to check method
chk_test = ['11', '12', '21', '22', '31', '32']
# WHEN: process_inst called with test data
process_command(projector=self.pjlink, cmd='INST', data=chk_data)
# THEN: Data should have been sorted and saved properly
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.source_available == chk_test, "Sources should have been sorted and saved"
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_lamp_invalid_missing_data(self, mock_log):
"""
Test process lamp with 1 lamp reply hours only and no on/off status
"""
# GIVEN: Test object
log_warning_calls = [call('({ip}) process_lamp(): Invalid data "45" - '
'Missing data'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "LAMP" with data "45"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for LAMP'.format(ip=self.pjlink.name))]
self.pjlink.lamp = None
# WHEN: Call process_command with 3 lamps
process_command(projector=self.pjlink, cmd='LAMP', data='45')
# THEN: Lamp should have been set with proper lamp status
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert not self.pjlink.lamp, 'Projector lamp info should not have changed'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_lamp_invalid_nan(self, mock_log):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
self.pjlink.lamp = [{'Hours': 00000, 'On': True},
{'Hours': 11111, 'On': False}]
log_warning_calls = [call('({ip}) process_lamp(): Invalid data "11111 1 22222 0 '
'333A3 1"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "LAMP" with data "11111 1 22222 0 '
'333A3 1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for LAMP'.format(ip=self.pjlink.name))]
# WHEN: Call process_command with invalid lamp data
process_command(projector=self.pjlink, cmd='LAMP', data='11111 1 22222 0 333A3 1')
# THEN: lamps should not have changed
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert 2 == len(self.pjlink.lamp), 'Projector lamp list should not have changed'
assert self.pjlink.lamp[0]['On'], 'Lamp 1 power status should not have changed'
assert 0 == self.pjlink.lamp[0]['Hours'], 'Lamp 1 hours should not have changed'
assert not self.pjlink.lamp[1]['On'], 'Lamp 2 power status should not have changed'
assert 11111 == self.pjlink.lamp[1]['Hours'], 'Lamp 2 hours should not have changed'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_lamp_multiple(self, mock_log):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "LAMP" with data "11111 1 22222 0 '
'33333 1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for LAMP'.format(ip=self.pjlink.name))]
self.pjlink.lamp = None
# WHEN: Call process_command with 3 lamps
process_command(projector=self.pjlink, cmd='LAMP', data='11111 1 22222 0 33333 1')
# THEN: Lamp should have been set with proper lamp status
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert 3 == len(self.pjlink.lamp), 'Projector should have 3 lamps specified'
assert self.pjlink.lamp[0]['On'], 'Lamp 1 power status should have been set to TRUE'
assert 11111 == self.pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been set to 11111'
assert not self.pjlink.lamp[1]['On'], 'Lamp 2 power status should have been set to FALSE'
assert 22222 == self.pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been set to 22222'
assert self.pjlink.lamp[2]['On'], 'Lamp 3 power status should have been set to TRUE'
assert 33333 == self.pjlink.lamp[2]['Hours'], 'Lamp 3 hours should have been set to 33333'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_lamp_single(self, mock_log):
"""
Test status lamp on/off and hours
"""
# GIVEN: Test object
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "LAMP" with data "11111 1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for LAMP'.format(ip=self.pjlink.name))]
self.pjlink.lamp = None
# WHEN: Call process_command with 3 lamps
process_command(projector=self.pjlink, cmd='LAMP', data='11111 1')
# THEN: Lamp should have been set with proper lamp status
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert 1 == len(self.pjlink.lamp), 'Projector should have 1 lamp specified'
assert self.pjlink.lamp[0]['On'], 'Lamp 1 power status should have been set to TRUE'
assert 11111 == self.pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been set to 11111'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_name(self, mock_log):
"""
Test saving NAME data from projector
"""
# GIVEN: Test object
chk_data = "Some Name the End-User Set IN Projector"
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "NAME" with data '
'"Some Name the End-User Set IN Projector"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for NAME'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector PJLink name to '
'"Some Name the End-User Set IN Projector"'.format(ip=self.pjlink.name))]
# WHEN: process_name called with test data
process_command(projector=self.pjlink, cmd='NAME', data=chk_data)
# THEN: name should be set and logged
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.pjlink_name == chk_data, 'Name test data should have been saved'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_powr_invalid(self, mock_log, mock_UpdateIcons, mock_change_status, mock_send_command):
"""
Test process_powr invalid call
"""
# GIVEN: Test object
self.pjlink.power = S_STANDBY
log_warning_calls = [call('({ip}) Unknown power response: "99"'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "POWR" with data "99"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for POWR'.format(ip=self.pjlink.name)),
call('({ip}) Processing POWR command'.format(ip=self.pjlink.name))]
# WHEN: process_command called with test data
process_command(projector=self.pjlink, cmd='POWR', data='99')
# THEN: Projector power should not have changed
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert S_STANDBY == self.pjlink.power, 'Power should not have changed'
mock_UpdateIcons.emit.assert_not_called()
mock_change_status.assert_not_called()
mock_send_command.assert_not_called()
@patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_powr_off(self, mock_log, mock_UpdateIcons, mock_change_status, mock_send_command):
"""
Test status power to OFF
"""
# GIVEN: Test object
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "POWR" with data "0"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for POWR'.format(ip=self.pjlink.name)),
call('({ip}) Processing POWR command'.format(ip=self.pjlink.name))]
self.pjlink.power = S_ON
# WHEN: process_name called with test data
process_command(projector=self.pjlink, cmd='POWR', data=PJLINK_POWR_STATUS[S_STANDBY])
# THEN: Power should be set to ON
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert S_STANDBY == self.pjlink.power, 'Power should have been set to OFF'
assert mock_UpdateIcons.emit.called, 'projectorUpdateIcons should have been called'
assert not mock_send_command.called, 'send_command should not have been called'
mock_change_status.assert_called_once_with(S_STANDBY)
@patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons')
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_powr_on(self, mock_log, mock_UpdateIcons, mock_change_status, mock_send_command):
"""
Test status power to ON
"""
# GIVEN: Test object
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "POWR" with data "1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for POWR'.format(ip=self.pjlink.name)),
call('({ip}) Processing POWR command'.format(ip=self.pjlink.name))]
self.pjlink.power = S_STANDBY
# WHEN: process_name called with test data
process_command(projector=self.pjlink, cmd='POWR', data=PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert S_ON == self.pjlink.power, 'Power should have been set to ON'
assert mock_UpdateIcons.emit.called, 'projectorUpdateIcons should have been called'
mock_send_command.assert_called_once_with('INST')
mock_change_status.assert_called_once_with(S_ON)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_rfil_save(self, mock_log):
"""
Test saving filter type
"""
# GIVEN: Test object
new_data = 'Filter Type Test'
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "RFIL" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for RFIL'.format(ip=self.pjlink.name))]
self.pjlink.model_filter = None
# WHEN: Filter model is received
process_command(projector=self.pjlink, cmd='RFIL', data=new_data)
# THEN: Filter model number should be saved
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.model_filter == new_data, 'Filter model should have been saved'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_rfil_nosave(self, mock_log):
"""
Test saving filter type previously saved
"""
# GIVEN: Test object
old_data = 'Old filter type'
new_data = 'Filter Type Test'
log_warning_calls = [call('({ip}) Filter model already set'.format(ip=self.pjlink.name)),
call('({ip}) Saved model: "{data}"'.format(ip=self.pjlink.name, data=old_data)),
call('({ip}) New model: "{data}"'.format(ip=self.pjlink.name, data=new_data))]
log_debug_calls = [call('({ip}) Processing command "RFIL" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for RFIL'.format(ip=self.pjlink.name))]
self.pjlink.model_filter = old_data
# WHEN: Filter model is received
process_command(projector=self.pjlink, cmd='RFIL', data=new_data)
# THEN: Filter model number should be saved
assert self.pjlink.model_filter != new_data, 'Filter model should NOT have been saved'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_rlmp_save(self, mock_log):
"""
Test saving lamp type
"""
# GIVEN: Test object
new_data = 'Lamp Type Test'
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "RLMP" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for RLMP'.format(ip=self.pjlink.name))]
self.pjlink.model_lamp = None
# WHEN: Filter model is received
process_command(projector=self.pjlink, cmd='RLMP', data=new_data)
# THEN: Filter model number should be saved
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.model_lamp == new_data, 'Lamp model should have been saved'
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_rlmp_nosave(self, mock_log):
"""
Test saving lamp type previously saved
"""
# GIVEN: Test object
old_data = 'Old filter type'
new_data = 'Filter Type Test'
log_warning_calls = [call('({ip}) Lamp model already set'.format(ip=self.pjlink.name)),
call('({ip}) Saved lamp: "{data}"'.format(ip=self.pjlink.name, data=old_data)),
call('({ip}) New lamp: "{data}"'.format(ip=self.pjlink.name, data=new_data))]
log_debug_calls = [call('({ip}) Processing command "RLMP" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for RLMP'.format(ip=self.pjlink.name))]
self.pjlink.model_lamp = old_data
# WHEN: Filter model is received
process_command(projector=self.pjlink, cmd='RLMP', data=new_data)
# THEN: Filter model number should be saved
assert self.pjlink.model_lamp != new_data, 'Lamp model should NOT have been saved'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_snum_different(self, mock_log):
"""
Test projector serial number different than saved serial number
"""
# GIVEN: Test object
new_data = 'Test Serial Number'
old_data = 'Previous serial number'
log_warning_calls = [call('({ip}) Projector serial number does not match '
'saved serial number'.format(ip=self.pjlink.name)),
call('({ip}) Saved: "{data}"'.format(ip=self.pjlink.name, data=old_data)),
call('({ip}) Received: "{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) NOT saving serial number'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "SNUM" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for SNUM'.format(ip=self.pjlink.name))]
self.pjlink.serial_no = old_data
# WHEN: No serial number is set and we receive serial number command
process_command(projector=self.pjlink, cmd='SNUM', data=new_data)
# THEN: Serial number should be set
assert self.pjlink.serial_no != new_data, 'Projector serial number should NOT have been set'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_snum_set(self, mock_log):
"""
Test saving serial number from projector
"""
# GIVEN: Test object
new_data = 'Test Serial Number'
self.pjlink.serial_no = None
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "SNUM" with data "{data}"'.format(ip=self.pjlink.name,
data=new_data)),
call('({ip}) Calling function for SNUM'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector serial number to '
'"{data}"'.format(ip=self.pjlink.name, data=new_data))]
# WHEN: No serial number is set and we receive serial number command
process_command(projector=self.pjlink, cmd='SNUM', data=new_data)
# THEN: Serial number should be set
assert self.pjlink.serial_no == new_data, 'Projector serial number should have been set'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_sver_changed(self, mock_log):
"""
Test invalid software version information - Received different than saved
"""
# GIVEN: Test object
old_data = 'Test 1 Subtest 1'
new_data = 'Test 1 Subtest 2'
log_warning_calls = [call('({ip}) Projector software version does not match '
'saved software version'.format(ip=self.pjlink.name)),
call('({ip}) Saved: "{data}"'.format(ip=self.pjlink.name, data=old_data)),
call('({ip}) Received: "{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Updating software version'.format(ip=self.pjlink.name))]
log_debug_calls = [call('({ip}) Processing command "SVER" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for SVER'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector software version to '
'"{data}"'.format(ip=self.pjlink.name, data=new_data))]
self.pjlink.sw_version = old_data
# WHEN: process_sver called with invalid data
process_command(self.pjlink, cmd='SVER', data=new_data)
# THEN: Version information should change
assert self.pjlink.sw_version == new_data, 'Software version should have changed'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_sver_invalid(self, mock_log):
"""
Test invalid software version information - too long
"""
# GIVEN: Test object
new_data = 'This is a test software version line that is too long based on PJLink version 2 specs'
log_warning_calls = [call('Invalid software version - too long')]
log_debug_calls = [call('({ip}) Processing command "SVER" with data "{data}"'.format(ip=self.pjlink.name,
data=new_data)),
call('({ip}) Calling function for SVER'.format(ip=self.pjlink.name))]
self.pjlink.sw_version = None
self.pjlink.sw_version_received = None
# WHEN: process_sver called with invalid data
process_command(projector=self.pjlink, cmd='SVER', data=new_data)
# THEN: Version information should not change
assert not self.pjlink.sw_version, 'Software version should not have changed'
assert not self.pjlink.sw_version_received, 'Received software version should not have changed'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_projector_sver_save(self, mock_log):
"""
Test invalid software version information - too long
"""
# GIVEN: Test object
new_data = 'Test 1 Subtest 1'
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "SVER" with data '
'"{data}"'.format(ip=self.pjlink.name, data=new_data)),
call('({ip}) Calling function for SVER'.format(ip=self.pjlink.name)),
call('({ip}) Setting projector software version to '
'"{data}"'.format(ip=self.pjlink.name, data=new_data))]
self.pjlink.sw_version = None
self.pjlink.sw_version_received = None
# WHEN: process_sver called with invalid data
process_command(projector=self.pjlink, cmd='SVER', data=new_data)
# THEN: Version information should not change
assert self.pjlink.sw_version == new_data, 'Software version should have been updated'
assert not self.pjlink.sw_version_received, 'Received version field should not have changed'
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)

View File

@ -0,0 +1,253 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import E_NO_AUTHENTICATION, STATUS_CODE, S_AUTHENTICATE, S_CONNECT
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from openlp.core.projectors.pjlinkcommands import process_command
from tests.resources.projector.data import TEST1_DATA, TEST_PIN, TEST_SALT
class TestPJLinkCommands(TestCase):
"""
Tests PJLink commands part 3
"""
def setUp(self):
"""
Initialize test state(s)
"""
# Default PJLink instance for tests
self.pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
"""
Cleanup test state(s)
"""
del(self.pjlink)
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_authenticate(self, mock_log):
"""
Test initial connection prompt with authentication
"""
# GIVEN: Initial mocks and data
log_error_calls = []
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "1 {data}"'.format(ip=self.pjlink.name,
data=TEST_SALT)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name)),
call('({ip}) PJLINK: Returning {data}'.format(ip=self.pjlink.name,
data=STATUS_CODE[S_AUTHENTICATE]))]
self.pjlink.pin = TEST_PIN
# WHEN: process_pjlink called with no authentication required
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='1 {salt}'.format(salt=TEST_SALT))
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == S_AUTHENTICATE, 'Should have returned {data}'.format(data=STATUS_CODE[S_AUTHENTICATE])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_authenticate_pin_not_set_error(self, mock_log):
"""
Test initial connection prompt with authentication and no pin set
"""
# GIVEN: Initial mocks and data
log_error_calls = [call('({ip}) Authenticate connection but no PIN - aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "1 {data}"'.format(ip=self.pjlink.name,
data=TEST_SALT)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = None
# WHEN: process_pjlink called with no authentication required
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='1 {salt}'.format(salt=TEST_SALT))
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_authenticate_token_invalid(self, mock_log):
"""
Test initial connection prompt with authentication and bad token
"""
# GIVEN: Initial mocks and data
bad_token = 'abcdefgh'
log_error_calls = [call('({ip}) Authentication token invalid (not a hexadecimal number) - '
'aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data '
'"1 {data}"'.format(ip=self.pjlink.name, data=bad_token)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = TEST_SALT
# WHEN: process_pjlink called with bad token
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='1 {data}'.format(data=bad_token))
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_authenticate_token_length(self, mock_log):
"""
Test initial connection prompt with authentication and bad token
"""
# GIVEN: Initial mocks and data
bad_token = '1234abcde' # Length should be 8, this is 9
log_error_calls = [call('({ip}) Authentication token invalid (size) - '
'aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data '
'"1 {data}"'.format(ip=self.pjlink.name, data=bad_token)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = TEST_SALT
# WHEN: process_pjlink called with bad token
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='1 {data}'.format(data=bad_token))
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_authenticate_token_missing(self, mock_log):
"""
Test initial connection prompt with authentication and missing token
"""
# GIVEN: Initial mocks and data
log_error_calls = [call('({ip}) Authenticated connection but not enough info - '
'aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = TEST_SALT
# WHEN: process_pjlink called with bad token
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='1')
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_normal(self, mock_log):
"""
Test processing PJLINK initial prompt
"""
# GIVEN: Mocks and data
log_error_calls = []
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "0"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name)),
call('({ip}) PJLINK: Returning {data}'.format(ip=self.pjlink.name,
data=STATUS_CODE[S_CONNECT]))]
self.pjlink.pin = None
# WHEN: process_pjlink called with no authentication required
chk = process_command(projector=self.pjlink, cmd='PJLINK', data="0")
# THEN: proper processing should have occured
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == S_CONNECT, 'Should have returned {data}'.format(data=STATUS_CODE[S_CONNECT])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_normal_pin_set_error(self, mock_log):
"""
Test process_pjlinnk called with no authentication but pin is set
"""
# GIVEN: Initial mocks and data
log_error_calls = [call('({ip}) Normal connection but PIN set - '
'aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "0"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = TEST_PIN
# WHEN: process_pjlink called with invalid authentication scheme
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='0')
# THEN: Proper calls should be made
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])
@patch.object(openlp.core.projectors.pjlinkcommands, 'log')
def test_process_pjlink_normal_with_token(self, mock_log):
"""
Test process_pjlinnk called with no authentication but pin is set
"""
# GIVEN: Initial mocks and data
log_error_calls = [call('({ip}) Normal connection with extra information - '
'aborting'.format(ip=self.pjlink.name))]
log_warning_calls = []
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data '
'"0 {data}"'.format(ip=self.pjlink.name, data=TEST_SALT)),
call('({ip}) Calling function for PJLINK'.format(ip=self.pjlink.name)),
call('({ip}) Processing PJLINK command'.format(ip=self.pjlink.name))]
self.pjlink.pin = TEST_PIN
# WHEN: process_pjlink called with invalid authentication scheme
chk = process_command(projector=self.pjlink, cmd='PJLINK', data='0 {data}'.format(data=TEST_SALT))
# THEN: Proper calls should be made
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert chk == E_NO_AUTHENTICATION, \
'Should have returned {data}'.format(data=STATUS_CODE[E_NO_AUTHENTICATION])

View File

@ -20,7 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # # along with this program. If not, see <https://www.gnu.org/licenses/>. #
########################################################################## ##########################################################################
""" """
Package to test the openlp.core.projectors.pjlink base package. Package to test the openlp.core.projectors.pjlink base package part 1.
""" """
from unittest import TestCase from unittest import TestCase
from unittest.mock import MagicMock, call, patch from unittest.mock import MagicMock, call, patch

View File

@ -20,7 +20,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # # along with this program. If not, see <https://www.gnu.org/licenses/>. #
########################################################################## ##########################################################################
""" """
Package to test the openlp.core.projectors.pjlink base package. Package to test the openlp.core.projectors.pjlink base package part 2.
""" """
from unittest import TestCase from unittest import TestCase
from unittest.mock import call, patch from unittest.mock import call, patch
@ -96,10 +96,10 @@ class TestPJLinkBase(TestCase):
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_change_status.called_with(E_NETWORK, 'Error while sending data to projector') mock_change_status.called_with(E_NETWORK, 'Error while sending data to projector')
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert mock_timer.start.called, 'Timer should have been called' assert mock_timer.start.called, 'Timer should have been called'
assert (not mock_reset.called), 'reset_information() should not should have been called' assert not mock_reset.called, 'reset_information() should not should have been called'
assert mock_disconnect.called, 'disconnect_from_host() should have been called' assert mock_disconnect.called, 'disconnect_from_host() should have been called'
assert self.pjlink.send_busy, 'send_busy should be True' assert self.pjlink.send_busy, 'send_busy should be True'
@ -127,41 +127,10 @@ class TestPJLinkBase(TestCase):
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_timer.called), 'Timer should not have been called' assert not mock_timer.called, 'Timer should not have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'reset_information')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_local_send_command_no_data_queue_check(self, mock_log, mock_reset, mock_state):
"""
Test _underscore_send_command last queue length check
"""
# GIVEN: Test object
log_error_calls = []
log_warning_calls = [call('({ip}) _send_command(): No data to send'.format(ip=self.pjlink.name))]
log_debug_calls = []
mock_state.return_value = QSOCKET_STATE[S_CONNECTED]
self.pjlink.priority_queue = []
# WHEN: _send_command called with no data and queue's emtpy
# Patch some attributes here since they are not available until after instantiation
with patch.object(self.pjlink, 'socket_timer') as mock_timer, \
patch.object(self.pjlink, 'send_queue') as mock_queue:
# Unlikely case of send_queue not really empty, but len(send_queue) returns 0
mock_queue.return_value = ['test']
mock_queue.__len__.return_value = 0
self.pjlink._send_command(data=None)
# THEN:
mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
assert (not self.pjlink.priority_queue), 'Priority queue should be empty'
assert (not mock_timer.called), 'Timer should not have been called'
assert (not mock_reset.called), 'reset_information() should not have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'write') @patch.object(openlp.core.projectors.pjlink.PJLink, 'write')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') @patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host')
@ -204,11 +173,11 @@ class TestPJLinkBase(TestCase):
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert mock_timer.start.called, 'Timer should have been called' assert mock_timer.start.called, 'Timer should have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert (not mock_disconnect.called), 'disconnect_from_host() should not have been called' assert not mock_disconnect.called, 'disconnect_from_host() should not have been called'
assert self.pjlink.send_busy, 'send_busy flag should be True' assert self.pjlink.send_busy, 'send_busy flag should be True'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') @patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host')
@ -242,12 +211,12 @@ class TestPJLinkBase(TestCase):
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert (self.pjlink.send_queue == [test_command]), 'Send queue should have one entry' assert self.pjlink.send_queue == [test_command], 'Send queue should have one entry'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_timer.called), 'Timer should not have been called' assert not mock_timer.called, 'Timer should not have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_disconnect.called, 'disconnect_from_host() should have been called' assert mock_disconnect.called, 'disconnect_from_host() should have been called'
assert (not self.pjlink.send_busy), 'send_busy flag should be False' assert not self.pjlink.send_busy, 'send_busy flag should be False'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'write') @patch.object(openlp.core.projectors.pjlink.PJLink, 'write')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') @patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host')
@ -292,11 +261,11 @@ class TestPJLinkBase(TestCase):
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert mock_timer.start.called, 'Timer should have been called' assert mock_timer.start.called, 'Timer should have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert (not mock_disconnect.called), 'disconnect_from_host() should not have been called' assert not mock_disconnect.called, 'disconnect_from_host() should not have been called'
assert self.pjlink.send_busy, 'send_busy flag should be True' assert self.pjlink.send_busy, 'send_busy flag should be True'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'write') @patch.object(openlp.core.projectors.pjlink.PJLink, 'write')
@ -345,10 +314,10 @@ class TestPJLinkBase(TestCase):
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.send_queue, 'Send queue should have one entry' assert self.pjlink.send_queue, 'Send queue should have one entry'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert mock_timer.start.called, 'Timer should have been called' assert mock_timer.start.called, 'Timer should have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert (not mock_disconnect.called), 'disconnect_from_host() should not have been called' assert not mock_disconnect.called, 'disconnect_from_host() should not have been called'
assert self.pjlink.send_busy, 'send_busy flag should be True' assert self.pjlink.send_busy, 'send_busy flag should be True'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -389,9 +358,9 @@ class TestPJLinkBase(TestCase):
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.send_queue, 'Send queue should have one entry' assert self.pjlink.send_queue, 'Send queue should have one entry'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_timer.start.called), 'Timer should not have been called' assert not mock_timer.start.called, 'Timer should not have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert self.pjlink.send_busy, 'send_busy flag should be True' assert self.pjlink.send_busy, 'send_busy flag should be True'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -433,10 +402,10 @@ class TestPJLinkBase(TestCase):
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert self.pjlink.priority_queue, 'Priority queue should have one entry' assert self.pjlink.priority_queue, 'Priority queue should have one entry'
assert (not mock_timer.start.called), 'Timer should not have been called' assert not mock_timer.start.called, 'Timer should not have been called'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert self.pjlink.send_busy, 'send_busy flag should be True' assert self.pjlink.send_busy, 'send_busy flag should be True'
# ------------ Test PJLink.send_command ---------- # ------------ Test PJLink.send_command ----------
@ -471,7 +440,7 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -504,8 +473,8 @@ class TestPJLinkBase(TestCase):
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
mock_priority.append.assert_called_with(test_command) mock_priority.append.assert_called_with(test_command)
assert (not mock_send.append.called), 'send_queue should not have changed' assert not mock_send.append.called, 'send_queue should not have changed'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -537,9 +506,9 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (self.pjlink.send_queue == [test_command]), 'Send queue should have one entry' assert self.pjlink.send_queue == [test_command], 'Send queue should have one entry'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -571,9 +540,9 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (self.pjlink.priority_queue == [test_command]), 'Priority queue should have one entry' assert self.pjlink.priority_queue == [test_command], 'Priority queue should have one entry'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -600,10 +569,10 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert (not mock_send_command.called), '_underscore_send_command() should not have been called' assert not mock_send_command.called, '_underscore_send_command() should not have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@patch.object(openlp.core.projectors.pjlink.PJLink, 'reset_information') @patch.object(openlp.core.projectors.pjlink.PJLink, 'reset_information')
@ -633,8 +602,8 @@ class TestPJLinkBase(TestCase):
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert self.pjlink.send_queue, 'Send queue should have one entry' assert self.pjlink.send_queue, 'Send queue should have one entry'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -664,9 +633,9 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert self.pjlink.priority_queue, 'Priority queue should have one entry' assert self.pjlink.priority_queue, 'Priority queue should have one entry'
assert (not mock_reset.called), 'reset_information() should not have been called' assert not mock_reset.called, 'reset_information() should not have been called'
assert mock_send_command.called, '_underscore_send_command() should have been called' assert mock_send_command.called, '_underscore_send_command() should have been called'
@patch.object(openlp.core.projectors.pjlink.PJLink, 'state') @patch.object(openlp.core.projectors.pjlink.PJLink, 'state')
@ -692,7 +661,7 @@ class TestPJLinkBase(TestCase):
mock_log.debug.assert_has_calls(log_debug_calls) mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warning_calls) mock_log.warning.assert_has_calls(log_warning_calls)
mock_log.error.assert_has_calls(log_error_calls) mock_log.error.assert_has_calls(log_error_calls)
assert (not self.pjlink.send_queue), 'Send queue should be empty' assert not self.pjlink.send_queue, 'Send queue should be empty'
assert (not self.pjlink.priority_queue), 'Priority queue should be empty' assert not self.pjlink.priority_queue, 'Priority queue should be empty'
assert mock_reset.called, 'reset_information() should have been called' assert mock_reset.called, 'reset_information() should have been called'
assert (not mock_send_command.called), '_underscore_send_command() should not have been called' assert not mock_send_command.called, '_underscore_send_command() should not have been called'

View File

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink base package part 3.
"""
from unittest import TestCase
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import PJLINK_CLASS, STATUS_CODE, \
S_NOT_CONNECTED, S_OFF, S_ON, QSOCKET_STATE
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkBase(TestCase):
"""
Tests for the PJLink module
"""
def setUp(self):
"""
Initialize test state(s)
"""
# Default PJLink instance for tests
self.pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
"""
Cleanup test state(s)
"""
del(self.pjlink)
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_projector_reset_information(self, mock_log):
"""
Test reset_information() resets all information and stops timers
"""
# GIVEN: Test object
log_debug_calls = [call('({ip}) reset_information() connect status is '
'S_NOT_CONNECTED'.format(ip=self.pjlink.name)),
call('({ip}): Calling poll_timer.stop()'.format(ip=self.pjlink.name)),
call('({ip}): Calling socket_timer.stop()'.format(ip=self.pjlink.name)),
call('({ip}): Calling status_timer.stop()'.format(ip=self.pjlink.name))]
# Attributes not available until instantiation, so mock here
with patch.object(self.pjlink, 'socket_timer') as mock_socket_timer, \
patch.object(self.pjlink, 'status_timer') as mock_status_timer, \
patch.object(self.pjlink, 'poll_timer') as mock_poll_timer, \
patch.object(self.pjlink, 'state') as mock_state:
mock_state.return_value = QSOCKET_STATE[S_NOT_CONNECTED]
# Set attributes to something other than None or {} or []
self.pjlink.fan = True
self.pjlink.filter_time = True
self.pjlink.lamp = True
self.pjlink.mac_adx_received = 'Some random MAC'
self.pjlink.manufacturer = 'PJLINK'
self.pjlink.model = '1'
self.pjlink.model_filter = 'Filter'
self.pjlink.model_lamp = 'Lamp'
self.pjlink.mute = True
self.pjlink.other_info = 'Another Test'
self.pjlink.pjlink_class = 2
self.pjlink.pjlink_name = 'OPENLPTEST'
self.pjlink.power = S_ON
self.pjlink.projector_errors = {'test1': True, 'test2': False}
self.pjlink.serial_no = 'Some Number'
self.pjlink.serial_no_received = 'Some Other Number'
self.pjlink.sw_version = 'Some Version'
self.pjlink.sw_version_received = 'Some Other Version'
self.pjlink.shutter = True
self.pjlink.source_available = True
self.pjlink.source = True
self.pjlink.status_timer_checks = {'test1': object(), 'test2': object()}
self.pjlink.send_busy = False
self.pjlink.send_queue = ['test1', 'test2']
self.pjlink.priority_queue = ['test1', 'test2']
# WHEN: reset_information() is called
self.pjlink.reset_information()
# THEN: All information should be reset and timers stopped
mock_log.debug.assert_has_calls(log_debug_calls)
assert self.pjlink.fan is None, 'fan should be None'
assert self.pjlink.filter_time is None, 'filter_time should be None'
assert self.pjlink.lamp is None, 'lamp should be None'
assert self.pjlink.mac_adx_received is None, 'mac_adx_received should be None'
assert self.pjlink.manufacturer is None, 'manufacturer should be None'
assert self.pjlink.model is None, 'model should be None'
assert self.pjlink.model_filter is None, 'model_filter should be None'
assert self.pjlink.model_lamp is None, 'model_lamp should be None'
assert not self.pjlink.mute, 'mute should be False'
assert self.pjlink.other_info is None, 'other should be None'
assert self.pjlink.pjlink_class == PJLINK_CLASS, 'pjlink_class should be {cls}'.format(cls=PJLINK_CLASS)
assert self.pjlink.pjlink_name is None, 'pjlink_name should be None'
assert self.pjlink.power == S_OFF, 'power should be {data}'.format(data=STATUS_CODE[S_OFF])
assert self.pjlink.projector_errors == {}, 'projector_errors should be an empty dict'
assert self.pjlink.serial_no is None, 'serial_no should be None'
assert self.pjlink.serial_no_received is None, 'serial_no_received should be None'
assert self.pjlink.sw_version is None, 'sw_version should be None'
assert self.pjlink.sw_version_received is None, 'sw_version_received should be None'
assert not self.pjlink.shutter, 'shutter should be False'
assert self.pjlink.source_available is None, 'source_available should be None'
assert self.pjlink.source is None, 'source should be None'
assert self.pjlink.status_timer_checks == {}, 'status_timer_checks should be an empty dict'
assert not self.pjlink.send_busy, 'send_busy should be False'
assert self.pjlink.send_queue == [], 'send_queue should be an empty list'
assert self.pjlink.priority_queue == [], 'priority_queue should be an empty list'
assert mock_socket_timer.stop.called, 'socket_timer.stop() should have been called'
assert mock_status_timer.stop.called, 'status_timer.stop() should have been called'
assert mock_poll_timer.stop.called, 'poll_timer.stop() should have been called'

View File

@ -23,12 +23,11 @@
Package to test the openlp.core.projectors.pjlink command routing. Package to test the openlp.core.projectors.pjlink command routing.
""" """
from unittest import TestCase, skip from unittest import TestCase
from unittest.mock import call, patch from unittest.mock import call, patch
import openlp.core.projectors.pjlink import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED, \ from openlp.core.projectors.constants import PJLINK_PREFIX
PJLINK_ERRORS, PJLINK_PREFIX, STATUS_MSG
from openlp.core.projectors.db import Projector from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA from tests.resources.projector.data import TEST1_DATA
@ -57,9 +56,9 @@ class TestPJLinkRouting(TestCase):
Test projector received valid command invalid version Test projector received valid command invalid version
""" """
# GIVEN: Test object # GIVEN: Test object
log_warning_text = [call('({ip}) get_data() Command reply version does not match ' log_warning_text = [call('({ip}) _send_command(): Nothing to send - returning'.format(ip=self.pjlink.name)),
'a valid command version'.format(ip=self.pjlink.name)), call('({ip}) get_data() Command reply version does not match '
call('({ip}) _send_command(): Nothing to send - returning'.format(ip=self.pjlink.name))] 'a valid command version'.format(ip=self.pjlink.name))]
log_debug_text = [call('({ip}) get_data(buffer="{pre}XCLSS=X"'.format(ip=self.pjlink.name, pre=PJLINK_PREFIX)), log_debug_text = [call('({ip}) get_data(buffer="{pre}XCLSS=X"'.format(ip=self.pjlink.name, pre=PJLINK_PREFIX)),
call('({ip}) get_data(): Checking new data "{pre}XCLSS=X"'.format(ip=self.pjlink.name, call('({ip}) get_data(): Checking new data "{pre}XCLSS=X"'.format(ip=self.pjlink.name,
pre=PJLINK_PREFIX)), pre=PJLINK_PREFIX)),
@ -84,10 +83,10 @@ class TestPJLinkRouting(TestCase):
Test projector receiving invalid command Test projector receiving invalid command
""" """
# GIVEN: Test object # GIVEN: Test object
log_warning_text = [call('({ip}) get_data(): Invalid packet - ' log_warning_text = [call('({ip}) _send_command(): Nothing to send - '
'unknown command "UNKN"'.format(ip=self.pjlink.name)), 'returning'.format(ip=self.pjlink.name)),
call('({ip}) _send_command(): Nothing to send - ' call('({ip}) get_data(): Invalid packet - '
'returning'.format(ip=self.pjlink.name))] 'unknown command "UNKN"'.format(ip=self.pjlink.name))]
log_debug_text = [call('({ip}) get_data(buffer="{pre}1UNKN=Huh?"'.format(ip=self.pjlink.name, log_debug_text = [call('({ip}) get_data(buffer="{pre}1UNKN=Huh?"'.format(ip=self.pjlink.name,
pre=PJLINK_PREFIX)), pre=PJLINK_PREFIX)),
call('({ip}) get_data(): Checking new data "{pre}1UNKN=Huh?"'.format(ip=self.pjlink.name, call('({ip}) get_data(): Checking new data "{pre}1UNKN=Huh?"'.format(ip=self.pjlink.name,
@ -114,9 +113,9 @@ class TestPJLinkRouting(TestCase):
Test projector received valid command with command version higher than projector Test projector received valid command with command version higher than projector
""" """
# GIVEN: Test object # GIVEN: Test object
log_warning_text = [call('({ip}) get_data(): Projector returned class reply higher than projector ' log_warning_text = [call('({ip}) _send_command(): Nothing to send - returning'.format(ip=self.pjlink.name)),
'stated class'.format(ip=self.pjlink.name)), call('({ip}) get_data(): Projector returned class reply higher than projector '
call('({ip}) _send_command(): Nothing to send - returning'.format(ip=self.pjlink.name))] 'stated class'.format(ip=self.pjlink.name))]
log_debug_text = [call('({ip}) get_data(buffer="{pre}2ACKN=X"'.format(ip=self.pjlink.name, log_debug_text = [call('({ip}) get_data(buffer="{pre}2ACKN=X"'.format(ip=self.pjlink.name,
pre=PJLINK_PREFIX)), pre=PJLINK_PREFIX)),
@ -134,119 +133,3 @@ class TestPJLinkRouting(TestCase):
mock_log.warning.assert_has_calls(log_warning_text) mock_log.warning.assert_has_calls(log_warning_text)
mock_log.debug.assert_has_calls(log_debug_text) mock_log.debug.assert_has_calls(log_debug_text)
assert (mock_process_cmd.call_count == 0), 'process_command should not have been called' assert (mock_process_cmd.call_count == 0), 'process_command should not have been called'
@skip('Needs update to new setup')
def test_routing_err1(self):
"""
Test ERR1 - Undefined projector function
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_error_text = [call('({ip}) CLSS: {msg}'.format(ip=self.pjlink.name, msg=STATUS_MSG[E_UNDEFINED]))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "ERR1"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name))]
# WHEN: process_command called with ERR1
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNDEFINED])
@skip('Needs update to new setup')
def test_routing_err2(self):
"""
Test ERR2 - Parameter Error
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_error_text = [call('({ip}) CLSS: {msg}'.format(ip=self.pjlink.name, msg=STATUS_MSG[E_PARAMETER]))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "ERR2"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name))]
# WHEN: process_command called with ERR2
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER])
# THEN: Appropriate log entries should have been made and methods called/not called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PARAMETER])
@skip('Needs update to new setup')
def test_routing_err3(self):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_error_text = [call('({ip}) CLSS: {msg}'.format(ip=self.pjlink.name, msg=STATUS_MSG[E_UNAVAILABLE]))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "ERR3"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name))]
# WHEN: process_command called with ERR3
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNAVAILABLE])
@skip('Needs update to new setup')
def test_routing_err4(self):
"""
Test ERR3 - Unavailable error
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_error_text = [call('({ip}) CLSS: {msg}'.format(ip=self.pjlink.name, msg=STATUS_MSG[E_PROJECTOR]))]
log_debug_text = [call('({ip}) Processing command "CLSS" with data "ERR4"'.format(ip=self.pjlink.name)),
call('({ip}) Calling function for CLSS'.format(ip=self.pjlink.name))]
# WHEN: process_command called with ERR4
pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR])
# THEN: Appropriate log entries should have been made and methods called
mock_log.error.assert_has_calls(log_error_text)
mock_log.debug.assert_has_calls(log_debug_text)
mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PROJECTOR])
@skip('Needs update to new setup')
def test_routing_erra(self):
"""
Test ERRA - Authentication Error
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'process_pjlink') as mock_process_pjlink, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') as mock_disconnect, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorAuthentication') as mock_authentication:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_error_calls = [call('({ip}) PJLINK: {msg}'.format(ip=self.pjlink.name,
msg=STATUS_MSG[E_AUTHENTICATION]))]
log_debug_calls = [call('({ip}) Processing command "PJLINK" with data "ERRA"'.format(ip=self.pjlink.name))]
# WHEN: process_command called with ERRA
pjlink.process_command(cmd='PJLINK', data=PJLINK_ERRORS[E_AUTHENTICATION])
# THEN: Appropriate log entries should have been made and methods called/not called
assert mock_disconnect.called is True, 'disconnect_from_host should have been called'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
mock_change_status.assert_called_once_with(status=E_AUTHENTICATION)
mock_authentication.emit.assert_called_once_with(pjlink.name)
mock_process_pjlink.assert_not_called()

View File

@ -1,853 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase, skip
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import E_ERROR, E_WARN, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \
PJLINK_POWR_STATUS, S_NOT_CONNECTED, S_OK, S_ON, S_STANDBY, STATUS_CODE
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkCommands(TestCase):
"""
Tests for the PJLinkCommands class part 1
"""
@skip('Needs update to new setup')
def test_projector_process_inf1(self):
"""
Test saving INF1 data (manufacturer)
"""
test_data = 'TEst INformation MultiCase'
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.manufacturer = None
# WHEN: process_inf called with test data
pjlink.process_inf1(data=test_data)
# THEN: Data should be saved
assert pjlink.manufacturer == test_data, 'Test data should have been saved'
@skip('Needs update to new setup')
def test_projector_process_inf2(self):
"""
Test saving INF2 data (model)
"""
test_data = 'TEst moDEl MultiCase'
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.model = None
# WHEN: process_inf called with test data
pjlink.process_inf2(data=test_data)
# THEN: Data should be saved
assert pjlink.model == test_data, 'Test data should have been saved'
@skip('Needs update to new setup')
def test_projector_process_info(self):
"""
Test saving INFO data (other information)
"""
test_data = 'TEst ExtrANEous MultiCase INformatoin that MFGR might Set'
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.other_info = None
# WHEN: process_inf called with test data
pjlink.process_info(data=test_data)
# THEN: Data should be saved
assert pjlink.other_info == test_data, 'Test data should have been saved'
@skip('Needs update to new setup')
def test_projector_process_avmt_bad_data(self):
"""
Test avmt bad data fail
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.shutter = True
pjlink.mute = True
# WHEN: Called with an invalid setting
pjlink.process_avmt('36')
# THEN: Shutter should be closed and mute should be True
assert pjlink.shutter is True, 'Shutter should changed'
assert pjlink.mute is True, 'Audio should not have changed'
assert mock_UpdateIcons.emit.called is False, 'Update icons should NOT have been called'
@skip('Needs update to new setup')
def test_projector_process_avmt_closed_muted(self):
"""
Test avmt status shutter closed and mute off
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.shutter = False
pjlink.mute = False
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('31')
# THEN: Shutter should be closed and mute should be True
assert pjlink.shutter is True, 'Shutter should have been set to closed'
assert pjlink.mute is True, 'Audio should be muted'
assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called'
@skip('Needs update to new setup')
def test_projector_process_avmt_shutter_closed(self):
"""
Test avmt status shutter closed and audio unchanged
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.shutter = False
pjlink.mute = True
# WHEN: Called with setting shutter closed and mute off
pjlink.process_avmt('11')
# THEN: Shutter should be True and mute should be False
assert pjlink.shutter is True, 'Shutter should have been set to closed'
assert pjlink.mute is True, 'Audio should not have changed'
assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called'
@skip('Needs update to new setup')
def test_projector_process_avmt_audio_muted(self):
"""
Test avmt status shutter unchanged and mute on
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.shutter = True
pjlink.mute = False
# WHEN: Called with setting shutter closed and mute on
pjlink.process_avmt('21')
# THEN: Shutter should be closed and mute should be True
assert pjlink.shutter is True, 'Shutter should not have changed'
assert pjlink.mute is True, 'Audio should be off'
assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called'
@skip('Needs update to new setup')
def test_projector_process_avmt_open_unmuted(self):
"""
Test avmt status shutter open and mute off
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.shutter = True
pjlink.mute = True
# WHEN: Called with setting shutter to closed and mute on
pjlink.process_avmt('30')
# THEN: Shutter should be closed and mute should be True
assert pjlink.shutter is False, 'Shutter should have been set to open'
assert pjlink.mute is False, 'Audio should be on'
assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called'
@skip('Needs update to new setup')
def test_projector_process_clss_one(self):
"""
Test class 1 sent from projector
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: Process class response
pjlink.process_clss('1')
# THEN: Projector class should be set to 1
assert pjlink.pjlink_class == '1', 'Projector should have set class=1'
@skip('Needs update to new setup')
def test_projector_process_clss_two(self):
"""
Test class 2 sent from projector
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: Process class response
pjlink.process_clss('2')
# THEN: Projector class should be set to 1
assert pjlink.pjlink_class == '2', 'Projector should have set class=2'
@skip('Needs update to new setup')
def test_projector_process_clss_invalid_nan(self):
"""
Test CLSS reply has no class number
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_debug_calls = [call('({ip}) reset_information() connect status is '
'{state}'.format(ip=pjlink.name, state=STATUS_CODE[S_NOT_CONNECTED])),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=pjlink.name))]
log_error_calls = [call('({ip}) NAN CLSS version reply "Z" - '
'defaulting to class "1"'.format(ip=pjlink.name))]
# WHEN: Process invalid reply
pjlink.process_clss('Z')
# THEN: Projector class should be set with default value
assert pjlink.pjlink_class == '1', 'Invalid NaN class reply should have set class=1'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_clss_invalid_no_version(self):
"""
Test CLSS reply has no class number
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_debug_calls = [call('({ip}) reset_information() connect status is '
'{state}'.format(ip=pjlink.name, state=STATUS_CODE[S_NOT_CONNECTED])),
call('({ip}) Setting pjlink_class for this projector to "1"'.format(ip=pjlink.name))]
log_error_calls = [call('({ip}) No numbers found in class version reply "Invalid" '
'- defaulting to class "1"'.format(ip=pjlink.name))]
# WHEN: Process invalid reply
pjlink.process_clss('Invalid')
# THEN: Projector class should be set with default value
assert pjlink.pjlink_class == '1', 'Invalid class reply should have set class=1'
mock_log.error.assert_has_calls(log_error_calls)
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_clss_nonstandard_reply_1(self):
"""
Test CLSS request returns non-standard reply 1
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: Process non-standard reply
pjlink.process_clss('Class 1')
# THEN: Projector class should be set with proper value
assert '1' == pjlink.pjlink_class, 'Non-standard class reply should have set class=1'
@skip('Needs update to new setup')
def test_projector_process_clss_nonstandard_reply_2(self):
"""
Test CLSS request returns non-standard reply 2
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: Process non-standard reply
pjlink.process_clss('Version2')
# THEN: Projector class should be set with proper value
assert '2' == pjlink.pjlink_class, 'Non-standard class reply should have set class=2'
@skip('Needs update to new setup')
def test_projector_process_erst_all_ok(self):
"""
Test to verify pjlink.projector_errors is set to None when no errors
"""
chk_data = '0' * PJLINK_ERST_DATA['DATA_LENGTH']
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: process_erst with no errors
pjlink.process_erst(chk_data)
# THEN: PJLink instance errors should be None
assert pjlink.projector_errors is None, 'projector_errors should have been set to None'
@skip('Needs update to new setup')
def test_projector_process_erst_data_invalid_length(self):
"""
Test test_projector_process_erst_data_invalid_length
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.projector_errors = None
chk_data = '0' * (PJLINK_ERST_DATA['DATA_LENGTH'] + 1)
log_debug_calls = [call('({ip}) reset_information() connect status is '
'{state}'.format(ip=pjlink.name, state=STATUS_CODE[S_NOT_CONNECTED]))]
log_warn_calls = [call('({ip}) Invalid error status response "0000000": '
'length != {chk}'.format(ip=pjlink.name, chk=PJLINK_ERST_DATA['DATA_LENGTH']))]
# WHEN: process_erst called with invalid data (too many values
pjlink.process_erst(chk_data)
# THEN: pjlink.projector_errors should be empty and warning logged
assert pjlink.projector_errors is None, 'There should be no errors'
mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_erst_data_invalid_nan(self):
"""
Test test_projector_process_erst_data_invalid_nan
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.projector_errors = None
chk_data = 'Z' + ('0' * (PJLINK_ERST_DATA['DATA_LENGTH'] - 1))
log_debug_calls = [call('({ip}) reset_information() connect status is '
'{state}'.format(ip=pjlink.name, state=STATUS_CODE[S_NOT_CONNECTED]))]
log_warn_calls = [call('({ip}) Invalid error status response "Z00000"'.format(ip=pjlink.name))]
# WHEN: process_erst called with invalid data (too many values
pjlink.process_erst(chk_data)
# THEN: pjlink.projector_errors should be empty and warning logged
assert pjlink.projector_errors is None, 'There should be no errors'
mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_erst_all_warn(self):
"""
Test test_projector_process_erst_all_warn
"""
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_WARN],
lamp=PJLINK_ERST_STATUS[E_WARN],
temp=PJLINK_ERST_STATUS[E_WARN],
cover=PJLINK_ERST_STATUS[E_WARN],
filt=PJLINK_ERST_STATUS[E_WARN],
other=PJLINK_ERST_STATUS[E_WARN])
chk_test = {'Fan': E_WARN,
'Lamp': E_WARN,
'Temperature': E_WARN,
'Cover': E_WARN,
'Filter': E_WARN,
'Other': E_WARN}
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
pjlink.process_erst(chk_data)
# THEN: PJLink instance errors should match chk_value
assert pjlink.projector_errors == chk_test, 'Projector errors should be all E_WARN'
@skip('Needs update to new setup')
def test_projector_process_erst_all_error(self):
"""
Test test_projector_process_erst_all_error
"""
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_ERROR],
lamp=PJLINK_ERST_STATUS[E_ERROR],
temp=PJLINK_ERST_STATUS[E_ERROR],
cover=PJLINK_ERST_STATUS[E_ERROR],
filt=PJLINK_ERST_STATUS[E_ERROR],
other=PJLINK_ERST_STATUS[E_ERROR])
chk_test = {'Fan': E_ERROR,
'Lamp': E_ERROR,
'Temperature': E_ERROR,
'Cover': E_ERROR,
'Filter': E_ERROR,
'Other': E_ERROR}
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
pjlink.process_erst(chk_data)
# THEN: PJLink instance errors should match chk_value
assert pjlink.projector_errors == chk_test, 'Projector errors should be all E_ERROR'
@skip('Needs update to new setup')
def test_projector_process_erst_warn_cover_only(self):
"""
Test test_projector_process_erst_warn_cover_only
"""
chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[S_OK],
lamp=PJLINK_ERST_STATUS[S_OK],
temp=PJLINK_ERST_STATUS[S_OK],
cover=PJLINK_ERST_STATUS[E_WARN],
filt=PJLINK_ERST_STATUS[S_OK],
other=PJLINK_ERST_STATUS[S_OK])
chk_test = {'Cover': E_WARN}
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.projector_errors = None
# WHEN: process_erst with status set to WARN
pjlink.process_erst(chk_data)
# THEN: PJLink instance errors should match only cover warning
assert 1 == len(pjlink.projector_errors), 'There should only be 1 error listed in projector_errors'
assert 'Cover' in pjlink.projector_errors, '"Cover" should be the only error listed'
assert pjlink.projector_errors['Cover'] == E_WARN, '"Cover" should have E_WARN listed as error'
assert chk_test == pjlink.projector_errors, 'projector_errors should match test errors'
@skip('Needs update to new setup')
def test_projector_process_inpt_valid(self):
"""
Test input source status shows current input
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.source = '11'
log_debug_calls = [call('({ip}) reset_information() connect status is '
'S_NOT_CONNECTED'.format(ip=pjlink.name))]
chk_source_available = ['11', '12', '21', '22', '31', '32']
pjlink.source_available = chk_source_available
# WHEN: Called with input source
pjlink.process_inpt('21')
# THEN: Input selected should reflect current input
assert pjlink.source == '21', 'Input source should be set to "21"'
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_input_not_in_list(self):
"""
Test setting input outside of available inputs
TODO: Future test
"""
pass
@skip('Needs update to new setup')
def test_projector_process_input_not_in_default(self):
"""
Test setting input with no sources available
TODO: Future test
"""
pass
@skip('Needs update to new setup')
def test_projector_process_input_invalid(self):
"""
Test setting input with an invalid value
TODO: Future test
"""
@skip('Needs update to new setup')
def test_projector_process_inst_class_1(self):
"""
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}) 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']
# WHEN: process_inst called with test data
pjlink.process_inst(data=chk_data)
# THEN: Data should have been sorted and saved properly
assert pjlink.source_available == chk_test, "Sources should have been sorted and saved"
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_lamp_invalid(self):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.lamp = [{'Hours': 00000, 'On': True},
{'Hours': 11111, 'On': False}]
log_data = [call('({ip}) process_lamp(): Invalid data "11111 1 22222 0 333A3 1"'.format(ip=pjlink.name))]
# WHEN: Call process_command with invalid lamp data
pjlink.process_lamp('11111 1 22222 0 333A3 1')
# THEN: lamps should not have changed
assert 2 == len(pjlink.lamp), 'Projector should have kept 2 lamps specified'
assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have stayed TRUE'
assert 00000 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been left at 00000'
assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have stayed FALSE'
assert 11111 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been left at 11111'
mock_log.warning.assert_has_calls(log_data)
@skip('Needs update to new setup')
def test_projector_process_lamp_multiple(self):
"""
Test status multiple lamp on/off and hours
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.lamp = []
# WHEN: Call process_command with invalid lamp data
pjlink.process_lamp('11111 1 22222 0 33333 1')
# THEN: Lamp should have been set with proper lamp status
assert 3 == len(pjlink.lamp), 'Projector should have 3 lamps specified'
assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have been set to TRUE'
assert 11111 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been set to 11111'
assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have been set to FALSE'
assert 22222 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been set to 22222'
assert pjlink.lamp[2]['On'] is True, 'Lamp 3 power status should have been set to TRUE'
assert 33333 == pjlink.lamp[2]['Hours'], 'Lamp 3 hours should have been set to 33333'
@skip('Needs update to new setup')
def test_projector_process_lamp_single(self):
"""
Test status lamp on/off and hours
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.lamp = []
# WHEN: Call process_command with invalid lamp data
pjlink.process_lamp('22222 1')
# THEN: Lamp should have been set with status=ON and hours=22222
assert 1 == len(pjlink.lamp), 'Projector should have only 1 lamp'
assert pjlink.lamp[0]['On'] is True, 'Lamp power status should have been set to TRUE'
assert 22222 == pjlink.lamp[0]['Hours'], 'Lamp hours should have been set to 22222'
@skip('Needs update to new setup')
def test_projector_process_lamp_single_hours_only(self):
"""
Test process lamp with 1 lamp reply hours only and no on/off status
"""
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.lamp = []
# WHEN: Process lamp command called with only hours and no lamp power state
pjlink.process_lamp("45")
# THEN: Lamp should show hours as 45 and lamp power as Unavailable
assert 1 == len(pjlink.lamp), 'There should only be 1 lamp available'
assert 45 == pjlink.lamp[0]['Hours'], 'Lamp hours should have equalled 45'
assert pjlink.lamp[0]['On'] is None, 'Lamp power should be "None"'
@skip('Needs update to new setup')
def test_projector_process_name(self):
"""
Test saving NAME data from projector
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
chk_data = "Some Name the End-User Set IN Projector"
log_debug_calls = [call('({ip}) Setting projector PJLink name to '
'"Some Name the End-User Set IN Projector"'.format(ip=pjlink.name))]
# WHEN: process_name called with test data
pjlink.process_name(data=chk_data)
# THEN: name should be set and logged
assert pjlink.pjlink_name == chk_data, 'Name test data should have been saved'
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_powr_on(self):
"""
Test status power to ON
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.power = S_STANDBY
# WHEN: process_name called with test data
pjlink.process_powr(data=PJLINK_POWR_STATUS[S_ON])
# THEN: Power should be set to ON
assert pjlink.power == S_ON, 'Power should have been set to ON'
assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called'
mock_send_command.assert_called_once_with('INST')
mock_change_status.assert_called_once_with(S_ON)
@skip('Needs update to new setup')
def test_projector_process_powr_invalid(self):
"""
Test process_powr invalid call
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.power = S_STANDBY
log_warn_calls = [call('({ip}) Unknown power response: "99"'.format(ip=pjlink.name))]
# WHEN: process_name called with test data
pjlink.process_powr(data='99')
# THEN: Power should be set to ON
assert pjlink.power == S_STANDBY, 'Power should not have changed'
mock_UpdateIcons.emit.assert_not_called()
mock_change_status.assert_not_called()
mock_send_command.assert_not_called()
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_powr_off(self):
"""
Test status power to STANDBY
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \
patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.power = S_ON
# WHEN: process_name called with test data
pjlink.process_powr(data=PJLINK_POWR_STATUS[S_STANDBY])
# THEN: Power should be set to ON
assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY'
mock_UpdateIcons.emit.assert_called_with()
mock_change_status.assert_called_with(313)
mock_send_command.assert_not_called()
@skip('Needs update to new setup')
def test_projector_process_rfil_save(self):
"""
Test saving filter type
"""
filter_model = 'Filter Type Test'
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.model_filter = None
# WHEN: Filter model is received
pjlink.process_rfil(data=filter_model)
# THEN: Filter model number should be saved
assert pjlink.model_filter == filter_model, 'Filter type should have been saved'
@skip('Needs update to new setup')
def test_projector_process_rfil_nosave(self):
"""
Test saving filter type previously saved
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.model_filter = 'Old filter type'
filter_model = 'Filter Type Test'
log_warn_calls = [call('({ip}) Filter model already set'.format(ip=pjlink.name)),
call('({ip}) Saved model: "Old filter type"'.format(ip=pjlink.name)),
call('({ip}) New model: "Filter Type Test"'.format(ip=pjlink.name))]
# WHEN: Filter model is received
pjlink.process_rfil(data=filter_model)
# THEN: Filter model number should be saved
assert pjlink.model_filter != filter_model, 'Filter type should NOT have been saved'
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_rlmp_save(self):
"""
Test saving lamp type
"""
# GIVEN: Test object
# GIVEN: Test object
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.model_lamp = None
lamp_model = 'Lamp Type Test'
# WHEN: Filter model is received
pjlink.process_rlmp(data=lamp_model)
# THEN: Filter model number should be saved
assert pjlink.model_lamp == lamp_model, 'Lamp type should have been saved'
@skip('Needs update to new setup')
def test_projector_process_rlmp_nosave(self):
"""
Test saving lamp type previously saved
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.model_lamp = 'Old lamp type'
lamp_model = 'Lamp Type Test'
log_warn_calls = [call('({ip}) Lamp model already set'.format(ip=pjlink.name)),
call('({ip}) Saved lamp: "Old lamp type"'.format(ip=pjlink.name)),
call('({ip}) New lamp: "Lamp Type Test"'.format(ip=pjlink.name))]
# WHEN: Filter model is received
pjlink.process_rlmp(data=lamp_model)
# THEN: Filter model number should be saved
assert pjlink.model_lamp != lamp_model, 'Lamp type should NOT have been saved'
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_snum_set(self):
"""
Test saving serial number from projector
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.serial_no = None
log_debug_calls = [call('({ip}) Setting projector serial number to '
'"Test Serial Number"'.format(ip=pjlink.name))]
test_number = 'Test Serial Number'
# WHEN: No serial number is set and we receive serial number command
pjlink.process_snum(data=test_number)
# THEN: Serial number should be set
assert pjlink.serial_no == test_number, 'Projector serial number should have been set'
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_snum_different(self):
"""
Test projector serial number different than saved serial number
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.serial_no = 'Previous serial number'
log_warn_calls = [call('({ip}) Projector serial number does not match '
'saved serial number'.format(ip=pjlink.name)),
call('({ip}) Saved: "Previous serial number"'.format(ip=pjlink.name)),
call('({ip}) Received: "Test Serial Number"'.format(ip=pjlink.name)),
call('({ip}) NOT saving serial number'.format(ip=pjlink.name))]
test_number = 'Test Serial Number'
# WHEN: No serial number is set and we receive serial number command
pjlink.process_snum(data=test_number)
# THEN: Serial number should be set
assert pjlink.serial_no != test_number, 'Projector serial number should NOT have been set'
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_sver(self):
"""
Test invalid software version information - too long
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.sw_version = None
pjlink.sw_version_received = None
test_data = 'Test 1 Subtest 1'
log_debug_calls = [call('({ip}) Setting projector software version to '
'"Test 1 Subtest 1"'.format(ip=pjlink.name))]
# WHEN: process_sver called with invalid data
pjlink.process_sver(data=test_data)
# THEN: Version information should not change
assert pjlink.sw_version == test_data, 'Software version should have been updated'
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_projector_process_sver_changed(self):
"""
Test invalid software version information - Received different than saved
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
test_data_old = 'Test 1 Subtest 1'
test_data_new = 'Test 1 Subtest 2'
log_warn_calls = [call('({ip}) Projector software version does not match '
'saved software version'.format(ip=pjlink.name)),
call('({ip}) Saved: "Test 1 Subtest 1"'.format(ip=pjlink.name)),
call('({ip}) Received: "Test 1 Subtest 2"'.format(ip=pjlink.name)),
call('({ip}) Updating software version'.format(ip=pjlink.name))]
pjlink.sw_version = test_data_old
# WHEN: process_sver called with invalid data
pjlink.process_sver(data=test_data_new)
# THEN: Version information should not change
assert pjlink.sw_version == test_data_new, 'Software version should have changed'
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Needs update to new setup')
def test_projector_process_sver_invalid(self):
"""
Test invalid software version information - too long
"""
test_data = 'This is a test software version line that is too long based on PJLink version 2 specs'
log_warn_calls = [call('Invalid software version - too long')]
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.sw_version = None
# WHEN: process_sver called with invalid data
pjlink.process_sver(data=test_data)
# THEN: Version information should not change
assert pjlink.sw_version is None, 'Software version should not have changed'
assert pjlink.sw_version_received is None, 'Received software version should not have changed'
mock_log.warning.assert_has_calls(log_warn_calls)

View File

@ -1,356 +0,0 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 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/>. #
##########################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase, skip
from unittest.mock import call, patch
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import PJLINK_PORT, S_CONNECTED, S_OFF, S_ON
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink, PJLinkUDP
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST_HASH, TEST_PIN, TEST_SALT
class TestPJLinkCommands(TestCase):
"""
Tests for the PJLinkCommands class part 2
"""
@skip('Needs update to new setup')
def test_projector_reset_information(self):
"""
Test reset_information() resets all information and stops timers
"""
# GIVEN: Test object
with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log:
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_debug_calls = [call('({ip}): Calling poll_timer.stop()'.format(ip=pjlink.name)),
call('({ip}): Calling socket_timer.stop()'.format(ip=pjlink.name))]
# timer and socket_timer not available until instantiation, so mock here
with patch.object(pjlink, 'socket_timer') as mock_socket_timer, \
patch.object(pjlink, 'poll_timer') as mock_timer:
pjlink.power = S_ON
pjlink.pjlink_name = 'OPENLPTEST'
pjlink.manufacturer = 'PJLINK'
pjlink.model = '1'
pjlink.shutter = True
pjlink.mute = True
pjlink.lamp = True
pjlink.fan = True
pjlink.source_available = True
pjlink.other_info = 'ANOTHER TEST'
pjlink.send_queue = True
pjlink.send_busy = True
# WHEN: reset_information() is called
pjlink.reset_information()
# THEN: All information should be reset and timers stopped
assert pjlink.power == S_OFF, 'Projector power should be OFF'
assert pjlink.pjlink_name is None, 'Projector pjlink_name should be None'
assert pjlink.manufacturer is None, 'Projector manufacturer should be None'
assert pjlink.model is None, 'Projector model should be None'
assert pjlink.shutter is None, 'Projector shutter should be None'
assert pjlink.mute is None, 'Projector shuttter should be None'
assert pjlink.lamp is None, 'Projector lamp should be None'
assert pjlink.fan is None, 'Projector fan should be None'
assert pjlink.source_available is None, 'Projector source_available should be None'
assert pjlink.source is None, 'Projector source should be None'
assert pjlink.other_info is None, 'Projector other_info should be None'
assert pjlink.send_queue == [], 'Projector send_queue should be an empty list'
assert pjlink.send_busy is False, 'Projector send_busy should be False'
assert mock_timer.stop.called is True, 'Projector timer.stop() should have been called'
assert mock_socket_timer.stop.called is True, 'Projector socket_timer.stop() should have been called'
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Needs update to new setup')
def test_process_pjlink_normal(self):
"""
Test initial connection prompt with no authentication
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, "log").start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
mock_readyRead = patch('openlp.core.projectors.pjlink.PJLink.readyRead').start()
mock_change_status = patch('openlp.core.projectors.pjlink.PJLink.change_status').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pin = None
log_check = [call('({ip}) process_pjlink(): Sending "CLSS" initial command'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with no authentication required
pjlink.process_pjlink(data="0")
# THEN: proper processing should have occured
mock_log.debug.has_calls(log_check)
mock_disconnect_from_host.assert_not_called()
assert 1 == mock_readyRead.connect.call_count, 'Should have only been called once'
mock_change_status.assert_called_once_with(S_CONNECTED)
mock_send_command.assert_called_with(cmd='CLSS', priority=True, salt=None)
@skip('Needs update to new setup')
def test_process_pjlink_authenticate(self):
"""
Test initial connection prompt with authentication
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, "log").start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
mock_readyRead = patch('openlp.core.projectors.pjlink.PJLink.readyRead').start()
mock_change_status = patch('openlp.core.projectors.pjlink.PJLink.change_status').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pin = TEST_PIN
log_check = [call('({ip}) process_pjlink(): Sending "CLSS" initial command'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with no authentication required
pjlink.process_pjlink(data='1 {salt}'.format(salt=TEST_SALT))
# THEN: proper processing should have occured
mock_log.debug.has_calls(log_check)
mock_disconnect_from_host.assert_not_called()
assert 1 == mock_readyRead.connect.call_count, 'Should have only been called once'
mock_change_status.assert_called_once_with(S_CONNECTED)
mock_send_command.assert_called_with(cmd='CLSS', priority=True, salt=TEST_HASH)
@skip('Needs update to new setup')
def test_process_pjlink_normal_pin_set_error(self):
"""
Test process_pjlinnk called with no authentication but pin is set
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pin = TEST_PIN
log_check = [call('({ip}) Normal connection but PIN set - aborting'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with invalid authentication scheme
pjlink.process_pjlink(data='0')
# THEN: Proper calls should be made
mock_log.error.assert_has_calls(log_check)
assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
mock_send_command.assert_not_called()
@skip('Needs update to new setup')
def test_process_pjlink_normal_with_salt_error(self):
"""
Test process_pjlinnk called with no authentication but pin is set
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pin = TEST_PIN
log_check = [call('({ip}) Normal connection with extra information - aborting'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with invalid authentication scheme
pjlink.process_pjlink(data='0 {salt}'.format(salt=TEST_SALT))
# THEN: Proper calls should be made
mock_log.error.assert_has_calls(log_check)
assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
mock_send_command.assert_not_called()
@skip('Needs update to new setup')
def test_process_pjlink_invalid_authentication_scheme_length_error(self):
"""
Test initial connection prompt with authentication scheme longer than 1 character
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_check = [call('({ip}) Invalid initial authentication scheme - aborting'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with invalid authentication scheme
pjlink.process_pjlink(data='01')
# THEN: socket should be closed and invalid data logged
mock_log.error.assert_has_calls(log_check)
assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
mock_send_command.assert_not_called()
@skip('Needs update to new setup')
def test_process_pjlink_invalid_authentication_data_length_error(self):
"""
Test initial connection prompt with authentication no salt
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
log_check = [call('({ip}) Authenticated connection but not enough info - aborting'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with no salt
pjlink.process_pjlink(data='1')
# THEN: socket should be closed and invalid data logged
mock_log.error.assert_has_calls(log_check)
assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
mock_send_command.assert_not_called()
@skip('Needs update to new setup')
def test_process_pjlink_authenticate_pin_not_set_error(self):
"""
Test process_pjlink authentication but pin not set
"""
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch('openlp.core.projectors.pjlink.PJLink.disconnect_from_host').start()
mock_send_command = patch('openlp.core.projectors.pjlink.PJLink.send_command').start()
pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
pjlink.pin = None
log_check = [call('({ip}) Authenticate connection but no PIN - aborting'.format(ip=pjlink.name)), ]
# WHEN: process_pjlink called with no salt
pjlink.process_pjlink(data='1 {salt}'.format(salt=TEST_SALT))
# THEN: socket should be closed and invalid data logged
mock_log.error.assert_has_calls(log_check)
assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
mock_send_command.assert_not_called()
@skip('Change to pjlink_udp.get_datagram() call')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_ackn_duplicate(self, mock_log):
"""
Test process_ackn method with multiple calls with same data
"""
# TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
# GIVEN: Test setup
pjlink = PJLink(projector=self.test_list[0])
check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
log_warn_calls = [call('(___TEST_ONE___) Host {host} already replied - '
'ignoring'.format(host=TEST1_DATA['ip']))]
log_debug_calls = [call('PJlinkCommands(args=() kwargs={})'),
call('(___TEST_ONE___) reset_information() connect status is S_NOT_CONNECTED'),
call('(___TEST_ONE___) Processing ACKN packet'),
call('(___TEST_ONE___) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
call('(___TEST_ONE___) Processing ACKN packet')]
# WHEN: process_ackn called twice with same data
pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
# THEN: pjlink_udp.ack_list should equal test_list
# NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
if pjlink.ackn_list != check_list:
# Check this way so we can print differences to stdout
print('\nackn_list: ', pjlink.ackn_list)
print('test_list: ', check_list, '\n')
assert pjlink.ackn_list == check_list
mock_log.debug.assert_has_calls(log_debug_calls)
mock_log.warning.assert_has_calls(log_warn_calls)
@skip('Change to pjlink_udp.get_datagram() call')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_ackn_multiple(self, mock_log):
"""
Test process_ackn method with multiple calls
"""
# TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
# GIVEN: Test setup
pjlink_udp = PJLinkUDP(projector_list=self.test_list)
check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT},
TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}}
log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
call('(UDP) Processing ACKN packet'),
call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
call('(UDP) Processing ACKN packet'),
call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))]
# WHEN: process_ackn called twice with different data
pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT)
# THEN: pjlink_udp.ack_list should equal test_list
# NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
if pjlink_udp.ackn_list != check_list:
# Check this way so we can print differences to stdout
print('\nackn_list: ', pjlink_udp.ackn_list)
print('test_list: ', check_list)
assert pjlink_udp.ackn_list == check_list
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Change to pjlink_udp.get_datagram() call')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_ackn_single(self, mock_log):
"""
Test process_ackn method with single call
"""
# TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
# GIVEN: Test setup
pjlink_udp = PJLinkUDP(projector_list=self.test_list)
check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
call('(UDP) Processing ACKN packet'),
call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))]
# WHEN: process_ackn called twice with different data
pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
# THEN: pjlink_udp.ack_list should equal test_list
# NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
if pjlink_udp.ackn_list != check_list:
# Check this way so we can print differences to stdout
print('\nackn_list: ', pjlink_udp.ackn_list)
print('test_list: ', check_list)
assert pjlink_udp.ackn_list == check_list
mock_log.debug.assert_has_calls(log_debug_calls)
@skip('Change to pjlink_udp.get_datagram() call')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_srch(self, mock_log):
"""
Test process_srch method
"""
# TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
# GIVEN: Test setup
log_warn_calls = [call('(UDP) SRCH packet received from {ip} - ignoring'.format(ip=TEST1_DATA['ip'])), ]
log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), ]
pjlink_udp = PJLinkUDP(projector_list=self.test_list)
# WHEN: process_srch called
pjlink_udp.process_srch(data=None, host=TEST1_DATA['ip'], port=PJLINK_PORT)
# THEN: log entries should be entered
mock_log.warning.assert_has_calls(log_warn_calls)
mock_log.debug.assert_has_calls(log_debug_calls)