This commit is contained in:
Phill Ridout 2017-12-18 19:59:35 +00:00
commit 590f44e212
37 changed files with 958 additions and 704 deletions

View File

@ -25,8 +25,6 @@
Initialization for the openlp.core.projectors modules.
"""
from openlp.core.projectors.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING
class DialogSourceStyle(object):
"""

View File

@ -144,6 +144,24 @@ PJLINK_VALID_CMD = {
}
}
# QAbstractSocketState enums converted to string
S_QSOCKET_STATE = {
0: 'QSocketState - UnconnectedState',
1: 'QSocketState - HostLookupState',
2: 'QSocketState - ConnectingState',
3: 'QSocketState - ConnectedState',
4: 'QSocketState - BoundState',
5: 'QSocketState - ListeningState (internal use only)',
6: 'QSocketState - ClosingState',
'UnconnectedState': 0,
'HostLookupState': 1,
'ConnectingState': 2,
'ConnectedState': 3,
'BoundState': 4,
'ListeningState': 5,
'ClosingState': 6
}
# Error and status codes
S_OK = E_OK = 0 # E_OK included since I sometimes forget
# Error codes. Start at 200 so we don't duplicate system error codes.

View File

@ -415,7 +415,7 @@ class ProjectorDB(Manager):
for key in projector.source_available:
item = self.get_object_filtered(ProjectorSource,
and_(ProjectorSource.code == key,
ProjectorSource.projector_id == projector.dbid))
ProjectorSource.projector_id == projector.id))
if item is None:
source_dict[key] = PJLINK_DEFAULT_CODES[key]
else:

View File

@ -58,8 +58,7 @@ from openlp.core.projectors.constants import CONNECTION_ERRORS, CR, ERROR_MSG, E
E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, E_OK, \
E_PARAMETER, E_PROJECTOR, E_SOCKET_TIMEOUT, E_UNAVAILABLE, E_UNDEFINED, PJLINK_ERRORS, PJLINK_ERST_DATA, \
PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_POWR_STATUS, PJLINK_VALID_CMD, \
STATUS_STRING, S_CONNECTED, S_CONNECTING, S_INFO, S_NETWORK_RECEIVED, S_NETWORK_SENDING, \
S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS
STATUS_STRING, S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_QSOCKET_STATE, S_STATUS
log = logging.getLogger(__name__)
log.debug('pjlink loaded')
@ -111,7 +110,7 @@ class PJLinkCommands(object):
"""
log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
super().__init__()
# Map command to function
# Map PJLink command to method
self.pjlink_functions = {
'AVMT': self.process_avmt,
'CLSS': self.process_clss,
@ -123,7 +122,9 @@ class PJLinkCommands(object):
'INST': self.process_inst,
'LAMP': self.process_lamp,
'NAME': self.process_name,
'PJLINK': self.check_login,
'PJLINK': self.process_pjlink,
# TODO: Part of check_login refactor - remove when done
# 'PJLINK': self.check_login,
'POWR': self.process_powr,
'SNUM': self.process_snum,
'SVER': self.process_sver,
@ -135,7 +136,8 @@ class PJLinkCommands(object):
"""
Initialize instance variables. Also used to reset projector-specific information to default.
"""
log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip,
state=S_QSOCKET_STATE[self.state()]))
self.fan = None # ERST
self.filter_time = None # FILT
self.lamp = None # LAMP
@ -165,6 +167,7 @@ class PJLinkCommands(object):
self.socket_timer.stop()
self.send_busy = False
self.send_queue = []
self.priority_queue = []
def process_command(self, cmd, data):
"""
@ -176,18 +179,19 @@ class PJLinkCommands(object):
log.debug('({ip}) Processing command "{cmd}" with data "{data}"'.format(ip=self.ip,
cmd=cmd,
data=data))
# Check if we have a future command not available yet
_cmd = cmd.upper()
# cmd should already be in uppercase, but data may be in mixed-case.
# Due to some replies should stay as mixed-case, validate using separate uppercase check
_data = data.upper()
if _cmd not in PJLINK_VALID_CMD:
log.error("({ip}) Ignoring command='{cmd}' (Invalid/Unknown)".format(ip=self.ip, cmd=cmd))
# Check if we have a future command not available yet
if cmd not in PJLINK_VALID_CMD:
log.error('({ip}) Ignoring command="{cmd}" (Invalid/Unknown)'.format(ip=self.ip, cmd=cmd))
return
elif _data == 'OK':
log.debug('({ip}) Command "{cmd}" returned OK'.format(ip=self.ip, cmd=cmd))
# A command returned successfully, no further processing needed
return
elif _cmd not in self.pjlink_functions:
log.warning("({ip}) Unable to process command='{cmd}' (Future option)".format(ip=self.ip, cmd=cmd))
# A command returned successfully, so do a query on command to verify status
return self.send_command(cmd=cmd)
elif cmd not in self.pjlink_functions:
log.warning('({ip}) Unable to process command="{cmd}" (Future option?)'.format(ip=self.ip, cmd=cmd))
return
elif _data in PJLINK_ERRORS:
# Oops - projector error
@ -211,12 +215,10 @@ class PJLinkCommands(object):
elif _data == PJLINK_ERRORS[E_PROJECTOR]:
# Projector/display error
self.change_status(E_PROJECTOR)
self.receive_data_signal()
return
# Command checks already passed
log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd))
self.receive_data_signal()
self.pjlink_functions[_cmd](data)
self.pjlink_functions[cmd](data)
def process_avmt(self, data):
"""
@ -259,19 +261,19 @@ class PJLinkCommands(object):
# : Received: '%1CLSS=Class 1' (Optoma)
# : Received: '%1CLSS=Version1' (BenQ)
if len(data) > 1:
log.warning("({ip}) Non-standard CLSS reply: '{data}'".format(ip=self.ip, data=data))
log.warning('({ip}) Non-standard CLSS reply: "{data}"'.format(ip=self.ip, data=data))
# Due to stupid projectors not following standards (Optoma, BenQ comes to mind),
# AND the different responses that can be received, the semi-permanent way to
# fix the class reply is to just remove all non-digit characters.
try:
clss = re.findall('\d', data)[0] # Should only be the first match
except IndexError:
log.error("({ip}) No numbers found in class version reply '{data}' - "
"defaulting to class '1'".format(ip=self.ip, data=data))
log.error('({ip}) No numbers found in class version reply "{data}" - '
'defaulting to class "1"'.format(ip=self.ip, data=data))
clss = '1'
elif not data.isdigit():
log.error("({ip}) NAN clss version reply '{data}' - "
"defaulting to class '1'".format(ip=self.ip, data=data))
log.error('({ip}) NAN CLSS version reply "{data}" - '
'defaulting to class "1"'.format(ip=self.ip, data=data))
clss = '1'
else:
clss = data
@ -289,7 +291,7 @@ class PJLinkCommands(object):
"""
if len(data) != PJLINK_ERST_DATA['DATA_LENGTH']:
count = PJLINK_ERST_DATA['DATA_LENGTH']
log.warning("{ip}) Invalid error status response '{data}': length != {count}".format(ip=self.ip,
log.warning('{ip}) Invalid error status response "{data}": length != {count}'.format(ip=self.ip,
data=data,
count=count))
return
@ -297,7 +299,7 @@ class PJLinkCommands(object):
datacheck = int(data)
except ValueError:
# Bad data - ignore
log.warning("({ip}) Invalid error status response '{data}'".format(ip=self.ip, data=data))
log.warning('({ip}) Invalid error status response "{data}"'.format(ip=self.ip, data=data))
return
if datacheck == 0:
self.projector_errors = None
@ -430,6 +432,51 @@ class PJLinkCommands(object):
log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name))
return
def process_pjlink(self, data):
"""
Process initial socket connection to terminal.
:param data: Initial packet with authentication scheme
"""
log.debug('({ip}) Processing PJLINK command'.format(ip=self.ip))
chk = data.split(' ')
if len(chk[0]) != 1:
# Invalid - after splitting, first field should be 1 character, either '0' or '1' only
log.error('({ip}) Invalid initial authentication scheme - aborting'.format(ip=self.ip))
return self.disconnect_from_host()
elif chk[0] == '0':
# Normal connection no authentication
if len(chk) > 1:
# Invalid data - there should be nothing after a normal authentication scheme
log.error('({ip}) Normal connection with extra information - aborting'.format(ip=self.ip))
return self.disconnect_from_host()
elif self.pin:
log.error('({ip}) Normal connection but PIN set - aborting'.format(ip=self.ip))
return self.disconnect_from_host()
else:
data_hash = None
elif chk[0] == '1':
if len(chk) < 2:
# Not enough information for authenticated connection
log.error('({ip}) Authenticated connection but not enough info - aborting'.format(ip=self.ip))
return self.disconnect_from_host()
elif not self.pin:
log.error('({ip}) Authenticate connection but no PIN - aborting'.format(ip=self.ip))
return self.disconnect_from_host()
else:
data_hash = str(qmd5_hash(salt=chk[1].encode('utf-8'), data=self.pin.encode('utf-8')),
encoding='ascii')
# Passed basic checks, so start connection
self.readyRead.connect(self.get_socket)
if not self.no_poll:
log.debug('({ip}) process_pjlink(): Starting timer'.format(ip=self.ip))
self.timer.setInterval(2000) # Set 2 seconds for initial information
self.timer.start()
self.change_status(S_CONNECTED)
log.debug('({ip}) process_pjlink(): Sending "CLSS" initial command'.format(ip=self.ip))
# Since this is an initial connection, make it a priority just in case
return self.send_command(cmd="CLSS", salt=data_hash, priority=True)
def process_powr(self, data):
"""
Power status. See PJLink specification for format.
@ -450,7 +497,7 @@ class PJLinkCommands(object):
self.send_command('INST')
else:
# Log unknown status response
log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
log.warning('({ip}) Unknown power response: "{data}"'.format(ip=self.ip, data=data))
return
def process_rfil(self, data):
@ -460,9 +507,9 @@ class PJLinkCommands(object):
if self.model_filter is None:
self.model_filter = data
else:
log.warning("({ip}) Filter model already set".format(ip=self.ip))
log.warning("({ip}) Saved model: '{old}'".format(ip=self.ip, old=self.model_filter))
log.warning("({ip}) New model: '{new}'".format(ip=self.ip, new=data))
log.warning('({ip}) Filter model already set'.format(ip=self.ip))
log.warning('({ip}) Saved model: "{old}"'.format(ip=self.ip, old=self.model_filter))
log.warning('({ip}) New model: "{new}"'.format(ip=self.ip, new=data))
def process_rlmp(self, data):
"""
@ -471,9 +518,9 @@ class PJLinkCommands(object):
if self.model_lamp is None:
self.model_lamp = data
else:
log.warning("({ip}) Lamp model already set".format(ip=self.ip))
log.warning("({ip}) Saved lamp: '{old}'".format(ip=self.ip, old=self.model_lamp))
log.warning("({ip}) New lamp: '{new}'".format(ip=self.ip, new=data))
log.warning('({ip}) Lamp model already set'.format(ip=self.ip))
log.warning('({ip}) Saved lamp: "{old}"'.format(ip=self.ip, old=self.model_lamp))
log.warning('({ip}) New lamp: "{new}"'.format(ip=self.ip, new=data))
def process_snum(self, data):
"""
@ -482,16 +529,16 @@ class PJLinkCommands(object):
:param data: Serial number from projector.
"""
if self.serial_no is None:
log.debug("({ip}) Setting projector serial number to '{data}'".format(ip=self.ip, data=data))
log.debug('({ip}) Setting projector serial number to "{data}"'.format(ip=self.ip, data=data))
self.serial_no = data
self.db_update = False
else:
# Compare serial numbers and see if we got the same projector
if self.serial_no != data:
log.warning("({ip}) Projector serial number does not match saved serial number".format(ip=self.ip))
log.warning("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.serial_no))
log.warning("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
log.warning("({ip}) NOT saving serial number".format(ip=self.ip))
log.warning('({ip}) Projector serial number does not match saved serial number'.format(ip=self.ip))
log.warning('({ip}) Saved: "{old}"'.format(ip=self.ip, old=self.serial_no))
log.warning('({ip}) Received: "{new}"'.format(ip=self.ip, new=data))
log.warning('({ip}) NOT saving serial number'.format(ip=self.ip))
self.serial_no_received = data
def process_sver(self, data):
@ -500,20 +547,20 @@ class PJLinkCommands(object):
"""
if len(data) > 32:
# Defined in specs max version is 32 characters
log.warning("Invalid software version - too long")
log.warning('Invalid software version - too long')
return
elif self.sw_version is None:
log.debug("({ip}) Setting projector software version to '{data}'".format(ip=self.ip, data=data))
log.debug('({ip}) Setting projector software version to "{data}"'.format(ip=self.ip, data=data))
self.sw_version = data
self.db_update = True
else:
# Compare software version and see if we got the same projector
if self.serial_no != data:
log.warning("({ip}) Projector software version does not match saved "
"software version".format(ip=self.ip))
log.warning("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.sw_version))
log.warning("({ip}) Received: '{new}'".format(ip=self.ip, new=data))
log.warning("({ip}) Saving new serial number as sw_version_received".format(ip=self.ip))
log.warning('({ip}) Projector software version does not match saved '
'software version'.format(ip=self.ip))
log.warning('({ip}) Saved: "{old}"'.format(ip=self.ip, old=self.sw_version))
log.warning('({ip}) Received: "{new}"'.format(ip=self.ip, new=data))
log.warning('({ip}) Saving new serial number as sw_version_received'.format(ip=self.ip))
self.sw_version_received = data
@ -540,9 +587,9 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
:param poll_time: Time (in seconds) to poll connected projector
:param socket_timeout: Time (in seconds) to abort the connection if no response
"""
log.debug('PJlink(projector={projector}, args={args} kwargs={kwargs})'.format(projector=projector,
args=args,
kwargs=kwargs))
log.debug('PJlink(projector="{projector}", args="{args}" kwargs="{kwargs}")'.format(projector=projector,
args=args,
kwargs=kwargs))
super().__init__()
self.entry = projector
self.ip = self.entry.ip
@ -573,6 +620,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.widget = None # QListBox entry
self.timer = None # Timer that calls the poll_loop
self.send_queue = []
self.priority_queue = []
self.send_busy = False
# Socket timer for some possible brain-dead projectors or network cable pulled
self.socket_timer = None
@ -586,6 +634,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.connected.connect(self.check_login)
self.disconnected.connect(self.disconnect_from_host)
self.error.connect(self.get_error)
self.projectorReceivedData.connect(self._send_command)
def thread_stopped(self):
"""
@ -608,6 +657,10 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.projectorReceivedData.disconnect(self._send_command)
except TypeError:
pass
try:
self.readyRead.disconnect(self.get_socket) # Set in process_pjlink
except TypeError:
pass
self.disconnect_from_host()
self.deleteLater()
self.i_am_running = False
@ -625,10 +678,10 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
Retrieve information from projector that changes.
Normally called by timer().
"""
if self.state() != self.ConnectedState:
log.warning("({ip}) poll_loop(): Not connected - returning".format(ip=self.ip))
if self.state() != S_QSOCKET_STATE['ConnectedState']:
log.warning('({ip}) poll_loop(): Not connected - returning'.format(ip=self.ip))
return
log.debug('({ip}) Updating projector status'.format(ip=self.ip))
log.debug('({ip}) poll_loop(): Updating projector status'.format(ip=self.ip))
# Reset timer in case we were called from a set command
if self.timer.interval() < self.poll_time:
# Reset timer to 5 seconds
@ -640,28 +693,28 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
if self.pjlink_class == '2':
check_list.extend(['FILT', 'FREZ'])
for command in check_list:
self.send_command(command, queue=True)
self.send_command(command)
# The following commands do not change, so only check them once
if self.power == S_ON and self.source_available is None:
self.send_command('INST', queue=True)
self.send_command('INST')
if self.other_info is None:
self.send_command('INFO', queue=True)
self.send_command('INFO')
if self.manufacturer is None:
self.send_command('INF1', queue=True)
self.send_command('INF1')
if self.model is None:
self.send_command('INF2', queue=True)
self.send_command('INF2')
if self.pjlink_name is None:
self.send_command('NAME', queue=True)
self.send_command('NAME')
if self.pjlink_class == '2':
# Class 2 specific checks
if self.serial_no is None:
self.send_command('SNUM', queue=True)
self.send_command('SNUM')
if self.sw_version is None:
self.send_command('SVER', queue=True)
self.send_command('SVER')
if self.model_filter is None:
self.send_command('RFIL', queue=True)
self.send_command('RFIL')
if self.model_lamp is None:
self.send_command('RLMP', queue=True)
self.send_command('RLMP')
def _get_status(self, status):
"""
@ -713,14 +766,12 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
code=status_code,
message=status_message if msg is None else msg))
self.changeStatus.emit(self.ip, status, message)
self.projectorUpdateIcons.emit()
@QtCore.pyqtSlot()
def check_login(self, data=None):
"""
Processes the initial connection and authentication (if needed).
Starts poll timer if connection is established.
NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.
Processes the initial connection and convert to a PJLink packet if valid initial connection
:param data: Optional data if called from another routine
"""
@ -733,12 +784,12 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.change_status(E_SOCKET_TIMEOUT)
return
read = self.readLine(self.max_size)
self.readLine(self.max_size) # Clean out the trailing \r\n
self.readLine(self.max_size) # Clean out any trailing whitespace
if read is None:
log.warning('({ip}) read is None - socket error?'.format(ip=self.ip))
return
elif len(read) < 8:
log.warning('({ip}) Not enough data read)'.format(ip=self.ip))
log.warning('({ip}) Not enough data read - skipping'.format(ip=self.ip))
return
data = decode(read, 'utf-8')
# Possibility of extraneous data on input when reading.
@ -750,9 +801,16 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
# PJLink initial login will be:
# 'PJLink 0' - Unauthenticated login - no extra steps required.
# 'PJLink 1 XXXXXX' Authenticated login - extra processing required.
if not data.upper().startswith('PJLINK'):
# Invalid response
if not data.startswith('PJLINK'):
# Invalid initial packet - close socket
log.error('({ip}) Invalid initial packet received - closing socket'.format(ip=self.ip))
return self.disconnect_from_host()
log.debug('({ip}) check_login(): Formatting initial connection prompt to PJLink packet'.format(ip=self.ip))
return self.get_data('{start}{clss}{data}'.format(start=PJLINK_PREFIX,
clss='1',
data=data.replace(' ', '=', 1)).encode('utf-8'))
# TODO: The below is replaced by process_pjlink() - remove when working properly
"""
if '=' in data:
# Processing a login reply
data_check = data.strip().split('=')
@ -801,18 +859,19 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
log.debug('({ip}) Starting timer'.format(ip=self.ip))
self.timer.setInterval(2000) # Set 2 seconds for initial information
self.timer.start()
"""
def _trash_buffer(self, msg=None):
"""
Clean out extraneous stuff in the buffer.
"""
log.warning("({ip}) {message}".format(ip=self.ip, message='Invalid packet' if msg is None else msg))
log.warning('({ip}) {message}'.format(ip=self.ip, message='Invalid packet' if msg is None else msg))
self.send_busy = False
trash_count = 0
while self.bytesAvailable() > 0:
trash = self.read(self.max_size)
trash_count += len(trash)
log.debug("({ip}) Finished cleaning buffer - {count} bytes dropped".format(ip=self.ip,
log.debug('({ip}) Finished cleaning buffer - {count} bytes dropped'.format(ip=self.ip,
count=trash_count))
return
@ -824,7 +883,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
:param data: Data to process. buffer must be formatted as a proper PJLink packet.
:param ip: Destination IP for buffer.
"""
log.debug("({ip}) get_buffer(data='{buff}' ip='{ip_in}'".format(ip=self.ip, buff=data, ip_in=ip))
log.debug('({ip}) get_buffer(data="{buff}" ip="{ip_in}"'.format(ip=self.ip, buff=data, ip_in=ip))
if ip is None:
log.debug("({ip}) get_buffer() Don't know who data is for - exiting".format(ip=self.ip))
return
@ -842,38 +901,52 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
return
# Although we have a packet length limit, go ahead and use a larger buffer
read = self.readLine(1024)
log.debug("({ip}) get_socket(): '{buff}'".format(ip=self.ip, buff=read))
log.debug('({ip}) get_socket(): "{buff}"'.format(ip=self.ip, buff=read))
if read == -1:
# No data available
log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.ip))
return self.receive_data_signal()
self.socket_timer.stop()
return self.get_data(buff=read, ip=self.ip)
self.get_data(buff=read, ip=self.ip)
return self.receive_data_signal()
def get_data(self, buff, ip):
def get_data(self, buff, ip=None):
"""
Process received data
:param buff: Data to process.
:param ip: (optional) Destination IP.
"""
log.debug("({ip}) get_data(ip='{ip_in}' buffer='{buff}'".format(ip=self.ip, ip_in=ip, buff=buff))
# Since "self" is not available to options and the "ip" keyword is a "maybe I'll use in the future",
# set to default here
if ip is None:
ip = self.ip
log.debug('({ip}) get_data(ip="{ip_in}" buffer="{buff}"'.format(ip=self.ip, ip_in=ip, buff=buff))
# NOTE: Class2 has changed to some values being UTF-8
data_in = decode(buff, 'utf-8')
data = data_in.strip()
if (len(data) < 7) or (not data.startswith(PJLINK_PREFIX)):
return self._trash_buffer(msg='get_data(): Invalid packet - length or prefix')
# Initial packet checks
if (len(data) < 7):
return self._trash_buffer(msg='get_data(): Invalid packet - length')
elif len(data) > self.max_size:
return self._trash_buffer(msg='get_data(): Invalid packet - too long')
elif not data.startswith(PJLINK_PREFIX):
return self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
elif '=' not in data:
return self._trash_buffer(msg='get_data(): Invalid packet does not have equal')
return self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
header, data = data.split('=')
# At this point, the header should contain:
# "PVCCCC"
# Where:
# P = PJLINK_PREFIX
# V = PJLink class or version
# C = PJLink command
try:
version, cmd = header[1], header[2:]
version, cmd = header[1], header[2:].upper()
except ValueError as e:
self.change_status(E_INVALID_DATA)
log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in))
return self._trash_buffer('get_data(): Expected header + command + data')
if cmd not in PJLINK_VALID_CMD:
log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
@ -881,6 +954,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
if int(self.pjlink_class) < int(version):
log.warning('({ip}) get_data(): Projector returned class reply higher '
'than projector stated class'.format(ip=self.ip))
self.send_busy = False
return self.process_command(cmd, data)
@QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError)
@ -910,19 +984,18 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
self.reset_information()
return
def send_command(self, cmd, opts='?', salt=None, queue=False):
def send_command(self, cmd, opts='?', salt=None, priority=False):
"""
Add command to output queue if not already in queue.
:param cmd: Command to send
:param opts: Command option (if any) - defaults to '?' (get information)
:param salt: Optional salt for md5 hash initial authentication
:param queue: Option to force add to queue rather than sending directly
:param priority: Option to send packet now rather than queue it up
"""
if self.state() != self.ConnectedState:
log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
self.send_queue = []
return
return self.reset_information()
if cmd not in PJLINK_VALID_CMD:
log.error('({ip}) send_command(): Invalid command requested - ignoring.'.format(ip=self.ip))
return
@ -939,28 +1012,26 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
header = PJLINK_HEADER.format(linkclass=cmd_ver[0])
else:
# NOTE: Once we get to version 3 then think about looping
log.error('({ip}): send_command(): PJLink class check issue? aborting'.format(ip=self.ip))
log.error('({ip}): send_command(): PJLink class check issue? Aborting'.format(ip=self.ip))
return
out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt,
header=header,
command=cmd,
options=opts,
suffix=CR)
if out in self.send_queue:
# Already there, so don't add
log.debug('({ip}) send_command(out="{data}") Already in queue - skipping'.format(ip=self.ip,
data=out.strip()))
elif not queue and len(self.send_queue) == 0:
# Nothing waiting to send, so just send it
log.debug('({ip}) send_command(out="{data}") Sending data'.format(ip=self.ip, data=out.strip()))
return self._send_command(data=out)
if out in self.priority_queue:
log.debug('({ip}) send_command(): Already in priority queue - skipping'.format(ip=self.ip))
elif out in self.send_queue:
log.debug('({ip}) send_command(): Already in normal queue - skipping'.format(ip=self.ip))
else:
log.debug('({ip}) send_command(out="{data}") adding to queue'.format(ip=self.ip, data=out.strip()))
self.send_queue.append(out)
self.projectorReceivedData.emit()
log.debug('({ip}) send_command(): send_busy is {data}'.format(ip=self.ip, data=self.send_busy))
if not self.send_busy:
log.debug('({ip}) send_command() calling _send_string()'.format(ip=self.ip))
if priority:
log.debug('({ip}) send_command(): Adding to priority queue'.format(ip=self.ip))
self.priority_queue.append(out)
else:
log.debug('({ip}) send_command(): Adding to normal queue'.format(ip=self.ip))
self.send_queue.append(out)
if self.priority_queue or self.send_queue:
# May be some initial connection setup so make sure we send data
self._send_command()
@QtCore.pyqtSlot()
@ -971,43 +1042,53 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
:param data: Immediate data to send
:param utf8: Send as UTF-8 string otherwise send as ASCII string
"""
log.debug('({ip}) _send_string()'.format(ip=self.ip))
log.debug('({ip}) _send_string(): Connection status: {data}'.format(ip=self.ip, data=self.state()))
# Funny looking data check, but it's a quick check for data=None
log.debug('({ip}) _send_command(data="{data}")'.format(ip=self.ip, data=data.strip() if data else data))
log.debug('({ip}) _send_command(): Connection status: {data}'.format(ip=self.ip,
data=S_QSOCKET_STATE[self.state()]))
if self.state() != self.ConnectedState:
log.debug('({ip}) _send_string() Not connected - abort'.format(ip=self.ip))
self.send_queue = []
log.debug('({ip}) _send_command() Not connected - abort'.format(ip=self.ip))
self.send_busy = False
return
return self.disconnect_from_host()
if data and data not in self.priority_queue:
log.debug('({ip}) _send_command(): Priority packet - adding to priority queue'.format(ip=self.ip))
self.priority_queue.append(data)
if self.send_busy:
# Still waiting for response from last command sent
log.debug('({ip}) _send_command(): Still busy, returning'.format(ip=self.ip))
log.debug('({ip}) _send_command(): Priority queue = {data}'.format(ip=self.ip, data=self.priority_queue))
log.debug('({ip}) _send_command(): Normal queue = {data}'.format(ip=self.ip, data=self.send_queue))
return
if data is not None:
out = data
log.debug('({ip}) _send_string(data="{data}")'.format(ip=self.ip, data=out.strip()))
if len(self.priority_queue) != 0:
out = self.priority_queue.pop(0)
log.debug('({ip}) _send_command(): Getting priority queued packet'.format(ip=self.ip))
elif len(self.send_queue) != 0:
out = self.send_queue.pop(0)
log.debug('({ip}) _send_string(queued data="{data}"%s)'.format(ip=self.ip, data=out.strip()))
log.debug('({ip}) _send_command(): Getting normal queued packet'.format(ip=self.ip))
else:
# No data to send
log.debug('({ip}) _send_string(): No data to send'.format(ip=self.ip))
log.debug('({ip}) _send_command(): No data to send'.format(ip=self.ip))
self.send_busy = False
return
self.send_busy = True
log.debug('({ip}) _send_string(): Sending "{data}"'.format(ip=self.ip, data=out.strip()))
log.debug('({ip}) _send_string(): Queue = {data}'.format(ip=self.ip, data=self.send_queue))
log.debug('({ip}) _send_command(): Sending "{data}"'.format(ip=self.ip, data=out.strip()))
self.socket_timer.start()
sent = self.write(out.encode('{string_encoding}'.format(string_encoding='utf-8' if utf8 else 'ascii')))
self.waitForBytesWritten(2000) # 2 seconds should be enough
if sent == -1:
# Network error?
log.warning("({ip}) _send_command(): -1 received".format(ip=self.ip))
log.warning('({ip}) _send_command(): -1 received - disconnecting from host'.format(ip=self.ip))
self.change_status(E_NETWORK,
translate('OpenLP.PJLink', 'Error while sending data to projector'))
self.disconnect_from_host()
def connect_to_host(self):
"""
Initiate connection to projector.
"""
log.debug('{ip}) connect_to_host(): Starting connection'.format(ip=self.ip))
if self.state() == self.ConnectedState:
log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
return
@ -1023,22 +1104,19 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands):
if abort:
log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
else:
log.warning('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
self.reset_information()
log.warning('({ip}) disconnect_from_host(): Not connected'.format(ip=self.ip))
self.disconnectFromHost()
try:
self.readyRead.disconnect(self.get_socket)
except TypeError:
pass
log.debug('({ip}) disconnect_from_host() '
'Current status {data}'.format(ip=self.ip, data=self._get_status(self.status_connect)[0]))
if abort:
self.change_status(E_NOT_CONNECTED)
else:
log.debug('({ip}) disconnect_from_host() '
'Current status {data}'.format(ip=self.ip, data=self._get_status(self.status_connect)[0]))
if self.status_connect != E_NOT_CONNECTED:
self.change_status(S_NOT_CONNECTED)
self.change_status(S_NOT_CONNECTED)
self.reset_information()
self.projectorUpdateIcons.emit()
def get_av_mute_status(self):
"""

View File

@ -42,8 +42,8 @@ class TestApiError(TestCase):
raise NotFound()
# THEN: we get an error and a status
self.assertEquals('Not Found', context.exception.message, 'A Not Found exception should be thrown')
self.assertEquals(404, context.exception.status, 'A 404 status should be thrown')
assert 'Not Found' == context.exception.message, 'A Not Found exception should be thrown'
assert 404 == context.exception.status, 'A 404 status should be thrown'
def test_server_error(self):
"""
@ -55,5 +55,5 @@ class TestApiError(TestCase):
raise ServerError()
# THEN: we get an error and a status
self.assertEquals('Server Error', context.exception.message, 'A Not Found exception should be thrown')
self.assertEquals(500, context.exception.status, 'A 500 status should be thrown')
assert'Server Error' == context.exception.message, 'A Not Found exception should be thrown'
assert 500 == context.exception.status, 'A 500 status should be thrown'

View File

@ -53,8 +53,8 @@ class TestHttpServer(TestCase):
HttpServer()
# THEN: the api environment should have been created
self.assertEquals(1, mock_qthread.call_count, 'The qthread should have been called once')
self.assertEquals(1, mock_thread.call_count, 'The http thread should have been called once')
assert mock_qthread.call_count == 1, 'The qthread should have been called once'
assert mock_thread.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.http.server.HttpWorker')
@patch('openlp.core.api.http.server.QtCore.QThread')
@ -68,5 +68,5 @@ class TestHttpServer(TestCase):
HttpServer()
# THEN: the api environment should have been created
self.assertEquals(0, mock_qthread.call_count, 'The qthread should not have have been called')
self.assertEquals(0, mock_thread.call_count, 'The http thread should not have been called')
assert mock_qthread.call_count == 0, 'The qthread should not have have been called'
assert mock_thread.call_count == 0, 'The http thread should not have been called'

View File

@ -61,7 +61,7 @@ class TestRouting(TestCase):
application.dispatch(rqst)
# THEN: the not found returned
self.assertEqual(context.exception.args[0], 'Not Found', 'URL not found in dispatcher')
assert context.exception.args[0] == 'Not Found', 'URL not found in dispatcher'
# WHEN: when the URL is correct and dispatch called
rqst = MagicMock()
@ -69,8 +69,8 @@ class TestRouting(TestCase):
rqst.method = 'GET'
application.dispatch(rqst)
# THEN: the not found id called
self.assertEqual(1, application.route_map['^\\/test\\/image$']['GET'].call_count,
'main_index function should have been called')
assert 1 == application.route_map['^\\/test\\/image$']['GET'].call_count, \
'main_index function should have been called'
@test_endpoint.route('image')

View File

@ -58,4 +58,4 @@ class TestRemoteDeploy(TestCase):
deploy_zipfile(self.app_root_path, 'site.zip')
# THEN: test if www directory has been created
self.assertTrue((self.app_root_path / 'www').is_dir(), 'We should have a www directory')
assert (self.app_root_path / 'www').is_dir(), 'We should have a www directory'

View File

@ -81,8 +81,8 @@ class TestApiTab(TestCase, TestMixin):
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(ZERO_URL)
# THEN: the default ip address will be returned
self.assertTrue(re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address),
'The return value should be a valid ip address')
assert re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), \
'The return value should be a valid ip address'
def test_get_ip_address_with_ip(self):
"""
@ -93,7 +93,7 @@ class TestApiTab(TestCase, TestMixin):
# WHEN: the default ip address is given
ip_address = self.form.get_ip_address(given_ip)
# THEN: the default ip address will be returned
self.assertEqual(ip_address, given_ip, 'The return value should be %s' % given_ip)
assert ip_address == given_ip, 'The return value should be %s' % given_ip
def test_set_urls(self):
"""
@ -104,12 +104,11 @@ class TestApiTab(TestCase, TestMixin):
# WHEN: the urls are generated
self.form.set_urls()
# THEN: the following links are returned
self.assertEqual(self.form.remote_url.text(),
"<a href=\"http://192.168.1.1:4316/\">http://192.168.1.1:4316/</a>",
'The return value should be a fully formed link')
self.assertEqual(self.form.stage_url.text(),
"<a href=\"http://192.168.1.1:4316/stage\">http://192.168.1.1:4316/stage</a>",
'The return value should be a fully formed stage link')
self.assertEqual(self.form.live_url.text(),
"<a href=\"http://192.168.1.1:4316/main\">http://192.168.1.1:4316/main</a>",
'The return value should be a fully formed main link')
assert self.form.remote_url.text() == "<a href=\"http://192.168.1.1:4316/\">http://192.168.1.1:4316/</a>", \
'The return value should be a fully formed link'
assert self.form.stage_url.text() == \
"<a href=\"http://192.168.1.1:4316/stage\">http://192.168.1.1:4316/stage</a>", \
'The return value should be a fully formed stage link'
assert self.form.live_url.text() == \
"<a href=\"http://192.168.1.1:4316/main\">http://192.168.1.1:4316/main</a>", \
'The return value should be a fully formed main link'

View File

@ -74,8 +74,8 @@ class TestWSServer(TestCase, TestMixin):
WebSocketServer()
# THEN: the api environment should have been created
self.assertEquals(1, mock_qthread.call_count, 'The qthread should have been called once')
self.assertEquals(1, mock_worker.call_count, 'The http thread should have been called once')
assert mock_qthread.call_count == 1, 'The qthread should have been called once'
assert mock_worker.call_count == 1, 'The http thread should have been called once'
@patch('openlp.core.api.websockets.WebSocketWorker')
@patch('openlp.core.api.websockets.QtCore.QThread')
@ -89,8 +89,8 @@ class TestWSServer(TestCase, TestMixin):
WebSocketServer()
# THEN: the api environment should have been created
self.assertEquals(0, mock_qthread.call_count, 'The qthread should not have been called')
self.assertEquals(0, mock_worker.call_count, 'The http thread should not have been called')
assert mock_qthread.call_count == 0, 'The qthread should not have been called'
assert mock_worker.call_count == 0, 'The http thread should not have been called'
def test_main_poll(self):
"""
@ -102,8 +102,7 @@ class TestWSServer(TestCase, TestMixin):
Registry().register('live_controller', mocked_live_controller)
# THEN: the live json should be generated
main_json = self.poll.main_poll()
self.assertEquals(b'{"results": {"slide_count": 5}}', main_json,
'The return value should match the defined json')
assert b'{"results": {"slide_count": 5}}' == main_json, 'The return value should match the defined json'
def test_poll(self):
"""
@ -130,13 +129,13 @@ class TestWSServer(TestCase, TestMixin):
mocked_is_chords_active.return_value = True
poll_json = self.poll.poll()
# THEN: the live json should be generated and match expected results
self.assertTrue(poll_json['results']['blank'], 'The blank return value should be True')
self.assertFalse(poll_json['results']['theme'], 'The theme return value should be False')
self.assertFalse(poll_json['results']['display'], 'The display return value should be False')
self.assertFalse(poll_json['results']['isSecure'], 'The isSecure return value should be False')
self.assertFalse(poll_json['results']['isAuthorised'], 'The isAuthorised return value should be False')
self.assertTrue(poll_json['results']['twelve'], 'The twelve return value should be False')
self.assertEquals(poll_json['results']['version'], 3, 'The version return value should be 3')
self.assertEquals(poll_json['results']['slide'], 5, 'The slide return value should be 5')
self.assertEquals(poll_json['results']['service'], 21, 'The version return value should be 21')
self.assertEquals(poll_json['results']['item'], '23-34-45', 'The item return value should match 23-34-45')
assert poll_json['results']['blank'] is True, 'The blank return value should be True'
assert poll_json['results']['theme'] is False, 'The theme return value should be False'
assert poll_json['results']['display'] is False, 'The display return value should be False'
assert poll_json['results']['isSecure'] is False, 'The isSecure return value should be False'
assert poll_json['results']['isAuthorised'] is False, 'The isAuthorised return value should be False'
assert poll_json['results']['twelve'] is True, 'The twelve return value should be True'
assert poll_json['results']['version'] == 3, 'The version return value should be 3'
assert poll_json['results']['slide'] == 5, 'The slide return value should be 5'
assert poll_json['results']['service'] == 21, 'The version return value should be 21'
assert poll_json['results']['item'] == '23-34-45', 'The item return value should match 23-34-45'

View File

@ -59,8 +59,8 @@ class TestCategoryActionList(TestCase):
self.list.append(self.action1)
# THEN: The actions should (not) be in the list.
self.assertTrue(self.action1 in self.list)
self.assertFalse(self.action2 in self.list)
assert self.action1 in self.list
assert self.action2 not in self.list
def test_len(self):
"""
@ -69,14 +69,14 @@ class TestCategoryActionList(TestCase):
# GIVEN: The list.
# WHEN: Do nothing.
# THEN: Check the length.
self.assertEqual(len(self.list), 0, "The length should be 0.")
assert len(self.list) == 0, "The length should be 0."
# GIVEN: The list.
# WHEN: Append an action.
self.list.append(self.action1)
# THEN: Check the length.
self.assertEqual(len(self.list), 1, "The length should be 1.")
assert len(self.list) == 1, "The length should be 1."
def test_append(self):
"""
@ -88,10 +88,10 @@ class TestCategoryActionList(TestCase):
self.list.append(self.action2)
# THEN: Check if the actions are in the list and check if they have the correct weights.
self.assertTrue(self.action1 in self.list)
self.assertTrue(self.action2 in self.list)
self.assertEqual(self.list.actions[0], (0, self.action1))
self.assertEqual(self.list.actions[1], (1, self.action2))
assert self.action1 in self.list
assert self.action2 in self.list
assert self.list.actions[0] == (0, self.action1)
assert self.list.actions[1] == (1, self.action2)
def test_add(self):
"""
@ -106,11 +106,11 @@ class TestCategoryActionList(TestCase):
self.list.add(self.action2, action2_weight)
# THEN: Check if they were added and have the specified weights.
self.assertTrue(self.action1 in self.list)
self.assertTrue(self.action2 in self.list)
assert self.action1 in self.list
assert self.action2 in self.list
# Now check if action1 is second and action2 is first (due to their weights).
self.assertEqual(self.list.actions[0], (41, self.action2))
self.assertEqual(self.list.actions[1], (42, self.action1))
assert self.list.actions[0] == (41, self.action2)
assert self.list.actions[1] == (42, self.action1)
def test_iterator(self):
"""
@ -121,11 +121,11 @@ class TestCategoryActionList(TestCase):
self.list.add(self.action2)
# WHEN: Iterating over the list
list = [a for a in self.list]
local_list = [a for a in self.list]
# THEN: Make sure they are returned in correct order
self.assertEquals(len(self.list), 2)
self.assertIs(list[0], self.action1)
self.assertIs(list[1], self.action2)
assert len(self.list) == 2
assert local_list[0] is self.action1
assert local_list[1] is self.action2
def test_remove(self):
"""
@ -138,7 +138,7 @@ class TestCategoryActionList(TestCase):
self.list.remove(self.action1)
# THEN: Now the element should not be in the list anymore.
self.assertFalse(self.action1 in self.list)
assert self.action1 not in self.list
# THEN: Check if an exception is raised when trying to remove a not present action.
self.assertRaises(ValueError, self.list.remove, self.action2)

View File

@ -48,7 +48,7 @@ class TestCommonFunctions(TestCase):
extension_loader('glob', ['file2.py', 'file3.py'])
# THEN: `extension_loader` should not try to import any files
self.assertFalse(mocked_import_module.called)
assert mocked_import_module.called is False
def test_extension_loader_files_found(self):
"""
@ -69,7 +69,8 @@ class TestCommonFunctions(TestCase):
# THEN: `extension_loader` should only try to import the files that are matched by the blob, excluding the
# files listed in the `excluded_files` argument
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'), call('openlp.import_dir.file4')])
mocked_import_module.assert_has_calls([call('openlp.import_dir.file1'),
call('openlp.import_dir.file4')])
def test_extension_loader_import_error(self):
"""
@ -87,7 +88,7 @@ class TestCommonFunctions(TestCase):
extension_loader('glob')
# THEN: The `ImportError` should be caught and logged
self.assertTrue(mocked_logger.warning.called)
assert mocked_logger.warning.called
def test_extension_loader_os_error(self):
"""
@ -105,7 +106,7 @@ class TestCommonFunctions(TestCase):
extension_loader('glob')
# THEN: The `OSError` should be caught and logged
self.assertTrue(mocked_logger.warning.called)
assert mocked_logger.warning.called
def test_de_hump_conversion(self):
"""
@ -118,7 +119,7 @@ class TestCommonFunctions(TestCase):
new_string = de_hump(string)
# THEN: the new string should be converted to python format
self.assertEqual(new_string, "my_class", 'The class name should have been converted')
assert new_string == "my_class", 'The class name should have been converted'
def test_de_hump_static(self):
"""
@ -131,7 +132,7 @@ class TestCommonFunctions(TestCase):
new_string = de_hump(string)
# THEN: the new string should be converted to python format
self.assertEqual(new_string, "my_class", 'The class name should have been preserved')
assert new_string == "my_class", 'The class name should have been preserved'
def test_path_to_module(self):
"""
@ -144,7 +145,7 @@ class TestCommonFunctions(TestCase):
result = path_to_module(path)
# THEN: path_to_module should return the module name
self.assertEqual(result, 'openlp.core.ui.media.webkitplayer')
assert result == 'openlp.core.ui.media.webkitplayer'
def test_trace_error_handler(self):
"""
@ -174,9 +175,9 @@ class TestCommonFunctions(TestCase):
mocked_sys.platform = 'win32'
# THEN: The three platform functions should perform properly
self.assertTrue(is_win(), 'is_win() should return True')
self.assertFalse(is_macosx(), 'is_macosx() should return False')
self.assertFalse(is_linux(), 'is_linux() should return False')
assert is_win() is True, 'is_win() should return True'
assert is_macosx() is False, 'is_macosx() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_is_macosx(self):
"""
@ -190,9 +191,9 @@ class TestCommonFunctions(TestCase):
mocked_sys.platform = 'darwin'
# THEN: The three platform functions should perform properly
self.assertTrue(is_macosx(), 'is_macosx() should return True')
self.assertFalse(is_win(), 'is_win() should return False')
self.assertFalse(is_linux(), 'is_linux() should return False')
assert is_macosx() is True, 'is_macosx() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_linux() is False, 'is_linux() should return False'
def test_is_linux(self):
"""
@ -206,9 +207,9 @@ class TestCommonFunctions(TestCase):
mocked_sys.platform = 'linux3'
# THEN: The three platform functions should perform properly
self.assertTrue(is_linux(), 'is_linux() should return True')
self.assertFalse(is_win(), 'is_win() should return False')
self.assertFalse(is_macosx(), 'is_macosx() should return False')
assert is_linux() is True, 'is_linux() should return True'
assert is_win() is False, 'is_win() should return False'
assert is_macosx() is False, 'is_macosx() should return False'
def test_clean_button_text(self):
"""
@ -222,4 +223,4 @@ class TestCommonFunctions(TestCase):
actual_text = clean_button_text(input_text)
# THEN: The text should have been cleaned
self.assertEqual(expected_text, actual_text, 'The text should be clean')
assert expected_text == actual_text, 'The text should be clean'

View File

@ -59,7 +59,7 @@ class TestHttpUtils(TestCase, TestMixin):
# THEN: The user agent is a Linux (or ChromeOS) user agent
result = 'Linux' in user_agent or 'CrOS' in user_agent
self.assertTrue(result, 'The user agent should be a valid Linux user agent')
assert result is True, 'The user agent should be a valid Linux user agent'
def test_get_user_agent_windows(self):
"""
@ -74,7 +74,7 @@ class TestHttpUtils(TestCase, TestMixin):
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
self.assertIn('Windows', user_agent, 'The user agent should be a valid Windows user agent')
assert 'Windows' in user_agent, 'The user agent should be a valid Windows user agent'
def test_get_user_agent_macos(self):
"""
@ -89,7 +89,7 @@ class TestHttpUtils(TestCase, TestMixin):
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
self.assertIn('Mac OS X', user_agent, 'The user agent should be a valid OS X user agent')
assert 'Mac OS X' in user_agent, 'The user agent should be a valid OS X user agent'
def test_get_user_agent_default(self):
"""
@ -104,7 +104,7 @@ class TestHttpUtils(TestCase, TestMixin):
user_agent = get_user_agent()
# THEN: The user agent is a Linux (or ChromeOS) user agent
self.assertIn('NetBSD', user_agent, 'The user agent should be the default user agent')
assert 'NetBSD'in user_agent, 'The user agent should be the default user agent'
def test_get_web_page_no_url(self):
"""
@ -117,7 +117,7 @@ class TestHttpUtils(TestCase, TestMixin):
result = get_web_page(test_url)
# THEN: None should be returned
self.assertIsNone(result, 'The return value of get_web_page should be None')
assert result is None, 'The return value of get_web_page should be None'
@patch('openlp.core.common.httputils.requests')
@patch('openlp.core.common.httputils.get_user_agent')
@ -240,4 +240,4 @@ class TestHttpUtils(TestCase, TestMixin):
# THEN: socket.timeout should have been caught
# NOTE: Test is if $tmpdir/tempfile is still there, then test fails since ftw deletes bad downloaded files
assert not os.path.exists(self.tempfile), 'tempfile should have been deleted'
assert os.path.exists(self.tempfile) is False, 'tempfile should have been deleted'

View File

@ -63,8 +63,8 @@ class TestInit(TestCase, TestMixin):
add_actions(mocked_target, empty_list)
# THEN: The add method on the mocked target is never called
self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called')
self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called')
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
def test_add_actions_none_action(self):
"""
@ -79,7 +79,7 @@ class TestInit(TestCase, TestMixin):
# THEN: The addSeparator method is called, but the addAction method is never called
mocked_target.addSeparator.assert_called_with()
self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called')
assert mocked_target.addAction.call_count == 0, 'addAction method should not have been called'
def test_add_actions_add_action(self):
"""
@ -93,7 +93,7 @@ class TestInit(TestCase, TestMixin):
add_actions(mocked_target, action_list)
# THEN: The addSeparator method is not called, and the addAction method is called
self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called')
assert mocked_target.addSeparator.call_count == 0, 'addSeparator method should not have been called'
mocked_target.addAction.assert_called_with('action')
def test_add_actions_action_and_none(self):
@ -150,9 +150,8 @@ class TestInit(TestCase, TestMixin):
result = get_uno_command()
# THEN: The command 'libreoffice' should be called with the appropriate parameters
self.assertEquals(result,
'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"')
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_get_uno_command_only_soffice_command_exists(self):
"""
@ -169,8 +168,8 @@ class TestInit(TestCase, TestMixin):
result = get_uno_command()
# THEN: The command 'soffice' should be called with the appropriate parameters
self.assertEquals(result, 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=pipe,name=openlp_pipe;urp;"')
assert result == 'soffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=pipe,name=openlp_pipe;urp;"'
def test_get_uno_command_when_no_command_exists(self):
"""
@ -198,8 +197,8 @@ class TestInit(TestCase, TestMixin):
result = get_uno_command('socket')
# THEN: The connection parameters should be set for socket
self.assertEqual(result, 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard'
' "--accept=socket,host=localhost,port=2002;urp;"')
assert result == 'libreoffice --nologo --norestore --minimized --nodefault --nofirststartwizard' \
' "--accept=socket,host=localhost,port=2002;urp;"'
def test_get_filesystem_encoding_sys_function_not_called(self):
"""
@ -215,8 +214,8 @@ class TestInit(TestCase, TestMixin):
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
self.assertEqual(0, mocked_getdefaultencoding.called, 'getdefaultencoding should not have been called')
self.assertEqual('cp1252', result, 'The result should be "cp1252"')
assert mocked_getdefaultencoding.called == 0, 'getdefaultencoding should not have been called'
assert 'cp1252' == result, 'The result should be "cp1252"'
def test_get_filesystem_encoding_sys_function_is_called(self):
"""
@ -234,7 +233,7 @@ class TestInit(TestCase, TestMixin):
# THEN: getdefaultencoding should have been called
mocked_getfilesystemencoding.assert_called_with()
mocked_getdefaultencoding.assert_called_with()
self.assertEqual('utf-8', result, 'The result should be "utf-8"')
assert 'utf-8' == result, 'The result should be "utf-8"'
def test_clean_filename(self):
"""
@ -248,7 +247,7 @@ class TestInit(TestCase, TestMixin):
result = clean_filename(invalid_name)
# THEN: The file name should be cleaned.
self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.')
assert wanted_name == result, 'The file name should not contain any special characters.'
def test_delete_file_no_path(self):
"""
@ -259,7 +258,7 @@ class TestInit(TestCase, TestMixin):
result = delete_file(None)
# THEN: delete_file should return False
self.assertFalse(result, "delete_file should return False when called with None")
assert result is False, "delete_file should return False when called with None"
def test_delete_file_path_success(self):
"""
@ -272,7 +271,7 @@ class TestInit(TestCase, TestMixin):
result = delete_file(Path('path', 'file.ext'))
# THEN: delete_file should return True
self.assertTrue(result, 'delete_file should return True when it successfully deletes a file')
assert result is True, 'delete_file should return True when it successfully deletes a file'
def test_delete_file_path_no_file_exists(self):
"""
@ -286,8 +285,8 @@ class TestInit(TestCase, TestMixin):
result = delete_file(Path('path', 'file.ext'))
# THEN: The function should not attempt to delete the file and it should return True
self.assertFalse(mocked_unlink.called)
self.assertTrue(result, 'delete_file should return True when the file doesnt exist')
assert mocked_unlink.called is False
assert result is True, 'delete_file should return True when the file doesnt exist'
def test_delete_file_path_exception(self):
"""
@ -303,8 +302,8 @@ class TestInit(TestCase, TestMixin):
result = delete_file(Path('path', 'file.ext'))
# THEN: The exception should be logged and `delete_file` should return False
self.assertTrue(mocked_log.exception.called)
self.assertFalse(result, 'delete_file should return False when an OSError is raised')
assert mocked_log.exception.called
assert result is False, 'delete_file should return False when an OSError is raised'
def test_get_file_encoding_done(self):
"""
@ -323,9 +322,9 @@ class TestInit(TestCase, TestMixin):
# THEN: The feed method of UniversalDetector should only br called once before returning a result
mocked_open.assert_called_once_with('rb')
self.assertEqual(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()
self.assertEqual(result, encoding_result)
assert result == encoding_result
def test_get_file_encoding_eof(self):
"""
@ -345,9 +344,9 @@ class TestInit(TestCase, TestMixin):
# THEN: The feed method of UniversalDetector should have been called twice before returning a result
mocked_open.assert_called_once_with('rb')
self.assertEqual(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()
self.assertEqual(result, encoding_result)
assert result == encoding_result
def test_get_file_encoding_oserror(self):
"""
@ -364,4 +363,4 @@ class TestInit(TestCase, TestMixin):
# THEN: log.exception should be called and get_file_encoding should return None
mocked_log.exception.assert_called_once_with('Error detecting file encoding')
self.assertIsNone(result)
assert result is None

View File

@ -45,7 +45,7 @@ class TestOpenLPJsonDecoder(TestCase):
result = instance.object_hook({'__Path__': ['test', 'path']})
# THEN: A Path object should be returned
self.assertEqual(result, Path('test', 'path'))
assert result == Path('test', 'path')
def test_object_hook_non_path_object(self):
"""
@ -59,8 +59,8 @@ class TestOpenLPJsonDecoder(TestCase):
result = instance.object_hook({'key': 'value'})
# THEN: The object should be returned unchanged and a Path object should not have been initiated
self.assertEqual(result, {'key': 'value'})
self.assertFalse(mocked_path.called)
assert result == {'key': 'value'}
assert mocked_path.called is False
def test_json_decode(self):
"""
@ -73,7 +73,7 @@ class TestOpenLPJsonDecoder(TestCase):
obj = json.loads(json_string, cls=OpenLPJsonDecoder)
# THEN: The object returned should be a python version of the JSON string
self.assertEqual(obj, [Path('test', 'path1'), Path('test', 'path2')])
assert obj == [Path('test', 'path1'), Path('test', 'path2')]
class TestOpenLPJsonEncoder(TestCase):
@ -91,7 +91,7 @@ class TestOpenLPJsonEncoder(TestCase):
result = instance.default(Path('test', 'path'))
# THEN: A dictionary object that can be JSON encoded should be returned
self.assertEqual(result, {'__Path__': ('test', 'path')})
assert result == {'__Path__': ('test', 'path')}
def test_default_non_path_object(self):
"""
@ -119,4 +119,4 @@ class TestOpenLPJsonEncoder(TestCase):
json_string = json.dumps(obj, cls=OpenLPJsonEncoder)
# THEN: The JSON string return should be a representation of the object encoded
self.assertEqual(json_string, '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]')
assert json_string == '[{"__Path__": ["test", "path1"]}, {"__Path__": ["test", "path2"]}]'

View File

@ -46,7 +46,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
# GIVEN an Empty Registry
# WHEN there is no Application
# THEN the application should be none
self.assertEqual(self.application, None, 'The application value should be None')
assert self.application is None, 'The application value should be None'
def test_application(self):
"""
@ -59,7 +59,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
Registry().register('application', application)
# THEN the application should be none
self.assertEqual(self.application, application, 'The application value should match')
assert self.application == application, 'The application value should match'
@patch('openlp.core.common.mixins.is_win')
def test_application_on_windows(self, mocked_is_win):
@ -74,7 +74,7 @@ class TestRegistryProperties(TestCase, RegistryProperties):
Registry().register('application', application)
# THEN the application should be none
self.assertEqual(self.application, application, 'The application value should match')
assert self.application == application, 'The application value should match'
@patch('openlp.core.common.mixins.is_win')
def test_get_application_on_windows(self, mocked_is_win):
@ -93,6 +93,6 @@ class TestRegistryProperties(TestCase, RegistryProperties):
actual_application = reg_props.application
# THEN the application should be the mock object, and the correct function should have been called
self.assertEqual(mock_application, actual_application, 'The application value should match')
assert mock_application == actual_application, 'The application value should match'
mocked_is_win.assert_called_with()
mocked_get.assert_called_with('application')

View File

@ -47,8 +47,8 @@ class TestShutil(TestCase):
result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params)
# THEN: The positional and keyword args should not have changed
self.assertEqual(test_args, result_args)
self.assertEqual(test_kwargs, result_kwargs)
assert test_args == result_args
assert test_kwargs == result_kwargs
def test_replace_params_params(self):
"""
@ -63,8 +63,8 @@ class TestShutil(TestCase):
result_args, result_kwargs = replace_params(test_args, test_kwargs, test_params)
# THEN: The positional and keyword args should have have changed
self.assertEqual(result_args, (1, '2'))
self.assertEqual(result_kwargs, {'arg3': '3', 'arg4': 4})
assert result_args == (1, '2')
assert result_kwargs == {'arg3': '3', 'arg4': 4}
def test_copy(self):
"""
@ -82,7 +82,7 @@ class TestShutil(TestCase):
# :func:`shutil.copy` as a Path object.
mocked_shutil_copy.assert_called_once_with(os.path.join('source', 'test', 'path'),
os.path.join('destination', 'test', 'path'))
self.assertEqual(result, Path('destination', 'test', 'path'))
assert result == Path('destination', 'test', 'path')
def test_copy_follow_optional_params(self):
"""
@ -114,7 +114,7 @@ class TestShutil(TestCase):
# :func:`shutil.copyfile` as a Path object.
mocked_shutil_copyfile.assert_called_once_with(os.path.join('source', 'test', 'path'),
os.path.join('destination', 'test', 'path'))
self.assertEqual(result, Path('destination', 'test', 'path'))
assert result == Path('destination', 'test', 'path')
def test_copyfile_optional_params(self):
"""
@ -147,7 +147,7 @@ class TestShutil(TestCase):
# :func:`shutil.copytree` as a Path object.
mocked_shutil_copytree.assert_called_once_with(os.path.join('source', 'test', 'path'),
os.path.join('destination', 'test', 'path'))
self.assertEqual(result, Path('destination', 'test', 'path'))
assert result == Path('destination', 'test', 'path')
def test_copytree_optional_params(self):
"""
@ -177,12 +177,11 @@ class TestShutil(TestCase):
path = Path('test', 'path')
# WHEN: Calling :func:`openlp.core.common.path.rmtree` with the path parameter as Path object type
result = path.rmtree()
path.rmtree()
# THEN: :func:`shutil.rmtree` should have been called with the str equivalents of the Path object.
mocked_shutil_rmtree.assert_called_once_with(
os.path.join('test', 'path'), False, None)
self.assertIsNone(result)
def test_rmtree_optional_params(self):
"""
@ -214,7 +213,7 @@ class TestShutil(TestCase):
# THEN: :func:`shutil.which` should have been called with the command, and :func:`which` should return None.
mocked_shutil_which.assert_called_once_with('no_command')
self.assertIsNone(result)
assert result is None
def test_which_command(self):
"""
@ -230,7 +229,7 @@ class TestShutil(TestCase):
# THEN: :func:`shutil.which` should have been called with the command, and :func:`which` should return a
# Path object equivalent of the command path.
mocked_shutil_which.assert_called_once_with('command')
self.assertEqual(result, Path('path', 'to', 'command'))
assert result == Path('path', 'to', 'command')
class TestPath(TestCase):
@ -257,7 +256,7 @@ class TestPath(TestCase):
result = path_to_str(None)
# THEN: `path_to_str` should return an empty string
self.assertEqual(result, '')
assert result == ''
def test_path_to_str_path_object(self):
"""
@ -268,7 +267,7 @@ class TestPath(TestCase):
result = path_to_str(Path('test/path'))
# THEN: `path_to_str` should return a string representation of the Path object
self.assertEqual(result, os.path.join('test', 'path'))
assert result == os.path.join('test', 'path')
def test_str_to_path_type_error(self):
"""
@ -289,7 +288,7 @@ class TestPath(TestCase):
result = str_to_path('')
# THEN: `path_to_str` should return None
self.assertEqual(result, None)
assert result is None
def test_path_encode_json(self):
"""
@ -301,7 +300,7 @@ class TestPath(TestCase):
path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, extra=1, args=2)
# THEN: A Path object should have been returned
self.assertEqual(path, Path('path', 'to', 'fi.le'))
assert path == Path('path', 'to', 'fi.le')
def test_path_encode_json_base_path(self):
"""
@ -313,7 +312,7 @@ class TestPath(TestCase):
path = Path.encode_json({'__Path__': ['path', 'to', 'fi.le']}, base_path=Path('/base'))
# THEN: A Path object should have been returned with an absolute path
self.assertEqual(path, Path('/', 'base', 'path', 'to', 'fi.le'))
assert path == Path('/', 'base', 'path', 'to', 'fi.le')
def test_path_json_object(self):
"""
@ -326,7 +325,7 @@ class TestPath(TestCase):
obj = path.json_object(extra=1, args=2)
# THEN: A JSON decodable object should have been returned.
self.assertEqual(obj, {'__Path__': ('/', 'base', 'path', 'to', 'fi.le')})
assert obj == {'__Path__': ('/', 'base', 'path', 'to', 'fi.le')}
def test_path_json_object_base_path(self):
"""
@ -340,7 +339,7 @@ class TestPath(TestCase):
obj = path.json_object(base_path=Path('/', 'base'))
# THEN: A JSON decodable object should have been returned.
self.assertEqual(obj, {'__Path__': ('path', 'to', 'fi.le')})
assert obj == {'__Path__': ('path', 'to', 'fi.le')}
def test_create_paths_dir_exists(self):
"""

View File

@ -45,7 +45,7 @@ ip6_link_local = 'fe80::223:14ff:fe99:d315'
ip6_bad = 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff'
class testProjectorUtilities(TestCase):
class TestProjectorUtilities(TestCase):
"""
Validate functions in the projector utilities module
"""
@ -57,7 +57,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip4_loopback)
# THEN: Verify we received True
self.assertTrue(valid, 'IPv4 loopback address should have been valid')
assert valid, 'IPv4 loopback address should have been valid'
def test_ip4_local_valid(self):
"""
@ -67,7 +67,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip4_local)
# THEN: Verify we received True
self.assertTrue(valid, 'IPv4 local address should have been valid')
assert valid is True, 'IPv4 local address should have been valid'
def test_ip4_broadcast_valid(self):
"""
@ -77,7 +77,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip4_broadcast)
# THEN: Verify we received True
self.assertTrue(valid, 'IPv4 broadcast address should have been valid')
assert valid is True, 'IPv4 broadcast address should have been valid'
def test_ip4_address_invalid(self):
"""
@ -87,7 +87,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip4_bad)
# THEN: Verify we received True
self.assertFalse(valid, 'Bad IPv4 address should not have been valid')
assert valid is False, 'Bad IPv4 address should not have been valid'
def test_ip6_loopback_valid(self):
"""
@ -97,7 +97,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip6_loopback)
# THEN: Validate return
self.assertTrue(valid, 'IPv6 loopback address should have been valid')
assert valid is True, 'IPv6 loopback address should have been valid'
def test_ip6_local_valid(self):
"""
@ -107,7 +107,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip6_link_local)
# THEN: Validate return
self.assertTrue(valid, 'IPv6 link-local address should have been valid')
assert valid is True, 'IPv6 link-local address should have been valid'
def test_ip6_address_invalid(self):
"""
@ -117,7 +117,7 @@ class testProjectorUtilities(TestCase):
valid = verify_ip_address(addr=ip6_bad)
# THEN: Validate bad return
self.assertFalse(valid, 'IPv6 bad address should have been invalid')
assert valid is False, 'IPv6 bad address should have been invalid'
def test_md5_hash(self):
"""
@ -127,7 +127,7 @@ class testProjectorUtilities(TestCase):
hash_ = md5_hash(salt=salt.encode('utf-8'), data=pin.encode('utf-8'))
# THEN: Validate return has is same
self.assertEquals(hash_, test_hash, 'MD5 should have returned a good hash')
assert hash_ == test_hash, 'MD5 should have returned a good hash'
def test_md5_hash_bad(self):
"""
@ -137,7 +137,7 @@ class testProjectorUtilities(TestCase):
hash_ = md5_hash(salt=pin.encode('utf-8'), data=salt.encode('utf-8'))
# THEN: return data is different
self.assertNotEquals(hash_, test_hash, 'MD5 should have returned a bad hash')
assert hash_ is not test_hash, 'MD5 should have returned a bad hash'
def test_qmd5_hash(self):
"""
@ -147,7 +147,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=salt.encode('utf-8'), data=pin.encode('utf-8'))
# THEN: Validate return has is same
self.assertEquals(hash_, test_hash, 'Qt-MD5 should have returned a good hash')
assert hash_ == test_hash, 'Qt-MD5 should have returned a good hash'
def test_qmd5_hash_bad(self):
"""
@ -157,7 +157,7 @@ class testProjectorUtilities(TestCase):
hash_ = qmd5_hash(salt=pin.encode('utf-8'), data=salt.encode('utf-8'))
# THEN: return data is different
self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
assert hash_ is not test_hash, 'Qt-MD5 should have returned a bad hash'
def test_md5_non_ascii_string(self):
"""
@ -167,7 +167,7 @@ class testProjectorUtilities(TestCase):
hash_ = md5_hash(salt=test_non_ascii_string.encode('utf-8'), data=None)
# THEN: Valid MD5 hash should be returned
self.assertEqual(hash_, test_non_ascii_hash, 'MD5 should have returned a valid hash')
assert hash_ == test_non_ascii_hash, 'MD5 should have returned a valid hash'
def test_qmd5_non_ascii_string(self):
"""
@ -177,4 +177,4 @@ class testProjectorUtilities(TestCase):
hash_ = md5_hash(data=test_non_ascii_string.encode('utf-8'))
# THEN: Valid MD5 hash should be returned
self.assertEqual(hash_, test_non_ascii_hash, 'Qt-MD5 should have returned a valid hash')
assert hash_ == test_non_ascii_hash, 'Qt-MD5 should have returned a valid hash'

View File

@ -51,19 +51,19 @@ class TestRegistry(TestCase):
# THEN and I will get an exception
with self.assertRaises(KeyError) as context:
Registry().register('test1', mock_1)
self.assertEqual(context.exception.args[0], 'Duplicate service exception test1',
'KeyError exception should have been thrown for duplicate service')
assert context.exception.args[0] == 'Duplicate service exception test1', \
'KeyError exception should have been thrown for duplicate service'
# WHEN I try to get back a non existent component
# THEN I will get an exception
temp = Registry().get('test2')
self.assertEqual(temp, None, 'None should have been returned for missing service')
assert temp is None, 'None should have been returned for missing service'
# WHEN I try to replace a component I should be allowed
Registry().remove('test1')
# THEN I will get an exception
temp = Registry().get('test1')
self.assertEqual(temp, None, 'None should have been returned for deleted service')
assert temp is None, 'None should have been returned for deleted service'
def test_registry_function(self):
"""
@ -77,21 +77,21 @@ class TestRegistry(TestCase):
return_value = Registry().execute('test1')
# THEN: I expect then function to have been called and a return given
self.assertEqual(return_value[0], 'function_1', 'A return value is provided and matches')
assert return_value[0] == 'function_1', 'A return value is provided and matches'
# WHEN: I execute the a function with the same reference and execute the function
Registry().register_function('test1', self.dummy_function_1)
return_value = Registry().execute('test1')
# THEN: I expect then function to have been called and a return given
self.assertEqual(return_value, ['function_1', 'function_1'], 'A return value list is provided and matches')
assert return_value == ['function_1', 'function_1'], 'A return value list is provided and matches'
# WHEN: I execute the a 2nd function with the different reference and execute the function
Registry().register_function('test2', self.dummy_function_2)
return_value = Registry().execute('test2')
# THEN: I expect then function to have been called and a return given
self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
assert return_value[0] == 'function_2', 'A return value is provided and matches'
def test_registry_working_flags(self):
"""
@ -107,28 +107,28 @@ class TestRegistry(TestCase):
# THEN: we should be able retrieve the saved component
temp = Registry().get_flag('test1')
self.assertEquals(temp, my_data, 'The value should have been saved')
assert temp == my_data, 'The value should have been saved'
# WHEN: I add a component for the second time I am not mad.
# THEN and I will not get an exception
Registry().set_flag('test1', my_data2)
temp = Registry().get_flag('test1')
self.assertEquals(temp, my_data2, 'The value should have been updated')
assert temp == my_data2, 'The value should have been updated'
# WHEN I try to get back a non existent Working Flag
# THEN I will get an exception
with self.assertRaises(KeyError) as context1:
temp = Registry().get_flag('test2')
self.assertEqual(context1.exception.args[0], 'Working Flag test2 not found in list',
'KeyError exception should have been thrown for missing working flag')
assert context1.exception.args[0] == 'Working Flag test2 not found in list', \
'KeyError exception should have been thrown for missing working flag'
# WHEN I try to replace a working flag I should be allowed
Registry().remove_flag('test1')
# THEN I will get an exception
with self.assertRaises(KeyError) as context:
temp = Registry().get_flag('test1')
self.assertEqual(context.exception.args[0], 'Working Flag test1 not found in list',
'KeyError exception should have been thrown for duplicate working flag')
assert context.exception.args[0] == 'Working Flag test1 not found in list', \
'KeyError exception should have been thrown for duplicate working flag'
def test_remove_function(self):
"""
@ -142,7 +142,7 @@ class TestRegistry(TestCase):
Registry().remove_function('test1', self.dummy_function_1)
# THEN: The method should not be available.
assert not Registry().functions_list['test1'], 'The function should not be in the dict anymore.'
assert Registry().functions_list['test1'] == [], 'The function should not be in the dict anymore.'
def dummy_function_1(self):
return "function_1"
@ -174,7 +174,7 @@ class TestRegistryBase(TestCase):
PlainStub()
# THEN: Nothing is registered with the registry
self.assertEqual(len(Registry().functions_list), 0), 'The function should not be in the dict anymore.'
assert len(Registry().functions_list) == 0, 'The function should not be in the dict anymore.'
def test_registry_mixin_present(self):
"""
@ -187,4 +187,4 @@ class TestRegistryBase(TestCase):
RegistryStub()
# THEN: The bootstrap methods should be registered
self.assertEqual(len(Registry().functions_list), 2), 'The bootstrap functions should be in the dict.'
assert len(Registry().functions_list) == 2, 'The bootstrap functions should be in the dict.'

View File

@ -139,13 +139,13 @@ class TestSettings(TestCase, TestMixin):
extend = settings.value('extend')
# THEN the default value is returned
self.assertEqual('very wide', extend, 'The default value defined should be returned')
assert 'very wide' == extend, 'The default value defined should be returned'
# WHEN a new value is saved into config
Settings().setValue('test/extend', 'very short')
# THEN the new value is returned when re-read
self.assertEqual('very short', Settings().value('test/extend'), 'The saved value should be returned')
assert 'very short' == Settings().value('test/extend'), 'The saved value should be returned'
def test_settings_nonexisting(self):
"""Test the Settings on query for non-existing value"""
@ -155,7 +155,7 @@ class TestSettings(TestCase, TestMixin):
Settings().value('core/does not exists')
# THEN: An exception with the non-existing key should be thrown
self.assertEqual(str(cm.exception), "'core/does not exists'", 'We should get an exception')
assert str(cm.exception) == "'core/does not exists'", 'We should get an exception'
def test_extend_default_settings(self):
"""Test that the extend_default_settings method extends the default settings"""
@ -167,9 +167,8 @@ class TestSettings(TestCase, TestMixin):
Settings.extend_default_settings({'test/setting 3': 4, 'test/extended 1': 1, 'test/extended 2': 2})
# THEN: The _default_settings__ dictionary_ should have the new keys
self.assertEqual(
Settings.__default_settings__, {'test/setting 1': 1, 'test/setting 2': 2, 'test/setting 3': 4,
'test/extended 1': 1, 'test/extended 2': 2})
assert Settings.__default_settings__ == {'test/setting 1': 1, 'test/setting 2': 2, 'test/setting 3': 4,
'test/extended 1': 1, 'test/extended 2': 2}
@patch('openlp.core.common.settings.QtCore.QSettings.contains')
@patch('openlp.core.common.settings.QtCore.QSettings.value')

View File

@ -113,7 +113,7 @@ class TestRenderer(TestCase):
result = get_start_tags(given_raw_text)
# THEN: Check if the correct tuple is returned.
self.assertEqual(result, expected_tuple), 'A tuple should be returned containing the text with correct ' \
assert result == expected_tuple, 'A tuple should be returned containing the text with correct ' \
'tags, the opening tags, and the opening html tags.'
def test_word_split(self):
@ -128,7 +128,7 @@ class TestRenderer(TestCase):
result_words = words_split(given_line)
# THEN: The word lists should be the same.
self.assertListEqual(result_words, expected_words)
assert result_words == expected_words
def test_format_slide_logical_split(self):
"""
@ -145,7 +145,7 @@ class TestRenderer(TestCase):
result_words = renderer.format_slide(given_line, service_item)
# THEN: The word lists should be the same.
self.assertListEqual(result_words, expected_words)
assert result_words == expected_words
def test_format_slide_blank_before_split(self):
"""
@ -162,7 +162,7 @@ class TestRenderer(TestCase):
result_words = renderer.format_slide(given_line, service_item)
# THEN: The blanks have been removed.
self.assertListEqual(result_words, expected_words)
assert result_words == expected_words
def test_format_slide_blank_after_split(self):
"""
@ -179,7 +179,7 @@ class TestRenderer(TestCase):
result_words = renderer.format_slide(given_line, service_item)
# THEN: The blanks have been removed.
self.assertListEqual(result_words, expected_words)
assert result_words == expected_words
@patch('openlp.core.display.renderer.QtWebKitWidgets.QWebView')
@patch('openlp.core.display.renderer.build_lyrics_format_css')

View File

@ -75,6 +75,6 @@ class TestScreenList(TestCase):
# THEN: The screen should have been added and the screens should be identical
new_screen_count = len(self.screens.screen_list)
self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger')
self.assertEqual(SCREEN, self.screens.screen_list.pop(),
'The 2nd screen should be identical to the first screen')
assert old_screen_count + 1 == new_screen_count, 'The new_screens list should be bigger'
assert SCREEN == self.screens.screen_list.pop(), \
'The 2nd screen should be identical to the first screen'

View File

@ -80,8 +80,8 @@ class TestDB(TestCase):
MockedMetaData.assert_called_with(bind=mocked_engine)
mocked_sessionmaker.assert_called_with(autoflush=True, autocommit=False, bind=mocked_engine)
mocked_scoped_session.assert_called_with(mocked_sessionmaker_object)
self.assertIs(session, mocked_scoped_session_object, 'The ``session`` object should be the mock')
self.assertIs(metadata, mocked_metadata, 'The ``metadata`` object should be the mock')
assert session is mocked_scoped_session_object, 'The ``session`` object should be the mock'
assert metadata is mocked_metadata, 'The ``metadata`` object should be the mock'
def test_init_db_defaults(self):
"""
@ -94,8 +94,8 @@ class TestDB(TestCase):
session, metadata = init_db(db_url)
# THEN: Valid session and metadata objects should be returned
self.assertIsInstance(session, ScopedSession, 'The ``session`` object should be a ``ScopedSession`` instance')
self.assertIsInstance(metadata, MetaData, 'The ``metadata`` object should be a ``MetaData`` instance')
assert isinstance(session, ScopedSession), 'The ``session`` object should be a ``ScopedSession`` instance'
assert isinstance(metadata, MetaData), 'The ``metadata`` object should be a ``MetaData`` instance'
def test_get_upgrade_op(self):
"""
@ -116,7 +116,7 @@ class TestDB(TestCase):
op = get_upgrade_op(mocked_session)
# THEN: The op object should be mocked_op, and the correction function calls should have been made
self.assertIs(op, mocked_op, 'The return value should be the mocked object')
assert op is mocked_op, 'The return value should be the mocked object'
mocked_session.bind.connect.assert_called_with()
MockedMigrationContext.configure.assert_called_with(mocked_connection)
MockedOperations.assert_called_with(mocked_context)
@ -139,7 +139,7 @@ class TestDB(TestCase):
# THEN: The AppLocation.get_section_data_path and delete_file methods should have been called
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
mocked_delete_file.assert_called_with(test_location)
self.assertTrue(result, 'The result of delete_file should be True (was rigged that way)')
assert result is True, 'The result of delete_file should be True (was rigged that way)'
def test_delete_database_with_db_file_name(self):
"""
@ -160,7 +160,7 @@ class TestDB(TestCase):
# THEN: The AppLocation.get_section_data_path and delete_file methods should have been called
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
mocked_delete_file.assert_called_with(test_location)
self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)')
assert result is False, 'The result of delete_file should be False (was rigged that way)'
def test_skip_db_upgrade_with_no_database(self):
"""
@ -174,4 +174,4 @@ class TestDB(TestCase):
upgrade_db(url, mocked_upgrade)
# THEN: upgrade should NOT have been called
self.assertFalse(mocked_upgrade.called, 'Database upgrade function should NOT have been called')
assert mocked_upgrade.called is False, 'Database upgrade function should NOT have been called'

View File

@ -309,7 +309,7 @@ class Htmbuilder(TestCase, TestMixin):
html = build_html(item, screen, is_live, background, plugins=plugins)
# THEN: The returned html should match.
self.assertEqual(html, HTML, 'The returned html should match')
assert html == HTML, 'The returned html should match'
def test_build_background_css_radial(self):
"""
@ -325,7 +325,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_background_css(item, width)
# THEN: The returned css should match.
self.assertEqual(BACKGROUND_CSS_RADIAL, css, 'The background css should be equal.')
assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
def test_build_lyrics_css(self):
"""
@ -346,7 +346,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_lyrics_css(item)
# THEN: The css should be equal.
self.assertEqual(LYRICS_CSS, css, 'The lyrics css should be equal.')
assert LYRICS_CSS == css, 'The lyrics css should be equal.'
def test_build_lyrics_outline_css(self):
"""
@ -363,7 +363,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_lyrics_outline_css(theme_data)
# THEN: The css should be equal.
self.assertEqual(LYRICS_OUTLINE_CSS, css, 'The outline css should be equal.')
assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
def test_build_lyrics_format_css(self):
"""
@ -386,7 +386,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_lyrics_format_css(theme_data, width, height)
# THEN: They should be equal.
self.assertEqual(LYRICS_FORMAT_CSS, css, 'The lyrics format css should be equal.')
assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
def test_build_footer_css(self):
"""
@ -404,7 +404,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_footer_css(item, height)
# THEN: THE css should be the same.
self.assertEqual(FOOTER_CSS, css, 'The footer strings should be equal.')
assert FOOTER_CSS == css, 'The footer strings should be equal.'
def test_build_footer_css_wrap(self):
"""
@ -423,7 +423,7 @@ class Htmbuilder(TestCase, TestMixin):
css = build_footer_css(item, height)
# THEN: Footer should wrap
self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')
assert FOOTER_CSS_WRAP == css, 'The footer strings should be equal.'
def test_build_footer_invalid(self):
"""
@ -443,8 +443,8 @@ class Htmbuilder(TestCase, TestMixin):
css.append(build_footer_css(item, height))
# THEN: Footer should wrap
self.assertEqual(FOOTER_CSS_INVALID, css[0], 'The footer strings should be blank.')
self.assertEqual(FOOTER_CSS_INVALID, css[1], 'The footer strings should be blank.')
assert FOOTER_CSS_INVALID == css[0], 'The footer strings should be blank.'
assert FOOTER_CSS_INVALID == css[1], 'The footer strings should be blank.'
def test_webkit_version(self):
"""
@ -454,7 +454,7 @@ class Htmbuilder(TestCase, TestMixin):
webkit_ver = float(QtWebKit.qWebKitVersion())
# WHEN: Retrieving the webkit version
# THEN: Webkit versions should match
self.assertEquals(webkit_version(), webkit_ver, "The returned webkit version doesn't match the installed one")
assert webkit_version() == webkit_ver, "The returned webkit version doesn't match the installed one"
def test_build_chords_css(self):
"""
@ -468,4 +468,4 @@ class Htmbuilder(TestCase, TestMixin):
chord_css = build_chords_css()
# THEN: The build css should look as expected
self.assertEqual(CHORD_CSS_ENABLED, chord_css, 'The chord CSS should look as expected')
assert CHORD_CSS_ENABLED == chord_css, 'The chord CSS should look as expected'

View File

@ -84,7 +84,7 @@ class TestImageManager(TestCase, TestMixin):
# THEN a KeyError is thrown
with self.assertRaises(KeyError) as context:
self.image_manager.get_image(TEST_PATH, 'church1.jpg')
self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing image')
assert context.exception is not '', 'KeyError exception should have been thrown for missing image'
def test_different_dimension_image(self):
"""
@ -98,7 +98,7 @@ class TestImageManager(TestCase, TestMixin):
image = self.image_manager.get_image(full_path, 'church.jpg', 80, 80)
# THEN: The return should be of type image
self.assertEqual(isinstance(image, QtGui.QImage), True, 'The returned object should be a QImage')
assert isinstance(image, QtGui.QImage), 'The returned object should be a QImage'
# WHEN: adding the same image with different dimensions
self.image_manager.add_image(full_path, 'church.jpg', None, 100, 100)
@ -116,7 +116,7 @@ class TestImageManager(TestCase, TestMixin):
# WHEN: calling with correct image, but wrong dimensions
with self.assertRaises(KeyError) as context:
self.image_manager.get_image(full_path, 'church.jpg', 120, 120)
self.assertNotEquals(context.exception, '', 'KeyError exception should have been thrown for missing dimension')
assert context.exception is not '', 'KeyError exception should have been thrown for missing dimension'
def test_process_cache(self):
"""
@ -141,10 +141,8 @@ class TestImageManager(TestCase, TestMixin):
# is being processed (see mocked methods/functions).
# Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the #
# priority is adjusted to Priority.Lowest).
self.assertEqual(self.get_image_priority(image1), Priority.Normal,
"image1's priority should be 'Priority.Normal'")
self.assertEqual(self.get_image_priority(image2), Priority.Normal,
"image2's priority should be 'Priority.Normal'")
assert self.get_image_priority(image1) == Priority.Normal, "image1's priority should be 'Priority.Normal'"
assert self.get_image_priority(image2) == Priority.Normal, "image2's priority should be 'Priority.Normal'"
# WHEN: Add more images.
self.image_manager.add_image(TEST_PATH, image3, None)
@ -162,15 +160,15 @@ class TestImageManager(TestCase, TestMixin):
# Because empty() is not reliable, wait a litte; just to make sure.
time.sleep(0.1)
# THEN: The images' priority reflect how they were processed.
self.assertEqual(self.image_manager._conversion_queue.qsize(), 0, "The queue should be empty.")
self.assertEqual(self.get_image_priority(image1), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image2), Priority.Lowest,
"The image should have not been requested (=Lowest)")
self.assertEqual(self.get_image_priority(image3), Priority.Low,
"Only the QImage should have been requested (=Low).")
self.assertEqual(self.get_image_priority(image4), Priority.Urgent,
"The image bytes should have been requested (=Urgent).")
assert self.image_manager._conversion_queue.qsize() == 0, "The queue should be empty."
assert self.get_image_priority(image1) == Priority.Lowest, \
"The image should have not been requested (=Lowest)"
assert self.get_image_priority(image2) == Priority.Lowest, \
"The image should have not been requested (=Lowest)"
assert self.get_image_priority(image3) == Priority.Low, \
"Only the QImage should have been requested (=Low)."
assert self.get_image_priority(image4) == Priority.Urgent, \
"The image bytes should have been requested (=Urgent)."
def get_image_priority(self, image):
"""

View File

@ -49,8 +49,8 @@ class TestLib(TestCase):
true_result = str_to_bool(true_boolean)
# THEN: We should get back a True bool
self.assertIsInstance(true_result, bool, 'The result should be a boolean')
self.assertTrue(true_result, 'The result should be True')
assert isinstance(true_result, bool), 'The result should be a boolean'
assert true_result is True, 'The result should be True'
def test_str_to_bool_with_bool_false(self):
"""
@ -63,8 +63,8 @@ class TestLib(TestCase):
false_result = str_to_bool(false_boolean)
# THEN: We should get back a True bool
self.assertIsInstance(false_result, bool, 'The result should be a boolean')
self.assertFalse(false_result, 'The result should be True')
assert isinstance(false_result, bool), 'The result should be a boolean'
assert false_result is False, 'The result should be True'
def test_str_to_bool_with_integer(self):
"""
@ -77,7 +77,7 @@ class TestLib(TestCase):
int_result = str_to_bool(int_string)
# THEN: we should get back a false
self.assertFalse(int_result, 'The result should be False')
assert int_result is False, 'The result should be False'
def test_str_to_bool_with_invalid_string(self):
"""
@ -90,7 +90,7 @@ class TestLib(TestCase):
str_result = str_to_bool(invalid_string)
# THEN: we should get back a false
self.assertFalse(str_result, 'The result should be False')
assert str_result is False, 'The result should be False'
def test_str_to_bool_with_string_false(self):
"""
@ -103,7 +103,7 @@ class TestLib(TestCase):
false_result = str_to_bool(false_string)
# THEN: we should get back a false
self.assertFalse(false_result, 'The result should be False')
assert false_result is False, 'The result should be False'
def test_str_to_bool_with_string_no(self):
"""
@ -116,7 +116,7 @@ class TestLib(TestCase):
str_result = str_to_bool(no_string)
# THEN: we should get back a false
self.assertFalse(str_result, 'The result should be False')
assert str_result is False, 'The result should be False'
def test_str_to_bool_with_true_string_value(self):
"""
@ -129,7 +129,7 @@ class TestLib(TestCase):
true_result = str_to_bool(true_string)
# THEN: we should get back a true
self.assertTrue(true_result, 'The result should be True')
assert true_result is True, 'The result should be True'
def test_str_to_bool_with_yes_string_value(self):
"""
@ -142,7 +142,7 @@ class TestLib(TestCase):
str_result = str_to_bool(yes_string)
# THEN: we should get back a true
self.assertTrue(str_result, 'The result should be True')
assert str_result is True, 'The result should be True'
def test_get_text_file_string_no_file(self):
"""
@ -157,7 +157,7 @@ class TestLib(TestCase):
# THEN: The result should be False
file_path.is_file.assert_called_with()
self.assertFalse(result, 'False should be returned if no file exists')
assert result is False, 'False should be returned if no file exists'
def test_get_text_file_string_read_error(self):
"""
@ -176,7 +176,7 @@ class TestLib(TestCase):
# THEN: None should be returned
file_path.is_file.assert_called_once_with()
file_path.open.assert_called_once_with('r', encoding='utf-8')
self.assertIsNone(result, 'None should be returned if the file cannot be opened')
assert result is None, 'None should be returned if the file cannot be opened'
def test_get_text_file_string_decode_error(self):
"""
@ -195,7 +195,7 @@ class TestLib(TestCase):
result = build_icon(icon)
# THEN: The result should be the same icon as we passed in
self.assertIs(icon, result, 'The result should be the same icon as we passed in')
assert icon is result, 'The result should be the same icon as we passed in'
def test_build_icon_with_resource(self):
"""
@ -217,7 +217,7 @@ class TestLib(TestCase):
MockedQPixmap.assert_called_with(resource_uri)
# There really should be more assert statements here but due to type checking and things they all break. The
# best we can do is to assert that we get back a MagicMock object.
self.assertIsInstance(result, MagicMock, 'The result should be a MagicMock, because we mocked it out')
assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out'
def test_image_to_byte(self):
"""
@ -240,8 +240,8 @@ class TestLib(TestCase):
MockedQtCore.QBuffer.assert_called_with(mocked_byte_array)
mocked_buffer.open.assert_called_with('writeonly')
mocked_image.save.assert_called_with(mocked_buffer, "PNG")
self.assertFalse(mocked_byte_array.toBase64.called)
self.assertEqual(mocked_byte_array, result, 'The mocked out byte array should be returned')
assert mocked_byte_array.toBase64.called is False
assert mocked_byte_array == result, 'The mocked out byte array should be returned'
def test_image_to_byte_base_64(self):
"""
@ -266,8 +266,7 @@ class TestLib(TestCase):
mocked_buffer.open.assert_called_with('writeonly')
mocked_image.save.assert_called_with(mocked_buffer, "PNG")
mocked_byte_array.toBase64.assert_called_with()
self.assertEqual('base64mock', result, 'The result should be the return value of the mocked out '
'base64 method')
assert 'base64mock' == result, 'The result should be the return value of the mocked out base64 method'
def test_create_thumb_with_size(self):
"""
@ -286,16 +285,16 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created and scaled to the given size.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(thumb_size, QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size')
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert thumb_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -320,17 +319,16 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(str(thumb_path)).size(),
'The thumb should have the given size')
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -356,17 +354,16 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(expected_size, QtGui.QImageReader(str(thumb_path)).size(),
'The thumb should have the given size')
assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists'
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -392,17 +389,16 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(
expected_size, QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size')
assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists'
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -428,17 +424,16 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created, retaining its aspect ratio.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(
expected_size, QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size')
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -465,7 +460,7 @@ class TestLib(TestCase):
pass
# Only continue when the thumb does not exist.
self.assertFalse(thumb_path.exists(), 'Test was not run, because the thumb already exists.')
assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.'
# WHEN: Create the thumb.
with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
@ -474,10 +469,9 @@ class TestLib(TestCase):
# THEN: Check if the thumb was created with aspect ratio of 1.
self.assertTrue(thumb_path.exists(), 'Test was not ran, because the thumb already exists')
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(
expected_size_1, QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size')
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size_1 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# WHEN: Create the thumb.
with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size:
@ -485,10 +479,9 @@ class TestLib(TestCase):
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created with aspect ratio of 1.
self.assertIsInstance(icon, QtGui.QIcon, 'The icon should be a QIcon')
self.assertFalse(icon.isNull(), 'The icon should not be null')
self.assertEqual(
expected_size_2, QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size')
assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon'
assert icon.isNull() is False, 'The icon should not be null'
assert expected_size_2 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
@ -511,7 +504,7 @@ class TestLib(TestCase):
# THEN: The selectedIndexes function should have been called and the result should be true
mocked_list_widget.selectedIndexes.assert_called_with()
self.assertTrue(result, 'The result should be True')
assert result is True, 'The result should be True'
def test_check_item_selected_false(self):
"""
@ -532,7 +525,7 @@ class TestLib(TestCase):
# THEN: The selectedIndexes function should have been called and the result should be true
mocked_list_widget.selectedIndexes.assert_called_with()
MockedQtWidgets.QMessageBox.information.assert_called_with('parent', 'mocked translate', 'message')
self.assertFalse(result, 'The result should be False')
assert result is False, 'The result should be False'
def test_clean_tags(self):
"""
@ -554,7 +547,7 @@ class TestLib(TestCase):
result_string = clean_tags(string_to_pass)
# THEN: The strings should be identical.
self.assertEqual(wanted_string, result_string, 'The strings should be identical')
assert wanted_string == result_string, 'The strings should be identical'
def test_expand_tags(self):
"""
@ -593,7 +586,7 @@ class TestLib(TestCase):
result_string = expand_tags(string_to_pass)
# THEN: The strings should be identical.
self.assertEqual(wanted_string, result_string, 'The strings should be identical.')
assert wanted_string == result_string, 'The strings should be identical.'
def test_validate_thumb_file_does_not_exist(self):
"""
@ -609,7 +602,7 @@ class TestLib(TestCase):
# THEN: we should have called a few functions, and the result should be False
thumb_path.exists.assert_called_once_with()
self.assertFalse(result, 'The result should be False')
assert result is False, 'The result should be False'
def test_validate_thumb_file_exists_and_newer(self):
"""
@ -624,7 +617,7 @@ class TestLib(TestCase):
result = validate_thumb(file_path, thumb_path)
# THEN: `validate_thumb` should return True
self.assertTrue(result)
assert result is True
def test_validate_thumb_file_exists_and_older(self):
"""
@ -639,7 +632,7 @@ class TestLib(TestCase):
# THEN: `validate_thumb` should return False
thumb_path.stat.assert_called_once_with()
self.assertFalse(result, 'The result should be False')
assert result is False, 'The result should be False'
def test_resize_thumb(self):
"""
@ -658,9 +651,9 @@ class TestLib(TestCase):
# THEN: Check if the size is correct and the background was set.
result_size = image.size()
self.assertEqual(wanted_height, result_size.height(), 'The image should have the requested height.')
self.assertEqual(wanted_width, result_size.width(), 'The image should have the requested width.')
self.assertEqual(image.pixel(0, 0), wanted_background_rgb, 'The background should be white.')
assert wanted_height == result_size.height(), 'The image should have the requested height.'
assert wanted_width == result_size.width(), 'The image should have the requested width.'
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
def test_resize_thumb_ignoring_aspect_ratio(self):
"""
@ -679,9 +672,9 @@ class TestLib(TestCase):
# THEN: Check if the size is correct and the background was set.
result_size = image.size()
self.assertEqual(wanted_height, result_size.height(), 'The image should have the requested height.')
self.assertEqual(wanted_width, result_size.width(), 'The image should have the requested width.')
self.assertEqual(image.pixel(0, 0), wanted_background_rgb, 'The background should be white.')
assert wanted_height == result_size.height(), 'The image should have the requested height.'
assert wanted_width == result_size.width(), 'The image should have the requested width.'
assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.'
@patch('openlp.core.lib.QtCore.QLocale.createSeparatedList')
def test_create_separated_list_qlocate(self, mocked_createSeparatedList):
@ -696,8 +689,8 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We should have "Author 1, Author 2, and Author 3"
self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
'Author 2, and Author 3".')
assert string_result == 'Author 1, Author 2 and Author 3', \
'The string should be "Author 1, Author 2, and Author 3".'
def test_create_separated_list_empty_list(self):
"""
@ -710,7 +703,7 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We shoud have an emptry string.
self.assertEqual(string_result, '', 'The string sould be empty.')
assert string_result == '', 'The string sould be empty.'
def test_create_separated_list_with_one_item(self):
"""
@ -723,7 +716,7 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We should have "Author 1"
self.assertEqual(string_result, 'Author 1', 'The string should be "Author 1".')
assert string_result == 'Author 1', 'The string should be "Author 1".'
def test_create_separated_list_with_two_items(self):
"""
@ -736,7 +729,7 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We should have "Author 1 and Author 2"
self.assertEqual(string_result, 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".')
assert string_result == 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".'
def test_create_separated_list_with_three_items(self):
"""
@ -749,8 +742,8 @@ class TestLib(TestCase):
string_result = create_separated_list(string_list)
# THEN: We should have "Author 1, Author 2 and Author 3"
self.assertEqual(string_result, 'Author 1, Author 2 and Author 3', 'The string should be "Author 1, '
'Author 2, and Author 3".')
assert string_result == 'Author 1, Author 2 and Author 3', \
'The string should be "Author 1, Author 2, and Author 3".'
def test_expand_chords(self):
"""
@ -766,7 +759,7 @@ class TestLib(TestCase):
expected_html = '<span class="chordline firstchordline">H<span class="chord"><span><strong>C</strong></span>' \
'</span>alleluya.<span class="chord"><span><strong>F</strong></span></span><span class="ws">' \
'&nbsp;&nbsp;</span> <span class="chord"><span><strong>G</strong></span></span></span>'
self.assertEqual(expected_html, text_with_expanded_chords, 'The expanded chords should look as expected!')
assert expected_html == text_with_expanded_chords, 'The expanded chords should look as expected!'
def test_expand_chords2(self):
"""
@ -782,7 +775,7 @@ class TestLib(TestCase):
expected_html = '<span class="chordline firstchordline">I<span class="chord"><span><strong>D</strong></span>' \
'</span>&#x27;M NOT MOVED BY WHAT I SEE HALLE<span class="chord"><span><strong>F</strong>' \
'</span></span>LUJA<span class="chord"><span><strong>C</strong></span></span>H</span>'
self.assertEqual(expected_html, text_with_expanded_chords, 'The expanded chords should look as expected!')
assert expected_html == text_with_expanded_chords, 'The expanded chords should look as expected!'
def test_compare_chord_lyric_short_chord(self):
"""
@ -810,7 +803,7 @@ class TestLib(TestCase):
ret = compare_chord_lyric(chord, lyrics)
# THEN: The returned value should 4 because the chord is longer than the lyric
self.assertEquals(4, ret, 'The returned value should 4 because the chord is longer than the lyric')
assert 4 == ret, 'The returned value should 4 because the chord is longer than the lyric'
def test_find_formatting_tags(self):
"""
@ -825,7 +818,7 @@ class TestLib(TestCase):
active_tags = find_formatting_tags(lyrics, tags)
# THEN: The list of active tags should contain only 'st'
self.assertListEqual(['st'], active_tags, 'The list of active tags should contain only "st"')
assert ['st'] == active_tags, 'The list of active tags should contain only "st"'
def test_expand_chords_for_printing(self):
"""
@ -862,4 +855,4 @@ class TestLib(TestCase):
'<table class="segment" cellpadding="0" cellspacing="0" border="0" align="left"><tr ' \
'class="chordrow"><td class="chord">F</td></tr><tr><td class="lyrics">{st}{/st}&nbsp;</td>' \
'</tr></table></td></tr></table>'
self.assertEqual(expected_html, text_with_expanded_chords, 'The expanded chords should look as expected!')
assert expected_html == text_with_expanded_chords, 'The expanded chords should look as expected!'

View File

@ -69,11 +69,11 @@ class TestMediaManagerItem(TestCase, TestMixin):
# WHEN: Object is created
mmi.required_icons()
# THEN: Default icons should be populated
self.assertFalse(mmi.has_import_icon, 'There should be no import icon by default')
self.assertTrue(mmi.has_new_icon, 'By default a new icon should be present')
self.assertFalse(mmi.has_file_icon, 'There should be no file icon by default')
self.assertTrue(mmi.has_delete_icon, 'By default a delete icon should be present')
self.assertFalse(mmi.add_to_service_item, 'There should be no add_to_service icon by default')
assert mmi.has_import_icon is False, 'There should be no import icon by default'
assert mmi.has_new_icon is True, 'By default a new icon should be present'
assert mmi.has_file_icon is False, 'There should be no file icon by default'
assert mmi.has_delete_icon is True, 'By default a delete icon should be present'
assert mmi.add_to_service_item is False, 'There should be no add_to_service icon by default'
@patch('openlp.core.lib.mediamanageritem.Settings')
@patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click')
@ -111,5 +111,5 @@ class TestMediaManagerItem(TestCase, TestMixin):
mmi.on_double_clicked()
# THEN: on_live_click() should have been called
self.assertEqual(0, mocked_on_live_click.call_count, 'on_live_click() should not have been called')
self.assertEqual(0, mocked_on_preview_click.call_count, 'on_preview_click() should not have been called')
assert 0 == mocked_on_live_click.call_count, 'on_live_click() should not have been called'
assert 0 == mocked_on_preview_click.call_count, 'on_preview_click() should not have been called'

View File

@ -64,8 +64,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_media_manager()
# THEN: The create_media_manager_item() method should have been called
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
'The create_media_manager_item() method should not have been called.')
assert 0 == mocked_plugin.create_media_manager_item.call_count, \
'The create_media_manager_item() method should not have been called.'
def test_hook_media_manager_with_active_plugin(self):
"""
@ -97,8 +97,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_settings_tabs()
# THEN: The hook_settings_tabs() method should have been called
self.assertEqual(0, mocked_plugin.create_media_manager_item.call_count,
'The create_media_manager_item() method should not have been called.')
assert 0 == mocked_plugin.create_media_manager_item.call_count, \
'The create_media_manager_item() method should not have been called.'
def test_hook_settings_tabs_with_disabled_plugin_and_mocked_form(self):
"""
@ -117,10 +117,10 @@ class TestPluginManager(TestCase):
plugin_manager.hook_settings_tabs()
# THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same
self.assertEqual(0, mocked_plugin.create_settings_tab.call_count,
'The create_media_manager_item() method should not have been called.')
self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
'The plugins on the settings form should be the same as the plugins in the plugin manager')
assert 0 == mocked_plugin.create_settings_tab.call_count, \
'The create_media_manager_item() method should not have been called.'
assert mocked_settings_form.plugin_manager.plugins == plugin_manager.plugins, \
'The plugins on the settings form should be the same as the plugins in the plugin manager'
def test_hook_settings_tabs_with_active_plugin_and_mocked_form(self):
"""
@ -139,10 +139,10 @@ class TestPluginManager(TestCase):
plugin_manager.hook_settings_tabs()
# THEN: The create_media_manager_item() method should have been called with the mocked settings form
self.assertEqual(1, mocked_plugin.create_settings_tab.call_count,
'The create_media_manager_item() method should have been called once.')
self.assertEqual(plugin_manager.plugins, mocked_settings_form.plugin_manager.plugins,
'The plugins on the settings form should be the same as the plugins in the plugin manager')
assert 1 == mocked_plugin.create_settings_tab.call_count, \
'The create_media_manager_item() method should have been called once.'
assert plugin_manager.plugins == mocked_settings_form.plugin_manager.plugins, \
'The plugins on the settings form should be the same as the plugins in the plugin manager'
def test_hook_settings_tabs_with_active_plugin_and_no_form(self):
"""
@ -174,8 +174,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_import_menu()
# THEN: The create_media_manager_item() method should have been called
self.assertEqual(0, mocked_plugin.add_import_menu_item.call_count,
'The add_import_menu_item() method should not have been called.')
assert 0 == mocked_plugin.add_import_menu_item.call_count, \
'The add_import_menu_item() method should not have been called.'
def test_hook_import_menu_with_active_plugin(self):
"""
@ -207,8 +207,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_export_menu()
# THEN: The add_export_menu_item() method should not have been called
self.assertEqual(0, mocked_plugin.add_export_menu_item.call_count,
'The add_export_menu_item() method should not have been called.')
assert 0 == mocked_plugin.add_export_menu_item.call_count, \
'The add_export_menu_item() method should not have been called.'
def test_hook_export_menu_with_active_plugin(self):
"""
@ -241,8 +241,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_upgrade_plugin_settings(settings)
# THEN: The upgrade_settings() method should not have been called
self.assertEqual(0, mocked_plugin.upgrade_settings.call_count,
'The upgrade_settings() method should not have been called.')
assert 0 == mocked_plugin.upgrade_settings.call_count, \
'The upgrade_settings() method should not have been called.'
def test_hook_upgrade_plugin_settings_with_active_plugin(self):
"""
@ -275,8 +275,8 @@ class TestPluginManager(TestCase):
plugin_manager.hook_tools_menu()
# THEN: The add_tools_menu_item() method should have been called
self.assertEqual(0, mocked_plugin.add_tools_menu_item.call_count,
'The add_tools_menu_item() method should not have been called.')
assert 0 == mocked_plugin.add_tools_menu_item.call_count, \
'The add_tools_menu_item() method should not have been called.'
def test_hook_tools_menu_with_active_plugin(self):
"""
@ -310,7 +310,7 @@ class TestPluginManager(TestCase):
# THEN: The is_active() method should have been called, and initialise() method should NOT have been called
mocked_plugin.is_active.assert_called_with()
self.assertEqual(0, mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.')
assert 0 == mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.'
def test_initialise_plugins_with_active_plugin(self):
"""
@ -346,7 +346,7 @@ class TestPluginManager(TestCase):
# THEN: The is_active() method should have been called, and initialise() method should NOT have been called
mocked_plugin.is_active.assert_called_with()
self.assertEqual(0, mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.')
assert 0 == mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.'
def test_finalise_plugins_with_active_plugin(self):
"""
@ -380,7 +380,7 @@ class TestPluginManager(TestCase):
result = plugin_manager.get_plugin_by_name('Missing Plugin')
# THEN: The is_active() and finalise() methods should have been called
self.assertIsNone(result, 'The result for get_plugin_by_name should be None')
assert result is None, 'The result for get_plugin_by_name should be None'
def test_get_plugin_by_name_exists(self):
"""
@ -396,7 +396,7 @@ class TestPluginManager(TestCase):
result = plugin_manager.get_plugin_by_name('Mocked Plugin')
# THEN: The is_active() and finalise() methods should have been called
self.assertEqual(result, mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin')
assert result == mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin'
def test_new_service_created_with_disabled_plugin(self):
"""
@ -414,8 +414,8 @@ class TestPluginManager(TestCase):
# THEN: The isActive() method should have been called, and initialise() method should NOT have been called
mocked_plugin.is_active.assert_called_with()
self.assertEqual(0, mocked_plugin.new_service_created.call_count,
'The new_service_created() method should not have been called.')
assert 0 == mocked_plugin.new_service_created.call_count, \
'The new_service_created() method should not have been called.'
def test_new_service_created_with_active_plugin(self):
"""

View File

@ -28,7 +28,9 @@ from unittest.mock import MagicMock, patch
from openlp.core.common import md5_hash
from openlp.core.common.registry import Registry
from openlp.core.common.settings import Settings
from openlp.core.lib import ItemCapabilities, ServiceItem, ServiceItemType, FormattingTags
from tests.helpers.testmixin import TestMixin
from tests.utils import assert_length, convert_file_service_item
@ -59,19 +61,31 @@ RENDERED_VERSE = 'The Lord said to <span style="-webkit-text-fill-color:red">Noa
FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456']
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'service'))
__default_settings__ = {
'songs/enable chords': True,
}
class TestServiceItem(TestCase):
class TestServiceItem(TestCase, TestMixin):
def setUp(self):
"""
Set up the Registry
"""
self.build_settings()
Settings().extend_default_settings(__default_settings__)
Registry.create()
mocked_renderer = MagicMock()
mocked_renderer.format_slide.return_value = [VERSE]
Registry().register('renderer', mocked_renderer)
Registry().register('image_manager', MagicMock())
def tearDown(self):
"""
Clean up
"""
self.destroy_settings()
def test_service_item_basic(self):
"""
Test the Service Item - basic test
@ -82,8 +96,8 @@ class TestServiceItem(TestCase):
service_item = ServiceItem(None)
# THEN: We should get back a valid service item
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
self.assertTrue(service_item.missing_frames(), 'There should not be any frames in the service item')
assert service_item.is_valid is True, 'The new service item should be valid'
assert service_item.missing_frames() is True, 'There should not be any frames in the service item'
def test_service_item_load_custom_from_service(self):
"""
@ -99,7 +113,7 @@ class TestServiceItem(TestCase):
service_item.set_from_service(line)
# THEN: We should get back a valid service item
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
assert service_item.is_valid is True, 'The new service item should be valid'
assert_length(0, service_item._display_frames, 'The service item should have no display frames')
assert_length(5, service_item.capabilities, 'There should be 5 default custom item capabilities')
@ -107,14 +121,14 @@ class TestServiceItem(TestCase):
service_item.render(True)
# THEN: The frames should also be valid
self.assertEqual('Test Custom', service_item.get_display_title(), 'The title should be "Test Custom"')
self.assertEqual(CLEANED_VERSE[:-1], service_item.get_frames()[0]['text'],
'The returned text matches the input, except the last line feed')
self.assertEqual(RENDERED_VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1),
'The first line has been returned')
self.assertEqual('Slide 1', service_item.get_frame_title(0), '"Slide 1" has been returned as the title')
self.assertEqual('Slide 2', service_item.get_frame_title(1), '"Slide 2" has been returned as the title')
self.assertEqual('', service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3')
assert 'Test Custom' == service_item.get_display_title(), 'The title should be "Test Custom"'
assert CLEANED_VERSE[:-1] == service_item.get_frames()[0]['text'], \
'The returned text matches the input, except the last line feed'
assert RENDERED_VERSE.split('\n', 1)[0] == service_item.get_rendered_frame(1), \
'The first line has been returned'
assert 'Slide 1' == service_item.get_frame_title(0), '"Slide 1" has been returned as the title'
assert 'Slide 2' == service_item.get_frame_title(1), '"Slide 2" has been returned as the title'
assert '' == service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3'
def test_service_item_load_image_from_service(self):
"""
@ -138,26 +152,22 @@ class TestServiceItem(TestCase):
service_item.set_from_service(line, TEST_PATH)
# THEN: We should get back a valid service item
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
self.assertEqual(os.path.normpath(test_file), os.path.normpath(service_item.get_rendered_frame(0)),
'The first frame should match the path to the image')
self.assertEqual(frame_array, service_item.get_frames()[0],
'The return should match frame array1')
self.assertEqual(test_file, service_item.get_frame_path(0),
'The frame path should match the full path to the image')
self.assertEqual(image_name, service_item.get_frame_title(0),
'The frame title should match the image name')
self.assertEqual(image_name, service_item.get_display_title(),
'The display title should match the first image name')
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
'This service item should be able to be Maintained')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
'This service item should be able to be be Previewed')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
'This service item should be able to be run in a can be made to Loop')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
'This service item should be able to have new items added to it')
assert service_item.is_valid is True, 'The new service item should be valid'
assert os.path.normpath(test_file) == os.path.normpath(service_item.get_rendered_frame(0)), \
'The first frame should match the path to the image'
assert frame_array == service_item.get_frames()[0], 'The return should match frame array1'
assert test_file == service_item.get_frame_path(0), 'The frame path should match the full path to the image'
assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name'
assert image_name == service_item.get_display_title(), 'The display title should match the first image name'
assert service_item.is_image() is True, 'This service item should be of an "image" type'
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
'This service item should be able to be Maintained'
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
'This service item should be able to be be Previewed'
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
'This service item should be able to be run in a can be made to Loop'
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
'This service item should be able to have new items added to it'
def test_service_item_load_image_from_local_service(self):
"""
@ -193,35 +203,33 @@ class TestServiceItem(TestCase):
# This test is copied from service_item.py, but is changed since to conform to
# new layout of service item. The layout use in serviceitem_image_2.osd is actually invalid now.
self.assertTrue(service_item.is_valid, 'The first service item should be valid')
self.assertTrue(service_item2.is_valid, 'The second service item should be valid')
assert service_item.is_valid is True, 'The first service item should be valid'
assert service_item2.is_valid is True, 'The second service item should be valid'
# These test will fail on windows due to the difference in folder seperators
if os.name != 'nt':
self.assertEqual(test_file1, service_item.get_rendered_frame(0),
'The first frame should match the path to the image')
self.assertEqual(test_file2, service_item2.get_rendered_frame(0),
'The Second frame should match the path to the image')
self.assertEqual(frame_array1, service_item.get_frames()[0], 'The return should match the frame array1')
self.assertEqual(frame_array2, service_item2.get_frames()[0], 'The return should match the frame array2')
self.assertEqual(test_file1, service_item.get_frame_path(0),
'The frame path should match the full path to the image')
self.assertEqual(test_file2, service_item2.get_frame_path(0),
'The frame path should match the full path to the image')
self.assertEqual(image_name1, service_item.get_frame_title(0),
'The 1st frame title should match the image name')
self.assertEqual(image_name2, service_item2.get_frame_title(0),
'The 2nd frame title should match the image name')
self.assertEqual(service_item.name, service_item.title.lower(),
'The plugin name should match the display title, as there are > 1 Images')
self.assertTrue(service_item.is_image(), 'This service item should be of an "image" type')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanMaintain),
'This service item should be able to be Maintained')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanPreview),
'This service item should be able to be be Previewed')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanLoop),
'This service item should be able to be run in a can be made to Loop')
self.assertTrue(service_item.is_capable(ItemCapabilities.CanAppend),
'This service item should be able to have new items added to it')
assert test_file1 == service_item.get_rendered_frame(0), \
'The first frame should match the path to the image'
assert test_file2 == service_item2.get_rendered_frame(0), \
'The Second frame should match the path to the image'
assert frame_array1 == service_item.get_frames()[0], 'The return should match the frame array1'
assert frame_array2 == service_item2.get_frames()[0], 'The return should match the frame array2'
assert test_file1 == service_item.get_frame_path(0), \
'The frame path should match the full path to the image'
assert test_file2 == service_item2.get_frame_path(0), \
'The frame path should match the full path to the image'
assert image_name1 == service_item.get_frame_title(0), 'The 1st frame title should match the image name'
assert image_name2 == service_item2.get_frame_title(0), 'The 2nd frame title should match the image name'
assert service_item.name == service_item.title.lower(), \
'The plugin name should match the display title, as there are > 1 Images'
assert service_item.is_image() is True, 'This service item should be of an "image" type'
assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \
'This service item should be able to be Maintained'
assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \
'This service item should be able to be be Previewed'
assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \
'This service item should be able to be run in a can be made to Loop'
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
'This service item should be able to have new items added to it'
def test_add_from_command_for_a_presentation(self):
"""
@ -240,8 +248,8 @@ class TestServiceItem(TestCase):
service_item.add_from_command(TEST_PATH, presentation_name, image, display_title, notes)
# THEN: verify that it is setup as a Command and that the frame data matches
self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command'
assert service_item.get_frames()[0] == frame, 'Frames should match'
def test_add_from_comamnd_without_display_title_and_notes(self):
"""
@ -258,8 +266,8 @@ class TestServiceItem(TestCase):
service_item.add_from_command(TEST_PATH, image_name, image)
# THEN: verify that it is setup as a Command and that the frame data matches
self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command'
assert service_item.get_frames()[0] == frame, 'Frames should match'
@patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager')
@patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path')
@ -287,9 +295,9 @@ class TestServiceItem(TestCase):
service_item.add_from_command(TEST_PATH, presentation_name, thumb, display_title, notes)
# THEN: verify that it is setup as a Command and that the frame data matches
self.assertEqual(service_item.service_item_type, ServiceItemType.Command, 'It should be a Command')
self.assertEqual(service_item.get_frames()[0], frame, 'Frames should match')
self.assertEqual(1, mocked_image_manager.add_image.call_count, 'image_manager should be used')
assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command'
assert service_item.get_frames()[0] == frame, 'Frames should match'
assert 1 == mocked_image_manager.add_image.call_count, 'image_manager should be used'
def test_service_item_load_optical_media_from_service(self):
"""
@ -306,11 +314,11 @@ class TestServiceItem(TestCase):
service_item.set_from_service(line)
# THEN: We should get back a valid service item with optical media info
self.assertTrue(service_item.is_valid, 'The service item should be valid')
self.assertTrue(service_item.is_capable(ItemCapabilities.IsOptical), 'The item should be Optical')
self.assertEqual(service_item.start_time, 654.375, 'Start time should be 654.375')
self.assertEqual(service_item.end_time, 672.069, 'End time should be 672.069')
self.assertEqual(service_item.media_length, 17.694, 'Media length should be 17.694')
assert service_item.is_valid is True, 'The service item should be valid'
assert service_item.is_capable(ItemCapabilities.IsOptical) is True, 'The item should be Optical'
assert service_item.start_time == 654.375, 'Start time should be 654.375'
assert service_item.end_time == 672.069, 'End time should be 672.069'
assert service_item.media_length == 17.694, 'Media length should be 17.694'
def test_service_item_load_song_and_audio_from_service(self):
"""
@ -326,22 +334,22 @@ class TestServiceItem(TestCase):
service_item.set_from_service(line, '/test/')
# THEN: We should get back a valid service item
self.assertTrue(service_item.is_valid, 'The new service item should be valid')
assert_length(0, service_item._display_frames, 'The service item should have no display frames')
assert_length(7, service_item.capabilities, 'There should be 7 default custom item capabilities')
assert service_item.is_valid is True, 'The new service item should be valid'
assert 0 == len(service_item._display_frames), 'The service item should have no display frames'
assert 7 == len(service_item.capabilities), 'There should be 7 default custom item capabilities'
# WHEN: We render the frames of the service item
service_item.render(True)
# THEN: The frames should also be valid
self.assertEqual('Amazing Grace', service_item.get_display_title(), 'The title should be "Amazing Grace"')
self.assertEqual(CLEANED_VERSE[:-1], service_item.get_frames()[0]['text'],
'The returned text matches the input, except the last line feed')
self.assertEqual(RENDERED_VERSE.split('\n', 1)[0], service_item.get_rendered_frame(1),
'The first line has been returned')
self.assertEqual('Amazing Grace! how sweet the s', service_item.get_frame_title(0),
'"Amazing Grace! how sweet the s" has been returned as the title')
self.assertEqual('Twas grace that taught my hea', service_item.get_frame_title(1),
'"Twas grace that taught my hea" has been returned as the title')
self.assertEqual('/test/amazing_grace.mp3', service_item.background_audio[0],
'"/test/amazing_grace.mp3" should be in the background_audio list')
assert 'Amazing Grace' == service_item.get_display_title(), 'The title should be "Amazing Grace"'
assert CLEANED_VERSE[:-1] == service_item.get_frames()[0]['text'], \
'The returned text matches the input, except the last line feed'
assert RENDERED_VERSE.split('\n', 1)[0] == service_item.get_rendered_frame(1), \
'The first line has been returned'
assert 'Amazing Grace! how sweet the s' == service_item.get_frame_title(0), \
'"Amazing Grace! how sweet the s" has been returned as the title'
assert 'Twas grace that taught my hea' == service_item.get_frame_title(1), \
'"Twas grace that taught my hea" has been returned as the title'
assert '/test/amazing_grace.mp3' == service_item.background_audio[0], \
'"/test/amazing_grace.mp3" should be in the background_audio list'

View File

@ -90,8 +90,8 @@ class TestTheme(TestCase):
# THEN: The filename of the background should be correct
expected_filename = path / 'MyBeautifulTheme' / 'video.mp4'
self.assertEqual(expected_filename, theme.background_filename)
self.assertEqual('MyBeautifulTheme', theme.theme_name)
assert expected_filename == theme.background_filename
assert 'MyBeautifulTheme' == theme.theme_name
def test_save_retrieve(self):
"""
@ -107,9 +107,9 @@ class TestTheme(TestCase):
self.check_theme(lt)
def check_theme(self, theme):
self.assertEqual('#000000', theme.background_border_color, 'background_border_color should be "#000000"')
self.assertEqual('solid', theme.background_type, 'background_type should be "solid"')
self.assertEqual(0, theme.display_vertical_align, 'display_vertical_align should be 0')
self.assertFalse(theme.font_footer_bold, 'font_footer_bold should be False')
self.assertEqual('Arial', theme.font_main_name, 'font_main_name should be "Arial"')
self.assertEqual(47, len(theme.__dict__), 'The theme should have 47 attributes')
assert '#000000' == theme.background_border_color, 'background_border_color should be "#000000"'
assert 'solid' == theme.background_type, 'background_type should be "solid"'
assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0'
assert theme.font_footer_bold is False, 'font_footer_bold should be False'
assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"'
assert 47 == len(theme.__dict__), 'The theme should have 47 attributes'

View File

@ -49,8 +49,8 @@ class TestUi(TestCase):
add_welcome_page(wizard, ':/wizards/wizard_firsttime.bmp')
# THEN: The wizard should have one page with a pixmap.
self.assertEqual(1, len(wizard.pageIds()), 'The wizard should have one page.')
self.assertIsInstance(wizard.page(0).pixmap(QtWidgets.QWizard.WatermarkPixmap), QtGui.QPixmap)
assert 1 == len(wizard.pageIds()), 'The wizard should have one page.'
assert isinstance(wizard.page(0).pixmap(QtWidgets.QWizard.WatermarkPixmap), QtGui.QPixmap)
def test_create_button_box(self):
"""
@ -63,22 +63,22 @@ class TestUi(TestCase):
btnbox = create_button_box(dialog, 'my_btns', ['ok', 'save', 'cancel', 'close', 'defaults'])
# THEN: We should get a QDialogButtonBox with five buttons
self.assertIsInstance(btnbox, QtWidgets.QDialogButtonBox)
self.assertEqual(5, len(btnbox.buttons()))
assert isinstance(btnbox, QtWidgets.QDialogButtonBox)
assert 5 == len(btnbox.buttons())
# WHEN: We create the button box with a custom button
btnbox = create_button_box(dialog, 'my_btns', None, [QtWidgets.QPushButton('Custom')])
# THEN: We should get a QDialogButtonBox with one button
self.assertIsInstance(btnbox, QtWidgets.QDialogButtonBox)
self.assertEqual(1, len(btnbox.buttons()))
assert isinstance(btnbox, QtWidgets.QDialogButtonBox)
assert 1 == len(btnbox.buttons())
# WHEN: We create the button box with a custom button and a custom role
btnbox = create_button_box(dialog, 'my_btns', None,
[(QtWidgets.QPushButton('Help'), QtWidgets.QDialogButtonBox.HelpRole)])
# THEN: We should get a QDialogButtonBox with one button with a certain role
self.assertIsInstance(btnbox, QtWidgets.QDialogButtonBox)
self.assertEqual(1, len(btnbox.buttons()))
self.assertEqual(QtWidgets.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]))
assert isinstance(btnbox, QtWidgets.QDialogButtonBox)
assert 1 == len(btnbox.buttons())
assert QtWidgets.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0])
def test_create_horizontal_adjusting_combo_box(self):
"""
@ -91,9 +91,9 @@ class TestUi(TestCase):
combo = create_horizontal_adjusting_combo_box(dialog, 'combo1')
# THEN: We should get a ComboBox
self.assertIsInstance(combo, QtWidgets.QComboBox)
self.assertEqual('combo1', combo.objectName())
self.assertEqual(QtWidgets.QComboBox.AdjustToMinimumContentsLength, combo.sizeAdjustPolicy())
assert isinstance(combo, QtWidgets.QComboBox)
assert 'combo1' == combo.objectName()
assert QtWidgets.QComboBox.AdjustToMinimumContentsLength == combo.sizeAdjustPolicy()
def test_create_button(self):
"""
@ -106,26 +106,26 @@ class TestUi(TestCase):
btn = create_button(dialog, 'my_btn')
# THEN: We should get a button with a name
self.assertIsInstance(btn, QtWidgets.QPushButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
assert isinstance(btn, QtWidgets.QPushButton)
assert 'my_btn' == btn.objectName()
assert btn.isEnabled() is True
# WHEN: We create a button with some attributes
btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False)
# THEN: We should get a button with those attributes
self.assertIsInstance(btn, QtWidgets.QPushButton)
self.assertEqual('Hello', btn.text())
self.assertEqual('How are you?', btn.toolTip())
self.assertFalse(btn.isEnabled())
assert isinstance(btn, QtWidgets.QPushButton)
assert 'Hello' == btn.text()
assert 'How are you?' == btn.toolTip()
assert btn.isEnabled() is False
# WHEN: We create a toolbutton
btn = create_button(dialog, 'my_btn', btn_class='toolbutton')
# THEN: We should get a toolbutton
self.assertIsInstance(btn, QtWidgets.QToolButton)
self.assertEqual('my_btn', btn.objectName())
self.assertTrue(btn.isEnabled())
assert isinstance(btn, QtWidgets.QToolButton)
assert 'my_btn' == btn.objectName()
assert btn.isEnabled() is True
def test_create_action(self):
"""
@ -138,19 +138,19 @@ class TestUi(TestCase):
action = create_action(dialog, 'my_action')
# THEN: We should get a QAction
self.assertIsInstance(action, QtWidgets.QAction)
self.assertEqual('my_action', action.objectName())
assert isinstance(action, QtWidgets.QAction)
assert 'my_action' == action.objectName()
# WHEN: We create an action with some properties
action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp',
tooltip='my tooltip', statustip='my statustip')
# THEN: These properties should be set
self.assertIsInstance(action, QtWidgets.QAction)
self.assertEqual('my text', action.text())
self.assertIsInstance(action.icon(), QtGui.QIcon)
self.assertEqual('my tooltip', action.toolTip())
self.assertEqual('my statustip', action.statusTip())
assert isinstance(action, QtWidgets.QAction)
assert 'my text' == action.text()
assert isinstance(action.icon(), QtGui.QIcon)
assert 'my tooltip' == action.toolTip()
assert 'my statustip' == action.statusTip()
def test_create_action_on_mac_osx(self):
"""
@ -186,8 +186,8 @@ class TestUi(TestCase):
create_action(dialog, 'my_action')
# THEN: setIconVisibleInMenu should not be called
self.assertEqual(0, mocked_action.setIconVisibleInMenu.call_count,
'setIconVisibleInMenu should not have been called')
assert 0 == mocked_action.setIconVisibleInMenu.call_count, \
'setIconVisibleInMenu should not have been called'
def test_create_checked_disabled_invisible_action(self):
"""
@ -200,9 +200,9 @@ class TestUi(TestCase):
action = create_action(dialog, 'my_action', checked=True, enabled=False, visible=False)
# THEN: These properties should be set
self.assertTrue(action.isChecked(), 'The action should be checked')
self.assertFalse(action.isEnabled(), 'The action should be disabled')
self.assertFalse(action.isVisible(), 'The action should be invisble')
assert action.isChecked() is True, 'The action should be checked'
assert action.isEnabled() is False, 'The action should be disabled'
assert action.isVisible() is False, 'The action should be invisble'
def test_create_action_separator(self):
"""
@ -215,7 +215,7 @@ class TestUi(TestCase):
action = create_action(dialog, 'my_action', separator=True)
# THEN: The action should be a separator
self.assertTrue(action.isSeparator(), 'The action should be a separator')
assert action.isSeparator() is True, 'The action should be a separator'
def test_create_valign_selection_widgets(self):
"""
@ -228,11 +228,11 @@ class TestUi(TestCase):
label, combo = create_valign_selection_widgets(dialog)
# THEN: We should get a label and a combobox.
self.assertEqual(translate('OpenLP.Ui', '&Vertical Align:'), label.text())
self.assertIsInstance(combo, QtWidgets.QComboBox)
self.assertEqual(combo, label.buddy())
assert translate('OpenLP.Ui', '&Vertical Align:') == label.text()
assert isinstance(combo, QtWidgets.QComboBox)
assert combo == label.buddy()
for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]:
self.assertTrue(combo.findText(text) >= 0)
assert combo.findText(text) >= 0
def test_find_and_set_in_combo_box(self):
"""
@ -247,19 +247,19 @@ class TestUi(TestCase):
find_and_set_in_combo_box(combo, 'Four', set_missing=False)
# THEN: The index should not have changed
self.assertEqual(1, combo.currentIndex())
assert 1 == combo.currentIndex()
# WHEN: We call the method with a non-existing value
find_and_set_in_combo_box(combo, 'Four')
# THEN: The index should have been reset
self.assertEqual(0, combo.currentIndex())
assert 0 == combo.currentIndex()
# WHEN: We call the method with the default behavior
find_and_set_in_combo_box(combo, 'Three')
# THEN: The index should have changed
self.assertEqual(2, combo.currentIndex())
assert 2 == combo.currentIndex()
def test_create_widget_action(self):
"""
@ -272,8 +272,8 @@ class TestUi(TestCase):
action = create_widget_action(button, 'some action')
# THEN: The action should be returned
self.assertIsInstance(action, QtWidgets.QAction)
self.assertEqual(action.objectName(), 'some action')
assert isinstance(action, QtWidgets.QAction)
assert action.objectName() == 'some action'
def test_set_case_insensitive_completer(self):
"""
@ -288,5 +288,5 @@ class TestUi(TestCase):
# THEN: The Combobox should have a completer which is case insensitive
completer = line_edit.completer()
self.assertIsInstance(completer, QtWidgets.QCompleter)
self.assertEqual(completer.caseSensitivity(), QtCore.Qt.CaseInsensitive)
assert isinstance(completer, QtWidgets.QCompleter)
assert completer.caseSensitivity() == QtCore.Qt.CaseInsensitive

View File

@ -23,12 +23,11 @@
Package to test the openlp.core.projectors.pjlink base package.
"""
from unittest import TestCase
from unittest.mock import patch
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST_PIN, TEST_CONNECT_AUTHENTICATE, TEST_HASH, TEST1_DATA
from tests.resources.projector.data import TEST1_DATA
class TestPJLinkBugs(TestCase):
@ -80,43 +79,17 @@ class TestPJLinkBugs(TestCase):
"""
Test bug 1593882 no pin and authenticated request exception
"""
# GIVEN: Test object and mocks
mock_socket_timer = patch.object(self.pjlink_test, 'socket_timer').start()
mock_timer = patch.object(self.pjlink_test, 'timer').start()
mock_authentication = patch.object(self.pjlink_test, 'projectorAuthentication').start()
mock_ready_read = patch.object(self.pjlink_test, 'waitForReadyRead').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
pjlink = self.pjlink_test
pjlink.pin = None
mock_ready_read.return_value = True
# WHEN: call with authentication request and pin not set
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: 'No Authentication' signal should have been sent
mock_authentication.emit.assert_called_with(pjlink.ip)
# Test now part of test_projector_pjlink_commands_02
# Keeping here for bug reference
pass
def test_bug_1593883_pjlink_authentication(self):
"""
Test bugfix 1593883 pjlink authentication
Test bugfix 1593883 pjlink authentication and ticket 92187
"""
# GIVEN: Test object and data
mock_socket_timer = patch.object(self.pjlink_test, 'socket_timer').start()
mock_timer = patch.object(self.pjlink_test, 'timer').start()
mock_send_command = patch.object(self.pjlink_test, 'write').start()
mock_state = patch.object(self.pjlink_test, 'state').start()
mock_waitForReadyRead = patch.object(self.pjlink_test, 'waitForReadyRead').start()
pjlink = self.pjlink_test
pjlink.pin = TEST_PIN
mock_state.return_value = pjlink.ConnectedState
mock_waitForReadyRead.return_value = True
# WHEN: Athenticated connection is called
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: send_command should have the proper authentication
self.assertEqual("{test}".format(test=mock_send_command.call_args),
"call(b'{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))
# Test now part of test_projector_pjlink_commands_02
# Keeping here for bug reference
pass
def test_bug_1734275_process_lamp_nonstandard_reply(self):
"""

View File

@ -25,11 +25,11 @@ Package to test the openlp.core.projectors.pjlink base package.
from unittest import TestCase
from unittest.mock import call, patch, MagicMock
from openlp.core.projectors.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED
from openlp.core.projectors.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED, S_QSOCKET_STATE
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST1_DATA
from tests.resources.projector.data import TEST1_DATA
pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
@ -38,29 +38,17 @@ class TestPJLinkBase(TestCase):
"""
Tests for the PJLink module
"""
@patch.object(pjlink_test, 'readyRead')
@patch.object(pjlink_test, 'send_command')
@patch.object(pjlink_test, 'waitForReadyRead')
@patch('openlp.core.common.qmd5_hash')
def test_authenticated_connection_call(self,
mock_qmd5_hash,
mock_waitForReadyRead,
mock_send_command,
mock_readyRead):
"""
Ticket 92187: Fix for projector connect with PJLink authentication exception.
"""
# GIVEN: Test object
pjlink = pjlink_test
def setUp(self):
'''
TestPJLinkCommands part 2 initialization
'''
self.pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
# WHEN: Calling check_login with authentication request:
pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
# THEN: Should have called qmd5_hash
self.assertTrue(mock_qmd5_hash.called_with(TEST_SALT,
"Connection request should have been called with TEST_SALT"))
self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
"Connection request should have been called with TEST_PIN"))
def tearDown(self):
'''
TestPJLinkCommands part 2 cleanups
'''
self.pjlink_test = None
@patch.object(pjlink_test, 'change_status')
def test_status_change(self, mock_change_status):
@ -110,18 +98,18 @@ class TestPJLinkBase(TestCase):
# THEN: poll_loop should exit without calling any other method
self.assertFalse(pjlink.timer.called, 'Should have returned without calling any other method')
@patch.object(pjlink_test, 'send_command')
def test_poll_loop_start(self, mock_send_command):
def test_poll_loop_start(self):
"""
Test PJLink.poll_loop makes correct calls
"""
# GIVEN: test object and test data
pjlink = pjlink_test
pjlink.state = MagicMock()
pjlink.timer = MagicMock()
pjlink.timer.interval = MagicMock()
pjlink.timer.setInterval = MagicMock()
pjlink.timer.start = MagicMock()
# GIVEN: Mocks and test data
mock_state = patch.object(self.pjlink_test, 'state').start()
mock_state.return_value = S_QSOCKET_STATE['ConnectedState']
mock_timer = patch.object(self.pjlink_test, 'timer').start()
mock_timer.interval.return_value = 10
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
pjlink = self.pjlink_test
pjlink.poll_time = 20
pjlink.power = S_ON
pjlink.source_available = None
@ -130,19 +118,17 @@ class TestPJLinkBase(TestCase):
pjlink.model = None
pjlink.pjlink_name = None
pjlink.ConnectedState = S_CONNECTED
pjlink.timer.interval.return_value = 10
pjlink.state.return_value = S_CONNECTED
call_list = [
call('POWR', queue=True),
call('ERST', queue=True),
call('LAMP', queue=True),
call('AVMT', queue=True),
call('INPT', queue=True),
call('INST', queue=True),
call('INFO', queue=True),
call('INF1', queue=True),
call('INF2', queue=True),
call('NAME', queue=True),
call('POWR'),
call('ERST'),
call('LAMP'),
call('AVMT'),
call('INPT'),
call('INST'),
call('INFO'),
call('INF1'),
call('INF2'),
call('NAME'),
]
# WHEN: PJLink.poll_loop is called
@ -150,8 +136,8 @@ class TestPJLinkBase(TestCase):
# THEN: proper calls were made to retrieve projector data
# First, call to update the timer with the next interval
self.assertTrue(pjlink.timer.setInterval.called, 'Should have updated the timer')
self.assertTrue(mock_timer.setInterval.called)
# Next, should have called the timer to start
self.assertTrue(pjlink.timer.start.called, 'Should have started the timer')
self.assertTrue(mock_timer.start.called, 'Should have started the timer')
# Finally, should have called send_command with a list of projetctor status checks
mock_send_command.assert_has_calls(call_list, 'Should have queued projector queries')

View File

@ -46,6 +46,18 @@ class TestPJLinkRouting(TestCase):
"""
Tests for the PJLink module command routing
"""
def setUp(self):
'''
TestPJLinkCommands part 2 initialization
'''
self.pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
'''
TestPJLinkCommands part 2 cleanups
'''
self.pjlink_test = None
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_call_clss(self, mock_log):
"""
@ -163,21 +175,20 @@ class TestPJLinkRouting(TestCase):
mock_change_status.assert_called_once_with(E_AUTHENTICATION)
mock_log.error.assert_called_with(log_text)
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_future(self, mock_log):
def test_process_command_future(self):
"""
Test command valid but no method to process yet
"""
# GIVEN: Test object
pjlink = pjlink_test
log_text = "(127.0.0.1) Unable to process command='CLSS' (Future option)"
mock_log.reset_mock()
# Remove a valid command so we can test valid command but not available yet
pjlink.pjlink_functions.pop('CLSS')
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_functions = patch.object(self.pjlink_test, 'pjlink_functions').start()
mock_functions.return_value = []
pjlink = self.pjlink_test
log_text = '(111.111.111.111) Unable to process command="CLSS" (Future option?)'
# WHEN: process_command called with an unknown command
with patch.object(pjlink, 'pjlink_functions') as mock_functions:
pjlink.process_command(cmd='CLSS', data='DONT CARE')
pjlink.process_command(cmd='CLSS', data='DONT CARE')
# THEN: Error should be logged and no command called
self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
@ -196,29 +207,26 @@ class TestPJLinkRouting(TestCase):
# WHEN: process_command called with an unknown command
pjlink.process_command(cmd='Unknown', data='Dont Care')
log_text = "(127.0.0.1) Ignoring command='Unknown' (Invalid/Unknown)"
log_text = '(127.0.0.1) Ignoring command="Unknown" (Invalid/Unknown)'
# THEN: Error should be logged and no command called
self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
mock_log.error.assert_called_once_with(log_text)
@patch.object(pjlink_test, 'pjlink_functions')
@patch.object(openlp.core.projectors.pjlink, 'log')
def test_process_command_ok(self, mock_log, mock_functions):
def test_process_command_ok(self):
"""
Test command returned success
"""
# GIVEN: Test object
pjlink = pjlink_test
mock_functions.reset_mock()
mock_log.reset_mock()
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
# WHEN: process_command called with an unknown command
pjlink.process_command(cmd='CLSS', data='OK')
log_text = '(127.0.0.1) Command "CLSS" returned OK'
pjlink = self.pjlink_test
log_text = '(111.111.111.111) Command "POWR" returned OK'
# THEN: Error should be logged and no command called
self.assertFalse(mock_functions.called, 'Should not have gotten to the end of the method')
self.assertEqual(mock_log.debug.call_count, 2, 'log.debug() should have been called twice')
# Although we called it twice, only the last log entry is saved
# WHEN: process_command called with a command that returns OK
pjlink.process_command(cmd='POWR', data='OK')
# THEN: Appropriate calls should have been made
mock_log.debug.assert_called_with(log_text)
mock_send_command.assert_called_once_with(cmd='POWR')

View File

@ -47,7 +47,7 @@ for pos in range(0, len(PJLINK_ERST_DATA)):
class TestPJLinkCommands(TestCase):
"""
Tests for the PJLink module
Tests for the PJLinkCommands class part 1
"""
@patch.object(pjlink_test, 'changeStatus')
@patch.object(openlp.core.projectors.pjlink, 'log')
@ -580,7 +580,7 @@ class TestPJLinkCommands(TestCase):
# WHEN: Process invalid reply
pjlink.process_clss('Z')
log_text = "(127.0.0.1) NAN clss version reply 'Z' - defaulting to class '1'"
log_text = '(127.0.0.1) NAN CLSS version reply "Z" - defaulting to class "1"'
# THEN: Projector class should be set with default value
self.assertEqual(pjlink.pjlink_class, '1',
@ -597,7 +597,7 @@ class TestPJLinkCommands(TestCase):
# WHEN: Process invalid reply
pjlink.process_clss('Invalid')
log_text = "(127.0.0.1) No numbers found in class version reply 'Invalid' - defaulting to class '1'"
log_text = '(127.0.0.1) No numbers found in class version reply "Invalid" - defaulting to class "1"'
# THEN: Projector class should be set with default value
self.assertEqual(pjlink.pjlink_class, '1',
@ -627,7 +627,7 @@ class TestPJLinkCommands(TestCase):
# GIVEN: Test object
pjlink = pjlink_test
pjlink.projector_errors = None
log_text = "127.0.0.1) Invalid error status response '11111111': length != 6"
log_text = '127.0.0.1) Invalid error status response "11111111": length != 6'
# WHEN: process_erst called with invalid data (too many values
pjlink.process_erst('11111111')
@ -645,7 +645,7 @@ class TestPJLinkCommands(TestCase):
# GIVEN: Test object
pjlink = pjlink_test
pjlink.projector_errors = None
log_text = "(127.0.0.1) Invalid error status response '1111Z1'"
log_text = '(127.0.0.1) Invalid error status response "1111Z1"'
# WHEN: process_erst called with invalid data (too many values
pjlink.process_erst('1111Z1')
@ -671,8 +671,8 @@ class TestPJLinkCommands(TestCase):
# THEN: PJLink instance errors should match chk_value
for chk in pjlink.projector_errors:
self.assertEqual(pjlink.projector_errors[chk], chk_string,
"projector_errors['{chk}'] should have been set to {err}".format(chk=chk,
err=chk_string))
'projector_errors["{chk}"] should have been set to "{err}"'.format(chk=chk,
err=chk_string))
def test_projector_process_erst_all_error(self):
"""
@ -690,8 +690,8 @@ class TestPJLinkCommands(TestCase):
# THEN: PJLink instance errors should match chk_value
for chk in pjlink.projector_errors:
self.assertEqual(pjlink.projector_errors[chk], chk_string,
"projector_errors['{chk}'] should have been set to {err}".format(chk=chk,
err=chk_string))
'projector_errors["{chk}"] should have been set to "{err}"'.format(chk=chk,
err=chk_string))
def test_projector_process_erst_warn_cover_only(self):
"""
@ -744,9 +744,9 @@ class TestPJLinkCommands(TestCase):
pjlink = pjlink_test
pjlink.source_available = []
test_data = '21 10 30 31 11 20'
test_saved = ['10', '11', '20', '21', '30', '31']
log_data = '(127.0.0.1) Setting projector sources_available to ' \
'"[\'10\', \'11\', \'20\', \'21\', \'30\', \'31\']"'
test_saved = ["10", "11", "20", "21", "30", "31"]
log_data = "(127.0.0.1) Setting projector sources_available to " \
"\"['10', '11', '20', '21', '30', '31']\""
mock_UpdateIcons.reset_mock()
mock_log.reset_mock()
@ -1021,7 +1021,7 @@ class TestPJLinkCommands(TestCase):
pjlink.sw_version = None
pjlink.sw_version_received = None
test_data = 'Test 1 Subtest 1'
test_log = "(127.0.0.1) Setting projector software version to 'Test 1 Subtest 1'"
test_log = '(127.0.0.1) Setting projector software version to "Test 1 Subtest 1"'
mock_log.reset_mock()
# WHEN: process_sver called with invalid data

View File

@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2015 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
Package to test the openlp.core.projectors.pjlink commands package.
"""
from unittest import TestCase
from unittest.mock import patch, call
import openlp.core.projectors.pjlink
from openlp.core.projectors.constants import S_CONNECTED
from openlp.core.projectors.db import Projector
from openlp.core.projectors.pjlink import PJLink
from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT, TEST1_DATA
class TestPJLinkCommands(TestCase):
"""
Tests for the PJLinkCommands class part 2
"""
def setUp(self):
'''
TestPJLinkCommands part 2 initialization
'''
self.pjlink_test = PJLink(Projector(**TEST1_DATA), no_poll=True)
def tearDown(self):
'''
TestPJLinkCommands part 2 cleanups
'''
self.pjlink_test = None
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.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
mock_readyRead = patch.object(self.pjlink_test, 'readyRead').start()
mock_change_status = patch.object(self.pjlink_test, 'change_status').start()
pjlink = self.pjlink_test
pjlink.pin = None
log_check = [call("({111.111.111.111}) process_pjlink(): Sending 'CLSS' initial command'"), ]
# 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()
self.assertEqual(mock_readyRead.connect.call_count, 1, '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)
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.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
mock_readyRead = patch.object(self.pjlink_test, 'readyRead').start()
mock_change_status = patch.object(self.pjlink_test, 'change_status').start()
pjlink = self.pjlink_test
pjlink.pin = TEST_PIN
log_check = [call("({111.111.111.111}) process_pjlink(): Sending 'CLSS' initial command'"), ]
# 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()
self.assertEqual(mock_readyRead.connect.call_count, 1, '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)
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
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
pjlink = self.pjlink_test
pjlink.pin = TEST_PIN
log_check = [call('(111.111.111.111) Normal connection but PIN set - aborting'), ]
# 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)
self.assertEqual(mock_disconnect_from_host.call_count, 1, 'Should have only been called once')
mock_send_command.assert_not_called()
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
# GIVEN: Initial mocks and data
mock_log = patch.object(openlp.core.projectors.pjlink, 'log').start()
mock_disconnect_from_host = patch.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
pjlink = self.pjlink_test
pjlink.pin = TEST_PIN
log_check = [call('(111.111.111.111) Normal connection with extra information - aborting'), ]
# 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)
self.assertEqual(mock_disconnect_from_host.call_count, 1, 'Should have only been called once')
mock_send_command.assert_not_called()
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.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
pjlink = self.pjlink_test
log_check = [call('(111.111.111.111) Invalid initial authentication scheme - aborting'), ]
# 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)
self.assertEqual(mock_disconnect_from_host.call_count, 1, 'Should have only been called once')
mock_send_command.assert_not_called()
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.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
log_check = [call('(111.111.111.111) Authenticated connection but not enough info - aborting'), ]
pjlink = self.pjlink_test
# 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)
self.assertEqual(mock_disconnect_from_host.call_count, 1, 'Should have only been called once')
mock_send_command.assert_not_called()
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.object(self.pjlink_test, 'disconnect_from_host').start()
mock_send_command = patch.object(self.pjlink_test, 'send_command').start()
log_check = [call('(111.111.111.111) Authenticate connection but no PIN - aborting'), ]
pjlink = self.pjlink_test
pjlink.pin = None
# 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)
self.assertEqual(mock_disconnect_from_host.call_count, 1, 'Should have only been called once')
mock_send_command.assert_not_called()

View File

@ -106,7 +106,7 @@ class TestMainWindow(TestCase, TestMixin):
self.main_window.open_cmd_line_files("")
# THEN the file should not be opened
assert not mocked_load_file.called, 'load_file should not have been called'
assert mocked_load_file.called is False, 'load_file should not have been called'
def test_main_window_title(self):
"""