diff --git a/openlp/core/projectors/manager.py b/openlp/core/projectors/manager.py index cce66e32b..2db1d2966 100644 --- a/openlp/core/projectors/manager.py +++ b/openlp/core/projectors/manager.py @@ -332,9 +332,9 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM NOTE: Check if PJLinkUDP port needs to be started when adding """ if port in self.pjlink_udp: - log.warning('UDP Listener for port {port} already added - skipping'.format(port=port)) + log.warning(f'UDP Listener for port {port} already added - skipping') else: - log.debug('Adding UDP listener on port {port}'.format(port=port)) + log.debug(f'Adding UDP listener on port {port}') self.pjlink_udp[port] = PJLinkUDP(port=port) Registry().execute('udp_broadcast_add', port=port, callback=self.pjlink_udp[port].check_settings) @@ -344,20 +344,20 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM NOTE: Check if PJLinkUDP port needs to be closed/stopped when deleting """ - log.debug('Checking for UDP port {port} listener deletion'.format(port=port)) + log.debug(f'Checking for UDP port {port} listener deletion') if port not in self.pjlink_udp: - log.warning('UDP listener for port {port} not there - skipping delete'.format(port=port)) + log.warning(f'UDP listener for port {port} not there - skipping delete') return keep_port = False for item in self.projector_list: if port == item.link.port: keep_port = True if keep_port: - log.warning('UDP listener for port {port} needed for other projectors - skipping delete'.format(port=port)) + log.warning(f'UDP listener for port {port} needed for other projectors - skipping delete') return Registry().execute('udp_broadcast_remove', port=port) del self.pjlink_udp[port] - log.debug('UDP listener for port {port} deleted'.format(port=port)) + log.debug(f'UDP listener for port {port} deleted') def get_settings(self): """ @@ -382,7 +382,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM real_projector = item.data(QtCore.Qt.UserRole) projector_name = str(item.text()) visible = real_projector.link.status_connect >= S_CONNECTED - log.debug('({name}) Building menu - visible = {visible}'.format(name=projector_name, visible=visible)) + log.debug(f'({projector_name}) Building menu - visible = {visible}') self.delete_action.setVisible(True) self.edit_action.setVisible(True) self.connect_action.setVisible(not visible) @@ -424,7 +424,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM projectordb=self.projectordb, edit=edit) source = source_select_form.exec(projector.link) - log.debug('({ip}) source_select_form() returned {data}'.format(ip=projector.link.ip, data=source)) + log.debug(f'({projector.link.ip}) source_select_form() returned {source}') if source is not None and source > 0: projector.link.set_input_source(str(source)) return @@ -465,10 +465,10 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM projector = item.data(QtCore.Qt.UserRole) if QSOCKET_STATE[projector.link.state()] != S_CONNECTED: try: - log.debug('ProjectorManager: Calling connect_to_host() on "{ip}"'.format(ip=projector.link.ip)) + log.debug(f'ProjectorManager: Calling connect_to_host() on "{projector.link.ip}"') projector.link.connect_to_host() except Exception: - log.debug('ProjectorManager: "{ip}" already connected - skipping'.format(ip=projector.link.ip)) + log.debug(f'ProjectorManager: "{projector.link.ip}" already connected - skipping') return def on_connect_projector(self, opt=None): @@ -501,9 +501,9 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM projector = list_item.data(QtCore.Qt.UserRole) msg = QtWidgets.QMessageBox() msg.setText(translate('OpenLP.ProjectorManager', - 'Delete projector ({ip}) {name}?'.format(ip=projector.link.ip, - name=projector.link.name))) - msg.setInformativeText(translate('OpenLP.ProjectorManager', 'Are you sure you want to delete this projector?')) + f'Delete projector ({projector.link.ip}) {projector.link.name}?')) + msg.setInformativeText(translate('OpenLP.ProjectorManager', + 'Are you sure you want to delete this projector?')) msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) ans = msg.exec() @@ -541,16 +541,16 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM new_list = [] for item in self.projector_list: if item.link.db_item.id == projector.link.db_item.id: - log.debug('Removing projector "{item}"'.format(item=item.link.name)) + log.debug(f'Removing projector "{item.link.name}"') continue new_list.append(item) self.projector_list = new_list list_item = self.projector_list_widget.takeItem(self.projector_list_widget.currentRow()) list_item = None if not self.projectordb.delete_projector(projector.db_item): - log.warning('Delete projector {item} failed'.format(item=projector.db_item)) + log.warning(f'Delete projector {projector.db_item} failed') for item in self.projector_list: - log.debug('New projector list - item: {ip} {name}'.format(ip=item.link.ip, name=item.link.name)) + log.debug(f'New projector list - item: {item.link.ip} {item.link.name}') self.udp_listen_delete(old_port) def on_disconnect_projector(self, opt=None): @@ -760,7 +760,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM if start: item.link.connect_to_host() for item in self.projector_list: - log.debug('New projector list - item: ({ip}) {name}'.format(ip=item.link.ip, name=item.link.name)) + log.debug(f'New projector list - item: ({item.link.ip}) {item.link.name}') @QtCore.pyqtSlot(str) def add_projector_from_wizard(self, ip, opts=None): @@ -770,7 +770,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM :param ip: IP address of new record item to find :param opts: Needed by PyQt5 """ - log.debug('add_projector_from_wizard(ip={ip})'.format(ip=ip)) + log.debug(f'add_projector_from_wizard(ip={ip})') item = self.projectordb.get_projector_by_ip(ip) self.add_projector(item) @@ -781,7 +781,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM :param projector: Projector() instance of projector with updated information """ - log.debug('edit_projector_from_wizard(ip={ip})'.format(ip=projector.ip)) + log.debug(f'edit_projector_from_wizard(ip={projector.ip})') old_port = self.old_projector.link.port old_ip = self.old_projector.link.ip self.old_projector.link.name = projector.name @@ -836,22 +836,22 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM item = list_item break if item is None: - log.error('ProjectorManager: Unknown item "{ip}" - not updating status'.format(ip=ip)) + log.error(f'ProjectorManager: Unknown item "{ip}" - not updating status') return elif item.status == status: - log.debug('ProjectorManager: No status change for "{ip}" - not updating status'.format(ip=ip)) + log.debug(f'ProjectorManager: No status change for "{ip}" - not updating status') return item.status = status item.icon = self.status_icons[status] - log.debug('({name}) Updating icon with {code}'.format(name=item.link.name, code=STATUS_CODE[status])) + log.debug(f'({item.link.name}) Updating icon with {STATUS_CODE[status]}') item.widget.setIcon(item.icon) return self.update_icons() def get_toolbar_item(self, name, enabled=False, hidden=False): item = self.one_toolbar.findChild(QtWidgets.QAction, name) if item == 0: - log.debug('No item found with name "{name}"'.format(name=name)) + log.debug(f'No item found with name "{name}"') return item.setVisible(False if hidden else True) item.setEnabled(True if enabled else False) diff --git a/tests/openlp_core/projectors/__init__.py b/tests/openlp_core/projectors/__init__.py deleted file mode 100644 index 9c7075eb1..000000000 --- a/tests/openlp_core/projectors/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- 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 . # -########################################################################## -""" -:mod tests/openlp_core/projectors: Tests for projector code -""" diff --git a/tests/openlp_core/projectors/commands/__init__.py b/tests/openlp_core/projectors/commands/__init__.py deleted file mode 100644 index 61818bcc0..000000000 --- a/tests/openlp_core/projectors/commands/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -# -*- 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 . # -########################################################################## -""" -:mod tests/openlp_core/projectors/commands: Tests for PJLink commands -""" diff --git a/tests/openlp_core/projectors/commands/test_misc.py b/tests/openlp_core/projectors/commands/test_misc_commands.py similarity index 100% rename from tests/openlp_core/projectors/commands/test_misc.py rename to tests/openlp_core/projectors/commands/test_misc_commands.py diff --git a/tests/openlp_core/projectors/conftest.py b/tests/openlp_core/projectors/conftest.py index 08b55ca32..f06c706e8 100644 --- a/tests/openlp_core/projectors/conftest.py +++ b/tests/openlp_core/projectors/conftest.py @@ -19,12 +19,63 @@ # along with this program. If not, see . # ########################################################################## """ -Package to test the openlp.core.projectors.pjlink command routing. +Fixtures for projector tests """ import pytest -from openlp.core.projectors.db import Projector + +from unittest.mock import patch + +from openlp.core.projectors.db import Projector, ProjectorDB +from openlp.core.projectors.manager import ProjectorManager from openlp.core.projectors.pjlink import PJLink -from tests.resources.projector.data import TEST1_DATA +from tests.resources.projector.data import TEST_DB, TEST1_DATA + +''' +NOTE: Since Registry is a singleton, sleight of hand allows us to verify + calls to Registry.methods() + +@patch(path.to.imported.Registry) +def test_function(mock_registry): + mocked_registry = MagicMock() + mock_registry.return_value = mocked_registry + ... + assert mocked_registry.method.has_call(...) +''' + + +@pytest.fixture() +def projector_manager(settings): + with patch('openlp.core.projectors.db.init_url') as mocked_init_url: + mocked_init_url.return_value = 'sqlite:///%s' % TEST_DB + projectordb = ProjectorDB() + proj_manager = ProjectorManager(projectordb=projectordb) + yield proj_manager + projectordb.session.close() + del proj_manager + + +@pytest.fixture() +def projector_manager_nodb(settings): + proj_manager = ProjectorManager(projectordb=None) + yield proj_manager + del proj_manager + + +@pytest.fixture() +def projector_manager_mtdb(settings): + with patch('openlp.core.projectors.db.init_url') as mock_url: + mock_url.return_value = 'sqlite:///%s' % TEST_DB + t_db = ProjectorDB() + # Ensure we have an empty DB at the beginning of the test + for itm in t_db.get_projector_all(): + t_db.delete_projector(itm) + t_db.session.commit() + + t_manager = ProjectorManager(projectordb=t_db) + yield t_manager + t_db.session.close() + del t_db + del t_manager @pytest.fixture() diff --git a/tests/openlp_core/projectors/manager/test_boostrap.py b/tests/openlp_core/projectors/manager/test_boostrap.py new file mode 100644 index 000000000..284e686d8 --- /dev/null +++ b/tests/openlp_core/projectors/manager/test_boostrap.py @@ -0,0 +1,153 @@ +# -*- 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 . # +########################################################################## +""" +Test ProjectorManager.bootstrap_* methods +""" +import logging +import openlp.core.projectors.manager + +from unittest.mock import DEFAULT, MagicMock, patch + +test_module = openlp.core.projectors.manager.__name__ + + +@patch('openlp.core.projectors.manager.ProjectorDB') +def test_bootstrap_initialise(mock_db, projector_manager, caplog): + """ + Test ProjectorManager initializes with existing ProjectorDB instance + """ + # GIVEN: Test setup + caplog.set_level(logging.DEBUG) + logs = [(test_module, logging.DEBUG, 'Using existing ProjectorDB() instance')] + + with patch.multiple(projector_manager, + setup_ui=DEFAULT, + get_settings=DEFAULT) as mock_manager: + + # WHEN: we call bootstrap_initialise + caplog.clear() + projector_manager.bootstrap_initialise() + + # THEN: Appropriate entries and actions + assert caplog.record_tuples == logs, 'Invalid log entries' + mock_manager['setup_ui'].assert_called_once() + mock_manager['get_settings'].assert_called_once() + mock_db.assert_not_called() + + +@patch('openlp.core.projectors.manager.ProjectorDB') +def test_bootstrap_initialise_nodb(mock_db, projector_manager_nodb, caplog): + """ + Test ProjectorManager initializes with a new ProjectorDB instance + """ + # GIVEN: Test setup + caplog.set_level(logging.DEBUG) + logs = [(test_module, logging.DEBUG, 'Creating new ProjectorDB() instance')] + + with patch.multiple(projector_manager_nodb, + setup_ui=DEFAULT, + get_settings=DEFAULT) as mock_manager: + + # WHEN: we call bootstrap_initialise + caplog.clear() + projector_manager_nodb.bootstrap_initialise() + + # THEN: Appropriate entries and actions + assert caplog.record_tuples == logs, 'Invalid log entries' + mock_manager['setup_ui'].assert_called_once() + mock_manager['get_settings'].assert_called_once() + mock_db.assert_called_once() + + +@patch('openlp.core.projectors.manager.ProjectorEditForm') +@patch('openlp.core.projectors.manager.QtCore.QTimer') +def test_bootstrap_post_set_up_autostart_false(mock_timer, mocked_edit, projector_manager, settings, caplog): + """ + Test post-initialize calls proper setups + """ + # GIVEN: Test setup + caplog.set_level(logging.DEBUG) + logs = [(test_module, logging.DEBUG, 'Loading all projectors')] + + mock_newProjector = MagicMock() + mock_editProjector = MagicMock() + mock_edit = MagicMock() + mock_edit.newProjector = mock_newProjector + mock_edit.editProjector = mock_editProjector + mocked_edit.return_value = mock_edit + + settings.setValue('projector/connect on start', False) + projector_manager.bootstrap_initialise() + + with patch.multiple(projector_manager, + _load_projectors=DEFAULT, + projector_list_widget=DEFAULT) as mock_manager: + + # WHEN: Call to initialize is run + caplog.clear() + projector_manager.bootstrap_post_set_up() + + # THEN: verify calls and logs + mock_timer.assert_not_called() + mock_newProjector.connect.assert_called_once() + mock_editProjector.connect.assert_called_once() + mock_manager['_load_projectors'].assert_called_once(), + mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once() + assert caplog.record_tuples == logs, 'Invalid log entries' + + +@patch('openlp.core.projectors.manager.ProjectorEditForm') +@patch('openlp.core.projectors.manager.QtCore.QTimer') +def test_bootstrap_post_set_up_autostart_true(mock_timer, mocked_edit, projector_manager, settings, caplog): + """ + Test post-initialize calls proper setups + """ + # GIVEN: Test setup + caplog.set_level(logging.DEBUG) + logs = [(test_module, logging.DEBUG, 'Delaying 1.5 seconds before loading all projectors')] + + mock_newProjector = MagicMock() + mock_editProjector = MagicMock() + mock_edit = MagicMock() + mock_edit.newProjector = mock_newProjector + mock_edit.editProjector = mock_editProjector + + settings.setValue('projector/connect on start', True) + projector_manager.bootstrap_initialise() + + with patch.multiple(projector_manager, + _load_projectors=DEFAULT, + projector_list_widget=DEFAULT) as mock_manager: + mocked_edit.return_value = mock_edit + + # WHEN: Call to initialize is run + caplog.clear() + projector_manager.bootstrap_post_set_up() + + # THEN: verify calls and logs + mock_timer.assert_called_once() + mock_timer.return_value.singleShot.assert_called_once_with(1500, projector_manager._load_projectors) + + mock_newProjector.connect.assert_called_once() + mock_editProjector.connect.assert_called_once() + mock_manager['_load_projectors'].assert_not_called(), + mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once() + assert caplog.record_tuples == logs, 'Invalid log entries' diff --git a/tests/openlp_core/projectors/manager/test_misc_manager.py b/tests/openlp_core/projectors/manager/test_misc_manager.py new file mode 100644 index 000000000..cec3200d8 --- /dev/null +++ b/tests/openlp_core/projectors/manager/test_misc_manager.py @@ -0,0 +1,157 @@ +# -*- 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 . # +########################################################################## +""" +Test misc. functions with few test paths +""" +from unittest.mock import DEFAULT, patch + +from openlp.core.projectors.db import Projector + +from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA + + +def test_private_load_projectors(projector_manager_mtdb): + """ + Test that _load_projectors() retrieves all entries from projector database + """ + # GIVEN: Test environment + t_db = projector_manager_mtdb.projectordb # Shortcut helper + for itm in (TEST1_DATA, TEST2_DATA, TEST3_DATA): + t_db.add_projector(Projector(**itm)) + t_db.session.commit() + + t_list = t_db.get_projector_all() + + # Mock to keep from getting event not registered error in Registry() + # during bootstrap_post_set_up() + # Although we're testing _load_projectors, need to mock + # it first to get past bootstrap_post_set_up() before test + with patch.multiple(projector_manager_mtdb, + udp_listen_add=DEFAULT, + udp_listen_delete=DEFAULT, + _load_projectors=DEFAULT) as mock_manager: + # Satisfy Flake8 linting + mock_manager['udp_listen_add'].return_value = None + projector_manager_mtdb.bootstrap_initialise() + projector_manager_mtdb.bootstrap_post_set_up() + + # WHEN: Called + projector_manager_mtdb._load_projectors() + + assert len(projector_manager_mtdb.projector_list) == len(t_list), \ + 'Invalid number of entries between check and list' + + # Isolate the DB entries used to create projector_manager.projector_list + t_chk = [] + for dbitem in projector_manager_mtdb.projector_list: + t_chk.append(dbitem.db_item) + + 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): + """ + Test when add projector from GUI, appropriate method is called correctly + """ + # GIVEN: Test environment + with patch.multiple(projector_manager, + projectordb=DEFAULT, + add_projector=DEFAULT) as mock_manager: + t_item = Projector(**TEST1_DATA) + + mock_manager['projectordb'].get_projector_by_ip.return_value = t_item + + # WHEN: Called + projector_manager.add_projector_from_wizard(ip=t_item.ip) + + # THEN: appropriate calls made + mock_manager['add_projector'].assert_called_with(t_item) + + +def test_get_projector_list(projector_manager_mtdb): + """ + Test get_projector_list() returns valid entries + """ + # GIVEN: Test environment + t_db = projector_manager_mtdb.projectordb # Shortcut helper + for itm in (TEST1_DATA, TEST2_DATA, TEST3_DATA): + t_db.add_projector(Projector(**itm)) + t_list = t_db.get_projector_all() + + # Mock to keep from getting event not registered error in Registry() + # during bootstrap_post_set_up() + with patch.multiple(projector_manager_mtdb, + udp_listen_add=DEFAULT, + udp_listen_delete=DEFAULT) as mock_manager: + # Satisfy Flake8 linting + mock_manager['udp_listen_add'].return_value = None + projector_manager_mtdb.bootstrap_initialise() + projector_manager_mtdb.bootstrap_post_set_up() + + # WHEN: Called + t_chk = projector_manager_mtdb.get_projector_list() + + # THEN: DB items for both t_list and projector_list are the same + assert len(t_chk) == len(t_list), 'projector_list length mismatch with test items length' + + # Isolate the DB entries used to create projector_manager.projector_list + t_chk_list = [] + for dbitem in t_chk: + t_chk_list.append(dbitem.db_item) + assert t_list == t_chk_list, 'projector_list DB items do not match test items' diff --git a/tests/openlp_core/projectors/manager/test_udp.py b/tests/openlp_core/projectors/manager/test_udp.py new file mode 100644 index 000000000..37a20d5ab --- /dev/null +++ b/tests/openlp_core/projectors/manager/test_udp.py @@ -0,0 +1,133 @@ +# -*- 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 . # +########################################################################## +""" +Test ProjectorManager udp methods +""" +import logging + +from unittest.mock import MagicMock, patch + + +from tests.resources.projector.data import FakePJLinkUDP, FakeProjector + + +def test_udp_listen_add_duplicate(projector_manager, caplog): + """ + Test adding UDP port listener to port already registered + """ + # GIVEN: Initial setup + caplog.set_level(logging.DEBUG) + log_entries = ['UDP Listener for port 10 already added - skipping'] + port_list = {10: 'port1', 20: 'port2'} + projector_manager.pjlink_udp = port_list + + # WHEN: udp_listen_add is called with duplicate port number + caplog.clear() + projector_manager.udp_listen_add(port=10) + + # THEN: Verify log entry and registry entry not called + assert projector_manager.pjlink_udp == port_list, "Invalid ports in list" + assert caplog.messages == log_entries, "Invalid log entries" + + +@patch('openlp.core.projectors.manager.PJLinkUDP') +@patch('openlp.core.projectors.manager.Registry') +def test_udp_listen_add_new(mock_registry, mock_udp, projector_manager, caplog): + """ + Test adding new UDP port listener + """ + # GIVEN: Initial setup + caplog.set_level(logging.DEBUG) + mocked_registry = MagicMock() + mock_registry.return_value = mocked_registry + mock_udp.return_value = FakePJLinkUDP() + log_entries = ['Adding UDP listener on port 20'] + projector_manager.pjlink_udp = {10: 'port1'} + # WHEN: Adding new listener + caplog.clear() + projector_manager.udp_listen_add(port=20) + # THEN: Appropriate listener and log entries + assert 20 in projector_manager.pjlink_udp, "Port not added" + assert 2 == len(projector_manager.pjlink_udp), "Invalid ports in list" + assert type(projector_manager.pjlink_udp[20]) == FakePJLinkUDP, \ + 'PJLinkUDP instance should have been added' + assert mocked_registry.execute.has_call('udp_broadcast_add', port=20) + assert caplog.messages == log_entries, 'Invalid log entries' + + +def test_udp_listen_delete_missing(projector_manager, caplog): + """ + Test deleting UDP port listener not in list + """ + # GIVEN: Initial setup + caplog.set_level(logging.DEBUG) + projector_manager.pjlink_udp = {10: 'port1'} + log_entries = ['Checking for UDP port 20 listener deletion', + 'UDP listener for port 20 not there - skipping delete'] + # WHEN: Deleting port listener from dictinary + projector_manager.udp_listen_delete(port=20) + # THEN: Log missing port and exit method + assert projector_manager.pjlink_udp == {10: 'port1'}, "Invalid ports in list" + assert caplog.messages == log_entries, "Invalid log entries" + + +@patch('openlp.core.projectors.manager.Registry') +def test_udp_listen_delete_single(mock_registry, projector_manager, caplog): + """ + Test deleting UDP listener + """ + # GIVEN: Initial setup + mocked_registry = MagicMock() + mock_registry.return_value = mocked_registry + caplog.set_level(logging.DEBUG) + log_entries = ['Checking for UDP port 10 listener deletion', + 'UDP listener for port 10 deleted'] + port_list = {20: 'port2'} + projector_manager.pjlink_udp = {10: 'port1', **port_list} + projector_manager.projector_list = [FakeProjector(port=20)] + # WHEN: deleting a listener + caplog.clear() + projector_manager.udp_listen_delete(port=10) + # THEN: pjlink_udp and logs should have appropriate entries + assert caplog.messages == log_entries, 'Invalid log entries' + assert projector_manager.pjlink_udp == port_list, 'Invalid ports in list' + assert mocked_registry.execute.has_call('udp_broadcast_delete', port=10) + + +def test_udp_listen_delete_skip(projector_manager, caplog): + """ + Test not deleting UDP listener + """ + # GIVEN: Initial setup + caplog.set_level(logging.DEBUG) + log_entries = ['Checking for UDP port 10 listener deletion', + 'UDP listener for port 10 needed for other projectors - skipping delete'] + port_list = {10: 'port1', 20: 'port2'} + projector_manager.pjlink_udp = port_list + projector_manager.projector_list = [FakeProjector(port=10), + FakeProjector(port=20)] + # WHEN: deleting a listener + caplog.clear() + projector_manager.udp_listen_delete(port=10) + + # THEN: pjlink_udp and logs should have appropriate entries + assert caplog.messages == log_entries, 'Invalid log entries' + assert projector_manager.pjlink_udp == port_list, 'Invalid ports in list' diff --git a/tests/openlp_core/projectors/test_projectormanager.py b/tests/openlp_core/projectors/test_projectormanager.py deleted file mode 100644 index e303d1968..000000000 --- a/tests/openlp_core/projectors/test_projectormanager.py +++ /dev/null @@ -1,251 +0,0 @@ -# -*- 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 . # -########################################################################## -""" -Interface tests to test the themeManager class and related methods. -""" - -import pytest -import logging - -from unittest.mock import MagicMock, patch - -from openlp.core.projectors.db import ProjectorDB -from openlp.core.projectors.editform import ProjectorEditForm -from openlp.core.projectors.manager import ProjectorManager -from tests.resources.projector.data import TEST_DB - -''' -NOTE: Since Registry is a singleton, sleight of hand allows us to verify - calls to Registry.methods() - -@patch(path.to.imported.Registry) -def test_function(mock_registry): - mocked_registry = MagicMock() - mock_registry.return_value = mocked_registry - ... - assert mocked_registry.method.has_call(...) -''' - - -class FakeProjector(object): - """ - Helper test class - """ - def __init__(self, port=4352, name="Faker"): - self.link = self - self.port = port - self.name = name - - -class FakePJLinkUDP(object): - """ - Helper test class - """ - def __init__(self, *args, **kwargs): - pass - - def check_settings(self, *args, **kwargs): - pass - - -@pytest.fixture() -def projector_manager(settings): - with patch('openlp.core.projectors.db.init_url') as mocked_init_url: - mocked_init_url.return_value = 'sqlite:///%s' % TEST_DB - projectordb = ProjectorDB() - proj_manager = ProjectorManager(projectordb=projectordb) - yield proj_manager - projectordb.session.close() - del proj_manager - - -@pytest.fixture() -def projector_manager_nodb(settings): - proj_manager = ProjectorManager(projectordb=None) - yield proj_manager - del proj_manager - - -def test_bootstrap_initialise(projector_manager): - """ - Test initialize calls correct startup functions - """ - # WHEN: we call bootstrap_initialise - projector_manager.bootstrap_initialise() - # THEN: ProjectorDB is setup - assert type(projector_manager.projectordb) == ProjectorDB, \ - 'Initialization should have created a ProjectorDB() instance' - - -def test_bootstrap_initialise_nodb(projector_manager_nodb, caplog): - """ - Test log entry creating new projector DB - """ - caplog.set_level(logging.DEBUG) - log_entries = 'Creating new ProjectorDB() instance' - # WHEN: ProjectorManager created with no DB set - caplog.clear() - projector_manager_nodb.bootstrap_initialise() - # THEN: Log should indicate new DB being created - assert caplog.messages[3] == log_entries, "ProjectorManager should have indicated a new DB being created" - - -def test_bootstrap_post_set_up(projector_manager): - """ - Test post-initialize calls proper setups - """ - # GIVEN: setup mocks - projector_manager._load_projectors = MagicMock() - - # WHEN: Call to initialize is run - projector_manager.bootstrap_initialise() - projector_manager.bootstrap_post_set_up() - - # THEN: verify calls to retrieve saved projectors and edit page initialized - assert 1 == projector_manager._load_projectors.call_count, \ - 'Initialization should have called load_projectors()' - assert type(projector_manager.projector_form) == ProjectorEditForm, \ - 'Initialization should have created a Projector Edit Form' - assert projector_manager.projectordb is projector_manager.projector_form.projectordb, \ - 'ProjectorEditForm should be using same ProjectorDB() instance as ProjectorManager' - - -def test_bootstrap_post_set_up_autostart_projector(projector_manager_nodb, caplog): - """ - Test post-initialize calling log and QTimer on autostart - """ - # GIVEN: Setup mocks - with patch('openlp.core.projectors.manager.QtCore.QTimer.singleShot') as mock_timer: - caplog.set_level(logging.DEBUG) - log_entries = 'Delaying 1.5 seconds before loading all projectors' - # WHEN: Initializations called - projector_manager_nodb.bootstrap_initialise() - projector_manager_nodb.autostart = True - projector_manager_nodb.bootstrap_post_set_up() - - # THEN: verify log entries and timer calls - mock_timer.assert_called_once_with(1500, projector_manager_nodb._load_projectors) - assert caplog.messages[-1] == log_entries, "Invalid log entries" - - -def test_udp_listen_add_duplicate(projector_manager_nodb, caplog): - """ - Test adding UDP port listener to port already registered - """ - # GIVEN: Initial setup - caplog.set_level(logging.DEBUG) - log_entries = ['UDP Listener for port 10 already added - skipping'] - port_list = {10: 'port1', 20: 'port2'} - projector_manager_nodb.pjlink_udp = port_list - - # WHEN: udp_listen_add is called with duplicate port number - caplog.clear() - projector_manager_nodb.udp_listen_add(port=10) - - # THEN: Verify log entry and registry entry not called - assert projector_manager_nodb.pjlink_udp == port_list, "Invalid ports in list" - assert caplog.messages == log_entries, "Invalid log entries" - - -@patch('openlp.core.projectors.manager.PJLinkUDP') -@patch('openlp.core.projectors.manager.Registry') -def test_udp_listen_add_new(mock_registry, mock_udp, projector_manager_nodb, caplog): - """ - Test adding new UDP port listener - """ - # GIVEN: Initial setup - caplog.set_level(logging.DEBUG) - mocked_registry = MagicMock() - mock_registry.return_value = mocked_registry - mock_udp.return_value = FakePJLinkUDP() - log_entries = ['Adding UDP listener on port 20'] - projector_manager_nodb.pjlink_udp = {10: 'port1'} - # WHEN: Adding new listener - caplog.clear() - projector_manager_nodb.udp_listen_add(port=20) - # THEN: Appropriate listener and log entries - assert 20 in projector_manager_nodb.pjlink_udp, "Port not added" - assert 2 == len(projector_manager_nodb.pjlink_udp), "Invalid ports in list" - assert type(projector_manager_nodb.pjlink_udp[20]) == FakePJLinkUDP, \ - 'PJLinkUDP instance should have been added' - assert mocked_registry.execute.has_call('udp_broadcast_add', port=20) - assert caplog.messages == log_entries, 'Invalid log entries' - - -def test_udp_listen_delete_missing(projector_manager_nodb, caplog): - """ - Test deleting UDP port listener not in list - """ - # GIVEN: Initial setup - caplog.set_level(logging.DEBUG) - projector_manager_nodb.pjlink_udp = {10: 'port1'} - log_entries = ['Checking for UDP port 20 listener deletion', - 'UDP listener for port 20 not there - skipping delete'] - # WHEN: Deleting port listener from dictinary - projector_manager_nodb.udp_listen_delete(port=20) - # THEN: Log missing port and exit method - assert projector_manager_nodb.pjlink_udp == {10: 'port1'}, "Invalid ports in list" - assert caplog.messages == log_entries, "Invalid log entries" - - -@patch('openlp.core.projectors.manager.Registry') -def test_udp_listen_delete_single(mock_registry, projector_manager_nodb, caplog): - """ - Test deleting UDP listener - """ - # GIVEN: Initial setup - mocked_registry = MagicMock() - mock_registry.return_value = mocked_registry - caplog.set_level(logging.DEBUG) - log_entries = ['Checking for UDP port 10 listener deletion', - 'UDP listener for port 10 deleted'] - port_list = {20: 'port2'} - projector_manager_nodb.pjlink_udp = {10: 'port1', **port_list} - projector_manager_nodb.projector_list = [FakeProjector(port=20)] - # WHEN: deleting a listener - caplog.clear() - projector_manager_nodb.udp_listen_delete(port=10) - # THEN: pjlink_udp and logs should have appropriate entries - assert caplog.messages == log_entries, 'Invalid log entries' - assert projector_manager_nodb.pjlink_udp == port_list, 'Invalid ports in list' - assert mocked_registry.execute.has_call('udp_broadcast_delete', port=10) - - -def test_udp_listen_delete_skip(projector_manager_nodb, caplog): - """ - Test not deleting UDP listener - """ - # GIVEN: Initial setup - caplog.set_level(logging.DEBUG) - log_entries = ['Checking for UDP port 10 listener deletion', - 'UDP listener for port 10 needed for other projectors - skipping delete'] - port_list = {10: 'port1', 20: 'port2'} - projector_manager_nodb.pjlink_udp = port_list - projector_manager_nodb.projector_list = [FakeProjector(port=10), - FakeProjector(port=20)] - # WHEN: deleting a listener - caplog.clear() - projector_manager_nodb.udp_listen_delete(port=10) - print(projector_manager_nodb.pjlink_udp) - print(caplog.record_tuples) - # THEN: pjlink_udp and logs should have appropriate entries - assert caplog.messages == log_entries, 'Invalid log entries' - assert projector_manager_nodb.pjlink_udp == port_list, 'Invalid ports in list' diff --git a/tests/resources/projector/data.py b/tests/resources/projector/data.py index bd76437ca..33e208eaa 100644 --- a/tests/resources/projector/data.py +++ b/tests/resources/projector/data.py @@ -283,3 +283,24 @@ TEST_VIDEO_CODES = { '6Y': 'Internal Y', '6Z': 'Internal Z' } + + +class FakeProjector(object): + """ + Helper test class + """ + def __init__(self, port=4352, name="Faker"): + self.link = self + self.port = port + self.name = name + + +class FakePJLinkUDP(object): + """ + Helper test class + """ + def __init__(self, *args, **kwargs): + pass + + def check_settings(self, *args, **kwargs): + pass