Merge branch 'editform-tests' into 'master'

projector.editform refactoring and tests

See merge request openlp/openlp!441
This commit is contained in:
Raoul Snyman 2022-03-29 18:29:12 +00:00
commit b6135737e3
9 changed files with 956 additions and 230 deletions

View File

@ -40,6 +40,7 @@ PJLINK_SUFFIX = CR
PJLINK_SVER_MAX_LEN = 32 PJLINK_SVER_MAX_LEN = 32
PJLINK_TIMEOUT = 30.0 PJLINK_TIMEOUT = 30.0
PJLINK_TOKEN_SIZE = 8 # PJLINK 1 <token> : where <token> is 8 characters PJLINK_TOKEN_SIZE = 8 # PJLINK 1 <token> : where <token> is 8 characters
PJLINK_VALID_PORTS = range(1000, 32768)
# Error and status codes # Error and status codes
S_OK = E_OK = 0 # E_OK included since I sometimes forget S_OK = E_OK = 0 # E_OK included since I sometimes forget

View File

@ -26,9 +26,9 @@ import logging
from PyQt5 import QtCore, QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import verify_ip_address from openlp.core.common import verify_ip_address, Singleton
from openlp.core.common.i18n import translate from openlp.core.common.i18n import translate
from openlp.core.projectors.constants import PJLINK_PORT from openlp.core.projectors.constants import PJLINK_PORT, PJLINK_VALID_PORTS
from openlp.core.projectors.db import Projector from openlp.core.projectors.db import Projector
from openlp.core.ui.icons import UiIcons from openlp.core.ui.icons import UiIcons
@ -38,6 +38,82 @@ log.debug('editform loaded')
# TODO: Fix db entries for input source(s) # TODO: Fix db entries for input source(s)
_translate_group = 'OpenLP.ProjectorEditForm'
class MessageList(metaclass=Singleton):
"""
Consolidate the messages here. Simplify calls to QMessageBox.
"""
def __init__(self):
self.AddressDuplicate = {'title': translate(_translate_group, 'Duplicate Address'),
'text': translate(_translate_group,
'IP:port combination are already in the database <br /><br />'
'Please Enter a different IP:port combination.')
}
self.DatabaseError = {'title': translate(_translate_group, 'Database Error'),
'text': translate(_translate_group,
'There was an error saving projector information.<br /><br />'
'See the log for the error')
}
self.DatabaseID = {'title': translate(_translate_group, 'Database ID Error'),
'text': translate(_translate_group,
'Mismatch between this entry and the database.<br /><br />'
'See the log for possible issues.')
}
self.DatabaseMultiple = {'title': translate(_translate_group, 'Multiple Records'),
'text': translate(_translate_group,
'Multiple entries found in the database<br /><br />'
'Ensure Name and/or IP/Port data is unique')
}
self.IPBlank = {'title': translate(_translate_group, 'IP Address Not Set'),
'text': translate(_translate_group,
'You must enter an IP address.<br /><br />'
'Please enter a valid IP address.')
}
self.IPInvalid = {'title': translate(_translate_group, 'Invalid IP Address'),
'text': translate(_translate_group,
'IP address is not valid.<br /><br />'
'Please enter a valid IP address.')
}
self.NameBlank = {'title': translate(_translate_group, 'Name Not Set'),
'text': translate(_translate_group,
'You must enter a name for this entry.<br /><br />'
'Please enter a unique name for this entry.')
}
self.NameDuplicate = {'title': translate(_translate_group, 'Duplicate Name'),
'text': translate(_translate_group,
'Entries must have unique names.<br /><br />'
'Please enter a different name.')
}
self.ProjectorInvalid = {'title': translate(_translate_group, 'Invalid Projector'),
'text': translate(_translate_group,
'Projector instance not a valid PJLink or Projector Instance'
'<br /><br />See log for issue')
}
self.PortBlank = {'title': translate(_translate_group, 'Port Not Set'),
'text': translate(_translate_group,
'You must enter a port number for this entry.<br /><br />'
'Please enter a valid port number.')
}
self.PortInvalid = {'title': translate(_translate_group, 'Invalid Port Number'),
'text': translate(_translate_group,
'Port numbers below 1000 are reserved for admin use only.<br />'
'Port numbers above 32767 are not currently usable.<br /><br />'
'Please enter a valid port number between 1000 and 32767 inclusive.'
f'<br /><br />Default PJLink port is {PJLINK_PORT}')
}
@staticmethod
def show_warning(message, form=None):
"""
Display QMessageBox.warning()
"""
return QtWidgets.QMessageBox.warning(form, message['title'], message['text'])
Message = MessageList()
class Ui_ProjectorEditForm(object): class Ui_ProjectorEditForm(object):
""" """
@ -57,58 +133,61 @@ class Ui_ProjectorEditForm(object):
self.dialog_layout.setObjectName('dialog_layout') self.dialog_layout.setObjectName('dialog_layout')
self.dialog_layout.setSpacing(8) self.dialog_layout.setSpacing(8)
self.dialog_layout.setContentsMargins(8, 8, 8, 8) self.dialog_layout.setContentsMargins(8, 8, 8, 8)
# IP Address
self.ip_label = QtWidgets.QLabel(edit_projector_dialog)
self.ip_label.setObjectName('projector_edit_ip_label')
self.ip_text_edit = QtWidgets.QLineEdit(edit_projector_dialog)
self.ip_text_edit.setObjectName('projector_edit_ip_text')
self.ip_text_label = QtWidgets.QLabel(edit_projector_dialog)
self.ip_text_label.setObjectName('projector_show_ip_text')
self.dialog_layout.addWidget(self.ip_label, 0, 0)
# For new projector, use edit widget
self.dialog_layout.addWidget(self.ip_text_edit, 0, 1)
# For edit projector, use show widget
self.dialog_layout.addWidget(self.ip_text_label, 0, 1)
# Port number
self.port_label = QtWidgets.QLabel(edit_projector_dialog)
self.port_label.setObjectName('projector_edit_ip_label')
self.port_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.port_text.setObjectName('projector_edit_port_text')
self.dialog_layout.addWidget(self.port_label, 1, 0)
self.dialog_layout.addWidget(self.port_text, 1, 1)
# PIN
self.pin_label = QtWidgets.QLabel(edit_projector_dialog)
self.pin_label.setObjectName('projector_edit_pin_label')
self.pin_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.pin_label.setObjectName('projector_edit_pin_text')
self.dialog_layout.addWidget(self.pin_label, 2, 0)
self.dialog_layout.addWidget(self.pin_text, 2, 1)
# Name # Name
_row = 0 # If I decide to rearrange the layout again
self.name_label = QtWidgets.QLabel(edit_projector_dialog) self.name_label = QtWidgets.QLabel(edit_projector_dialog)
self.name_label.setObjectName('projector_edit_name_label') self.name_label.setObjectName('projector_edit_name_label')
self.name_text = QtWidgets.QLineEdit(edit_projector_dialog) self.name_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.name_text.setObjectName('projector_edit_name_text') self.name_text.setObjectName('projector_edit_name_text')
self.dialog_layout.addWidget(self.name_label, 3, 0) self.dialog_layout.addWidget(self.name_label, _row, 0)
self.dialog_layout.addWidget(self.name_text, 3, 1) self.dialog_layout.addWidget(self.name_text, _row, 1)
# IP Address
_row += 1
self.ip_label = QtWidgets.QLabel(edit_projector_dialog)
self.ip_label.setObjectName('projector_edit_ip_label')
self.ip_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.ip_text.setObjectName('projector_edit_ip_text')
self.dialog_layout.addWidget(self.ip_label, _row, 0)
self.dialog_layout.addWidget(self.ip_text, _row, 1)
# Port number
_row += 1
self.port_label = QtWidgets.QLabel(edit_projector_dialog)
self.port_label.setObjectName('projector_edit_ip_label')
self.port_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.port_text.setObjectName('projector_edit_port_text')
self.dialog_layout.addWidget(self.port_label, _row, 0)
self.dialog_layout.addWidget(self.port_text, _row, 1)
# PIN
_row += 1
self.pin_label = QtWidgets.QLabel(edit_projector_dialog)
self.pin_label.setObjectName('projector_edit_pin_label')
self.pin_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.pin_label.setObjectName('projector_edit_pin_text')
self.dialog_layout.addWidget(self.pin_label, _row, 0)
self.dialog_layout.addWidget(self.pin_text, _row, 1)
# Location # Location
_row += 1
self.location_label = QtWidgets.QLabel(edit_projector_dialog) self.location_label = QtWidgets.QLabel(edit_projector_dialog)
self.location_label.setObjectName('projector_edit_location_label') self.location_label.setObjectName('projector_edit_location_label')
self.location_text = QtWidgets.QLineEdit(edit_projector_dialog) self.location_text = QtWidgets.QLineEdit(edit_projector_dialog)
self.location_text.setObjectName('projector_edit_location_text') self.location_text.setObjectName('projector_edit_location_text')
self.dialog_layout.addWidget(self.location_label, 4, 0) self.dialog_layout.addWidget(self.location_label, _row, 0)
self.dialog_layout.addWidget(self.location_text, 4, 1) self.dialog_layout.addWidget(self.location_text, _row, 1)
# Notes # Notes
_row += 1
self.notes_label = QtWidgets.QLabel(edit_projector_dialog) self.notes_label = QtWidgets.QLabel(edit_projector_dialog)
self.notes_label.setObjectName('projector_edit_notes_label') self.notes_label.setObjectName('projector_edit_notes_label')
self.notes_text = QtWidgets.QPlainTextEdit(edit_projector_dialog) self.notes_text = QtWidgets.QPlainTextEdit(edit_projector_dialog)
self.notes_text.setObjectName('projector_edit_notes_text') self.notes_text.setObjectName('projector_edit_notes_text')
self.dialog_layout.addWidget(self.notes_label, 5, 0, alignment=QtCore.Qt.AlignTop) self.dialog_layout.addWidget(self.notes_label, _row, 0, alignment=QtCore.Qt.AlignTop)
self.dialog_layout.addWidget(self.notes_text, 5, 1) self.dialog_layout.addWidget(self.notes_text, _row, 1)
# Time for the buttons # Time for the buttons
self.button_box = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Help | self.button_box_edit = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Help |
QtWidgets.QDialogButtonBox.Save | QtWidgets.QDialogButtonBox.Save |
QtWidgets.QDialogButtonBox.Cancel) QtWidgets.QDialogButtonBox.Cancel)
self.dialog_layout.addWidget(self.button_box, 8, 0, 1, 2) self.dialog_layout.addWidget(self.button_box_edit, 8, 0, 1, 2)
self.button_box_view = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok)
self.dialog_layout.addWidget(self.button_box_view, 8, 0, 1, 2)
def retranslate_ui(self, edit_projector_dialog): def retranslate_ui(self, edit_projector_dialog):
if self.new_projector: if self.new_projector:
@ -118,8 +197,7 @@ class Ui_ProjectorEditForm(object):
title = translate('OpenLP.ProjectorEditForm', 'Edit Projector') title = translate('OpenLP.ProjectorEditForm', 'Edit Projector')
edit_projector_dialog.setWindowTitle(title) edit_projector_dialog.setWindowTitle(title)
self.ip_label.setText(translate('OpenLP.ProjectorEditForm', 'IP Address')) self.ip_label.setText(translate('OpenLP.ProjectorEditForm', 'IP Address'))
self.ip_text_edit.setText(self.projector.ip) self.ip_text.setText(self.projector.ip)
self.ip_text_label.setText(self.projector.ip)
self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number')) self.port_label.setText(translate('OpenLP.ProjectorEditForm', 'Port Number'))
self.port_text.setText(str(self.projector.port)) self.port_text.setText(str(self.projector.port))
self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN')) self.pin_label.setText(translate('OpenLP.ProjectorEditForm', 'PIN'))
@ -138,39 +216,51 @@ class ProjectorEditForm(QtWidgets.QDialog, Ui_ProjectorEditForm):
Class to add or edit a projector entry in the database. Class to add or edit a projector entry in the database.
Fields that are editable: Fields that are editable:
ip = Column(String(100)) (Only edit for new projector) name = Column(String(20))
ip = Column(String(100))
port = Column(String(8)) port = Column(String(8))
pin = Column(String(20)) pin = Column(String(20))
name = Column(String(20))
location = Column(String(30)) location = Column(String(30))
notes = Column(String(200)) notes = Column(String(200))
""" """
newProjector = QtCore.pyqtSignal(str) updateProjectors = QtCore.pyqtSignal()
editProjector = QtCore.pyqtSignal(object)
def __init__(self, parent=None, projectordb=None): def __init__(self, parent=None, projectordb=None):
super(ProjectorEditForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint | self.parent = parent
QtCore.Qt.WindowCloseButtonHint)
self.projectordb = projectordb self.projectordb = projectordb
self.new_projector = False
super(ProjectorEditForm, self).__init__(parent,
QtCore.Qt.WindowSystemMenuHint |
QtCore.Qt.WindowTitleHint |
QtCore.Qt.WindowCloseButtonHint)
self.setup_ui(self) self.setup_ui(self)
self.button_box.accepted.connect(self.accept_me) self.button_box_edit.accepted.connect(self.accept_me)
self.button_box.helpRequested.connect(self.help_me) self.button_box_edit.helpRequested.connect(self.help_me)
self.button_box.rejected.connect(self.cancel_me) self.button_box_edit.rejected.connect(self.cancel_me)
self.button_box_view.accepted.connect(self.cancel_me)
def exec(self, projector=None): def exec(self, projector=None, edit=True):
if projector is None: if projector is None:
self.projector = Projector() self.projector = Projector()
self.new_projector = True self.new_projector = True
self.ip_text_edit.setVisible(True)
self.ip_text_edit.setFocus()
self.ip_text_label.setVisible(False)
else: else:
if not isinstance(projector, Projector):
log.warning('edit_form() Projector type not valid for this form')
log.warning(f'editform() projector type is {type(projector)}')
return Message.show_warning(Message.ProjectorInvalid)
self.projector = projector self.projector = projector
self.new_projector = False self.new_projector = False
self.ip_text_edit.setVisible(False)
self.ip_text_label.setVisible(True) self.button_box_edit.setVisible(edit)
# Since it's already defined, IP address is unchangeable, so focus on port number self.button_box_view.setVisible(not edit)
self.port_text.setFocus() self.name_text.setReadOnly(not edit)
self.ip_text.setReadOnly(not edit)
self.port_text.setReadOnly(not edit)
self.pin_text.setReadOnly(not edit)
self.location_text.setReadOnly(not edit)
self.notes_text.setReadOnly(not edit)
if edit:
self.name_text.setFocus()
self.retranslate_ui(self) self.retranslate_ui(self)
reply = QtWidgets.QDialog.exec(self) reply = QtWidgets.QDialog.exec(self)
return reply return reply
@ -178,90 +268,79 @@ class ProjectorEditForm(QtWidgets.QDialog, Ui_ProjectorEditForm):
@QtCore.pyqtSlot() @QtCore.pyqtSlot()
def accept_me(self): def accept_me(self):
""" """
Validate input before accepting input. Validate inputs before accepting.
""" """
log.debug('accept_me() signal received') log.debug('accept_me() signal received')
valid = True
if len(self.name_text.text().strip()) < 1: # Verify name
QtWidgets.QMessageBox.warning(self, _name = self.name_text.text().strip()
translate('OpenLP.ProjectorEdit', 'Name Not Set'),
translate('OpenLP.ProjectorEdit', if len(_name) < 1:
'You must enter a name for this entry.<br />' return Message.show_warning(message=Message.NameBlank)
'Please enter a new name for this entry.')) _record = self.projectordb.get_projector(name=_name)
valid = False if len(_record) == 0:
return
name = self.name_text.text().strip()
record = self.projectordb.get_projector_by_name(name)
if record is not None and record.id != self.projector.id:
QtWidgets.QMessageBox.warning(self,
translate('OpenLP.ProjectorEdit', 'Duplicate Name'),
translate('OpenLP.ProjectorEdit',
'There is already an entry with name "{name}" in '
'the database as ID "{record}". <br />'
'Please enter a different name.'.format(name=name,
record=record.id)))
valid = False
return
if self.new_projector: if self.new_projector:
# Only validate a new entry - otherwise it's been previously verified if self.projector.id is not None:
adx = self.ip_text_edit.text() log.warning(f'editform(): No record found but projector had id={self.projector.id}')
valid = verify_ip_address(adx) return Message.show_warning(message=Message.DatabaseError)
if valid:
# With a valid IP - check if it's already in database so we don't duplicate
ip = self.projectordb.get_projector_by_ip(adx)
if ip is None:
valid = True
self.new_projector = True
elif ip.id != self.projector.id:
QtWidgets.QMessageBox.warning(self,
translate('OpenLP.ProjectorWizard', 'Duplicate IP Address'),
translate('OpenLP.ProjectorWizard',
'IP address "{ip}"<br />is already in the database '
'as ID {data}.<br /><br />Please Enter a different '
'IP address.'.format(ip=adx, data=ip.id)))
return
else: else:
QtWidgets.QMessageBox.warning(self, if self.projector.name.strip() == _name:
translate('OpenLP.ProjectorWizard', 'Invalid IP Address'), log.warning(f'editform(): No record found when there should be name="{_name}"')
translate('OpenLP.ProjectorWizard', return Message.show_warning(message=Message.DatabaseError)
'IP address "{ip}"<br>is not a valid IP address.' elif len(_record) == 1 and self.new_projector:
'<br /><br />Please enter a valid IP address.'.format(ip=adx))) log.warning(f'editform(): Name "{_name}" already in database')
valid = False return Message.show_warning(message=Message.NameDuplicate)
return elif len(_record) > 1:
port = int(self.port_text.text()) log.warning(f'editform(): Multiple records found for name "{_name}"')
if port < 1000 or port > 32767: for item in _record:
QtWidgets.QMessageBox.warning(self, log.warning(f'editform() Found record={item.id} name="{item.name}"')
translate('OpenLP.ProjectorWizard', 'Invalid Port Number'), return Message.show_warning(message=Message.DatabaseMultiple)
translate('OpenLP.ProjectorWizard',
'Port numbers below 1000 are reserved for admin use only, ' # Verify IP address
'<br />and port numbers above 32767 are not currently usable.' _ip = self.ip_text.text().strip()
'<br /><br />Please enter a valid port number between ' if len(_ip) < 1:
'1000 and 32767.<br /><br />' return Message.show_warning(message=Message.IPBlank)
'Default PJLink port is {port}'.format(port=PJLINK_PORT))) elif not verify_ip_address(_ip):
valid = False return Message.show_warning(message=Message.IPInvalid)
if valid:
if self.new_projector: # Verify valid port
self.projector.ip = self.ip_text_edit.text() _port = self.port_text.text().strip()
if len(_port) < 1:
return Message.show_warning(message=Message.PortBlank)
elif not _port.isdecimal():
return Message.show_warning(message=Message.PortInvalid)
elif int(_port) not in PJLINK_VALID_PORTS:
return Message.show_warning(message=Message.PortInvalid)
_port = int(_port)
# Verify valid ip:port address
check = self.projectordb.get_projector(ip=_ip, port=str(_port))
if len(check) == 1:
if self.projector.id != check[0].id:
log.warning(f'editform(): Address already in database {_ip}:{_port}')
return Message.show_warning(message=Message.AddressDuplicate)
elif len(check) > 1:
log.warning(f'editform(): Multiple records found for {_ip}:{_port}')
for chk in check:
log.warning(f'editform(): record={chk.id} name="{chk.name}" adx={chk.ip}:{chk.port}')
return Message.show_warning(message=Message.DatabaseMultiple)
self.projector.name = _name
self.projector.ip = _ip
self.projector.port = _port
self.projector.pin = self.pin_text.text() self.projector.pin = self.pin_text.text()
self.projector.port = int(self.port_text.text())
self.projector.name = self.name_text.text()
self.projector.location = self.location_text.text() self.projector.location = self.location_text.text()
self.projector.notes = self.notes_text.toPlainText() self.projector.notes = self.notes_text.toPlainText()
# TODO: Update calls when update_projector fixed
if self.new_projector: if self.new_projector:
saved = self.projectordb.add_projector(self.projector) _saved = self.projectordb.add_projector(self.projector)
else: else:
saved = self.projectordb.update_projector(self.projector) _saved = self.projectordb.update_projector(self.projector)
if not saved: if not _saved:
QtWidgets.QMessageBox.warning(self, return Message.show_warning(message=Message.DatabaseError)
translate('OpenLP.ProjectorEditForm', 'Database Error'),
translate('OpenLP.ProjectorEditForm', self.updateProjectors.emit()
'There was an error saving projector ' self.projector = None
'information. See the log for the error'))
return saved
if self.new_projector:
self.newProjector.emit(adx)
else:
self.editProjector.emit(self.projector)
self.close() self.close()
@QtCore.pyqtSlot() @QtCore.pyqtSlot()

View File

@ -205,6 +205,11 @@ class UiProjectorManager(object):
'&Edit Projector'), '&Edit Projector'),
icon=UiIcons().edit, icon=UiIcons().edit,
triggers=self.on_edit_projector) triggers=self.on_edit_projector)
self.view_action = create_widget_action(self.menu,
text=translate('OpenLP.ProjectorManager',
'&View Projector'),
icon=UiIcons().edit,
triggers=self.on_view_projector)
self.menu.addSeparator() self.menu.addSeparator()
self.connect_action = create_widget_action(self.menu, self.connect_action = create_widget_action(self.menu,
text=translate('OpenLP.ProjectorManager', text=translate('OpenLP.ProjectorManager',
@ -248,6 +253,11 @@ class UiProjectorManager(object):
'&Show Projector Screen'), '&Show Projector Screen'),
icon=UiIcons().projector, icon=UiIcons().projector,
triggers=self.on_show_projector) triggers=self.on_show_projector)
self.view_action = create_widget_action(self.menu,
text=translate('OpenLP.ProjectorManager',
'&Show Projector Screen'),
icon=UiIcons().projector,
triggers=self.on_view_projector)
self.menu.addSeparator() self.menu.addSeparator()
self.delete_action = create_widget_action(self.menu, self.delete_action = create_widget_action(self.menu,
text=translate('OpenLP.ProjectorManager', text=translate('OpenLP.ProjectorManager',
@ -321,8 +331,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
log.debug('Loading all projectors') log.debug('Loading all projectors')
self._load_projectors() self._load_projectors()
self.projector_form = ProjectorEditForm(self, projectordb=self.projectordb) self.projector_form = ProjectorEditForm(self, projectordb=self.projectordb)
self.projector_form.newProjector.connect(self.add_projector_from_wizard) self.projector_form.updateProjectors.connect(self._load_projectors)
self.projector_form.editProjector.connect(self.edit_projector_from_wizard)
self.projector_list_widget.itemSelectionChanged.connect(self.update_icons) self.projector_list_widget.itemSelectionChanged.connect(self.update_icons)
def udp_listen_add(self, port=PJLINK_PORT): def udp_listen_add(self, port=PJLINK_PORT):
@ -385,6 +394,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
log.debug(f'({projector_name}) Building menu - visible = {visible}') log.debug(f'({projector_name}) Building menu - visible = {visible}')
self.delete_action.setVisible(True) self.delete_action.setVisible(True)
self.edit_action.setVisible(True) self.edit_action.setVisible(True)
self.view_action.setVisible(True)
self.connect_action.setVisible(not visible) self.connect_action.setVisible(not visible)
self.disconnect_action.setVisible(visible) self.disconnect_action.setVisible(visible)
self.status_action.setVisible(visible) self.status_action.setVisible(visible)
@ -687,6 +697,19 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
message += '<b>{key}</b>: {data}<br />'.format(key=key, data=STATUS_MSG[val]) message += '<b>{key}</b>: {data}<br />'.format(key=key, data=STATUS_MSG[val])
QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message) QtWidgets.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message)
def on_view_projector(self, opt=None):
"""
Calls edit dialog as readonly with selected projector to show information
:param opt: Needed by PyQt5
"""
list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow())
projector = list_item.data(QtCore.Qt.UserRole)
if projector is None:
return
self.old_projector = projector
self.projector_form.exec(projector.db_item, edit=False)
def _add_projector(self, projector): def _add_projector(self, projector):
""" """
Helper app to build a projector instance Helper app to build a projector instance
@ -771,7 +794,7 @@ class ProjectorManager(QtWidgets.QWidget, RegistryBase, UiProjectorManager, LogM
def _load_projectors(self): def _load_projectors(self):
"""' """'
Load projectors - only call when initializing Load projectors - only call when initializing or after database changes
""" """
log.debug('_load_projectors()') log.debug('_load_projectors()')
self.projector_list_widget.clear() self.projector_list_widget.clear()

View File

@ -27,8 +27,10 @@ from pathlib import PurePath
from unittest.mock import patch from unittest.mock import patch
from openlp.core.projectors.db import Projector, ProjectorDB from openlp.core.projectors.db import Projector, ProjectorDB
from openlp.core.projectors.editform import ProjectorEditForm
from openlp.core.projectors.manager import ProjectorManager from openlp.core.projectors.manager import ProjectorManager
from openlp.core.projectors.pjlink import PJLink from openlp.core.projectors.pjlink import PJLink
from tests.helpers.projector import FakePJLink from tests.helpers.projector import FakePJLink
from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
@ -118,6 +120,28 @@ def projectordb(temp_folder, settings):
del proj del proj
@pytest.fixture()
def projector_editform(projectordb):
with patch('openlp.core.projectors.editform.QtWidgets.QMessageBox') as mock_msg_box, \
patch('openlp.core.projectors.editform.QtWidgets.QDialog') as mock_dialog_box:
_form = ProjectorEditForm(projectordb=projectordb)
_form.mock_msg_box = mock_msg_box
_form.mock_dialog_box = mock_dialog_box
yield _form
del _form
@pytest.fixture()
def projector_editform_mtdb(projectordb_mtdb):
with patch('openlp.core.projectors.editform.QtWidgets.QMessageBox') as mock_msg_box, \
patch('openlp.core.projectors.editform.QtWidgets.QDialog') as mock_dialog_box:
_form = ProjectorEditForm(projectordb=projectordb_mtdb)
_form.mock_msg_box = mock_msg_box
_form.mock_dialog_box = mock_dialog_box
yield _form
del _form
@pytest.fixture() @pytest.fixture()
def pjlink(): def pjlink():
pj_link = PJLink(Projector(**TEST1_DATA), no_poll=True) pj_link = PJLink(Projector(**TEST1_DATA), no_poll=True)

View File

@ -0,0 +1,535 @@
# -*- 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 ProjectorEditForm.accept_me
"""
import logging
import openlp.core.projectors.db
import openlp.core.projectors.editform
from unittest.mock import DEFAULT, patch
from openlp.core.projectors.constants import PJLINK_VALID_PORTS
from openlp.core.projectors.db import Projector
from tests.resources.projector.data import TEST1_DATA
_test_module = openlp.core.projectors.editform.__name__
_test_module_db = openlp.core.projectors.db.__name__
Message = openlp.core.projectors.editform.Message
def test_name_NameBlank(projector_editform_mtdb, caplog):
"""
Test when name field blank
"""
# GIVEN: Test setup
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received')]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText('')
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.NameBlank['title'],
Message.NameBlank['text']
)
def test_name_DatabaseError_id(projector_editform_mtdb, caplog):
"""
Test with mismatch ID between Projector() and DB
"""
# GIVEN: Test setup
t_id = TEST1_DATA['id']
t_name = TEST1_DATA['name']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module, logging.WARNING,
f'editform(): No record found but projector had id={t_id}')]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.projector.id = t_id
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseError['title'],
Message.DatabaseError['text']
)
def test_name_DatabaseError_name(projector_editform_mtdb, caplog):
"""
Test with mismatch between name and DB
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module, logging.WARNING,
f'editform(): No record found when there should be name="{t_name}"')]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.projector.name = t_name
projector_editform_mtdb.new_projector = False
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseError['title'],
Message.DatabaseError['text']
)
def test_name_NameDuplicate(projector_editform, caplog):
"""
Test when name duplicate
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
# As long as the new record port number is different, we should be good
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module, logging.WARNING, f'editform(): Name "{t_name}" already in database')
]
projector_editform.exec()
projector_editform.name_text.setText(t_name)
projector_editform.projector.name = t_name
# WHEN: Called
caplog.clear()
projector_editform.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform.mock_msg_box.warning.assert_called_once_with(None,
Message.NameDuplicate['title'],
Message.NameDuplicate['text']
)
def test_name_DatabaseMultiple(projector_editform, caplog):
"""
Test when multiple database records have the same name
"""
# GIVEN: Test setup
# Save another instance of TEST1_DATA
t_proj = Projector(**TEST1_DATA)
t_proj.id = None
projector_editform.projectordb.save_object(t_proj)
# Test variables
t_id1 = TEST1_DATA['id']
# There should only be 3 records in the DB, TEST[1,2,3]_DATA
# The above save_object() should have created record 4
t_id2 = t_proj.id
t_name = TEST1_DATA['name']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module, logging.WARNING, f'editform(): Multiple records found for name "{t_name}"'),
(_test_module, logging.WARNING, f'editform() Found record={t_id1} name="{t_name}"'),
(_test_module, logging.WARNING, f'editform() Found record={t_id2} name="{t_name}"')
]
projector_editform.exec()
projector_editform.name_text.setText(t_name)
# WHEN: Called
caplog.clear()
projector_editform.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseMultiple['title'],
Message.DatabaseMultiple['text']
)
def test_ip_IPBlank(projector_editform_mtdb, caplog):
"""
Test when IP field blank
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name')]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText('')
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.IPBlank['title'],
Message.IPBlank['text']
)
def test_ip_IPInvalid(projector_editform_mtdb, caplog):
"""
Test when IP invalid
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
t_ip = 'a'
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name')]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText(t_ip)
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.IPInvalid['title'],
Message.IPInvalid['text']
)
def test_port_PortBlank(projector_editform_mtdb, caplog):
"""
Test when port field blank
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
t_ip = TEST1_DATA['ip']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText(t_ip)
projector_editform_mtdb.port_text.setText('')
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.PortBlank['title'],
Message.PortBlank['text']
)
def test_port_PortInvalid_not_decimal(projector_editform_mtdb, caplog):
"""
Test when port not a decimal digit
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
t_ip = TEST1_DATA['ip']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText(t_ip)
projector_editform_mtdb.port_text.setText('a')
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.PortInvalid['title'],
Message.PortInvalid['text']
)
def test_port_PortInvalid_low(projector_editform_mtdb, caplog):
"""
Test when port number less than PJLINK_VALID_PORTS lower value
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
t_ip = TEST1_DATA['ip']
t_port = PJLINK_VALID_PORTS.start - 1
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText(t_ip)
projector_editform_mtdb.port_text.setText(str(t_port))
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.PortInvalid['title'],
Message.PortInvalid['text']
)
def test_port_PortInvalid_high(projector_editform_mtdb, caplog):
"""
Test when port number greater than PJLINK_VALID_PORTS higher value
"""
# GIVEN: Test setup
t_name = TEST1_DATA['name']
t_ip = TEST1_DATA['ip']
t_port = PJLINK_VALID_PORTS.stop + 1
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
]
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_name)
projector_editform_mtdb.ip_text.setText(t_ip)
projector_editform_mtdb.port_text.setText(str(t_port))
# WHEN: Called
caplog.clear()
projector_editform_mtdb.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.PortInvalid['title'],
Message.PortInvalid['text']
)
def test_adx_AddressDuplicate(projector_editform, caplog):
"""
Test when IP:Port address duplicate
"""
# GIVEN: Test setup
t_ip = TEST1_DATA['ip']
t_port = TEST1_DATA['port']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module_db, logging.DEBUG, 'Filter by IP Port'),
(_test_module, logging.WARNING, f'editform(): Address already in database {t_ip}:{t_port}')
]
projector_editform.exec()
projector_editform.name_text.setText('A Different Name Not In DB')
projector_editform.ip_text.setText(t_ip)
projector_editform.port_text.setText(str(t_port))
# WHEN: Called
caplog.clear()
projector_editform.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform.mock_msg_box.warning.assert_called_once_with(None,
Message.AddressDuplicate['title'],
Message.AddressDuplicate['text']
)
def test_adx_DatabaseMultiple(projector_editform, caplog):
"""
Test when database has multiple same IP:Port records
"""
# GIVEN: Test setup
t_proj = Projector(**TEST1_DATA)
t_proj.id = None
projector_editform.projectordb.save_object(t_proj)
t_id1 = TEST1_DATA['id']
t_id2 = t_proj.id
t_name = TEST1_DATA['name']
t_ip = TEST1_DATA['ip']
t_port = TEST1_DATA['port']
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'accept_me() signal received'),
(_test_module_db, logging.DEBUG, 'Filter by Name'),
(_test_module_db, logging.DEBUG, 'Filter by IP Port'),
(_test_module, logging.WARNING, f'editform(): Multiple records found for {t_ip}:{t_port}'),
(_test_module, logging.WARNING, f'editform(): record={t_id1} name="{t_name}" adx={t_ip}:{t_port}'),
(_test_module, logging.WARNING, f'editform(): record={t_id2} name="{t_name}" adx={t_ip}:{t_port}')
]
projector_editform.exec()
projector_editform.name_text.setText('A Different Name Not In DB')
projector_editform.ip_text.setText(t_ip)
projector_editform.port_text.setText(str(t_port))
# WHEN: Called
caplog.clear()
projector_editform.accept_me()
# THEN: Appropriate calls made
assert caplog.record_tuples == logs, 'Invalid logs'
projector_editform.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseMultiple['title'],
Message.DatabaseMultiple['text']
)
@patch.multiple(openlp.core.projectors.editform.ProjectorEditForm, updateProjectors=DEFAULT, close=DEFAULT)
@patch.object(openlp.core.projectors.db.ProjectorDB, 'add_projector')
def test_save_new(mock_add, projector_editform_mtdb, **kwargs):
"""
Test editform saving new projector instance where db fails to save
"""
# GIVEN: Test environment
mock_update = kwargs['updateProjectors']
mock_close = kwargs['close']
mock_add.return_value = True
t_proj = Projector(**TEST1_DATA)
t_proj.id = None
projector_editform_mtdb.new_projector = True
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_proj.name)
projector_editform_mtdb.ip_text.setText(t_proj.ip)
projector_editform_mtdb.port_text.setText(str(t_proj.port))
# WHEN: Called
projector_editform_mtdb.accept_me()
# THEN: appropriate message called
projector_editform_mtdb.mock_msg_box.warning.assert_not_called()
mock_update.emit.assert_called_once()
mock_close.assert_called_once()
@patch.multiple(openlp.core.projectors.editform.ProjectorEditForm, updateProjectors=DEFAULT, close=DEFAULT)
@patch.object(openlp.core.projectors.db.ProjectorDB, 'add_projector')
def test_save_new_fail(mock_add, projector_editform_mtdb, caplog, **kwargs):
"""
Test editform saving new projector instance where db fails to save
"""
# GIVEN: Test environment
mock_update = kwargs['updateProjectors']
mock_close = kwargs['close']
mock_add.return_value = False
caplog.set_level(logging.DEBUG)
t_proj = Projector(**TEST1_DATA)
t_proj.id = None
projector_editform_mtdb.new_projector = True
projector_editform_mtdb.exec()
projector_editform_mtdb.name_text.setText(t_proj.name)
projector_editform_mtdb.ip_text.setText(t_proj.ip)
projector_editform_mtdb.port_text.setText(str(t_proj.port))
# WHEN: Called
projector_editform_mtdb.accept_me()
# THEN: appropriate message called
mock_add.assert_called_once_with(projector_editform_mtdb.projector)
projector_editform_mtdb.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseError['title'],
Message.DatabaseError['text']
)
mock_update.assert_not_called()
mock_close.assert_not_called()
@patch.multiple(openlp.core.projectors.editform.ProjectorEditForm, updateProjectors=DEFAULT, close=DEFAULT)
@patch.object(openlp.core.projectors.db.ProjectorDB, 'update_projector')
def test_save_update(mock_add, projector_editform, **kwargs):
"""
Test editform update projector instance in database
"""
# GIVEN: Test environment
mock_update = kwargs['updateProjectors']
mock_close = kwargs['close']
mock_add.return_value = True
t_proj = Projector(**TEST1_DATA)
projector_editform.new_projector = True
projector_editform.exec(projector=t_proj)
projector_editform.name_text.setText(t_proj.name)
projector_editform.ip_text.setText(t_proj.ip)
projector_editform.port_text.setText(str(t_proj.port))
# WHEN: Called
projector_editform.accept_me()
# THEN: appropriate message called
projector_editform.mock_msg_box.warning.assert_not_called()
mock_update.emit.assert_called_once()
mock_close.assert_called_once()
@patch.multiple(openlp.core.projectors.editform.ProjectorEditForm, updateProjectors=DEFAULT, close=DEFAULT)
@patch.object(openlp.core.projectors.db.ProjectorDB, 'update_projector')
def test_save_update_fail(mock_add, projector_editform, caplog, **kwargs):
"""
Test editform updating projector instance where db fails to save
"""
# GIVEN: Test environment
mock_update = kwargs['updateProjectors']
mock_close = kwargs['close']
mock_add.return_value = False
caplog.set_level(logging.DEBUG)
t_proj = Projector(**TEST1_DATA)
projector_editform.exec(projector=t_proj)
projector_editform.name_text.setText(t_proj.name)
projector_editform.ip_text.setText(t_proj.ip)
projector_editform.port_text.setText(str(t_proj.port))
# WHEN: Called
projector_editform.accept_me()
# THEN: appropriate message called
mock_add.assert_called_once_with(projector_editform.projector)
projector_editform.mock_msg_box.warning.assert_called_once_with(None,
Message.DatabaseError['title'],
Message.DatabaseError['text']
)
mock_update.assert_not_called()
mock_close.assert_not_called()

View File

@ -0,0 +1,146 @@
# -*- 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 ProjectorEditForm methods that don't have many paths/options
"""
import logging
import openlp.core.projectors.editform
import openlp.core.projectors.db
from unittest.mock import patch
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA
_test_module = openlp.core.projectors.editform.__name__
_test_module_db = openlp.core.projectors.db.__name__
Message = openlp.core.projectors.editform.Message
Projector = openlp.core.projectors.db.Projector
ProjectorEditForm = openlp.core.projectors.editform.ProjectorEditForm
def test_exec_projector_bad(projector_editform, caplog):
"""
Test projector edit form with bad projector
"""
# GIVEN: Mocked setup
t_chk_item = str()
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.WARNING, 'edit_form() Projector type not valid for this form'),
(_test_module, logging.WARNING, f'editform() projector type is {type(t_chk_item)}')
]
# WHEN: Calling edit form with existing projector instance
projector_editform.exec(projector=t_chk_item)
# THEN: Appropriate calls and log entries
assert caplog.record_tuples == logs, 'Invalid log entries'
assert projector_editform.new_projector is False, 'new_projector should not have changed'
projector_editform.mock_dialog_box.exec.assert_not_called()
projector_editform.mock_msg_box.warning.assert_called_with(None,
Message.ProjectorInvalid['title'],
Message.ProjectorInvalid['text']
)
# TODO: Check signals for QDialogButtonBox (projector_editform.button_box_edit/button_box_view)
def test_exec_projector_edit(projector_editform, caplog):
"""
Test projector edit form with existing projector entry
:return:
"""
# GIVEN: Mocked setup
caplog.set_level(logging.DEBUG)
logs = [(_test_module_db, logging.DEBUG, 'Filter by ID')]
t_chk_db = projector_editform.projectordb.get_projector(id=TEST2_DATA['id'])[0]
projector_editform.new_projector = False
projector_editform.projector = None
# WHEN: Calling edit form with existing projector instance
projector_editform.exec(projector=Projector(**TEST2_DATA))
# THEN: Should be editing an existing entry
assert caplog.record_tuples == logs, 'Invalid log entries'
assert projector_editform.new_projector is False, 'Projector edit form should be marked as existing entry'
assert projector_editform.projector == t_chk_db, 'Entries should match'
projector_editform.mock_msg_box.assert_not_called()
# TODO: Check signals for QDialogButtonBox (projector_editform.button_box_edit/button_box_view)
def test_exec_projector_new(projector_editform_mtdb, caplog):
"""
Test projector edit form with existing projector entry
:return:
"""
# GIVEN: Mocked setup
caplog.set_level(logging.DEBUG)
logs = []
projector_editform_mtdb.new_projector = False
projector_editform_mtdb.projector = Projector(**TEST1_DATA)
# WHEN: Calling edit form with existing projector instance
projector_editform_mtdb.exec()
t_chk_item = projector_editform_mtdb.projector
# THEN: Should be editing an existing entry
assert caplog.record_tuples == logs, 'Invalid log entries'
assert projector_editform_mtdb.new_projector is True, 'Projector edit form should be marked as a new entry'
assert isinstance(t_chk_item, Projector)
assert t_chk_item.id is None, 'ID should have indicated new entry'
assert t_chk_item.name is None, 'Name should have indicated new entry'
assert t_chk_item.ip is None, 'IP should have indicated new entry'
# TODO: Check signals for QDialogButtonBox (projector_editform.button_box_edit/button_box_view)
def test_cancel_me(projector_editform_mtdb, caplog):
"""
Test cancel_me logs and calls close
"""
# GIVEN: Test setup
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'cancel_me() signal received')]
with patch.object(projector_editform_mtdb, 'close') as mock_close:
# WHEN: Called
projector_editform_mtdb.cancel_me()
# THEN: Appropriate log entries made
assert caplog.record_tuples == logs, 'Invalid log entries'
mock_close.assert_called_once()
def test_help_me(projector_editform_mtdb, caplog):
"""
TODO: Expand method, then expand test
"""
# GIVEN: Test setup
caplog.set_level(logging.DEBUG)
logs = [(_test_module, logging.DEBUG, 'help_me() signal received')]
# WHEN: Called
projector_editform_mtdb.help_me()
# THEN: Appropriate log entries made
assert caplog.record_tuples == logs, 'Invalid log entries'

View File

@ -87,11 +87,9 @@ def test_bootstrap_post_set_up_autostart_false(mock_timer, mocked_edit, projecto
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
logs = [(test_module, logging.DEBUG, 'Loading all projectors')] logs = [(test_module, logging.DEBUG, 'Loading all projectors')]
mock_newProjector = MagicMock() mock_updateProjectors = MagicMock()
mock_editProjector = MagicMock()
mock_edit = MagicMock() mock_edit = MagicMock()
mock_edit.newProjector = mock_newProjector mock_edit.updateProjectors = mock_updateProjectors
mock_edit.editProjector = mock_editProjector
mocked_edit.return_value = mock_edit mocked_edit.return_value = mock_edit
settings.setValue('projector/connect on start', False) settings.setValue('projector/connect on start', False)
@ -107,8 +105,7 @@ def test_bootstrap_post_set_up_autostart_false(mock_timer, mocked_edit, projecto
# THEN: verify calls and logs # THEN: verify calls and logs
mock_timer.assert_not_called() mock_timer.assert_not_called()
mock_newProjector.connect.assert_called_once() mock_updateProjectors.connect.assert_called_once()
mock_editProjector.connect.assert_called_once()
mock_manager['_load_projectors'].assert_called_once(), mock_manager['_load_projectors'].assert_called_once(),
mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once() mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once()
assert caplog.record_tuples == logs, 'Invalid log entries' assert caplog.record_tuples == logs, 'Invalid log entries'
@ -124,11 +121,9 @@ def test_bootstrap_post_set_up_autostart_true(mock_timer, mocked_edit, projector
caplog.set_level(logging.DEBUG) caplog.set_level(logging.DEBUG)
logs = [(test_module, logging.DEBUG, 'Delaying 1.5 seconds before loading all projectors')] logs = [(test_module, logging.DEBUG, 'Delaying 1.5 seconds before loading all projectors')]
mock_newProjector = MagicMock() mock_updateProjectors = MagicMock()
mock_editProjector = MagicMock()
mock_edit = MagicMock() mock_edit = MagicMock()
mock_edit.newProjector = mock_newProjector mock_edit.updateProjectors = mock_updateProjectors
mock_edit.editProjector = mock_editProjector
settings.setValue('projector/connect on start', True) settings.setValue('projector/connect on start', True)
projector_manager.bootstrap_initialise() projector_manager.bootstrap_initialise()
@ -146,8 +141,7 @@ def test_bootstrap_post_set_up_autostart_true(mock_timer, mocked_edit, projector
mock_timer.assert_called_once() mock_timer.assert_called_once()
mock_timer.return_value.singleShot.assert_called_once_with(1500, projector_manager._load_projectors) mock_timer.return_value.singleShot.assert_called_once_with(1500, projector_manager._load_projectors)
mock_newProjector.connect.assert_called_once() mock_updateProjectors.connect.assert_called_once()
mock_editProjector.connect.assert_called_once()
mock_manager['_load_projectors'].assert_not_called(), mock_manager['_load_projectors'].assert_not_called(),
mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once() mock_manager['projector_list_widget'].itemSelectionChanged.connect.assert_called_once()
assert caplog.record_tuples == logs, 'Invalid log entries' assert caplog.record_tuples == logs, 'Invalid log entries'

View File

@ -1,76 +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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Interface tests to test the openlp.core.projectors.editform.ProjectorEditForm()
class and methods.
"""
import pytest
from unittest.mock import patch
from openlp.core.projectors.db import Projector, ProjectorDB
from openlp.core.projectors.editform import ProjectorEditForm
from tests.resources.projector.data import TEST1_DATA, TEST_DB
@pytest.fixture()
def projector_form(settings):
with patch('openlp.core.projectors.db.init_url') as mocked_init_url:
mocked_init_url.return_value = 'sqlite:///' + TEST_DB
projectordb = ProjectorDB()
projector_frm = ProjectorEditForm(projectordb=projectordb)
yield projector_frm
projectordb.session.close()
del projector_frm
@patch('openlp.core.projectors.editform.QtWidgets.QDialog.exec')
def test_edit_form_add_projector(mocked_exec, projector_form):
"""
Test projector edit form with no parameters creates a new entry.
:return: None
"""
# GIVEN: Mocked setup
# WHEN: Calling edit form with no parameters
projector_form.exec()
item = projector_form.projector
# THEN: Should be creating a new instance
assert projector_form.new_projector, 'Projector edit form should be marked as a new entry'
assert (item.ip is None and item.name is None), 'Projector edit form should have a new Projector() instance to edit'
@patch('openlp.core.projectors.editform.QtWidgets.QDialog.exec')
def test_edit_form_edit_projector(mocked_exec, projector_form):
"""
Test projector edit form with existing projector entry
:return:
"""
# GIVEN: Mocked setup
# WHEN: Calling edit form with existing projector instance
projector_form.exec(projector=Projector(**TEST1_DATA))
item = projector_form.projector
# THEN: Should be editing an existing entry
assert projector_form.new_projector is False, 'Projector edit form should be marked as existing entry'
assert item.ip is TEST1_DATA['ip'] and item.name is TEST1_DATA['name'], \
'Projector edit form should have TEST1_DATA() instance to edit'