Merge lp:~alisonken1/openlp/pjlink2-p into lp:openlp
- pjlink2-p
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~alisonken1/openlp/pjlink2-p |
Merge into: | lp:openlp |
Diff against target: |
826 lines (+572/-40) 10 files modified
openlp/core/api/tab.py (+6/-11) openlp/core/common/__init__.py (+22/-0) openlp/core/projectors/manager.py (+2/-5) openlp/core/projectors/pjlink.py (+143/-18) tests/functional/openlp_core/api/test_tab.py (+6/-0) tests/openlp_core/__init__.py (+26/-0) tests/openlp_core/projectors/__init__.py (+1/-1) tests/openlp_core/projectors/test_projector_pjlink_udp.py (+360/-0) tests/openlp_core/projectors/test_projectorsourceform.py (+0/-2) tests/resources/projector/data.py (+6/-3) |
To merge this branch: | bzr merge lp:~alisonken1/openlp/pjlink2-p |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Needs Fixing | ||
Review via email: mp+337504@code.launchpad.net |
This proposal has been superseded by a proposal from 2018-02-11.
Commit message
PJLink2 Update P
Description of the change
- manager: Remove unused signal disconnect projectorNetwor
- Change PJLinkUDP.
- Add test_projector_
- Add test_projector_
- Add test_projector_
- Add test_projector_
- Add PJLinkUDP.
- Add PJLinkUDP.
- Add PJLinkUDP.
- Add PJLinkUDP.
- Move projector tests to tests/openlp_
- Update resources.
-------
lp:~alisonken1/openlp/pjlink2-p (revision 2810)
https:/
https:/
https:/
https:/
https:/
https:/
https:/
https:/
Ken Roberts (alisonken1) wrote : | # |
>> === modified file 'openlp/.version'
>> --- openlp/.version 2016-12-12 22:16:23 +0000
>> +++ openlp/.version 2018-02-10 09:09:49 +0000
>> @@ -1 +1 @@
>> -2.5.0
>> +2.5-bzr2809
>
> why?
huh?
Didn't know that happened. Must have been when I was twiddling with
pip install trying something out.
Reverted
- 2810. By Ken Roberts
-
PJLink2 Update P
- 2811. By Ken Roberts
-
Move MY_IP4 dict to get_local_ip4 function
Unmerged revisions
Preview Diff
1 | === modified file 'openlp/core/api/tab.py' |
2 | --- openlp/core/api/tab.py 2017-12-29 09:15:48 +0000 |
3 | +++ openlp/core/api/tab.py 2018-02-11 11:42:51 +0000 |
4 | @@ -24,6 +24,7 @@ |
5 | """ |
6 | from PyQt5 import QtCore, QtGui, QtNetwork, QtWidgets |
7 | |
8 | +from openlp.core.common import MY_IP4 |
9 | from openlp.core.common.i18n import UiStrings, translate |
10 | from openlp.core.common.registry import Registry |
11 | from openlp.core.common.settings import Settings |
12 | @@ -219,17 +220,11 @@ |
13 | else: return ip_address |
14 | """ |
15 | if ip_address == ZERO_URL: |
16 | - interfaces = QtNetwork.QNetworkInterface.allInterfaces() |
17 | - for interface in interfaces: |
18 | - if not interface.isValid(): |
19 | - continue |
20 | - if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)): |
21 | - continue |
22 | - for address in interface.addressEntries(): |
23 | - ip = address.ip() |
24 | - if ip.protocol() == QtNetwork.QAbstractSocket.IPv4Protocol and \ |
25 | - ip != QtNetwork.QHostAddress.LocalHost: |
26 | - return ip.toString() |
27 | + # In case we have more than one interface |
28 | + for key in iter(MY_IP4): |
29 | + ip_address = MY_IP4.get(key)['ip'] |
30 | + # We only want the first interface returned |
31 | + break |
32 | return ip_address |
33 | |
34 | def load(self): |
35 | |
36 | === modified file 'openlp/core/common/__init__.py' |
37 | --- openlp/core/common/__init__.py 2017-12-29 09:15:48 +0000 |
38 | +++ openlp/core/common/__init__.py 2018-02-11 11:42:51 +0000 |
39 | @@ -36,6 +36,7 @@ |
40 | |
41 | from PyQt5 import QtGui |
42 | from PyQt5.QtCore import QCryptographicHash as QHash |
43 | +from PyQt5.QtNetwork import QAbstractSocket, QHostAddress, QNetworkInterface |
44 | from chardet.universaldetector import UniversalDetector |
45 | |
46 | log = logging.getLogger(__name__ + '.__init__') |
47 | @@ -51,6 +52,27 @@ |
48 | NEW_LINE_REGEX = re.compile(r' ?(\r\n?|\n) ?') |
49 | WHITESPACE_REGEX = re.compile(r'[ \t]+') |
50 | |
51 | +# Get the local IPv4 active address(es) that are NOT localhost (lo or '127.0.0.1') |
52 | +log.debug('Getting local IPv4 interface(es) information') |
53 | +MY_IP4 = {} |
54 | +for iface in QNetworkInterface.allInterfaces(): |
55 | + if not iface.isValid() or not (iface.flags() & (QNetworkInterface.IsUp | QNetworkInterface.IsRunning)): |
56 | + continue |
57 | + for address in iface.addressEntries(): |
58 | + ip = address.ip() |
59 | + if (ip.protocol() == QAbstractSocket.IPv4Protocol) and (ip != QHostAddress.LocalHost): |
60 | + MY_IP4[iface.name()] = {'ip': ip.toString(), |
61 | + 'broadcast': address.broadcast().toString(), |
62 | + 'netmask': address.netmask().toString(), |
63 | + 'prefix': address.prefixLength(), |
64 | + 'localnet': QHostAddress(address.netmask().toIPv4Address() & |
65 | + ip.toIPv4Address()).toString() |
66 | + } |
67 | + log.debug('Adding {iface} to active list'.format(iface=iface.name())) |
68 | + |
69 | +if not MY_IP4: |
70 | + log.warning('No active IPv4 interfaces found') |
71 | + |
72 | |
73 | def trace_error_handler(logger): |
74 | """ |
75 | |
76 | === modified file 'openlp/core/projectors/manager.py' |
77 | --- openlp/core/projectors/manager.py 2018-01-13 05:41:42 +0000 |
78 | +++ openlp/core/projectors/manager.py 2018-02-11 11:42:51 +0000 |
79 | @@ -308,7 +308,6 @@ |
80 | self.settings_section = 'projector' |
81 | self.projectordb = projectordb |
82 | self.projector_list = [] |
83 | - self.pjlink_udp = PJLinkUDP(self.projector_list) |
84 | self.source_select_form = None |
85 | |
86 | def bootstrap_initialise(self): |
87 | @@ -323,6 +322,7 @@ |
88 | else: |
89 | log.debug('Using existing ProjectorDB() instance') |
90 | self.get_settings() |
91 | + self.pjlink_udp = PJLinkUDP(self.projector_list) |
92 | |
93 | def bootstrap_post_set_up(self): |
94 | """ |
95 | @@ -344,6 +344,7 @@ |
96 | """ |
97 | Retrieve the saved settings |
98 | """ |
99 | + log.debug('Updating ProjectorManager settings') |
100 | settings = Settings() |
101 | settings.beginGroup(self.settings_section) |
102 | self.autostart = settings.value('connect on start') |
103 | @@ -502,10 +503,6 @@ |
104 | if ans == msg.Cancel: |
105 | return |
106 | try: |
107 | - projector.link.projectorNetwork.disconnect(self.update_status) |
108 | - except (AttributeError, TypeError): |
109 | - pass |
110 | - try: |
111 | projector.link.changeStatus.disconnect(self.update_status) |
112 | except (AttributeError, TypeError): |
113 | pass |
114 | |
115 | === modified file 'openlp/core/projectors/pjlink.py' |
116 | --- openlp/core/projectors/pjlink.py 2018-01-13 05:41:42 +0000 |
117 | +++ openlp/core/projectors/pjlink.py 2018-02-11 11:42:51 +0000 |
118 | @@ -64,7 +64,7 @@ |
119 | log = logging.getLogger(__name__) |
120 | log.debug('pjlink loaded') |
121 | |
122 | -__all__ = ['PJLink'] |
123 | +__all__ = ['PJLink', 'PJLinkUDP'] |
124 | |
125 | # Shortcuts |
126 | SocketError = QtNetwork.QAbstractSocket.SocketError |
127 | @@ -79,22 +79,145 @@ |
128 | """ |
129 | Socket service for PJLink UDP socket. |
130 | """ |
131 | - # New commands available in PJLink Class 2 |
132 | - pjlink_udp_commands = [ |
133 | - 'ACKN', # Class 2 (cmd is SRCH) |
134 | - 'ERST', # Class 1/2 |
135 | - 'INPT', # Class 1/2 |
136 | - 'LKUP', # Class 2 (reply only - no cmd) |
137 | - 'POWR', # Class 1/2 |
138 | - 'SRCH' # Class 2 (reply is ACKN) |
139 | - ] |
140 | - |
141 | def __init__(self, projector_list, port=PJLINK_PORT): |
142 | """ |
143 | - Initialize socket |
144 | + Socket services for PJLink UDP packets. |
145 | + |
146 | + Since all UDP packets from any projector will come into the same |
147 | + port, process UDP packets here then route to the appropriate |
148 | + projector instance as needed. |
149 | """ |
150 | + # Keep track of currently defined projectors so we can route |
151 | + # inbound packets to the correct instance |
152 | + super().__init__() |
153 | self.projector_list = projector_list |
154 | self.port = port |
155 | + # Local defines |
156 | + self.ackn_list = {} # Replies from online projetors |
157 | + self.search_active = False |
158 | + self.search_time = 30000 # 30 seconds for allowed time |
159 | + self.search_timer = QtCore.QTimer() |
160 | + # New commands available in PJLink Class 2 |
161 | + # ACKN/SRCH is processed here since it's used to find available projectors |
162 | + # Other commands are processed by the individual projector instances |
163 | + self.pjlink_udp_functions = { |
164 | + 'ACKN': self.process_ackn, # Class 2, command is 'SRCH' |
165 | + 'ERST': None, # Class 1/2 |
166 | + 'INPT': None, # Class 1/2 |
167 | + 'LKUP': None, # Class 2 (reply only - no cmd) |
168 | + 'POWR': None, # Class 1/2 |
169 | + 'SRCH': self.process_srch # Class 2 (reply is ACKN) |
170 | + } |
171 | + |
172 | + self.readyRead.connect(self.get_datagram) |
173 | + log.debug('(UDP) PJLinkUDP() Initialized') |
174 | + |
175 | + @QtCore.pyqtSlot() |
176 | + def get_datagram(self): |
177 | + """ |
178 | + Retrieve packet and basic checks |
179 | + """ |
180 | + log.debug('(UDP) get_datagram() - Receiving data') |
181 | + read = self.pendingDatagramSize() |
182 | + if read < 0: |
183 | + log.warn('(UDP) No data (-1)') |
184 | + return |
185 | + if read < 1: |
186 | + log.warn('(UDP) get_datagram() called when pending data size is 0') |
187 | + return |
188 | + data, peer_address, peer_port = self.readDatagram(self.pendingDatagramSize()) |
189 | + log.debug('(UDP) {size} bytes received from {adx} on port {port}'.format(size=len(data), |
190 | + adx=peer_address, |
191 | + port=peer_port)) |
192 | + log.debug('(UDP) packet "{data}"'.format(data=data)) |
193 | + if len(data) < 0: |
194 | + log.warn('(UDP) No data (-1)') |
195 | + return |
196 | + elif len(data) < 8: |
197 | + # Minimum packet is '%2CCCC=' |
198 | + log.warn('(UDP) Invalid packet - not enough data') |
199 | + return |
200 | + elif data is None: |
201 | + log.warn('(UDP) No data (None)') |
202 | + return |
203 | + elif len(data) > PJLINK_MAX_PACKET: |
204 | + log.warn('(UDP) Invalid packet - length too long') |
205 | + return |
206 | + elif not data.startswith(PJLINK_PREFIX): |
207 | + log.warn('(UDP) Invalid packet - does not start with PJLINK_PREFIX') |
208 | + return |
209 | + elif data[1] != '2': |
210 | + log.warn('(UDP) Invalid packet - missing/invalid PJLink class version') |
211 | + return |
212 | + elif data[6] != '=': |
213 | + log.warn('(UDP) Invalid packet - separator missing') |
214 | + return |
215 | + # First two characters are header information we don't need at this time |
216 | + cmd, data = data[2:].split('=') |
217 | + if cmd not in self.pjlink_udp_functions: |
218 | + log.warn('(UDP) Invalid packet - not a valid PJLink UDP reply') |
219 | + return |
220 | + if self.pjlink_udp_functions[cmd] is not None: |
221 | + log.debug('(UDP) Processing {cmd} with "{data}"'.format(cmd=cmd, data=data)) |
222 | + return self.pjlink_udp_functions[cmd](data=data, host=peer_address, port=peer_port) |
223 | + else: |
224 | + log.debug('(UDP) Checking projector list for ip {host} to process'.format(host=peer_address)) |
225 | + for projector in self.projector_list: |
226 | + if peer_address == projector.ip: |
227 | + if cmd not in projector.pjlink_functions: |
228 | + log.error('(UDP) Could not find method to process ' |
229 | + '"{cmd}" in {host}'.format(cmd=cmd, host=projector.ip)) |
230 | + return |
231 | + log.debug('(UDP) Calling "{cmd}" in {host}'.format(cmd=cmd, host=projector.ip)) |
232 | + return projector.pjlink_functions[cmd](data=data) |
233 | + log.warn('(UDP) Could not find projector with ip {ip} to process packet'.format(ip=peer_address)) |
234 | + return |
235 | + |
236 | + def process_ackn(self, data, host, port): |
237 | + """ |
238 | + Process the ACKN command. |
239 | + |
240 | + :param data: Data in packet |
241 | + :param host: IP address of sending host |
242 | + :param port: Port received on |
243 | + """ |
244 | + log.debug('(UDP) Processing ACKN packet') |
245 | + if host not in self.ackn_list: |
246 | + log.debug('(UDP) Adding {host} to ACKN list'.format(host=host)) |
247 | + self.ackn_list[host] = {'data': data, |
248 | + 'port': port} |
249 | + else: |
250 | + log.warn('(UDP) Host {host} already replied - ignoring'.format(host=host)) |
251 | + |
252 | + def process_srch(self, data, host, port): |
253 | + """ |
254 | + Process the SRCH command. |
255 | + |
256 | + SRCH is processed by terminals so we ignore any packet. |
257 | + |
258 | + :param data: Data in packet |
259 | + :param host: IP address of sending host |
260 | + :param port: Port received on |
261 | + """ |
262 | + log.debug('(UDP) SRCH packet received - ignoring') |
263 | + return |
264 | + |
265 | + def search_start(self): |
266 | + """ |
267 | + Start search for projectors on local network |
268 | + """ |
269 | + self.search_active = True |
270 | + self.ackn_list = {} |
271 | + # TODO: Send SRCH packet here |
272 | + self.search_timer.singleShot(self.search_time, self.search_stop) |
273 | + |
274 | + @QtCore.pyqtSlot() |
275 | + def search_stop(self): |
276 | + """ |
277 | + Stop search |
278 | + """ |
279 | + self.search_active = False |
280 | + self.search_timer.stop() |
281 | |
282 | |
283 | class PJLinkCommands(object): |
284 | @@ -257,8 +380,9 @@ |
285 | else: |
286 | clss = data |
287 | self.pjlink_class = clss |
288 | - log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.entry.name, |
289 | - data=self.pjlink_class)) |
290 | + log.debug('({ip}) Setting pjlink_class for this projector ' |
291 | + 'to "{data}"'.format(ip=self.entry.name, |
292 | + data=self.pjlink_class)) |
293 | # Since we call this one on first connect, setup polling from here |
294 | if not self.no_poll: |
295 | log.debug('({ip}) process_pjlink(): Starting timer'.format(ip=self.entry.name)) |
296 | @@ -276,9 +400,10 @@ |
297 | """ |
298 | if len(data) != PJLINK_ERST_DATA['DATA_LENGTH']: |
299 | count = PJLINK_ERST_DATA['DATA_LENGTH'] |
300 | - log.warning('({ip}) Invalid error status response "{data}": length != {count}'.format(ip=self.entry.name, |
301 | - data=data, |
302 | - count=count)) |
303 | + log.warning('({ip}) Invalid error status response "{data}": ' |
304 | + 'length != {count}'.format(ip=self.entry.name, |
305 | + data=data, |
306 | + count=count)) |
307 | return |
308 | try: |
309 | datacheck = int(data) |
310 | @@ -557,7 +682,7 @@ |
311 | |
312 | class PJLink(QtNetwork.QTcpSocket, PJLinkCommands): |
313 | """ |
314 | - Socket service for PJLink TCP socket. |
315 | + Socket services for PJLink TCP packets. |
316 | """ |
317 | # Signals sent by this module |
318 | changeStatus = QtCore.pyqtSignal(str, int, str) |
319 | |
320 | === modified file 'tests/functional/openlp_core/api/test_tab.py' |
321 | --- tests/functional/openlp_core/api/test_tab.py 2017-12-29 09:15:48 +0000 |
322 | +++ tests/functional/openlp_core/api/test_tab.py 2018-02-11 11:42:51 +0000 |
323 | @@ -29,6 +29,7 @@ |
324 | from PyQt5 import QtWidgets |
325 | |
326 | from openlp.core.api.tab import ApiTab |
327 | +from openlp.core.common import MY_IP4 |
328 | from openlp.core.common.registry import Registry |
329 | from openlp.core.common.settings import Settings |
330 | from tests.helpers.testmixin import TestMixin |
331 | @@ -72,15 +73,18 @@ |
332 | del self.form |
333 | self.destroy_settings() |
334 | |
335 | + @patch.dict(MY_IP4, {'test': {'ip': '127.0.0.1'}}, clear=True) |
336 | def test_get_ip_address_default(self): |
337 | """ |
338 | Test the get_ip_address function with ZERO_URL |
339 | """ |
340 | # WHEN: the default ip address is given |
341 | ip_address = self.form.get_ip_address(ZERO_URL) |
342 | + |
343 | # THEN: the default ip address will be returned |
344 | assert re.match('\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', ip_address), \ |
345 | 'The return value should be a valid ip address' |
346 | + assert ip_address == '127.0.0.1', 'The return address should match the test address' |
347 | |
348 | def test_get_ip_address_with_ip(self): |
349 | """ |
350 | @@ -88,8 +92,10 @@ |
351 | """ |
352 | # GIVEN: An ip address |
353 | given_ip = '192.168.1.1' |
354 | + |
355 | # WHEN: the default ip address is given |
356 | ip_address = self.form.get_ip_address(given_ip) |
357 | + |
358 | # THEN: the default ip address will be returned |
359 | assert ip_address == given_ip, 'The return value should be %s' % given_ip |
360 | |
361 | |
362 | === added directory 'tests/openlp_core' |
363 | === added file 'tests/openlp_core/__init__.py' |
364 | --- tests/openlp_core/__init__.py 1970-01-01 00:00:00 +0000 |
365 | +++ tests/openlp_core/__init__.py 2018-02-11 11:42:51 +0000 |
366 | @@ -0,0 +1,26 @@ |
367 | +# -*- coding: utf-8 -*- |
368 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
369 | + |
370 | +############################################################################### |
371 | +# OpenLP - Open Source Lyrics Projection # |
372 | +# --------------------------------------------------------------------------- # |
373 | +# Copyright (c) 2008-2018 OpenLP Developers # |
374 | +# --------------------------------------------------------------------------- # |
375 | +# This program is free software; you can redistribute it and/or modify it # |
376 | +# under the terms of the GNU General Public License as published by the Free # |
377 | +# Software Foundation; version 2 of the License. # |
378 | +# # |
379 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
380 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
381 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
382 | +# more details. # |
383 | +# # |
384 | +# You should have received a copy of the GNU General Public License along # |
385 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
386 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
387 | +############################################################################### |
388 | +""" |
389 | +:mod: `tests.openlp_core` module |
390 | + |
391 | +Tests modules/files for module openlp.core |
392 | +""" |
393 | |
394 | === renamed directory 'tests/functional/openlp_core/projectors' => 'tests/openlp_core/projectors' |
395 | === modified file 'tests/openlp_core/projectors/__init__.py' |
396 | --- tests/functional/openlp_core/projectors/__init__.py 2017-11-10 11:59:38 +0000 |
397 | +++ tests/openlp_core/projectors/__init__.py 2018-02-11 11:42:51 +0000 |
398 | @@ -20,5 +20,5 @@ |
399 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
400 | ############################################################################### |
401 | """ |
402 | -Module-level functions for the functional test suite |
403 | +Module-level functions for the projector test suite |
404 | """ |
405 | |
406 | === added file 'tests/openlp_core/projectors/test_projector_pjlink_udp.py' |
407 | --- tests/openlp_core/projectors/test_projector_pjlink_udp.py 1970-01-01 00:00:00 +0000 |
408 | +++ tests/openlp_core/projectors/test_projector_pjlink_udp.py 2018-02-11 11:42:51 +0000 |
409 | @@ -0,0 +1,360 @@ |
410 | + |
411 | +# -*- coding: utf-8 -*- |
412 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
413 | + |
414 | +############################################################################### |
415 | +# OpenLP - Open Source Lyrics Projection # |
416 | +# --------------------------------------------------------------------------- # |
417 | +# Copyright (c) 2008-2018 OpenLP Developers # |
418 | +# --------------------------------------------------------------------------- # |
419 | +# This program is free software; you can redistribute it and/or modify it # |
420 | +# under the terms of the GNU General Public License as published by the Free # |
421 | +# Software Foundation; version 2 of the License. # |
422 | +# # |
423 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
424 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
425 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
426 | +# more details. # |
427 | +# # |
428 | +# You should have received a copy of the GNU General Public License along # |
429 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
430 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
431 | +############################################################################### |
432 | +""" |
433 | +Package to test the PJLink UDP functions |
434 | +""" |
435 | + |
436 | +from unittest import TestCase |
437 | +from unittest.mock import call, patch |
438 | + |
439 | +import openlp.core.projectors.pjlink |
440 | +from openlp.core.projectors.constants import PJLINK_MAX_PACKET, PJLINK_PORT, PJLINK_PREFIX |
441 | + |
442 | +from openlp.core.projectors.db import Projector |
443 | +from openlp.core.projectors.pjlink import PJLinkUDP |
444 | +from tests.resources.projector.data import TEST1_DATA, TEST2_DATA |
445 | + |
446 | + |
447 | +class TestPJLinkBase(TestCase): |
448 | + """ |
449 | + Tests for the PJLinkUDP class |
450 | + """ |
451 | + def setUp(self): |
452 | + """ |
453 | + Setup generic test conditions |
454 | + """ |
455 | + self.test_list = [Projector(**TEST1_DATA), Projector(**TEST2_DATA)] |
456 | + |
457 | + def tearDown(self): |
458 | + """ |
459 | + Close generic test condidtions |
460 | + """ |
461 | + self.test_list = None |
462 | + |
463 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
464 | + def test_get_datagram_data_invalid_class(self, mock_log): |
465 | + """ |
466 | + Test get_datagram with invalid class number |
467 | + """ |
468 | + # GIVEN: Test setup |
469 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
470 | + log_warn_calls = [call('(UDP) Invalid packet - missing/invalid PJLink class version')] |
471 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
472 | + call('(UDP) get_datagram() - Receiving data'), |
473 | + call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), |
474 | + call('(UDP) packet "%1ACKN=11:11:11:11:11:11"')] |
475 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
476 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
477 | + mock_datagram.return_value = 24 |
478 | + mock_read.return_value = ('{prefix}1ACKN={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), |
479 | + TEST1_DATA['ip'], PJLINK_PORT) |
480 | + |
481 | + # WHEN: get_datagram called with 0 bytes ready |
482 | + pjlink_udp.get_datagram() |
483 | + |
484 | + # THEN: Log entries should be made and method returns |
485 | + mock_log.debug.assert_has_calls(log_debug_calls) |
486 | + mock_log.warn.assert_has_calls(log_warn_calls) |
487 | + |
488 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
489 | + def test_get_datagram_data_invalid_command(self, mock_log): |
490 | + """ |
491 | + Test get_datagram with invalid PJLink UDP command |
492 | + """ |
493 | + # GIVEN: Test setup |
494 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
495 | + log_warn_calls = [call('(UDP) Invalid packet - not a valid PJLink UDP reply')] |
496 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
497 | + call('(UDP) get_datagram() - Receiving data'), |
498 | + call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), |
499 | + call('(UDP) packet "%2DUMB=11:11:11:11:11:11"')] |
500 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
501 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
502 | + mock_datagram.return_value = 24 |
503 | + mock_read.return_value = ('{prefix}2DUMB={mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), |
504 | + TEST1_DATA['ip'], PJLINK_PORT) |
505 | + |
506 | + # WHEN: get_datagram called with 0 bytes ready |
507 | + pjlink_udp.get_datagram() |
508 | + |
509 | + # THEN: Log entries should be made and method returns |
510 | + mock_log.debug.assert_has_calls(log_debug_calls) |
511 | + mock_log.warn.assert_has_calls(log_warn_calls) |
512 | + |
513 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
514 | + def test_get_datagram_data_invalid_prefix(self, mock_log): |
515 | + """ |
516 | + Test get_datagram when prefix != PJLINK_PREFIX |
517 | + """ |
518 | + # GIVEN: Test setup |
519 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
520 | + log_warn_calls = [call('(UDP) Invalid packet - does not start with PJLINK_PREFIX')] |
521 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
522 | + call('(UDP) get_datagram() - Receiving data'), |
523 | + call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), |
524 | + call('(UDP) packet "$2ACKN=11:11:11:11:11:11"')] |
525 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
526 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
527 | + mock_datagram.return_value = 24 |
528 | + mock_read.return_value = ('{prefix}2ACKN={mac}'.format(prefix='$', mac=TEST1_DATA['mac_adx']), |
529 | + TEST1_DATA['ip'], PJLINK_PORT) |
530 | + |
531 | + # WHEN: get_datagram called with 0 bytes ready |
532 | + pjlink_udp.get_datagram() |
533 | + |
534 | + # THEN: Log entries should be made and method returns |
535 | + mock_log.debug.assert_has_calls(log_debug_calls) |
536 | + mock_log.warn.assert_has_calls(log_warn_calls) |
537 | + |
538 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
539 | + def test_get_datagram_data_invalid_separator(self, mock_log): |
540 | + """ |
541 | + Test get_datagram when separator not equal to = |
542 | + """ |
543 | + # GIVEN: Test setup |
544 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
545 | + log_warn_calls = [call('(UDP) Invalid packet - separator missing')] |
546 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
547 | + call('(UDP) get_datagram() - Receiving data'), |
548 | + call('(UDP) 24 bytes received from 111.111.111.111 on port 4352'), |
549 | + call('(UDP) packet "%2ACKN 11:11:11:11:11:11"')] |
550 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
551 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
552 | + mock_datagram.return_value = 24 |
553 | + mock_read.return_value = ('{prefix}2ACKN {mac}'.format(prefix=PJLINK_PREFIX, mac=TEST1_DATA['mac_adx']), |
554 | + TEST1_DATA['ip'], PJLINK_PORT) |
555 | + |
556 | + # WHEN: get_datagram called with 0 bytes ready |
557 | + pjlink_udp.get_datagram() |
558 | + |
559 | + # THEN: Log entries should be made and method returns |
560 | + mock_log.debug.assert_has_calls(log_debug_calls) |
561 | + mock_log.warn.assert_has_calls(log_warn_calls) |
562 | + |
563 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
564 | + def test_get_datagram_data_long(self, mock_log): |
565 | + """ |
566 | + Test get_datagram when datagram > PJLINK_MAX_PACKET |
567 | + """ |
568 | + # GIVEN: Test setup |
569 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
570 | + log_warn_calls = [call('(UDP) Invalid packet - length too long')] |
571 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
572 | + call('(UDP) get_datagram() - Receiving data'), |
573 | + call('(UDP) 143 bytes received from 111.111.111.111 on port 4352'), |
574 | + call('(UDP) packet "%2ACKN={long}"'.format(long='X' * PJLINK_MAX_PACKET))] |
575 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
576 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
577 | + mock_datagram.return_value = PJLINK_MAX_PACKET + 7 |
578 | + mock_read.return_value = ('{prefix}2ACKN={long}'.format(prefix=PJLINK_PREFIX, |
579 | + long='X' * PJLINK_MAX_PACKET), |
580 | + TEST1_DATA['ip'], PJLINK_PORT) |
581 | + |
582 | + # WHEN: get_datagram called with 0 bytes ready |
583 | + pjlink_udp.get_datagram() |
584 | + |
585 | + # THEN: Log entries should be made and method returns |
586 | + mock_log.debug.assert_has_calls(log_debug_calls) |
587 | + mock_log.warn.assert_has_calls(log_warn_calls) |
588 | + |
589 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
590 | + def test_get_datagram_data_negative_zero_length(self, mock_log): |
591 | + """ |
592 | + Test get_datagram when pendingDatagramSize = 0 |
593 | + """ |
594 | + # GIVEN: Test setup |
595 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
596 | + log_warn_calls = [call('(UDP) No data (-1)')] |
597 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
598 | + call('(UDP) get_datagram() - Receiving data')] |
599 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
600 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
601 | + mock_datagram.return_value = -1 |
602 | + mock_read.return_value = ('', TEST1_DATA['ip'], PJLINK_PORT) |
603 | + |
604 | + # WHEN: get_datagram called with 0 bytes ready |
605 | + pjlink_udp.get_datagram() |
606 | + |
607 | + # THEN: Log entries should be made and method returns |
608 | + mock_log.warn.assert_has_calls(log_warn_calls) |
609 | + mock_log.debug.assert_has_calls(log_debug_calls) |
610 | + |
611 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
612 | + def test_get_datagram_data_no_data(self, mock_log): |
613 | + """ |
614 | + Test get_datagram when data length = 0 |
615 | + """ |
616 | + # GIVEN: Test setup |
617 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
618 | + log_warn_calls = [call('(UDP) Invalid packet - not enough data')] |
619 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
620 | + call('(UDP) get_datagram() - Receiving data')] |
621 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
622 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
623 | + mock_datagram.return_value = 1 |
624 | + mock_read.return_value = ('', TEST1_DATA['ip'], PJLINK_PORT) |
625 | + |
626 | + # WHEN: get_datagram called with 0 bytes ready |
627 | + pjlink_udp.get_datagram() |
628 | + |
629 | + # THEN: Log entries should be made and method returns |
630 | + mock_log.warn.assert_has_calls(log_warn_calls) |
631 | + mock_log.debug.assert_has_calls(log_debug_calls) |
632 | + |
633 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
634 | + def test_get_datagram_data_short(self, mock_log): |
635 | + """ |
636 | + Test get_datagram when data length < 8 |
637 | + """ |
638 | + # GIVEN: Test setup |
639 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
640 | + log_warn_calls = [call('(UDP) Invalid packet - not enough data')] |
641 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
642 | + call('(UDP) get_datagram() - Receiving data')] |
643 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram, \ |
644 | + patch.object(pjlink_udp, 'readDatagram') as mock_read: |
645 | + mock_datagram.return_value = 6 |
646 | + mock_read.return_value = ('{prefix}2ACKN'.format(prefix=PJLINK_PREFIX), TEST1_DATA['ip'], PJLINK_PORT) |
647 | + |
648 | + # WHEN: get_datagram called with 0 bytes ready |
649 | + pjlink_udp.get_datagram() |
650 | + |
651 | + # THEN: Log entries should be made and method returns |
652 | + mock_log.warn.assert_has_calls(log_warn_calls) |
653 | + mock_log.debug.assert_has_calls(log_debug_calls) |
654 | + |
655 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
656 | + def test_get_datagram_pending_zero_length(self, mock_log): |
657 | + """ |
658 | + Test get_datagram when pendingDatagramSize = 0 |
659 | + """ |
660 | + # GIVEN: Test setup |
661 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
662 | + log_warn_calls = [call('(UDP) get_datagram() called when pending data size is 0')] |
663 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
664 | + call('(UDP) get_datagram() - Receiving data')] |
665 | + with patch.object(pjlink_udp, 'pendingDatagramSize') as mock_datagram: |
666 | + mock_datagram.return_value = 0 |
667 | + |
668 | + # WHEN: get_datagram called with 0 bytes ready |
669 | + pjlink_udp.get_datagram() |
670 | + |
671 | + # THEN: Log entries should be made and method returns |
672 | + mock_log.warn.assert_has_calls(log_warn_calls) |
673 | + mock_log.debug.assert_has_calls(log_debug_calls) |
674 | + |
675 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
676 | + def test_process_ackn_duplicate(self, mock_log): |
677 | + """ |
678 | + Test process_ackn method with multiple calls with same data |
679 | + """ |
680 | + # GIVEN: Test setup |
681 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
682 | + check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} |
683 | + log_warn_calls = [call('(UDP) Host {host} already replied - ignoring'.format(host=TEST1_DATA['ip']))] |
684 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
685 | + call('(UDP) Processing ACKN packet'), |
686 | + call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), |
687 | + call('(UDP) Processing ACKN packet')] |
688 | + |
689 | + # WHEN: process_ackn called twice with same data |
690 | + pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) |
691 | + pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) |
692 | + |
693 | + # THEN: pjlink_udp.ack_list should equal test_list |
694 | + # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? |
695 | + if pjlink_udp.ackn_list != check_list: |
696 | + # Check this way so we can print differences to stdout |
697 | + print('\nackn_list: ', pjlink_udp.ackn_list) |
698 | + print('test_list: ', check_list) |
699 | + assert pjlink_udp.ackn_list == check_list |
700 | + mock_log.debug.assert_has_calls(log_debug_calls) |
701 | + mock_log.warn.assert_has_calls(log_warn_calls) |
702 | + |
703 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
704 | + def test_process_ackn_multiple(self, mock_log): |
705 | + """ |
706 | + Test process_ackn method with multiple calls |
707 | + """ |
708 | + # GIVEN: Test setup |
709 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
710 | + check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}, |
711 | + TEST2_DATA['ip']: {'data': TEST2_DATA['mac_adx'], 'port': PJLINK_PORT}} |
712 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
713 | + call('(UDP) Processing ACKN packet'), |
714 | + call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip'])), |
715 | + call('(UDP) Processing ACKN packet'), |
716 | + call('(UDP) Adding {host} to ACKN list'.format(host=TEST2_DATA['ip']))] |
717 | + |
718 | + # WHEN: process_ackn called twice with different data |
719 | + pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) |
720 | + pjlink_udp.process_ackn(data=TEST2_DATA['mac_adx'], host=TEST2_DATA['ip'], port=PJLINK_PORT) |
721 | + |
722 | + # THEN: pjlink_udp.ack_list should equal test_list |
723 | + # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? |
724 | + if pjlink_udp.ackn_list != check_list: |
725 | + # Check this way so we can print differences to stdout |
726 | + print('\nackn_list: ', pjlink_udp.ackn_list) |
727 | + print('test_list: ', check_list) |
728 | + assert pjlink_udp.ackn_list == check_list |
729 | + mock_log.debug.assert_has_calls(log_debug_calls) |
730 | + |
731 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
732 | + def test_process_ackn_single(self, mock_log): |
733 | + """ |
734 | + Test process_ackn method with single call |
735 | + """ |
736 | + # GIVEN: Test setup |
737 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
738 | + check_list = {TEST1_DATA['ip']: {'data': TEST1_DATA['mac_adx'], 'port': PJLINK_PORT}} |
739 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
740 | + call('(UDP) Processing ACKN packet'), |
741 | + call('(UDP) Adding {host} to ACKN list'.format(host=TEST1_DATA['ip']))] |
742 | + |
743 | + # WHEN: process_ackn called twice with different data |
744 | + pjlink_udp.process_ackn(data=TEST1_DATA['mac_adx'], host=TEST1_DATA['ip'], port=PJLINK_PORT) |
745 | + |
746 | + # THEN: pjlink_udp.ack_list should equal test_list |
747 | + # NOTE: This assert only returns AssertionError - does not list differences. Maybe add a compare function? |
748 | + if pjlink_udp.ackn_list != check_list: |
749 | + # Check this way so we can print differences to stdout |
750 | + print('\nackn_list: ', pjlink_udp.ackn_list) |
751 | + print('test_list: ', check_list) |
752 | + assert pjlink_udp.ackn_list == check_list |
753 | + mock_log.debug.assert_has_calls(log_debug_calls) |
754 | + |
755 | + @patch.object(openlp.core.projectors.pjlink, 'log') |
756 | + def test_process_srch(self, mock_log): |
757 | + """ |
758 | + Test process_srch method |
759 | + """ |
760 | + # GIVEN: Test setup |
761 | + pjlink_udp = PJLinkUDP(projector_list=self.test_list) |
762 | + log_debug_calls = [call('(UDP) PJLinkUDP() Initialized'), |
763 | + call('(UDP) SRCH packet received - ignoring')] |
764 | + |
765 | + # WHEN: process_srch called |
766 | + pjlink_udp.process_srch(data=None, host=None, port=None) |
767 | + |
768 | + # THEN: debug log entry should be entered |
769 | + mock_log.debug.assert_has_calls(log_debug_calls) |
770 | |
771 | === renamed file 'tests/functional/openlp_core/common/test_projector_utilities.py' => 'tests/openlp_core/projectors/test_projector_utilities.py' |
772 | === renamed file 'tests/interfaces/openlp_core/ui/test_projectoreditform.py' => 'tests/openlp_core/projectors/test_projectoreditform.py' |
773 | === renamed file 'tests/interfaces/openlp_core/ui/test_projectormanager.py' => 'tests/openlp_core/projectors/test_projectormanager.py' |
774 | === renamed file 'tests/interfaces/openlp_core/ui/test_projectorsourceform.py' => 'tests/openlp_core/projectors/test_projectorsourceform.py' |
775 | --- tests/interfaces/openlp_core/ui/test_projectorsourceform.py 2017-12-29 09:15:48 +0000 |
776 | +++ tests/openlp_core/projectors/test_projectorsourceform.py 2018-02-11 11:42:51 +0000 |
777 | @@ -125,7 +125,6 @@ |
778 | select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb) |
779 | select_form.edit = True |
780 | select_form.exec(projector=self.projector) |
781 | - projector = select_form.projector |
782 | |
783 | # THEN: Verify all 4 buttons are available |
784 | assert len(select_form.button_box.buttons()) == 4, \ |
785 | @@ -144,7 +143,6 @@ |
786 | select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb) |
787 | select_form.edit = False |
788 | select_form.exec(projector=self.projector) |
789 | - projector = select_form.projector |
790 | |
791 | # THEN: Verify only 2 buttons are available |
792 | assert len(select_form.button_box.buttons()) == 2, \ |
793 | |
794 | === modified file 'tests/resources/projector/data.py' |
795 | --- tests/resources/projector/data.py 2017-12-29 09:15:48 +0000 |
796 | +++ tests/resources/projector/data.py 2018-02-11 11:42:51 +0000 |
797 | @@ -45,7 +45,8 @@ |
798 | serial_no='Serial Number 1', |
799 | sw_version='Version 1', |
800 | model_filter='Filter type 1', |
801 | - model_lamp='Lamp type 1') |
802 | + model_lamp='Lamp type 1', |
803 | + mac_adx='11:11:11:11:11:11') |
804 | |
805 | TEST2_DATA = dict(ip='222.222.222.222', |
806 | port='2222', |
807 | @@ -56,7 +57,8 @@ |
808 | serial_no='Serial Number 2', |
809 | sw_version='Version 2', |
810 | model_filter='Filter type 2', |
811 | - model_lamp='Lamp type 2') |
812 | + model_lamp='Lamp type 2', |
813 | + mac_adx='22:22:22:22:22:22') |
814 | |
815 | TEST3_DATA = dict(ip='333.333.333.333', |
816 | port='3333', |
817 | @@ -67,7 +69,8 @@ |
818 | serial_no='Serial Number 3', |
819 | sw_version='Version 3', |
820 | model_filter='Filter type 3', |
821 | - model_lamp='Lamp type 3') |
822 | + model_lamp='Lamp type 3', |
823 | + mac_adx='33:33:33:33:33:33') |
824 | |
825 | TEST_VIDEO_CODES = { |
826 | '11': 'RGB 1', |
One question and a request to refactor.