Merge lp:~alisonken1/openlp/pjlink2-q into lp:openlp

Proposed by Ken Roberts
Status: Merged
Merged at revision: 2818
Proposed branch: lp:~alisonken1/openlp/pjlink2-q
Merge into: lp:openlp
Diff against target: 1482 lines (+437/-501)
8 files modified
openlp/core/projectors/constants.py (+53/-26)
openlp/core/projectors/editform.py (+42/-27)
openlp/core/projectors/manager.py (+3/-18)
openlp/core/projectors/pjlink.py (+158/-149)
tests/openlp_core/projectors/test_projector_db.py (+35/-2)
tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py (+21/-21)
tests/openlp_core/projectors/test_projector_pjlink_commands_02.py (+115/-4)
tests/openlp_core/projectors/test_projector_pjlink_udp.py (+10/-254)
To merge this branch: bzr merge lp:~alisonken1/openlp/pjlink2-q
Reviewer Review Type Date Requested Status
Tim Bentley Approve
Phill Approve
Review via email: mp+343669@code.launchpad.net

This proposal supersedes a proposal from 2018-04-20.

Commit message

PJLink2 update Q

Description of the change

PJLink2 update Q

--------------------------------------------------------------------------------

lp:~alisonken1/openlp/pjlink2-q (revision 2818)
https://ci.openlp.io/job/Branch-01-Pull/2506/ [SUCCESS]
https://ci.openlp.io/job/Branch-02a-Linux-Tests/2407/ [SUCCESS]
https://ci.openlp.io/job/Branch-02b-macOS-Tests/193/ [FAILURE]
https://ci.openlp.io/job/Branch-03a-Build-Source/105/ [SUCCESS]
https://ci.openlp.io/job/Branch-03b-Build-macOS/98/ [SUCCESS]
https://ci.openlp.io/job/Branch-04a-Code-Analysis/1567/ [SUCCESS]
https://ci.openlp.io/job/Branch-04b-Test-Coverage/1380/ [SUCCESS]
https://ci.openlp.io/job/Branch-05-AppVeyor-Tests/309/ [FAILURE]

----------------------------------------------------------------------------------

- Fix test_projector_db:TestProjectorDBUpdate segfault by
    creating self.main_window steps
- Pep8 on openlp/core/common/__init__.get_local_ip4()
- Collapse import from projector.constants from line-per-item to multi-items-per-line
- Change pjlink_functions to include class version for projector instance in command
- Set projector.editform to only allow editing IP address field for new entry only (used as db key entry)
- Collapse projector.manager imports from entry-per-line to compact import
- projector.pjlink:
    - Merge pjlink_functions_udp into pjlink_functions
    - Change pjlink_functions to add instance-specific version to commands
    - Fix command checks to changed pjlink_funcions
    - Update process_clss to update PJLink version in pjlink_functions
    - Update reset_information to update PJLink version in pjlink_functions
- renamed test_projectorsourceform.py to test_projector_sourceform.py
- renamed test_projectoreditform.py to test_projector_editform.py
- Fix projector tests
- Fix list creation in constants (remove unneeded comma)
- Rename editform ip_text_show label to ip_text_label
- Refactor editform IP address check
- Add TODO on commented code block for breaking incoming packet into parts

To post a comment you must log in.
Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Sorry will clash with one of my fixes which has been merged

See question

review: Needs Fixing
Revision history for this message
Ken Roberts (alisonken1) wrote : Posted in a previous version of this proposal

> Sorry will clash with one of my fixes which has been merged
>
> See question

>> 'ACKN': {'version': ['2', ],
>> + 'default': '2',

>Why do we have a missing slot?

2 new commands in PJLink are only valid for version 2 and have no version 1 equivalent.
Missing slot is to maintain compatibility with version checker.

>> - def process_clss(self, data):
>> + def process_clss(self, data, *args, **kwargs):

>why args
Some commands take host,port arguments but not all of them do.
Since there is a common entry point, need to keep availability of consuming extra args/kwargs without causing exception due to extra parameters.
(Yes, there should be no args passed only keyword args, but better safe than sorry)

Revision history for this message
Ken Roberts (alisonken1) wrote : Posted in a previous version of this proposal

which fix is clashing? I tried a merge from trunk and it merged fine.

Revision history for this message
Tim Bentley (trb143) wrote : Posted in a previous version of this proposal

Looks OK but projectors is not my strong point.

review: Approve
Revision history for this message
Phill (phill-ridout) wrote : Posted in a previous version of this proposal

A few issues / questions. See in line.

review: Needs Fixing
Revision history for this message
Phill (phill-ridout) wrote :

Looks good to me. Thanks!

review: Approve
Revision history for this message
Tim Bentley (trb143) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'openlp/core/projectors/constants.py'
2--- openlp/core/projectors/constants.py 2018-01-03 00:35:14 +0000
3+++ openlp/core/projectors/constants.py 2018-04-20 06:16:23 +0000
4@@ -154,110 +154,137 @@
5 S_INFO
6 ]
7
8-# NOTE: Changed format to account for some commands are both class 1 and 2
9+# NOTE: Changed format to account for some commands are both class 1 and 2.
10+# Make sure the sequence of 'version' is lowest-to-highest.
11 PJLINK_VALID_CMD = {
12- 'ACKN': {'version': ['2', ],
13+ 'ACKN': {'version': ['2'],
14+ 'default': '2',
15 'description': translate('OpenLP.PJLinkConstants',
16 'Acknowledge a PJLink SRCH command - returns MAC address.')
17 },
18- 'AVMT': {'version': ['1', ],
19+ 'AVMT': {'version': ['1'],
20+ 'default': '1',
21 'description': translate('OpenLP.PJLinkConstants',
22 'Blank/unblank video and/or mute audio.')
23 },
24- 'CLSS': {'version': ['1', ],
25+ 'CLSS': {'version': ['1'],
26+ 'default': '1',
27 'description': translate('OpenLP.PJLinkConstants',
28 'Query projector PJLink class support.')
29 },
30 'ERST': {'version': ['1', '2'],
31+ 'default': '1',
32 'description': translate('OpenLP.PJLinkConstants',
33 'Query error status from projector. '
34 'Returns fan/lamp/temp/cover/filter/other error status.')
35 },
36- 'FILT': {'version': ['2', ],
37+ 'FILT': {'version': ['2'],
38+ 'default': '1',
39 'description': translate('OpenLP.PJLinkConstants',
40 'Query number of hours on filter.')
41 },
42- 'FREZ': {'version': ['2', ],
43+ 'FREZ': {'version': ['2'],
44+ 'default': '1',
45 'description': translate('OpenLP.PJLinkConstants',
46 'Freeze or unfreeze current image being projected.')
47 },
48- 'INF1': {'version': ['1', ],
49+ 'INF1': {'version': ['1'],
50+ 'default': '1',
51 'description': translate('OpenLP.PJLinkConstants',
52 'Query projector manufacturer name.')
53 },
54- 'INF2': {'version': ['1', ],
55+ 'INF2': {'version': ['1'],
56+ 'default': '1',
57 'description': translate('OpenLP.PJLinkConstants',
58 'Query projector product name.')
59 },
60- 'INFO': {'version': ['1', ],
61+ 'INFO': {'version': ['1'],
62+ 'default': '1',
63 'description': translate('OpenLP.PJLinkConstants',
64 'Query projector for other information set by manufacturer.')
65 },
66- 'INNM': {'version': ['2', ],
67+ 'INNM': {'version': ['2'],
68+ 'default': '2',
69 'description': translate('OpenLP.PJLinkConstants',
70 'Query specified input source name')
71 },
72- 'INPT': {'version': ['1', ],
73+ 'INPT': {'version': ['1'],
74+ 'default': '1',
75 'description': translate('OpenLP.PJLinkConstants',
76 'Switch to specified video source.')
77 },
78- 'INST': {'version': ['1', ],
79+ 'INST': {'version': ['1'],
80+ 'default': '1',
81 'description': translate('OpenLP.PJLinkConstants',
82 'Query available input sources.')
83 },
84- 'IRES': {'version:': ['2', ],
85+ 'IRES': {'version': ['2'],
86+ 'default': '2',
87 'description': translate('OpenLP.PJLinkConstants',
88 'Query current input resolution.')
89 },
90- 'LAMP': {'version': ['1', ],
91+ 'LAMP': {'version': ['1'],
92+ 'default': '1',
93 'description': translate('OpenLP.PJLinkConstants',
94 'Query lamp time and on/off status. Multiple lamps supported.')
95 },
96- 'LKUP': {'version': ['2', ],
97+ 'LKUP': {'version': ['2'],
98+ 'default': '2',
99 'description': translate('OpenLP.PJLinkConstants',
100 'UDP Status - Projector is now available on network. Includes MAC address.')
101 },
102- 'MVOL': {'version': ['2', ],
103+ 'MVOL': {'version': ['2'],
104+ 'default': '1',
105 'description': translate('OpenLP.PJLinkConstants',
106 'Adjust microphone volume by 1 step.')
107 },
108- 'NAME': {'version': ['1', ],
109+ 'NAME': {'version': ['1'],
110+ 'default': '1',
111 'description': translate('OpenLP.PJLinkConstants',
112 'Query customer-set projector name.')
113 },
114- 'PJLINK': {'version': ['1', ],
115+ 'PJLINK': {'version': ['1'],
116+ 'default': '1',
117 'description': translate('OpenLP.PJLinkConstants',
118 'Initial connection with authentication/no authentication request.')
119 },
120- 'POWR': {'version': ['1', ],
121+ 'POWR': {'version': ['1'],
122+ 'default': '1',
123 'description': translate('OpenLP.PJLinkConstants',
124 'Turn lamp on or off/standby.')
125 },
126- 'RFIL': {'version': ['2', ],
127+ 'RFIL': {'version': ['2'],
128+ 'default': '2',
129 'description': translate('OpenLP.PJLinkConstants',
130 'Query replacement air filter model number.')
131 },
132- 'RLMP': {'version': ['2', ],
133+ 'RLMP': {'version': ['2'],
134+ 'default': '2',
135 'description': translate('OpenLP.PJLinkConstants',
136 'Query replacement lamp model number.')
137 },
138- 'RRES': {'version': ['2', ],
139+ 'RRES': {'version': ['2'],
140+ 'default': '2',
141 'description': translate('OpenLP.PJLinkConstants',
142 'Query recommended resolution.')
143 },
144- 'SNUM': {'version': ['2', ],
145+ 'SNUM': {'version': ['2'],
146+ 'default': '2',
147 'description': translate('OpenLP.PJLinkConstants',
148 'Query projector serial number.')
149 },
150- 'SRCH': {'version': ['2', ],
151+ 'SRCH': {'version': ['2'],
152+ 'default': '2',
153 'description': translate('OpenLP.PJLinkConstants',
154 'UDP broadcast search request for available projectors. Reply is ACKN.')
155 },
156- 'SVER': {'version': ['2', ],
157+ 'SVER': {'version': ['2'],
158+ 'default': '2',
159 'description': translate('OpenLP.PJLinkConstants',
160 'Query projector software version number.')
161 },
162- 'SVOL': {'version': ['2', ],
163+ 'SVOL': {'version': ['2'],
164+ 'default': '2',
165 'description': translate('OpenLP.PJLinkConstants',
166 'Adjust speaker volume by 1 step.')
167 }
168
169=== modified file 'openlp/core/projectors/editform.py'
170--- openlp/core/projectors/editform.py 2017-12-29 09:15:48 +0000
171+++ openlp/core/projectors/editform.py 2018-04-20 06:16:23 +0000
172@@ -58,10 +58,15 @@
173 # IP Address
174 self.ip_label = QtWidgets.QLabel(edit_projector_dialog)
175 self.ip_label.setObjectName('projector_edit_ip_label')
176- self.ip_text = QtWidgets.QLineEdit(edit_projector_dialog)
177- self.ip_text.setObjectName('projector_edit_ip_text')
178+ self.ip_text_edit = QtWidgets.QLineEdit(edit_projector_dialog)
179+ self.ip_text_edit.setObjectName('projector_edit_ip_text')
180+ self.ip_text_label = QtWidgets.QLabel(edit_projector_dialog)
181+ self.ip_text_label.setObjectName('projector_show_ip_text')
182 self.dialog_layout.addWidget(self.ip_label, 0, 0)
183- self.dialog_layout.addWidget(self.ip_text, 0, 1)
184+ # For new projector, use edit widget
185+ self.dialog_layout.addWidget(self.ip_text_edit, 0, 1)
186+ # For edit projector, use show widget
187+ self.dialog_layout.addWidget(self.ip_text_label, 0, 1)
188 # Port number
189 self.port_label = QtWidgets.QLabel(edit_projector_dialog)
190 self.port_label.setObjectName('projector_edit_ip_label')
191@@ -111,8 +116,8 @@
192 title = translate('OpenLP.ProjectorEditForm', 'Edit Projector')
193 edit_projector_dialog.setWindowTitle(title)
194 self.ip_label.setText(translate('OpenLP.ProjectorEditForm', 'IP Address'))
195- self.ip_text.setText(self.projector.ip)
196- self.ip_text.setFocus()
197+ self.ip_text_edit.setText(self.projector.ip)
198+ self.ip_text_label.setText(self.projector.ip)
199 self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number'))
200 self.port_text.setText(str(self.projector.port))
201 self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN'))
202@@ -131,7 +136,7 @@
203 Class to add or edit a projector entry in the database.
204
205 Fields that are editable:
206- ip = Column(String(100))
207+ ip = Column(String(100)) (Only edit for new projector)
208 port = Column(String(8))
209 pin = Column(String(20))
210 name = Column(String(20))
211@@ -154,9 +159,16 @@
212 if projector is None:
213 self.projector = Projector()
214 self.new_projector = True
215+ self.ip_text_edit.setVisible(True)
216+ self.ip_text_edit.setFocus()
217+ self.ip_text_label.setVisible(False)
218 else:
219 self.projector = projector
220 self.new_projector = False
221+ self.ip_text_edit.setVisible(False)
222+ self.ip_text_label.setVisible(True)
223+ # Since it's already defined, IP address is unchangeable, so focus on port number
224+ self.port_text.setFocus()
225 self.retranslateUi(self)
226 reply = QtWidgets.QDialog.exec(self)
227 return reply
228@@ -187,30 +199,32 @@
229 record=record.id)))
230 valid = False
231 return
232- adx = self.ip_text.text()
233- valid = verify_ip_address(adx)
234- if valid:
235- ip = self.projectordb.get_projector_by_ip(adx)
236- if ip is None:
237- valid = True
238- self.new_projector = True
239- elif ip.id != self.projector.id:
240+ if self.new_projector:
241+ # Only validate a new entry - otherwise it's been previously verified
242+ adx = self.ip_text_edit.text()
243+ valid = verify_ip_address(adx)
244+ if valid:
245+ # With a valid IP - check if it's already in database so we don't duplicate
246+ ip = self.projectordb.get_projector_by_ip(adx)
247+ if ip is None:
248+ valid = True
249+ self.new_projector = True
250+ elif ip.id != self.projector.id:
251+ QtWidgets.QMessageBox.warning(self,
252+ translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'),
253+ translate('OpenLP.ProjectorWizard',
254+ 'IP address "{ip}"<br />is already in the database '
255+ 'as ID {data}.<br /><br />Please Enter a different '
256+ 'IP address.'.format(ip=adx, data=ip.id)))
257+ return
258+ else:
259 QtWidgets.QMessageBox.warning(self,
260- translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'),
261+ translate('OpenLP.ProjectorWizard', 'Invalid IP Address'),
262 translate('OpenLP.ProjectorWizard',
263- 'IP address "{ip}"<br />is already in the database '
264- 'as ID {data}.<br /><br />Please Enter a different '
265- 'IP address.'.format(ip=adx, data=ip.id)))
266+ 'IP address "{ip}"<br>is not a valid IP address.'
267+ '<br /><br />Please enter a valid IP address.'.format(ip=adx)))
268 valid = False
269 return
270- else:
271- QtWidgets.QMessageBox.warning(self,
272- translate('OpenLP.ProjectorWizard', 'Invalid IP Address'),
273- translate('OpenLP.ProjectorWizard',
274- 'IP address "{ip}"<br>is not a valid IP address.'
275- '<br /><br />Please enter a valid IP address.'.format(ip=adx)))
276- valid = False
277- return
278 port = int(self.port_text.text())
279 if port < 1000 or port > 32767:
280 QtWidgets.QMessageBox.warning(self,
281@@ -223,7 +237,8 @@
282 'Default PJLink port is {port}'.format(port=PJLINK_PORT)))
283 valid = False
284 if valid:
285- self.projector.ip = self.ip_text.text()
286+ if self.new_projector:
287+ self.projector.ip = self.ip_text_edit.text()
288 self.projector.pin = self.pin_text.text()
289 self.projector.port = int(self.port_text.text())
290 self.projector.name = self.name_text.text()
291
292=== modified file 'openlp/core/projectors/manager.py'
293--- openlp/core/projectors/manager.py 2018-02-11 11:42:13 +0000
294+++ openlp/core/projectors/manager.py 2018-04-20 06:16:23 +0000
295@@ -35,24 +35,9 @@
296 from openlp.core.common.settings import Settings
297 from openlp.core.lib.ui import create_widget_action
298 from openlp.core.projectors import DialogSourceStyle
299-from openlp.core.projectors.constants import \
300- E_AUTHENTICATION, \
301- E_ERROR, \
302- E_NETWORK, \
303- E_NOT_CONNECTED, \
304- E_UNKNOWN_SOCKET_ERROR, \
305- S_CONNECTED, \
306- S_CONNECTING, \
307- S_COOLDOWN, \
308- S_INITIALIZE, \
309- S_NOT_CONNECTED, \
310- S_OFF, \
311- S_ON, \
312- S_STANDBY, \
313- S_WARMUP, \
314- STATUS_CODE, \
315- STATUS_MSG, \
316- QSOCKET_STATE
317+from openlp.core.projectors.constants import E_AUTHENTICATION, E_ERROR, E_NETWORK, E_NOT_CONNECTED, \
318+ E_UNKNOWN_SOCKET_ERROR, S_CONNECTED, S_CONNECTING, S_COOLDOWN, S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, \
319+ S_STANDBY, S_WARMUP, STATUS_CODE, STATUS_MSG, QSOCKET_STATE
320
321 from openlp.core.projectors.db import ProjectorDB
322 from openlp.core.projectors.editform import ProjectorEditForm
323
324=== modified file 'openlp/core/projectors/pjlink.py'
325--- openlp/core/projectors/pjlink.py 2018-02-11 11:42:13 +0000
326+++ openlp/core/projectors/pjlink.py 2018-04-20 06:16:23 +0000
327@@ -57,8 +57,7 @@
328 from openlp.core.projectors.constants import CONNECTION_ERRORS, PJLINK_CLASS, PJLINK_DEFAULT_CODES, PJLINK_ERRORS, \
329 PJLINK_ERST_DATA, PJLINK_ERST_STATUS, PJLINK_MAX_PACKET, PJLINK_PREFIX, PJLINK_PORT, PJLINK_POWR_STATUS, \
330 PJLINK_SUFFIX, PJLINK_VALID_CMD, PROJECTOR_STATE, STATUS_CODE, STATUS_MSG, QSOCKET_STATE, \
331- E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_INVALID_DATA, E_NETWORK, E_NOT_CONNECTED, \
332- E_SOCKET_TIMEOUT, \
333+ E_AUTHENTICATION, E_CONNECTION_REFUSED, E_GENERAL, E_NETWORK, E_NOT_CONNECTED, E_SOCKET_TIMEOUT, \
334 S_CONNECTED, S_CONNECTING, S_NOT_CONNECTED, S_OFF, S_OK, S_ON
335
336 log = logging.getLogger(__name__)
337@@ -93,22 +92,9 @@
338 self.projector_list = projector_list
339 self.port = port
340 # Local defines
341- self.ackn_list = {} # Replies from online projetors
342 self.search_active = False
343 self.search_time = 30000 # 30 seconds for allowed time
344 self.search_timer = QtCore.QTimer()
345- # New commands available in PJLink Class 2
346- # ACKN/SRCH is processed here since it's used to find available projectors
347- # Other commands are processed by the individual projector instances
348- self.pjlink_udp_functions = {
349- 'ACKN': self.process_ackn, # Class 2, command is 'SRCH'
350- 'ERST': None, # Class 1/2
351- 'INPT': None, # Class 1/2
352- 'LKUP': None, # Class 2 (reply only - no cmd)
353- 'POWR': None, # Class 1/2
354- 'SRCH': self.process_srch # Class 2 (reply is ACKN)
355- }
356-
357 self.readyRead.connect(self.get_datagram)
358 log.debug('(UDP) PJLinkUDP() Initialized')
359
360@@ -118,88 +104,26 @@
361 Retrieve packet and basic checks
362 """
363 log.debug('(UDP) get_datagram() - Receiving data')
364- read = self.pendingDatagramSize()
365- if read < 0:
366- log.warn('(UDP) No data (-1)')
367+ read_size = self.pendingDatagramSize()
368+ if read_size < 0:
369+ log.warning('(UDP) No data (-1)')
370 return
371- if read < 1:
372- log.warn('(UDP) get_datagram() called when pending data size is 0')
373+ if read_size < 1:
374+ log.warning('(UDP) get_datagram() called when pending data size is 0')
375 return
376 data, peer_address, peer_port = self.readDatagram(self.pendingDatagramSize())
377 log.debug('(UDP) {size} bytes received from {adx} on port {port}'.format(size=len(data),
378 adx=peer_address,
379 port=peer_port))
380 log.debug('(UDP) packet "{data}"'.format(data=data))
381- if len(data) < 0:
382- log.warn('(UDP) No data (-1)')
383- return
384- elif len(data) < 8:
385- # Minimum packet is '%2CCCC='
386- log.warn('(UDP) Invalid packet - not enough data')
387- return
388- elif data is None:
389- log.warn('(UDP) No data (None)')
390- return
391- elif len(data) > PJLINK_MAX_PACKET:
392- log.warn('(UDP) Invalid packet - length too long')
393- return
394- elif not data.startswith(PJLINK_PREFIX):
395- log.warn('(UDP) Invalid packet - does not start with PJLINK_PREFIX')
396- return
397- elif data[1] != '2':
398- log.warn('(UDP) Invalid packet - missing/invalid PJLink class version')
399- return
400- elif data[6] != '=':
401- log.warn('(UDP) Invalid packet - separator missing')
402- return
403- # First two characters are header information we don't need at this time
404- cmd, data = data[2:].split('=')
405- if cmd not in self.pjlink_udp_functions:
406- log.warn('(UDP) Invalid packet - not a valid PJLink UDP reply')
407- return
408- if self.pjlink_udp_functions[cmd] is not None:
409- log.debug('(UDP) Processing {cmd} with "{data}"'.format(cmd=cmd, data=data))
410- return self.pjlink_udp_functions[cmd](data=data, host=peer_address, port=peer_port)
411- else:
412- log.debug('(UDP) Checking projector list for ip {host} to process'.format(host=peer_address))
413- for projector in self.projector_list:
414- if peer_address == projector.ip:
415- if cmd not in projector.pjlink_functions:
416- log.error('(UDP) Could not find method to process '
417- '"{cmd}" in {host}'.format(cmd=cmd, host=projector.ip))
418- return
419- log.debug('(UDP) Calling "{cmd}" in {host}'.format(cmd=cmd, host=projector.ip))
420- return projector.pjlink_functions[cmd](data=data)
421- log.warn('(UDP) Could not find projector with ip {ip} to process packet'.format(ip=peer_address))
422- return
423-
424- def process_ackn(self, data, host, port):
425- """
426- Process the ACKN command.
427-
428- :param data: Data in packet
429- :param host: IP address of sending host
430- :param port: Port received on
431- """
432- log.debug('(UDP) Processing ACKN packet')
433- if host not in self.ackn_list:
434- log.debug('(UDP) Adding {host} to ACKN list'.format(host=host))
435- self.ackn_list[host] = {'data': data,
436- 'port': port}
437- else:
438- log.warn('(UDP) Host {host} already replied - ignoring'.format(host=host))
439-
440- def process_srch(self, data, host, port):
441- """
442- Process the SRCH command.
443-
444- SRCH is processed by terminals so we ignore any packet.
445-
446- :param data: Data in packet
447- :param host: IP address of sending host
448- :param port: Port received on
449- """
450- log.debug('(UDP) SRCH packet received - ignoring')
451+ # Send to appropriate instance to process packet
452+ log.debug('(UDP) Checking projector list for ip {host} to process'.format(host=peer_address))
453+ for projector in self.projector_list:
454+ if peer_address == projector.ip:
455+ # Dispatch packet to appropriate remote instance
456+ log.debug('(UDP) Dispatching packet to {host}'.format(host=projector.entry.name))
457+ return projector.get_data(buff=data, ip=peer_address, host=peer_address, port=peer_port)
458+ log.warning('(UDP) Could not find projector with ip {ip} to process packet'.format(ip=peer_address))
459 return
460
461 def search_start(self):
462@@ -224,6 +148,8 @@
463 """
464 Process replies from PJLink projector.
465 """
466+ # List of IP addresses and mac addresses found via UDP search command
467+ ackn_list = []
468
469 def __init__(self, *args, **kwargs):
470 """
471@@ -231,24 +157,47 @@
472 """
473 log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs))
474 super().__init__()
475- # Map PJLink command to method
476+ # Map PJLink command to method and include pjlink class version for this instance
477+ # Default initial pjlink class version is '1'
478 self.pjlink_functions = {
479- 'AVMT': self.process_avmt,
480- 'CLSS': self.process_clss,
481- 'ERST': self.process_erst,
482- 'INFO': self.process_info,
483- 'INF1': self.process_inf1,
484- 'INF2': self.process_inf2,
485- 'INPT': self.process_inpt,
486- 'INST': self.process_inst,
487- 'LAMP': self.process_lamp,
488- 'NAME': self.process_name,
489- 'PJLINK': self.process_pjlink,
490- 'POWR': self.process_powr,
491- 'SNUM': self.process_snum,
492- 'SVER': self.process_sver,
493- 'RFIL': self.process_rfil,
494- 'RLMP': self.process_rlmp
495+ 'ACKN': {"method": self.process_ackn, # Class 2 (command is SRCH)
496+ "version": "2"},
497+ 'AVMT': {"method": self.process_avmt,
498+ "version": "1"},
499+ 'CLSS': {"method": self.process_clss,
500+ "version": "1"},
501+ 'ERST': {"method": self.process_erst,
502+ "version": "1"},
503+ 'INFO': {"method": self.process_info,
504+ "version": "1"},
505+ 'INF1': {"method": self.process_inf1,
506+ "version": "1"},
507+ 'INF2': {"method": self.process_inf2,
508+ "version": "1"},
509+ 'INPT': {"method": self.process_inpt,
510+ "version": "1"},
511+ 'INST': {"method": self.process_inst,
512+ "version": "1"},
513+ 'LAMP': {"method": self.process_lamp,
514+ "version": "1"},
515+ 'LKUP': {"method": self.process_lkup, # Class 2 (reply only - no cmd)
516+ "version": "2"},
517+ 'NAME': {"method": self.process_name,
518+ "version": "1"},
519+ 'PJLINK': {"method": self.process_pjlink,
520+ "version": "1"},
521+ 'POWR': {"method": self.process_powr,
522+ "version": "1"},
523+ 'SNUM': {"method": self.process_snum,
524+ "version": "1"},
525+ 'SRCH': {"method": self.process_srch, # Class 2 (reply is ACKN)
526+ "version": "2"},
527+ 'SVER': {"method": self.process_sver,
528+ "version": "1"},
529+ 'RFIL': {"method": self.process_rfil,
530+ "version": "1"},
531+ 'RLMP': {"method": self.process_rlmp,
532+ "version": "1"}
533 }
534
535 def reset_information(self):
536@@ -287,8 +236,11 @@
537 self.send_busy = False
538 self.send_queue = []
539 self.priority_queue = []
540+ # Reset default version in command routing dict
541+ for cmd in self.pjlink_functions:
542+ self.pjlink_functions[cmd]["version"] = PJLINK_VALID_CMD[cmd]['default']
543
544- def process_command(self, cmd, data):
545+ def process_command(self, cmd, data, *args, **kwargs):
546 """
547 Verifies any return error code. Calls the appropriate command handler.
548
549@@ -320,9 +272,25 @@
550 return self.change_status(status=E_AUTHENTICATION)
551 # Command checks already passed
552 log.debug('({ip}) Calling function for {cmd}'.format(ip=self.entry.name, cmd=cmd))
553- self.pjlink_functions[cmd](data=data)
554-
555- def process_avmt(self, data):
556+ self.pjlink_functions[cmd]["method"](data=data, *args, **kwargs)
557+
558+ def process_ackn(self, data, host, port):
559+ """
560+ Process the ACKN command.
561+
562+ :param data: Data in packet
563+ :param host: IP address of sending host
564+ :param port: Port received on
565+ """
566+ log.debug('({ip}) Processing ACKN packet'.format(ip=self.entry.name))
567+ if host not in self.ackn_list:
568+ log.debug('({ip}) Adding {host} to ACKN list'.format(ip=self.entry.name, host=host))
569+ self.ackn_list[host] = {'data': data,
570+ 'port': port}
571+ else:
572+ log.warning('({ip}) Host {host} already replied - ignoring'.format(ip=self.entry.name, host=host))
573+
574+ def process_avmt(self, data, *args, **kwargs):
575 """
576 Process shutter and speaker status. See PJLink specification for format.
577 Update self.mute (audio) and self.shutter (video shutter).
578@@ -351,7 +319,7 @@
579 self.projectorUpdateIcons.emit()
580 return
581
582- def process_clss(self, data):
583+ def process_clss(self, data, *args, **kwargs):
584 """
585 PJLink class that this projector supports. See PJLink specification for format.
586 Updates self.class.
587@@ -367,12 +335,13 @@
588 # Due to stupid projectors not following standards (Optoma, BenQ comes to mind),
589 # AND the different responses that can be received, the semi-permanent way to
590 # fix the class reply is to just remove all non-digit characters.
591- try:
592- clss = re.findall('\d', data)[0] # Should only be the first match
593- except IndexError:
594+ chk = re.findall('\d', data)
595+ if len(chk) < 1:
596 log.error('({ip}) No numbers found in class version reply "{data}" - '
597 'defaulting to class "1"'.format(ip=self.entry.name, data=data))
598 clss = '1'
599+ else:
600+ clss = chk[0] # Should only be the first match
601 elif not data.isdigit():
602 log.error('({ip}) NAN CLSS version reply "{data}" - '
603 'defaulting to class "1"'.format(ip=self.entry.name, data=data))
604@@ -383,6 +352,11 @@
605 log.debug('({ip}) Setting pjlink_class for this projector '
606 'to "{data}"'.format(ip=self.entry.name,
607 data=self.pjlink_class))
608+ # Update method class versions
609+ for cmd in self.pjlink_functions:
610+ if self.pjlink_class in PJLINK_VALID_CMD[cmd]['version']:
611+ self.pjlink_functions[cmd]['version'] = self.pjlink_class
612+
613 # Since we call this one on first connect, setup polling from here
614 if not self.no_poll:
615 log.debug('({ip}) process_pjlink(): Starting timer'.format(ip=self.entry.name))
616@@ -391,7 +365,7 @@
617
618 return
619
620- def process_erst(self, data):
621+ def process_erst(self, data, *args, **kwargs):
622 """
623 Error status. See PJLink Specifications for format.
624 Updates self.projector_errors
625@@ -443,7 +417,7 @@
626 PJLINK_ERST_STATUS[other]
627 return
628
629- def process_inf1(self, data):
630+ def process_inf1(self, data, *args, **kwargs):
631 """
632 Manufacturer name set in projector.
633 Updates self.manufacturer
634@@ -455,7 +429,7 @@
635 data=self.manufacturer))
636 return
637
638- def process_inf2(self, data):
639+ def process_inf2(self, data, *args, **kwargs):
640 """
641 Projector Model set in projector.
642 Updates self.model.
643@@ -466,7 +440,7 @@
644 log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.entry.name, data=self.model))
645 return
646
647- def process_info(self, data):
648+ def process_info(self, data, *args, **kwargs):
649 """
650 Any extra info set in projector.
651 Updates self.other_info.
652@@ -477,7 +451,7 @@
653 log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.entry.name, data=self.other_info))
654 return
655
656- def process_inpt(self, data):
657+ def process_inpt(self, data, *args, **kwargs):
658 """
659 Current source input selected. See PJLink specification for format.
660 Update self.source
661@@ -499,7 +473,7 @@
662 log.debug('({ip}) Setting data source to "{data}"'.format(ip=self.entry.name, data=self.source))
663 return
664
665- def process_inst(self, data):
666+ def process_inst(self, data, *args, **kwargs):
667 """
668 Available source inputs. See PJLink specification for format.
669 Updates self.source_available
670@@ -516,7 +490,7 @@
671 data=self.source_available))
672 return
673
674- def process_lamp(self, data):
675+ def process_lamp(self, data, *args, **kwargs):
676 """
677 Lamp(s) status. See PJLink Specifications for format.
678 Data may have more than 1 lamp to process.
679@@ -542,7 +516,18 @@
680 self.lamp = lamps
681 return
682
683- def process_name(self, data):
684+ def process_lkup(self, data, host, port):
685+ """
686+ Process reply indicating remote is available for connection
687+
688+ :param data: Data packet from remote
689+ :param host: Remote IP address
690+ :param port: Local port packet received on
691+ """
692+ # TODO: Check if autoconnect is enabled and connect?
693+ pass
694+
695+ def process_name(self, data, *args, **kwargs):
696 """
697 Projector name set in projector.
698 Updates self.pjlink_name
699@@ -553,7 +538,7 @@
700 log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.entry.name, data=self.pjlink_name))
701 return
702
703- def process_pjlink(self, data):
704+ def process_pjlink(self, data, *args, **kwargs):
705 """
706 Process initial socket connection to terminal.
707
708@@ -594,7 +579,7 @@
709 # Since this is an initial connection, make it a priority just in case
710 return self.send_command(cmd="CLSS", salt=data_hash, priority=True)
711
712- def process_powr(self, data):
713+ def process_powr(self, data, *args, **kwargs):
714 """
715 Power status. See PJLink specification for format.
716 Update self.power with status. Update icons if change from previous setting.
717@@ -617,7 +602,7 @@
718 log.warning('({ip}) Unknown power response: "{data}"'.format(ip=self.entry.name, data=data))
719 return
720
721- def process_rfil(self, data):
722+ def process_rfil(self, data, *args, **kwargs):
723 """
724 Process replacement filter type
725 """
726@@ -628,7 +613,7 @@
727 log.warning('({ip}) Saved model: "{old}"'.format(ip=self.entry.name, old=self.model_filter))
728 log.warning('({ip}) New model: "{new}"'.format(ip=self.entry.name, new=data))
729
730- def process_rlmp(self, data):
731+ def process_rlmp(self, data, *args, **kwargs):
732 """
733 Process replacement lamp type
734 """
735@@ -639,7 +624,7 @@
736 log.warning('({ip}) Saved lamp: "{old}"'.format(ip=self.entry.name, old=self.model_lamp))
737 log.warning('({ip}) New lamp: "{new}"'.format(ip=self.entry.name, new=data))
738
739- def process_snum(self, data):
740+ def process_snum(self, data, *args, **kwargs):
741 """
742 Serial number of projector.
743
744@@ -659,7 +644,20 @@
745 log.warning('({ip}) NOT saving serial number'.format(ip=self.entry.name))
746 self.serial_no_received = data
747
748- def process_sver(self, data):
749+ def process_srch(self, data, host, port):
750+ """
751+ Process the SRCH command.
752+
753+ SRCH is processed by terminals so we ignore any packet.
754+
755+ :param data: Data in packet
756+ :param host: IP address of sending host
757+ :param port: Port received on
758+ """
759+ log.warning('(UDP) SRCH packet received from {host} - ignoring'.format(host=host))
760+ return
761+
762+ def process_sver(self, data, *args, **kwargs):
763 """
764 Software version of projector
765 """
766@@ -716,6 +714,7 @@
767 self.pin = self.entry.pin
768 self.port = self.entry.port
769 self.pjlink_class = PJLINK_CLASS if self.entry.pjlink_class is None else self.entry.pjlink_class
770+ self.ackn_list = {} # Replies from online projectors (Class 2 option)
771 self.db_update = False # Use to check if db needs to be updated prior to exiting
772 # Poll time 20 seconds unless called with something else
773 self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000
774@@ -916,7 +915,10 @@
775 """
776 Clean out extraneous stuff in the buffer.
777 """
778- log.warning('({ip}) {message}'.format(ip=self.entry.name, message='Invalid packet' if msg is None else msg))
779+ log.debug('({ip}) Cleaning buffer - msg = "{message}"'.format(ip=self.entry.name, message=msg))
780+ if msg is None:
781+ msg = 'Invalid packet'
782+ log.warning('({ip}) {message}'.format(ip=self.entry.name, message=msg))
783 self.send_busy = False
784 trash_count = 0
785 while self.bytesAvailable() > 0:
786@@ -960,7 +962,7 @@
787 self.socket_timer.stop()
788 return self.get_data(buff=read, ip=self.ip)
789
790- def get_data(self, buff, ip=None):
791+ def get_data(self, buff, ip=None, *args, **kwargs):
792 """
793 Process received data
794
795@@ -973,45 +975,61 @@
796 ip = self.ip
797 log.debug('({ip}) get_data(ip="{ip_in}" buffer="{buff}"'.format(ip=self.entry.name, ip_in=ip, buff=buff))
798 # NOTE: Class2 has changed to some values being UTF-8
799- data_in = decode(buff, 'utf-8')
800+ if isinstance(buff, bytes):
801+ data_in = decode(buff, 'utf-8')
802+ else:
803+ data_in = buff
804 data = data_in.strip()
805 # Initial packet checks
806 if (len(data) < 7):
807 self._trash_buffer(msg='get_data(): Invalid packet - length')
808 return self.receive_data_signal()
809 elif len(data) > self.max_size:
810- self._trash_buffer(msg='get_data(): Invalid packet - too long')
811+ self._trash_buffer(msg='get_data(): Invalid packet - too long ({length} bytes)'.format(length=len(data)))
812 return self.receive_data_signal()
813 elif not data.startswith(PJLINK_PREFIX):
814 self._trash_buffer(msg='get_data(): Invalid packet - PJLink prefix missing')
815 return self.receive_data_signal()
816- elif '=' not in data:
817+ elif data[6] != '=':
818 self._trash_buffer(msg='get_data(): Invalid reply - Does not have "="')
819 return self.receive_data_signal()
820 log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.entry.name, data=data))
821 header, data = data.split('=')
822+ log.debug('({ip}) get_data() header="{header}" data="{data}"'.format(ip=self.entry.name,
823+ header=header, data=data))
824 # At this point, the header should contain:
825 # "PVCCCC"
826 # Where:
827 # P = PJLINK_PREFIX
828 # V = PJLink class or version
829 # C = PJLink command
830+ version, cmd = header[1], header[2:].upper()
831+ log.debug('({ip}) get_data() version="{version}" cmd="{cmd}"'.format(ip=self.entry.name,
832+ version=version, cmd=cmd))
833+ # TODO: Below commented for now since it seems to cause issues with testing some invalid data.
834+ # Revisit after more refactoring is finished.
835+ '''
836 try:
837 version, cmd = header[1], header[2:].upper()
838+ log.debug('({ip}) get_data() version="{version}" cmd="{cmd}"'.format(ip=self.entry.name,
839+ version=version, cmd=cmd))
840 except ValueError as e:
841 self.change_status(E_INVALID_DATA)
842 log.warning('({ip}) get_data(): Received data: "{data}"'.format(ip=self.entry.name, data=data_in))
843 self._trash_buffer('get_data(): Expected header + command + data')
844 return self.receive_data_signal()
845+ '''
846 if cmd not in PJLINK_VALID_CMD:
847- log.warning('({ip}) get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.entry.name,
848+ self._trash_buffer('get_data(): Invalid packet - unknown command "{data}"'.format(ip=self.entry.name,
849 data=cmd))
850- self._trash_buffer(msg='get_data(): Unknown command "{data}"'.format(data=cmd))
851- return self.receive_data_signal()
852- if int(self.pjlink_class) < int(version):
853+ return self.receive_data_signal()
854+ elif version not in PJLINK_VALID_CMD[cmd]['version']:
855+ self._trash_buffer(msg='get_data() Command reply version does not match a valid command version')
856+ return self.receive_data_signal()
857+ elif int(self.pjlink_class) < int(version):
858 log.warning('({ip}) get_data(): Projector returned class reply higher '
859 'than projector stated class'.format(ip=self.entry.name))
860- self.process_command(cmd, data)
861+ self.process_command(cmd, data, *args, **kwargs)
862 return self.receive_data_signal()
863
864 @QtCore.pyqtSlot(QtNetwork.QAbstractSocket.SocketError)
865@@ -1063,16 +1081,7 @@
866 data=opts,
867 salt='' if salt is None
868 else ' with hash'))
869- cmd_ver = PJLINK_VALID_CMD[cmd]['version']
870- if self.pjlink_class in PJLINK_VALID_CMD[cmd]['version']:
871- header = PJLINK_HEADER.format(linkclass=self.pjlink_class)
872- elif len(cmd_ver) == 1 and (int(cmd_ver[0]) < int(self.pjlink_class)):
873- # Typically a class 1 only command
874- header = PJLINK_HEADER.format(linkclass=cmd_ver[0])
875- else:
876- # NOTE: Once we get to version 3 then think about looping
877- log.error('({ip}): send_command(): PJLink class check issue? Aborting'.format(ip=self.entry.name))
878- return
879+ header = PJLINK_HEADER.format(linkclass=self.pjlink_functions[cmd]["version"])
880 out = '{salt}{header}{command} {options}{suffix}'.format(salt="" if salt is None else salt,
881 header=header,
882 command=cmd,
883
884=== modified file 'tests/openlp_core/projectors/test_projector_db.py'
885--- tests/openlp_core/projectors/test_projector_db.py 2018-01-13 05:41:42 +0000
886+++ tests/openlp_core/projectors/test_projector_db.py 2018-04-20 06:16:23 +0000
887@@ -29,12 +29,15 @@
888 import shutil
889 from tempfile import mkdtemp
890 from unittest import TestCase
891-from unittest.mock import patch
892+from unittest.mock import MagicMock, patch
893
894+from openlp.core.common.registry import Registry
895 from openlp.core.lib.db import upgrade_db
896 from openlp.core.projectors import upgrade
897 from openlp.core.projectors.constants import PJLINK_PORT
898 from openlp.core.projectors.db import Manufacturer, Model, Projector, ProjectorDB, ProjectorSource, Source
899+from openlp.core.ui.mainwindow import MainWindow
900+from tests.helpers.testmixin import TestMixin
901 from tests.resources.projector.data import TEST_DB_PJLINK1, TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
902 from tests.utils.constants import TEST_RESOURCES_PATH
903
904@@ -122,7 +125,7 @@
905 assert updated_to_version == latest_version, 'The projector DB should have been upgrade to the latest version'
906
907
908-class TestProjectorDB(TestCase):
909+class TestProjectorDB(TestCase, TestMixin):
910 """
911 Test case for ProjectorDB
912 """
913@@ -131,6 +134,33 @@
914 """
915 Set up anything necessary for all tests
916 """
917+ # Create a test app to keep from segfaulting
918+ Registry.create()
919+ self.registry = Registry()
920+ self.setup_application()
921+ # Mock cursor busy/normal methods.
922+ self.app.set_busy_cursor = MagicMock()
923+ self.app.set_normal_cursor = MagicMock()
924+ self.app.args = []
925+ Registry().register('application', self.app)
926+ Registry().set_flag('no_web_server', True)
927+ # Mock classes and methods used by mainwindow.
928+ with patch('openlp.core.ui.mainwindow.SettingsForm'), \
929+ patch('openlp.core.ui.mainwindow.ImageManager'), \
930+ patch('openlp.core.ui.mainwindow.LiveController'), \
931+ patch('openlp.core.ui.mainwindow.PreviewController'), \
932+ patch('openlp.core.ui.mainwindow.OpenLPDockWidget'), \
933+ patch('openlp.core.ui.mainwindow.QtWidgets.QToolBox'), \
934+ patch('openlp.core.ui.mainwindow.QtWidgets.QMainWindow.addDockWidget'), \
935+ patch('openlp.core.ui.mainwindow.ServiceManager'), \
936+ patch('openlp.core.ui.mainwindow.ThemeManager'), \
937+ patch('openlp.core.ui.mainwindow.ProjectorManager'), \
938+ patch('openlp.core.ui.mainwindow.Renderer'), \
939+ patch('openlp.core.ui.mainwindow.websockets.WebSocketServer'), \
940+ patch('openlp.core.ui.mainwindow.server.HttpServer'):
941+ self.main_window = MainWindow()
942+
943+ # Create a temporary database directory and database
944 self.tmp_folder = mkdtemp(prefix='openlp_')
945 tmpdb_url = 'sqlite:///{db}'.format(db=os.path.join(self.tmp_folder, TEST_DB))
946 mocked_init_url.return_value = tmpdb_url
947@@ -139,9 +169,12 @@
948 def tearDown(self):
949 """
950 Clean up
951+
952+ Delete all the C++ objects at the end so that we don't have a segfault
953 """
954 self.projector.session.close()
955 self.projector = None
956+ del self.main_window
957 # Ignore errors since windows can have problems with locked files
958 shutil.rmtree(self.tmp_folder, ignore_errors=True)
959
960
961=== renamed file 'tests/openlp_core/projectors/test_projectoreditform.py' => 'tests/openlp_core/projectors/test_projector_editform.py'
962=== modified file 'tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py'
963--- tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py 2018-01-13 05:41:42 +0000
964+++ tests/openlp_core/projectors/test_projector_pjlink_cmd_routing.py 2018-04-20 06:16:23 +0000
965@@ -39,30 +39,31 @@
966 """
967 Tests for the PJLink module command routing
968 """
969- def test_get_data_unknown_command(self):
970+ @patch.object(openlp.core.projectors.pjlink, 'log')
971+ def test_get_data_unknown_command(self, mock_log):
972 """
973 Test not a valid command
974 """
975 # GIVEN: Test object
976- with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
977- patch.object(openlp.core.projectors.pjlink.PJLink, '_trash_buffer') as mock_buffer:
978-
979- pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
980- pjlink.pjlink_functions = MagicMock()
981- log_warning_text = [call('({ip}) get_data(): Invalid packet - '
982- 'unknown command "UNK"'.format(ip=pjlink.name))]
983- log_debug_text = [call('({ip}) get_data(ip="111.111.111.111" '
984- 'buffer="b\'%1UNK=Huh?\'"'.format(ip=pjlink.name)),
985- call('({ip}) get_data(): Checking new data "%1UNK=Huh?"'.format(ip=pjlink.name))]
986-
987- # WHEN: get_data called with an unknown command
988- pjlink.get_data(buff='{prefix}1UNK=Huh?'.format(prefix=PJLINK_PREFIX).encode('utf-8'))
989-
990- # THEN: Appropriate log entries should have been made and methods called/not called
991- mock_log.debug.assert_has_calls(log_debug_text)
992- mock_log.warning.assert_has_calls(log_warning_text)
993- assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions'
994- assert mock_buffer.called is True, 'Should have called _trash_buffer'
995+ pjlink = PJLink(Projector(**TEST1_DATA), no_poll=True)
996+ pjlink.pjlink_functions = MagicMock()
997+ log_warning_text = [call('({ip}) get_data(): Invalid packet - '
998+ 'unknown command "UNKN"'.format(ip=pjlink.name))]
999+ log_debug_text = [call('(___TEST_ONE___) get_data(ip="111.111.111.111" buffer="%1UNKN=Huh?"'),
1000+ call('(___TEST_ONE___) get_data(): Checking new data "%1UNKN=Huh?"'),
1001+ call('(___TEST_ONE___) get_data() header="%1UNKN" data="Huh?"'),
1002+ call('(___TEST_ONE___) get_data() version="1" cmd="UNKN"'),
1003+ call('(___TEST_ONE___) Cleaning buffer - msg = "get_data(): '
1004+ 'Invalid packet - unknown command "UNKN""'),
1005+ call('(___TEST_ONE___) Finished cleaning buffer - 0 bytes dropped')]
1006+
1007+ # WHEN: get_data called with an unknown command
1008+ pjlink.get_data(buff='{prefix}1UNKN=Huh?'.format(prefix=PJLINK_PREFIX))
1009+
1010+ # THEN: Appropriate log entries should have been made and methods called/not called
1011+ mock_log.warning.assert_has_calls(log_warning_text)
1012+ mock_log.debug.assert_has_calls(log_debug_text)
1013+ assert pjlink.pjlink_functions.called is False, 'Should not have accessed pjlink_functions'
1014
1015 def test_process_command_call_clss(self):
1016 """
1017@@ -219,7 +220,6 @@
1018 """
1019 Test command returned success
1020 """
1021- # GIVEN: Initial mocks and data
1022 # GIVEN: Test object and mocks
1023 with patch.object(openlp.core.projectors.pjlink, 'log') as mock_log, \
1024 patch.object(openlp.core.projectors.pjlink.PJLink, 'send_command') as mock_send_command, \
1025
1026=== modified file 'tests/openlp_core/projectors/test_projector_pjlink_commands_02.py'
1027--- tests/openlp_core/projectors/test_projector_pjlink_commands_02.py 2018-01-13 05:41:42 +0000
1028+++ tests/openlp_core/projectors/test_projector_pjlink_commands_02.py 2018-04-20 06:16:23 +0000
1029@@ -22,14 +22,14 @@
1030 """
1031 Package to test the openlp.core.projectors.pjlink commands package.
1032 """
1033-from unittest import TestCase
1034+from unittest import TestCase, skip
1035 from unittest.mock import patch, call
1036
1037 import openlp.core.projectors.pjlink
1038-from openlp.core.projectors.constants import S_CONNECTED, S_OFF, S_ON
1039+from openlp.core.projectors.constants import PJLINK_PORT, S_CONNECTED, S_OFF, S_ON
1040 from openlp.core.projectors.db import Projector
1041-from openlp.core.projectors.pjlink import PJLink
1042-from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT, TEST1_DATA
1043+from openlp.core.projectors.pjlink import PJLink, PJLinkUDP
1044+from tests.resources.projector.data import TEST_HASH, TEST_PIN, TEST_SALT, TEST1_DATA, TEST2_DATA
1045
1046
1047 class TestPJLinkCommands(TestCase):
1048@@ -235,3 +235,114 @@
1049 mock_log.error.assert_has_calls(log_check)
1050 assert 1 == mock_disconnect_from_host.call_count, 'Should have only been called once'
1051 mock_send_command.assert_not_called()
1052+
1053+ @skip('Change to pjlink_udp.get_datagram() call')
1054+ @patch.object(openlp.core.projectors.pjlink, 'log')
1055+ def test_process_ackn_duplicate(self, mock_log):
1056+ """
1057+ Test process_ackn method with multiple calls with same data
1058+ """
1059+ # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
1060+
1061+ # GIVEN: Test setup
1062+ pjlink = PJLink(projector=self.test_list[0])
1063+ check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
1064+ log_warn_calls = [call('(___TEST_ONE___) Host {host} already replied - '
1065+ 'ignoring'.format(host=TEST1_DATA['ip']))]
1066+ log_debug_calls = [call('PJlinkCommands(args=() kwargs={})'),
1067+ call('(___TEST_ONE___) reset_information() connect status is S_NOT_CONNECTED'),
1068+ call('(___TEST_ONE___) Processing ACKN packet'),
1069+ call('(___TEST_ONE___) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
1070+ call('(___TEST_ONE___) Processing ACKN packet')]
1071+
1072+ # WHEN: process_ackn called twice with same data
1073+ pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1074+ pjlink.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1075+
1076+ # THEN: pjlink_udp.ack_list should equal test_list
1077+ # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1078+ if pjlink.ackn_list != check_list:
1079+ # Check this way so we can print differences to stdout
1080+ print('\nackn_list: ', pjlink.ackn_list)
1081+ print('test_list: ', check_list, '\n')
1082+ assert pjlink.ackn_list == check_list
1083+ mock_log.debug.assert_has_calls(log_debug_calls)
1084+ mock_log.warning.assert_has_calls(log_warn_calls)
1085+
1086+ @skip('Change to pjlink_udp.get_datagram() call')
1087+ @patch.object(openlp.core.projectors.pjlink, 'log')
1088+ def test_process_ackn_multiple(self, mock_log):
1089+ """
1090+ Test process_ackn method with multiple calls
1091+ """
1092+ # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
1093+
1094+ # GIVEN: Test setup
1095+ pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1096+ check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT},
1097+ TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}}
1098+ log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1099+ call('(UDP) Processing ACKN packet'),
1100+ call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
1101+ call('(UDP) Processing ACKN packet'),
1102+ call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))]
1103+
1104+ # WHEN: process_ackn called twice with different data
1105+ pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1106+ pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT)
1107+
1108+ # THEN: pjlink_udp.ack_list should equal test_list
1109+ # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1110+ if pjlink_udp.ackn_list != check_list:
1111+ # Check this way so we can print differences to stdout
1112+ print('\nackn_list: ', pjlink_udp.ackn_list)
1113+ print('test_list: ', check_list)
1114+ assert pjlink_udp.ackn_list == check_list
1115+ mock_log.debug.assert_has_calls(log_debug_calls)
1116+
1117+ @skip('Change to pjlink_udp.get_datagram() call')
1118+ @patch.object(openlp.core.projectors.pjlink, 'log')
1119+ def test_process_ackn_single(self, mock_log):
1120+ """
1121+ Test process_ackn method with single call
1122+ """
1123+ # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
1124+
1125+ # GIVEN: Test setup
1126+ pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1127+ check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
1128+ log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1129+ call('(UDP) Processing ACKN packet'),
1130+ call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))]
1131+
1132+ # WHEN: process_ackn called twice with different data
1133+ pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1134+
1135+ # THEN: pjlink_udp.ack_list should equal test_list
1136+ # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1137+ if pjlink_udp.ackn_list != check_list:
1138+ # Check this way so we can print differences to stdout
1139+ print('\nackn_list: ', pjlink_udp.ackn_list)
1140+ print('test_list: ', check_list)
1141+ assert pjlink_udp.ackn_list == check_list
1142+ mock_log.debug.assert_has_calls(log_debug_calls)
1143+
1144+ @skip('Change to pjlink_udp.get_datagram() call')
1145+ @patch.object(openlp.core.projectors.pjlink, 'log')
1146+ def test_process_srch(self, mock_log):
1147+ """
1148+ Test process_srch method
1149+ """
1150+ # TODO: Change this to call pjlink_udp.get_datagram() so ACKN can be processed properly
1151+
1152+ # GIVEN: Test setup
1153+ log_warn_calls = [call('(UDP) SRCH packet received from {ip} - ignoring'.format(ip=TEST1_DATA['ip'])), ]
1154+ log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), ]
1155+ pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1156+
1157+ # WHEN: process_srch called
1158+ pjlink_udp.process_srch(data=None, host=TEST1_DATA['ip'], port=PJLINK_PORT)
1159+
1160+ # THEN: log entries should be entered
1161+ mock_log.warning.assert_has_calls(log_warn_calls)
1162+ mock_log.debug.assert_has_calls(log_debug_calls)
1163
1164=== modified file 'tests/openlp_core/projectors/test_projector_pjlink_udp.py'
1165--- tests/openlp_core/projectors/test_projector_pjlink_udp.py 2018-02-11 11:42:13 +0000
1166+++ tests/openlp_core/projectors/test_projector_pjlink_udp.py 2018-04-20 06:16:23 +0000
1167@@ -28,10 +28,10 @@
1168 from unittest.mock import call, patch
1169
1170 import openlp.core.projectors.pjlink
1171-from openlp.core.projectors.constants import PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_PREFIX
1172+from openlp.core.projectors.constants import PJLINK_PORT
1173
1174 from openlp.core.projectors.db import Projector
1175-from openlp.core.projectors.pjlink import PJLinkUDP
1176+from openlp.core.projectors.pjlink import PJLinkUDP, PJLink
1177 from tests.resources.projector.data import TEST1_DATA, TEST2_DATA
1178
1179
1180@@ -43,7 +43,8 @@
1181 """
1182 Setup generic test conditions
1183 """
1184- self.test_list = [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]
1185+ self.test_list = [PJLink(projector=Projector(**TEST1_DATA)),
1186+ PJLink(projector=Projector(**TEST2_DATA))]
1187
1188 def tearDown(self):
1189 """
1190@@ -52,132 +53,6 @@
1191 self.test_list = None
1192
1193 @patch.object(openlp.core.projectors.pjlink, 'log')
1194- def test_get_datagram_data_invalid_class(self, mock_log):
1195- """
1196- Test get_datagram with invalid class number
1197- """
1198- # GIVEN: Test setup
1199- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1200- log_warn_calls = [call('(UDP) Invalid packet - missing/invalid PJLink class version')]
1201- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1202- call('(UDP) get_datagram() - Receiving data'),
1203- call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'),
1204- call('(UDP) packet "%1ACKN=11:11:11:11:11:11"')]
1205- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1206- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1207- mock_datagram.return_value = 24
1208- mock_read.return_value = ('{prefix}1ACKN={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']),
1209- TEST1_DATA['ip'], PJLINK_PORT)
1210-
1211- # WHEN: get_datagram called with 0 bytes ready
1212- pjlink_udp.get_datagram()
1213-
1214- # THEN: Log entries should be made and method returns
1215- mock_log.debug.assert_has_calls(log_debug_calls)
1216- mock_log.warn.assert_has_calls(log_warn_calls)
1217-
1218- @patch.object(openlp.core.projectors.pjlink, 'log')
1219- def test_get_datagram_data_invalid_command(self, mock_log):
1220- """
1221- Test get_datagram with invalid PJLink UDP command
1222- """
1223- # GIVEN: Test setup
1224- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1225- log_warn_calls = [call('(UDP) Invalid packet - not a valid PJLink UDP reply')]
1226- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1227- call('(UDP) get_datagram() - Receiving data'),
1228- call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'),
1229- call('(UDP) packet "%2DUMB=11:11:11:11:11:11"')]
1230- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1231- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1232- mock_datagram.return_value = 24
1233- mock_read.return_value = ('{prefix}2DUMB={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']),
1234- TEST1_DATA['ip'], PJLINK_PORT)
1235-
1236- # WHEN: get_datagram called with 0 bytes ready
1237- pjlink_udp.get_datagram()
1238-
1239- # THEN: Log entries should be made and method returns
1240- mock_log.debug.assert_has_calls(log_debug_calls)
1241- mock_log.warn.assert_has_calls(log_warn_calls)
1242-
1243- @patch.object(openlp.core.projectors.pjlink, 'log')
1244- def test_get_datagram_data_invalid_prefix(self, mock_log):
1245- """
1246- Test get_datagram when prefix != PJLINK_PREFIX
1247- """
1248- # GIVEN: Test setup
1249- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1250- log_warn_calls = [call('(UDP) Invalid packet - does not start with PJLINK_PREFIX')]
1251- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1252- call('(UDP) get_datagram() - Receiving data'),
1253- call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'),
1254- call('(UDP) packet "$2ACKN=11:11:11:11:11:11"')]
1255- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1256- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1257- mock_datagram.return_value = 24
1258- mock_read.return_value = ('{prefix}2ACKN={mac}'.format(prefix='$', mac=TEST1_DATA['mac_adx']),
1259- TEST1_DATA['ip'], PJLINK_PORT)
1260-
1261- # WHEN: get_datagram called with 0 bytes ready
1262- pjlink_udp.get_datagram()
1263-
1264- # THEN: Log entries should be made and method returns
1265- mock_log.debug.assert_has_calls(log_debug_calls)
1266- mock_log.warn.assert_has_calls(log_warn_calls)
1267-
1268- @patch.object(openlp.core.projectors.pjlink, 'log')
1269- def test_get_datagram_data_invalid_separator(self, mock_log):
1270- """
1271- Test get_datagram when separator not equal to =
1272- """
1273- # GIVEN: Test setup
1274- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1275- log_warn_calls = [call('(UDP) Invalid packet - separator missing')]
1276- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1277- call('(UDP) get_datagram() - Receiving data'),
1278- call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'),
1279- call('(UDP) packet "%2ACKN 11:11:11:11:11:11"')]
1280- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1281- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1282- mock_datagram.return_value = 24
1283- mock_read.return_value = ('{prefix}2ACKN {mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']),
1284- TEST1_DATA['ip'], PJLINK_PORT)
1285-
1286- # WHEN: get_datagram called with 0 bytes ready
1287- pjlink_udp.get_datagram()
1288-
1289- # THEN: Log entries should be made and method returns
1290- mock_log.debug.assert_has_calls(log_debug_calls)
1291- mock_log.warn.assert_has_calls(log_warn_calls)
1292-
1293- @patch.object(openlp.core.projectors.pjlink, 'log')
1294- def test_get_datagram_data_long(self, mock_log):
1295- """
1296- Test get_datagram when datagram > PJLINK_MAX_PACKET
1297- """
1298- # GIVEN: Test setup
1299- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1300- log_warn_calls = [call('(UDP) Invalid packet - length too long')]
1301- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1302- call('(UDP) get_datagram() - Receiving data'),
1303- call('(UDP) 143 bytes received from 111.111.111.111 on port 4352'),
1304- call('(UDP) packet "%2ACKN={long}"'.format(long='X' * PJLINK_MAX_PACKET))]
1305- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1306- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1307- mock_datagram.return_value = PJLINK_MAX_PACKET + 7
1308- mock_read.return_value = ('{prefix}2ACKN={long}'.format(prefix=PJLINK_PREFIX,
1309- long='X' * PJLINK_MAX_PACKET),
1310- TEST1_DATA['ip'], PJLINK_PORT)
1311-
1312- # WHEN: get_datagram called with 0 bytes ready
1313- pjlink_udp.get_datagram()
1314-
1315- # THEN: Log entries should be made and method returns
1316- mock_log.debug.assert_has_calls(log_debug_calls)
1317- mock_log.warn.assert_has_calls(log_warn_calls)
1318-
1319- @patch.object(openlp.core.projectors.pjlink, 'log')
1320 def test_get_datagram_data_negative_zero_length(self, mock_log):
1321 """
1322 Test get_datagram when pendingDatagramSize = 0
1323@@ -196,7 +71,7 @@
1324 pjlink_udp.get_datagram()
1325
1326 # THEN: Log entries should be made and method returns
1327- mock_log.warn.assert_has_calls(log_warn_calls)
1328+ mock_log.warning.assert_has_calls(log_warn_calls)
1329 mock_log.debug.assert_has_calls(log_debug_calls)
1330
1331 @patch.object(openlp.core.projectors.pjlink, 'log')
1332@@ -206,41 +81,18 @@
1333 """
1334 # GIVEN: Test setup
1335 pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1336- log_warn_calls = [call('(UDP) Invalid packet - not enough data')]
1337- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1338- call('(UDP) get_datagram() - Receiving data')]
1339+ log_warn_calls = [call('(UDP) get_datagram() called when pending data size is 0')]
1340+ log_debug_calls = [call('(UDP) get_datagram() - Receiving data')]
1341 with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1342 patch.object(pjlink_udp, 'readDatagram') as mock_read:
1343- mock_datagram.return_value = 1
1344+ mock_datagram.return_value = 0
1345 mock_read.return_value = ('', TEST1_DATA['ip'], PJLINK_PORT)
1346
1347 # WHEN: get_datagram called with 0 bytes ready
1348 pjlink_udp.get_datagram()
1349
1350 # THEN: Log entries should be made and method returns
1351- mock_log.warn.assert_has_calls(log_warn_calls)
1352- mock_log.debug.assert_has_calls(log_debug_calls)
1353-
1354- @patch.object(openlp.core.projectors.pjlink, 'log')
1355- def test_get_datagram_data_short(self, mock_log):
1356- """
1357- Test get_datagram when data length < 8
1358- """
1359- # GIVEN: Test setup
1360- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1361- log_warn_calls = [call('(UDP) Invalid packet - not enough data')]
1362- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1363- call('(UDP) get_datagram() - Receiving data')]
1364- with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \
1365- patch.object(pjlink_udp, 'readDatagram') as mock_read:
1366- mock_datagram.return_value = 6
1367- mock_read.return_value = ('{prefix}2ACKN'.format(prefix=PJLINK_PREFIX), TEST1_DATA['ip'], PJLINK_PORT)
1368-
1369- # WHEN: get_datagram called with 0 bytes ready
1370- pjlink_udp.get_datagram()
1371-
1372- # THEN: Log entries should be made and method returns
1373- mock_log.warn.assert_has_calls(log_warn_calls)
1374+ mock_log.warning.assert_has_calls(log_warn_calls)
1375 mock_log.debug.assert_has_calls(log_debug_calls)
1376
1377 @patch.object(openlp.core.projectors.pjlink, 'log')
1378@@ -260,101 +112,5 @@
1379 pjlink_udp.get_datagram()
1380
1381 # THEN: Log entries should be made and method returns
1382- mock_log.warn.assert_has_calls(log_warn_calls)
1383+ mock_log.warning.assert_has_calls(log_warn_calls)
1384 mock_log.debug.assert_has_calls(log_debug_calls)
1385-
1386- @patch.object(openlp.core.projectors.pjlink, 'log')
1387- def test_process_ackn_duplicate(self, mock_log):
1388- """
1389- Test process_ackn method with multiple calls with same data
1390- """
1391- # GIVEN: Test setup
1392- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1393- check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
1394- log_warn_calls = [call('(UDP) Host {host} already replied - ignoring'.format(host=TEST1_DATA['ip']))]
1395- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1396- call('(UDP) Processing ACKN packet'),
1397- call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
1398- call('(UDP) Processing ACKN packet')]
1399-
1400- # WHEN: process_ackn called twice with same data
1401- pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1402- pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1403-
1404- # THEN: pjlink_udp.ack_list should equal test_list
1405- # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1406- if pjlink_udp.ackn_list != check_list:
1407- # Check this way so we can print differences to stdout
1408- print('\nackn_list: ', pjlink_udp.ackn_list)
1409- print('test_list: ', check_list)
1410- assert pjlink_udp.ackn_list == check_list
1411- mock_log.debug.assert_has_calls(log_debug_calls)
1412- mock_log.warn.assert_has_calls(log_warn_calls)
1413-
1414- @patch.object(openlp.core.projectors.pjlink, 'log')
1415- def test_process_ackn_multiple(self, mock_log):
1416- """
1417- Test process_ackn method with multiple calls
1418- """
1419- # GIVEN: Test setup
1420- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1421- check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT},
1422- TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}}
1423- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1424- call('(UDP) Processing ACKN packet'),
1425- call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])),
1426- call('(UDP) Processing ACKN packet'),
1427- call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))]
1428-
1429- # WHEN: process_ackn called twice with different data
1430- pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1431- pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT)
1432-
1433- # THEN: pjlink_udp.ack_list should equal test_list
1434- # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1435- if pjlink_udp.ackn_list != check_list:
1436- # Check this way so we can print differences to stdout
1437- print('\nackn_list: ', pjlink_udp.ackn_list)
1438- print('test_list: ', check_list)
1439- assert pjlink_udp.ackn_list == check_list
1440- mock_log.debug.assert_has_calls(log_debug_calls)
1441-
1442- @patch.object(openlp.core.projectors.pjlink, 'log')
1443- def test_process_ackn_single(self, mock_log):
1444- """
1445- Test process_ackn method with single call
1446- """
1447- # GIVEN: Test setup
1448- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1449- check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}}
1450- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1451- call('(UDP) Processing ACKN packet'),
1452- call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))]
1453-
1454- # WHEN: process_ackn called twice with different data
1455- pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT)
1456-
1457- # THEN: pjlink_udp.ack_list should equal test_list
1458- # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function?
1459- if pjlink_udp.ackn_list != check_list:
1460- # Check this way so we can print differences to stdout
1461- print('\nackn_list: ', pjlink_udp.ackn_list)
1462- print('test_list: ', check_list)
1463- assert pjlink_udp.ackn_list == check_list
1464- mock_log.debug.assert_has_calls(log_debug_calls)
1465-
1466- @patch.object(openlp.core.projectors.pjlink, 'log')
1467- def test_process_srch(self, mock_log):
1468- """
1469- Test process_srch method
1470- """
1471- # GIVEN: Test setup
1472- pjlink_udp = PJLinkUDP(projector_list=self.test_list)
1473- log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'),
1474- call('(UDP) SRCH packet received - ignoring')]
1475-
1476- # WHEN: process_srch called
1477- pjlink_udp.process_srch(data=None, host=None, port=None)
1478-
1479- # THEN: debug log entry should be entered
1480- mock_log.debug.assert_has_calls(log_debug_calls)
1481
1482=== renamed file 'tests/openlp_core/projectors/test_projectorsourceform.py' => 'tests/openlp_core/projectors/test_projector_sourceform.py'