Merge lp:~alisonken1/openlp/pjlink2g into lp:openlp
- pjlink2g
- Merge into trunk
Status: | Superseded |
---|---|
Proposed branch: | lp:~alisonken1/openlp/pjlink2g |
Merge into: | lp:openlp |
Diff against target: |
1988 lines (+916/-809) 9 files modified
openlp/core/lib/__init__.py (+1/-1) openlp/core/lib/projector/constants.py (+1/-1) openlp/core/lib/projector/pjlink.py (+419/-420) openlp/core/lib/projector/upgrade.py (+5/-5) openlp/core/ui/projector/manager.py (+1/-1) tests/functional/openlp_core_lib/test_projector_constants.py (+2/-2) tests/functional/openlp_core_lib/test_projector_db.py (+4/-4) tests/functional/openlp_core_lib/test_projector_pjlink_base.py (+15/-375) tests/functional/openlp_core_lib/test_projector_pjlink_commands.py (+468/-0) |
To merge this branch: | bzr merge lp:~alisonken1/openlp/pjlink2g |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Tim Bentley | Needs Fixing | ||
Review via email: mp+328634@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-08-07.
Commit message
PJLink2 update
Description of the change
- Break PJLink class into base class and process commands class
- Restructure class methods
- Break projector PJLink tests into pjlink_base and pjlink_commands
- Restructure test methods
- Remove unused test imports
- Rename several tests
- Remove extraneous test (test_projector
- Added tests for process_erst reply
-------
lp:~alisonken1/openlp/pjlink2g (revision 2756)
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
[SUCCESS] https:/
Ken Roberts (alisonken1) wrote : | # |
Tim Bentley (trb143) wrote : | # |
Some comments on the code as we are improving it !
- 2757. By Ken Roberts
-
Code cleanups
- 2758. By Ken Roberts
-
Fix AVMT test
Unmerged revisions
Preview Diff
1 | === modified file 'openlp/core/lib/__init__.py' |
2 | --- openlp/core/lib/__init__.py 2017-05-20 05:51:58 +0000 |
3 | +++ openlp/core/lib/__init__.py 2017-08-07 00:09:00 +0000 |
4 | @@ -621,5 +621,5 @@ |
5 | from .renderer import Renderer |
6 | from .mediamanageritem import MediaManagerItem |
7 | from .projector.db import ProjectorDB, Projector |
8 | -from .projector.pjlink1 import PJLink |
9 | +from .projector.pjlink import PJLink |
10 | from .projector.constants import PJLINK_PORT, ERROR_MSG, ERROR_STRING |
11 | |
12 | === modified file 'openlp/core/lib/projector/constants.py' |
13 | --- openlp/core/lib/projector/constants.py 2017-06-09 12:12:39 +0000 |
14 | +++ openlp/core/lib/projector/constants.py 2017-08-07 00:09:00 +0000 |
15 | @@ -154,7 +154,7 @@ |
16 | }, |
17 | 'SRCH': {'version': ['2', ], |
18 | 'description': translate('OpenLP.PJLinkConstants', |
19 | - 'UDP broadcast search request for available projectors.') |
20 | + 'UDP broadcast search request for available projectors. Reply is ACKN.') |
21 | }, |
22 | 'SVER': {'version': ['2', ], |
23 | 'description': translate('OpenLP.PJLinkConstants', |
24 | |
25 | === renamed file 'openlp/core/lib/projector/pjlink1.py' => 'openlp/core/lib/projector/pjlink.py' |
26 | --- openlp/core/lib/projector/pjlink1.py 2017-07-20 15:31:50 +0000 |
27 | +++ openlp/core/lib/projector/pjlink.py 2017-08-07 00:09:00 +0000 |
28 | @@ -20,14 +20,17 @@ |
29 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
30 | ############################################################################### |
31 | """ |
32 | - :mod:`openlp.core.lib.projector.pjlink1` module |
33 | + :mod:`openlp.core.lib.projector.pjlink` module |
34 | Provides the necessary functions for connecting to a PJLink-capable projector. |
35 | |
36 | - See PJLink Class 1 Specifications for details. |
37 | - http://pjlink.jbmia.or.jp/english/dl.html |
38 | - |
39 | - Section 5-1 PJLink Specifications |
40 | - |
41 | + PJLink Class 1 Specifications. |
42 | + http://pjlink.jbmia.or.jp/english/dl_class1.html |
43 | + Section 5-1 PJLink Specifications |
44 | + Section 5-5 Guidelines for Input Terminals |
45 | + |
46 | + PJLink Class 2 Specifications. |
47 | + http://pjlink.jbmia.or.jp/english/dl_class2.html |
48 | + Section 5-1 PJLink Specifications |
49 | Section 5-5 Guidelines for Input Terminals |
50 | |
51 | NOTE: |
52 | @@ -40,7 +43,7 @@ |
53 | import logging |
54 | log = logging.getLogger(__name__) |
55 | |
56 | -log.debug('pjlink1 loaded') |
57 | +log.debug('pjlink loaded') |
58 | |
59 | __all__ = ['PJLink'] |
60 | |
61 | @@ -69,7 +72,403 @@ |
62 | PJLINK_SUFFIX = CR |
63 | |
64 | |
65 | -class PJLink(QtNetwork.QTcpSocket): |
66 | +class PJLinkCommands(object): |
67 | + """ |
68 | + Process replies from PJLink projector. |
69 | + """ |
70 | + |
71 | + def __init__(self, *args, **kwargs): |
72 | + """ |
73 | + Setup for the process commands |
74 | + """ |
75 | + log.debug('PJlinkCommands(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs)) |
76 | + super().__init__() |
77 | + # Map command to function |
78 | + self.pjlink_functions = { |
79 | + 'AVMT': self.process_avmt, |
80 | + 'CLSS': self.process_clss, |
81 | + 'ERST': self.process_erst, |
82 | + 'INFO': self.process_info, |
83 | + 'INF1': self.process_inf1, |
84 | + 'INF2': self.process_inf2, |
85 | + 'INPT': self.process_inpt, |
86 | + 'INST': self.process_inst, |
87 | + 'LAMP': self.process_lamp, |
88 | + 'NAME': self.process_name, |
89 | + 'PJLINK': self.check_login, |
90 | + 'POWR': self.process_powr, |
91 | + 'SNUM': self.process_snum, |
92 | + 'SVER': self.process_sver, |
93 | + 'RFIL': self.process_rfil, |
94 | + 'RLMP': self.process_rlmp |
95 | + } |
96 | + |
97 | + def reset_information(self): |
98 | + """ |
99 | + Initialize instance variables. Also used to reset projector-specific information to default. |
100 | + """ |
101 | + log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state())) |
102 | + self.fan = None # ERST |
103 | + self.filter_time = None # FILT |
104 | + self.lamp = None # LAMP |
105 | + self.mac_adx_received = None # ACKN |
106 | + self.manufacturer = None # INF1 |
107 | + self.model = None # INF2 |
108 | + self.model_filter = None # RFIL |
109 | + self.model_lamp = None # RLMP |
110 | + self.mute = None # AVMT |
111 | + self.other_info = None # INFO |
112 | + self.pjlink_class = PJLINK_CLASS # Default class |
113 | + self.pjlink_name = None # NAME |
114 | + self.power = S_OFF # POWR |
115 | + self.serial_no = None # SNUM |
116 | + self.serial_no_received = None |
117 | + self.sw_version = None # SVER |
118 | + self.sw_version_received = None |
119 | + self.shutter = None # AVMT |
120 | + self.source_available = None # INST |
121 | + self.source = None # INPT |
122 | + # These should be part of PJLink() class, but set here for convenience |
123 | + if hasattr(self, 'timer'): |
124 | + log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip)) |
125 | + self.timer.stop() |
126 | + if hasattr(self, 'socket_timer'): |
127 | + log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip)) |
128 | + self.socket_timer.stop() |
129 | + self.send_busy = False |
130 | + self.send_queue = [] |
131 | + |
132 | + def process_command(self, cmd, data): |
133 | + """ |
134 | + Verifies any return error code. Calls the appropriate command handler. |
135 | + |
136 | + :param cmd: Command to process |
137 | + :param data: Data being processed |
138 | + """ |
139 | + log.debug('({ip}) Processing command "{cmd}" with data "{data}"'.format(ip=self.ip, |
140 | + cmd=cmd, |
141 | + data=data)) |
142 | + # Check if we have a future command not available yet |
143 | + _cmd = cmd.upper() |
144 | + _data = data.upper() |
145 | + if _cmd not in PJLINK_VALID_CMD: |
146 | + log.error("({ip}) Ignoring command='{cmd}' (Invalid/Unknown)".format(ip=self.ip, cmd=cmd)) |
147 | + return |
148 | + elif _cmd not in self.pjlink_functions: |
149 | + log.warn("({ip}) Unable to process command='{cmd}' (Future option)".format(ip=self.ip, cmd=cmd)) |
150 | + return |
151 | + elif _data in PJLINK_ERRORS: |
152 | + # Oops - projector error |
153 | + log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data)) |
154 | + if _data == 'ERRA': |
155 | + # Authentication error |
156 | + self.disconnect_from_host() |
157 | + self.change_status(E_AUTHENTICATION) |
158 | + log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip)) |
159 | + self.projectorAuthentication.emit(self.name) |
160 | + elif _data == 'ERR1': |
161 | + # Undefined command |
162 | + self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED], |
163 | + data=cmd)) |
164 | + elif _data == 'ERR2': |
165 | + # Invalid parameter |
166 | + self.change_status(E_PARAMETER) |
167 | + elif _data == 'ERR3': |
168 | + # Projector busy |
169 | + self.change_status(E_UNAVAILABLE) |
170 | + elif _data == 'ERR4': |
171 | + # Projector/display error |
172 | + self.change_status(E_PROJECTOR) |
173 | + self.receive_data_signal() |
174 | + return |
175 | + # Command succeeded - no extra information |
176 | + elif _data == 'OK': |
177 | + log.debug('({ip}) Command returned OK'.format(ip=self.ip)) |
178 | + # A command returned successfully |
179 | + self.receive_data_signal() |
180 | + return |
181 | + # Command checks already passed |
182 | + log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) |
183 | + self.receive_data_signal() |
184 | + self.pjlink_functions[_cmd](data) |
185 | + |
186 | + def process_avmt(self, data): |
187 | + """ |
188 | + Process shutter and speaker status. See PJLink specification for format. |
189 | + Update self.mute (audio) and self.shutter (video shutter). |
190 | + |
191 | + :param data: Shutter and audio status |
192 | + """ |
193 | + settings = {'11': {'shutter': True, 'mute': self.mute}, |
194 | + '21': {'shutter': self.shutter, 'mute': True}, |
195 | + '30': {'shutter': False, 'mute': False}, |
196 | + '31': {'shutter': True, 'mute': True} |
197 | + } |
198 | + if data in settings: |
199 | + shutter = settings[data]['shutter'] |
200 | + mute = settings[data]['mute'] |
201 | + # Check if we need to update the icons |
202 | + update_icons = (shutter != self.shutter) or (mute != self.mute) |
203 | + self.shutter = shutter |
204 | + self.mute = mute |
205 | + else: |
206 | + log.warning('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data)) |
207 | + if update_icons: |
208 | + self.projectorUpdateIcons.emit() |
209 | + return |
210 | + |
211 | + def process_clss(self, data): |
212 | + """ |
213 | + PJLink class that this projector supports. See PJLink specification for format. |
214 | + Updates self.class. |
215 | + |
216 | + :param data: Class that projector supports. |
217 | + """ |
218 | + # bug 1550891: Projector returns non-standard class response: |
219 | + # : Expected: '%1CLSS=1' |
220 | + # : Received: '%1CLSS=Class 1' (Optoma) |
221 | + # : Received: '%1CLSS=Version1' (BenQ) |
222 | + if len(data) > 1: |
223 | + log.warn("({ip}) Non-standard CLSS reply: '{data}'".format(ip=self.ip, data=data)) |
224 | + # Due to stupid projectors not following standards (Optoma, BenQ comes to mind), |
225 | + # AND the different responses that can be received, the semi-permanent way to |
226 | + # fix the class reply is to just remove all non-digit characters. |
227 | + try: |
228 | + clss = re.findall('\d', data)[0] # Should only be the first match |
229 | + except IndexError: |
230 | + log.error("({ip}) No numbers found in class version reply - defaulting to class '1'".format(ip=self.ip)) |
231 | + clss = '1' |
232 | + elif not data.isdigit(): |
233 | + log.error("({ip}) NAN class version reply - defaulting to class '1'".format(ip=self.ip)) |
234 | + clss = '1' |
235 | + else: |
236 | + clss = data |
237 | + self.pjlink_class = clss |
238 | + log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip, |
239 | + data=self.pjlink_class)) |
240 | + return |
241 | + |
242 | + def process_erst(self, data): |
243 | + """ |
244 | + Error status. See PJLink Specifications for format. |
245 | + Updates self.projector_errors |
246 | + |
247 | +\ :param data: Error status |
248 | + """ |
249 | + try: |
250 | + datacheck = int(data) |
251 | + except ValueError: |
252 | + # Bad data - ignore |
253 | + return |
254 | + if datacheck == 0: |
255 | + self.projector_errors = None |
256 | + else: |
257 | + self.projector_errors = {} |
258 | + # Fan |
259 | + if data[0] != '0': |
260 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \ |
261 | + PJLINK_ERST_STATUS[data[0]] |
262 | + # Lamp |
263 | + if data[1] != '0': |
264 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \ |
265 | + PJLINK_ERST_STATUS[data[1]] |
266 | + # Temp |
267 | + if data[2] != '0': |
268 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \ |
269 | + PJLINK_ERST_STATUS[data[2]] |
270 | + # Cover |
271 | + if data[3] != '0': |
272 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \ |
273 | + PJLINK_ERST_STATUS[data[3]] |
274 | + # Filter |
275 | + if data[4] != '0': |
276 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \ |
277 | + PJLINK_ERST_STATUS[data[4]] |
278 | + # Other |
279 | + if data[5] != '0': |
280 | + self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \ |
281 | + PJLINK_ERST_STATUS[data[5]] |
282 | + return |
283 | + |
284 | + def process_inf1(self, data): |
285 | + """ |
286 | + Manufacturer name set in projector. |
287 | + Updates self.manufacturer |
288 | + |
289 | + :param data: Projector manufacturer |
290 | + """ |
291 | + self.manufacturer = data |
292 | + log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer)) |
293 | + return |
294 | + |
295 | + def process_inf2(self, data): |
296 | + """ |
297 | + Projector Model set in projector. |
298 | + Updates self.model. |
299 | + |
300 | + :param data: Model name |
301 | + """ |
302 | + self.model = data |
303 | + log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model)) |
304 | + return |
305 | + |
306 | + def process_info(self, data): |
307 | + """ |
308 | + Any extra info set in projector. |
309 | + Updates self.other_info. |
310 | + |
311 | + :param data: Projector other info |
312 | + """ |
313 | + self.other_info = data |
314 | + log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info)) |
315 | + return |
316 | + |
317 | + def process_inpt(self, data): |
318 | + """ |
319 | + Current source input selected. See PJLink specification for format. |
320 | + Update self.source |
321 | + |
322 | + :param data: Currently selected source |
323 | + """ |
324 | + self.source = data |
325 | + log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source)) |
326 | + return |
327 | + |
328 | + def process_inst(self, data): |
329 | + """ |
330 | + Available source inputs. See PJLink specification for format. |
331 | + Updates self.source_available |
332 | + |
333 | + :param data: Sources list |
334 | + """ |
335 | + sources = [] |
336 | + check = data.split() |
337 | + for source in check: |
338 | + sources.append(source) |
339 | + sources.sort() |
340 | + self.source_available = sources |
341 | + self.projectorUpdateIcons.emit() |
342 | + log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip, |
343 | + data=self.source_available)) |
344 | + return |
345 | + |
346 | + def process_lamp(self, data): |
347 | + """ |
348 | + Lamp(s) status. See PJLink Specifications for format. |
349 | + Data may have more than 1 lamp to process. |
350 | + Update self.lamp dictionary with lamp status. |
351 | + |
352 | + :param data: Lamp(s) status. |
353 | + """ |
354 | + lamps = [] |
355 | + data_dict = data.split() |
356 | + while data_dict: |
357 | + try: |
358 | + fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True} |
359 | + except ValueError: |
360 | + # In case of invalid entry |
361 | + log.warning('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data)) |
362 | + return |
363 | + lamps.append(fill) |
364 | + data_dict.pop(0) # Remove lamp hours |
365 | + data_dict.pop(0) # Remove lamp on/off |
366 | + self.lamp = lamps |
367 | + return |
368 | + |
369 | + def process_name(self, data): |
370 | + """ |
371 | + Projector name set in projector. |
372 | + Updates self.pjlink_name |
373 | + |
374 | + :param data: Projector name |
375 | + """ |
376 | + self.pjlink_name = data |
377 | + log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name)) |
378 | + return |
379 | + |
380 | + def process_powr(self, data): |
381 | + """ |
382 | + Power status. See PJLink specification for format. |
383 | + Update self.power with status. Update icons if change from previous setting. |
384 | + |
385 | + :param data: Power status |
386 | + """ |
387 | + log.debug('({ip}: Processing POWR command'.format(ip=self.ip)) |
388 | + if data in PJLINK_POWR_STATUS: |
389 | + power = PJLINK_POWR_STATUS[data] |
390 | + update_icons = self.power != power |
391 | + self.power = power |
392 | + self.change_status(PJLINK_POWR_STATUS[data]) |
393 | + if update_icons: |
394 | + self.projectorUpdateIcons.emit() |
395 | + # Update the input sources available |
396 | + if power == S_ON: |
397 | + self.send_command('INST') |
398 | + else: |
399 | + # Log unknown status response |
400 | + log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data)) |
401 | + return |
402 | + |
403 | + def process_rfil(self, data): |
404 | + """ |
405 | + Process replacement filter type |
406 | + """ |
407 | + if self.model_filter is None: |
408 | + self.model_filter = data |
409 | + else: |
410 | + log.warn("({ip}) Filter model already set".format(ip=self.ip)) |
411 | + log.warn("({ip}) Saved model: '{old}'".format(ip=self.ip, old=self.model_filter)) |
412 | + log.warn("({ip}) New model: '{new}'".format(ip=self.ip, new=data)) |
413 | + |
414 | + def process_rlmp(self, data): |
415 | + """ |
416 | + Process replacement lamp type |
417 | + """ |
418 | + if self.model_lamp is None: |
419 | + self.model_lamp = data |
420 | + else: |
421 | + log.warn("({ip}) Lamp model already set".format(ip=self.ip)) |
422 | + log.warn("({ip}) Saved lamp: '{old}'".format(ip=self.ip, old=self.model_lamp)) |
423 | + log.warn("({ip}) New lamp: '{new}'".format(ip=self.ip, new=data)) |
424 | + |
425 | + def process_snum(self, data): |
426 | + """ |
427 | + Serial number of projector. |
428 | + |
429 | + :param data: Serial number from projector. |
430 | + """ |
431 | + if self.serial_no is None: |
432 | + log.debug("({ip}) Setting projector serial number to '{data}'".format(ip=self.ip, data=data)) |
433 | + self.serial_no = data |
434 | + self.db_update = False |
435 | + else: |
436 | + # Compare serial numbers and see if we got the same projector |
437 | + if self.serial_no != data: |
438 | + log.warn("({ip}) Projector serial number does not match saved serial number".format(ip=self.ip)) |
439 | + log.warn("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.serial_no)) |
440 | + log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data)) |
441 | + log.warn("({ip}) NOT saving serial number".format(ip=self.ip)) |
442 | + self.serial_no_received = data |
443 | + |
444 | + def process_sver(self, data): |
445 | + """ |
446 | + Software version of projector |
447 | + """ |
448 | + if self.sw_version is None: |
449 | + log.debug("({ip}) Setting projector software version to '{data}'".format(ip=self.ip, data=data)) |
450 | + self.sw_version = data |
451 | + self.db_update = True |
452 | + else: |
453 | + # Compare software version and see if we got the same projector |
454 | + if self.serial_no != data: |
455 | + log.warn("({ip}) Projector software version does not match saved software version".format(ip=self.ip)) |
456 | + log.warn("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.sw_version)) |
457 | + log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data)) |
458 | + log.warn("({ip}) NOT saving serial number".format(ip=self.ip)) |
459 | + self.sw_version_received = data |
460 | + |
461 | + |
462 | +class PJLink(PJLinkCommands, QtNetwork.QTcpSocket): |
463 | """ |
464 | Socket service for connecting to a PJLink-capable projector. |
465 | """ |
466 | @@ -84,17 +483,18 @@ |
467 | |
468 | # New commands available in PJLink Class 2 |
469 | pjlink_udp_commands = [ |
470 | - 'ACKN', |
471 | + 'ACKN', # Class 2 |
472 | 'ERST', # Class 1 or 2 |
473 | 'INPT', # Class 1 or 2 |
474 | - 'LKUP', |
475 | + 'LKUP', # Class 2 |
476 | 'POWR', # Class 1 or 2 |
477 | - 'SRCH' |
478 | + 'SRCH' # Class 2 |
479 | ] |
480 | |
481 | - def __init__(self, name=None, ip=None, port=PJLINK_PORT, pin=None, *args, **kwargs): |
482 | + def __init__(self, port=PJLINK_PORT, *args, **kwargs): |
483 | """ |
484 | Setup for instance. |
485 | + Options should be in kwargs except for port which does have a default. |
486 | |
487 | :param name: Display name |
488 | :param ip: IP address to connect to |
489 | @@ -109,23 +509,16 @@ |
490 | :param socket_timeout: Time (in seconds) to abort the connection if no response |
491 | """ |
492 | log.debug('PJlink(args={args} kwargs={kwargs})'.format(args=args, kwargs=kwargs)) |
493 | - self.name = name |
494 | - self.ip = ip |
495 | - self.port = port |
496 | - self.pin = pin |
497 | super().__init__() |
498 | - self.model_lamp = None |
499 | - self.model_filter = None |
500 | - self.mac_adx = kwargs.get('mac_adx') |
501 | - self.serial_no = None |
502 | - self.serial_no_received = None # Used only if saved serial number is different than received serial number |
503 | - self.dbid = None |
504 | - self.db_update = False # Use to check if db needs to be updated prior to exiting |
505 | - self.location = None |
506 | - self.notes = None |
507 | self.dbid = kwargs.get('dbid') |
508 | + self.ip = kwargs.get('ip') |
509 | self.location = kwargs.get('location') |
510 | + self.mac_adx = kwargs.get('mac_adx') |
511 | + self.name = kwargs.get('name') |
512 | self.notes = kwargs.get('notes') |
513 | + self.pin = kwargs.get('pin') |
514 | + self.port = port |
515 | + self.db_update = False # Use to check if db needs to be updated prior to exiting |
516 | # Poll time 20 seconds unless called with something else |
517 | self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000 |
518 | # Timeout 5 seconds unless called with something else |
519 | @@ -141,8 +534,6 @@ |
520 | # Add enough space to input buffer for extraneous \n \r |
521 | self.max_size = PJLINK_MAX_PACKET + 2 |
522 | self.setReadBufferSize(self.max_size) |
523 | - # PJLink information |
524 | - self.pjlink_class = '1' # Default class |
525 | self.reset_information() |
526 | # Set from ProjectorManager.add_projector() |
527 | self.widget = None # QListBox entry |
528 | @@ -151,58 +542,6 @@ |
529 | self.send_busy = False |
530 | # Socket timer for some possible brain-dead projectors or network cable pulled |
531 | self.socket_timer = None |
532 | - # Map command to function |
533 | - self.pjlink_functions = { |
534 | - 'AVMT': self.process_avmt, |
535 | - 'CLSS': self.process_clss, |
536 | - 'ERST': self.process_erst, |
537 | - 'INFO': self.process_info, |
538 | - 'INF1': self.process_inf1, |
539 | - 'INF2': self.process_inf2, |
540 | - 'INPT': self.process_inpt, |
541 | - 'INST': self.process_inst, |
542 | - 'LAMP': self.process_lamp, |
543 | - 'NAME': self.process_name, |
544 | - 'PJLINK': self.check_login, |
545 | - 'POWR': self.process_powr, |
546 | - 'SNUM': self.process_snum, |
547 | - 'SVER': self.process_sver, |
548 | - 'RFIL': self.process_rfil, |
549 | - 'RLMP': self.process_rlmp |
550 | - } |
551 | - |
552 | - def reset_information(self): |
553 | - """ |
554 | - Reset projector-specific information to default |
555 | - """ |
556 | - log.debug('({ip}) reset_information() connect status is {state}'.format(ip=self.ip, state=self.state())) |
557 | - self.send_queue = [] |
558 | - self.power = S_OFF |
559 | - self.pjlink_name = None |
560 | - self.manufacturer = None |
561 | - self.model = None |
562 | - self.serial_no = None |
563 | - self.serial_no_received = None |
564 | - self.sw_version = None |
565 | - self.sw_version_received = None |
566 | - self.mac_adx = None |
567 | - self.shutter = None |
568 | - self.mute = None |
569 | - self.lamp = None |
570 | - self.model_lamp = None |
571 | - self.fan = None |
572 | - self.filter_time = None |
573 | - self.model_filter = None |
574 | - self.source_available = None |
575 | - self.source = None |
576 | - self.other_info = None |
577 | - if hasattr(self, 'timer'): |
578 | - log.debug('({ip}): Calling timer.stop()'.format(ip=self.ip)) |
579 | - self.timer.stop() |
580 | - if hasattr(self, 'socket_timer'): |
581 | - log.debug('({ip}): Calling socket_timer.stop()'.format(ip=self.ip)) |
582 | - self.socket_timer.stop() |
583 | - self.send_busy = False |
584 | |
585 | def thread_started(self): |
586 | """ |
587 | @@ -290,28 +629,6 @@ |
588 | if self.model_lamp is None: |
589 | self.send_command('RLMP', queue=True) |
590 | |
591 | - def process_rfil(self, data): |
592 | - """ |
593 | - Process replacement filter type |
594 | - """ |
595 | - if self.model_filter is None: |
596 | - self.model_filter = data |
597 | - else: |
598 | - log.warn("({ip}) Filter model already set".format(ip=self.ip)) |
599 | - log.warn("({ip}) Saved model: '{old}'".format(ip=self.ip, old=self.model_filter)) |
600 | - log.warn("({ip}) New model: '{new}'".format(ip=self.ip, new=data)) |
601 | - |
602 | - def process_rlmp(self, data): |
603 | - """ |
604 | - Process replacement lamp type |
605 | - """ |
606 | - if self.model_lamp is None: |
607 | - self.model_lamp = data |
608 | - else: |
609 | - log.warn("({ip}) Lamp model already set".format(ip=self.ip)) |
610 | - log.warn("({ip}) Saved lamp: '{old}'".format(ip=self.ip, old=self.model_lamp)) |
611 | - log.warn("({ip}) New lamp: '{new}'".format(ip=self.ip, new=data)) |
612 | - |
613 | def _get_status(self, status): |
614 | """ |
615 | Helper to retrieve status/error codes and convert to strings. |
616 | @@ -474,6 +791,7 @@ |
617 | self.send_busy = False |
618 | return |
619 | read = self.readLine(self.max_size) |
620 | + log.debug("({ip}) get_data(): '{buff}'".format(ip=self.ip, buff=read)) |
621 | if read == -1: |
622 | # No data available |
623 | log.debug('({ip}) get_data(): No data available (-1)'.format(ip=self.ip)) |
624 | @@ -626,317 +944,6 @@ |
625 | self.change_status(E_NETWORK, |
626 | translate('OpenLP.PJLink', 'Error while sending data to projector')) |
627 | |
628 | - def process_command(self, cmd, data): |
629 | - """ |
630 | - Verifies any return error code. Calls the appropriate command handler. |
631 | - |
632 | - :param cmd: Command to process |
633 | - :param data: Data being processed |
634 | - """ |
635 | - log.debug('({ip}) Processing command "{cmd}" with data "{data}"'.format(ip=self.ip, |
636 | - cmd=cmd, |
637 | - data=data)) |
638 | - # Check if we have a future command not available yet |
639 | - if cmd not in PJLINK_VALID_CMD: |
640 | - log.error('({ip}) Unknown command received - ignoring'.format(ip=self.ip)) |
641 | - return |
642 | - elif cmd not in self.pjlink_functions: |
643 | - log.warn('({ip}) Future command received - unable to process yet'.format(ip=self.ip)) |
644 | - return |
645 | - elif data in PJLINK_ERRORS: |
646 | - # Oops - projector error |
647 | - log.error('({ip}) Projector returned error "{data}"'.format(ip=self.ip, data=data)) |
648 | - if data.upper() == 'ERRA': |
649 | - # Authentication error |
650 | - self.disconnect_from_host() |
651 | - self.change_status(E_AUTHENTICATION) |
652 | - log.debug('({ip}) emitting projectorAuthentication() signal'.format(ip=self.ip)) |
653 | - self.projectorAuthentication.emit(self.name) |
654 | - elif data.upper() == 'ERR1': |
655 | - # Undefined command |
656 | - self.change_status(E_UNDEFINED, '{error}: "{data}"'.format(error=ERROR_MSG[E_UNDEFINED], |
657 | - data=cmd)) |
658 | - elif data.upper() == 'ERR2': |
659 | - # Invalid parameter |
660 | - self.change_status(E_PARAMETER) |
661 | - elif data.upper() == 'ERR3': |
662 | - # Projector busy |
663 | - self.change_status(E_UNAVAILABLE) |
664 | - elif data.upper() == 'ERR4': |
665 | - # Projector/display error |
666 | - self.change_status(E_PROJECTOR) |
667 | - self.receive_data_signal() |
668 | - return |
669 | - # Command succeeded - no extra information |
670 | - elif data.upper() == 'OK': |
671 | - log.debug('({ip}) Command returned OK'.format(ip=self.ip)) |
672 | - # A command returned successfully |
673 | - self.receive_data_signal() |
674 | - return |
675 | - # Command checks already passed |
676 | - log.debug('({ip}) Calling function for {cmd}'.format(ip=self.ip, cmd=cmd)) |
677 | - self.receive_data_signal() |
678 | - self.pjlink_functions[cmd](data) |
679 | - |
680 | - def process_lamp(self, data): |
681 | - """ |
682 | - Lamp(s) status. See PJLink Specifications for format. |
683 | - Data may have more than 1 lamp to process. |
684 | - Update self.lamp dictionary with lamp status. |
685 | - |
686 | - :param data: Lamp(s) status. |
687 | - """ |
688 | - lamps = [] |
689 | - data_dict = data.split() |
690 | - while data_dict: |
691 | - try: |
692 | - fill = {'Hours': int(data_dict[0]), 'On': False if data_dict[1] == '0' else True} |
693 | - except ValueError: |
694 | - # In case of invalid entry |
695 | - log.warning('({ip}) process_lamp(): Invalid data "{data}"'.format(ip=self.ip, data=data)) |
696 | - return |
697 | - lamps.append(fill) |
698 | - data_dict.pop(0) # Remove lamp hours |
699 | - data_dict.pop(0) # Remove lamp on/off |
700 | - self.lamp = lamps |
701 | - return |
702 | - |
703 | - def process_powr(self, data): |
704 | - """ |
705 | - Power status. See PJLink specification for format. |
706 | - Update self.power with status. Update icons if change from previous setting. |
707 | - |
708 | - :param data: Power status |
709 | - """ |
710 | - log.debug('({ip}: Processing POWR command'.format(ip=self.ip)) |
711 | - if data in PJLINK_POWR_STATUS: |
712 | - power = PJLINK_POWR_STATUS[data] |
713 | - update_icons = self.power != power |
714 | - self.power = power |
715 | - self.change_status(PJLINK_POWR_STATUS[data]) |
716 | - if update_icons: |
717 | - self.projectorUpdateIcons.emit() |
718 | - # Update the input sources available |
719 | - if power == S_ON: |
720 | - self.send_command('INST') |
721 | - else: |
722 | - # Log unknown status response |
723 | - log.warning('({ip}) Unknown power response: {data}'.format(ip=self.ip, data=data)) |
724 | - return |
725 | - |
726 | - def process_avmt(self, data): |
727 | - """ |
728 | - Process shutter and speaker status. See PJLink specification for format. |
729 | - Update self.mute (audio) and self.shutter (video shutter). |
730 | - |
731 | - :param data: Shutter and audio status |
732 | - """ |
733 | - shutter = self.shutter |
734 | - mute = self.mute |
735 | - if data == '11': |
736 | - shutter = True |
737 | - mute = False |
738 | - elif data == '21': |
739 | - shutter = False |
740 | - mute = True |
741 | - elif data == '30': |
742 | - shutter = False |
743 | - mute = False |
744 | - elif data == '31': |
745 | - shutter = True |
746 | - mute = True |
747 | - else: |
748 | - log.warning('({ip}) Unknown shutter response: {data}'.format(ip=self.ip, data=data)) |
749 | - update_icons = shutter != self.shutter |
750 | - update_icons = update_icons or mute != self.mute |
751 | - self.shutter = shutter |
752 | - self.mute = mute |
753 | - if update_icons: |
754 | - self.projectorUpdateIcons.emit() |
755 | - return |
756 | - |
757 | - def process_inpt(self, data): |
758 | - """ |
759 | - Current source input selected. See PJLink specification for format. |
760 | - Update self.source |
761 | - |
762 | - :param data: Currently selected source |
763 | - """ |
764 | - self.source = data |
765 | - log.info('({ip}) Setting data source to "{data}"'.format(ip=self.ip, data=self.source)) |
766 | - return |
767 | - |
768 | - def process_clss(self, data): |
769 | - """ |
770 | - PJLink class that this projector supports. See PJLink specification for format. |
771 | - Updates self.class. |
772 | - |
773 | - :param data: Class that projector supports. |
774 | - """ |
775 | - # bug 1550891: Projector returns non-standard class response: |
776 | - # : Expected: '%1CLSS=1' |
777 | - # : Received: '%1CLSS=Class 1' (Optoma) |
778 | - # : Received: '%1CLSS=Version1' (BenQ) |
779 | - if len(data) > 1: |
780 | - log.warn("({ip}) Non-standard CLSS reply: '{data}'".format(ip=self.ip, data=data)) |
781 | - # Due to stupid projectors not following standards (Optoma, BenQ comes to mind), |
782 | - # AND the different responses that can be received, the semi-permanent way to |
783 | - # fix the class reply is to just remove all non-digit characters. |
784 | - try: |
785 | - clss = re.findall('\d', data)[0] # Should only be the first match |
786 | - except IndexError: |
787 | - log.error("({ip}) No numbers found in class version reply - defaulting to class '1'".format(ip=self.ip)) |
788 | - clss = '1' |
789 | - elif not data.isdigit(): |
790 | - log.error("({ip}) NAN class version reply - defaulting to class '1'".format(ip=self.ip)) |
791 | - clss = '1' |
792 | - else: |
793 | - clss = data |
794 | - self.pjlink_class = clss |
795 | - log.debug('({ip}) Setting pjlink_class for this projector to "{data}"'.format(ip=self.ip, |
796 | - data=self.pjlink_class)) |
797 | - return |
798 | - |
799 | - def process_name(self, data): |
800 | - """ |
801 | - Projector name set in projector. |
802 | - Updates self.pjlink_name |
803 | - |
804 | - :param data: Projector name |
805 | - """ |
806 | - self.pjlink_name = data |
807 | - log.debug('({ip}) Setting projector PJLink name to "{data}"'.format(ip=self.ip, data=self.pjlink_name)) |
808 | - return |
809 | - |
810 | - def process_inf1(self, data): |
811 | - """ |
812 | - Manufacturer name set in projector. |
813 | - Updates self.manufacturer |
814 | - |
815 | - :param data: Projector manufacturer |
816 | - """ |
817 | - self.manufacturer = data |
818 | - log.debug('({ip}) Setting projector manufacturer data to "{data}"'.format(ip=self.ip, data=self.manufacturer)) |
819 | - return |
820 | - |
821 | - def process_inf2(self, data): |
822 | - """ |
823 | - Projector Model set in projector. |
824 | - Updates self.model. |
825 | - |
826 | - :param data: Model name |
827 | - """ |
828 | - self.model = data |
829 | - log.debug('({ip}) Setting projector model to "{data}"'.format(ip=self.ip, data=self.model)) |
830 | - return |
831 | - |
832 | - def process_info(self, data): |
833 | - """ |
834 | - Any extra info set in projector. |
835 | - Updates self.other_info. |
836 | - |
837 | - :param data: Projector other info |
838 | - """ |
839 | - self.other_info = data |
840 | - log.debug('({ip}) Setting projector other_info to "{data}"'.format(ip=self.ip, data=self.other_info)) |
841 | - return |
842 | - |
843 | - def process_inst(self, data): |
844 | - """ |
845 | - Available source inputs. See PJLink specification for format. |
846 | - Updates self.source_available |
847 | - |
848 | - :param data: Sources list |
849 | - """ |
850 | - sources = [] |
851 | - check = data.split() |
852 | - for source in check: |
853 | - sources.append(source) |
854 | - sources.sort() |
855 | - self.source_available = sources |
856 | - self.projectorUpdateIcons.emit() |
857 | - log.debug('({ip}) Setting projector sources_available to "{data}"'.format(ip=self.ip, |
858 | - data=self.source_available)) |
859 | - return |
860 | - |
861 | - def process_erst(self, data): |
862 | - """ |
863 | - Error status. See PJLink Specifications for format. |
864 | - Updates self.projector_errors |
865 | - |
866 | - :param data: Error status |
867 | - """ |
868 | - try: |
869 | - datacheck = int(data) |
870 | - except ValueError: |
871 | - # Bad data - ignore |
872 | - return |
873 | - if datacheck == 0: |
874 | - self.projector_errors = None |
875 | - else: |
876 | - self.projector_errors = {} |
877 | - # Fan |
878 | - if data[0] != '0': |
879 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Fan')] = \ |
880 | - PJLINK_ERST_STATUS[data[0]] |
881 | - # Lamp |
882 | - if data[1] != '0': |
883 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Lamp')] = \ |
884 | - PJLINK_ERST_STATUS[data[1]] |
885 | - # Temp |
886 | - if data[2] != '0': |
887 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Temperature')] = \ |
888 | - PJLINK_ERST_STATUS[data[2]] |
889 | - # Cover |
890 | - if data[3] != '0': |
891 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Cover')] = \ |
892 | - PJLINK_ERST_STATUS[data[3]] |
893 | - # Filter |
894 | - if data[4] != '0': |
895 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Filter')] = \ |
896 | - PJLINK_ERST_STATUS[data[4]] |
897 | - # Other |
898 | - if data[5] != '0': |
899 | - self.projector_errors[translate('OpenLP.ProjectorPJLink', 'Other')] = \ |
900 | - PJLINK_ERST_STATUS[data[5]] |
901 | - return |
902 | - |
903 | - def process_snum(self, data): |
904 | - """ |
905 | - Serial number of projector. |
906 | - |
907 | - :param data: Serial number from projector. |
908 | - """ |
909 | - if self.serial_no is None: |
910 | - log.debug("({ip}) Setting projector serial number to '{data}'".format(ip=self.ip, data=data)) |
911 | - self.serial_no = data |
912 | - self.db_update = False |
913 | - else: |
914 | - # Compare serial numbers and see if we got the same projector |
915 | - if self.serial_no != data: |
916 | - log.warn("({ip}) Projector serial number does not match saved serial number".format(ip=self.ip)) |
917 | - log.warn("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.serial_no)) |
918 | - log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data)) |
919 | - log.warn("({ip}) NOT saving serial number".format(ip=self.ip)) |
920 | - self.serial_no_received = data |
921 | - |
922 | - def process_sver(self, data): |
923 | - """ |
924 | - Software version of projector |
925 | - """ |
926 | - if self.sw_version is None: |
927 | - log.debug("({ip}) Setting projector software version to '{data}'".format(ip=self.ip, data=data)) |
928 | - self.sw_version = data |
929 | - self.db_update = True |
930 | - else: |
931 | - # Compare software version and see if we got the same projector |
932 | - if self.serial_no != data: |
933 | - log.warn("({ip}) Projector software version does not match saved software version".format(ip=self.ip)) |
934 | - log.warn("({ip}) Saved: '{old}'".format(ip=self.ip, old=self.sw_version)) |
935 | - log.warn("({ip}) Received: '{new}'".format(ip=self.ip, new=data)) |
936 | - log.warn("({ip}) NOT saving serial number".format(ip=self.ip)) |
937 | - self.sw_version_received = data |
938 | - |
939 | def connect_to_host(self): |
940 | """ |
941 | Initiate connection to projector. |
942 | @@ -1098,11 +1105,3 @@ |
943 | self.send_busy = False |
944 | self.projectorReceivedData.emit() |
945 | return |
946 | - |
947 | - def _not_implemented(self, cmd): |
948 | - """ |
949 | - Log when a future PJLink command has not been implemented yet. |
950 | - """ |
951 | - log.warn("({ip}) Future command '{cmd}' has not been implemented yet".format(ip=self.ip, |
952 | - cmd=cmd)) |
953 | - return |
954 | |
955 | === modified file 'openlp/core/lib/projector/upgrade.py' |
956 | --- openlp/core/lib/projector/upgrade.py 2017-07-07 23:43:50 +0000 |
957 | +++ openlp/core/lib/projector/upgrade.py 2017-08-07 00:09:00 +0000 |
958 | @@ -42,7 +42,7 @@ |
959 | """ |
960 | Version 1 upgrade - old db might/might not be versioned. |
961 | """ |
962 | - log.debug('Skipping upgrade_1 of projector DB - not used') |
963 | + log.debug('Skipping projector DB upgrade to version 1 - not used') |
964 | |
965 | |
966 | def upgrade_2(session, metadata): |
967 | @@ -60,14 +60,14 @@ |
968 | :param session: DB session instance |
969 | :param metadata: Metadata of current DB |
970 | """ |
971 | + log.debug('Checking projector DB upgrade to version 2') |
972 | projector_table = Table('projector', metadata, autoload=True) |
973 | - if 'mac_adx' not in [col.name for col in projector_table.c.values()]: |
974 | - log.debug("Upgrading projector DB to version '2'") |
975 | + upgrade_db = 'mac_adx' not in [col.name for col in projector_table.c.values()] |
976 | + if upgrade_db: |
977 | new_op = get_upgrade_op(session) |
978 | new_op.add_column('projector', Column('mac_adx', types.String(18), server_default=null())) |
979 | new_op.add_column('projector', Column('serial_no', types.String(30), server_default=null())) |
980 | new_op.add_column('projector', Column('sw_version', types.String(30), server_default=null())) |
981 | new_op.add_column('projector', Column('model_filter', types.String(30), server_default=null())) |
982 | new_op.add_column('projector', Column('model_lamp', types.String(30), server_default=null())) |
983 | - else: |
984 | - log.warn("Skipping upgrade_2 of projector DB") |
985 | + log.debug('{status} projector DB upgrade to version 2'.format(status='Updated' if upgrade_db else 'Skipping')) |
986 | |
987 | === modified file 'openlp/core/ui/projector/manager.py' |
988 | --- openlp/core/ui/projector/manager.py 2017-07-07 23:43:50 +0000 |
989 | +++ openlp/core/ui/projector/manager.py 2017-08-07 00:09:00 +0000 |
990 | @@ -38,7 +38,7 @@ |
991 | E_NETWORK, E_NOT_CONNECTED, E_UNKNOWN_SOCKET_ERROR, STATUS_STRING, S_CONNECTED, S_CONNECTING, S_COOLDOWN, \ |
992 | S_INITIALIZE, S_NOT_CONNECTED, S_OFF, S_ON, S_STANDBY, S_WARMUP |
993 | from openlp.core.lib.projector.db import ProjectorDB |
994 | -from openlp.core.lib.projector.pjlink1 import PJLink |
995 | +from openlp.core.lib.projector.pjlink import PJLink |
996 | from openlp.core.lib.projector.pjlink2 import PJLinkUDP |
997 | from openlp.core.ui.projector.editform import ProjectorEditForm |
998 | from openlp.core.ui.projector.sourceselectform import SourceSelectTabs, SourceSelectSingle |
999 | |
1000 | === modified file 'tests/functional/openlp_core_lib/test_projector_constants.py' |
1001 | --- tests/functional/openlp_core_lib/test_projector_constants.py 2017-06-29 02:58:08 +0000 |
1002 | +++ tests/functional/openlp_core_lib/test_projector_constants.py 2017-08-07 00:09:00 +0000 |
1003 | @@ -22,7 +22,7 @@ |
1004 | """ |
1005 | Package to test the openlp.core.lib.projector.constants package. |
1006 | """ |
1007 | -from unittest import TestCase, skip |
1008 | +from unittest import TestCase |
1009 | |
1010 | |
1011 | class TestProjectorConstants(TestCase): |
1012 | @@ -40,4 +40,4 @@ |
1013 | from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES |
1014 | |
1015 | # THEN: Verify dictionary was build correctly |
1016 | - self.assertEquals(PJLINK_DEFAULT_CODES, TEST_VIDEO_CODES, 'PJLink video strings should match') |
1017 | + self.assertEqual(PJLINK_DEFAULT_CODES, TEST_VIDEO_CODES, 'PJLink video strings should match') |
1018 | |
1019 | === modified file 'tests/functional/openlp_core_lib/test_projector_db.py' |
1020 | --- tests/functional/openlp_core_lib/test_projector_db.py 2017-06-29 02:58:08 +0000 |
1021 | +++ tests/functional/openlp_core_lib/test_projector_db.py 2017-08-07 00:09:00 +0000 |
1022 | @@ -29,8 +29,8 @@ |
1023 | import shutil |
1024 | from tempfile import mkdtemp |
1025 | |
1026 | -from unittest import TestCase, skip |
1027 | -from unittest.mock import MagicMock, patch |
1028 | +from unittest import TestCase |
1029 | +from unittest.mock import patch |
1030 | |
1031 | from openlp.core.lib.projector import upgrade |
1032 | from openlp.core.lib.db import upgrade_db |
1033 | @@ -413,7 +413,7 @@ |
1034 | Test add_projector() fail |
1035 | """ |
1036 | # GIVEN: Test entry in the database |
1037 | - ignore_result = self.projector.add_projector(Projector(**TEST1_DATA)) |
1038 | + self.projector.add_projector(Projector(**TEST1_DATA)) |
1039 | |
1040 | # WHEN: Attempt to add same projector entry |
1041 | results = self.projector.add_projector(Projector(**TEST1_DATA)) |
1042 | @@ -439,7 +439,7 @@ |
1043 | Test update_projector() when entry not in database |
1044 | """ |
1045 | # GIVEN: Projector entry in database |
1046 | - ignore_result = self.projector.add_projector(Projector(**TEST1_DATA)) |
1047 | + self.projector.add_projector(Projector(**TEST1_DATA)) |
1048 | projector = Projector(**TEST2_DATA) |
1049 | |
1050 | # WHEN: Attempt to update data with a different ID |
1051 | |
1052 | === renamed file 'tests/functional/openlp_core_lib/test_projector_pjlink1.py' => 'tests/functional/openlp_core_lib/test_projector_pjlink_base.py' |
1053 | --- tests/functional/openlp_core_lib/test_projector_pjlink1.py 2017-06-29 02:58:08 +0000 |
1054 | +++ tests/functional/openlp_core_lib/test_projector_pjlink_base.py 2017-08-07 00:09:00 +0000 |
1055 | @@ -20,21 +20,20 @@ |
1056 | # Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
1057 | ############################################################################### |
1058 | """ |
1059 | -Package to test the openlp.core.lib.projector.pjlink1 package. |
1060 | +Package to test the openlp.core.lib.projector.pjlink base package. |
1061 | """ |
1062 | from unittest import TestCase |
1063 | from unittest.mock import call, patch, MagicMock |
1064 | |
1065 | -from openlp.core.lib.projector.pjlink1 import PJLink |
1066 | -from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_OFF, S_STANDBY, S_ON, \ |
1067 | - PJLINK_POWR_STATUS, S_CONNECTED |
1068 | +from openlp.core.lib.projector.pjlink import PJLink |
1069 | +from openlp.core.lib.projector.constants import E_PARAMETER, ERROR_STRING, S_ON, S_CONNECTED |
1070 | |
1071 | from tests.resources.projector.data import TEST_PIN, TEST_SALT, TEST_CONNECT_AUTHENTICATE, TEST_HASH |
1072 | |
1073 | pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) |
1074 | |
1075 | |
1076 | -class TestPJLink(TestCase): |
1077 | +class TestPJLinkBase(TestCase): |
1078 | """ |
1079 | Tests for the PJLink module |
1080 | """ |
1081 | @@ -42,7 +41,10 @@ |
1082 | @patch.object(pjlink_test, 'send_command') |
1083 | @patch.object(pjlink_test, 'waitForReadyRead') |
1084 | @patch('openlp.core.common.qmd5_hash') |
1085 | - def test_authenticated_connection_call(self, mock_qmd5_hash, mock_waitForReadyRead, mock_send_command, |
1086 | + def test_authenticated_connection_call(self, |
1087 | + mock_qmd5_hash, |
1088 | + mock_waitForReadyRead, |
1089 | + mock_send_command, |
1090 | mock_readyRead): |
1091 | """ |
1092 | Ticket 92187: Fix for projector connect with PJLink authentication exception. |
1093 | @@ -59,140 +61,6 @@ |
1094 | self.assertTrue(mock_qmd5_hash.called_with(TEST_PIN, |
1095 | "Connection request should have been called with TEST_PIN")) |
1096 | |
1097 | - def test_projector_process_rfil_save(self): |
1098 | - """ |
1099 | - Test saving filter type |
1100 | - """ |
1101 | - # GIVEN: Test object |
1102 | - pjlink = pjlink_test |
1103 | - pjlink.model_filter = None |
1104 | - filter_model = 'Filter Type Test' |
1105 | - |
1106 | - # WHEN: Filter model is received |
1107 | - pjlink.process_rfil(data=filter_model) |
1108 | - |
1109 | - # THEN: Filter model number should be saved |
1110 | - self.assertEqual(pjlink.model_filter, filter_model, 'Filter type should have been saved') |
1111 | - |
1112 | - def test_projector_process_rfil_nosave(self): |
1113 | - """ |
1114 | - Test saving filter type previously saved |
1115 | - """ |
1116 | - # GIVEN: Test object |
1117 | - pjlink = pjlink_test |
1118 | - pjlink.model_filter = 'Old filter type' |
1119 | - filter_model = 'Filter Type Test' |
1120 | - |
1121 | - # WHEN: Filter model is received |
1122 | - pjlink.process_rfil(data=filter_model) |
1123 | - |
1124 | - # THEN: Filter model number should be saved |
1125 | - self.assertNotEquals(pjlink.model_filter, filter_model, 'Filter type should NOT have been saved') |
1126 | - |
1127 | - def test_projector_process_rlmp_save(self): |
1128 | - """ |
1129 | - Test saving lamp type |
1130 | - """ |
1131 | - # GIVEN: Test object |
1132 | - pjlink = pjlink_test |
1133 | - pjlink.model_lamp = None |
1134 | - lamp_model = 'Lamp Type Test' |
1135 | - |
1136 | - # WHEN: Filter model is received |
1137 | - pjlink.process_rlmp(data=lamp_model) |
1138 | - |
1139 | - # THEN: Filter model number should be saved |
1140 | - self.assertEqual(pjlink.model_lamp, lamp_model, 'Lamp type should have been saved') |
1141 | - |
1142 | - def test_projector_process_rlmp_nosave(self): |
1143 | - """ |
1144 | - Test saving lamp type previously saved |
1145 | - """ |
1146 | - # GIVEN: Test object |
1147 | - pjlink = pjlink_test |
1148 | - pjlink.model_lamp = 'Old lamp type' |
1149 | - lamp_model = 'Filter Type Test' |
1150 | - |
1151 | - # WHEN: Filter model is received |
1152 | - pjlink.process_rlmp(data=lamp_model) |
1153 | - |
1154 | - # THEN: Filter model number should be saved |
1155 | - self.assertNotEquals(pjlink.model_lamp, lamp_model, 'Lamp type should NOT have been saved') |
1156 | - |
1157 | - def test_projector_process_snum_set(self): |
1158 | - """ |
1159 | - Test saving serial number from projector |
1160 | - """ |
1161 | - # GIVEN: Test object |
1162 | - pjlink = pjlink_test |
1163 | - pjlink.serial_no = None |
1164 | - test_number = 'Test Serial Number' |
1165 | - |
1166 | - # WHEN: No serial number is set and we receive serial number command |
1167 | - pjlink.process_snum(data=test_number) |
1168 | - |
1169 | - # THEN: Serial number should be set |
1170 | - self.assertEqual(pjlink.serial_no, test_number, |
1171 | - 'Projector serial number should have been set') |
1172 | - |
1173 | - def test_projector_process_snum_different(self): |
1174 | - """ |
1175 | - Test projector serial number different than saved serial number |
1176 | - """ |
1177 | - # GIVEN: Test object |
1178 | - pjlink = pjlink_test |
1179 | - pjlink.serial_no = 'Previous serial number' |
1180 | - test_number = 'Test Serial Number' |
1181 | - |
1182 | - # WHEN: No serial number is set and we receive serial number command |
1183 | - pjlink.process_snum(data=test_number) |
1184 | - |
1185 | - # THEN: Serial number should be set |
1186 | - self.assertNotEquals(pjlink.serial_no, test_number, |
1187 | - 'Projector serial number should NOT have been set') |
1188 | - |
1189 | - def test_projector_clss_one(self): |
1190 | - """ |
1191 | - Test class 1 sent from projector |
1192 | - """ |
1193 | - # GIVEN: Test object |
1194 | - pjlink = pjlink_test |
1195 | - |
1196 | - # WHEN: Process class response |
1197 | - pjlink.process_clss('1') |
1198 | - |
1199 | - # THEN: Projector class should be set to 1 |
1200 | - self.assertEqual(pjlink.pjlink_class, '1', |
1201 | - 'Projector should have returned class=1') |
1202 | - |
1203 | - def test_projector_clss_two(self): |
1204 | - """ |
1205 | - Test class 2 sent from projector |
1206 | - """ |
1207 | - # GIVEN: Test object |
1208 | - pjlink = pjlink_test |
1209 | - |
1210 | - # WHEN: Process class response |
1211 | - pjlink.process_clss('2') |
1212 | - |
1213 | - # THEN: Projector class should be set to 1 |
1214 | - self.assertEqual(pjlink.pjlink_class, '2', |
1215 | - 'Projector should have returned class=2') |
1216 | - |
1217 | - def test_bug_1550891_non_standard_class_reply(self): |
1218 | - """ |
1219 | - Bugfix 1550891: CLSS request returns non-standard reply |
1220 | - """ |
1221 | - # GIVEN: Test object |
1222 | - pjlink = pjlink_test |
1223 | - |
1224 | - # WHEN: Process non-standard reply |
1225 | - pjlink.process_clss('Class 1') |
1226 | - |
1227 | - # THEN: Projector class should be set with proper value |
1228 | - self.assertEqual(pjlink.pjlink_class, '1', |
1229 | - 'Non-standard class reply should have set class=1') |
1230 | - |
1231 | @patch.object(pjlink_test, 'change_status') |
1232 | def test_status_change(self, mock_change_status): |
1233 | """ |
1234 | @@ -210,242 +78,13 @@ |
1235 | 'change_status should have been called with "{}"'.format( |
1236 | ERROR_STRING[E_PARAMETER])) |
1237 | |
1238 | - @patch.object(pjlink_test, 'process_inpt') |
1239 | - def test_projector_return_ok(self, mock_process_inpt): |
1240 | - """ |
1241 | - Test projector calls process_inpt command when process_command is called with INPT option |
1242 | - """ |
1243 | - # GIVEN: Test object |
1244 | - pjlink = pjlink_test |
1245 | - |
1246 | - # WHEN: process_command is called with INST command and 31 input: |
1247 | - pjlink.process_command('INPT', '31') |
1248 | - |
1249 | - # THEN: process_inpt method should have been called with 31 |
1250 | - mock_process_inpt.called_with('31', |
1251 | - "process_inpt should have been called with 31") |
1252 | - |
1253 | - @patch.object(pjlink_test, 'projectorReceivedData') |
1254 | - def test_projector_process_lamp(self, mock_projectorReceivedData): |
1255 | - """ |
1256 | - Test status lamp on/off and hours |
1257 | - """ |
1258 | - # GIVEN: Test object |
1259 | - pjlink = pjlink_test |
1260 | - |
1261 | - # WHEN: Call process_command with lamp data |
1262 | - pjlink.process_command('LAMP', '22222 1') |
1263 | - |
1264 | - # THEN: Lamp should have been set with status=ON and hours=22222 |
1265 | - self.assertEqual(pjlink.lamp[0]['On'], True, |
1266 | - 'Lamp power status should have been set to TRUE') |
1267 | - self.assertEqual(pjlink.lamp[0]['Hours'], 22222, |
1268 | - 'Lamp hours should have been set to 22222') |
1269 | - |
1270 | - @patch.object(pjlink_test, 'projectorReceivedData') |
1271 | - def test_projector_process_multiple_lamp(self, mock_projectorReceivedData): |
1272 | - """ |
1273 | - Test status multiple lamp on/off and hours |
1274 | - """ |
1275 | - # GIVEN: Test object |
1276 | - pjlink = pjlink_test |
1277 | - |
1278 | - # WHEN: Call process_command with lamp data |
1279 | - pjlink.process_command('LAMP', '11111 1 22222 0 33333 1') |
1280 | - |
1281 | - # THEN: Lamp should have been set with proper lamp status |
1282 | - self.assertEqual(len(pjlink.lamp), 3, |
1283 | - 'Projector should have 3 lamps specified') |
1284 | - self.assertEqual(pjlink.lamp[0]['On'], True, |
1285 | - 'Lamp 1 power status should have been set to TRUE') |
1286 | - self.assertEqual(pjlink.lamp[0]['Hours'], 11111, |
1287 | - 'Lamp 1 hours should have been set to 11111') |
1288 | - self.assertEqual(pjlink.lamp[1]['On'], False, |
1289 | - 'Lamp 2 power status should have been set to FALSE') |
1290 | - self.assertEqual(pjlink.lamp[1]['Hours'], 22222, |
1291 | - 'Lamp 2 hours should have been set to 22222') |
1292 | - self.assertEqual(pjlink.lamp[2]['On'], True, |
1293 | - 'Lamp 3 power status should have been set to TRUE') |
1294 | - self.assertEqual(pjlink.lamp[2]['Hours'], 33333, |
1295 | - 'Lamp 3 hours should have been set to 33333') |
1296 | - |
1297 | - @patch.object(pjlink_test, 'projectorReceivedData') |
1298 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1299 | - @patch.object(pjlink_test, 'send_command') |
1300 | - @patch.object(pjlink_test, 'change_status') |
1301 | - def test_projector_process_power_on(self, mock_change_status, |
1302 | - mock_send_command, |
1303 | - mock_UpdateIcons, |
1304 | - mock_ReceivedData): |
1305 | - """ |
1306 | - Test status power to ON |
1307 | - """ |
1308 | - # GIVEN: Test object and preset |
1309 | - pjlink = pjlink_test |
1310 | - pjlink.power = S_STANDBY |
1311 | - |
1312 | - # WHEN: Call process_command with turn power on command |
1313 | - pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON]) |
1314 | - |
1315 | - # THEN: Power should be set to ON |
1316 | - self.assertEqual(pjlink.power, S_ON, 'Power should have been set to ON') |
1317 | - mock_send_command.assert_called_once_with('INST') |
1318 | - self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') |
1319 | - |
1320 | - @patch.object(pjlink_test, 'projectorReceivedData') |
1321 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1322 | - @patch.object(pjlink_test, 'send_command') |
1323 | - @patch.object(pjlink_test, 'change_status') |
1324 | - def test_projector_process_power_off(self, mock_change_status, |
1325 | - mock_send_command, |
1326 | - mock_UpdateIcons, |
1327 | - mock_ReceivedData): |
1328 | - """ |
1329 | - Test status power to STANDBY |
1330 | - """ |
1331 | - # GIVEN: Test object and preset |
1332 | - pjlink = pjlink_test |
1333 | - pjlink.power = S_ON |
1334 | - |
1335 | - # WHEN: Call process_command with turn power on command |
1336 | - pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY]) |
1337 | - |
1338 | - # THEN: Power should be set to STANDBY |
1339 | - self.assertEqual(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY') |
1340 | - self.assertEqual(mock_send_command.called, False, 'send_command should not have been called') |
1341 | - self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') |
1342 | - |
1343 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1344 | - def test_projector_process_avmt_closed_unmuted(self, mock_projectorReceivedData): |
1345 | - """ |
1346 | - Test avmt status shutter closed and audio muted |
1347 | - """ |
1348 | - # GIVEN: Test object |
1349 | - pjlink = pjlink_test |
1350 | - pjlink.shutter = False |
1351 | - pjlink.mute = True |
1352 | - |
1353 | - # WHEN: Called with setting shutter closed and mute off |
1354 | - pjlink.process_avmt('11') |
1355 | - |
1356 | - # THEN: Shutter should be True and mute should be False |
1357 | - self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') |
1358 | - self.assertFalse(pjlink.mute, 'Audio should be off') |
1359 | - |
1360 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1361 | - def test_projector_process_avmt_open_muted(self, mock_projectorReceivedData): |
1362 | - """ |
1363 | - Test avmt status shutter open and mute on |
1364 | - """ |
1365 | - # GIVEN: Test object |
1366 | - pjlink = pjlink_test |
1367 | - pjlink.shutter = True |
1368 | - pjlink.mute = False |
1369 | - |
1370 | - # WHEN: Called with setting shutter closed and mute on |
1371 | - pjlink.process_avmt('21') |
1372 | - |
1373 | - # THEN: Shutter should be closed and mute should be True |
1374 | - self.assertFalse(pjlink.shutter, 'Shutter should have been set to closed') |
1375 | - self.assertTrue(pjlink.mute, 'Audio should be off') |
1376 | - |
1377 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1378 | - def test_projector_process_avmt_open_unmuted(self, mock_projectorReceivedData): |
1379 | - """ |
1380 | - Test avmt status shutter open and mute off off |
1381 | - """ |
1382 | - # GIVEN: Test object |
1383 | - pjlink = pjlink_test |
1384 | - pjlink.shutter = True |
1385 | - pjlink.mute = True |
1386 | - |
1387 | - # WHEN: Called with setting shutter to closed and mute on |
1388 | - pjlink.process_avmt('30') |
1389 | - |
1390 | - # THEN: Shutter should be closed and mute should be True |
1391 | - self.assertFalse(pjlink.shutter, 'Shutter should have been set to open') |
1392 | - self.assertFalse(pjlink.mute, 'Audio should be on') |
1393 | - |
1394 | - @patch.object(pjlink_test, 'projectorUpdateIcons') |
1395 | - def test_projector_process_avmt_closed_muted(self, mock_projectorReceivedData): |
1396 | - """ |
1397 | - Test avmt status shutter closed and mute off |
1398 | - """ |
1399 | - # GIVEN: Test object |
1400 | - pjlink = pjlink_test |
1401 | - pjlink.shutter = False |
1402 | - pjlink.mute = False |
1403 | - |
1404 | - # WHEN: Called with setting shutter to closed and mute on |
1405 | - pjlink.process_avmt('31') |
1406 | - |
1407 | - # THEN: Shutter should be closed and mute should be True |
1408 | - self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') |
1409 | - self.assertTrue(pjlink.mute, 'Audio should be on') |
1410 | - |
1411 | - def test_projector_process_input(self): |
1412 | - """ |
1413 | - Test input source status shows current input |
1414 | - """ |
1415 | - # GIVEN: Test object |
1416 | - pjlink = pjlink_test |
1417 | - pjlink.source = '0' |
1418 | - |
1419 | - # WHEN: Called with input source |
1420 | - pjlink.process_inpt('1') |
1421 | - |
1422 | - # THEN: Input selected should reflect current input |
1423 | - self.assertEqual(pjlink.source, '1', 'Input source should be set to "1"') |
1424 | - |
1425 | - def test_projector_reset_information(self): |
1426 | - """ |
1427 | - Test reset_information() resets all information and stops timers |
1428 | - """ |
1429 | - # GIVEN: Test object and test data |
1430 | - pjlink = pjlink_test |
1431 | - pjlink.power = S_ON |
1432 | - pjlink.pjlink_name = 'OPENLPTEST' |
1433 | - pjlink.manufacturer = 'PJLINK' |
1434 | - pjlink.model = '1' |
1435 | - pjlink.shutter = True |
1436 | - pjlink.mute = True |
1437 | - pjlink.lamp = True |
1438 | - pjlink.fan = True |
1439 | - pjlink.source_available = True |
1440 | - pjlink.other_info = 'ANOTHER TEST' |
1441 | - pjlink.send_queue = True |
1442 | - pjlink.send_busy = True |
1443 | - pjlink.timer = MagicMock() |
1444 | - pjlink.socket_timer = MagicMock() |
1445 | - |
1446 | - # WHEN: reset_information() is called |
1447 | - with patch.object(pjlink.timer, 'stop') as mock_timer: |
1448 | - with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer: |
1449 | - pjlink.reset_information() |
1450 | - |
1451 | - # THEN: All information should be reset and timers stopped |
1452 | - self.assertEqual(pjlink.power, S_OFF, 'Projector power should be OFF') |
1453 | - self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None') |
1454 | - self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None') |
1455 | - self.assertIsNone(pjlink.model, 'Projector model should be None') |
1456 | - self.assertIsNone(pjlink.shutter, 'Projector shutter should be None') |
1457 | - self.assertIsNone(pjlink.mute, 'Projector shuttter should be None') |
1458 | - self.assertIsNone(pjlink.lamp, 'Projector lamp should be None') |
1459 | - self.assertIsNone(pjlink.fan, 'Projector fan should be None') |
1460 | - self.assertIsNone(pjlink.source_available, 'Projector source_available should be None') |
1461 | - self.assertIsNone(pjlink.source, 'Projector source should be None') |
1462 | - self.assertIsNone(pjlink.other_info, 'Projector other_info should be None') |
1463 | - self.assertEqual(pjlink.send_queue, [], 'Projector send_queue should be an empty list') |
1464 | - self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False') |
1465 | - self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called') |
1466 | - self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called') |
1467 | - |
1468 | @patch.object(pjlink_test, 'send_command') |
1469 | @patch.object(pjlink_test, 'waitForReadyRead') |
1470 | @patch.object(pjlink_test, 'projectorAuthentication') |
1471 | @patch.object(pjlink_test, 'timer') |
1472 | @patch.object(pjlink_test, 'socket_timer') |
1473 | - def test_bug_1593882_no_pin_authenticated_connection(self, mock_socket_timer, |
1474 | + def test_bug_1593882_no_pin_authenticated_connection(self, |
1475 | + mock_socket_timer, |
1476 | mock_timer, |
1477 | mock_authentication, |
1478 | mock_ready_read, |
1479 | @@ -469,7 +108,8 @@ |
1480 | @patch.object(pjlink_test, '_send_command') |
1481 | @patch.object(pjlink_test, 'timer') |
1482 | @patch.object(pjlink_test, 'socket_timer') |
1483 | - def test_bug_1593883_pjlink_authentication(self, mock_socket_timer, |
1484 | + def test_bug_1593883_pjlink_authentication(self, |
1485 | + mock_socket_timer, |
1486 | mock_timer, |
1487 | mock_send_command, |
1488 | mock_state, |
1489 | @@ -491,7 +131,7 @@ |
1490 | "call(data='{hash}%1CLSS ?\\r')".format(hash=TEST_HASH)) |
1491 | |
1492 | @patch.object(pjlink_test, 'disconnect_from_host') |
1493 | - def socket_abort_test(self, mock_disconnect): |
1494 | + def test_socket_abort(self, mock_disconnect): |
1495 | """ |
1496 | Test PJLink.socket_abort calls disconnect_from_host |
1497 | """ |
1498 | @@ -504,7 +144,7 @@ |
1499 | # THEN: disconnect_from_host should be called |
1500 | self.assertTrue(mock_disconnect.called, 'Should have called disconnect_from_host') |
1501 | |
1502 | - def poll_loop_not_connected_test(self): |
1503 | + def test_poll_loop_not_connected(self): |
1504 | """ |
1505 | Test PJLink.poll_loop not connected return |
1506 | """ |
1507 | @@ -522,7 +162,7 @@ |
1508 | self.assertFalse(pjlink.timer.called, 'Should have returned without calling any other method') |
1509 | |
1510 | @patch.object(pjlink_test, 'send_command') |
1511 | - def poll_loop_start_test(self, mock_send_command): |
1512 | + def test_poll_loop_start(self, mock_send_command): |
1513 | """ |
1514 | Test PJLink.poll_loop makes correct calls |
1515 | """ |
1516 | |
1517 | === added file 'tests/functional/openlp_core_lib/test_projector_pjlink_commands.py' |
1518 | --- tests/functional/openlp_core_lib/test_projector_pjlink_commands.py 1970-01-01 00:00:00 +0000 |
1519 | +++ tests/functional/openlp_core_lib/test_projector_pjlink_commands.py 2017-08-07 00:09:00 +0000 |
1520 | @@ -0,0 +1,468 @@ |
1521 | +# -*- coding: utf-8 -*- |
1522 | +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 |
1523 | + |
1524 | +############################################################################### |
1525 | +# OpenLP - Open Source Lyrics Projection # |
1526 | +# --------------------------------------------------------------------------- # |
1527 | +# Copyright (c) 2008-2015 OpenLP Developers # |
1528 | +# --------------------------------------------------------------------------- # |
1529 | +# This program is free software; you can redistribute it and/or modify it # |
1530 | +# under the terms of the GNU General Public License as published by the Free # |
1531 | +# Software Foundation; version 2 of the License. # |
1532 | +# # |
1533 | +# This program is distributed in the hope that it will be useful, but WITHOUT # |
1534 | +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # |
1535 | +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for # |
1536 | +# more details. # |
1537 | +# # |
1538 | +# You should have received a copy of the GNU General Public License along # |
1539 | +# with this program; if not, write to the Free Software Foundation, Inc., 59 # |
1540 | +# Temple Place, Suite 330, Boston, MA 02111-1307 USA # |
1541 | +############################################################################### |
1542 | +""" |
1543 | +Package to test the openlp.core.lib.projector.pjlink commands package. |
1544 | +""" |
1545 | +from unittest import TestCase |
1546 | +from unittest.mock import patch, MagicMock |
1547 | + |
1548 | +from openlp.core.lib.projector.pjlink import PJLink |
1549 | +from openlp.core.lib.projector.constants import PJLINK_ERST_STATUS, PJLINK_POWR_STATUS, \ |
1550 | + S_OFF, S_STANDBY, S_ON |
1551 | + |
1552 | +from tests.resources.projector.data import TEST_PIN |
1553 | + |
1554 | +pjlink_test = PJLink(name='test', ip='127.0.0.1', pin=TEST_PIN, no_poll=True) |
1555 | + |
1556 | +# ERST status codes |
1557 | +ERST_OK, ERST_WARN, ERST_ERR = '0', '1', '2' |
1558 | + |
1559 | + |
1560 | +class TestPJLinkCommands(TestCase): |
1561 | + """ |
1562 | + Tests for the PJLink module |
1563 | + """ |
1564 | + def test_projector_reset_information(self): |
1565 | + """ |
1566 | + Test reset_information() resets all information and stops timers |
1567 | + """ |
1568 | + # GIVEN: Test object and test data |
1569 | + pjlink = pjlink_test |
1570 | + pjlink.power = S_ON |
1571 | + pjlink.pjlink_name = 'OPENLPTEST' |
1572 | + pjlink.manufacturer = 'PJLINK' |
1573 | + pjlink.model = '1' |
1574 | + pjlink.shutter = True |
1575 | + pjlink.mute = True |
1576 | + pjlink.lamp = True |
1577 | + pjlink.fan = True |
1578 | + pjlink.source_available = True |
1579 | + pjlink.other_info = 'ANOTHER TEST' |
1580 | + pjlink.send_queue = True |
1581 | + pjlink.send_busy = True |
1582 | + pjlink.timer = MagicMock() |
1583 | + pjlink.socket_timer = MagicMock() |
1584 | + |
1585 | + # WHEN: reset_information() is called |
1586 | + with patch.object(pjlink.timer, 'stop') as mock_timer: |
1587 | + with patch.object(pjlink.socket_timer, 'stop') as mock_socket_timer: |
1588 | + pjlink.reset_information() |
1589 | + |
1590 | + # THEN: All information should be reset and timers stopped |
1591 | + self.assertEqual(pjlink.power, S_OFF, 'Projector power should be OFF') |
1592 | + self.assertIsNone(pjlink.pjlink_name, 'Projector pjlink_name should be None') |
1593 | + self.assertIsNone(pjlink.manufacturer, 'Projector manufacturer should be None') |
1594 | + self.assertIsNone(pjlink.model, 'Projector model should be None') |
1595 | + self.assertIsNone(pjlink.shutter, 'Projector shutter should be None') |
1596 | + self.assertIsNone(pjlink.mute, 'Projector shuttter should be None') |
1597 | + self.assertIsNone(pjlink.lamp, 'Projector lamp should be None') |
1598 | + self.assertIsNone(pjlink.fan, 'Projector fan should be None') |
1599 | + self.assertIsNone(pjlink.source_available, 'Projector source_available should be None') |
1600 | + self.assertIsNone(pjlink.source, 'Projector source should be None') |
1601 | + self.assertIsNone(pjlink.other_info, 'Projector other_info should be None') |
1602 | + self.assertEqual(pjlink.send_queue, [], 'Projector send_queue should be an empty list') |
1603 | + self.assertFalse(pjlink.send_busy, 'Projector send_busy should be False') |
1604 | + self.assertTrue(mock_timer.called, 'Projector timer.stop() should have been called') |
1605 | + self.assertTrue(mock_socket_timer.called, 'Projector socket_timer.stop() should have been called') |
1606 | + |
1607 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1608 | + def test_projector_process_avmt_closed_muted(self, mock_projectorReceivedData): |
1609 | + """ |
1610 | + Test avmt status shutter closed and mute off |
1611 | + """ |
1612 | + # GIVEN: Test object |
1613 | + pjlink = pjlink_test |
1614 | + pjlink.shutter = False |
1615 | + pjlink.mute = False |
1616 | + |
1617 | + # WHEN: Called with setting shutter to closed and mute on |
1618 | + pjlink.process_avmt('31') |
1619 | + |
1620 | + # THEN: Shutter should be closed and mute should be True |
1621 | + self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') |
1622 | + self.assertTrue(pjlink.mute, 'Audio should be off') |
1623 | + |
1624 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1625 | + def test_projector_process_avmt_shutter_closed(self, mock_projectorReceivedData): |
1626 | + """ |
1627 | + Test avmt status shutter closed and audio muted |
1628 | + """ |
1629 | + # GIVEN: Test object |
1630 | + pjlink = pjlink_test |
1631 | + pjlink.shutter = False |
1632 | + pjlink.mute = True |
1633 | + |
1634 | + # WHEN: Called with setting shutter closed and mute off |
1635 | + pjlink.process_avmt('11') |
1636 | + |
1637 | + # THEN: Shutter should be True and mute should be False |
1638 | + self.assertTrue(pjlink.shutter, 'Shutter should have been set to closed') |
1639 | + self.assertTrue(pjlink.mute, 'Audio should not have changed') |
1640 | + |
1641 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1642 | + def test_projector_process_avmt_audio_muted(self, mock_projectorReceivedData): |
1643 | + """ |
1644 | + Test avmt status shutter open and mute on |
1645 | + """ |
1646 | + # GIVEN: Test object |
1647 | + pjlink = pjlink_test |
1648 | + pjlink.shutter = True |
1649 | + pjlink.mute = False |
1650 | + |
1651 | + # WHEN: Called with setting shutter closed and mute on |
1652 | + pjlink.process_avmt('21') |
1653 | + |
1654 | + # THEN: Shutter should be closed and mute should be True |
1655 | + self.assertTrue(pjlink.shutter, 'Shutter should not have changed') |
1656 | + self.assertTrue(pjlink.mute, 'Audio should be off') |
1657 | + |
1658 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1659 | + def test_projector_process_avmt_open_unmuted(self, mock_projectorReceivedData): |
1660 | + """ |
1661 | + Test avmt status shutter open and mute off off |
1662 | + """ |
1663 | + # GIVEN: Test object |
1664 | + pjlink = pjlink_test |
1665 | + pjlink.shutter = True |
1666 | + pjlink.mute = True |
1667 | + |
1668 | + # WHEN: Called with setting shutter to closed and mute on |
1669 | + pjlink.process_avmt('30') |
1670 | + |
1671 | + # THEN: Shutter should be closed and mute should be True |
1672 | + self.assertFalse(pjlink.shutter, 'Shutter should have been set to open') |
1673 | + self.assertFalse(pjlink.mute, 'Audio should be on') |
1674 | + |
1675 | + def test_projector_process_clss_one(self): |
1676 | + """ |
1677 | + Test class 1 sent from projector |
1678 | + """ |
1679 | + # GIVEN: Test object |
1680 | + pjlink = pjlink_test |
1681 | + |
1682 | + # WHEN: Process class response |
1683 | + pjlink.process_clss('1') |
1684 | + |
1685 | + # THEN: Projector class should be set to 1 |
1686 | + self.assertEqual(pjlink.pjlink_class, '1', |
1687 | + 'Projector should have returned class=1') |
1688 | + |
1689 | + def test_projector_process_clss_two(self): |
1690 | + """ |
1691 | + Test class 2 sent from projector |
1692 | + """ |
1693 | + # GIVEN: Test object |
1694 | + pjlink = pjlink_test |
1695 | + |
1696 | + # WHEN: Process class response |
1697 | + pjlink.process_clss('2') |
1698 | + |
1699 | + # THEN: Projector class should be set to 1 |
1700 | + self.assertEqual(pjlink.pjlink_class, '2', |
1701 | + 'Projector should have returned class=2') |
1702 | + |
1703 | + def test_projector_process_clss_nonstandard_reply(self): |
1704 | + """ |
1705 | + Bugfix 1550891: CLSS request returns non-standard reply |
1706 | + """ |
1707 | + # GIVEN: Test object |
1708 | + pjlink = pjlink_test |
1709 | + |
1710 | + # WHEN: Process non-standard reply |
1711 | + pjlink.process_clss('Class 1') |
1712 | + |
1713 | + # THEN: Projector class should be set with proper value |
1714 | + self.assertEqual(pjlink.pjlink_class, '1', |
1715 | + 'Non-standard class reply should have set class=1') |
1716 | + |
1717 | + def test_projector_process_erst_all_ok(self): |
1718 | + """ |
1719 | + Test test_projector_process_erst_all_ok |
1720 | + """ |
1721 | + # GIVEN: Test object |
1722 | + pjlink = pjlink_test |
1723 | + chk_test = ERST_OK |
1724 | + |
1725 | + # WHEN: process_erst with no errors |
1726 | + pjlink.process_erst('{fan}{lamp}{temp}{cover}{filter}{other}'.format(fan=chk_test, |
1727 | + lamp=chk_test, |
1728 | + temp=chk_test, |
1729 | + cover=chk_test, |
1730 | + filter=chk_test, |
1731 | + other=chk_test)) |
1732 | + |
1733 | + # PJLink instance errors should be None |
1734 | + self.assertIsNone(pjlink.projector_errors, 'projector_errors should have been set to None') |
1735 | + |
1736 | + def test_projector_process_erst_all_warn(self): |
1737 | + """ |
1738 | + Test test_projector_process_erst_all_warn |
1739 | + """ |
1740 | + # GIVEN: Test object |
1741 | + pjlink = pjlink_test |
1742 | + chk_test = ERST_WARN |
1743 | + chk_code = PJLINK_ERST_STATUS[chk_test] |
1744 | + chk_value = {'Fan': chk_code, |
1745 | + 'Lamp': chk_code, |
1746 | + 'Temperature': chk_code, |
1747 | + 'Cover': chk_code, |
1748 | + 'Filter': chk_code, |
1749 | + 'Other': chk_code |
1750 | + } |
1751 | + |
1752 | + # WHEN: process_erst with status set to WARN |
1753 | + pjlink.process_erst('{fan}{lamp}{temp}{cover}{filter}{other}'.format(fan=chk_test, |
1754 | + lamp=chk_test, |
1755 | + temp=chk_test, |
1756 | + cover=chk_test, |
1757 | + filter=chk_test, |
1758 | + other=chk_test)) |
1759 | + |
1760 | + # PJLink instance errors should match chk_value |
1761 | + self.assertEqual(pjlink.projector_errors, chk_value, |
1762 | + 'projector_errors should have been set to all {err}'.format(err=chk_code)) |
1763 | + |
1764 | + def test_projector_process_erst_all_error(self): |
1765 | + """ |
1766 | + Test test_projector_process_erst_all_error |
1767 | + """ |
1768 | + # GIVEN: Test object |
1769 | + pjlink = pjlink_test |
1770 | + chk_test = ERST_ERR |
1771 | + chk_code = PJLINK_ERST_STATUS[chk_test] |
1772 | + chk_value = {'Fan': chk_code, |
1773 | + 'Lamp': chk_code, |
1774 | + 'Temperature': chk_code, |
1775 | + 'Cover': chk_code, |
1776 | + 'Filter': chk_code, |
1777 | + 'Other': chk_code |
1778 | + } |
1779 | + |
1780 | + # WHEN: process_erst with status set to ERROR |
1781 | + pjlink.process_erst('{fan}{lamp}{temp}{cover}{filter}{other}'.format(fan=chk_test, |
1782 | + lamp=chk_test, |
1783 | + temp=chk_test, |
1784 | + cover=chk_test, |
1785 | + filter=chk_test, |
1786 | + other=chk_test)) |
1787 | + |
1788 | + # PJLink instance errors should be set to chk_value |
1789 | + self.assertEqual(pjlink.projector_errors, chk_value, |
1790 | + 'projector_errors should have been set to all {err}'.format(err=chk_code)) |
1791 | + |
1792 | + def test_projector_process_inpt(self): |
1793 | + """ |
1794 | + Test input source status shows current input |
1795 | + """ |
1796 | + # GIVEN: Test object |
1797 | + pjlink = pjlink_test |
1798 | + pjlink.source = '0' |
1799 | + |
1800 | + # WHEN: Called with input source |
1801 | + pjlink.process_inpt('1') |
1802 | + |
1803 | + # THEN: Input selected should reflect current input |
1804 | + self.assertEqual(pjlink.source, '1', 'Input source should be set to "1"') |
1805 | + |
1806 | + @patch.object(pjlink_test, 'projectorReceivedData') |
1807 | + def test_projector_process_lamp_single(self, mock_projectorReceivedData): |
1808 | + """ |
1809 | + Test status lamp on/off and hours |
1810 | + """ |
1811 | + # GIVEN: Test object |
1812 | + pjlink = pjlink_test |
1813 | + |
1814 | + # WHEN: Call process_command with lamp data |
1815 | + pjlink.process_command('LAMP', '22222 1') |
1816 | + |
1817 | + # THEN: Lamp should have been set with status=ON and hours=22222 |
1818 | + self.assertEqual(pjlink.lamp[0]['On'], True, |
1819 | + 'Lamp power status should have been set to TRUE') |
1820 | + self.assertEqual(pjlink.lamp[0]['Hours'], 22222, |
1821 | + 'Lamp hours should have been set to 22222') |
1822 | + |
1823 | + @patch.object(pjlink_test, 'projectorReceivedData') |
1824 | + def test_projector_process_lamp_multiple(self, mock_projectorReceivedData): |
1825 | + """ |
1826 | + Test status multiple lamp on/off and hours |
1827 | + """ |
1828 | + # GIVEN: Test object |
1829 | + pjlink = pjlink_test |
1830 | + |
1831 | + # WHEN: Call process_command with lamp data |
1832 | + pjlink.process_command('LAMP', '11111 1 22222 0 33333 1') |
1833 | + |
1834 | + # THEN: Lamp should have been set with proper lamp status |
1835 | + self.assertEqual(len(pjlink.lamp), 3, |
1836 | + 'Projector should have 3 lamps specified') |
1837 | + self.assertEqual(pjlink.lamp[0]['On'], True, |
1838 | + 'Lamp 1 power status should have been set to TRUE') |
1839 | + self.assertEqual(pjlink.lamp[0]['Hours'], 11111, |
1840 | + 'Lamp 1 hours should have been set to 11111') |
1841 | + self.assertEqual(pjlink.lamp[1]['On'], False, |
1842 | + 'Lamp 2 power status should have been set to FALSE') |
1843 | + self.assertEqual(pjlink.lamp[1]['Hours'], 22222, |
1844 | + 'Lamp 2 hours should have been set to 22222') |
1845 | + self.assertEqual(pjlink.lamp[2]['On'], True, |
1846 | + 'Lamp 3 power status should have been set to TRUE') |
1847 | + self.assertEqual(pjlink.lamp[2]['Hours'], 33333, |
1848 | + 'Lamp 3 hours should have been set to 33333') |
1849 | + |
1850 | + @patch.object(pjlink_test, 'projectorReceivedData') |
1851 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1852 | + @patch.object(pjlink_test, 'send_command') |
1853 | + @patch.object(pjlink_test, 'change_status') |
1854 | + def test_projector_process_powr_on(self, |
1855 | + mock_change_status, |
1856 | + mock_send_command, |
1857 | + mock_UpdateIcons, |
1858 | + mock_ReceivedData): |
1859 | + """ |
1860 | + Test status power to ON |
1861 | + """ |
1862 | + # GIVEN: Test object and preset |
1863 | + pjlink = pjlink_test |
1864 | + pjlink.power = S_STANDBY |
1865 | + |
1866 | + # WHEN: Call process_command with turn power on command |
1867 | + pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_ON]) |
1868 | + |
1869 | + # THEN: Power should be set to ON |
1870 | + self.assertEqual(pjlink.power, S_ON, 'Power should have been set to ON') |
1871 | + mock_send_command.assert_called_once_with('INST') |
1872 | + self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') |
1873 | + |
1874 | + @patch.object(pjlink_test, 'projectorReceivedData') |
1875 | + @patch.object(pjlink_test, 'projectorUpdateIcons') |
1876 | + @patch.object(pjlink_test, 'send_command') |
1877 | + @patch.object(pjlink_test, 'change_status') |
1878 | + def test_projector_process_powr_off(self, |
1879 | + mock_change_status, |
1880 | + mock_send_command, |
1881 | + mock_UpdateIcons, |
1882 | + mock_ReceivedData): |
1883 | + """ |
1884 | + Test status power to STANDBY |
1885 | + """ |
1886 | + # GIVEN: Test object and preset |
1887 | + pjlink = pjlink_test |
1888 | + pjlink.power = S_ON |
1889 | + |
1890 | + # WHEN: Call process_command with turn power on command |
1891 | + pjlink.process_command('POWR', PJLINK_POWR_STATUS[S_STANDBY]) |
1892 | + |
1893 | + # THEN: Power should be set to STANDBY |
1894 | + self.assertEqual(pjlink.power, S_STANDBY, 'Power should have been set to STANDBY') |
1895 | + self.assertEqual(mock_send_command.called, False, 'send_command should not have been called') |
1896 | + self.assertEqual(mock_UpdateIcons.emit.called, True, 'projectorUpdateIcons should have been called') |
1897 | + |
1898 | + def test_projector_process_rfil_save(self): |
1899 | + """ |
1900 | + Test saving filter type |
1901 | + """ |
1902 | + # GIVEN: Test object |
1903 | + pjlink = pjlink_test |
1904 | + pjlink.model_filter = None |
1905 | + filter_model = 'Filter Type Test' |
1906 | + |
1907 | + # WHEN: Filter model is received |
1908 | + pjlink.process_rfil(data=filter_model) |
1909 | + |
1910 | + # THEN: Filter model number should be saved |
1911 | + self.assertEqual(pjlink.model_filter, filter_model, 'Filter type should have been saved') |
1912 | + |
1913 | + def test_projector_process_rfil_nosave(self): |
1914 | + """ |
1915 | + Test saving filter type previously saved |
1916 | + """ |
1917 | + # GIVEN: Test object |
1918 | + pjlink = pjlink_test |
1919 | + pjlink.model_filter = 'Old filter type' |
1920 | + filter_model = 'Filter Type Test' |
1921 | + |
1922 | + # WHEN: Filter model is received |
1923 | + pjlink.process_rfil(data=filter_model) |
1924 | + |
1925 | + # THEN: Filter model number should be saved |
1926 | + self.assertNotEquals(pjlink.model_filter, filter_model, 'Filter type should NOT have been saved') |
1927 | + |
1928 | + def test_projector_process_rlmp_save(self): |
1929 | + """ |
1930 | + Test saving lamp type |
1931 | + """ |
1932 | + # GIVEN: Test object |
1933 | + pjlink = pjlink_test |
1934 | + pjlink.model_lamp = None |
1935 | + lamp_model = 'Lamp Type Test' |
1936 | + |
1937 | + # WHEN: Filter model is received |
1938 | + pjlink.process_rlmp(data=lamp_model) |
1939 | + |
1940 | + # THEN: Filter model number should be saved |
1941 | + self.assertEqual(pjlink.model_lamp, lamp_model, 'Lamp type should have been saved') |
1942 | + |
1943 | + def test_projector_process_rlmp_nosave(self): |
1944 | + """ |
1945 | + Test saving lamp type previously saved |
1946 | + """ |
1947 | + # GIVEN: Test object |
1948 | + pjlink = pjlink_test |
1949 | + pjlink.model_lamp = 'Old lamp type' |
1950 | + lamp_model = 'Filter Type Test' |
1951 | + |
1952 | + # WHEN: Filter model is received |
1953 | + pjlink.process_rlmp(data=lamp_model) |
1954 | + |
1955 | + # THEN: Filter model number should be saved |
1956 | + self.assertNotEquals(pjlink.model_lamp, lamp_model, 'Lamp type should NOT have been saved') |
1957 | + |
1958 | + def test_projector_process_snum_set(self): |
1959 | + """ |
1960 | + Test saving serial number from projector |
1961 | + """ |
1962 | + # GIVEN: Test object |
1963 | + pjlink = pjlink_test |
1964 | + pjlink.serial_no = None |
1965 | + test_number = 'Test Serial Number' |
1966 | + |
1967 | + # WHEN: No serial number is set and we receive serial number command |
1968 | + pjlink.process_snum(data=test_number) |
1969 | + |
1970 | + # THEN: Serial number should be set |
1971 | + self.assertEqual(pjlink.serial_no, test_number, |
1972 | + 'Projector serial number should have been set') |
1973 | + |
1974 | + def test_projector_process_snum_different(self): |
1975 | + """ |
1976 | + Test projector serial number different than saved serial number |
1977 | + """ |
1978 | + # GIVEN: Test object |
1979 | + pjlink = pjlink_test |
1980 | + pjlink.serial_no = 'Previous serial number' |
1981 | + test_number = 'Test Serial Number' |
1982 | + |
1983 | + # WHEN: No serial number is set and we receive serial number command |
1984 | + pjlink.process_snum(data=test_number) |
1985 | + |
1986 | + # THEN: Serial number should be set |
1987 | + self.assertNotEquals(pjlink.serial_no, test_number, |
1988 | + 'Projector serial number should NOT have been set') |
Other than tests - there should be no new code here, only restructured code.