ProjectorManager tests and refactoring 2022-02-27

This commit is contained in:
Ken Roberts 2022-03-01 15:52:28 +00:00 committed by Raoul Snyman
parent e4f0dc6557
commit f7a28e5754
6 changed files with 819 additions and 145 deletions

View File

@ -437,57 +437,44 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
""" """
self.projector_form.exec() self.projector_form.exec()
def on_blank_projector(self, opt=None): def on_blank_projector(self, item=None, opt=None):
""" """
Calls projector thread to send blank screen command Calls projector(s) thread to send blank screen command
:param opt: Needed by PyQt5 :param item: Optional ProjectorItem() instance in case of direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.set_shutter_closed() return item.pjlink.set_shutter_closed()
except AttributeError:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.set_shutter_closed()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.set_shutter_closed()
except Exception:
continue
def on_doubleclick_item(self, item, opt=None): def on_doubleclick_item(self, item):
""" """
When item is doubleclicked, will connect to projector. When item is doubleclicked, will connect to projector.
:param item: List widget item for connection. :param item: List widget item for connection.
:param opt: Needed by PyQt5
""" """
projector = item.data(QtCore.Qt.UserRole) projector = item.data(QtCore.Qt.UserRole)
if QSOCKET_STATE[projector.link.state()] != S_CONNECTED: if QSOCKET_STATE[projector.link.state()] == S_CONNECTED:
try: log.debug(f'ProjectorManager: "{projector.pjlink.name}" already connected - skipping')
log.debug(f'ProjectorManager: Calling connect_to_host() on "{projector.link.ip}"') else:
log.debug(f'ProjectorManager: "{projector.pjlink.name}" calling connect_to_host()')
projector.link.connect_to_host() projector.link.connect_to_host()
except Exception:
log.debug(f'ProjectorManager: "{projector.link.ip}" already connected - skipping')
return return
def on_connect_projector(self, opt=None): def on_connect_projector(self, item=None, opt=None):
""" """
Calls projector thread to connect to projector Calls projector thread to connect to projector
:param opt: Needed by PyQt5 :param item: (Optional) ProjectorItem() for direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.connect_to_host() return item.pjlink.connect_to_host()
except AttributeError: else:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.connect_to_host()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.connect_to_host()
except Exception:
continue
def on_delete_projector(self, opt=None): def on_delete_projector(self, opt=None):
""" """
@ -553,23 +540,18 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
log.debug(f'New projector list - item: {item.link.ip} {item.link.name}') log.debug(f'New projector list - item: {item.link.ip} {item.link.name}')
self.udp_listen_delete(old_port) self.udp_listen_delete(old_port)
def on_disconnect_projector(self, opt=None): def on_disconnect_projector(self, item=None, opt=None):
""" """
Calls projector thread to disconnect from projector Calls projector thread to disconnect from projector
:param opt: Needed by PyQt5 :param item: (Optional) ProjectorItem() for direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.disconnect_from_host() return item.pjlink.disconnect_from_host()
except AttributeError: else:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.disconnect_from_host()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.disconnect_from_host()
except Exception:
continue
def on_edit_projector(self, opt=None): def on_edit_projector(self, opt=None):
""" """
@ -586,59 +568,44 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
self.projector_form.exec(projector.db_item) self.projector_form.exec(projector.db_item)
projector.db_item = self.projectordb.get_projector_by_id(self.old_projector.db_item.id) projector.db_item = self.projectordb.get_projector_by_id(self.old_projector.db_item.id)
def on_poweroff_projector(self, opt=None): def on_poweroff_projector(self, item=None, opt=None):
""" """
Calls projector link to send Power Off command Calls projector thread to turn projector off
:param opt: Needed by PyQt5 :param item: (Optional) ProjectorItem() for direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.set_power_off() return item.pjlink.set_power_off()
except AttributeError: else:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.set_power_off()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.set_power_off()
except Exception:
continue
def on_poweron_projector(self, opt=None): def on_poweron_projector(self, item=None, opt=None):
""" """
Calls projector link to send Power On command Calls projector thread to turn projector on
:param opt: Needed by PyQt5 :param item: (Optional) ProjectorItem() for direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.set_power_on() return item.pjlink.set_power_on()
except AttributeError: else:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.set_power_on()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.set_power_on()
except Exception:
continue
def on_show_projector(self, opt=None): def on_show_projector(self, item=None, opt=None):
""" """
Calls projector thread to send open shutter command Calls projector thread to open shutter
:param opt: Needed by PyQt5 :param item: (Optional) ProjectorItem() for direct call
:param opt: (Deprecated)
""" """
try: if item is not None:
opt.link.set_shutter_open() return item.pjlink.set_shutter_open()
except AttributeError: else:
for list_item in self.projector_list_widget.selectedItems(): for list_item in self.projector_list_widget.selectedItems():
if list_item is None: list_item.data(QtCore.Qt.UserRole).pjlink.set_shutter_open()
return
projector = list_item.data(QtCore.Qt.UserRole)
try:
projector.link.set_shutter_open()
except Exception:
continue
def on_status_projector(self, opt=None): def on_status_projector(self, opt=None):
""" """
@ -740,27 +707,27 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
:param projector: Projector instance to add :param projector: Projector instance to add
:param start: Start projector if True :param start: Start projector if True
""" """
item = ProjectorItem(link=self._add_projector(projector)) item = ProjectorItem(parent=self, item=self._add_projector(projector))
item.db_item = projector item.db_item = projector
item.icon = QtGui.QIcon(self.status_icons[S_NOT_CONNECTED]) item.icon = QtGui.QIcon(self.status_icons[S_NOT_CONNECTED])
widget = QtWidgets.QListWidgetItem(item.icon, widget = QtWidgets.QListWidgetItem(item.icon,
item.link.name, item.pjlink.name,
self.projector_list_widget self.projector_list_widget
) )
widget.setData(QtCore.Qt.UserRole, item) widget.setData(QtCore.Qt.UserRole, item)
item.link.db_item = item.db_item item.pjlink.db_item = item.db_item
item.widget = widget item.widget = widget
item.link.changeStatus.connect(self.update_status) item.pjlink.changeStatus.connect(self.update_status)
item.link.projectorAuthentication.connect(self.authentication_error) item.pjlink.projectorAuthentication.connect(self.authentication_error)
item.link.projectorNoAuthentication.connect(self.no_authentication_error) item.pjlink.projectorNoAuthentication.connect(self.no_authentication_error)
item.link.projectorUpdateIcons.connect(self.update_icons) item.pjlink.projectorUpdateIcons.connect(self.update_icons)
# Add UDP listener for new projector port # Add UDP listener for new projector port
self.udp_listen_add(item.link.port) self.udp_listen_add(item.pjlink.port)
self.projector_list.append(item) self.projector_list.append(item)
if start: if start:
item.link.connect_to_host() item.pjlink.connect_to_host()
for item in self.projector_list: for item in self.projector_list:
log.debug(f'New projector list - item: ({item.link.ip}) {item.link.name}') log.debug(f'New projector list - item: ({item.pjlink.ip}) {item.pjlink.name}')
@QtCore.pyqtSlot(str) @QtCore.pyqtSlot(str)
def add_projector_from_wizard(self, ip, opts=None): def add_projector_from_wizard(self, ip, opts=None):
@ -969,13 +936,20 @@ class ProjectorItem(QtCore.QObject):
Class for the projector list widget item. Class for the projector list widget item.
NOTE: Actual PJLink class instance should be saved as self.link NOTE: Actual PJLink class instance should be saved as self.link
""" """
def __init__(self, link=None): def __init__(self, parent=None, link=None, item=None):
""" """
Initialization for ProjectorItem instance Initialization for ProjectorItem instance
:param link: PJLink instance for QListWidgetItem :param link: (Deprecated) PJLink instance for QListWidgetItem
:param item: PJLink instance for QListWidgetItem
""" """
# Refactor so self.link is renamed self.pjlink to clarify reference
if link is None:
self.link = item
self.pjlink = item
else:
self.link = link self.link = link
self.pjlink = link
self.thread = None self.thread = None
self.icon = None self.icon = None
self.widget = None self.widget = None

View File

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2022 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Help classes/functions for PJLink Projector tests
"""
from unittest.mock import MagicMock
from PyQt5 import QtNetwork
from openlp.core.projectors.constants import S_OK, S_NOT_CONNECTED
from openlp.core.projectors.db import Projector
from tests.resources.projector.data import TEST1_DATA
class FakePJLink(object):
def __init__(self, projector=None, *args, **kwargs):
# Signal mocks
self.projectorStatus = MagicMock()
self.projectorAuthentication = MagicMock()
self.projectorNoAuthentication = MagicMock()
self.projectorReceivedData = MagicMock()
self.projectorUpdateIcons = MagicMock()
# Method mocks
self.changeStatus = MagicMock()
self.connect_to_host = MagicMock()
self.disconnect_from_host = MagicMock()
self.poll_timer = MagicMock()
self.set_power_off = MagicMock()
self.set_power_on = MagicMock()
self.set_shutter_closed = MagicMock()
self.set_shutter_open = MagicMock()
self.socket_timer = MagicMock()
self.status_timer = MagicMock()
self.state = MagicMock()
# Some tests that may include what it thinks are ProjectorItem()
# If ProjectorItem() is called, will probably overwrite these - OK
self.link = self
self.pjlink = self
# Normal entries from PJLink
self.entry = Projector(**TEST1_DATA) if projector is None else projector
self.ip = self.entry.ip
self.qhost = QtNetwork.QHostAddress(self.ip)
self.location = self.entry.location
self.mac_adx = self.entry.mac_adx
self.name = self.entry.name
self.notes = self.entry.notes
self.pin = self.entry.pin
self.port = int(self.entry.port)
self.pjlink_class = "1" if self.entry.pjlink_class is None else self.entry.pjlink_class
self.poll_time = 20000 if 'poll_time' not in kwargs else kwargs['poll_time'] * 1000
self.socket_timeout = 5000 if 'socket_timeout' not in kwargs else kwargs['socket_timeout'] * 1000
self.no_poll = 'no_poll' in kwargs
self.status_connect = S_NOT_CONNECTED
self.last_command = ''
self.projector_status = S_NOT_CONNECTED
self.error_status = S_OK
self.send_queue = []
self.priority_queue = []
self.send_busy = False
self.status_timer_checks = {} # Keep track of events for the status timer
# Default mock return values

View File

@ -20,6 +20,11 @@
########################################################################## ##########################################################################
""" """
Test misc. functions with few test paths Test misc. functions with few test paths
_load_projectors()
add_projector_from_wizard()
get_projector_list()
""" """
from unittest.mock import DEFAULT, patch from unittest.mock import DEFAULT, patch
@ -47,9 +52,7 @@ def test_private_load_projectors(projector_manager_mtdb):
with patch.multiple(projector_manager_mtdb, with patch.multiple(projector_manager_mtdb,
udp_listen_add=DEFAULT, udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT, udp_listen_delete=DEFAULT,
_load_projectors=DEFAULT) as mock_manager: _load_projectors=DEFAULT):
# Satisfy Flake8 linting
mock_manager['udp_listen_add'].return_value = None
projector_manager_mtdb.bootstrap_initialise() projector_manager_mtdb.bootstrap_initialise()
projector_manager_mtdb.bootstrap_post_set_up() projector_manager_mtdb.bootstrap_post_set_up()
@ -67,44 +70,6 @@ def test_private_load_projectors(projector_manager_mtdb):
assert t_chk == t_list, 'projector_list DB items do not match test items' assert t_chk == t_list, 'projector_list DB items do not match test items'
def test_on_edit_input(projector_manager):
"""
Test calling edit projector input GUI from input selection icon makes appropriate calls
"""
# GIVEN: Test environment
with patch.object(projector_manager, 'on_select_input') as mock_edit:
# WHEN: Called
projector_manager.on_edit_input()
# THEN: select input called with edit option
mock_edit.assert_called_with(opt=None, edit=True)
def test_on_add_projector(projector_manager):
"""
Test add new projector edit GUI is called properly
"""
# GIVEN: Test environment
# Mock to keep from getting event not registered error in Registry()
# during bootstrap_post_set_up()
with patch.multiple(projector_manager,
udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT) as mock_manager:
# Satisfy Flake8 linting
mock_manager['udp_listen_add'].return_value = None
projector_manager.bootstrap_initialise()
projector_manager.bootstrap_post_set_up()
with patch.object(projector_manager, 'projector_form') as mock_form:
# WHEN called
projector_manager.on_add_projector()
# THEN: projector form called
mock_form.exec.assert_called_once()
def test_add_projector_from_wizard(projector_manager): def test_add_projector_from_wizard(projector_manager):
""" """
Test when add projector from GUI, appropriate method is called correctly Test when add projector from GUI, appropriate method is called correctly
@ -138,9 +103,7 @@ def test_get_projector_list(projector_manager_mtdb):
# during bootstrap_post_set_up() # during bootstrap_post_set_up()
with patch.multiple(projector_manager_mtdb, with patch.multiple(projector_manager_mtdb,
udp_listen_add=DEFAULT, udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT) as mock_manager: udp_listen_delete=DEFAULT):
# Satisfy Flake8 linting
mock_manager['udp_listen_add'].return_value = None
projector_manager_mtdb.bootstrap_initialise() projector_manager_mtdb.bootstrap_initialise()
projector_manager_mtdb.bootstrap_post_set_up() projector_manager_mtdb.bootstrap_post_set_up()

View File

@ -0,0 +1,511 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2022 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Test methods called by toolbar icons part 1
on_blank_projector()
on_connect_projector()
on_disconnect_projector()
on_poweroff_projector()
on_poweron_projector()
on_show_projector()
"""
from unittest.mock import DEFAULT, patch
from openlp.core.projectors.db import Projector
from tests.helpers.projector import FakePJLink
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA
def helper_method(test_fixture, method, effect, test_item=None):
"""
Boilerplate for tests
:param test_fixture: Fixture used for testing
:param method: Method in fixture to test
:param effect: Dict of items for testing
{'item': Item class
'select': Boolean to enable selected() in projector_list_widget
:param test_item: (Optional) Item to call method with
"""
with patch.multiple(test_fixture,
udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT,
update_icons=DEFAULT,
_add_projector=DEFAULT) as mock_manager:
mock_manager['_add_projector'].side_effect = [item['item'] for item in effect]
test_fixture.bootstrap_initialise()
# projector_list_widget created here
test_fixture.bootstrap_post_set_up()
# Add ProjectorItem instances to projector_list_widget
for item in effect:
test_fixture.add_projector(projector=item['item'])
# Set at least one instance as selected to verify projector_list_widget is not called
for item in range(len(effect)):
test_fixture.projector_list_widget.item(item).setSelected(effect[item]['select'])
# WHEN: Called with projector instance
method(item=test_item)
def test_on_blank_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_blank_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.set_shutter_closed() should be called
t_1.set_shutter_closed.assert_called_once()
t_2.set_shutter_closed.assert_not_called()
t_3.set_shutter_closed.assert_not_called()
def test_on_blank_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_blank_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': True},
{'item': t_3, 'select': False}
]
)
# THEN: Only t_3.set_shutter_closed() should be called
t_1.set_shutter_closed.assert_not_called()
t_2.set_shutter_closed.assert_called_once()
t_3.set_shutter_closed.assert_not_called()
def test_on_blank_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_blank_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 set_shutter_closed() should be called
t_1.set_shutter_closed.assert_called_once()
t_2.set_shutter_closed.assert_not_called()
t_3.set_shutter_closed.assert_called_once()
def test_on_connect_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_connect_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.connect_to_host() should be called
t_1.connect_to_host.assert_called_once()
t_2.connect_to_host.assert_not_called()
t_3.connect_to_host.assert_not_called()
def test_on_connect_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_connect_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: Only t_3.connect_to_host() should be called
t_1.connect_to_host.assert_not_called()
t_2.connect_to_host.assert_not_called()
t_3.connect_to_host.assert_called_once()
def test_on_connect_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_connect_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 connect_to_host() should be called
t_1.connect_to_host.assert_called_once()
t_2.connect_to_host.assert_not_called()
t_3.connect_to_host.assert_called_once()
def test_on_disconnect_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_disconnect_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.disconnect_from_host() should be called
t_1.disconnect_from_host.assert_called_once()
t_2.disconnect_from_host.assert_not_called()
t_3.disconnect_from_host.assert_not_called()
def test_on_disconnect_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_disconnect_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: Only t_3.disconnect_from_host() should be called
t_1.disconnect_from_host.assert_not_called()
t_2.disconnect_from_host.assert_not_called()
t_3.disconnect_from_host.assert_called_once()
def test_on_disconnect_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_disconnect_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 disconnect_from_host() should be called
t_1.disconnect_from_host.assert_called_once()
t_2.disconnect_from_host.assert_not_called()
t_3.disconnect_from_host.assert_called_once()
def test_on_poweroff_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweroff_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.set_power_off() should be called
t_1.set_power_off.assert_called_once()
t_2.set_power_off.assert_not_called()
t_3.set_power_off.assert_not_called()
def test_on_poweroff_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweroff_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: Only t_3.set_power_off() should be called
t_1.set_power_off.assert_not_called()
t_2.set_power_off.assert_not_called()
t_3.set_power_off.assert_called_once()
def test_on_poweroff_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweroff_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 set_power_off() should be called
t_1.set_power_off.assert_called_once()
t_2.set_power_off.assert_not_called()
t_3.set_power_off.assert_called_once()
def test_on_poweron_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweron_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.set_power_on() should be called
t_1.set_power_on.assert_called_once()
t_2.set_power_on.assert_not_called()
t_3.set_power_on.assert_not_called()
def test_on_poweron_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweron_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: Only t_3.set_power_on() should be called
t_1.set_power_on.assert_not_called()
t_2.set_power_on.assert_not_called()
t_3.set_power_on.assert_called_once()
def test_on_poweron_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_poweron_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 set_power_on() should be called
t_1.set_power_on.assert_called_once()
t_2.set_power_on.assert_not_called()
t_3.set_power_on.assert_called_once()
def test_on_show_projector_direct(projector_manager_mtdb):
"""
Test calling method directly - projector_list_widget should not be called
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_show_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
],
test_item=t_1
)
# THEN: Only t_1.set_shutter_open() should be called
t_1.set_shutter_open.assert_called_once()
t_2.set_shutter_open.assert_not_called()
t_3.set_shutter_open.assert_not_called()
def test_on_show_projector_one_item(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_show_projector,
effect=[{'item': t_1, 'select': False},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: Only t_3.set_shutter_open() should be called
t_1.set_shutter_open.assert_not_called()
t_2.set_shutter_open.assert_not_called()
t_3.set_shutter_open.assert_called_once()
def test_on_show_projector_multiple_items(projector_manager_mtdb):
"""
Test calling method using projector_list_widget with more than one item selected
"""
# GIVEN: Test setup
t_1 = FakePJLink(Projector(**TEST1_DATA))
t_2 = FakePJLink(Projector(**TEST2_DATA))
t_3 = FakePJLink(Projector(**TEST3_DATA))
# WHEN: called
helper_method(test_fixture=projector_manager_mtdb,
method=projector_manager_mtdb.on_show_projector,
effect=[{'item': t_1, 'select': True},
{'item': t_2, 'select': False},
{'item': t_3, 'select': True}
]
)
# THEN: t_1 and t_3 set_shutter_open() should be called
t_1.set_shutter_open.assert_called_once()
t_2.set_shutter_open.assert_not_called()
t_3.set_shutter_open.assert_called_once()

View File

@ -0,0 +1,141 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2022 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Test methods called by toolbar icons with minimal flow paths and tests
on_add_projector()
on_doubleclick_item()
on_edit_input()
"""
import logging
import openlp.core.projectors.manager
from unittest.mock import DEFAULT, patch
from openlp.core.projectors.constants import QSOCKET_STATE, \
S_CONNECTED, S_NOT_CONNECTED
from tests.helpers.projector import FakePJLink
test_module = openlp.core.projectors.manager.__name__
def test_on_add_projector(projector_manager):
"""
Test add new projector edit GUI is called properly
"""
# GIVEN: Test environment
# Mock to keep from getting event not registered error in Registry()
# during bootstrap_post_set_up()
with patch.multiple(projector_manager,
udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT):
projector_manager.bootstrap_initialise()
projector_manager.bootstrap_post_set_up()
# Have to wait for projector_manager.bootstrap_post_set_up() before projector_form is initialized
with patch.object(projector_manager, 'projector_form') as mock_form:
# WHEN called
projector_manager.on_add_projector()
# THEN: projector form called
mock_form.exec.assert_called_once()
def test_on_edit_input(projector_manager, pjlink):
"""
Test on_edit_input calls on_select_input properly
"""
# GIVEN: Test setup
with patch.object(projector_manager, 'on_select_input') as mock_input:
# WHEN: Called with pjlink instance
projector_manager.on_edit_input(opt=pjlink)
# THEN: appropriate call made
mock_input.assert_called_with(opt=pjlink, edit=True)
def test_on_doubleclick_item_connected(projector_manager_mtdb, caplog):
"""
Test projector.connect_to_host() not called when status is S_CONNECTED
"""
t_1 = FakePJLink()
caplog.set_level(logging.DEBUG)
logs = [(test_module, logging.DEBUG,
f'ProjectorManager: "{t_1.pjlink.name}" already connected - skipping')]
with patch.multiple(projector_manager_mtdb,
udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT,
update_icons=DEFAULT,
_add_projector=DEFAULT) as mock_manager:
projector_manager_mtdb.bootstrap_initialise()
# projector_list_widget created here
projector_manager_mtdb.bootstrap_post_set_up()
# Add ProjectorItem instances to projector_list_widget
mock_manager['_add_projector'].return_value = t_1
projector_manager_mtdb.add_projector(projector=t_1)
# WHEN: Called
t_1.state.return_value = QSOCKET_STATE[S_CONNECTED]
caplog.clear()
projector_manager_mtdb.on_doubleclick_item(projector_manager_mtdb.projector_list_widget.item(0))
assert caplog.record_tuples == logs, 'Invalid log entries'
t_1.connect_to_host.assert_not_called()
def test_on_doubleclick_item_not_connected(projector_manager_mtdb, caplog):
"""
Test projector.connect_to_host() called
"""
t_1 = FakePJLink()
caplog.set_level(logging.DEBUG)
logs = [(test_module, logging.DEBUG,
f'ProjectorManager: "{t_1.pjlink.name}" calling connect_to_host()')]
with patch.multiple(projector_manager_mtdb,
udp_listen_add=DEFAULT,
udp_listen_delete=DEFAULT,
update_icons=DEFAULT,
_add_projector=DEFAULT) as mock_manager:
projector_manager_mtdb.bootstrap_initialise()
# projector_list_widget created here
projector_manager_mtdb.bootstrap_post_set_up()
# Add ProjectorItem instances to projector_list_widget
mock_manager['_add_projector'].return_value = t_1
projector_manager_mtdb.add_projector(projector=t_1)
# WHEN: Called
t_1.state.return_value = QSOCKET_STATE[S_NOT_CONNECTED]
caplog.clear()
projector_manager_mtdb.on_doubleclick_item(projector_manager_mtdb.projector_list_widget.item(0))
assert caplog.record_tuples == logs, 'Invalid log entries'
t_1.connect_to_host.assert_called_once()

View File

@ -35,7 +35,8 @@ TEST_HASH = '5d8409bc1c3fa39749434aa3a5c38682'
TEST_CONNECT_AUTHENTICATE = 'PJLink 1 {salt}'.format(salt=TEST_SALT) TEST_CONNECT_AUTHENTICATE = 'PJLink 1 {salt}'.format(salt=TEST_SALT)
TEST1_DATA = dict(ip='111.111.111.111', TEST1_DATA = dict(id=1,
ip='111.111.111.111',
port='1111', port='1111',
pin='1111', pin='1111',
name='___TEST_ONE___', name='___TEST_ONE___',
@ -47,7 +48,8 @@ TEST1_DATA = dict(ip='111.111.111.111',
model_lamp='Lamp type 1', model_lamp='Lamp type 1',
mac_adx='11:11:11:11:11:11') mac_adx='11:11:11:11:11:11')
TEST2_DATA = dict(ip='222.222.222.222', TEST2_DATA = dict(id=2,
ip='222.222.222.222',
port='2222', port='2222',
pin='2222', pin='2222',
name='___TEST_TWO___', name='___TEST_TWO___',
@ -59,7 +61,8 @@ TEST2_DATA = dict(ip='222.222.222.222',
model_lamp='Lamp type 2', model_lamp='Lamp type 2',
mac_adx='22:22:22:22:22:22') mac_adx='22:22:22:22:22:22')
TEST3_DATA = dict(ip='333.333.333.333', TEST3_DATA = dict(id=3,
ip='333.333.333.333',
port='3333', port='3333',
pin='3333', pin='3333',
name='___TEST_THREE___', name='___TEST_THREE___',