diff --git a/openlp/core/projectors/constants.py b/openlp/core/projectors/constants.py index 92568b190..cabef8169 100644 --- a/openlp/core/projectors/constants.py +++ b/openlp/core/projectors/constants.py @@ -32,9 +32,128 @@ log.debug('projector_constants loaded') # Set common constants. CR = chr(0x0D) # \r LF = chr(0x0A) # \n -PJLINK_PORT = 4352 -TIMEOUT = 30.0 +PJLINK_CLASS = '1' # Default to class 1 until we query the projector PJLINK_MAX_PACKET = 136 +PJLINK_PREFIX = '%' +PJLINK_PORT = 4352 +PJLINK_SUFFIX = CR +PJLINK_TIMEOUT = 30.0 + +# 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. +E_GENERAL = 200 # Unknown error +E_NOT_CONNECTED = 201 +E_UNDEFINED = 202 # PJLink ERR1 +E_PARAMETER = 203 # PJLink ERR2 +E_UNAVAILABLE = 204 # PJLink ERR3 +E_PROJECTOR = 205 # PJLink ERR4 +E_AUTHENTICATION = 206 # PJLink ERRA +E_NO_AUTHENTICATION = 207 # PJLink authentication mismatch between projector and program +E_PREFIX = 208 # PJLink invalid prefix for packet +E_CLASS = 209 # PJLink class version mismatch +E_INVALID_DATA = 210 +E_WARN = 211 +E_ERROR = 212 +E_FAN = 213 +E_LAMP = 214 +E_TEMP = 215 +E_COVER = 216 +E_FILTER = 217 +E_UNKNOWN = 218 + +# Remap Qt socket error codes to local error codes +E_CONNECTION_REFUSED = 230 +E_REMOTE_HOST_CLOSED_CONNECTION = 231 +E_HOST_NOT_FOUND = 232 +E_SOCKET_ACCESS = 233 +E_SOCKET_RESOURCE = 234 +E_SOCKET_TIMEOUT = 235 +E_DATAGRAM_TOO_LARGE = 236 +E_NETWORK = 237 +E_ADDRESS_IN_USE = 238 +E_SOCKET_ADDRESS_NOT_AVAILABLE = 239 +E_UNSUPPORTED_SOCKET_OPERATION = 240 +E_PROXY_AUTHENTICATION_REQUIRED = 241 +E_SLS_HANDSHAKE_FAILED = 242 +E_UNFINISHED_SOCKET_OPERATION = 243 +E_PROXY_CONNECTION_REFUSED = 244 +E_PROXY_CONNECTION_CLOSED = 245 +E_PROXY_CONNECTION_TIMEOUT = 246 +E_PROXY_NOT_FOUND = 247 +E_PROXY_PROTOCOL = 248 +E_UNKNOWN_SOCKET_ERROR = 249 + +# Status codes start at 300 + +# Remap Qt socket states to local status codes +S_NOT_CONNECTED = 300 +S_HOST_LOOKUP = 301 +S_CONNECTING = 302 +S_CONNECTED = 303 +S_BOUND = 304 +S_LISTENING = 305 # Listed as internal use only in QAbstractSocket +S_CLOSING = 306 + +# Projector states +S_INITIALIZE = 310 +S_STATUS = 311 +S_OFF = 312 +S_STANDBY = 313 +S_WARMUP = 314 +S_ON = 315 +S_COOLDOWN = 316 +S_INFO = 317 + +# Information that does not affect status +S_NETWORK_IDLE = 400 +S_NETWORK_SENDING = 401 +S_NETWORK_RECEIVING = 402 + +# Map PJlink errors to local status +PJLINK_ERRORS = { + 'ERRA': E_AUTHENTICATION, # Authentication error + 'ERR1': E_UNDEFINED, # Undefined command error + 'ERR2': E_PARAMETER, # Invalid parameter error + 'ERR3': E_UNAVAILABLE, # Projector busy + 'ERR4': E_PROJECTOR, # Projector or display failure + E_AUTHENTICATION: 'ERRA', + E_UNDEFINED: 'ERR1', + E_PARAMETER: 'ERR2', + E_UNAVAILABLE: 'ERR3', + E_PROJECTOR: 'ERR4' +} + +# Map QAbstractSocketState enums to local status +QSOCKET_STATE = { + 0: S_NOT_CONNECTED, # 'UnconnectedState', + 1: S_HOST_LOOKUP, # 'HostLookupState', + 2: S_CONNECTING, # 'ConnectingState', + 3: S_CONNECTED, # 'ConnectedState', + 4: S_BOUND, # 'BoundState', + 5: S_LISTENING, # 'ListeningState' - Noted as "Internal Use Only" on Qt website + 6: S_CLOSING, # 'ClosingState', + S_NOT_CONNECTED: 0, + S_HOST_LOOKUP: 1, + S_CONNECTING: 2, + S_CONNECTED: 3, + S_BOUND: 4, + S_LISTENING: 5, + S_CLOSING: 6 +} + +PROJECTOR_STATE = [ + S_INITIALIZE, + S_STATUS, + S_OFF, + S_STANDBY, + S_WARMUP, + S_ON, + S_COOLDOWN, + S_INFO +] + # NOTE: Changed format to account for some commands are both class 1 and 2 PJLINK_VALID_CMD = { 'ACKN': {'version': ['2', ], @@ -144,227 +263,140 @@ 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 -} +CONNECTION_ERRORS = [ + E_ADDRESS_IN_USE, + E_CONNECTION_REFUSED, + E_DATAGRAM_TOO_LARGE, + E_HOST_NOT_FOUND, + E_NETWORK, + E_NOT_CONNECTED, + E_PROXY_AUTHENTICATION_REQUIRED, + E_PROXY_CONNECTION_CLOSED, + E_PROXY_CONNECTION_REFUSED, + E_PROXY_CONNECTION_TIMEOUT, + E_PROXY_NOT_FOUND, + E_PROXY_PROTOCOL, + E_REMOTE_HOST_CLOSED_CONNECTION, + E_SLS_HANDSHAKE_FAILED, + E_SOCKET_ACCESS, + E_SOCKET_ADDRESS_NOT_AVAILABLE, + E_SOCKET_RESOURCE, + E_SOCKET_TIMEOUT, + E_UNFINISHED_SOCKET_OPERATION, + E_UNKNOWN_SOCKET_ERROR, + E_UNSUPPORTED_SOCKET_OPERATION +] -# 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. -E_GENERAL = 200 # Unknown error -E_NOT_CONNECTED = 201 -E_FAN = 202 -E_LAMP = 203 -E_TEMP = 204 -E_COVER = 205 -E_FILTER = 206 -E_NO_AUTHENTICATION = 207 # PIN set and no authentication set on projector -E_UNDEFINED = 208 # ERR1 -E_PARAMETER = 209 # ERR2 -E_UNAVAILABLE = 210 # ERR3 -E_PROJECTOR = 211 # ERR4 -E_INVALID_DATA = 212 -E_WARN = 213 -E_ERROR = 214 -E_AUTHENTICATION = 215 # ERRA -E_CLASS = 216 -E_PREFIX = 217 +PROJECTOR_ERRORS = [ + E_AUTHENTICATION, + E_CLASS, + E_INVALID_DATA, + E_NO_AUTHENTICATION, + E_PARAMETER, + E_PREFIX, + E_PROJECTOR, + E_UNAVAILABLE, + E_UNDEFINED, + E_UNKNOWN +] -# Remap Qt socket error codes to projector error codes -E_CONNECTION_REFUSED = 230 -E_REMOTE_HOST_CLOSED_CONNECTION = 231 -E_HOST_NOT_FOUND = 232 -E_SOCKET_ACCESS = 233 -E_SOCKET_RESOURCE = 234 -E_SOCKET_TIMEOUT = 235 -E_DATAGRAM_TOO_LARGE = 236 -E_NETWORK = 237 -E_ADDRESS_IN_USE = 238 -E_SOCKET_ADDRESS_NOT_AVAILABLE = 239 -E_UNSUPPORTED_SOCKET_OPERATION = 240 -E_PROXY_AUTHENTICATION_REQUIRED = 241 -E_SLS_HANDSHAKE_FAILED = 242 -E_UNFINISHED_SOCKET_OPERATION = 243 -E_PROXY_CONNECTION_REFUSED = 244 -E_PROXY_CONNECTION_CLOSED = 245 -E_PROXY_CONNECTION_TIMEOUT = 246 -E_PROXY_NOT_FOUND = 247 -E_PROXY_PROTOCOL = 248 -E_UNKNOWN_SOCKET_ERROR = -1 - -# Status codes start at 300 -S_NOT_CONNECTED = 300 -S_CONNECTING = 301 -S_CONNECTED = 302 -S_INITIALIZE = 303 -S_STATUS = 304 -S_OFF = 305 -S_STANDBY = 306 -S_WARMUP = 307 -S_ON = 308 -S_COOLDOWN = 309 -S_INFO = 310 - -# Information that does not affect status -S_NETWORK_SENDING = 400 -S_NETWORK_RECEIVED = 401 - -CONNECTION_ERRORS = { - E_NOT_CONNECTED, E_NO_AUTHENTICATION, E_AUTHENTICATION, E_CLASS, - E_PREFIX, E_CONNECTION_REFUSED, E_REMOTE_HOST_CLOSED_CONNECTION, - E_HOST_NOT_FOUND, E_SOCKET_ACCESS, E_SOCKET_RESOURCE, E_SOCKET_TIMEOUT, - E_DATAGRAM_TOO_LARGE, E_NETWORK, E_ADDRESS_IN_USE, E_SOCKET_ADDRESS_NOT_AVAILABLE, - E_UNSUPPORTED_SOCKET_OPERATION, E_PROXY_AUTHENTICATION_REQUIRED, - E_SLS_HANDSHAKE_FAILED, E_UNFINISHED_SOCKET_OPERATION, E_PROXY_CONNECTION_REFUSED, - E_PROXY_CONNECTION_CLOSED, E_PROXY_CONNECTION_TIMEOUT, E_PROXY_NOT_FOUND, - E_PROXY_PROTOCOL, E_UNKNOWN_SOCKET_ERROR -} - -PJLINK_ERRORS = { - 'ERRA': E_AUTHENTICATION, # Authentication error - 'ERR1': E_UNDEFINED, # Undefined command error - 'ERR2': E_PARAMETER, # Invalid parameter error - 'ERR3': E_UNAVAILABLE, # Projector busy - 'ERR4': E_PROJECTOR, # Projector or display failure - E_AUTHENTICATION: 'ERRA', - E_UNDEFINED: 'ERR1', - E_PARAMETER: 'ERR2', - E_UNAVAILABLE: 'ERR3', - E_PROJECTOR: 'ERR4' -} - -# Map error/status codes to string -ERROR_STRING = { - 0: 'S_OK', - E_GENERAL: 'E_GENERAL', - E_NOT_CONNECTED: 'E_NOT_CONNECTED', - E_FAN: 'E_FAN', - E_LAMP: 'E_LAMP', - E_TEMP: 'E_TEMP', - E_COVER: 'E_COVER', - E_FILTER: 'E_FILTER', - E_AUTHENTICATION: 'E_AUTHENTICATION', - E_NO_AUTHENTICATION: 'E_NO_AUTHENTICATION', - E_UNDEFINED: 'E_UNDEFINED', - E_PARAMETER: 'E_PARAMETER', - E_UNAVAILABLE: 'E_UNAVAILABLE', - E_PROJECTOR: 'E_PROJECTOR', - E_INVALID_DATA: 'E_INVALID_DATA', - E_WARN: 'E_WARN', - E_ERROR: 'E_ERROR', - E_CLASS: 'E_CLASS', - E_PREFIX: 'E_PREFIX', # Last projector error - E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED', # First QtSocket error - E_REMOTE_HOST_CLOSED_CONNECTION: 'E_REMOTE_HOST_CLOSED_CONNECTION', - E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND', - E_SOCKET_ACCESS: 'E_SOCKET_ACCESS', - E_SOCKET_RESOURCE: 'E_SOCKET_RESOURCE', - E_SOCKET_TIMEOUT: 'E_SOCKET_TIMEOUT', - E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE', - E_NETWORK: 'E_NETWORK', +# Show status code as string +STATUS_CODE = { E_ADDRESS_IN_USE: 'E_ADDRESS_IN_USE', - E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE', - E_UNSUPPORTED_SOCKET_OPERATION: 'E_UNSUPPORTED_SOCKET_OPERATION', + E_AUTHENTICATION: 'E_AUTHENTICATION', + E_CLASS: 'E_CLASS', + E_CONNECTION_REFUSED: 'E_CONNECTION_REFUSED', + E_COVER: 'E_COVER', + E_DATAGRAM_TOO_LARGE: 'E_DATAGRAM_TOO_LARGE', + E_ERROR: 'E_ERROR', + E_FAN: 'E_FAN', + E_FILTER: 'E_FILTER', + E_GENERAL: 'E_GENERAL', + E_HOST_NOT_FOUND: 'E_HOST_NOT_FOUND', + E_INVALID_DATA: 'E_INVALID_DATA', + E_LAMP: 'E_LAMP', + E_NETWORK: 'E_NETWORK', + E_NO_AUTHENTICATION: 'E_NO_AUTHENTICATION', + E_NOT_CONNECTED: 'E_NOT_CONNECTED', + E_PARAMETER: 'E_PARAMETER', + E_PREFIX: 'E_PREFIX', + E_PROJECTOR: 'E_PROJECTOR', E_PROXY_AUTHENTICATION_REQUIRED: 'E_PROXY_AUTHENTICATION_REQUIRED', - E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED', - E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION', - E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED', E_PROXY_CONNECTION_CLOSED: 'E_PROXY_CONNECTION_CLOSED', + E_PROXY_CONNECTION_REFUSED: 'E_PROXY_CONNECTION_REFUSED', E_PROXY_CONNECTION_TIMEOUT: 'E_PROXY_CONNECTION_TIMEOUT', E_PROXY_NOT_FOUND: 'E_PROXY_NOT_FOUND', E_PROXY_PROTOCOL: 'E_PROXY_PROTOCOL', - E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR' -} - -STATUS_STRING = { - S_NOT_CONNECTED: 'S_NOT_CONNECTED', - S_CONNECTING: 'S_CONNECTING', - S_CONNECTED: 'S_CONNECTED', - S_STATUS: 'S_STATUS', - S_OFF: 'S_OFF', - S_INITIALIZE: 'S_INITIALIZE', - S_STANDBY: 'S_STANDBY', - S_WARMUP: 'S_WARMUP', - S_ON: 'S_ON', + E_REMOTE_HOST_CLOSED_CONNECTION: 'E_REMOTE_HOST_CLOSED_CONNECTION', + E_SLS_HANDSHAKE_FAILED: 'E_SLS_HANDSHAKE_FAILED', + E_SOCKET_ACCESS: 'E_SOCKET_ACCESS', + E_SOCKET_ADDRESS_NOT_AVAILABLE: 'E_SOCKET_ADDRESS_NOT_AVAILABLE', + E_SOCKET_RESOURCE: 'E_SOCKET_RESOURCE', + E_SOCKET_TIMEOUT: 'E_SOCKET_TIMEOUT', + E_TEMP: 'E_TEMP', + E_UNAVAILABLE: 'E_UNAVAILABLE', + E_UNDEFINED: 'E_UNDEFINED', + E_UNFINISHED_SOCKET_OPERATION: 'E_UNFINISHED_SOCKET_OPERATION', + E_UNKNOWN: 'E_UNKNOWN', + E_UNKNOWN_SOCKET_ERROR: 'E_UNKNOWN_SOCKET_ERROR', + E_UNSUPPORTED_SOCKET_OPERATION: 'E_UNSUPPORTED_SOCKET_OPERATION', + E_WARN: 'E_WARN', + S_BOUND: 'S_BOUND', S_COOLDOWN: 'S_COOLDOWN', + S_CLOSING: 'S_CLOSING', + S_CONNECTED: 'S_CONNECTED', + S_CONNECTING: 'S_CONNECTING', + S_HOST_LOOKUP: 'S_HOST_LOOKUP', S_INFO: 'S_INFO', + S_INITIALIZE: 'S_INITIALIZE', + S_LISTENING: 'S_LISTENING', + S_NETWORK_RECEIVING: 'S_NETWORK_RECEIVING', S_NETWORK_SENDING: 'S_NETWORK_SENDING', - S_NETWORK_RECEIVED: 'S_NETWORK_RECEIVED' + S_NETWORK_IDLE: 'S_NETWORK_IDLE', + S_NOT_CONNECTED: 'S_NOT_CONNECTED', + S_OFF: 'S_OFF', + S_OK: 'S_OK', # S_OK or E_OK + S_ON: 'S_ON', + S_STANDBY: 'S_STANDBY', + S_STATUS: 'S_STATUS', + S_WARMUP: 'S_WARMUP', } -# Map error/status codes to message strings -ERROR_MSG = { - E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK - E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'), - E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected error'), - E_LAMP: translate('OpenLP.ProjectorConstants', 'Lamp error'), - E_FAN: translate('OpenLP.ProjectorConstants', 'Fan error'), - E_TEMP: translate('OpenLP.ProjectorConstants', 'High temperature detected'), - E_COVER: translate('OpenLP.ProjectorConstants', 'Cover open detected'), - E_FILTER: translate('OpenLP.ProjectorConstants', 'Check filter'), - E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'Authentication Error'), - E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'Undefined Command'), - E_PARAMETER: translate('OpenLP.ProjectorConstants', 'Invalid Parameter'), - E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'Projector Busy'), - E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'Projector/Display Error'), - E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'Invalid packet received'), - E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'), - E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'), - E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'), - E_PREFIX: translate('OpenLP.ProjectorConstants', 'Invalid prefix character'), - E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants', - 'The connection was refused by the peer (or timed out)'), - E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants', - 'The remote host closed the connection'), - E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'), - E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants', - 'The socket operation failed because the application ' - 'lacked the required privileges'), - E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants', - 'The local system ran out of resources (e.g., too many sockets)'), - E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants', - 'The socket operation timed out'), - E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants', - 'The datagram was larger than the operating system\'s limit'), - E_NETWORK: translate('OpenLP.ProjectorConstants', - 'An error occurred with the network (Possibly someone pulled the plug?)'), +# Map status codes to message strings +STATUS_MSG = { E_ADDRESS_IN_USE: translate('OpenLP.ProjectorConstants', 'The address specified with socket.bind() ' 'is already in use and was set to be exclusive'), - E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants', - 'The address specified to socket.bind() ' - 'does not belong to the host'), - E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants', - 'The requested socket operation is not supported by the local ' - 'operating system (e.g., lack of IPv6 support)'), + E_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERRA: Authentication Error"'), + E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants', + 'The connection was refused by the peer (or timed out)'), + E_COVER: translate('OpenLP.ProjectorConstants', 'Projector cover open detected'), + E_CLASS: translate('OpenLP.ProjectorConstants', 'PJLink class not supported'), + E_DATAGRAM_TOO_LARGE: translate('OpenLP.ProjectorConstants', + "The datagram was larger than the operating system's limit"), + E_ERROR: translate('OpenLP.ProjectorConstants', 'Error condition detected'), + E_FAN: translate('OpenLP.ProjectorConstants', 'Projector fan error'), + E_FILTER: translate('OpenLP.ProjectorConstants', 'Projector check filter'), + E_GENERAL: translate('OpenLP.ProjectorConstants', 'General projector error'), + E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'), + E_INVALID_DATA: translate('OpenLP.ProjectorConstants', 'PJLink invalid packet received'), + E_LAMP: translate('OpenLP.ProjectorConstants', 'Projector lamp error'), + E_NETWORK: translate('OpenLP.ProjectorConstants', + 'An error occurred with the network (Possibly someone pulled the plug?)'), + E_NO_AUTHENTICATION: translate('OpenLP.ProjectorConstants', 'PJlink authentication Mismatch Error'), + E_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Projector not connected error'), + E_PARAMETER: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR2: Invalid Parameter"'), + E_PREFIX: translate('OpenLP.ProjectorConstants', 'PJLink Invalid prefix character'), + E_PROJECTOR: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR4: Projector/Display Error"'), E_PROXY_AUTHENTICATION_REQUIRED: translate('OpenLP.ProjectorConstants', 'The socket is using a proxy, ' 'and the proxy requires authentication'), - E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants', - 'The SSL/TLS handshake failed'), - E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants', - 'The last operation attempted has not finished yet ' - '(still in progress in the background)'), - E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants', - 'Could not contact the proxy server because the connection ' - 'to that server was denied'), E_PROXY_CONNECTION_CLOSED: translate('OpenLP.ProjectorConstants', 'The connection to the proxy server was closed unexpectedly ' '(before the connection to the final peer was established)'), + E_PROXY_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants', + 'Could not contact the proxy server because the connection ' + 'to that server was denied'), E_PROXY_CONNECTION_TIMEOUT: translate('OpenLP.ProjectorConstants', 'The connection to the proxy server timed out or the proxy ' 'server stopped responding in the authentication phase.'), @@ -373,51 +405,91 @@ ERROR_MSG = { E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants', 'The connection negotiation with the proxy server failed because the ' 'response from the proxy server could not be understood'), - E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'), - S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'), - S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'), + E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants', + 'The remote host closed the connection'), + E_SLS_HANDSHAKE_FAILED: translate('OpenLP.ProjectorConstants', + 'The SSL/TLS handshake failed'), + E_SOCKET_ADDRESS_NOT_AVAILABLE: translate('OpenLP.ProjectorConstants', + 'The address specified to socket.bind() ' + 'does not belong to the host'), + E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants', + 'The socket operation failed because the application ' + 'lacked the required privileges'), + E_SOCKET_RESOURCE: translate('OpenLP.ProjectorConstants', + 'The local system ran out of resources (e.g., too many sockets)'), + E_SOCKET_TIMEOUT: translate('OpenLP.ProjectorConstants', + 'The socket operation timed out'), + E_TEMP: translate('OpenLP.ProjectorConstants', 'Projector high temperature detected'), + E_UNAVAILABLE: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR3: Busy"'), + E_UNDEFINED: translate('OpenLP.ProjectorConstants', 'PJLink returned "ERR1: Undefined Command"'), + E_UNFINISHED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants', + 'The last operation attempted has not finished yet ' + '(still in progress in the background)'), + E_UNKNOWN: translate('OpenLP.ProjectorConstants', 'Unknown condiction detected'), + E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified socket error occurred'), + E_UNSUPPORTED_SOCKET_OPERATION: translate('OpenLP.ProjectorConstants', + 'The requested socket operation is not supported by the local ' + 'operating system (e.g., lack of IPv6 support)'), + E_WARN: translate('OpenLP.ProjectorConstants', 'Warning condition detected'), + S_BOUND: translate('OpenLP.ProjectorConstants', 'Socket is bount to an address or port'), + S_CLOSING: translate('OpenLP.ProjectorConstants', 'Socket is about to close'), S_CONNECTED: translate('OpenLP.ProjectorConstants', 'Connected'), - S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'), - S_OFF: translate('OpenLP.ProjectorConstants', 'Off'), - S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'), - S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'), - S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'), - S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'), + S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'), S_COOLDOWN: translate('OpenLP.ProjectorConstants', 'Cooldown in progress'), + S_HOST_LOOKUP: translate('OpenLP.ProjectorConstants', 'Performing a host name lookup'), S_INFO: translate('OpenLP.ProjectorConstants', 'Projector Information available'), + S_INITIALIZE: translate('OpenLP.ProjectorConstants', 'Initialize in progress'), + S_LISTENING: translate('OpenLP.ProjectorConstants', 'Socket it listening (internal use only)'), + S_NETWORK_IDLE: translate('OpenLP.ProjectorConstants', 'No network activity at this time'), + S_NETWORK_RECEIVING: translate('OpenLP.ProjectorConstants', 'Received data'), S_NETWORK_SENDING: translate('OpenLP.ProjectorConstants', 'Sending data'), - S_NETWORK_RECEIVED: translate('OpenLP.ProjectorConstants', 'Received data') + S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not Connected'), + S_OFF: translate('OpenLP.ProjectorConstants', 'Off'), + S_OK: translate('OpenLP.ProjectorConstants', 'OK'), + S_ON: translate('OpenLP.ProjectorConstants', 'Power is on'), + S_STANDBY: translate('OpenLP.ProjectorConstants', 'Power in standby'), + S_STATUS: translate('OpenLP.ProjectorConstants', 'Getting status'), + S_WARMUP: translate('OpenLP.ProjectorConstants', 'Warmup in progress'), } -# Map ERST return code positions to equipment +# Map ERST reply positions to equipment +PJLINK_ERST_LIST = { + "FAN": translate('OpenLP.PJLink', 'Fan'), + "LAMP": translate('OpenLP.PJLink', 'Lamp'), + "TEMP": translate('OpenLP.PJLink', 'Temperature'), + "COVER": translate('OpenLP.PJLink', 'Cover'), + "FILTER": translate('OpenLP.PJLink', 'Filter'), + "OTHER": translate('OpenPL.PJLink', 'Other') +} + +# Map projector item to ERST data position PJLINK_ERST_DATA = { - 'DATA_LENGTH': 6, - 0: 'FAN', - 1: 'LAMP', - 2: 'TEMP', - 3: 'COVER', - 4: 'FILTER', - 5: 'OTHER', + 'DATA_LENGTH': 6, # Zero based so enums are 0-5 'FAN': 0, 'LAMP': 1, 'TEMP': 2, 'COVER': 3, 'FILTER': 4, - 'OTHER': 5 + 'OTHER': 5, + 0: 'FAN', + 1: 'LAMP', + 2: 'TEMP', + 3: 'COVER', + 4: 'FILTER', + 5: 'OTHER' } -# Map for ERST return codes to string +# Map ERST reply codes to string PJLINK_ERST_STATUS = { - '0': 'OK', - '1': ERROR_STRING[E_WARN], - '2': ERROR_STRING[E_ERROR], - 'OK': '0', - E_OK: '0', + '0': S_OK, + '1': E_WARN, + '2': E_ERROR, + S_OK: '0', E_WARN: '1', E_ERROR: '2' } -# Map for POWR return codes to status code +# Map POWR return codes to status code PJLINK_POWR_STATUS = { '0': S_STANDBY, '1': S_ON, diff --git a/openlp/core/projectors/manager.py b/openlp/core/projectors/manager.py index 25cf750ac..db871cbfd 100644 --- a/openlp/core/projectors/manager.py +++ b/openlp/core/projectors/manager.py @@ -35,9 +35,25 @@ from openlp.core.common.registry import RegistryBase from openlp.core.common.settings import Settings from openlp.core.lib.ui import create_widget_action from openlp.core.projectors import DialogSourceStyle -from openlp.core.projectors.constants import ERROR_MSG, ERROR_STRING, E_AUTHENTICATION, E_ERROR, \ - E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \ - S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP +from openlp.core.projectors.constants import \ + E_AUTHENTICATION, \ + E_ERROR, \ + E_NETWORK, \ + E_NOT_CONNECTED, \ + E_UNKNOWN_SOCKET_ERROR, \ + S_CONNECTED, \ + S_CONNECTING, \ + S_COOLDOWN, \ + S_INITIALIZE, \ + S_NOT_CONNECTED, \ + S_OFF, \ + S_ON, \ + S_STANDBY, \ + S_WARMUP, \ + STATUS_CODE, \ + STATUS_MSG, \ + QSOCKET_STATE + from openlp.core.projectors.db import ProjectorDB from openlp.core.projectors.editform import ProjectorEditForm from openlp.core.projectors.pjlink import PJLink, PJLinkUDP @@ -440,11 +456,12 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM :param opt: Needed by PyQt5 """ projector = item.data(QtCore.Qt.UserRole) - if projector.link.state() != projector.link.ConnectedState: + if QSOCKET_STATE[projector.link.state()] != S_CONNECTED: try: + log.debug('ProjectorManager: Calling connect_to_host() on "{ip}"'.format(ip=projector.link.ip)) projector.link.connect_to_host() except: - pass + log.debug('ProjectorManager: "{ip}" already connected - skipping'.format(ip=projector.link.ip)) return def on_connect_projector(self, opt=None): @@ -647,7 +664,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM 'Other info'), data=projector.link.other_info) message += '{title}: {data}
'.format(title=translate('OpenLP.ProjectorManager', 'Power status'), - data=ERROR_MSG[projector.link.power]) + data=STATUS_MSG[projector.link.power]) message += '{title}: {data}
'.format(title=translate('OpenLP.ProjectorManager', 'Shutter is'), data=translate('OpenLP.ProjectorManager', 'Closed') if projector.link.shutter @@ -692,7 +709,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM else: message += '{data}'.format(data=translate('OpenLP.ProjectorManager', 'Current errors/warnings')) for (key, val) in projector.link.projector_errors.items(): - message += '{key}: {data}
'.format(key=key, data=ERROR_MSG[val]) + message += '{key}: {data}
'.format(key=key, data=STATUS_MSG[val]) QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message) def _add_projector(self, projector): @@ -817,31 +834,18 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM if ip == list_item.link.ip: item = list_item break - message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg - if status in STATUS_STRING: - status_code = STATUS_STRING[status] - message = ERROR_MSG[status] if msg is None else msg - elif status in ERROR_STRING: - status_code = ERROR_STRING[status] - message = ERROR_MSG[status] if msg is None else msg - else: - status_code = status - message = ERROR_MSG[status] if msg is None else msg - log.debug('({name}) updateStatus(status={status}) message: "{message}"'.format(name=item.link.name, - status=status_code, - message=message)) - if status in STATUS_ICONS: - if item.status == status: - return - item.status = status - item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status])) - if status in ERROR_STRING: - status_code = ERROR_STRING[status] - elif status in STATUS_STRING: - status_code = STATUS_STRING[status] - log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=status_code)) - item.widget.setIcon(item.icon) - self.update_icons() + if item is None: + log.error('ProjectorManager: Unknown item "{ip}" - not updating status'.format(ip=ip)) + return + elif item.status == status: + log.debug('ProjectorManager: No status change for "{ip}" - not updating status'.format(ip=ip)) + return + + item.status = status + item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status])) + log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=STATUS_CODE[status])) + item.widget.setIcon(item.icon) + return self.update_icons() def get_toolbar_item(self, name, enabled=False, hidden=False): item = self.one_toolbar.findChild(QtWidgets.QAction, name) @@ -877,7 +881,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM self.get_toolbar_item('show_projector_multiple', hidden=True) elif count == 1: projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole) - connected = projector.link.state() == projector.link.ConnectedState + connected = QSOCKET_STATE[projector.link.state()] == S_CONNECTED power = projector.link.power == S_ON self.get_toolbar_item('connect_projector_multiple', hidden=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=True) diff --git a/openlp/core/projectors/pjlink.py b/openlp/core/projectors/pjlink.py index cee92bc0e..2f1aad76c 100644 --- a/openlp/core/projectors/pjlink.py +++ b/openlp/core/projectors/pjlink.py @@ -54,11 +54,12 @@ from PyQt5 import QtCore, QtNetwork from openlp.core.common import qmd5_hash from openlp.core.common.i18n import translate -from openlp.core.projectors.constants import CONNECTION_ERRORS, CR, ERROR_MSG, ERROR_STRING, \ - 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_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_QSOCKET_STATE, S_STATUS +from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \ + PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \ + PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \ + E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \ + E_OK, E_SOCKET_TIMEOUT, \ + S_CONNECTED, S_CONNECTING, S_INFO, S_NOT_CONNECTED, S_OFF, S_OK, S_ON, S_STATUS log = logging.getLogger(__name__) log.debug('pjlink loaded') @@ -69,12 +70,9 @@ __all__ = ['PJLink'] SocketError = QtNetwork.QAbstractSocket.SocketError SocketSTate = QtNetwork.QAbstractSocket.SocketState -PJLINK_PREFIX = '%' -PJLINK_CLASS = '1' # Default to class 1 until we query the projector # Add prefix here, but defer linkclass expansion until later when we have the actual # PJLink class for the command PJLINK_HEADER = '{prefix}{{linkclass}}'.format(prefix=PJLINK_PREFIX) -PJLINK_SUFFIX = CR class PJLinkUDP(QtNetwork.QUdpSocket): @@ -136,8 +134,9 @@ class PJLinkCommands(object): """ Initialize instance variables. Also used to reset projector-specific information to default. """ + conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]] log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, - state=S_QSOCKET_STATE[self.state()])) + state=conn_state)) self.fan = None # ERST self.filter_time = None # FILT self.lamp = None # LAMP @@ -183,42 +182,25 @@ class PJLinkCommands(object): # Due to some replies should stay as mixed-case, validate using separate uppercase check _data = data.upper() # 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)) + if 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 == 'OK': log.debug('({ip}) Command "{cmd}" returned OK'.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 - log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data)) - if _data == PJLINK_ERRORS[E_AUTHENTICATION]: - # Authentication error + log.error('({ip}) {cmd}: {err}'.format(ip=self.ip, + cmd=cmd, + err=STATUS_MSG[PJLINK_ERRORS[_data]])) + if PJLINK_ERRORS[_data] == E_AUTHENTICATION: self.disconnect_from_host() - self.change_status(E_AUTHENTICATION) - log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip)) self.projectorAuthentication.emit(self.name) - elif _data == PJLINK_ERRORS[E_UNDEFINED]: - # Projector does not recognize command - self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED], - data=cmd)) - elif _data == PJLINK_ERRORS[E_PARAMETER]: - # Invalid parameter - self.change_status(E_PARAMETER) - elif _data == PJLINK_ERRORS[E_UNAVAILABLE]: - # Projector busy - self.change_status(E_UNAVAILABLE) - elif _data == PJLINK_ERRORS[E_PROJECTOR]: - # Projector/display error - self.change_status(E_PROJECTOR) - return + return self.change_status(status=E_AUTHENTICATION) # Command checks already passed log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) - self.pjlink_functions[cmd](data) + self.pjlink_functions[cmd](data=data) def process_avmt(self, data): """ @@ -313,22 +295,22 @@ class PJLinkCommands(object): data[PJLINK_ERST_DATA['COVER']], data[PJLINK_ERST_DATA['FILTER']], data[PJLINK_ERST_DATA['OTHER']]) - if fan != PJLINK_ERST_STATUS[E_OK]: + if fan != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \ PJLINK_ERST_STATUS[fan] - if lamp != PJLINK_ERST_STATUS[E_OK]: + if lamp != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \ PJLINK_ERST_STATUS[lamp] - if temp != PJLINK_ERST_STATUS[E_OK]: + if temp != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \ PJLINK_ERST_STATUS[temp] - if cover != PJLINK_ERST_STATUS[E_OK]: + if cover != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \ PJLINK_ERST_STATUS[cover] - if filt != PJLINK_ERST_STATUS[E_OK]: + if filt != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \ PJLINK_ERST_STATUS[filt] - if other != PJLINK_ERST_STATUS[E_OK]: + if other != PJLINK_ERST_STATUS[S_OK]: self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \ PJLINK_ERST_STATUS[other] return @@ -373,8 +355,18 @@ class PJLinkCommands(object): :param data: Currently selected source """ + # First, see if we have a valid input based on what is installed (if available) + if self.source_available is not None: + # We have available inputs, so verify it's in the list + if data not in self.source_available: + log.warn('({ip}) Input source not listed in available sources - ignoring'.format(ip=self.ip)) + return + elif data not in PJLINK_DEFAULT_CODES: + # Hmm - no sources available yet, so check with PJLink defaults + log.warn('({ip}) Input source not listed as a PJLink available source - ignoring'.format(ip=self.ip)) + return self.source = data - log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source)) + log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source)) return def process_inst(self, data): @@ -390,7 +382,6 @@ class PJLinkCommands(object): sources.append(source) sources.sort() self.source_available = sources - self.projectorUpdateIcons.emit() log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip, data=self.source_available)) return @@ -551,17 +542,15 @@ class PJLinkCommands(object): return elif self.sw_version is None: 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: + if self.sw_version != 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)) - self.sw_version_received = data + log.warning('({ip}) Updating software version'.format(ip=self.ip)) + self.sw_version = data + self.db_update = True class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): @@ -678,7 +667,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Retrieve information from projector that changes. Normally called by timer(). """ - if self.state() != S_QSOCKET_STATE['ConnectedState']: + if QSOCKET_STATE[self.state()] != S_CONNECTED: log.warning('({ip}) poll_loop(): Not connected - returning'.format(ip=self.ip)) return log.debug('({ip}) poll_loop(): Updating projector status'.format(ip=self.ip)) @@ -688,13 +677,8 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): self.timer.setInterval(self.poll_time) # Restart timer self.timer.start() - # These commands may change during connection - check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT'] - if self.pjlink_class == '2': - check_list.extend(['FILT', 'FREZ']) - for command in check_list: - self.send_command(command) # The following commands do not change, so only check them once + # Call them first in case other functions rely on something here if self.power == S_ON and self.source_available is None: self.send_command('INST') if self.other_info is None: @@ -715,22 +699,28 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): self.send_command('RFIL') if self.model_lamp is None: self.send_command('RLMP') + # These commands may change during connection + check_list = ['POWR', 'ERST', 'LAMP', 'AVMT', 'INPT'] + if self.pjlink_class == '2': + check_list.extend(['FILT', 'FREZ']) + for command in check_list: + self.send_command(command) def _get_status(self, status): """ Helper to retrieve status/error codes and convert to strings. :param status: Status/Error code - :returns: (Status/Error code, String) + :returns: tuple (-1 if code not INT, None) + :returns: tuple (string: code as string, None if no description) + :returns: tuple (string: code as string, string: Status/Error description) """ if not isinstance(status, int): - return -1, 'Invalid status code' - elif status in ERROR_STRING: - return ERROR_STRING[status], ERROR_MSG[status] - elif status in STATUS_STRING: - return STATUS_STRING[status], ERROR_MSG[status] + return -1, None + elif status not in STATUS_MSG: + return None, None else: - return status, translate('OpenLP.PJLink', 'Unknown status') + return STATUS_CODE[status], STATUS_MSG[status] def change_status(self, status, msg=None): """ @@ -740,19 +730,27 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): :param status: Status code :param msg: Optional message """ - message = translate('OpenLP.PJLink', 'No message') if msg is None else msg - (code, message) = self._get_status(status) - if msg is not None: - message = msg + if status in STATUS_CODE: + log.debug('({ip}) Changing status to {status} ' + '"{msg}"'.format(ip=self.ip, + status=STATUS_CODE[status], + msg=msg if msg is not None else STATUS_MSG[status])) + else: + log.warning('({ip}) Unknown status change code: {code}'.format(ip=self.ip, + code=status)) + return if status in CONNECTION_ERRORS: - # Projector, connection state - self.projector_status = self.error_status = self.status_connect = E_NOT_CONNECTED - elif status >= S_NOT_CONNECTED and status < S_STATUS: + # Connection state error affects both socket and projector + self.error_status = status + self.status_connect = E_NOT_CONNECTED + elif status >= S_NOT_CONNECTED and status in QSOCKET_STATE: + # Socket connection status update self.status_connect = status - self.projector_status = S_NOT_CONNECTED - elif status <= S_INFO: - self.status_connect = S_CONNECTED + elif status >= S_NOT_CONNECTED and status in PROJECTOR_STATE: + # Only affects the projector status self.projector_status = status + + # These log entries are for troubleshooting only (status_code, status_message) = self._get_status(self.status_connect) log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip, code=status_code, @@ -765,6 +763,15 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip, code=status_code, message=status_message if msg is None else msg)) + + # Now that we logged extra information for debugging, broadcast the original change/message + (code, message) = self._get_status(status) + if msg is not None: + message = msg + elif message is None: + # No message for status code + message = translate('OpenLP.PJLink', 'No message') if msg is None else msg + self.changeStatus.emit(self.ip, status, message) self.projectorUpdateIcons.emit() @@ -794,7 +801,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): data = decode(read, 'utf-8') # Possibility of extraneous data on input when reading. # Clean out extraneous characters in buffer. - self.readLine(self.max_size) + self.read(1024) log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip())) # At this point, we should only have the initial login prompt with # possible authentication @@ -805,6 +812,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): # Invalid initial packet - close socket log.error('({ip}) Invalid initial packet received - closing socket'.format(ip=self.ip)) return self.disconnect_from_host() + # Convert the initial login prompt with the expected PJLink normal command format for processing log.debug('({ip}) check_login(): Formatting initial connection prompt to PJLink packet'.format(ip=self.ip)) return self.get_data('{start}{clss}{data}'.format(start=PJLINK_PREFIX, clss='1', @@ -895,7 +903,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Get data from TCP socket. """ log.debug('({ip}) get_socket(): Reading data'.format(ip=self.ip)) - if self.state() != self.ConnectedState: + if QSOCKET_STATE[self.state()] != S_CONNECTED: log.debug('({ip}) get_socket(): Not connected - returning'.format(ip=self.ip)) self.send_busy = False return @@ -907,8 +915,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): log.debug('({ip}) get_socket(): No data available (-1)'.format(ip=self.ip)) return self.receive_data_signal() self.socket_timer.stop() - self.get_data(buff=read, ip=self.ip) - return self.receive_data_signal() + return self.get_data(buff=read, ip=self.ip) def get_data(self, buff, ip=None): """ @@ -927,13 +934,17 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): data = data_in.strip() # Initial packet checks if (len(data) < 7): - return self._trash_buffer(msg='get_data(): Invalid packet - length') + self._trash_buffer(msg='get_data(): Invalid packet - length') + return self.receive_data_signal() elif len(data) > self.max_size: - return self._trash_buffer(msg='get_data(): Invalid packet - too long') + self._trash_buffer(msg='get_data(): Invalid packet - too long') + return self.receive_data_signal() elif not data.startswith(PJLINK_PREFIX): - return self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing') + self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing') + return self.receive_data_signal() elif '=' not in data: - return self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="') + self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="') + return self.receive_data_signal() 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: @@ -947,15 +958,17 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): 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)) - return self._trash_buffer('get_data(): Expected header + command + data') + self._trash_buffer('get_data(): Expected header + command + data') + return self.receive_data_signal() if cmd not in PJLINK_VALID_CMD: log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd)) - return self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd)) + self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd)) + return self.receive_data_signal() 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) + self.process_command(cmd, data) + return self.receive_data_signal() @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError) def get_error(self, err): @@ -993,7 +1006,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): :param salt: Optional salt for md5 hash initial authentication :param priority: Option to send packet now rather than queue it up """ - if self.state() != self.ConnectedState: + if QSOCKET_STATE[self.state()] != S_CONNECTED: log.warning('({ip}) send_command(): Not connected - returning'.format(ip=self.ip)) return self.reset_information() if cmd not in PJLINK_VALID_CMD: @@ -1018,7 +1031,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): header=header, command=cmd, options=opts, - suffix=CR) + suffix=PJLINK_SUFFIX) 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: @@ -1044,9 +1057,10 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): """ # 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)) + conn_state = STATUS_CODE[QSOCKET_STATE[self.state()]] log.debug('({ip}) _send_command(): Connection status: {data}'.format(ip=self.ip, - data=S_QSOCKET_STATE[self.state()])) - if self.state() != self.ConnectedState: + data=conn_state)) + if QSOCKET_STATE[self.state()] != S_CONNECTED: log.debug('({ip}) _send_command() Not connected - abort'.format(ip=self.ip)) self.send_busy = False return self.disconnect_from_host() @@ -1088,10 +1102,11 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): """ Initiate connection to projector. """ - log.debug('{ip}) connect_to_host(): Starting connection'.format(ip=self.ip)) - if self.state() == self.ConnectedState: + log.debug('({ip}) connect_to_host(): Starting connection'.format(ip=self.ip)) + if QSOCKET_STATE[self.state()] == S_CONNECTED: log.warning('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip)) return + self.error_status = S_OK self.change_status(S_CONNECTING) self.connectToHost(self.ip, self.port if isinstance(self.port, int) else int(self.port)) @@ -1100,12 +1115,13 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): """ Close socket and cleanup. """ - if abort or self.state() != self.ConnectedState: + if abort or QSOCKET_STATE[self.state()] != S_NOT_CONNECTED: if abort: log.warning('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip)) + self.abort() else: log.warning('({ip}) disconnect_from_host(): Not connected'.format(ip=self.ip)) - self.disconnectFromHost() + self.disconnectFromHost() try: self.readyRead.disconnect(self.get_socket) except TypeError: @@ -1201,7 +1217,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): elif src not in self.source_available: return log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src)) - self.send_command(cmd='INPT', opts=src) + self.send_command(cmd='INPT', opts=src, priority=True) self.poll_loop() def set_power_on(self): @@ -1209,7 +1225,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Send command to turn power to on. """ log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip)) - self.send_command(cmd='POWR', opts='1') + self.send_command(cmd='POWR', opts='1', priority=True) self.poll_loop() def set_power_off(self): @@ -1217,7 +1233,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Send command to turn power to standby. """ log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip)) - self.send_command(cmd='POWR', opts='0') + self.send_command(cmd='POWR', opts='0', priority=True) self.poll_loop() def set_shutter_closed(self): @@ -1225,7 +1241,7 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Send command to set shutter to closed position. """ log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip)) - self.send_command(cmd='AVMT', opts='11') + self.send_command(cmd='AVMT', opts='11', priority=True) self.poll_loop() def set_shutter_open(self): @@ -1233,8 +1249,9 @@ class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): Send command to set shutter to open position. """ log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip)) - self.send_command(cmd='AVMT', opts='10') + self.send_command(cmd='AVMT', opts='10', priority=True) self.poll_loop() + self.projectorUpdateIcons.emit() def receive_data_signal(self): """ diff --git a/tests/functional/openlp_core/projectors/test_projector_constants.py b/tests/functional/openlp_core/projectors/test_projector_constants.py index ed1afa677..6ef4d2118 100644 --- a/tests/functional/openlp_core/projectors/test_projector_constants.py +++ b/tests/functional/openlp_core/projectors/test_projector_constants.py @@ -23,12 +23,51 @@ Package to test the openlp.core.projectors.constants module. """ from unittest import TestCase +from openlp.core.projectors import constants +from openlp.core.projectors.constants import STATUS_CODE, STATUS_MSG class TestProjectorConstants(TestCase): """ Test specific functions in the projector constants module. """ + def test_defined_codes_in_status_code(self): + """ + Test defined status/error codes have equivalent strings + """ + check = [] + missing_str = [] + + # GIVEN: List of defined E_* and S_* items defined in constants + for item in constants.__dict__: + if item.startswith('E_') or item.startswith('S_'): + check.append(item) + + # WHEN: Verify defined list against STATUS_STR + for item in check: + if constants.__dict__[item] not in STATUS_CODE: + missing_str.append(item) + + # THEN: There should be no missing items + assert 0 == len(missing_str), 'Status string missing: {msg}'.format(msg=missing_str) + + def test_status_code_in_status_message(self): + """ + Test if entries in STATUS_CODE have equivalent descriptions in STATUS_MSG + """ + missing_msg = [] + + # GIVEN: Test items + check = STATUS_CODE + + # WHEN: Verify each entry in STATUS_MSG against STATUS_CODE + for item in check: + if item not in STATUS_MSG: + missing_msg.append(item) + + # THEN: There should be no missing items + assert 0 == len(missing_msg), 'Status message missing: {msg}'.format(msg=missing_msg) + def test_build_pjlink_video_label(self): """ Test building PJLINK_DEFAULT_CODES dictionary diff --git a/tests/functional/openlp_core/projectors/test_projector_pjlink_base.py b/tests/functional/openlp_core/projectors/test_projector_pjlink_base.py index cc6f6e0cb..ca699e4eb 100644 --- a/tests/functional/openlp_core/projectors/test_projector_pjlink_base.py +++ b/tests/functional/openlp_core/projectors/test_projector_pjlink_base.py @@ -25,7 +25,7 @@ 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, S_QSOCKET_STATE +from openlp.core.projectors.constants import E_PARAMETER, STATUS_CODE, S_ON, S_CONNECTED, QSOCKET_STATE from openlp.core.projectors.db import Projector from openlp.core.projectors.pjlink import PJLink from tests.resources.projector.data import TEST1_DATA @@ -64,7 +64,7 @@ class TestPJLinkBase(TestCase): # as first parameter mock_change_status.called_with(E_PARAMETER, 'change_status should have been called with "{}"'.format( - ERROR_STRING[E_PARAMETER])) + STATUS_CODE[E_PARAMETER])) @patch.object(pjlink_test, 'disconnect_from_host') def test_socket_abort(self, mock_disconnect): @@ -103,7 +103,7 @@ class TestPJLinkBase(TestCase): """ # GIVEN: Mocks and test data mock_state = patch.object(self.pjlink_test, 'state').start() - mock_state.return_value = S_QSOCKET_STATE['ConnectedState'] + mock_state.return_value = QSOCKET_STATE[S_CONNECTED] 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() @@ -116,7 +116,6 @@ class TestPJLinkBase(TestCase): pjlink.manufacturer = None pjlink.model = None pjlink.pjlink_name = None - pjlink.ConnectedState = S_CONNECTED call_list = [ call('POWR'), call('ERST'), diff --git a/tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py b/tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py index 4ff133061..74623b9c3 100644 --- a/tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py +++ b/tests/functional/openlp_core/projectors/test_projector_pjlink_cmd_routing.py @@ -24,209 +24,222 @@ Package to test the openlp.core.projectors.pjlink command routing. """ from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, call, patch import openlp.core.projectors.pjlink -from openlp.core.projectors.constants import PJLINK_ERRORS, \ +from openlp.core.projectors.constants import PJLINK_ERRORS, PJLINK_PREFIX, STATUS_MSG, \ E_AUTHENTICATION, E_PARAMETER, E_PROJECTOR, E_UNAVAILABLE, E_UNDEFINED from openlp.core.projectors.db import Projector from openlp.core.projectors.pjlink import PJLink -''' -from openlp.core.projectors.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \ - PJLINK_POWR_STATUS, PJLINK_VALID_CMD, E_WARN, E_ERROR, S_OFF, S_STANDBY, S_ON -''' -from tests.resources.projector.data import TEST_PIN, TEST1_DATA - -pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True) -pjlink_test.ip = '127.0.0.1' +from tests.resources.projector.data import TEST1_DATA 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 test_get_data_unknown_command(self): + """ + Test not a valid command + """ + # GIVEN: Test object + log_warning_text = [call('(111.111.111.111) get_data(): Invalid packet - unknown command "UNK"')] + log_debug_text = [call('(111.111.111.111) get_data(ip="111.111.111.111" buffer="b\'%1UNK=Huh?\'"'), + call('(111.111.111.111) get_data(): Checking new data "%1UNK=Huh?"')] - def tearDown(self): - ''' - TestPJLinkCommands part 2 cleanups - ''' - self.pjlink_test = None + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, '_trash_buffer') as mock_buffer: - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_call_clss(self, mock_log): + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.pjlink_functions = MagicMock() + + # WHEN: get_data called with an unknown command + pjlink.get_data(buff='{prefix}1UNK=Huh?'.format(prefix=PJLINK_PREFIX).encode('utf-8')) + + # THEN: Appropriate log entries should have been made and methods called/not called + mock_log.debug.assert_has_calls(log_debug_text) + mock_log.warning.assert_has_calls(log_warning_text) + assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions' + assert mock_buffer.called is True, 'Should have called _trash_buffer' + + def test_process_command_call_clss(self): """ Test process_command calls proper function """ - # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Calling function for CLSS' - mock_log.reset_mock() - mock_process_clss = MagicMock() - pjlink.pjlink_functions['CLSS'] = mock_process_clss + # GIVEN: Test object and mocks + log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "1"'), + call('(111.111.111.111) Calling function for CLSS')] - # WHEN: process_command is called with valid function and data - pjlink.process_command(cmd='CLSS', data='1') + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: - # THEN: Process method should have been called properly - mock_log.debug.assert_called_with(log_text) - mock_process_clss.assert_called_with('1') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - @patch.object(pjlink_test, 'change_status') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_err1(self, mock_log, mock_change_status): - """ - Test ERR1 - Undefined projector function - """ - # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Projector returned error "ERR1"' - mock_change_status.reset_mock() - mock_log.reset_mock() + # WHEN: process_command is called with valid function and data + pjlink.process_command(cmd='CLSS', data='1') - # WHEN: process_command called with ERR1 - pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED]) + # THEN: Appropriate log entries should have been made and methods called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_process_clss.assert_called_once_with(data='1') - # THEN: Error should be logged and status_change should be called - mock_change_status.assert_called_once_with(E_UNDEFINED, 'Undefined Command: "CLSS"') - mock_log.error.assert_called_with(log_text) - - @patch.object(pjlink_test, 'change_status') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_err2(self, mock_log, mock_change_status): - """ - Test ERR2 - Parameter Error - """ - # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Projector returned error "ERR2"' - mock_change_status.reset_mock() - mock_log.reset_mock() - - # WHEN: process_command called with ERR2 - pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER]) - - # THEN: Error should be logged and status_change should be called - mock_change_status.assert_called_once_with(E_PARAMETER) - mock_log.error.assert_called_with(log_text) - - @patch.object(pjlink_test, 'change_status') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_err3(self, mock_log, mock_change_status): - """ - Test ERR3 - Unavailable error - """ - # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Projector returned error "ERR3"' - mock_change_status.reset_mock() - mock_log.reset_mock() - - # WHEN: process_command called with ERR3 - pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE]) - - # THEN: Error should be logged and status_change should be called - mock_change_status.assert_called_once_with(E_UNAVAILABLE) - mock_log.error.assert_called_with(log_text) - - @patch.object(pjlink_test, 'change_status') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_err4(self, mock_log, mock_change_status): - """ - Test ERR3 - Unavailable error - """ - # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Projector returned error "ERR4"' - mock_change_status.reset_mock() - mock_log.reset_mock() - - # WHEN: process_command called with ERR3 - pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR]) - - # THEN: Error should be logged and status_change should be called - mock_change_status.assert_called_once_with(E_PROJECTOR) - mock_log.error.assert_called_with(log_text) - - @patch.object(pjlink_test, 'projectorAuthentication') - @patch.object(pjlink_test, 'change_status') - @patch.object(pjlink_test, 'disconnect_from_host') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_erra(self, mock_log, mock_disconnect, mock_change_status, mock_err_authenticate): + def test_process_command_erra(self): """ Test ERRA - Authentication Error """ # GIVEN: Test object - pjlink = pjlink_test - log_text = '(127.0.0.1) Projector returned error "ERRA"' - mock_change_status.reset_mock() - mock_log.reset_mock() + log_error_calls = [call('(111.111.111.111) PJLINK: {msg}'.format(msg=STATUS_MSG[E_AUTHENTICATION]))] + log_debug_calls = [call('(111.111.111.111) Processing command "PJLINK" with data "ERRA"')] - # WHEN: process_command called with ERRA - pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_AUTHENTICATION]) + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_pjlink') as mock_process_pjlink, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'disconnect_from_host') as mock_disconnect, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorAuthentication') as mock_authentication: - # THEN: Error should be logged and several methods should be called - self.assertTrue(mock_disconnect.called, 'disconnect_from_host should have been called') - mock_change_status.assert_called_once_with(E_AUTHENTICATION) - mock_log.error.assert_called_with(log_text) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + + # WHEN: process_command called with ERRA + pjlink.process_command(cmd='PJLINK', data=PJLINK_ERRORS[E_AUTHENTICATION]) + + # THEN: Appropriate log entries should have been made and methods called/not called + assert mock_disconnect.called is True, 'disconnect_from_host should have been called' + mock_log.error.assert_has_calls(log_error_calls) + mock_log.debug.assert_has_calls(log_debug_calls) + mock_change_status.assert_called_once_with(status=E_AUTHENTICATION) + mock_authentication.emit.assert_called_once_with(pjlink.name) + mock_process_pjlink.assert_not_called() + + def test_process_command_err1(self): + """ + Test ERR1 - Undefined projector function + """ + # GIVEN: Test object + log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNDEFINED]))] + log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR1"'), + call('(111.111.111.111) Calling function for CLSS')] + + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + + # WHEN: process_command called with ERR1 + pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNDEFINED]) + + # THEN: Appropriate log entries should have been made and methods called + mock_log.error.assert_has_calls(log_error_text) + mock_log.debug.assert_has_calls(log_debug_text) + mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNDEFINED]) + + def test_process_command_err2(self): + """ + Test ERR2 - Parameter Error + """ + # GIVEN: Test object + log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PARAMETER]))] + log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR2"'), + call('(111.111.111.111) Calling function for CLSS')] + + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + + # WHEN: process_command called with ERR2 + pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PARAMETER]) + + # THEN: Appropriate log entries should have been made and methods called/not called + mock_log.error.assert_has_calls(log_error_text) + mock_log.debug.assert_has_calls(log_debug_text) + mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PARAMETER]) + + def test_process_command_err3(self): + """ + Test ERR3 - Unavailable error + """ + # GIVEN: Test object + log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_UNAVAILABLE]))] + log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR3"'), + call('(111.111.111.111) Calling function for CLSS')] + + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + + # WHEN: process_command called with ERR3 + pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_UNAVAILABLE]) + + # THEN: Appropriate log entries should have been made and methods called + mock_log.error.assert_has_calls(log_error_text) + mock_log.debug.assert_has_calls(log_debug_text) + mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_UNAVAILABLE]) + + def test_process_command_err4(self): + """ + Test ERR3 - Unavailable error + """ + # GIVEN: Test object + log_error_text = [call('(111.111.111.111) CLSS: {msg}'.format(msg=STATUS_MSG[E_PROJECTOR]))] + log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "ERR4"'), + call('(111.111.111.111) Calling function for CLSS')] + + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + + # WHEN: process_command called with ERR4 + pjlink.process_command(cmd='CLSS', data=PJLINK_ERRORS[E_PROJECTOR]) + + # THEN: Appropriate log entries should have been made and methods called + mock_log.error.assert_has_calls(log_error_text) + mock_log.debug.assert_has_calls(log_debug_text) + mock_process_clss.assert_called_once_with(data=PJLINK_ERRORS[E_PROJECTOR]) def test_process_command_future(self): """ Test command valid but no method to process yet """ - # 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 - 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') - mock_log.warning.assert_called_once_with(log_text) - - @patch.object(pjlink_test, 'pjlink_functions') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_process_command_invalid(self, mock_log, mock_functions): - """ - Test not a valid command - """ # GIVEN: Test object - pjlink = pjlink_test - mock_functions.reset_mock() - mock_log.reset_mock() + log_warning_text = [call('(111.111.111.111) Unable to process command="CLSS" (Future option?)')] + log_debug_text = [call('(111.111.111.111) Processing command "CLSS" with data "Huh?"')] - # 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)' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: - # 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) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.pjlink_functions = MagicMock() + + # WHEN: Processing a possible future command + pjlink.process_command(cmd='CLSS', data="Huh?") + + # THEN: Appropriate log entries should have been made and methods called/not called + mock_log.debug.assert_has_calls(log_debug_text) + mock_log.warning.assert_has_calls(log_warning_text) + assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions' + assert mock_process_clss.called is False, 'Should not have called process_clss' def test_process_command_ok(self): """ Test command returned success """ # 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() + # GIVEN: Test object and mocks + log_debug_calls = [call('(111.111.111.111) Processing command "CLSS" with data "OK"'), + call('(111.111.111.111) Command "CLSS" returned OK')] - pjlink = self.pjlink_test - log_text = '(111.111.111.111) Command "POWR" returned OK' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'process_clss') as mock_process_clss: - # WHEN: process_command called with a command that returns OK - pjlink.process_command(cmd='POWR', data='OK') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: Appropriate calls should have been made - mock_log.debug.assert_called_with(log_text) - mock_send_command.assert_called_once_with(cmd='POWR') + # WHEN: process_command is called with valid function and data + pjlink.process_command(cmd='CLSS', data='OK') + + # THEN: Appropriate log entries should have been made and methods called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd='CLSS') + mock_process_clss.assert_not_called() diff --git a/tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py b/tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py index 73a66a0f0..c8551b594 100644 --- a/tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py +++ b/tests/functional/openlp_core/projectors/test_projector_pjlink_commands_01.py @@ -23,937 +23,1017 @@ Package to test the openlp.core.projectors.pjlink commands package. """ from unittest import TestCase -from unittest.mock import patch +from unittest.mock import call, patch import openlp.core.projectors.pjlink -from openlp.core.projectors.constants import ERROR_STRING, PJLINK_ERST_DATA, PJLINK_ERST_STATUS, \ - PJLINK_POWR_STATUS, \ - E_ERROR, E_NOT_CONNECTED, E_SOCKET_ADDRESS_NOT_AVAILABLE, E_UNKNOWN_SOCKET_ERROR, E_WARN, \ - S_CONNECTED, S_OFF, S_ON, S_NOT_CONNECTED, S_CONNECTING, S_STANDBY +from openlp.core.projectors.constants import PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_POWR_STATUS, \ + STATUS_CODE, STATUS_MSG, E_ERROR, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, E_WARN, \ + S_CONNECTED, S_CONNECTING, S_OFF, S_OK, S_ON, S_NOT_CONNECTED, S_STANDBY from openlp.core.projectors.db import Projector from openlp.core.projectors.pjlink import PJLink -from tests.resources.projector.data import TEST_PIN, TEST1_DATA -pjlink_test = PJLink(Projector(**TEST1_DATA), pin=TEST_PIN, no_poll=True) -pjlink_test.ip = '127.0.0.1' - -# Create a list of ERST positional data so we don't have to redo the same buildup multiple times -PJLINK_ERST_POSITIONS = [] -for pos in range(0, len(PJLINK_ERST_DATA)): - if pos in PJLINK_ERST_DATA: - PJLINK_ERST_POSITIONS.append(PJLINK_ERST_DATA[pos]) +from tests.resources.projector.data import TEST1_DATA class TestPJLinkCommands(TestCase): """ Tests for the PJLinkCommands class part 1 """ - @patch.object(pjlink_test, 'changeStatus') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_change_status_connection_error(self, mock_log, mock_change_status): + def test_projector_change_status_unknown_socket_error(self): """ Test change_status with connection error """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_status = 0 - pjlink.status_connect = 0 - test_code = E_UNKNOWN_SOCKET_ERROR - mock_change_status.reset_mock() - mock_log.reset_mock() + log_debug_calls = [ + call('(111.111.111.111) Changing status to ' + '{status} "{msg}"'.format(status=STATUS_CODE[E_UNKNOWN_SOCKET_ERROR], + msg=STATUS_MSG[E_UNKNOWN_SOCKET_ERROR])), + call('(111.111.111.111) status_connect: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[E_NOT_CONNECTED], + msg=STATUS_MSG[E_NOT_CONNECTED])), + call('(111.111.111.111) projector_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=STATUS_MSG[S_OK])), + call('(111.111.111.111) error_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[E_UNKNOWN_SOCKET_ERROR], + msg=STATUS_MSG[E_UNKNOWN_SOCKET_ERROR]))] - # WHEN: change_status called with unknown socket error - pjlink.change_status(status=test_code, msg=None) + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: - # THEN: Proper settings should change and signals sent - self.assertEqual(pjlink.projector_status, E_NOT_CONNECTED, 'Projector status should be NOT CONNECTED') - self.assertEqual(pjlink.status_connect, E_NOT_CONNECTED, 'Status connect should be NOT CONNECTED') - mock_change_status.emit.assert_called_once_with(pjlink.ip, E_UNKNOWN_SOCKET_ERROR, - 'An unidentified error occurred') - self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_status = 0 + pjlink.status_connect = 0 - @patch.object(pjlink_test, 'changeStatus') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_change_status_connection_status_connecting(self, mock_log, mock_change_status): + # WHEN: change_status called with unknown socket error + pjlink.change_status(status=E_UNKNOWN_SOCKET_ERROR) + + # THEN: Proper settings should change and signals sent + mock_log.debug.assert_has_calls(log_debug_calls) + assert pjlink.projector_status == S_OK, 'Projector status should not have changed' + assert pjlink.status_connect == E_NOT_CONNECTED, 'Status connect should be NOT CONNECTED' + assert mock_UpdateIcons.emit.called is True, 'Should have called UpdateIcons' + mock_changeStatus.emit.assert_called_once_with(pjlink.ip, E_UNKNOWN_SOCKET_ERROR, + STATUS_MSG[E_UNKNOWN_SOCKET_ERROR]) + + def test_projector_change_status_connection_status_connecting(self): + """ + Test change_status with connecting status + """ + log_debug_calls = [ + call('(111.111.111.111) Changing status to ' + '{status} "{msg}"'.format(status=STATUS_CODE[S_CONNECTING], + msg=STATUS_MSG[S_CONNECTING])), + call('(111.111.111.111) status_connect: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_CONNECTING], + msg=STATUS_MSG[S_CONNECTING])), + call('(111.111.111.111) projector_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=STATUS_MSG[S_OK])), + call('(111.111.111.111) error_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=STATUS_MSG[S_OK]))] + + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_status = 0 + pjlink.status_connect = 0 + + # WHEN: change_status called with CONNECTING + pjlink.change_status(status=S_CONNECTING) + + # THEN: Proper settings should change and signals sent + mock_log.debug.assert_has_calls(log_debug_calls) + mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_CONNECTING, STATUS_MSG[S_CONNECTING]) + assert pjlink.projector_status == S_OK, 'Projector status should not have changed' + assert pjlink.status_connect == S_CONNECTING, 'Status connect should be CONNECTING' + assert mock_UpdateIcons.emit.called is True, 'Should have called UpdateIcons' + + def test_projector_change_status_connection_status_connected(self): + """ + Test change_status with connected status + """ + log_debug_calls = [ + call('(111.111.111.111) Changing status to ' + '{status} "{msg}"'.format(status=STATUS_CODE[S_CONNECTED], + msg=STATUS_MSG[S_CONNECTED])), + call('(111.111.111.111) status_connect: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_CONNECTED], + msg=STATUS_MSG[S_CONNECTED])), + call('(111.111.111.111) projector_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=STATUS_MSG[S_OK])), + call('(111.111.111.111) error_status: ' + '{code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=STATUS_MSG[S_OK]))] + + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus: + + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_status = 0 + pjlink.status_connect = 0 + + # WHEN: change_status called with CONNECTED + pjlink.change_status(status=S_CONNECTED) + + # THEN: Proper settings should change and signals sent + mock_log.debug.assert_has_calls(log_debug_calls) + mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_CONNECTED, 'Connected') + assert pjlink.projector_status == S_OK, 'Projector status should not have changed' + assert pjlink.status_connect == S_CONNECTED, 'Status connect should be CONNECTED' + + def test_projector_change_status_connection_status_with_message(self): """ Test change_status with connection status """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_status = 0 - pjlink.status_connect = 0 - test_code = S_CONNECTING - mock_change_status.reset_mock() - mock_log.reset_mock() - - # WHEN: change_status called with unknown socket error - pjlink.change_status(status=test_code, msg=None) - - # THEN: Proper settings should change and signals sent - self.assertEqual(pjlink.projector_status, S_NOT_CONNECTED, 'Projector status should be NOT CONNECTED') - self.assertEqual(pjlink.status_connect, S_CONNECTING, 'Status connect should be CONNECTING') - mock_change_status.emit.assert_called_once_with(pjlink.ip, S_CONNECTING, 'Connecting') - self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times') - - @patch.object(pjlink_test, 'changeStatus') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_change_status_connection_status_connected(self, mock_log, mock_change_status): - """ - Test change_status with connection status - """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_status = 0 - pjlink.status_connect = 0 - test_code = S_ON - mock_change_status.reset_mock() - mock_log.reset_mock() - - # WHEN: change_status called with unknown socket error - pjlink.change_status(status=test_code, msg=None) - - # THEN: Proper settings should change and signals sent - self.assertEqual(pjlink.projector_status, S_ON, 'Projector status should be ON') - self.assertEqual(pjlink.status_connect, S_CONNECTED, 'Status connect should be CONNECTED') - mock_change_status.emit.assert_called_once_with(pjlink.ip, S_ON, 'Power is on') - self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times') - - @patch.object(pjlink_test, 'changeStatus') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_change_status_connection_status_with_message(self, mock_log, mock_change_status): - """ - Test change_status with connection status - """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_status = 0 - pjlink.status_connect = 0 test_message = 'Different Status Message than default' - test_code = S_ON - mock_change_status.reset_mock() - mock_log.reset_mock() + log_debug_calls = [ + call('(111.111.111.111) Changing status to {status} "{msg}"'.format(status=STATUS_CODE[S_ON], + msg=test_message)), + call('(111.111.111.111) status_connect: {code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=test_message)), + call('(111.111.111.111) projector_status: {code}: "{msg}"'.format(code=STATUS_CODE[S_ON], + msg=test_message)), + call('(111.111.111.111) error_status: {code}: "{msg}"'.format(code=STATUS_CODE[S_OK], + msg=test_message))] - # WHEN: change_status called with unknown socket error - pjlink.change_status(status=test_code, msg=test_message) + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'changeStatus') as mock_changeStatus: - # THEN: Proper settings should change and signals sent - self.assertEqual(pjlink.projector_status, S_ON, 'Projector status should be ON') - self.assertEqual(pjlink.status_connect, S_CONNECTED, 'Status connect should be CONNECTED') - mock_change_status.emit.assert_called_once_with(pjlink.ip, S_ON, test_message) - self.assertEqual(mock_log.debug.call_count, 3, 'Debug log should have been called 3 times') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_status = 0 + pjlink.status_connect = 0 - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_av_mute_status(self, mock_log, mock_send_command): + # WHEN: change_status called with projector ON status + pjlink.change_status(status=S_ON, msg=test_message) + + # THEN: Proper settings should change and signals sent + mock_log.debug.assert_has_calls(log_debug_calls) + mock_changeStatus.emit.assert_called_once_with(pjlink.ip, S_ON, test_message) + assert pjlink.projector_status == S_ON, 'Projector status should be ON' + assert pjlink.status_connect == S_OK, 'Status connect should not have changed' + + def test_projector_get_av_mute_status(self): """ Test sending command to retrieve shutter/audio state """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'AVMT' - test_log = '(127.0.0.1) Sending AVMT command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_av_mute_status is called - pjlink.get_av_mute_status() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_av_mute_status is called + pjlink.get_av_mute_status() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_available_inputs(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_available_inputs(self): """ Test sending command to retrieve avaliable inputs """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'INST' - test_log = '(127.0.0.1) Sending INST command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_available_inputs is called - pjlink.get_available_inputs() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_available_inputs is called + pjlink.get_available_inputs() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_error_status(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_error_status(self): """ Test sending command to retrieve projector error status """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'ERST' - test_log = '(127.0.0.1) Sending ERST command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # WHEN: get_error_status is called - pjlink.get_error_status() + # WHEN: get_error_status is called + pjlink.get_error_status() - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_input_source(self, mock_log, mock_send_command): + def test_projector_get_input_source(self): """ Test sending command to retrieve current input """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'INPT' - test_log = '(127.0.0.1) Sending INPT command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_input_source is called - pjlink.get_input_source() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_input_source() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_lamp_status(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_lamp_status(self): """ Test sending command to retrieve lamp(s) status """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'LAMP' - test_log = '(127.0.0.1) Sending LAMP command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_lamp_status is called - pjlink.get_lamp_status() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_lamp_status() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_manufacturer(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_manufacturer(self): """ Test sending command to retrieve manufacturer name """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'INF1' - test_log = '(127.0.0.1) Sending INF1 command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_manufacturer is called - pjlink.get_manufacturer() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_manufacturer() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_model(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_model(self): """ Test sending command to get model information """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'INF2' - test_log = '(127.0.0.1) Sending INF2 command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_model is called - pjlink.get_model() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_model() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_name(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_name(self): """ Test sending command to get user-assigned name """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'NAME' - test_log = '(127.0.0.1) Sending NAME command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_name is called - pjlink.get_name() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_name() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_other_info(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_other_info(self): """ Test sending command to retrieve other information """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'INFO' - test_log = '(127.0.0.1) Sending INFO command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_other_info is called - pjlink.get_other_info() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_other_info() - @patch.object(pjlink_test, 'send_command') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_get_power_status(self, mock_log, mock_send_command): + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) + + def test_projector_get_power_status(self): """ Test sending command to retrieve current power state """ - # GIVEN: Test object - pjlink = pjlink_test - mock_log.reset_mock() - mock_send_command.reset_mock() test_data = 'POWR' - test_log = '(127.0.0.1) Sending POWR command' + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Sending {cmd} command'.format(cmd=test_data))] - # WHEN: get_power_status called - pjlink.get_power_status() + # GIVEN: Test object and mocks + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: log data and send_command should have been called - mock_log.debug.assert_called_once_with(test_log) - mock_send_command.assert_called_once_with(cmd=test_data) + # WHEN: get_input_source is called + pjlink.get_power_status() - def test_projector_get_status_error(self): - """ - Test to check returned information for error code - """ - # GIVEN: Test object - pjlink = pjlink_test - test_string = 'E_SOCKET_ADDRESS_NOT_AVAILABLE' - test_message = 'The address specified to socket.bind() does not belong to the host' - - # WHEN: get_status called - string, message = pjlink._get_status(status=E_SOCKET_ADDRESS_NOT_AVAILABLE) - - # THEN: Proper strings should have been returned - self.assertEqual(string, test_string, 'Code as string should have been returned') - self.assertEqual(message, test_message, 'Description of code should have been returned') + # THEN: log data and send_command should have been called + mock_log.debug.assert_has_calls(log_debug_calls) + mock_send_command.assert_called_once_with(cmd=test_data) def test_projector_get_status_invalid(self): """ Test to check returned information for error code """ # GIVEN: Test object - pjlink = pjlink_test - test_string = 'Test string since get_status will only work with int' - test_message = 'Invalid status code' + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + test_string = 'NaN test' # WHEN: get_status called - string, message = pjlink._get_status(status=test_string) + code, message = pjlink._get_status(status=test_string) - # THEN: Proper strings should have been returned - self.assertEqual(string, -1, 'Should have returned -1 as a bad status check') - self.assertEqual(message, test_message, 'Error message should have been returned') + # THEN: Proper data should have been returned + assert code == -1, 'Should have returned -1 as a bad status check' + assert message is None, 'Invalid code type should have returned None for message' - def test_projector_get_status_status(self): + def test_projector_get_status_valid(self): """ Test to check returned information for status codes """ # GIVEN: Test object - pjlink = pjlink_test - test_string = 'S_NOT_CONNECTED' - test_message = 'Not connected' + test_message = 'Not Connected' + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) # WHEN: get_status called - string, message = pjlink._get_status(status=S_NOT_CONNECTED) + code, message = pjlink._get_status(status=S_NOT_CONNECTED) # THEN: Proper strings should have been returned - self.assertEqual(string, test_string, 'Code as string should have been returned') - self.assertEqual(message, test_message, 'Description of code should have been returned') + assert code == 'S_NOT_CONNECTED', 'Code returned should have been the same code that was sent' + assert message == test_message, 'Description of code should have been returned' def test_projector_get_status_unknown(self): """ Test to check returned information for unknown code """ # GIVEN: Test object - pjlink = pjlink_test - test_string = 999999 - test_message = 'Unknown status' + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) # WHEN: get_status called - string, message = pjlink._get_status(status=test_string) + code, message = pjlink._get_status(status=9999) # THEN: Proper strings should have been returned - self.assertEqual(string, test_string, 'Received code should have been returned') - self.assertEqual(message, test_message, 'Unknown status string should have been returned') + assert code is None, 'Code returned should have been the same code that was sent' + assert message is None, 'Should have returned None as message' def test_projector_process_inf1(self): """ Test saving INF1 data (manufacturer) """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.manufacturer = None test_data = 'TEst INformation MultiCase' + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.manufacturer = None + # WHEN: process_inf called with test data pjlink.process_inf1(data=test_data) # THEN: Data should be saved - self.assertEqual(pjlink.manufacturer, test_data, 'Test data should have been saved') + assert pjlink.manufacturer == test_data, 'Test data should have been saved' def test_projector_process_inf2(self): """ Test saving INF2 data (model) """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.model = None test_data = 'TEst moDEl MultiCase' + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.model = None + # WHEN: process_inf called with test data pjlink.process_inf2(data=test_data) # THEN: Data should be saved - self.assertEqual(pjlink.model, test_data, 'Test data should have been saved') + assert pjlink.model == test_data, 'Test data should have been saved' def test_projector_process_info(self): """ Test saving INFO data (other information) """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.other_info = None test_data = 'TEst ExtrANEous MultiCase INformatoin that MFGR might Set' + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.other_info = None + # WHEN: process_inf called with test data pjlink.process_info(data=test_data) # THEN: Data should be saved - self.assertEqual(pjlink.other_info, test_data, 'Test data should have been saved') + assert pjlink.other_info == test_data, 'Test data should have been saved' - @patch.object(pjlink_test, 'projectorUpdateIcons') - def test_projector_process_avmt_bad_data(self, mock_UpdateIcons): + def test_projector_process_avmt_bad_data(self): """ Test avmt bad data fail """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.shutter = True - pjlink.mute = True + with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.shutter = True + pjlink.mute = True - # WHEN: Called with an invalid setting - pjlink.process_avmt('36') + # WHEN: Called with an invalid setting + pjlink.process_avmt('36') - # THEN: Shutter should be closed and mute should be True - self.assertTrue(pjlink.shutter, 'Shutter should changed') - self.assertTrue(pjlink.mute, 'Audio should not have changed') - self.assertFalse(mock_UpdateIcons.emit.called, 'Update icons should NOT have been called') + # THEN: Shutter should be closed and mute should be True + assert pjlink.shutter is True, 'Shutter should changed' + assert pjlink.mute is True, 'Audio should not have changed' + assert mock_UpdateIcons.emit.called is False, 'Update icons should NOT have been called' - @patch.object(pjlink_test, 'projectorUpdateIcons') - def test_projector_process_avmt_closed_muted(self, mock_UpdateIcons): + def test_projector_process_avmt_closed_muted(self): """ Test avmt status shutter closed and mute off """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.shutter = False - pjlink.mute = False + with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.shutter = False + pjlink.mute = False - # WHEN: Called with setting shutter to closed and mute on - pjlink.process_avmt('31') + # WHEN: Called with setting shutter to closed and mute on + pjlink.process_avmt('31') - # THEN: Shutter should be closed and mute should be True - self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') - self.assertTrue(pjlink.mute, 'Audio should be muted') - self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called') + # THEN: Shutter should be closed and mute should be True + assert pjlink.shutter is True, 'Shutter should have been set to closed' + assert pjlink.mute is True, 'Audio should be muted' + assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called' - @patch.object(pjlink_test, 'projectorUpdateIcons') - def test_projector_process_avmt_shutter_closed(self, mock_UpdateIcons): + def test_projector_process_avmt_shutter_closed(self): """ Test avmt status shutter closed and audio unchanged """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.shutter = False - pjlink.mute = True + with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.shutter = False + pjlink.mute = True - # WHEN: Called with setting shutter closed and mute off - pjlink.process_avmt('11') + # WHEN: Called with setting shutter closed and mute off + pjlink.process_avmt('11') - # THEN: Shutter should be True and mute should be False - self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') - self.assertTrue(pjlink.mute, 'Audio should not have changed') - self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called') + # THEN: Shutter should be True and mute should be False + assert pjlink.shutter is True, 'Shutter should have been set to closed' + assert pjlink.mute is True, 'Audio should not have changed' + assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called' - @patch.object(pjlink_test, 'projectorUpdateIcons') - def test_projector_process_avmt_audio_muted(self, mock_UpdateIcons): + def test_projector_process_avmt_audio_muted(self): """ Test avmt status shutter unchanged and mute on """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.shutter = True - pjlink.mute = False + with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.shutter = True + pjlink.mute = False - # WHEN: Called with setting shutter closed and mute on - pjlink.process_avmt('21') + # WHEN: Called with setting shutter closed and mute on + pjlink.process_avmt('21') - # THEN: Shutter should be closed and mute should be True - self.assertTrue(pjlink.shutter, 'Shutter should not have changed') - self.assertTrue(pjlink.mute, 'Audio should be off') - self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called') + # THEN: Shutter should be closed and mute should be True + assert pjlink.shutter is True, 'Shutter should not have changed' + assert pjlink.mute is True, 'Audio should be off' + assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called' - @patch.object(pjlink_test, 'projectorUpdateIcons') - def test_projector_process_avmt_open_unmuted(self, mock_UpdateIcons): + def test_projector_process_avmt_open_unmuted(self): """ Test avmt status shutter open and mute off """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.shutter = True - pjlink.mute = True + with patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.shutter = True + pjlink.mute = True - # WHEN: Called with setting shutter to closed and mute on - pjlink.process_avmt('30') + # WHEN: Called with setting shutter to closed and mute on + pjlink.process_avmt('30') - # THEN: Shutter should be closed and mute should be True - self.assertFalse(pjlink.shutter, 'Shutter should have been set to open') - self.assertFalse(pjlink.mute, 'Audio should be on') - self.assertTrue(mock_UpdateIcons.emit.called, 'Update icons should have been called') + # THEN: Shutter should be closed and mute should be True + assert pjlink.shutter is False, 'Shutter should have been set to open' + assert pjlink.mute is False, 'Audio should be on' + assert mock_UpdateIcons.emit.called is True, 'Update icons should have been called' def test_projector_process_clss_one(self): """ Test class 1 sent from projector """ # GIVEN: Test object - pjlink = pjlink_test + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) # WHEN: Process class response pjlink.process_clss('1') # THEN: Projector class should be set to 1 - self.assertEqual(pjlink.pjlink_class, '1', - 'Projector should have set class=1') + assert pjlink.pjlink_class == '1', 'Projector should have set class=1' def test_projector_process_clss_two(self): """ Test class 2 sent from projector """ # GIVEN: Test object - pjlink = pjlink_test + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) # WHEN: Process class response pjlink.process_clss('2') # THEN: Projector class should be set to 1 - self.assertEqual(pjlink.pjlink_class, '2', - 'Projector should have set class=2') + assert pjlink.pjlink_class == '2', 'Projector should have set class=2' - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_clss_invalid_nan(self, mock_log): + def test_projector_process_clss_invalid_nan(self): """ Test CLSS reply has no class number """ + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Setting pjlink_class for this projector to "1"')] + log_error_calls = [call('(111.111.111.111) NAN CLSS version reply "Z" - defaulting to class "1"')] + # GIVEN: Test object - pjlink = pjlink_test + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # WHEN: Process invalid reply - pjlink.process_clss('Z') - log_text = '(127.0.0.1) NAN CLSS version reply "Z" - defaulting to class "1"' + # WHEN: Process invalid reply + pjlink.process_clss('Z') - # THEN: Projector class should be set with default value - self.assertEqual(pjlink.pjlink_class, '1', - 'Non-standard class reply should have set class=1') - mock_log.error.assert_called_once_with(log_text) + # THEN: Projector class should be set with default value + assert pjlink.pjlink_class == '1', 'Invalid NaN class reply should have set class=1' + mock_log.error.assert_has_calls(log_error_calls) + mock_log.debug.assert_has_calls(log_debug_calls) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_clss_invalid_no_version(self, mock_log): + def test_projector_process_clss_invalid_no_version(self): """ Test CLSS reply has no class number """ + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED])), + call('(111.111.111.111) Setting pjlink_class for this projector to "1"')] + log_error_calls = [call('(111.111.111.111) No numbers found in class version reply "Invalid" ' + '- defaulting to class "1"')] + # GIVEN: Test object - pjlink = pjlink_test + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # 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"' + # WHEN: Process invalid reply + pjlink.process_clss('Invalid') - # THEN: Projector class should be set with default value - self.assertEqual(pjlink.pjlink_class, '1', - 'Non-standard class reply should have set class=1') - mock_log.error.assert_called_once_with(log_text) + # THEN: Projector class should be set with default value + assert pjlink.pjlink_class == '1', 'Invalid class reply should have set class=1' + mock_log.error.assert_has_calls(log_error_calls) + mock_log.debug.assert_has_calls(log_debug_calls) def test_projector_process_erst_all_ok(self): """ - Test test_projector_process_erst_all_ok + Test to verify pjlink.projector_errors is set to None when no errors """ + chk_data = '0' * PJLINK_ERST_DATA['DATA_LENGTH'] + # GIVEN: Test object - pjlink = pjlink_test - chk_test = PJLINK_ERST_STATUS['OK'] - chk_param = chk_test * len(PJLINK_ERST_POSITIONS) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) # WHEN: process_erst with no errors - pjlink.process_erst(chk_param) + pjlink.process_erst(chk_data) # THEN: PJLink instance errors should be None - self.assertIsNone(pjlink.projector_errors, 'projector_errors should have been set to None') + assert pjlink.projector_errors is None, 'projector_errors should have been set to None' - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_erst_data_invalid_length(self, mock_log): + def test_projector_process_erst_data_invalid_length(self): """ Test test_projector_process_erst_data_invalid_length """ + chk_data = '0' * (PJLINK_ERST_DATA['DATA_LENGTH'] + 1) + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED]))] + log_warn_calls = [call('111.111.111.111) Invalid error status response "0000000": ' + 'length != {chk}'.format(chk=PJLINK_ERST_DATA['DATA_LENGTH']))] + # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_errors = None - log_text = '127.0.0.1) Invalid error status response "11111111": length != 6' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_errors = None - # WHEN: process_erst called with invalid data (too many values - pjlink.process_erst('11111111') + # WHEN: process_erst called with invalid data (too many values + pjlink.process_erst(chk_data) - # THEN: pjlink.projector_errors should be empty and warning logged - self.assertIsNone(pjlink.projector_errors, 'There should be no errors') - self.assertTrue(mock_log.warning.called, 'Warning should have been logged') - mock_log.warning.assert_called_once_with(log_text) + # THEN: pjlink.projector_errors should be empty and warning logged + assert pjlink.projector_errors is None, 'There should be no errors' + mock_log.debug.assert_has_calls(log_debug_calls) + mock_log.warning.assert_has_calls(log_warn_calls) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_erst_data_invalid_nan(self, mock_log): + def test_projector_process_erst_data_invalid_nan(self): """ Test test_projector_process_erst_data_invalid_nan """ + chk_data = 'Z' + ('0' * (PJLINK_ERST_DATA['DATA_LENGTH'] - 1)) + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is ' + '{state}'.format(state=STATUS_CODE[S_NOT_CONNECTED]))] + log_warn_calls = [call('(111.111.111.111) Invalid error status response "Z00000"')] + # GIVEN: Test object - pjlink = pjlink_test - pjlink.projector_errors = None - log_text = '(127.0.0.1) Invalid error status response "1111Z1"' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_errors = None - # WHEN: process_erst called with invalid data (too many values - pjlink.process_erst('1111Z1') + # WHEN: process_erst called with invalid data (too many values + pjlink.process_erst(chk_data) - # THEN: pjlink.projector_errors should be empty and warning logged - self.assertIsNone(pjlink.projector_errors, 'There should be no errors') - self.assertTrue(mock_log.warning.called, 'Warning should have been logged') - mock_log.warning.assert_called_once_with(log_text) + # THEN: pjlink.projector_errors should be empty and warning logged + assert pjlink.projector_errors is None, 'There should be no errors' + mock_log.debug.assert_has_calls(log_debug_calls) + mock_log.warning.assert_has_calls(log_warn_calls) def test_projector_process_erst_all_warn(self): """ Test test_projector_process_erst_all_warn """ + chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_WARN], + lamp=PJLINK_ERST_STATUS[E_WARN], + temp=PJLINK_ERST_STATUS[E_WARN], + cover=PJLINK_ERST_STATUS[E_WARN], + filt=PJLINK_ERST_STATUS[E_WARN], + other=PJLINK_ERST_STATUS[E_WARN]) + chk_test = {'Fan': E_WARN, + 'Lamp': E_WARN, + 'Temperature': E_WARN, + 'Cover': E_WARN, + 'Filter': E_WARN, + 'Other': E_WARN} + # GIVEN: Test object - pjlink = pjlink_test - chk_test = PJLINK_ERST_STATUS[E_WARN] - chk_string = ERROR_STRING[E_WARN] - chk_param = chk_test * len(PJLINK_ERST_POSITIONS) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_errors = None # WHEN: process_erst with status set to WARN - pjlink.process_erst(chk_param) + pjlink.process_erst(chk_data) # 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)) + assert pjlink.projector_errors == chk_test, 'Projector errors should be all E_WARN' def test_projector_process_erst_all_error(self): """ Test test_projector_process_erst_all_error """ + chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[E_ERROR], + lamp=PJLINK_ERST_STATUS[E_ERROR], + temp=PJLINK_ERST_STATUS[E_ERROR], + cover=PJLINK_ERST_STATUS[E_ERROR], + filt=PJLINK_ERST_STATUS[E_ERROR], + other=PJLINK_ERST_STATUS[E_ERROR]) + chk_test = {'Fan': E_ERROR, + 'Lamp': E_ERROR, + 'Temperature': E_ERROR, + 'Cover': E_ERROR, + 'Filter': E_ERROR, + 'Other': E_ERROR} + # GIVEN: Test object - pjlink = pjlink_test - chk_test = PJLINK_ERST_STATUS[E_ERROR] - chk_string = ERROR_STRING[E_ERROR] - chk_param = chk_test * len(PJLINK_ERST_POSITIONS) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_errors = None # WHEN: process_erst with status set to WARN - pjlink.process_erst(chk_param) + pjlink.process_erst(chk_data) # 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)) + assert pjlink.projector_errors == chk_test, 'Projector errors should be all E_ERROR' def test_projector_process_erst_warn_cover_only(self): """ Test test_projector_process_erst_warn_cover_only """ + chk_data = '{fan}{lamp}{temp}{cover}{filt}{other}'.format(fan=PJLINK_ERST_STATUS[S_OK], + lamp=PJLINK_ERST_STATUS[S_OK], + temp=PJLINK_ERST_STATUS[S_OK], + cover=PJLINK_ERST_STATUS[E_WARN], + filt=PJLINK_ERST_STATUS[S_OK], + other=PJLINK_ERST_STATUS[S_OK]) + chk_test = {'Cover': E_WARN} + # GIVEN: Test object - pjlink = pjlink_test - chk_test = PJLINK_ERST_STATUS[E_WARN] - chk_string = ERROR_STRING[E_WARN] - pos = PJLINK_ERST_DATA['COVER'] - build_chk = [] - for check in range(0, len(PJLINK_ERST_POSITIONS)): - if check == pos: - build_chk.append(chk_test) - else: - build_chk.append(PJLINK_ERST_STATUS['OK']) - chk_param = ''.join(build_chk) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.projector_errors = None - # WHEN: process_erst with cover only set to WARN and all others set to OK - pjlink.process_erst(chk_param) + # WHEN: process_erst with status set to WARN + pjlink.process_erst(chk_data) - # THEN: Only COVER should have an error - self.assertEqual(len(pjlink.projector_errors), 1, 'projector_errors should only have 1 error') - self.assertTrue(('Cover' in pjlink.projector_errors), 'projector_errors should have an error for "Cover"') - self.assertEqual(pjlink.projector_errors['Cover'], - chk_string, - 'projector_errors["Cover"] should have error "{err}"'.format(err=chk_string)) + # THEN: PJLink instance errors should match only cover warning + assert 1 == len(pjlink.projector_errors), 'There should only be 1 error listed in projector_errors' + assert 'Cover' in pjlink.projector_errors, '"Cover" should be the only error listed' + assert pjlink.projector_errors['Cover'] == E_WARN, '"Cover" should have E_WARN listed as error' + assert chk_test == pjlink.projector_errors, 'projector_errors should match test errors' - def test_projector_process_inpt(self): + def test_projector_process_inpt_valid(self): """ Test input source status shows current input """ + log_debug_calls = [call('(111.111.111.111) reset_information() connect status is S_NOT_CONNECTED')] + chk_source_available = ['11', '12', '21', '22', '31', '32'] + # GIVEN: Test object - pjlink = pjlink_test - pjlink.source = '0' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.source_available = chk_source_available + pjlink.source = '11' - # WHEN: Called with input source - pjlink.process_inpt('1') + # WHEN: Called with input source + pjlink.process_inpt('21') - # THEN: Input selected should reflect current input - self.assertEqual(pjlink.source, '1', 'Input source should be set to "1"') + # THEN: Input selected should reflect current input + assert pjlink.source == '21', 'Input source should be set to "21"' + mock_log.debug.assert_has_calls(log_debug_calls) - @patch.object(pjlink_test, 'projectorUpdateIcons') - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_inst(self, mock_log, mock_UpdateIcons): + def test_projector_process_input_not_in_list(self): + """ + Test setting input outside of available inputs + + TODO: Future test + """ + pass + + def test_projector_process_input_not_in_default(self): + """ + Test setting input with no sources available + TODO: Future test + """ + pass + + def test_projector_process_input_invalid(self): + """ + Test setting input with an invalid value + + TODO: Future test + """ + + def test_projector_process_inst_class_1(self): """ Test saving video source available information """ + log_debug_calls = [call('(111.111.111.111) Setting projector sources_available to ' + '"[\'11\', \'12\', \'21\', \'22\', \'31\', \'32\']"')] + chk_data = '21 12 11 22 32 31' # Although they should already be sorted, use unsorted to verify method + chk_test = ['11', '12', '21', '22', '31', '32'] + # GIVEN: Test object - 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']\"" - mock_UpdateIcons.reset_mock() - mock_log.reset_mock() + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.source_available = [] - # WHEN: process_inst called with test data - pjlink.process_inst(data=test_data) + # WHEN: process_inst called with test data + pjlink.process_inst(data=chk_data) - # THEN: Data should have been sorted and saved properly - self.assertEqual(pjlink.source_available, test_saved, "Sources should have been sorted and saved") - mock_log.debug.assert_called_once_with(log_data) - self.assertTrue(mock_UpdateIcons.emit.called, 'Update Icons should have been called') + # THEN: Data should have been sorted and saved properly + assert pjlink.source_available == chk_test, "Sources should have been sorted and saved" + mock_log.debug.assert_has_calls(log_debug_calls) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_lamp_invalid(self, mock_log): + def test_projector_process_lamp_invalid(self): """ Test status multiple lamp on/off and hours """ + log_data = [call('(111.111.111.111) process_lamp(): Invalid data "11111 1 22222 0 333A3 1"')] + # GIVEN: Test object - pjlink = pjlink_test - pjlink.lamp = [{'Hours': 00000, 'On': True}, - {'Hours': 11111, 'On': False}] - log_data = '(127.0.0.1) process_lamp(): Invalid data "11111 1 22222 0 333A3 1"' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.lamp = [{'Hours': 00000, 'On': True}, + {'Hours': 11111, 'On': False}] - # WHEN: Call process_command with invalid lamp data - pjlink.process_lamp('11111 1 22222 0 333A3 1') + # WHEN: Call process_command with invalid lamp data + pjlink.process_lamp('11111 1 22222 0 333A3 1') - # THEN: lamps should not have changed - self.assertEqual(len(pjlink.lamp), 2, - 'Projector should have kept 2 lamps specified') - self.assertEqual(pjlink.lamp[0]['On'], True, - 'Lamp 1 power status should have been set to TRUE') - self.assertEqual(pjlink.lamp[0]['Hours'], 00000, - 'Lamp 1 hours should have been left at 00000') - self.assertEqual(pjlink.lamp[1]['On'], False, - 'Lamp 2 power status should have been set to FALSE') - self.assertEqual(pjlink.lamp[1]['Hours'], 11111, - 'Lamp 2 hours should have been left at 11111') - mock_log.warning.assert_called_once_with(log_data) + # THEN: lamps should not have changed + assert 2 == len(pjlink.lamp), 'Projector should have kept 2 lamps specified' + assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have stayed TRUE' + assert 00000 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been left at 00000' + assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have stayed FALSE' + assert 11111 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been left at 11111' + mock_log.warning.assert_has_calls(log_data) def test_projector_process_lamp_multiple(self): """ Test status multiple lamp on/off and hours """ # GIVEN: Test object - pjlink = pjlink_test - pjlink.lamps = [] + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.lamp = [] - # WHEN: Call process_command with lamp data + # WHEN: Call process_command with invalid lamp data pjlink.process_lamp('11111 1 22222 0 33333 1') # THEN: Lamp should have been set with proper lamp status - self.assertEqual(len(pjlink.lamp), 3, - 'Projector should have 3 lamps specified') - self.assertEqual(pjlink.lamp[0]['On'], True, - 'Lamp 1 power status should have been set to TRUE') - self.assertEqual(pjlink.lamp[0]['Hours'], 11111, - 'Lamp 1 hours should have been set to 11111') - self.assertEqual(pjlink.lamp[1]['On'], False, - 'Lamp 2 power status should have been set to FALSE') - self.assertEqual(pjlink.lamp[1]['Hours'], 22222, - 'Lamp 2 hours should have been set to 22222') - self.assertEqual(pjlink.lamp[2]['On'], True, - 'Lamp 3 power status should have been set to TRUE') - self.assertEqual(pjlink.lamp[2]['Hours'], 33333, - 'Lamp 3 hours should have been set to 33333') + assert 3 == len(pjlink.lamp), 'Projector should have 3 lamps specified' + assert pjlink.lamp[0]['On'] is True, 'Lamp 1 power status should have been set to TRUE' + assert 11111 == pjlink.lamp[0]['Hours'], 'Lamp 1 hours should have been set to 11111' + assert pjlink.lamp[1]['On'] is False, 'Lamp 2 power status should have been set to FALSE' + assert 22222 == pjlink.lamp[1]['Hours'], 'Lamp 2 hours should have been set to 22222' + assert pjlink.lamp[2]['On'] is True, 'Lamp 3 power status should have been set to TRUE' + assert 33333 == pjlink.lamp[2]['Hours'], 'Lamp 3 hours should have been set to 33333' def test_projector_process_lamp_single(self): """ Test status lamp on/off and hours """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.lamps = [] - # WHEN: Call process_command with lamp data + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.lamp = [] + + # WHEN: Call process_command with invalid lamp data pjlink.process_lamp('22222 1') # THEN: Lamp should have been set with status=ON and hours=22222 - self.assertEqual(pjlink.lamp[0]['On'], True, - 'Lamp power status should have been set to TRUE') - self.assertEqual(pjlink.lamp[0]['Hours'], 22222, - 'Lamp hours should have been set to 22222') + assert 1 == len(pjlink.lamp), 'Projector should have only 1 lamp' + assert pjlink.lamp[0]['On'] is True, 'Lamp power status should have been set to TRUE' + assert 22222 == pjlink.lamp[0]['Hours'], 'Lamp hours should have been set to 22222' - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_name(self, mock_log): + def test_projector_process_name(self): """ Test saving NAME data from projector """ - # GIVEN: Test data - pjlink = pjlink_test - test_data = "Some Name the End-User Set IN Projector" - test_log = '(127.0.0.1) Setting projector PJLink name to "Some Name the End-User Set IN Projector"' - mock_log.reset_mock() + chk_data = "Some Name the End-User Set IN Projector" + log_debug_calls = [call('(111.111.111.111) Setting projector PJLink name to ' + '"Some Name the End-User Set IN Projector"')] - # WHEN: process_name called with test data - pjlink.process_name(data=test_data) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) - # THEN: name should be set and logged - self.assertEqual(pjlink.pjlink_name, test_data, 'Name test data should have been saved') - mock_log.debug.assert_called_once_with(test_log) + # WHEN: process_name called with test data + pjlink.process_name(data=chk_data) - @patch.object(pjlink_test, 'projectorUpdateIcons') - @patch.object(pjlink_test, 'send_command') - @patch.object(pjlink_test, 'change_status') - def test_projector_process_powr_on(self, - mock_change_status, - mock_send_command, - mock_UpdateIcons): + # THEN: name should be set and logged + assert pjlink.pjlink_name == chk_data, 'Name test data should have been saved' + mock_log.debug.assert_has_calls(log_debug_calls) + + def test_projector_process_powr_on(self): """ Test status power to ON """ - # GIVEN: Test object and preset - pjlink = pjlink_test - pjlink.power = S_STANDBY - test_data = PJLINK_POWR_STATUS[S_ON] + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: - # WHEN: Call process_command with turn power on command - pjlink.process_command(cmd='POWR', data=test_data) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.power = S_STANDBY - # THEN: Power should be set to ON - self.assertEqual(pjlink.power, S_ON, 'Power should have been set to ON') - mock_send_command.assert_called_once_with('INST') - mock_change_status.assert_called_once_with(PJLINK_POWR_STATUS[test_data]) - self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') + # WHEN: process_name called with test data + pjlink.process_powr(data=PJLINK_POWR_STATUS[S_ON]) - @patch.object(pjlink_test, 'projectorUpdateIcons') - @patch.object(pjlink_test, 'send_command') - @patch.object(pjlink_test, 'change_status') - def test_projector_process_powr_invalid(self, - mock_change_status, - mock_send_command, - mock_UpdateIcons): + # THEN: Power should be set to ON + assert pjlink.power == S_ON, 'Power should have been set to ON' + assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called' + mock_send_command.assert_called_once_with('INST') + mock_change_status.assert_called_once_with(S_ON) + + def test_projector_process_powr_invalid(self): """ Test process_powr invalid call """ - # GIVEN: Test object and preset - pjlink = pjlink_test - pjlink.power = S_STANDBY - test_data = '99' + log_warn_calls = [call('(111.111.111.111) Unknown power response: "99"')] - # WHEN: Call process_command with turn power on command - pjlink.process_command(cmd='POWR', data=test_data) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: - # THEN: Power should be set to ON - self.assertEqual(pjlink.power, S_STANDBY, 'Power should not have changed') - self.assertFalse(mock_change_status.called, 'Change status should not have been called') - self.assertFalse(mock_send_command.called, 'send_command("INST") should not have been called') - self.assertFalse(mock_UpdateIcons.emit.called, 'projectorUpdateIcons should not have been called') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.power = S_STANDBY - @patch.object(pjlink_test, 'projectorUpdateIcons') - @patch.object(pjlink_test, 'send_command') - @patch.object(pjlink_test, 'change_status') - def test_projector_process_powr_off(self, - mock_change_status, - mock_send_command, - mock_UpdateIcons): + # WHEN: process_name called with test data + pjlink.process_powr(data='99') + + # THEN: Power should be set to ON + assert pjlink.power == S_STANDBY, 'Power should not have changed' + assert mock_UpdateIcons.emit.called is False, 'projectorUpdateIcons() should not have been called' + mock_change_status.called is False, 'change_status() should not have been called' + mock_send_command.called is False, 'send_command() should not have been called' + mock_log.warning.assert_has_calls(log_warn_calls) + + def test_projector_process_powr_off(self): """ Test status power to STANDBY """ - # GIVEN: Test object and preset - pjlink = pjlink_test - pjlink.power = S_ON - test_data = PJLINK_POWR_STATUS[S_STANDBY] + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'change_status') as mock_change_status, \ + patch.object(openlp.core.projectors.pjlink.PJLink, 'projectorUpdateIcons') as mock_UpdateIcons: - # WHEN: Call process_command with turn power on command - pjlink.process_command(cmd='POWR', data=test_data) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.power = S_ON - # THEN: Power should be set to STANDBY - self.assertEqual(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY') - self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') - mock_change_status.assert_called_once_with(PJLINK_POWR_STATUS[test_data]) - self.assertFalse(mock_send_command.called, "send_command['INST'] should not have been called") + # WHEN: process_name called with test data + pjlink.process_powr(data=PJLINK_POWR_STATUS[S_STANDBY]) + + # THEN: Power should be set to ON + assert pjlink.power == S_STANDBY, 'Power should have changed to S_STANDBY' + assert mock_UpdateIcons.emit.called is True, 'projectorUpdateIcons should have been called' + mock_change_status.called is True, 'change_status should have been called' + mock_send_command.called is False, 'send_command should not have been called' def test_projector_process_rfil_save(self): """ Test saving filter type """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.model_filter = None filter_model = 'Filter Type Test' + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.model_filter = None + # WHEN: Filter model is received pjlink.process_rfil(data=filter_model) # THEN: Filter model number should be saved - self.assertEqual(pjlink.model_filter, filter_model, 'Filter type should have been saved') + assert pjlink.model_filter == filter_model, 'Filter type should have been saved' def test_projector_process_rfil_nosave(self): """ Test saving filter type previously saved """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.model_filter = 'Old filter type' filter_model = 'Filter Type Test' + log_warn_calls = [call('(111.111.111.111) Filter model already set'), + call('(111.111.111.111) Saved model: "Old filter type"'), + call('(111.111.111.111) New model: "Filter Type Test"')] - # WHEN: Filter model is received - pjlink.process_rfil(data=filter_model) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: - # THEN: Filter model number should be saved - self.assertNotEquals(pjlink.model_filter, filter_model, 'Filter type should NOT have been saved') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.model_filter = 'Old filter type' + + # WHEN: Filter model is received + pjlink.process_rfil(data=filter_model) + + # THEN: Filter model number should be saved + assert pjlink.model_filter != filter_model, 'Filter type should NOT have been saved' + mock_log.warning.assert_has_calls(log_warn_calls) def test_projector_process_rlmp_save(self): """ Test saving lamp type """ # GIVEN: Test object - pjlink = pjlink_test + # GIVEN: Test object + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) pjlink.model_lamp = None lamp_model = 'Lamp Type Test' @@ -961,159 +1041,179 @@ class TestPJLinkCommands(TestCase): pjlink.process_rlmp(data=lamp_model) # THEN: Filter model number should be saved - self.assertEqual(pjlink.model_lamp, lamp_model, 'Lamp type should have been saved') + assert pjlink.model_lamp == lamp_model, 'Lamp type should have been saved' def test_projector_process_rlmp_nosave(self): """ Test saving lamp type previously saved """ + lamp_model = 'Lamp Type Test' + log_warn_calls = [call('(111.111.111.111) Lamp model already set'), + call('(111.111.111.111) Saved lamp: "Old lamp type"'), + call('(111.111.111.111) New lamp: "Lamp Type Test"')] + # GIVEN: Test object - pjlink = pjlink_test - pjlink.model_lamp = 'Old lamp type' - lamp_model = 'Filter Type Test' + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: - # WHEN: Filter model is received - pjlink.process_rlmp(data=lamp_model) + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.model_lamp = 'Old lamp type' - # THEN: Filter model number should be saved - self.assertNotEquals(pjlink.model_lamp, lamp_model, 'Lamp type should NOT have been saved') + # WHEN: Filter model is received + pjlink.process_rlmp(data=lamp_model) + + # THEN: Filter model number should be saved + assert pjlink.model_lamp != lamp_model, 'Lamp type should NOT have been saved' + mock_log.warning.assert_has_calls(log_warn_calls) def test_projector_process_snum_set(self): """ Test saving serial number from projector """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.serial_no = None + log_debug_calls = [call('(111.111.111.111) Setting projector serial number to "Test Serial Number"')] test_number = 'Test Serial Number' - # WHEN: No serial number is set and we receive serial number command - pjlink.process_snum(data=test_number) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: - # THEN: Serial number should be set - self.assertEqual(pjlink.serial_no, test_number, - 'Projector serial number should have been set') + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.serial_no = None + + # WHEN: No serial number is set and we receive serial number command + pjlink.process_snum(data=test_number) + + # THEN: Serial number should be set + assert pjlink.serial_no == test_number, 'Projector serial number should have been set' + mock_log.debug.assert_has_calls(log_debug_calls) def test_projector_process_snum_different(self): """ Test projector serial number different than saved serial number """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.serial_no = 'Previous serial number' + log_warn_calls = [call('(111.111.111.111) Projector serial number does not match saved serial number'), + call('(111.111.111.111) Saved: "Previous serial number"'), + call('(111.111.111.111) Received: "Test Serial Number"'), + call('(111.111.111.111) NOT saving serial number')] test_number = 'Test Serial Number' - # WHEN: No serial number is set and we receive serial number command - pjlink.process_snum(data=test_number) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.serial_no = 'Previous serial number' - # THEN: Serial number should be set - self.assertNotEquals(pjlink.serial_no, test_number, - 'Projector serial number should NOT have been set') + # WHEN: No serial number is set and we receive serial number command + pjlink.process_snum(data=test_number) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_sver(self, mock_log): + # THEN: Serial number should be set + assert pjlink.serial_no != test_number, 'Projector serial number should NOT have been set' + mock_log.warning.assert_has_calls(log_warn_calls) + + def test_projector_process_sver(self): """ Test invalid software version information - too long """ - # GIVEN: Test object - pjlink = pjlink_test - 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"' - mock_log.reset_mock() + log_debug_calls = [call('(111.111.111.111) Setting projector software version to "Test 1 Subtest 1"')] - # WHEN: process_sver called with invalid data - pjlink.process_sver(data=test_data) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.sw_version = None + pjlink.sw_version_received = None - # THEN: Version information should not change - self.assertEqual(pjlink.sw_version, test_data, 'Software version should have been updated') - self.assertIsNone(pjlink.sw_version_received, 'Received software version should not have changed') - mock_log.debug.assert_called_once_with(test_log) + # WHEN: process_sver called with invalid data + pjlink.process_sver(data=test_data) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_sver_changed(self, mock_log): + # THEN: Version information should not change + assert pjlink.sw_version == test_data, 'Software version should have been updated' + mock_log.debug.assert_has_calls(log_debug_calls) + + def test_projector_process_sver_changed(self): """ Test invalid software version information - Received different than saved """ - # GIVEN: Test object - pjlink = pjlink_test - test_data_new = 'Test 1 Subtest 2' test_data_old = 'Test 1 Subtest 1' - pjlink.sw_version = test_data_old - pjlink.sw_version_received = None - test_log = '(127.0.0.1) Saving new serial number as sw_version_received' - mock_log.reset_mock() + test_data_new = 'Test 1 Subtest 2' + log_warn_calls = [call('(111.111.111.111) Projector software version does not match saved software version'), + call('(111.111.111.111) Saved: "Test 1 Subtest 1"'), + call('(111.111.111.111) Received: "Test 1 Subtest 2"'), + call('(111.111.111.111) Updating software version')] - # WHEN: process_sver called with invalid data - pjlink.process_sver(data=test_data_new) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.sw_version = test_data_old - # THEN: Version information should not change - self.assertEqual(pjlink.sw_version, test_data_old, 'Software version should not have been updated') - self.assertEqual(pjlink.sw_version_received, test_data_new, - 'Received software version should have been changed') - self.assertEqual(mock_log.warning.call_count, 4, 'log.warn should have been called 4 times') - # There was 4 calls, but only the last one is checked with this method - mock_log.warning.assert_called_with(test_log) + # WHEN: process_sver called with invalid data + pjlink.process_sver(data=test_data_new) - @patch.object(openlp.core.projectors.pjlink, 'log') - def test_projector_process_sver_invalid(self, mock_log): + # THEN: Version information should not change + assert pjlink.sw_version == test_data_new, 'Software version should have changed' + mock_log.warning.assert_has_calls(log_warn_calls) + + def test_projector_process_sver_invalid(self): """ Test invalid software version information - too long """ - # GIVEN: Test object - pjlink = pjlink_test - pjlink.sw_version = None - pjlink.sw_version_received = None test_data = 'This is a test software version line that is too long based on PJLink version 2 specs' - test_log = "Invalid software version - too long" - mock_log.reset_mock() + log_warn_calls = [call('Invalid software version - too long')] - # WHEN: process_sver called with invalid data - pjlink.process_sver(data=test_data) + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + pjlink.sw_version = None - # THEN: Version information should not change - self.assertIsNone(pjlink.sw_version, 'Software version should not have changed') - self.assertIsNone(pjlink.sw_version_received, 'Received software version should not have changed') - mock_log.warning.assert_called_once_with(test_log) + # WHEN: process_sver called with invalid data + pjlink.process_sver(data=test_data) + + # THEN: Version information should not change + assert pjlink.sw_version is None, 'Software version should not have changed' + assert pjlink.sw_version_received is None, 'Received software version should not have changed' + mock_log.warning.assert_has_calls(log_warn_calls) def test_projector_reset_information(self): """ Test reset_information() resets all information and stops timers """ - # GIVEN: Test object and test data - pjlink = pjlink_test - pjlink.power = S_ON - pjlink.pjlink_name = 'OPENLPTEST' - pjlink.manufacturer = 'PJLINK' - pjlink.model = '1' - pjlink.shutter = True - pjlink.mute = True - pjlink.lamp = True - pjlink.fan = True - pjlink.source_available = True - pjlink.other_info = 'ANOTHER TEST' - pjlink.send_queue = True - pjlink.send_busy = True + log_debug_calls = [call('(111.111.111.111): Calling timer.stop()'), + call('(111.111.111.111): Calling socket_timer.stop()')] - # WHEN: reset_information() is called - with patch.object(pjlink, 'timer') as mock_timer: - with patch.object(pjlink, 'socket_timer') as mock_socket_timer: + # GIVEN: Test object + with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log: + pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True) + # timer and socket_timer not available until instantiation, so mock here + with patch.object(pjlink, 'socket_timer') as mock_socket_timer, \ + patch.object(pjlink, 'timer') as mock_timer: + + pjlink.power = S_ON + pjlink.pjlink_name = 'OPENLPTEST' + pjlink.manufacturer = 'PJLINK' + pjlink.model = '1' + pjlink.shutter = True + pjlink.mute = True + pjlink.lamp = True + pjlink.fan = True + pjlink.source_available = True + pjlink.other_info = 'ANOTHER TEST' + pjlink.send_queue = True + pjlink.send_busy = True + + # WHEN: reset_information() is called pjlink.reset_information() - # THEN: All information should be reset and timers stopped - self.assertEqual(pjlink.power, S_OFF, 'Projector power should be OFF') - self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None') - self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None') - self.assertIsNone(pjlink.model, 'Projector model should be None') - self.assertIsNone(pjlink.shutter, 'Projector shutter should be None') - self.assertIsNone(pjlink.mute, 'Projector shuttter should be None') - self.assertIsNone(pjlink.lamp, 'Projector lamp should be None') - self.assertIsNone(pjlink.fan, 'Projector fan should be None') - self.assertIsNone(pjlink.source_available, 'Projector source_available should be None') - self.assertIsNone(pjlink.source, 'Projector source should be None') - self.assertIsNone(pjlink.other_info, 'Projector other_info should be None') - self.assertEqual(pjlink.send_queue, [], 'Projector send_queue should be an empty list') - self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False') - self.assertTrue(mock_timer.stop.called, 'Projector timer.stop() should have been called') - self.assertTrue(mock_socket_timer.stop.called, 'Projector socket_timer.stop() should have been called') + # THEN: All information should be reset and timers stopped + assert pjlink.power == S_OFF, 'Projector power should be OFF' + assert pjlink.pjlink_name is None, 'Projector pjlink_name should be None' + assert pjlink.manufacturer is None, 'Projector manufacturer should be None' + assert pjlink.model is None, 'Projector model should be None' + assert pjlink.shutter is None, 'Projector shutter should be None' + assert pjlink.mute is None, 'Projector shuttter should be None' + assert pjlink.lamp is None, 'Projector lamp should be None' + assert pjlink.fan is None, 'Projector fan should be None' + assert pjlink.source_available is None, 'Projector source_available should be None' + assert pjlink.source is None, 'Projector source should be None' + assert pjlink.other_info is None, 'Projector other_info should be None' + assert pjlink.send_queue == [], 'Projector send_queue should be an empty list' + assert pjlink.send_busy is False, 'Projector send_busy should be False' + assert mock_timer.stop.called is True, 'Projector timer.stop() should have been called' + assert mock_socket_timer.stop.called is True, 'Projector socket_timer.stop() should have been called' + mock_log.debug.assert_has_calls(log_debug_calls)