Merge trunk in

This commit is contained in:
Simon Hanna 2016-01-09 01:03:54 +01:00
commit 8ccfde6929
53 changed files with 355 additions and 227 deletions

View File

@ -43,3 +43,5 @@ __pycache__
.coverage .coverage
cover cover
*.kdev4 *.kdev4
./.coveragerc
./coverage

View File

@ -55,7 +55,7 @@ def init_db(url, auto_flush=True, auto_commit=False, base=None):
metadata = MetaData(bind=engine) metadata = MetaData(bind=engine)
else: else:
base.metadata.bind = engine base.metadata.bind = engine
metadata = None metadata = base.metadata
session = scoped_session(sessionmaker(autoflush=auto_flush, autocommit=auto_commit, bind=engine)) session = scoped_session(sessionmaker(autoflush=auto_flush, autocommit=auto_commit, bind=engine))
return session, metadata return session, metadata
@ -227,13 +227,12 @@ class Manager(object):
""" """
self.is_dirty = False self.is_dirty = False
self.session = None self.session = None
# See if we're using declarative_base with a pre-existing session. self.db_url = None
log.debug('Manager: Testing for pre-existing session') if db_file_name:
if session is not None: log.debug('Manager: Creating new DB url')
log.debug('Manager: Using existing session')
else:
log.debug('Manager: Creating new session')
self.db_url = init_url(plugin_name, db_file_name) self.db_url = init_url(plugin_name, db_file_name)
else:
self.db_url = init_url(plugin_name)
if upgrade_mod: if upgrade_mod:
try: try:
db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod) db_ver, up_ver = upgrade_db(self.db_url, upgrade_mod)
@ -248,10 +247,13 @@ class Manager(object):
'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url) 'not be loaded.\n\nDatabase: %s') % (db_ver, up_ver, self.db_url)
) )
return return
try: if not session:
self.session = init_schema(self.db_url) try:
except (SQLAlchemyError, DBAPIError): self.session = init_schema(self.db_url)
handle_db_error(plugin_name, db_file_name) except (SQLAlchemyError, DBAPIError):
handle_db_error(plugin_name, db_file_name)
else:
self.session = session
def save_object(self, object_instance, commit=True): def save_object(self, object_instance, commit=True):
""" """
@ -344,13 +346,13 @@ class Manager(object):
for try_count in range(3): for try_count in range(3):
try: try:
return self.session.query(object_class).filter(filter_clause).first() return self.session.query(object_class).filter(filter_clause).first()
except OperationalError: except OperationalError as oe:
# This exception clause is for users running MySQL which likes to terminate connections on its own # This exception clause is for users running MySQL which likes to terminate connections on its own
# without telling anyone. See bug #927473. However, other dbms can raise it, usually in a # without telling anyone. See bug #927473. However, other dbms can raise it, usually in a
# non-recoverable way. So we only retry 3 times. # non-recoverable way. So we only retry 3 times.
log.exception('Probably a MySQL issue, "MySQL has gone away"') if try_count >= 2 or 'MySQL has gone away' in str(oe):
if try_count >= 2:
raise raise
log.exception('Probably a MySQL issue, "MySQL has gone away"')
def get_all_objects(self, object_class, filter_clause=None, order_by_ref=None): def get_all_objects(self, object_class, filter_clause=None, order_by_ref=None):
""" """

View File

@ -20,18 +20,18 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
:mod:`openlp.core.lib.projector.db` module :mod:`openlp.core.lib.projector.db` module
Provides the database functions for the Projector module. Provides the database functions for the Projector module.
The Manufacturer, Model, Source tables keep track of the video source The Manufacturer, Model, Source tables keep track of the video source
strings used for display of input sources. The Source table maps strings used for display of input sources. The Source table maps
manufacturer-defined or user-defined strings from PJLink default strings manufacturer-defined or user-defined strings from PJLink default strings
to end-user readable strings; ex: PJLink code 11 would map "RGB 1" to end-user readable strings; ex: PJLink code 11 would map "RGB 1"
default string to "RGB PC (analog)" string. default string to "RGB PC (analog)" string.
(Future feature). (Future feature).
The Projector table keeps track of entries for controlled projectors. The Projector table keeps track of entries for controlled projectors.
""" """
import logging import logging
@ -218,19 +218,19 @@ class ProjectorDB(Manager):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
log.debug('ProjectorDB().__init__(args="%s", kwargs="%s")' % (args, kwargs)) log.debug('ProjectorDB().__init__(args="%s", kwargs="%s")' % (args, kwargs))
super().__init__(plugin_name='projector', super().__init__(plugin_name='projector', init_schema=self.init_schema)
init_schema=self.init_schema)
log.debug('ProjectorDB() Initialized using db url %s' % self.db_url) log.debug('ProjectorDB() Initialized using db url %s' % self.db_url)
log.debug('Session: %s', self.session)
def init_schema(*args, **kwargs): def init_schema(self, *args, **kwargs):
""" """
Setup the projector database and initialize the schema. Setup the projector database and initialize the schema.
Declarative uses table classes to define schema. Declarative uses table classes to define schema.
""" """
url = init_url('projector') self.db_url = init_url('projector')
session, metadata = init_db(url, base=Base) session, metadata = init_db(self.db_url, base=Base)
Base.metadata.create_all(checkfirst=True) metadata.create_all(checkfirst=True)
return session return session
def get_projector_by_id(self, dbid): def get_projector_by_id(self, dbid):

View File

@ -24,7 +24,7 @@ The About dialog.
""" """
import webbrowser import webbrowser
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.utils import get_application_version from openlp.core.utils import get_application_version
@ -40,7 +40,8 @@ class AboutForm(QtWidgets.QDialog, UiAboutDialog):
""" """
Do some initialisation stuff Do some initialisation stuff
""" """
super(AboutForm, self).__init__(parent) super(AboutForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self._setup() self._setup()
def _setup(self): def _setup(self):

View File

@ -89,7 +89,8 @@ class ExceptionForm(QtWidgets.QDialog, Ui_ExceptionDialog, RegistryProperties):
""" """
Constructor. Constructor.
""" """
super(ExceptionForm, self).__init__() super(ExceptionForm, self).__init__(None, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.settings_section = 'crashreport' self.settings_section = 'crashreport'
self.report_text = '**OpenLP Bug Report**\n' \ self.report_text = '**OpenLP Bug Report**\n' \

View File

@ -23,7 +23,7 @@
The file rename dialog. The file rename dialog.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from .filerenamedialog import Ui_FileRenameDialog from .filerenamedialog import Ui_FileRenameDialog
@ -38,7 +38,8 @@ class FileRenameForm(QtWidgets.QDialog, Ui_FileRenameDialog, RegistryProperties)
""" """
Constructor Constructor
""" """
super(FileRenameForm, self).__init__(Registry().get('main_window')) super(FileRenameForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self._setup() self._setup()
def _setup(self): def _setup(self):

View File

@ -22,7 +22,7 @@
""" """
The language selection dialog. The language selection dialog.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.lib.ui import create_action from openlp.core.lib.ui import create_action
from openlp.core.utils import LanguageManager from openlp.core.utils import LanguageManager
@ -37,7 +37,8 @@ class FirstTimeLanguageForm(QtWidgets.QDialog, Ui_FirstTimeLanguageDialog):
""" """
Constructor Constructor
""" """
super(FirstTimeLanguageForm, self).__init__(parent) super(FirstTimeLanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.qm_list = LanguageManager.get_qm_list() self.qm_list = LanguageManager.get_qm_list()
self.language_combo_box.addItem('Autodetect') self.language_combo_box.addItem('Autodetect')

View File

@ -25,7 +25,7 @@ Custom tags can be defined and saved. The Custom Tag arrays are saved in a json
Base Tags cannot be changed. Base Tags cannot be changed.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import translate from openlp.core.common import translate
from openlp.core.lib import FormattingTags from openlp.core.lib import FormattingTags
@ -51,7 +51,8 @@ class FormattingTagForm(QtWidgets.QDialog, Ui_FormattingTagDialog, FormattingTag
""" """
Constructor Constructor
""" """
super(FormattingTagForm, self).__init__(parent) super(FormattingTagForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self._setup() self._setup()

View File

@ -24,7 +24,7 @@ The actual plugin view form
""" """
import logging import logging
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import RegistryProperties, translate from openlp.core.common import RegistryProperties, translate
from openlp.core.lib import PluginStatus from openlp.core.lib import PluginStatus
@ -41,7 +41,8 @@ class PluginForm(QtWidgets.QDialog, Ui_PluginViewDialog, RegistryProperties):
""" """
Constructor Constructor
""" """
super(PluginForm, self).__init__(parent) super(PluginForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.active_plugin = None self.active_plugin = None
self.programatic_change = False self.programatic_change = False
self.setupUi(self) self.setupUi(self)

View File

@ -112,7 +112,8 @@ class PrintServiceForm(QtWidgets.QDialog, Ui_PrintServiceDialog, RegistryPropert
""" """
Constructor Constructor
""" """
super(PrintServiceForm, self).__init__(Registry().get('main_window')) super(PrintServiceForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.printer = QtPrintSupport.QPrinter() self.printer = QtPrintSupport.QPrinter()
self.print_dialog = QtPrintSupport.QPrintDialog(self.printer, self) self.print_dialog = QtPrintSupport.QPrintDialog(self.printer, self)
self.document = QtGui.QTextDocument() self.document = QtGui.QTextDocument()

View File

@ -144,7 +144,8 @@ class ProjectorEditForm(QDialog, Ui_ProjectorEditForm):
editProjector = pyqtSignal(object) editProjector = pyqtSignal(object)
def __init__(self, parent=None, projectordb=None): def __init__(self, parent=None, projectordb=None):
super(ProjectorEditForm, self).__init__(parent=parent) super(ProjectorEditForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.projectordb = projectordb self.projectordb = projectordb
self.setupUi(self) self.setupUi(self)
self.button_box.accepted.connect(self.accept_me) self.button_box.accepted.connect(self.accept_me)

View File

@ -236,7 +236,8 @@ class SourceSelectTabs(QDialog):
:param projectordb: ProjectorDB session to use :param projectordb: ProjectorDB session to use
""" """
log.debug('Initializing SourceSelectTabs()') log.debug('Initializing SourceSelectTabs()')
super(SourceSelectTabs, self).__init__(parent) super(SourceSelectTabs, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setMinimumWidth(350) self.setMinimumWidth(350)
self.projectordb = projectordb self.projectordb = projectordb
self.edit = edit self.edit = edit
@ -385,7 +386,8 @@ class SourceSelectSingle(QDialog):
""" """
log.debug('Initializing SourceSelectSingle()') log.debug('Initializing SourceSelectSingle()')
self.projectordb = projectordb self.projectordb = projectordb
super(SourceSelectSingle, self).__init__(parent) super(SourceSelectSingle, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.edit = edit self.edit = edit
if self.edit: if self.edit:
title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text') title = translate('OpenLP.SourceSelectForm', 'Edit Projector Source Text')

View File

@ -22,7 +22,7 @@
""" """
The service item edit dialog The service item edit dialog
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties from openlp.core.common import Registry, RegistryProperties
@ -37,7 +37,8 @@ class ServiceItemEditForm(QtWidgets.QDialog, Ui_ServiceItemEditDialog, RegistryP
""" """
Constructor Constructor
""" """
super(ServiceItemEditForm, self).__init__(Registry().get('main_window')) super(ServiceItemEditForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.item_list = [] self.item_list = []
self.list_widget.currentRowChanged.connect(self.on_current_row_changed) self.list_widget.currentRowChanged.connect(self.on_current_row_changed)

View File

@ -22,7 +22,7 @@
""" """
The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm` class. The :mod:`~openlp.core.ui.servicenoteform` module contains the `ServiceNoteForm` class.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, RegistryProperties, translate from openlp.core.common import Registry, RegistryProperties, translate
from openlp.core.lib import SpellTextEdit from openlp.core.lib import SpellTextEdit
@ -37,7 +37,8 @@ class ServiceNoteForm(QtWidgets.QDialog, RegistryProperties):
""" """
Constructor Constructor
""" """
super(ServiceNoteForm, self).__init__(Registry().get('main_window')) super(ServiceNoteForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()

View File

@ -46,7 +46,8 @@ class SettingsForm(QtWidgets.QDialog, Ui_SettingsDialog, RegistryProperties):
""" """
Registry().register('settings_form', self) Registry().register('settings_form', self)
Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up) Registry().register_function('bootstrap_post_set_up', self.bootstrap_post_set_up)
super(SettingsForm, self).__init__(parent) super(SettingsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.processes = [] self.processes = []
self.setupUi(self) self.setupUi(self)
self.setting_list_widget.currentRowChanged.connect(self.list_item_changed) self.setting_list_widget.currentRowChanged.connect(self.list_item_changed)

View File

@ -44,7 +44,8 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
""" """
Constructor Constructor
""" """
super(ShortcutListForm, self).__init__(parent) super(ShortcutListForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.changed_actions = {} self.changed_actions = {}
self.action_list = ActionList.get_instance() self.action_list = ActionList.get_instance()

View File

@ -22,7 +22,7 @@
""" """
The actual start time form. The actual start time form.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from .starttimedialog import Ui_StartTimeDialog from .starttimedialog import Ui_StartTimeDialog
@ -38,7 +38,8 @@ class StartTimeForm(QtWidgets.QDialog, Ui_StartTimeDialog, RegistryProperties):
""" """
Constructor Constructor
""" """
super(StartTimeForm, self).__init__(Registry().get('main_window')) super(StartTimeForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self): def exec(self):

View File

@ -36,10 +36,11 @@ class AlertForm(QtWidgets.QDialog, Ui_AlertDialog):
""" """
Initialise the alert form Initialise the alert form
""" """
super(AlertForm, self).__init__(Registry().get('main_window'),
QtCore.Qt.WindowSystemMenuHint | QtCore.Qt.WindowTitleHint)
self.manager = plugin.manager self.manager = plugin.manager
self.plugin = plugin self.plugin = plugin
self.item_id = None self.item_id = None
super(AlertForm, self).__init__(Registry().get('main_window'))
self.setupUi(self) self.setupUi(self)
self.display_button.clicked.connect(self.on_display_clicked) self.display_button.clicked.connect(self.on_display_clicked)
self.display_close_button.clicked.connect(self.on_display_close_clicked) self.display_close_button.clicked.connect(self.on_display_close_clicked)

View File

@ -49,7 +49,8 @@ class BookNameForm(QDialog, Ui_BookNameDialog):
""" """
Constructor Constructor
""" """
super(BookNameForm, self).__init__(parent) super(BookNameForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.custom_signals() self.custom_signals()
self.book_names = BibleStrings().BookNames self.book_names = BibleStrings().BookNames

View File

@ -24,7 +24,7 @@ import logging
import os import os
import re import re
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import RegistryProperties, UiStrings, translate from openlp.core.common import RegistryProperties, UiStrings, translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -45,7 +45,8 @@ class EditBibleForm(QtWidgets.QDialog, Ui_EditBibleDialog, RegistryProperties):
""" """
Constructor Constructor
""" """
super(EditBibleForm, self).__init__(parent) super(EditBibleForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.media_item = media_item self.media_item = media_item
self.book_names = BibleStrings().BookNames self.book_names = BibleStrings().BookNames
self.setupUi(self) self.setupUi(self)

View File

@ -26,6 +26,7 @@ Module implementing LanguageForm.
import logging import logging
from PyQt5.QtWidgets import QDialog from PyQt5.QtWidgets import QDialog
from PyQt5 import QtCore
from openlp.core.common import translate from openlp.core.common import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -46,7 +47,8 @@ class LanguageForm(QDialog, Ui_LanguageDialog):
""" """
Constructor Constructor
""" """
super(LanguageForm, self).__init__(parent) super(LanguageForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self, bible_name): def exec(self, bible_name):

View File

@ -254,6 +254,7 @@ class BibleMediaItem(MediaManagerItem):
def on_focus(self): def on_focus(self):
if self.quickTab.isVisible(): if self.quickTab.isVisible():
self.quick_search_edit.setFocus() self.quick_search_edit.setFocus()
self.quick_search_edit.selectAll()
else: else:
self.advanced_book_combo_box.setFocus() self.advanced_book_combo_box.setFocus()

View File

@ -22,7 +22,7 @@
import logging import logging
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import Registry, translate from openlp.core.common import Registry, translate
from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box from openlp.core.lib.ui import critical_error_message_box, find_and_set_in_combo_box
@ -44,7 +44,8 @@ class EditCustomForm(QtWidgets.QDialog, Ui_CustomEditDialog):
""" """
Constructor Constructor
""" """
super(EditCustomForm, self).__init__(parent) super(EditCustomForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.manager = manager self.manager = manager
self.media_item = media_item self.media_item = media_item
self.setupUi(self) self.setupUi(self)

View File

@ -22,7 +22,7 @@
import logging import logging
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from .editcustomslidedialog import Ui_CustomSlideEditDialog from .editcustomslidedialog import Ui_CustomSlideEditDialog
@ -39,7 +39,8 @@ class EditCustomSlideForm(QtWidgets.QDialog, Ui_CustomSlideEditDialog):
""" """
Constructor Constructor
""" """
super(EditCustomSlideForm, self).__init__(parent) super(EditCustomSlideForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
# Connecting signals and slots # Connecting signals and slots
self.insert_button.clicked.connect(self.on_insert_button_clicked) self.insert_button.clicked.connect(self.on_insert_button_clicked)

View File

@ -190,8 +190,7 @@ class CustomMediaItem(MediaManagerItem):
if QtWidgets.QMessageBox.question( if QtWidgets.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('CustomPlugin.MediaItem', translate('CustomPlugin.MediaItem',
'Are you sure you want to delete the %n selected custom slide(s)?', 'Are you sure you want to delete the "%d" selected custom slide(s)?') %len(items),
'', QtCore.QCoreApplication.CodecForTr, len(items)),
QtWidgets.QMessageBox.StandardButtons( QtWidgets.QMessageBox.StandardButtons(
QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
@ -208,6 +207,7 @@ class CustomMediaItem(MediaManagerItem):
Set the focus Set the focus
""" """
self.search_text_edit.setFocus() self.search_text_edit.setFocus()
self.search_text_edit.selectAll()
def generate_slide_data(self, service_item, item=None, xml_version=False, def generate_slide_data(self, service_item, item=None, xml_version=False,
remote=False, context=ServiceItemContext.Service): remote=False, context=ServiceItemContext.Service):

View File

@ -20,7 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import translate from openlp.core.common import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -35,7 +35,8 @@ class AddGroupForm(QtWidgets.QDialog, Ui_AddGroupDialog):
""" """
Constructor Constructor
""" """
super(AddGroupForm, self).__init__(parent) super(AddGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self, clear=True, show_top_level_group=False, selected_group=None): def exec(self, clear=True, show_top_level_group=False, selected_group=None):

View File

@ -20,7 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.plugins.images.forms.choosegroupdialog import Ui_ChooseGroupDialog from openlp.plugins.images.forms.choosegroupdialog import Ui_ChooseGroupDialog
@ -33,7 +33,8 @@ class ChooseGroupForm(QtWidgets.QDialog, Ui_ChooseGroupDialog):
""" """
Constructor Constructor
""" """
super(ChooseGroupForm, self).__init__(parent) super(ChooseGroupForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self, selected_group=None): def exec(self, selected_group=None):

View File

@ -52,7 +52,8 @@ class MediaClipSelectorForm(QtWidgets.QDialog, Ui_MediaClipSelector, RegistryPro
""" """
Constructor Constructor
""" """
super(MediaClipSelectorForm, self).__init__(parent) super(MediaClipSelectorForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.vlc_instance = None self.vlc_instance = None
self.vlc_media_player = None self.vlc_media_player = None
self.vlc_media = None self.vlc_media = None

View File

@ -272,7 +272,7 @@ window.OpenLP = {
value[0] = OpenLP.escapeString(value[0]) value[0] = OpenLP.escapeString(value[0])
} }
var txt = ""; var txt = "";
if (value[2].length > 0) { if (value.length > 2) {
txt = value[1] + " ( " + value[2] + " )"; txt = value[1] + " ( " + value[2] + " )";
} else { } else {
txt = value[1]; txt = value[1];

View File

@ -20,7 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -35,7 +35,8 @@ class AuthorsForm(QtWidgets.QDialog, Ui_AuthorsDialog):
""" """
Set up the screen and common data Set up the screen and common data
""" """
super(AuthorsForm, self).__init__(parent) super(AuthorsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.auto_display_name = False self.auto_display_name = False
self.first_name_edit.textEdited.connect(self.on_first_name_edited) self.first_name_edit.textEdited.connect(self.on_first_name_edited)

View File

@ -55,7 +55,8 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
""" """
Constructor Constructor
""" """
super(EditSongForm, self).__init__(parent) super(EditSongForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.media_item = media_item self.media_item = media_item
self.song = None self.song = None
# can this be automated? # can this be automated?
@ -76,7 +77,7 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
self.verse_edit_all_button.clicked.connect(self.on_verse_edit_all_button_clicked) self.verse_edit_all_button.clicked.connect(self.on_verse_edit_all_button_clicked)
self.verse_delete_button.clicked.connect(self.on_verse_delete_button_clicked) self.verse_delete_button.clicked.connect(self.on_verse_delete_button_clicked)
self.verse_list_widget.itemClicked.connect(self.on_verse_list_view_clicked) self.verse_list_widget.itemClicked.connect(self.on_verse_list_view_clicked)
self.verse_order_edit.textChanged.connect(self.on_verse_order_text_changed) self.verse_order_edit.textEdited.connect(self.on_verse_order_text_changed)
self.theme_add_button.clicked.connect(self.theme_manager.on_add_theme) self.theme_add_button.clicked.connect(self.theme_manager.on_add_theme)
self.maintenance_button.clicked.connect(self.on_maintenance_button_clicked) self.maintenance_button.clicked.connect(self.on_maintenance_button_clicked)
self.from_file_button.clicked.connect(self.on_audio_add_from_file_button_clicked) self.from_file_button.clicked.connect(self.on_audio_add_from_file_button_clicked)
@ -803,6 +804,8 @@ class EditSongForm(QtWidgets.QDialog, Ui_EditSongDialog, RegistryProperties):
:param text: The text of the verse order edit (ignored). :param text: The text of the verse order edit (ignored).
""" """
# First make sure that all letters entered in the verse order field are uppercase
self.verse_order_edit.setText(text.upper())
# Extract all verses which were used in the order. # Extract all verses which were used in the order.
verses_in_order = self._extract_verse_order(self.verse_order_edit.text()) verses_in_order = self._extract_verse_order(self.verse_order_edit.text())
# Find the verses which were not used in the order. # Find the verses which were not used in the order.

View File

@ -23,7 +23,7 @@
import re import re
import logging import logging
from PyQt5 import QtGui, QtWidgets from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.plugins.songs.lib import VerseType from openlp.plugins.songs.lib import VerseType
from .editversedialog import Ui_EditVerseDialog from .editversedialog import Ui_EditVerseDialog
@ -41,7 +41,8 @@ class EditVerseForm(QtWidgets.QDialog, Ui_EditVerseDialog):
""" """
Constructor Constructor
""" """
super(EditVerseForm, self).__init__(parent) super(EditVerseForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.has_single_verse = False self.has_single_verse = False
self.insert_button.clicked.connect(self.on_insert_button_clicked) self.insert_button.clicked.connect(self.on_insert_button_clicked)

View File

@ -37,7 +37,8 @@ class MediaFilesForm(QtWidgets.QDialog, Ui_MediaFilesDialog):
log.info('%s MediaFilesForm loaded', __name__) log.info('%s MediaFilesForm loaded', __name__)
def __init__(self, parent): def __init__(self, parent):
super(MediaFilesForm, self).__init__() super(MediaFilesForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def populate_files(self, files): def populate_files(self, files):

View File

@ -23,7 +23,7 @@
This module contains the song book form This module contains the song book form
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -38,7 +38,8 @@ class SongBookForm(QtWidgets.QDialog, Ui_SongBookDialog):
""" """
Constructor Constructor
""" """
super(SongBookForm, self).__init__(parent) super(SongBookForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self, clear=True): def exec(self, clear=True):

View File

@ -44,7 +44,8 @@ class SongMaintenanceForm(QtWidgets.QDialog, Ui_SongMaintenanceDialog, RegistryP
""" """
Constructor Constructor
""" """
super(SongMaintenanceForm, self).__init__(parent) super(SongMaintenanceForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.manager = manager self.manager = manager
self.author_form = AuthorsForm(self) self.author_form = AuthorsForm(self)

View File

@ -81,7 +81,8 @@ class SongSelectForm(QtWidgets.QDialog, Ui_SongSelectDialog):
""" """
def __init__(self, parent=None, plugin=None, db_manager=None): def __init__(self, parent=None, plugin=None, db_manager=None):
QtWidgets.QDialog.__init__(self, parent) QtWidgets.QDialog.__init__(self, parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.plugin = plugin self.plugin = plugin
self.db_manager = db_manager self.db_manager = db_manager
self.setup_ui(self) self.setup_ui(self)

View File

@ -23,7 +23,7 @@
This module contains the topic edit form. This module contains the topic edit form.
""" """
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.lib import translate from openlp.core.lib import translate
from openlp.core.lib.ui import critical_error_message_box from openlp.core.lib.ui import critical_error_message_box
@ -38,7 +38,8 @@ class TopicsForm(QtWidgets.QDialog, Ui_TopicsDialog):
""" """
Constructor Constructor
""" """
super(TopicsForm, self).__init__(parent) super(TopicsForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
def exec(self, clear=True): def exec(self, clear=True):

View File

@ -112,6 +112,7 @@ class SongMediaItem(MediaManagerItem):
def on_focus(self): def on_focus(self):
self.search_text_edit.setFocus() self.search_text_edit.setFocus()
self.search_text_edit.selectAll()
def config_update(self): def config_update(self):
""" """
@ -363,8 +364,8 @@ class SongMediaItem(MediaManagerItem):
items = self.list_view.selectedIndexes() items = self.list_view.selectedIndexes()
if QtWidgets.QMessageBox.question( if QtWidgets.QMessageBox.question(
self, UiStrings().ConfirmDelete, self, UiStrings().ConfirmDelete,
translate('SongsPlugin.MediaItem', 'Are you sure you want to delete the %n selected song(s)?', '', translate('SongsPlugin.MediaItem',
QtCore.QCoreApplication.CodecForTr, len(items)), 'Are you sure you want to delete the "%d" selected song(s)?') % len(items),
QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No), QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No),
QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No:
return return

View File

@ -20,7 +20,7 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from openlp.core.common import RegistryProperties, translate from openlp.core.common import RegistryProperties, translate
from openlp.plugins.songusage.lib.db import SongUsageItem from openlp.plugins.songusage.lib.db import SongUsageItem
@ -36,7 +36,8 @@ class SongUsageDeleteForm(QtWidgets.QDialog, Ui_SongUsageDeleteDialog, RegistryP
Constructor Constructor
""" """
self.manager = manager self.manager = manager
super(SongUsageDeleteForm, self).__init__(parent) super(SongUsageDeleteForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.setupUi(self) self.setupUi(self)
self.button_box.clicked.connect(self.on_button_box_clicked) self.button_box.clicked.connect(self.on_button_box_clicked)

View File

@ -23,7 +23,7 @@
import logging import logging
import os import os
from PyQt5 import QtWidgets from PyQt5 import QtCore, QtWidgets
from sqlalchemy.sql import and_ from sqlalchemy.sql import and_
from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate
@ -44,7 +44,8 @@ class SongUsageDetailForm(QtWidgets.QDialog, Ui_SongUsageDetailDialog, RegistryP
""" """
Initialise the form Initialise the form
""" """
super(SongUsageDetailForm, self).__init__(parent) super(SongUsageDetailForm, self).__init__(parent, QtCore.Qt.WindowSystemMenuHint
| QtCore.Qt.WindowTitleHint)
self.plugin = plugin self.plugin = plugin
self.setupUi(self) self.setupUi(self)

View File

@ -22,26 +22,3 @@
""" """
Module-level functions for the functional test suite Module-level functions for the functional test suite
""" """
import os
from tests.functional import patch
from openlp.core.common import is_win
from .test_projectordb import tmpfile
def setUp():
if not is_win():
# Wine creates a sharing violation during tests. Ignore.
try:
os.remove(tmpfile)
except:
pass
def tearDown():
"""
Ensure test suite has been cleaned up after tests
"""
patch.stopall()

View File

@ -60,6 +60,22 @@ class TestMediaManagerItem(TestCase, TestMixin):
# THEN: on_preview_click() should have been called # THEN: on_preview_click() should have been called
mocked_on_preview_click.assert_called_with() mocked_on_preview_click.assert_called_with()
def required_icons_test(self):
"""
Test the default icons for plugins
"""
# GIVEN: A MediaManagerItem
mmi = MediaManagerItem(None)
# WHEN: Object is created
mmi.required_icons()
# THEN: Default icons should be populated
self.assertFalse(mmi.has_import_icon, 'There should be no import icon by default')
self.assertTrue(mmi.has_new_icon, 'By default a new icon should be present')
self.assertFalse(mmi.has_file_icon, 'There should be no file icon by default')
self.assertTrue(mmi.has_delete_icon, 'By default a delete icon should be present')
self.assertFalse(mmi.add_to_service_item, 'There should be no add_to_service icon by default')
@patch(u'openlp.core.lib.mediamanageritem.Settings') @patch(u'openlp.core.lib.mediamanageritem.Settings')
@patch(u'openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click') @patch(u'openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click')
def on_double_clicked_go_live_test(self, mocked_on_live_click, MockedSettings): def on_double_clicked_go_live_test(self, mocked_on_live_click, MockedSettings):

View File

@ -25,15 +25,13 @@ record functions.
PREREQUISITE: add_record() and get_all() functions validated. PREREQUISITE: add_record() and get_all() functions validated.
""" """
import os
from unittest import TestCase from unittest import TestCase
from tests.functional import MagicMock, patch
from openlp.core.lib.projector.db import Projector, ProjectorDB, ProjectorSource from openlp.core.lib.projector.db import Projector, ProjectorDB, ProjectorSource
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.functional import MagicMock, patch
from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
tmpfile = '/tmp/openlp-test-projectordb.sql'
def compare_data(one, two): def compare_data(one, two):
@ -60,15 +58,15 @@ def compare_source(one, two):
one.text == two.text one.text == two.text
def add_records(self, test): def add_records(projector_db, test):
""" """
Add record if not in database Add record if not in database
""" """
record_list = self.projector.get_projector_all() record_list = projector_db.get_projector_all()
if len(record_list) < 1: if len(record_list) < 1:
added = False added = False
for record in test: for record in test:
added = self.projector.add_projector(record) or added added = projector_db.add_projector(record) or added
return added return added
for new_record in test: for new_record in test:
@ -76,7 +74,7 @@ def add_records(self, test):
for record in record_list: for record in record_list:
if compare_data(record, new_record): if compare_data(record, new_record):
break break
added = self.projector.add_projector(new_record) added = projector_db.add_projector(new_record)
return added return added
@ -88,15 +86,17 @@ class TestProjectorDB(TestCase):
""" """
Set up anything necessary for all tests Set up anything necessary for all tests
""" """
if not hasattr(self, 'projector'): with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url: if os.path.exists(TEST_DB):
mocked_init_url.return_value = 'sqlite:///%s' % tmpfile os.unlink(TEST_DB)
self.projector = ProjectorDB() mocked_init_url.return_value = 'sqlite:///%s' % TEST_DB
self.projector = ProjectorDB()
def tearDown(self): def tearDown(self):
""" """
Clean up Clean up
""" """
self.projector.session.close()
self.projector = None self.projector = None
def find_record_by_ip_test(self): def find_record_by_ip_test(self):
@ -104,13 +104,13 @@ class TestProjectorDB(TestCase):
Test find record by IP Test find record by IP
""" """
# GIVEN: Record entries in database # GIVEN: Record entries in database
add_records(self, [TEST1_DATA, TEST2_DATA]) add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)])
# WHEN: Search for record using IP # WHEN: Search for record using IP
record = self.projector.get_projector_by_ip(TEST2_DATA.ip) record = self.projector.get_projector_by_ip(TEST2_DATA['ip'])
# THEN: Verify proper record returned # THEN: Verify proper record returned
self.assertTrue(compare_data(TEST2_DATA, record), self.assertTrue(compare_data(Projector(**TEST2_DATA), record),
'Record found should have been test_2 data') 'Record found should have been test_2 data')
def find_record_by_name_test(self): def find_record_by_name_test(self):
@ -118,13 +118,13 @@ class TestProjectorDB(TestCase):
Test find record by name Test find record by name
""" """
# GIVEN: Record entries in database # GIVEN: Record entries in database
add_records(self, [TEST1_DATA, TEST2_DATA]) add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)])
# WHEN: Search for record using name # WHEN: Search for record using name
record = self.projector.get_projector_by_name(TEST2_DATA.name) record = self.projector.get_projector_by_name(TEST2_DATA['name'])
# THEN: Verify proper record returned # THEN: Verify proper record returned
self.assertTrue(compare_data(TEST2_DATA, record), self.assertTrue(compare_data(Projector(**TEST2_DATA), record),
'Record found should have been test_2 data') 'Record found should have been test_2 data')
def record_delete_test(self): def record_delete_test(self):
@ -132,14 +132,14 @@ class TestProjectorDB(TestCase):
Test record can be deleted Test record can be deleted
""" """
# GIVEN: Record in database # GIVEN: Record in database
add_records(self, [TEST3_DATA, ]) add_records(self.projector, [Projector(**TEST3_DATA), ])
record = self.projector.get_projector_by_ip(TEST3_DATA.ip) record = self.projector.get_projector_by_ip(TEST3_DATA['ip'])
# WHEN: Record deleted # WHEN: Record deleted
self.projector.delete_projector(record) self.projector.delete_projector(record)
# THEN: Verify record not retrievable # THEN: Verify record not retrievable
found = self.projector.get_projector_by_ip(TEST3_DATA.ip) found = self.projector.get_projector_by_ip(TEST3_DATA['ip'])
self.assertFalse(found, 'test_3 record should have been deleted') self.assertFalse(found, 'test_3 record should have been deleted')
def record_edit_test(self): def record_edit_test(self):
@ -147,34 +147,35 @@ class TestProjectorDB(TestCase):
Test edited record returns the same record ID with different data Test edited record returns the same record ID with different data
""" """
# GIVEN: Record entries in database # GIVEN: Record entries in database
add_records(self, [TEST1_DATA, TEST2_DATA]) add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)])
# WHEN: We retrieve a specific record # WHEN: We retrieve a specific record
record = self.projector.get_projector_by_ip(TEST1_DATA.ip) record = self.projector.get_projector_by_ip(TEST1_DATA['ip'])
record_id = record.id record_id = record.id
# WHEN: Data is changed # WHEN: Data is changed
record.ip = TEST3_DATA.ip record.ip = TEST3_DATA['ip']
record.port = TEST3_DATA.port record.port = TEST3_DATA['port']
record.pin = TEST3_DATA.pin record.pin = TEST3_DATA['pin']
record.name = TEST3_DATA.name record.name = TEST3_DATA['name']
record.location = TEST3_DATA.location record.location = TEST3_DATA['location']
record.notes = TEST3_DATA.notes record.notes = TEST3_DATA['notes']
updated = self.projector.update_projector(record) updated = self.projector.update_projector(record)
self.assertTrue(updated, 'Save updated record should have returned True') self.assertTrue(updated, 'Save updated record should have returned True')
record = self.projector.get_projector_by_ip(TEST3_DATA.ip) record = self.projector.get_projector_by_ip(TEST3_DATA['ip'])
# THEN: Record ID should remain the same, but data should be changed # THEN: Record ID should remain the same, but data should be changed
self.assertEqual(record_id, record.id, 'Edited record should have the same ID') self.assertEqual(record_id, record.id, 'Edited record should have the same ID')
self.assertTrue(compare_data(TEST3_DATA, record), 'Edited record should have new data') self.assertTrue(compare_data(Projector(**TEST3_DATA), record), 'Edited record should have new data')
def source_add_test(self): def source_add_test(self):
""" """
Test source entry for projector item Test source entry for projector item
""" """
# GIVEN: Record entries in database # GIVEN: Record entries in database
self.projector.add_projector(TEST1_DATA) projector1 = Projector(**TEST1_DATA)
item = self.projector.get_projector_by_id(TEST1_DATA.id) self.projector.add_projector(projector1)
item = self.projector.get_projector_by_id(projector1.id)
item_id = item.id item_id = item.id
# WHEN: A source entry is saved for item # WHEN: A source entry is saved for item
@ -184,3 +185,4 @@ class TestProjectorDB(TestCase):
# THEN: Projector should have the same source entry # THEN: Projector should have the same source entry
item = self.projector.get_projector_by_id(item_id) item = self.projector.get_projector_by_id(item_id)
self.assertTrue(compare_source(item.source_list[0], source)) self.assertTrue(compare_source(item.source_list[0], source))

View File

@ -22,7 +22,7 @@
""" """
Package to test the openlp.core.ui.slidecontroller package. Package to test the openlp.core.ui.slidecontroller package.
""" """
from unittest import TestCase from unittest import TestCase, skipUnless
from PyQt5 import QtCore from PyQt5 import QtCore
@ -141,13 +141,14 @@ class TestMainDisplay(TestCase, TestMixin):
mocked_songs_plugin.refresh_css.assert_called_with(main_display.frame) mocked_songs_plugin.refresh_css.assert_called_with(main_display.frame)
mocked_bibles_plugin.refresh_css.assert_called_with(main_display.frame) mocked_bibles_plugin.refresh_css.assert_called_with(main_display.frame)
@patch('openlp.core.ui.maindisplay.is_macosx') @skipUnless(is_macosx(), 'Can only run test on Mac OS X due to pyobjc dependency.')
def macosx_display_window_flags_state_test(self, is_macosx): def macosx_display_window_flags_state_test(self, is_macosx):
""" """
Test that on Mac OS X we set the proper window flags Test that on Mac OS X we set the proper window flags
""" """
if not is_macosx():
self.skipTest('Can only run test on Mac OS X due to pyobjc dependency.')
# GIVEN: A new SlideController instance on Mac OS X. # GIVEN: A new SlideController instance on Mac OS X.
is_macosx.return_value = True
self.screens.set_current_display(0) self.screens.set_current_display(0)
display = MagicMock() display = MagicMock()
@ -159,12 +160,11 @@ class TestMainDisplay(TestCase, TestMixin):
main_display.windowFlags(), main_display.windowFlags(),
'The window flags should be Qt.Window, and Qt.FramelessWindowHint.') 'The window flags should be Qt.Window, and Qt.FramelessWindowHint.')
@skipUnless(is_macosx(), 'Can only run test on Mac OS X due to pyobjc dependency.')
def macosx_display_test(self): def macosx_display_test(self):
""" """
Test display on Mac OS X Test display on Mac OS X
""" """
if not is_macosx():
self.skipTest('Can only run test on Mac OS X due to pyobjc dependency.')
# GIVEN: A new SlideController instance on Mac OS X. # GIVEN: A new SlideController instance on Mac OS X.
self.screens.set_current_display(0) self.screens.set_current_display(0)
display = MagicMock() display = MagicMock()

View File

@ -257,3 +257,22 @@ class TestMediaItem(TestCase, TestMixin):
# THEN: They should not match # THEN: They should not match
self.assertFalse(result, "Authors should not match") self.assertFalse(result, "Authors should not match")
def build_remote_search_test(self):
"""
Test results for the remote search api
"""
# GIVEN: A Song and a search a JSON array should be returned.
mock_song = MagicMock()
mock_song.id = 123
mock_song.title = 'My Song'
mock_song.search_title = 'My Song'
mock_song.alternate_title = 'My alternative'
self.media_item.search_entire = MagicMock()
self.media_item.search_entire.return_value = [mock_song]
# WHEN: I process a search
search_results = self.media_item.search('My Song', False)
# THEN: The correct formatted results are returned
self.assertEqual(search_results, [[123, 'My Song', 'My alternative']])

View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2015 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; version 2 of the License. #
# #
# 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, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
"""
This module contains tests for the SongFormat class
"""
from unittest import TestCase
from openlp.plugins.songs.lib.importer import SongFormat
class TestSongFormat(TestCase):
"""
Test the functions in the :class:`SongFormat` class.
"""
def test_get_format_list(self):
"""
Test that get_format_list() returns all available formats
"""
# GIVEN: The SongFormat class
# WHEN: Retrieving the format list
# THEN: All SongFormats should be returned
self.assertEquals(len(SongFormat.get_format_list()), len(SongFormat.__attributes__),
"The returned SongFormats don't match the stored ones")
def test_get_attributed_no_attributes(self):
"""
Test that SongFormat.get(song_format) returns all attributes associated with the given song_format
"""
# GIVEN: A SongFormat
# WHEN: Retrieving all attributes of a SongFormat
for song_format in SongFormat.get_format_list():
# THEN: All attributes associated with the SongFormat should be returned
self.assertEquals(SongFormat.get(song_format), SongFormat.__attributes__[song_format],
"The returned attributes don't match the stored ones")
def test_get_attributed_single_attribute(self):
"""
Test that SongFormat.get(song_format, attribute) returns only one -and the correct- attribute
"""
# GIVEN: A SongFormat
for song_format in SongFormat.get_format_list():
# WHEN: Retrieving an attribute that overrides the default values
for attribute in SongFormat.get(song_format).keys():
# THEN: Return the attribute
self.assertEquals(SongFormat.get(song_format, attribute), SongFormat.get(song_format)[attribute],
"The returned attribute doesn't match the stored one")
# WHEN: Retrieving an attribute that was not overridden
for attribute in SongFormat.__defaults__.keys():
if attribute not in SongFormat.get(song_format).keys():
# THEN: Return the default value
self.assertEquals(SongFormat.get(song_format, attribute), SongFormat.__defaults__[attribute],
"The returned attribute does not match the default values stored")
def test_get_attributed_multiple_attributes(self):
"""
Test that multiple attributes can be retrieved for a song_format
"""
# GIVEN: A SongFormat
# WHEN: Retrieving multiple attributes at the same time
for song_format in SongFormat.get_format_list():
# THEN: Return all attributes that were specified
self.assertEquals(len(SongFormat.get(song_format, 'canDisable', 'availability')), 2,
"Did not return the correct number of attributes when retrieving multiple attributes at once")

View File

@ -22,35 +22,3 @@
""" """
Module-level functions for the functional test suite Module-level functions for the functional test suite
""" """
import os
from openlp.core.common import is_win
from tests.interfaces import patch
from .test_projectormanager import tmpfile
def setUp():
"""
Set up this module of tests
"""
if not is_win():
# Wine creates a sharing violation during tests. Ignore.
try:
os.remove(tmpfile)
except:
pass
def tearDown():
"""
Ensure test suite has been cleaned up after tests
"""
patch.stopall()
if not is_win():
try:
# In case of changed schema, remove old test file
os.remove(tmpfile)
except FileNotFoundError:
pass

View File

@ -23,7 +23,7 @@
Interface tests to test the openlp.core.ui.projector.editform.ProjectorEditForm() Interface tests to test the openlp.core.ui.projector.editform.ProjectorEditForm()
class and methods. class and methods.
""" """
import os
from unittest import TestCase from unittest import TestCase
from openlp.core.common import Registry, Settings from openlp.core.common import Registry, Settings
@ -32,7 +32,7 @@ from openlp.core.ui import ProjectorEditForm
from tests.functional import patch from tests.functional import patch
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA
class TestProjectorEditForm(TestCase, TestMixin): class TestProjectorEditForm(TestCase, TestMixin):
@ -49,7 +49,9 @@ class TestProjectorEditForm(TestCase, TestMixin):
self.setup_application() self.setup_application()
Registry.create() Registry.create()
with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url: with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
mocked_init_url.return_value = 'sqlite://' if os.path.exists(TEST_DB):
os.unlink(TEST_DB)
mocked_init_url.return_value = 'sqlite:///' + TEST_DB
self.projectordb = ProjectorDB() self.projectordb = ProjectorDB()
self.projector_form = ProjectorEditForm(projectordb=self.projectordb) self.projector_form = ProjectorEditForm(projectordb=self.projectordb)
@ -93,11 +95,13 @@ class TestProjectorEditForm(TestCase, TestMixin):
with patch('openlp.core.ui.projector.editform.QDialog.exec'): with patch('openlp.core.ui.projector.editform.QDialog.exec'):
# WHEN: Calling edit form with existing projector instance # WHEN: Calling edit form with existing projector instance
self.projector_form.exec(projector=TEST1_DATA) self.projector_form.exec(projector=Projector(**TEST1_DATA))
item = self.projector_form.projector item = self.projector_form.projector
# THEN: Should be editing an existing entry # THEN: Should be editing an existing entry
self.assertFalse(self.projector_form.new_projector, self.assertFalse(self.projector_form.new_projector,
'Projector edit form should be marked as existing entry') 'Projector edit form should be marked as existing entry')
self.assertTrue((item.ip is TEST1_DATA.ip and item.name is TEST1_DATA.name), self.assertTrue((item.ip is TEST1_DATA['ip'] and item.name is TEST1_DATA['name']),
'Projector edit form should have TEST1_DATA() instance to edit') 'Projector edit form should have TEST1_DATA() instance to edit')

View File

@ -22,7 +22,6 @@
""" """
Interface tests to test the themeManager class and related methods. Interface tests to test the themeManager class and related methods.
""" """
import os import os
from unittest import TestCase from unittest import TestCase
@ -33,9 +32,7 @@ from tests.helpers.testmixin import TestMixin
from openlp.core.ui import ProjectorManager, ProjectorEditForm from openlp.core.ui import ProjectorManager, ProjectorEditForm
from openlp.core.lib.projector.db import Projector, ProjectorDB from openlp.core.lib.projector.db import Projector, ProjectorDB
from tests.resources.projector.data import TEST1_DATA, TEST2_DATA, TEST3_DATA from tests.resources.projector.data import TEST_DB, TEST1_DATA, TEST2_DATA, TEST3_DATA
tmpfile = '/tmp/openlp-test-projectormanager.sql'
class TestProjectorManager(TestCase, TestMixin): class TestProjectorManager(TestCase, TestMixin):
@ -49,12 +46,13 @@ class TestProjectorManager(TestCase, TestMixin):
self.build_settings() self.build_settings()
self.setup_application() self.setup_application()
Registry.create() Registry.create()
if not hasattr(self, 'projector_manager'): with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url:
with patch('openlp.core.lib.projector.db.init_url') as mocked_init_url: if os.path.exists(TEST_DB):
mocked_init_url.return_value = 'sqlite:///%s' % tmpfile os.unlink(TEST_DB)
self.projectordb = ProjectorDB() mocked_init_url.return_value = 'sqlite:///%s' % TEST_DB
if not hasattr(self, 'projector_manager'): self.projectordb = ProjectorDB()
self.projector_manager = ProjectorManager(projectordb=self.projectordb) if not hasattr(self, 'projector_manager'):
self.projector_manager = ProjectorManager(projectordb=self.projectordb)
def tearDown(self): def tearDown(self):
""" """

View File

@ -20,24 +20,24 @@
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
""" """
:mod: `tests.interfaces.openlp_core_ui.test_projectorsourceform` module :mod: `tests.interfaces.openlp_core_ui.test_projectorsourceform` module
Tests for the Projector Source Select form. Tests for the Projector Source Select form.
""" """
import logging import logging
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
log.debug('test_projectorsourceform loaded') log.debug('test_projectorsourceform loaded')
import os
from unittest import TestCase from unittest import TestCase
from PyQt5.QtWidgets import QDialog from PyQt5.QtWidgets import QDialog
from tests.functional import patch from tests.functional import patch
from tests.functional.openlp_core_lib.test_projectordb import tmpfile
from tests.helpers.testmixin import TestMixin from tests.helpers.testmixin import TestMixin
from tests.resources.projector.data import TEST_DB, TEST1_DATA from tests.resources.projector.data import TEST_DB, TEST1_DATA
from openlp.core.common import Registry, Settings from openlp.core.common import Registry, Settings
from openlp.core.lib.projector.db import ProjectorDB from openlp.core.lib.projector.db import ProjectorDB, Projector
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES
from openlp.core.ui.projector.sourceselectform import source_group, SourceSelectSingle from openlp.core.ui.projector.sourceselectform import source_group, SourceSelectSingle
@ -65,7 +65,9 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
""" """
Set up anything necessary for all tests Set up anything necessary for all tests
""" """
mocked_init_url.return_value = 'sqlite:///{}'.format(tmpfile) if os.path.exists(TEST_DB):
os.unlink(TEST_DB)
mocked_init_url.return_value = 'sqlite:///{}'.format(TEST_DB)
self.build_settings() self.build_settings()
self.setup_application() self.setup_application()
Registry.create() Registry.create()
@ -73,10 +75,10 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
if not hasattr(self, 'projectordb'): if not hasattr(self, 'projectordb'):
self.projectordb = ProjectorDB() self.projectordb = ProjectorDB()
# Retrieve/create a database record # Retrieve/create a database record
self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip) self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA['ip'])
if not self.projector: if not self.projector:
self.projectordb.add_projector(projector=TEST1_DATA) self.projectordb.add_projector(projector=Projector(**TEST1_DATA))
self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip) self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA['ip'])
self.projector.dbid = self.projector.id self.projector.dbid = self.projector.id
self.projector.db_item = self.projector self.projector.db_item = self.projector

View File

@ -130,7 +130,7 @@ class TestBibleHTTP(TestCase):
# THEN: The list should not be None, and some known bibles should be there # THEN: The list should not be None, and some known bibles should be there
self.assertIsNotNone(bibles) self.assertIsNotNone(bibles)
self.assertIn(('New Int. Readers Version', 'NIRV', 'en'), bibles) self.assertIn(('New Int. Readers Version', 'NIRV', 'en'), bibles)
self.assertIn(('Българската Библия', 'BLG', 'bg'), bibles) self.assertIn(('Священное Писание, Восточный перевод', 'CARS', 'ru'), bibles)
def biblegateway_get_bibles_test(self): def biblegateway_get_bibles_test(self):
""" """

View File

@ -142,3 +142,18 @@ class TestEditSongForm(TestCase, TestMixin):
'CCLI label should not be "{}"'.format(UiStrings().CCLINumberLabel)) 'CCLI label should not be "{}"'.format(UiStrings().CCLINumberLabel))
self.assertEquals(form.ccli_label.text(), UiStrings().CCLISongNumberLabel, self.assertEquals(form.ccli_label.text(), UiStrings().CCLISongNumberLabel,
'CCLI label text should be "{}"'.format(UiStrings().CCLISongNumberLabel)) 'CCLI label text should be "{}"'.format(UiStrings().CCLISongNumberLabel))
def verse_order_lowercase_test(self):
"""
Test that entering a verse order in lowercase automatically converts to uppercase
"""
# GIVEN; Mocked methods
form = self.form
# WHEN: We enter a verse order in lowercase
form.verse_order_edit.setText('v1 v2 c1 v3 c1 v4 c1')
# Need to manually trigger this method as it is only triggered by manual input
form.on_verse_order_text_changed(form.verse_order_edit.text())
# THEN: The verse order should be converted to uppercase
self.assertEqual(form.verse_order_edit.text(), 'V1 V2 C1 V3 C1 V4 C1')

View File

@ -24,28 +24,28 @@ The :mod:`tests.resources.projector.data file contains test data
""" """
import os import os
from openlp.core.lib.projector.db import Projector from tempfile import gettempdir
# Test data # Test data
TEST_DB = os.path.join('tmp', 'openlp-test-projectordb.sql') TEST_DB = os.path.join(gettempdir(), 'openlp-test-projectordb.sql')
TEST1_DATA = Projector(ip='111.111.111.111', TEST1_DATA = dict(ip='111.111.111.111',
port='1111', port='1111',
pin='1111', pin='1111',
name='___TEST_ONE___', name='___TEST_ONE___',
location='location one', location='location one',
notes='notes one') notes='notes one')
TEST2_DATA = Projector(ip='222.222.222.222', TEST2_DATA = dict(ip='222.222.222.222',
port='2222', port='2222',
pin='2222', pin='2222',
name='___TEST_TWO___', name='___TEST_TWO___',
location='location two', location='location two',
notes='notes two') notes='notes two')
TEST3_DATA = Projector(ip='333.333.333.333', TEST3_DATA = dict(ip='333.333.333.333',
port='3333', port='3333',
pin='3333', pin='3333',
name='___TEST_THREE___', name='___TEST_THREE___',
location='location three', location='location three',
notes='notes three') notes='notes three')