Merge lp:~alisonken1/openlp/2.4-bug-1593883-projector-authentication into lp:openlp/2.4

Proposed by Ken Roberts
Status: Merged
Merged at revision: 2639
Proposed branch: lp:~alisonken1/openlp/2.4-bug-1593883-projector-authentication
Merge into: lp:openlp/2.4
Diff against target: 1082 lines (+452/-98)
5 files modified
openlp/core/common/__init__.py (+2/-1)
openlp/core/lib/projector/constants.py (+5/-1)
openlp/core/lib/projector/pjlink1.py (+125/-83)
tests/functional/openlp_core_common/test_projector_utilities.py (+3/-4)
tests/functional/openlp_core_lib/test_projector_pjlink1.py (+317/-9)
To merge this branch: bzr merge lp:~alisonken1/openlp/2.4-bug-1593883-projector-authentication
Reviewer Review Type Date Requested Status
Raoul Snyman Approve
Tomas Groth Approve
Review via email: mp+297820@code.launchpad.net

Commit message

Bugfix 1593882 and 1593883 - projector authorization backport

Description of the change

Bugfix 1593882 and 1593883 - projector authorization backport

- Fix exception when authenticated connection requested and pin is None
- Fix pjlink authentication (use python hash instead of qt hash)
- Fix md5_hash functions
- Fix qmd5_hash functions
- Added tests for bugfixes

--------------------------------
lp:~alisonken1/openlp/2.4-bug-1593883-projector-authentication (revision 2637)
[SUCCESS] https://ci.openlp.io/job/Branch-01-Pull/1630/
[SUCCESS] https://ci.openlp.io/job/Branch-02-Functional-Tests/1541/
[SUCCESS] https://ci.openlp.io/job/Branch-03-Interface-Tests/1479/
[SUCCESS] https://ci.openlp.io/job/Branch-04a-Windows_Functional_Tests/1248/
[SUCCESS] https://ci.openlp.io/job/Branch-04b-Windows_Interface_Tests/838/
[SUCCESS] https://ci.openlp.io/job/Branch-05a-Code_Analysis/906/
[SUCCESS] https://ci.openlp.io/job/Branch-05b-Test_Coverage/774/

To post a comment you must log in.
Revision history for this message
Tomas Groth (tomasgroth) wrote :

Given the problems we're seeing in trunk, I'm not too happy about all thw string-format changes... Are you sure they all work? Have they all been triggered/tested?

review: Needs Information
Revision history for this message
Ken Roberts (alisonken1) wrote :

Sorry about the string format changes - forgot I had changed that when I copied the file.

The projector code is pretty well exercised when I run it at church, so I believe I would have seen an issue if the formatting was incorrect.

Still adding tests as I make merge requests.

Revision history for this message
Raoul Snyman (raoul-snyman) wrote :

I agree with Tomas about testing the strings out. Even if it's just going through everything with your emulator. Either that or write some tests :-P

Revision history for this message
Ken Roberts (alisonken1) wrote :

I tested the trunk code at church with the Eiki XL200 projector. I just finished testing the 2.4 backport code using the emulator. All of the buttons, both the projector and input source edit wizards, and reviewed the log entries. The only thing I found was a minor issue with port validation (the network port field being empty) that will be included in a later fix.

Revision history for this message
Tomas Groth (tomasgroth) :
review: Approve
Revision history for this message
Raoul Snyman (raoul-snyman) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/common/__init__.py'
2--- openlp/core/common/__init__.py 2015-12-31 22:46:06 +0000
3+++ openlp/core/common/__init__.py 2016-06-18 03:24:30 +0000
4@@ -219,7 +219,8 @@
5 log.debug('qmd5_hash(salt="%s"' % salt)
6 hash_obj = QHash(QHash.Md5)
7 hash_obj.addData(salt)
8- hash_obj.addData(data)
9+ if data:
10+ hash_obj.addData(data)
11 hash_value = hash_obj.result().toHex()
12 log.debug('qmd5_hash() returning "%s"' % hash_value)
13 return hash_value.data()
14
15=== modified file 'openlp/core/lib/projector/constants.py'
16--- openlp/core/lib/projector/constants.py 2015-12-31 22:46:06 +0000
17+++ openlp/core/lib/projector/constants.py 2016-06-18 03:24:30 +0000
18@@ -297,7 +297,11 @@
19 PJLINK_POWR_STATUS = {'0': S_STANDBY,
20 '1': S_ON,
21 '2': S_COOLDOWN,
22- '3': S_WARMUP}
23+ '3': S_WARMUP,
24+ S_STANDBY: '0',
25+ S_ON: '1',
26+ S_COOLDOWN: '2',
27+ S_WARMUP: '3'}
28
29 PJLINK_DEFAULT_SOURCES = {'1': translate('OpenLP.DB', 'RGB'),
30 '2': translate('OpenLP.DB', 'Video'),
31
32=== modified file 'openlp/core/lib/projector/pjlink1.py'
33--- openlp/core/lib/projector/pjlink1.py 2016-03-03 17:15:58 +0000
34+++ openlp/core/lib/projector/pjlink1.py 2016-06-18 03:24:30 +0000
35@@ -49,7 +49,7 @@
36 from PyQt5.QtCore import pyqtSignal, pyqtSlot
37 from PyQt5.QtNetwork import QAbstractSocket, QTcpSocket
38
39-from openlp.core.common import translate, qmd5_hash
40+from openlp.core.common import translate, md5_hash
41 from openlp.core.lib.projector.constants import *
42
43 # Shortcuts
44@@ -58,7 +58,7 @@
45
46 PJLINK_PREFIX = '%'
47 PJLINK_CLASS = '1'
48-PJLINK_HEADER = '%s%s' % (PJLINK_PREFIX, PJLINK_CLASS)
49+PJLINK_HEADER = '{prefix}{linkclass}'.format(prefix=PJLINK_PREFIX, linkclass=PJLINK_CLASS)
50 PJLINK_SUFFIX = CR
51
52
53@@ -91,7 +91,7 @@
54 :param poll_time: Time (in seconds) to poll connected projector
55 :param socket_timeout: Time (in seconds) to abort the connection if no response
56 """
57- log.debug('PJlink(args="%s" kwargs="%s")' % (args, kwargs))
58+ log.debug('PJlink(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
59 self.name = name
60 self.ip = ip
61 self.port = port
62@@ -147,7 +147,7 @@
63 """
64 Reset projector-specific information to default
65 """
66- log.debug('(%s) reset_information() connect status is %s' % (self.ip, self.state()))
67+ log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state()))
68 self.power = S_OFF
69 self.pjlink_name = None
70 self.manufacturer = None
71@@ -160,8 +160,10 @@
72 self.source = None
73 self.other_info = None
74 if hasattr(self, 'timer'):
75+ log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip))
76 self.timer.stop()
77 if hasattr(self, 'socket_timer'):
78+ log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip))
79 self.socket_timer.stop()
80 self.send_queue = []
81 self.send_busy = False
82@@ -170,7 +172,7 @@
83 """
84 Connects signals to methods when thread is started.
85 """
86- log.debug('(%s) Thread starting' % self.ip)
87+ log.debug('({ip}) Thread starting'.format(ip=self.ip))
88 self.i_am_running = True
89 self.connected.connect(self.check_login)
90 self.disconnected.connect(self.disconnect_from_host)
91@@ -180,7 +182,7 @@
92 """
93 Cleanups when thread is stopped.
94 """
95- log.debug('(%s) Thread stopped' % self.ip)
96+ log.debug('({ip}) Thread stopped'.format(ip=self.ip))
97 try:
98 self.connected.disconnect(self.check_login)
99 except TypeError:
100@@ -206,7 +208,7 @@
101 Aborts connection and closes socket in case of brain-dead projectors.
102 Should normally be called by socket_timer().
103 """
104- log.debug('(%s) socket_abort() - Killing connection' % self.ip)
105+ log.debug('({ip}) socket_abort() - Killing connection'.format(ip=self.ip))
106 self.disconnect_from_host(abort=True)
107
108 def poll_loop(self):
109@@ -216,7 +218,7 @@
110 """
111 if self.state() != self.ConnectedState:
112 return
113- log.debug('(%s) Updating projector status' % self.ip)
114+ log.debug('({ip}) Updating projector status'.format(ip=self.ip))
115 # Reset timer in case we were called from a set command
116 if self.timer.interval() < self.poll_time:
117 # Reset timer to 5 seconds
118@@ -276,11 +278,17 @@
119 self.status_connect = S_CONNECTED
120 self.projector_status = status
121 (status_code, status_message) = self._get_status(self.status_connect)
122- log.debug('(%s) status_connect: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
123+ log.debug('({ip}) status_connect: {code}: "{message}"'.format(ip=self.ip,
124+ code=status_code,
125+ message=status_message if msg is None else msg))
126 (status_code, status_message) = self._get_status(self.projector_status)
127- log.debug('(%s) projector_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
128+ log.debug('({ip}) projector_status: {code}: "{message}"'.format(ip=self.ip,
129+ code=status_code,
130+ message=status_message if msg is None else msg))
131 (status_code, status_message) = self._get_status(self.error_status)
132- log.debug('(%s) error_status: %s: %s' % (self.ip, status_code, status_message if msg is None else msg))
133+ log.debug('({ip}) error_status: {code}: "{message}"'.format(ip=self.ip,
134+ code=status_code,
135+ message=status_message if msg is None else msg))
136 self.changeStatus.emit(self.ip, status, message)
137
138 @pyqtSlot()
139@@ -289,29 +297,31 @@
140 Processes the initial connection and authentication (if needed).
141 Starts poll timer if connection is established.
142
143+ NOTE: Qt md5 hash function doesn't work with projector authentication. Use the python md5 hash function.
144+
145 :param data: Optional data if called from another routine
146 """
147- log.debug('(%s) check_login(data="%s")' % (self.ip, data))
148+ log.debug('({ip}) check_login(data="{data}")'.format(ip=self.ip, data=data))
149 if data is None:
150 # Reconnected setup?
151 if not self.waitForReadyRead(2000):
152 # Possible timeout issue
153- log.error('(%s) Socket timeout waiting for login' % self.ip)
154+ log.error('({ip}) Socket timeout waiting for login'.format(ip=self.ip))
155 self.change_status(E_SOCKET_TIMEOUT)
156 return
157 read = self.readLine(self.maxSize)
158 dontcare = self.readLine(self.maxSize) # Clean out the trailing \r\n
159 if read is None:
160- log.warn('(%s) read is None - socket error?' % self.ip)
161+ log.warn('({ip}) read is None - socket error?'.format(ip=self.ip))
162 return
163 elif len(read) < 8:
164- log.warn('(%s) Not enough data read)' % self.ip)
165+ log.warn('({ip}) Not enough data read)'.format(ip=self.ip))
166 return
167 data = decode(read, 'ascii')
168 # Possibility of extraneous data on input when reading.
169 # Clean out extraneous characters in buffer.
170 dontcare = self.readLine(self.maxSize)
171- log.debug('(%s) check_login() read "%s"' % (self.ip, data.strip()))
172+ log.debug('({ip}) check_login() read "{data}"'.format(ip=self.ip, data=data.strip()))
173 # At this point, we should only have the initial login prompt with
174 # possible authentication
175 # PJLink initial login will be:
176@@ -326,26 +336,35 @@
177 else:
178 # Process initial connection
179 data_check = data.strip().split(' ')
180- log.debug('(%s) data_check="%s"' % (self.ip, data_check))
181+ log.debug('({ip}) data_check="{data}"'.format(ip=self.ip, data=data_check))
182 # Check for projector reporting an error
183 if data_check[1].upper() == 'ERRA':
184 # Authentication error
185 self.disconnect_from_host()
186 self.change_status(E_AUTHENTICATION)
187- log.debug('(%s) emitting projectorAuthentication() signal' % self.name)
188+ log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.name))
189 return
190 elif data_check[1] == '0' and self.pin is not None:
191 # Pin set and no authentication needed
192+ log.warning('({ip}) Regular connection but PIN set'.format(ip=self.name))
193 self.disconnect_from_host()
194 self.change_status(E_AUTHENTICATION)
195- log.debug('(%s) emitting projectorNoAuthentication() signal' % self.name)
196+ log.debug('({ip}) Emitting projectorNoAuthentication() signal'.format(ip=self.name))
197 self.projectorNoAuthentication.emit(self.name)
198 return
199 elif data_check[1] == '1':
200 # Authenticated login with salt
201- log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
202- log.debug('(%s) pin="%s"' % (self.ip, self.pin))
203- salt = qmd5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii'))
204+ if self.pin is None:
205+ log.warning('({ip}) Authenticated connection but no pin set'.format(ip=self.name))
206+ self.disconnect_from_host()
207+ self.change_status(E_AUTHENTICATION)
208+ log.debug('({ip}) Emitting projectorAuthentication() signal'.format(ip=self.name))
209+ self.projectorAuthentication.emit(self.name)
210+ return
211+ else:
212+ log.debug('({ip}) Setting hash with salt="{data}"'.format(ip=self.ip, data=data_check[2]))
213+ log.debug('({ip}) pin="{data}"'.format(ip=self.ip, data=self.pin))
214+ salt = md5_hash(salt=data_check[2].encode('ascii'), data=self.pin.encode('ascii'))
215 else:
216 salt = None
217 # We're connected at this point, so go ahead and do regular I/O
218@@ -355,7 +374,7 @@
219 self.send_command(cmd='CLSS', salt=salt)
220 self.waitForReadyRead()
221 if (not self.no_poll) and (self.state() == self.ConnectedState):
222- log.debug('(%s) Starting timer' % self.ip)
223+ log.debug('({ip}) Starting timer'.format(ip=self.ip))
224 self.timer.setInterval(2000) # Set 2 seconds for initial information
225 self.timer.start()
226
227@@ -364,15 +383,15 @@
228 """
229 Socket interface to retrieve data.
230 """
231- log.debug('(%s) get_data(): Reading data' % self.ip)
232+ log.debug('({ip}) get_data(): Reading data'.format(ip=self.ip))
233 if self.state() != self.ConnectedState:
234- log.debug('(%s) get_data(): Not connected - returning' % self.ip)
235+ log.debug('({ip}) get_data(): Not connected - returning'.format(ip=self.ip))
236 self.send_busy = False
237 return
238 read = self.readLine(self.maxSize)
239 if read == -1:
240 # No data available
241- log.debug('(%s) get_data(): No data available (-1)' % self.ip)
242+ log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip))
243 self.send_busy = False
244 self.projectorReceivedData.emit()
245 return
246@@ -382,11 +401,11 @@
247 data = data_in.strip()
248 if len(data) < 7:
249 # Not enough data for a packet
250- log.debug('(%s) get_data(): Packet length < 7: "%s"' % (self.ip, data))
251+ log.debug('({ip}) get_data(): Packet length < 7: "{data}"'.format(ip=self.ip, data=data))
252 self.send_busy = False
253 self.projectorReceivedData.emit()
254 return
255- log.debug('(%s) get_data(): Checking new data "%s"' % (self.ip, data))
256+ log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
257 if data.upper().startswith('PJLINK'):
258 # Reconnected from remote host disconnect ?
259 self.check_login(data)
260@@ -394,7 +413,7 @@
261 self.projectorReceivedData.emit()
262 return
263 elif '=' not in data:
264- log.warn('(%s) get_data(): Invalid packet received' % self.ip)
265+ log.warn('({ip}) get_data(): Invalid packet received'.format(ip=self.ip))
266 self.send_busy = False
267 self.projectorReceivedData.emit()
268 return
269@@ -402,15 +421,15 @@
270 try:
271 (prefix, class_, cmd, data) = (data_split[0][0], data_split[0][1], data_split[0][2:], data_split[1])
272 except ValueError as e:
273- log.warn('(%s) get_data(): Invalid packet - expected header + command + data' % self.ip)
274- log.warn('(%s) get_data(): Received data: "%s"' % (self.ip, read))
275+ log.warn('({ip}) get_data(): Invalid packet - expected header + command + data'.format(ip=self.ip))
276+ log.warn('({ip}) get_data(): Received data: "{data}"'.format(ip=self.ip, data=data_in.strip()))
277 self.change_status(E_INVALID_DATA)
278 self.send_busy = False
279 self.projectorReceivedData.emit()
280 return
281
282 if not (self.pjlink_class in PJLINK_VALID_CMD and cmd in PJLINK_VALID_CMD[self.pjlink_class]):
283- log.warn('(%s) get_data(): Invalid packet - unknown command "%s"' % (self.ip, cmd))
284+ log.warn('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.ip, data=cmd))
285 self.send_busy = False
286 self.projectorReceivedData.emit()
287 return
288@@ -424,7 +443,7 @@
289
290 :param err: Error code
291 """
292- log.debug('(%s) get_error(err=%s): %s' % (self.ip, err, self.errorString()))
293+ log.debug('({ip}) get_error(err={error}): {data}'.format(ip=self.ip, error=err, data=self.errorString()))
294 if err <= 18:
295 # QSocket errors. Redefined in projector.constants so we don't mistake
296 # them for system errors
297@@ -453,32 +472,35 @@
298 :param queue: Option to force add to queue rather than sending directly
299 """
300 if self.state() != self.ConnectedState:
301- log.warn('(%s) send_command(): Not connected - returning' % self.ip)
302+ log.warn('({ip}) send_command(): Not connected - returning'.format(ip=self.ip))
303 self.send_queue = []
304 return
305 self.projectorNetwork.emit(S_NETWORK_SENDING)
306- log.debug('(%s) send_command(): Building cmd="%s" opts="%s" %s' % (self.ip,
307- cmd,
308- opts,
309- '' if salt is None else 'with hash'))
310- if salt is None:
311- out = '%s%s %s%s' % (PJLINK_HEADER, cmd, opts, CR)
312- else:
313- out = '%s%s%s %s%s' % (salt, PJLINK_HEADER, cmd, opts, CR)
314+ log.debug('({ip}) send_command(): Building cmd="{command}" opts="{data}"{salt}'.format(ip=self.ip,
315+ command=cmd,
316+ data=opts,
317+ salt='' if salt is None
318+ else ' with hash'))
319+ out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt,
320+ header=PJLINK_HEADER,
321+ command=cmd,
322+ options=opts,
323+ suffix=CR)
324 if out in self.send_queue:
325 # Already there, so don't add
326- log.debug('(%s) send_command(out="%s") Already in queue - skipping' % (self.ip, out.strip()))
327+ log.debug('({ip}) send_command(out="{data}") Already in queue - skipping'.format(ip=self.ip,
328+ data=out.strip()))
329 elif not queue and len(self.send_queue) == 0:
330 # Nothing waiting to send, so just send it
331- log.debug('(%s) send_command(out="%s") Sending data' % (self.ip, out.strip()))
332+ log.debug('({ip}) send_command(out="{data}") Sending data'.format(ip=self.ip, data=out.strip()))
333 return self._send_command(data=out)
334 else:
335- log.debug('(%s) send_command(out="%s") adding to queue' % (self.ip, out.strip()))
336+ log.debug('({ip}) send_command(out="{data}") adding to queue'.format(ip=self.ip, data=out.strip()))
337 self.send_queue.append(out)
338 self.projectorReceivedData.emit()
339- log.debug('(%s) send_command(): send_busy is %s' % (self.ip, self.send_busy))
340+ log.debug('({ip}) send_command(): send_busy is {data}'.format(ip=self.ip, data=self.send_busy))
341 if not self.send_busy:
342- log.debug('(%s) send_command() calling _send_string()')
343+ log.debug('({ip}) send_command() calling _send_string()'.format(ip=self.ip))
344 self._send_command()
345
346 @pyqtSlot()
347@@ -488,10 +510,10 @@
348
349 :param data: Immediate data to send
350 """
351- log.debug('(%s) _send_string()' % self.ip)
352- log.debug('(%s) _send_string(): Connection status: %s' % (self.ip, self.state()))
353+ log.debug('({ip}) _send_string()'.format(ip=self.ip))
354+ log.debug('({ip}) _send_string(): Connection status: {data}'.format(ip=self.ip, data=self.state()))
355 if self.state() != self.ConnectedState:
356- log.debug('(%s) _send_string() Not connected - abort' % self.ip)
357+ log.debug('({ip}) _send_string() Not connected - abort'.format(ip=self.ip))
358 self.send_queue = []
359 self.send_busy = False
360 return
361@@ -500,30 +522,26 @@
362 return
363 if data is not None:
364 out = data
365- log.debug('(%s) _send_string(data=%s)' % (self.ip, out.strip()))
366+ log.debug('({ip}) _send_string(data="{data}")'.format(ip=self.ip, data=out.strip()))
367 elif len(self.send_queue) != 0:
368 out = self.send_queue.pop(0)
369- log.debug('(%s) _send_string(queued data=%s)' % (self.ip, out.strip()))
370+ log.debug('({ip}) _send_string(queued data="{data}"%s)'.format(ip=self.ip, data=out.strip()))
371 else:
372 # No data to send
373- log.debug('(%s) _send_string(): No data to send' % self.ip)
374+ log.debug('({ip}) _send_string(): No data to send'.format(ip=self.ip))
375 self.send_busy = False
376 return
377 self.send_busy = True
378- log.debug('(%s) _send_string(): Sending "%s"' % (self.ip, out.strip()))
379- log.debug('(%s) _send_string(): Queue = %s' % (self.ip, self.send_queue))
380+ log.debug('({ip}) _send_string(): Sending "{data}"'.format(ip=self.ip, data=out.strip()))
381+ log.debug('({ip}) _send_string(): Queue = {data}'.format(ip=self.ip, data=self.send_queue))
382 self.socket_timer.start()
383- try:
384- self.projectorNetwork.emit(S_NETWORK_SENDING)
385- sent = self.write(out.encode('ascii'))
386- self.waitForBytesWritten(2000) # 2 seconds should be enough
387- if sent == -1:
388- # Network error?
389- self.change_status(E_NETWORK,
390- translate('OpenLP.PJLink1', 'Error while sending data to projector'))
391- except SocketError as e:
392- self.disconnect_from_host(abort=True)
393- self.changeStatus(E_NETWORK, '%s : %s' % (e.error(), e.errorString()))
394+ self.projectorNetwork.emit(S_NETWORK_SENDING)
395+ sent = self.write(out.encode('ascii'))
396+ self.waitForBytesWritten(2000) # 2 seconds should be enough
397+ if sent == -1:
398+ # Network error?
399+ self.change_status(E_NETWORK,
400+ translate('OpenLP.PJLink1', 'Error while sending data to projector'))
401
402 def process_command(self, cmd, data):
403 """
404@@ -532,19 +550,21 @@
405 :param cmd: Command to process
406 :param data: Data being processed
407 """
408- log.debug('(%s) Processing command "%s"' % (self.ip, cmd))
409+ log.debug('({ip}) Processing command "{data}"'.format(ip=self.ip, data=cmd))
410 if data in PJLINK_ERRORS:
411 # Oops - projector error
412+ log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data))
413 if data.upper() == 'ERRA':
414 # Authentication error
415 self.disconnect_from_host()
416 self.change_status(E_AUTHENTICATION)
417- log.debug('(%s) emitting projectorAuthentication() signal' % self.ip)
418+ log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip))
419 self.projectorAuthentication.emit(self.name)
420 elif data.upper() == 'ERR1':
421 # Undefined command
422- self.change_status(E_UNDEFINED, '%s "%s"' %
423- (translate('OpenLP.PJLink1', 'Undefined command:'), cmd))
424+ self.change_status(E_UNDEFINED, '{error} "{data}"'.format(error=translate('OpenLP.PJLink1',
425+ 'Undefined command:'),
426+ data=cmd))
427 elif data.upper() == 'ERR2':
428 # Invalid parameter
429 self.change_status(E_PARAMETER)
430@@ -559,7 +579,7 @@
431 return
432 # Command succeeded - no extra information
433 elif data.upper() == 'OK':
434- log.debug('(%s) Command returned OK' % self.ip)
435+ log.debug('({ip}) Command returned OK'.format(ip=self.ip))
436 # A command returned successfully, recheck data
437 self.send_busy = False
438 self.projectorReceivedData.emit()
439@@ -568,7 +588,7 @@
440 if cmd in self.PJLINK1_FUNC:
441 self.PJLINK1_FUNC[cmd](data)
442 else:
443- log.warn('(%s) Invalid command %s' % (self.ip, cmd))
444+ log.warn('({ip}) Invalid command {data}'.format(ip=self.ip, data=cmd))
445 self.send_busy = False
446 self.projectorReceivedData.emit()
447
448@@ -587,7 +607,7 @@
449 fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True}
450 except ValueError:
451 # In case of invalid entry
452- log.warn('(%s) process_lamp(): Invalid data "%s"' % (self.ip, data))
453+ log.warn('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data))
454 return
455 lamps.append(fill)
456 data_dict.pop(0) # Remove lamp hours
457@@ -614,7 +634,7 @@
458 self.send_command('INST')
459 else:
460 # Log unknown status response
461- log.warn('Unknown power response: %s' % data)
462+ log.warn('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data))
463 return
464
465 def process_avmt(self, data):
466@@ -639,7 +659,7 @@
467 shutter = True
468 mute = True
469 else:
470- log.warn('Unknown shutter response: %s' % data)
471+ log.warn('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data))
472 update_icons = shutter != self.shutter
473 update_icons = update_icons or mute != self.mute
474 self.shutter = shutter
475@@ -656,6 +676,7 @@
476 :param data: Currently selected source
477 """
478 self.source = data
479+ log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source))
480 return
481
482 def process_clss(self, data):
483@@ -674,7 +695,8 @@
484 else:
485 clss = data
486 self.pjlink_class = clss
487- log.debug('(%s) Setting pjlink_class for this projector to "%s"' % (self.ip, self.pjlink_class))
488+ log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip,
489+ data=self.pjlink_class))
490 return
491
492 def process_name(self, data):
493@@ -685,6 +707,7 @@
494 :param data: Projector name
495 """
496 self.pjlink_name = data
497+ log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name))
498 return
499
500 def process_inf1(self, data):
501@@ -695,6 +718,7 @@
502 :param data: Projector manufacturer
503 """
504 self.manufacturer = data
505+ log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer))
506 return
507
508 def process_inf2(self, data):
509@@ -705,6 +729,7 @@
510 :param data: Model name
511 """
512 self.model = data
513+ log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model))
514 return
515
516 def process_info(self, data):
517@@ -715,6 +740,7 @@
518 :param data: Projector other info
519 """
520 self.other_info = data
521+ log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info))
522 return
523
524 def process_inst(self, data):
525@@ -731,6 +757,8 @@
526 sources.sort()
527 self.source_available = sources
528 self.projectorUpdateIcons.emit()
529+ log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip,
530+ data=self.source_available))
531 return
532
533 def process_erst(self, data):
534@@ -780,7 +808,7 @@
535 Initiate connection to projector.
536 """
537 if self.state() == self.ConnectedState:
538- log.warn('(%s) connect_to_host(): Already connected - returning' % self.ip)
539+ log.warn('({ip}) connect_to_host(): Already connected - returning'.format(ip=self.ip))
540 return
541 self.change_status(S_CONNECTING)
542 self.connectToHost(self.ip, self.port if type(self.port) is int else int(self.port))
543@@ -792,9 +820,9 @@
544 """
545 if abort or self.state() != self.ConnectedState:
546 if abort:
547- log.warn('(%s) disconnect_from_host(): Aborting connection' % self.ip)
548+ log.warn('({ip}) disconnect_from_host(): Aborting connection'.format(ip=self.ip))
549 else:
550- log.warn('(%s) disconnect_from_host(): Not connected - returning' % self.ip)
551+ log.warn('({ip}) disconnect_from_host(): Not connected - returning'.format(ip=self.ip))
552 self.reset_information()
553 self.disconnectFromHost()
554 try:
555@@ -804,8 +832,8 @@
556 if abort:
557 self.change_status(E_NOT_CONNECTED)
558 else:
559- log.debug('(%s) disconnect_from_host() Current status %s' % (self.ip,
560- self._get_status(self.status_connect)[0]))
561+ log.debug('({ip}) disconnect_from_host() '
562+ 'Current status {data}'.format(ip=self.ip, data=self._get_status(self.status_connect)[0]))
563 if self.status_connect != E_NOT_CONNECTED:
564 self.change_status(S_NOT_CONNECTED)
565 self.reset_information()
566@@ -815,60 +843,70 @@
567 """
568 Send command to retrieve available source inputs.
569 """
570+ log.debug('({ip}) Sending INST command'.format(ip=self.ip))
571 return self.send_command(cmd='INST')
572
573 def get_error_status(self):
574 """
575 Send command to retrieve currently known errors.
576 """
577+ log.debug('({ip}) Sending ERST command'.format(ip=self.ip))
578 return self.send_command(cmd='ERST')
579
580 def get_input_source(self):
581 """
582 Send command to retrieve currently selected source input.
583 """
584+ log.debug('({ip}) Sending INPT command'.format(ip=self.ip))
585 return self.send_command(cmd='INPT')
586
587 def get_lamp_status(self):
588 """
589 Send command to return the lap status.
590 """
591+ log.debug('({ip}) Sending LAMP command'.format(ip=self.ip))
592 return self.send_command(cmd='LAMP')
593
594 def get_manufacturer(self):
595 """
596 Send command to retrieve manufacturer name.
597 """
598+ log.debug('({ip}) Sending INF1 command'.format(ip=self.ip))
599 return self.send_command(cmd='INF1')
600
601 def get_model(self):
602 """
603 Send command to retrieve the model name.
604 """
605+ log.debug('({ip}) Sending INF2 command'.format(ip=self.ip))
606 return self.send_command(cmd='INF2')
607
608 def get_name(self):
609 """
610 Send command to retrieve name as set by end-user (if set).
611 """
612+ log.debug('({ip}) Sending NAME command'.format(ip=self.ip))
613 return self.send_command(cmd='NAME')
614
615 def get_other_info(self):
616 """
617 Send command to retrieve extra info set by manufacturer.
618 """
619+ log.debug('({ip}) Sending INFO command'.format(ip=self.ip))
620 return self.send_command(cmd='INFO')
621
622 def get_power_status(self):
623 """
624 Send command to retrieve power status.
625 """
626+ log.debug('({ip}) Sending POWR command'.format(ip=self.ip))
627 return self.send_command(cmd='POWR')
628
629 def get_shutter_status(self):
630 """
631 Send command to retrieve shutter status.
632 """
633+ log.debug('({ip}) Sending AVMT command'.format(ip=self.ip))
634 return self.send_command(cmd='AVMT')
635
636 def set_input_source(self, src=None):
637@@ -878,12 +916,12 @@
638
639 :param src: Video source to select in projector
640 """
641- log.debug('(%s) set_input_source(src=%s)' % (self.ip, src))
642+ log.debug('({ip}) set_input_source(src="{data}")'.format(ip=self.ip, data=src))
643 if self.source_available is None:
644 return
645 elif src not in self.source_available:
646 return
647- log.debug('(%s) Setting input source to %s' % (self.ip, src))
648+ log.debug('({ip}) Setting input source to "{data}"'.format(ip=self.ip, data=src))
649 self.send_command(cmd='INPT', opts=src)
650 self.poll_loop()
651
652@@ -891,6 +929,7 @@
653 """
654 Send command to turn power to on.
655 """
656+ log.debug('({ip}) Setting POWR to 1 (on)'.format(ip=self.ip))
657 self.send_command(cmd='POWR', opts='1')
658 self.poll_loop()
659
660@@ -898,6 +937,7 @@
661 """
662 Send command to turn power to standby.
663 """
664+ log.debug('({ip}) Setting POWR to 0 (standby)'.format(ip=self.ip))
665 self.send_command(cmd='POWR', opts='0')
666 self.poll_loop()
667
668@@ -905,6 +945,7 @@
669 """
670 Send command to set shutter to closed position.
671 """
672+ log.debug('({ip}) Setting AVMT to 11 (shutter closed)'.format(ip=self.ip))
673 self.send_command(cmd='AVMT', opts='11')
674 self.poll_loop()
675
676@@ -912,5 +953,6 @@
677 """
678 Send command to set shutter to open position.
679 """
680+ log.debug('({ip}) Setting AVMT to "10" (shutter open)'.format(ip=self.ip))
681 self.send_command(cmd='AVMT', opts='10')
682 self.poll_loop()
683
684=== modified file 'tests/functional/openlp_core_common/test_projector_utilities.py'
685--- tests/functional/openlp_core_common/test_projector_utilities.py 2016-01-09 17:21:20 +0000
686+++ tests/functional/openlp_core_common/test_projector_utilities.py 2016-06-18 03:24:30 +0000
687@@ -23,13 +23,12 @@
688 Package to test the openlp.core.ui.projector.networkutils package.
689 """
690
691-import os
692-
693 from unittest import TestCase
694
695 from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
696
697 from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_HASH
698+
699 salt = TEST_SALT
700 pin = TEST_PIN
701 test_hash = TEST_HASH
702@@ -148,7 +147,7 @@
703 hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))
704
705 # THEN: Validate return has is same
706- self.assertEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a good hash')
707+ self.assertEquals(hash_, test_hash.encode('ascii'), 'Qt-MD5 should have returned a good hash')
708
709 def test_qmd5_hash_bad(self):
710 """
711@@ -158,7 +157,7 @@
712 hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))
713
714 # THEN: return data is different
715- self.assertNotEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a bad hash')
716+ self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
717
718 def test_md5_non_ascii_string(self):
719 """
720
721=== modified file 'tests/functional/openlp_core_lib/test_projector_pjlink1.py'
722--- tests/functional/openlp_core_lib/test_projector_pjlink1.py 2016-03-03 17:15:58 +0000
723+++ tests/functional/openlp_core_lib/test_projector_pjlink1.py 2016-06-18 03:24:30 +0000
724@@ -26,13 +26,29 @@
725 from unittest import TestCase
726
727 from openlp.core.lib.projector.pjlink1 import PJLink1
728+from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_WARMUP, S_ON, \
729+ S_COOLDOWN, PJLINK_POWR_STATUS
730
731 from tests.functional import patch
732-from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE
733+from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH
734
735 pjlink_test = PJLink1(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True)
736
737
738+class DummyTimer(object):
739+ '''
740+ Dummy class to fake timers
741+ '''
742+ def __init__(self, *args, **kwargs):
743+ pass
744+
745+ def start(self, *args, **kwargs):
746+ pass
747+
748+ def stop(self, *args, **kwargs):
749+ pass
750+
751+
752 class TestPJLink(TestCase):
753 """
754 Tests for the PJLink module
755@@ -41,13 +57,10 @@
756 @patch.object(pjlink_test, 'send_command')
757 @patch.object(pjlink_test, 'waitForReadyRead')
758 @patch('openlp.core.common.qmd5_hash')
759- def authenticated_connection_call_test(self,
760- mock_qmd5_hash,
761- mock_waitForReadyRead,
762- mock_send_command,
763+ def test_authenticated_connection_call(self, mock_qmd5_hash, mock_waitForReadyRead, mock_send_command,
764 mock_readyRead):
765 """
766- Fix for projector connect with PJLink authentication exception. Ticket 92187.
767+ Ticket 92187: Fix for projector connect with PJLink authentication exception.
768 """
769 # GIVEN: Test object
770 pjlink = pjlink_test
771@@ -61,9 +74,23 @@
772 self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN,
773 "Connection request should have been called with TEST_PIN"))
774
775- def non_standard_class_reply_test(self):
776- """
777- bugfix 1550891 - CLSS request returns non-standard 'Class N' reply
778+ def test_projector_class(self):
779+ """
780+ Test class version from projector
781+ """
782+ # GIVEN: Test object
783+ pjlink = pjlink_test
784+
785+ # WHEN: Process class response
786+ pjlink.process_clss('1')
787+
788+ # THEN: Projector class should be set to 1
789+ self.assertEquals(pjlink.pjlink_class, '1',
790+ 'Projector should have returned class=1')
791+
792+ def test_non_standard_class_reply(self):
793+ """
794+ Bugfix 1550891: CLSS request returns non-standard 'Class N' reply
795 """
796 # GIVEN: Test object
797 pjlink = pjlink_test
798@@ -74,3 +101,284 @@
799 # THEN: Projector class should be set with proper value
800 self.assertEquals(pjlink.pjlink_class, '1',
801 'Non-standard class reply should have set proper class')
802+
803+ @patch.object(pjlink_test, 'change_status')
804+ def test_status_change(self, mock_change_status):
805+ """
806+ Test process_command call with ERR2 (Parameter) status
807+ """
808+ # GIVEN: Test object
809+ pjlink = pjlink_test
810+
811+ # WHEN: process_command is called with "ERR2" status from projector
812+ pjlink.process_command('POWR', 'ERR2')
813+
814+ # THEN: change_status should have called change_status with E_UNDEFINED
815+ # as first parameter
816+ mock_change_status.called_with(E_PARAMETER,
817+ 'change_status should have been called with "{}"'.format(
818+ ERROR_STRING[E_PARAMETER]))
819+
820+ @patch.object(pjlink_test, 'process_inpt')
821+ def test_projector_return_ok(self, mock_process_inpt):
822+ """
823+ Test projector calls process_inpt command when process_command is called with INPT option
824+ """
825+ # GIVEN: Test object
826+ pjlink = pjlink_test
827+
828+ # WHEN: process_command is called with INST command and 31 input:
829+ pjlink.process_command('INPT', '31')
830+
831+ # THEN: process_inpt method should have been called with 31
832+ mock_process_inpt.called_with('31',
833+ "process_inpt should have been called with 31")
834+
835+ @patch.object(pjlink_test, 'projectorReceivedData')
836+ def test_projector_process_lamp(self, mock_projectorReceivedData):
837+ """
838+ Test status lamp on/off and hours
839+ """
840+ # GIVEN: Test object
841+ pjlink = pjlink_test
842+
843+ # WHEN: Call process_command with lamp data
844+ pjlink.process_command('LAMP', '22222 1')
845+
846+ # THEN: Lamp should have been set with status=ON and hours=22222
847+ self.assertEquals(pjlink.lamp[0]['On'], True,
848+ 'Lamp power status should have been set to TRUE')
849+ self.assertEquals(pjlink.lamp[0]['Hours'], 22222,
850+ 'Lamp hours should have been set to 22222')
851+
852+ @patch.object(pjlink_test, 'projectorReceivedData')
853+ def test_projector_process_multiple_lamp(self, mock_projectorReceivedData):
854+ """
855+ Test status multiple lamp on/off and hours
856+ """
857+ # GIVEN: Test object
858+ pjlink = pjlink_test
859+
860+ # WHEN: Call process_command with lamp data
861+ pjlink.process_command('LAMP', '11111 1 22222 0 33333 1')
862+
863+ # THEN: Lamp should have been set with proper lamp status
864+ self.assertEquals(len(pjlink.lamp), 3,
865+ 'Projector should have 3 lamps specified')
866+ self.assertEquals(pjlink.lamp[0]['On'], True,
867+ 'Lamp 1 power status should have been set to TRUE')
868+ self.assertEquals(pjlink.lamp[0]['Hours'], 11111,
869+ 'Lamp 1 hours should have been set to 11111')
870+ self.assertEquals(pjlink.lamp[1]['On'], False,
871+ 'Lamp 2 power status should have been set to FALSE')
872+ self.assertEquals(pjlink.lamp[1]['Hours'], 22222,
873+ 'Lamp 2 hours should have been set to 22222')
874+ self.assertEquals(pjlink.lamp[2]['On'], True,
875+ 'Lamp 3 power status should have been set to TRUE')
876+ self.assertEquals(pjlink.lamp[2]['Hours'], 33333,
877+ 'Lamp 3 hours should have been set to 33333')
878+
879+ @patch.object(pjlink_test, 'projectorReceivedData')
880+ def test_projector_process_power_on(self, mock_projectorReceivedData):
881+ """
882+ Test status power to ON
883+ """
884+ # GIVEN: Test object and preset
885+ pjlink = pjlink_test
886+ pjlink.power = S_STANDBY
887+
888+ # WHEN: Call process_command with turn power on command
889+ pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON])
890+
891+ # THEN: Power should be set to ON
892+ self.assertEquals(pjlink.power, S_ON, 'Power should have been set to ON')
893+
894+ @patch.object(pjlink_test, 'projectorReceivedData')
895+ def test_projector_process_power_off(self, mock_projectorReceivedData):
896+ """
897+ Test status power to STANDBY
898+ """
899+ # GIVEN: Test object and preset
900+ pjlink = pjlink_test
901+ pjlink.power = S_ON
902+
903+ # WHEN: Call process_command with turn power on command
904+ pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY])
905+
906+ # THEN: Power should be set to STANDBY
907+ self.assertEquals(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY')
908+
909+ @patch.object(pjlink_test, 'projectorUpdateIcons')
910+ def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData):
911+ """
912+ Test avmt status shutter closed and audio muted
913+ """
914+ # GIVEN: Test object
915+ pjlink = pjlink_test
916+ pjlink.shutter = False
917+ pjlink.mute = True
918+
919+ # WHEN: Called with setting shutter closed and mute off
920+ pjlink.process_avmt('11')
921+
922+ # THEN: Shutter should be True and mute should be False
923+ self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
924+ self.assertFalse(pjlink.mute, 'Audio should be off')
925+
926+ @patch.object(pjlink_test, 'projectorUpdateIcons')
927+ def test_projector_process_avmt_open_muted(self, mock_projectorReceivedData):
928+ """
929+ Test avmt status shutter open and mute on
930+ """
931+ # GIVEN: Test object
932+ pjlink = pjlink_test
933+ pjlink.shutter = True
934+ pjlink.mute = False
935+
936+ # WHEN: Called with setting shutter closed and mute on
937+ pjlink.process_avmt('21')
938+
939+ # THEN: Shutter should be closed and mute should be True
940+ self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed')
941+ self.assertTrue(pjlink.mute, 'Audio should be off')
942+
943+ @patch.object(pjlink_test, 'projectorUpdateIcons')
944+ def test_projector_process_avmt_open_unmuted(self, mock_projectorReceivedData):
945+ """
946+ Test avmt status shutter open and mute off off
947+ """
948+ # GIVEN: Test object
949+ pjlink = pjlink_test
950+ pjlink.shutter = True
951+ pjlink.mute = True
952+
953+ # WHEN: Called with setting shutter to closed and mute on
954+ pjlink.process_avmt('30')
955+
956+ # THEN: Shutter should be closed and mute should be True
957+ self.assertFalse(pjlink.shutter, 'Shutter should have been set to open')
958+ self.assertFalse(pjlink.mute, 'Audio should be on')
959+
960+ @patch.object(pjlink_test, 'projectorUpdateIcons')
961+ def test_projector_process_avmt_closed_muted(self, mock_projectorReceivedData):
962+ """
963+ Test avmt status shutter closed and mute off
964+ """
965+ # GIVEN: Test object
966+ pjlink = pjlink_test
967+ pjlink.shutter = False
968+ pjlink.mute = False
969+
970+ # WHEN: Called with setting shutter to closed and mute on
971+ pjlink.process_avmt('31')
972+
973+ # THEN: Shutter should be closed and mute should be True
974+ self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed')
975+ self.assertTrue(pjlink.mute, 'Audio should be on')
976+
977+ def test_projector_process_input(self):
978+ """
979+ Test input source status shows current input
980+ """
981+ # GIVEN: Test object
982+ pjlink = pjlink_test
983+ pjlink.source = '0'
984+
985+ # WHEN: Called with input source
986+ pjlink.process_inpt('1')
987+
988+ # THEN: Input selected should reflect current input
989+ self.assertEquals(pjlink.source, '1', 'Input source should be set to "1"')
990+
991+ def test_projector_reset_information(self):
992+ """
993+ Test reset_information() resets all information and stops timers
994+ """
995+ # GIVEN: Test object and test data
996+ pjlink = pjlink_test
997+ pjlink.power = S_ON
998+ pjlink.pjlink_name = 'OPENLPTEST'
999+ pjlink.manufacturer = 'PJLINK'
1000+ pjlink.model = '1'
1001+ pjlink.shutter = True
1002+ pjlink.mute = True
1003+ pjlink.lamp = True
1004+ pjlink.fan = True
1005+ pjlink.source_available = True
1006+ pjlink.other_info = 'ANOTHER TEST'
1007+ pjlink.send_queue = True
1008+ pjlink.send_busy = True
1009+ pjlink.timer = DummyTimer()
1010+ pjlink.socket_timer = DummyTimer()
1011+
1012+ # WHEN: reset_information() is called
1013+ with patch.object(pjlink.timer, 'stop') as mock_timer:
1014+ with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer:
1015+ pjlink.reset_information()
1016+
1017+ # THEN: All information should be reset and timers stopped
1018+ self.assertEquals(pjlink.power, S_OFF, 'Projector power should be OFF')
1019+ self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None')
1020+ self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None')
1021+ self.assertIsNone(pjlink.model, 'Projector model should be None')
1022+ self.assertIsNone(pjlink.shutter, 'Projector shutter should be None')
1023+ self.assertIsNone(pjlink.mute, 'Projector shuttter should be None')
1024+ self.assertIsNone(pjlink.lamp, 'Projector lamp should be None')
1025+ self.assertIsNone(pjlink.fan, 'Projector fan should be None')
1026+ self.assertIsNone(pjlink.source_available, 'Projector source_available should be None')
1027+ self.assertIsNone(pjlink.source, 'Projector source should be None')
1028+ self.assertIsNone(pjlink.other_info, 'Projector other_info should be None')
1029+ self.assertEquals(pjlink.send_queue, [], 'Projector send_queue should be an empty list')
1030+ self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False')
1031+ self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called')
1032+ self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called')
1033+
1034+ @patch.object(pjlink_test, 'send_command')
1035+ @patch.object(pjlink_test, 'waitForReadyRead')
1036+ @patch.object(pjlink_test, 'projectorAuthentication')
1037+ @patch.object(pjlink_test, 'timer')
1038+ @patch.object(pjlink_test, 'socket_timer')
1039+ def test_bug_1593882_no_pin_authenticated_connection(self, mock_socket_timer,
1040+ mock_timer,
1041+ mock_authentication,
1042+ mock_ready_read,
1043+ mock_send_command):
1044+ """
1045+ Test bug 1593882 no pin and authenticated request exception
1046+ """
1047+ # GIVEN: Test object and mocks
1048+ pjlink = pjlink_test
1049+ pjlink.pin = None
1050+ mock_ready_read.return_value = True
1051+
1052+ # WHEN: call with authentication request and pin not set
1053+ pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
1054+
1055+ # THEN: No Authentication signal should have been sent
1056+ mock_authentication.emit.assert_called_with(pjlink.name)
1057+
1058+ @patch.object(pjlink_test, 'waitForReadyRead')
1059+ @patch.object(pjlink_test, 'state')
1060+ @patch.object(pjlink_test, '_send_command')
1061+ @patch.object(pjlink_test, 'timer')
1062+ @patch.object(pjlink_test, 'socket_timer')
1063+ def test_bug_1593883_pjlink_authentication(self, mock_socket_timer,
1064+ mock_timer,
1065+ mock_send_command,
1066+ mock_state,
1067+ mock_waitForReadyRead):
1068+ """
1069+ Test bugfix 1593883 pjlink authentication
1070+ """
1071+ # GIVEN: Test object and data
1072+ pjlink = pjlink_test
1073+ pjlink.pin = TEST_PIN
1074+ mock_state.return_value = pjlink.ConnectedState
1075+ mock_waitForReadyRead.return_value = True
1076+
1077+ # WHEN: Athenticated connection is called
1078+ pjlink.check_login(data=TEST_CONNECT_AUTHENTICATE)
1079+
1080+ # THEN: send_command should have the proper authentication
1081+ self.assertEquals("{test}".format(test=mock_send_command.call_args),
1082+ "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH))

Subscribers

People subscribed via source and target branches

to all changes: