diff --git a/openlp/core/display/screens.py b/openlp/core/display/screens.py index ac72bf55d..9b53347e9 100644 --- a/openlp/core/display/screens.py +++ b/openlp/core/display/screens.py @@ -23,7 +23,6 @@ The :mod:`screen` module provides management functionality for a machines' displays. """ -import json import logging from PyQt5 import QtCore @@ -90,6 +89,30 @@ class ScreenList(object): for screen in self.screens: yield screen + def __len__(self): + """ + Make sure we can call "len" on this object + """ + return len(self.screens) + + @property + def current(self): + """ + Return the first "current" desktop + + NOTE: This is a HACK to ease the upgrade process + """ + # Get the first display screen + for screen in self.screens: + if screen.is_display: + return screen + # If there's no display screen, get the first primary screen + for screen in self.screens: + if screen.is_primary: + return screen + # Otherwise just return the first screen + return self.screens[0] + @classmethod def create(cls, desktop): """ @@ -100,7 +123,7 @@ class ScreenList(object): screen_list = cls() screen_list.desktop = desktop screen_list.screens = [] - screen_list.screen_count_changed() + screen_list.on_screen_count_changed() screen_list.load_screen_settings() screen_list.desktop.resized.connect(screen_list.on_screen_resolution_changed) screen_list.desktop.screenCountChanged.connect(screen_list.on_screen_count_changed) @@ -138,7 +161,7 @@ class ScreenList(object): # Add new screens. for number in range(self.desktop.screenCount()): if not self.has_screen(number): - self.screens.append(Screen(number, self.desktop.getGeometry(number), + self.screens.append(Screen(number, self.desktop.screenGeometry(number), self.desktop.primaryScreen() == number)) # We do not want to send this message at start up. if changed_screen is not None: @@ -201,7 +224,7 @@ class ScreenList(object): 'core/monitors': '{}' } Settings.extend_default_settings(screen_settings) - monitors = json.loads(Settings().value('core/monitors')) + monitors = Settings().value('core/monitors') for screen in self.screens: monitor = monitors.get(screen.number) if monitor: diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py index 81a1b659d..0317b8320 100644 --- a/openlp/core/lib/imagemanager.py +++ b/openlp/core/lib/imagemanager.py @@ -176,8 +176,8 @@ class ImageManager(QtCore.QObject): super(ImageManager, self).__init__() Registry().register('image_manager', self) current_screen = ScreenList().current - self.width = current_screen['size'].width() - self.height = current_screen['size'].height() + self.width = current_screen.display_geometry.width() + self.height = current_screen.display_geometry.height() self._cache = {} self.image_thread = ImageThread(self) self._conversion_queue = PriorityQueue() @@ -190,8 +190,8 @@ class ImageManager(QtCore.QObject): """ log.debug('update_display') current_screen = ScreenList().current - self.width = current_screen['size'].width() - self.height = current_screen['size'].height() + self.width = current_screen.display_geometry.width() + self.height = current_screen.display_geometry.height() # Mark the images as dirty for a rebuild by setting the image and byte stream to None. for image in list(self._cache.values()): self._reset_image(image) diff --git a/openlp/core/widgets/docks.py b/openlp/core/widgets/docks.py index a1b4e9789..8bc3cb04f 100644 --- a/openlp/core/widgets/docks.py +++ b/openlp/core/widgets/docks.py @@ -48,7 +48,7 @@ class OpenLPDockWidget(QtWidgets.QDockWidget): self.setWindowIcon(build_icon(icon)) # Sort out the minimum width. screens = ScreenList() - main_window_docbars = screens.current['size'].width() // 5 + main_window_docbars = screens.current.display_geometry.width() // 5 if main_window_docbars > 300: self.setMinimumWidth(300) else: diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 715e4e3e7..369c54437 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -249,7 +249,7 @@ class PdfDocument(PresentationDocument): self.image_files.append(image_path) self.num_pages = len(self.image_files) return True - size = ScreenList().current['size'] + size = ScreenList().current.display_geometry # Generate images from PDF that will fit the frame. runlog = '' try: diff --git a/tests/functional/openlp_core/display/test_screens.py b/tests/functional/openlp_core/display/test_screens.py index 258f3b69a..6aac3cd9f 100644 --- a/tests/functional/openlp_core/display/test_screens.py +++ b/tests/functional/openlp_core/display/test_screens.py @@ -62,19 +62,26 @@ class TestScreenList(TestCase): del self.screens del self.application - def test_add_desktop(self): + def test_create_screen_list(self): """ - Test the ScreenList.screen_count_changed method to check if new monitors are detected by OpenLP. + Create the screen list """ - # GIVEN: The screen list at its current size - old_screen_count = len(self.screens.screen_list) + # GIVEN: Mocked desktop + mocked_desktop = MagicMock() + mocked_desktop.screenCount.return_value = 2 + mocked_desktop.screenGeometry.side_effect = [ + QtCore.QRect(0, 0, 1024, 768), + QtCore.QRect(1024, 0, 1024, 768) + ] + mocked_desktop.primaryScreen.return_value = 0 - # WHEN: We add a new screen - self.desktop.screenCount.return_value = SCREEN['number'] + 1 - self.screens.screen_count_changed(old_screen_count) + # WHEN: create() is called + screen_list = ScreenList.create(mocked_desktop) - # THEN: The screen should have been added and the screens should be identical - new_screen_count = len(self.screens.screen_list) - self.assertEqual(old_screen_count + 1, new_screen_count, 'The new_screens list should be bigger') - self.assertEqual(SCREEN, self.screens.screen_list.pop(), - 'The 2nd screen should be identical to the first screen') + # THEN: The correct screens have been set up + assert screen_list.screens[0].number == 0 + assert screen_list.screens[0].geometry == QtCore.QRect(0, 0, 1024, 768) + assert screen_list.screens[0].is_primary is True + assert screen_list.screens[1].number == 1 + assert screen_list.screens[1].geometry == QtCore.QRect(1024, 0, 1024, 768) + assert screen_list.screens[1].is_primary is False diff --git a/tests/functional/openlp_core/ui/test_exceptionform.py b/tests/functional/openlp_core/ui/test_exceptionform.py index 3bc99d63f..f3cf81420 100644 --- a/tests/functional/openlp_core/ui/test_exceptionform.py +++ b/tests/functional/openlp_core/ui/test_exceptionform.py @@ -24,7 +24,7 @@ Package to test the openlp.core.ui.exeptionform package. """ import os import tempfile - +from collections import OrderedDict from unittest import TestCase from unittest.mock import call, patch @@ -45,40 +45,44 @@ exceptionform.VLC_VERSION = 'VLC Test' MAIL_ITEM_TEXT = ('**OpenLP Bug Report**\nVersion: Trunk Test\n\n--- Details of the Exception. ---\n\n' 'Description Test\n\n --- Exception Traceback ---\nopenlp: Traceback Test\n' '--- System information ---\nPlatform: Nose Test\n\n--- Library Versions ---\n' - 'Python: Python Test\nQt5: Qt5 test\nPyQt5: PyQt5 Test\nQtWebkit: Webkit Test\n' - 'SQLAlchemy: SqlAlchemy Test\nSQLAlchemy Migrate: Migrate Test\nBeautifulSoup: BeautifulSoup Test\n' - 'lxml: ETree Test\nChardet: CHARDET Test\nPyEnchant: Enchant Test\nMako: Mako Test\n' - 'pyICU: ICU Test\npyUNO bridge: UNO Bridge Test\nVLC: VLC Test\n\n') + 'Python: Python Test\nQt5: Qt5 Test\nPyQt5: PyQt5 Test\n' + 'SQLAlchemy: SQLAlchemy Test\nAlembic: Alembic Test\nBeautifulSoup: BeautifulSoup Test\n' + 'lxml: ETree Test\nChardet: Chardet Test\nPyEnchant: PyEnchant Test\nMako: Mako Test\n' + 'pyICU: pyICU Test\nVLC: VLC Test\nPyUNO: UNO Bridge Test\n') +LIBRARY_VERSIONS = OrderedDict([ + ('Python', 'Python Test'), + ('Qt5', 'Qt5 Test'), + ('PyQt5', 'PyQt5 Test'), + ('SQLAlchemy', 'SQLAlchemy Test'), + ('Alembic', 'Alembic Test'), + ('BeautifulSoup', 'BeautifulSoup Test'), + ('lxml', 'ETree Test'), + ('Chardet', 'Chardet Test'), + ('PyEnchant', 'PyEnchant Test'), + ('Mako', 'Mako Test'), + ('pyICU', 'pyICU Test'), + ('VLC', 'VLC Test') +]) -@patch("openlp.core.ui.exceptionform.Qt.qVersion") -@patch("openlp.core.ui.exceptionform.QtGui.QDesktopServices.openUrl") -@patch("openlp.core.ui.exceptionform.get_version") -@patch("openlp.core.ui.exceptionform.sqlalchemy") -@patch("openlp.core.ui.exceptionform.bs4") -@patch("openlp.core.ui.exceptionform.etree") -@patch("openlp.core.ui.exceptionform.is_linux") -@patch("openlp.core.ui.exceptionform.platform.platform") -@patch("openlp.core.ui.exceptionform.platform.python_version") +@patch('openlp.core.ui.exceptionform.QtGui.QDesktopServices.openUrl') +@patch('openlp.core.ui.exceptionform.get_version') +@patch('openlp.core.ui.exceptionform.get_library_versions') +@patch('openlp.core.ui.exceptionform.is_linux') +@patch('openlp.core.ui.exceptionform.platform.platform') class TestExceptionForm(TestMixin, TestCase): """ Test functionality of exception form functions """ def __method_template_for_class_patches(self, __PLACEHOLDER_FOR_LOCAL_METHOD_PATCH_DECORATORS_GO_HERE__, - mocked_python_version, mocked_platform, mocked_is_linux, - mocked_etree, mocked_bs4, mocked_sqlalchemy, mocked_get_version, - mocked_openlurl, mocked_qversion): + mocked_platform, mocked_is_linux, mocked_get_library_versions, + mocked_get_version, mocked_openlurl): """ Template so you don't have to remember the layout of class mock options for methods """ - mocked_etree.__version__ = 'ETree Test' - mocked_bs4.__version__ = 'BeautifulSoup Test' - mocked_sqlalchemy.__version__ = 'SqlAlchemy Test' - mocked_python_version.return_value = 'Python Test' - mocked_platform.return_value = 'Nose Test' - mocked_qversion.return_value = 'Qt5 test' mocked_is_linux.return_value = False mocked_get_version.return_value = 'Trunk Test' + mocked_get_library_versions.return_value = LIBRARY_VERSIONS def setUp(self): self.setup_application() @@ -98,31 +102,21 @@ class TestExceptionForm(TestMixin, TestCase): @patch("openlp.core.ui.exceptionform.FileDialog") @patch("openlp.core.ui.exceptionform.QtCore.QUrl") @patch("openlp.core.ui.exceptionform.QtCore.QUrlQuery.addQueryItem") - @patch("openlp.core.ui.exceptionform.Qt") - def test_on_send_report_button_clicked(self, mocked_qt, mocked_add_query_item, mocked_qurl, mocked_file_dialog, - mocked_ui_exception_dialog, mocked_python_version, mocked_platform, - mocked_is_linux, mocked_etree, mocked_bs4, mocked_sqlalchemy, - mocked_get_version, mocked_openlurl, mocked_qversion): + def test_on_send_report_button_clicked(self, mocked_add_query_item, mocked_qurl, mocked_file_dialog, + mocked_ui_exception_dialog, mocked_platform, mocked_is_linux, + mocked_get_library_versions, mocked_get_version, mocked_openlurl): """ Test send report creates the proper system information text """ # GIVEN: Test environment - mocked_etree.__version__ = 'ETree Test' - mocked_bs4.__version__ = 'BeautifulSoup Test' - mocked_sqlalchemy.__version__ = 'SqlAlchemy Test' - mocked_python_version.return_value = 'Python Test' mocked_platform.return_value = 'Nose Test' - mocked_qversion.return_value = 'Qt5 test' mocked_is_linux.return_value = False mocked_get_version.return_value = 'Trunk Test' - mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test' - mocked_is_linux.return_value = False - mocked_get_version.return_value = 'Trunk Test' - + mocked_get_library_versions.return_value = LIBRARY_VERSIONS test_form = exceptionform.ExceptionForm() test_form.file_attachment = None - with patch.object(test_form, '_pyuno_import') as mock_pyuno, \ + with patch.object(test_form, '_get_pyuno_version') as mock_pyuno, \ patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback, \ patch.object(test_form.description_text_edit, 'toPlainText') as mock_description: mock_pyuno.return_value = 'UNO Bridge Test' @@ -136,24 +130,15 @@ class TestExceptionForm(TestMixin, TestCase): mocked_add_query_item.assert_called_with('body', MAIL_ITEM_TEXT) @patch("openlp.core.ui.exceptionform.FileDialog.getSaveFileName") - @patch("openlp.core.ui.exceptionform.Qt") - def test_on_save_report_button_clicked(self, mocked_qt, mocked_save_filename, mocked_python_version, - mocked_platform, mocked_is_linux, mocked_etree, mocked_bs4, - mocked_sqlalchemy, mocked_get_version, mocked_openlurl, - mocked_qversion): + def test_on_save_report_button_clicked(self, mocked_save_filename, mocked_platform, mocked_is_linux, + mocked_get_library_versions, mocked_get_version, mocked_openlurl): """ Test save report saves the correct information to a file """ - mocked_etree.__version__ = 'ETree Test' - mocked_bs4.__version__ = 'BeautifulSoup Test' - mocked_sqlalchemy.__version__ = 'SqlAlchemy Test' - mocked_python_version.return_value = 'Python Test' mocked_platform.return_value = 'Nose Test' - mocked_qversion.return_value = 'Qt5 test' - mocked_qt.PYQT_VERSION_STR = 'PyQt5 Test' mocked_is_linux.return_value = False mocked_get_version.return_value = 'Trunk Test' - + mocked_get_library_versions.return_value = LIBRARY_VERSIONS with patch.object(Path, 'open') as mocked_path_open: test_path = Path('testfile.txt') mocked_save_filename.return_value = test_path, 'ext' @@ -161,7 +146,7 @@ class TestExceptionForm(TestMixin, TestCase): test_form = exceptionform.ExceptionForm() test_form.file_attachment = None - with patch.object(test_form, '_pyuno_import') as mock_pyuno, \ + with patch.object(test_form, '_get_pyuno_version') as mock_pyuno, \ patch.object(test_form.exception_text_edit, 'toPlainText') as mock_traceback, \ patch.object(test_form.description_text_edit, 'toPlainText') as mock_description: mock_pyuno.return_value = 'UNO Bridge Test' diff --git a/tests/functional/openlp_core/ui/test_mainwindow.py b/tests/functional/openlp_core/ui/test_mainwindow.py index 5e1a69cbc..8987d4095 100644 --- a/tests/functional/openlp_core/ui/test_mainwindow.py +++ b/tests/functional/openlp_core/ui/test_mainwindow.py @@ -67,9 +67,12 @@ class TestMainWindow(TestCase, TestMixin): self.add_toolbar_action_patcher = patch('openlp.core.ui.mainwindow.create_action') self.mocked_add_toolbar_action = self.add_toolbar_action_patcher.start() self.mocked_add_toolbar_action.side_effect = self._create_mock_action - with patch('openlp.core.display.screens.ScreenList.__instance__', spec=ScreenList) as mocked_screen_list: - mocked_screen_list.current = {'number': 0, 'size': QtCore.QSize(600, 800), 'primary': True} - self.main_window = MainWindow() + mocked_desktop = MagicMock() + mocked_desktop.screenCount.return_value = 1 + mocked_desktop.screenGeometry.return_value = QtCore.QRect(0, 0, 1024, 768) + mocked_desktop.primaryScreen.return_value = 1 + ScreenList.create(mocked_desktop) + self.main_window = MainWindow() def tearDown(self): """ diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py index a39eb4096..bcab71873 100644 --- a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -23,7 +23,6 @@ This module contains tests for the PdfController """ import os -import shutil from tempfile import mkdtemp from unittest import TestCase, SkipTest from unittest.mock import MagicMock, patch diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index e384319f0..418c08ddf 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -108,7 +108,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport): self._title_assignment_list.append(title) -class TestFieldDesc: +class FakeFieldDesc: def __init__(self, name, field_type, size): self.name = name self.field_type = field_type @@ -120,11 +120,11 @@ CODE_PAGE_MAPPINGS = [ (852, 'cp1250'), (737, 'cp1253'), (775, 'cp1257'), (855, 'cp1251'), (857, 'cp1254'), (866, 'cp1251'), (869, 'cp1253'), (862, 'cp1255'), (874, 'cp874')] TEST_FIELD_DESCS = [ - TestFieldDesc('Title', FieldType.String, 50), - TestFieldDesc('Text Percentage Bottom', FieldType.Int16, 2), TestFieldDesc('RecID', FieldType.Int32, 4), - TestFieldDesc('Default Background', FieldType.Logical, 1), TestFieldDesc('Words', FieldType.Memo, 250), - TestFieldDesc('Words', FieldType.Memo, 250), TestFieldDesc('BK Bitmap', FieldType.Blob, 10), - TestFieldDesc('Last Modified', FieldType.Timestamp, 10)] + FakeFieldDesc('Title', FieldType.String, 50), + FakeFieldDesc('Text Percentage Bottom', FieldType.Int16, 2), FakeFieldDesc('RecID', FieldType.Int32, 4), + FakeFieldDesc('Default Background', FieldType.Logical, 1), FakeFieldDesc('Words', FieldType.Memo, 250), + FakeFieldDesc('Words', FieldType.Memo, 250), FakeFieldDesc('BK Bitmap', FieldType.Blob, 10), + FakeFieldDesc('Last Modified', FieldType.Timestamp, 10)] TEST_FIELDS = [ b'A Heart Like Thine\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0', 32868, 2147483750, 129, b'{\\rtf1\\ansi\\deff0\\deftab254{\\fonttbl{\\f0\\fnil\\fcharset0 Arial;}{\\f1\\fnil\\fcharset0 Verdana;}}' diff --git a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py index 02c365a4b..fbae6a8bc 100644 --- a/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py +++ b/tests/functional/openlp_plugins/songs/test_worshipcenterproimport.py @@ -34,9 +34,9 @@ except ImportError: CAN_RUN_TESTS = False -class TestRecord(object): +class FakeRecord(object): """ - Microsoft Access Driver is not available on non Microsoft Systems for this reason the :class:`TestRecord` is used + Microsoft Access Driver is not available on non Microsoft Systems for this reason the :class:`FakeRecord` is used to simulate a recordset that would be returned by pyobdc. """ def __init__(self, id_, field, value): @@ -66,12 +66,12 @@ if CAN_RUN_TESTS: self._title_assignment_list.append(title) -RECORDSET_TEST_DATA = [TestRecord(1, 'TITLE', 'Amazing Grace'), - TestRecord(1, 'AUTHOR', 'John Newton'), - TestRecord(1, 'CCLISONGID', '12345'), - TestRecord(1, 'COMMENTS', 'The original version'), - TestRecord(1, 'COPY', 'Public Domain'), - TestRecord( +RECORDSET_TEST_DATA = [FakeRecord(1, 'TITLE', 'Amazing Grace'), + FakeRecord(1, 'AUTHOR', 'John Newton'), + FakeRecord(1, 'CCLISONGID', '12345'), + FakeRecord(1, 'COMMENTS', 'The original version'), + FakeRecord(1, 'COPY', 'Public Domain'), + FakeRecord( 1, 'LYRICS', 'Amazing grace! How&crlf;sweet the sound&crlf;That saved a wretch like me!&crlf;' 'I once was lost,&crlf;but now am found;&crlf;Was blind, but now I see.&crlf;&crlf;' @@ -88,8 +88,8 @@ RECORDSET_TEST_DATA = [TestRecord(1, 'TITLE', 'Amazing Grace'), 'Shall be forever mine.&crlf;&crlf;When we\'ve been there&crlf;ten thousand years,&crlf;' 'Bright shining as the sun,&crlf;We\'ve no less days to&crlf;sing God\'s praise&crlf;' 'Than when we\'d first begun.&crlf;&crlf;'), - TestRecord(2, 'TITLE', 'Beautiful Garden Of Prayer, The'), - TestRecord( + FakeRecord(2, 'TITLE', 'Beautiful Garden Of Prayer, The'), + FakeRecord( 2, 'LYRICS', 'There\'s a garden where&crlf;Jesus is waiting,&crlf;' 'There\'s a place that&crlf;is wondrously fair,&crlf;For it glows with the&crlf;'