diff --git a/tests/conftest.py b/tests/conftest.py index 52c3c762c..562173459 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -82,11 +82,11 @@ def settings(qapp, registry): def mock_settings(registry): """A Mock Settings() instance""" # Create and register a mock settings object to work with - mock_settings = MagicMock() - Registry().register('settings', mock_settings) - yield mock_settings + mk_settings = MagicMock() + Registry().register('settings', mk_settings) + yield mk_settings Registry().remove('settings') - del mock_settings + del mk_settings @pytest.fixture(scope='function') diff --git a/tests/functional/openlp_core/lib/test_lib.py b/tests/functional/openlp_core/lib/test_lib.py index 149ed3f0d..756db3147 100644 --- a/tests/functional/openlp_core/lib/test_lib.py +++ b/tests/functional/openlp_core/lib/test_lib.py @@ -23,8 +23,8 @@ Package to test the openlp.core.lib package. """ import io import os +import pytest from pathlib import Path -from unittest import TestCase from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtGui @@ -34,188 +34,191 @@ from openlp.core.lib import DataType, build_icon, check_item_selected, create_se from tests.utils.constants import RESOURCE_PATH -class TestLib(TestCase): +def test_str_to_bool_with_bool_true(): + """ + Test the str_to_bool function with boolean input of True + """ + # GIVEN: A boolean value set to true + true_boolean = True - def test_str_to_bool_with_bool_true(self): - """ - Test the str_to_bool function with boolean input of True - """ - # GIVEN: A boolean value set to true - true_boolean = True + # WHEN: We "convert" it to a bool + true_result = str_to_bool(true_boolean) - # WHEN: We "convert" it to a bool - true_result = str_to_bool(true_boolean) + # THEN: We should get back a True bool + assert isinstance(true_result, bool), 'The result should be a boolean' + assert true_result is True, 'The result should be True' - # THEN: We should get back a True bool - assert isinstance(true_result, bool), 'The result should be a boolean' - assert true_result is True, 'The result should be True' - def test_str_to_bool_with_bool_false(self): - """ - Test the str_to_bool function with boolean input of False - """ - # GIVEN: A boolean value set to false - false_boolean = False +def test_str_to_bool_with_bool_false(): + """ + Test the str_to_bool function with boolean input of False + """ + # GIVEN: A boolean value set to false + false_boolean = False - # WHEN: We "convert" it to a bool - false_result = str_to_bool(false_boolean) + # WHEN: We "convert" it to a bool + false_result = str_to_bool(false_boolean) - # THEN: We should get back a True bool - assert isinstance(false_result, bool), 'The result should be a boolean' - assert false_result is False, 'The result should be True' + # THEN: We should get back a True bool + assert isinstance(false_result, bool), 'The result should be a boolean' + assert false_result is False, 'The result should be True' - def test_str_to_bool_with_integer(self): - """ - Test the str_to_bool function with an integer input - """ - # GIVEN: An integer value - int_string = 1 - # WHEN: we convert it to a bool - int_result = str_to_bool(int_string) +def test_str_to_bool_with_integer(): + """ + Test the str_to_bool function with an integer input + """ + # GIVEN: An integer value + int_string = 1 - # THEN: we should get back a false - assert int_result is False, 'The result should be False' + # WHEN: we convert it to a bool + int_result = str_to_bool(int_string) - def test_str_to_bool_with_invalid_string(self): - """ - Test the str_to_bool function with an invalid string - """ - # GIVEN: An string value with completely invalid input - invalid_string = 'my feet are wet' + # THEN: we should get back a false + assert int_result is False, 'The result should be False' - # WHEN: we convert it to a bool - str_result = str_to_bool(invalid_string) - # THEN: we should get back a false - assert str_result is False, 'The result should be False' +def test_str_to_bool_with_invalid_string(): + """ + Test the str_to_bool function with an invalid string + """ + # GIVEN: An string value with completely invalid input + invalid_string = 'my feet are wet' - def test_str_to_bool_with_string_false(self): - """ - Test the str_to_bool function with a string saying "false" - """ - # GIVEN: A string set to "false" - false_string = 'false' + # WHEN: we convert it to a bool + str_result = str_to_bool(invalid_string) - # WHEN: we convert it to a bool - false_result = str_to_bool(false_string) + # THEN: we should get back a false + assert str_result is False, 'The result should be False' - # THEN: we should get back a false - assert false_result is False, 'The result should be False' - def test_str_to_bool_with_string_no(self): - """ - Test the str_to_bool function with a string saying "NO" - """ - # GIVEN: An string set to "NO" - no_string = 'NO' +def test_str_to_bool_with_string_false(): + """ + Test the str_to_bool function with a string saying "false" + """ + # GIVEN: A string set to "false" + false_string = 'false' - # WHEN: we convert it to a bool - str_result = str_to_bool(no_string) + # WHEN: we convert it to a bool + false_result = str_to_bool(false_string) - # THEN: we should get back a false - assert str_result is False, 'The result should be False' + # THEN: we should get back a false + assert false_result is False, 'The result should be False' - def test_str_to_bool_with_true_string_value(self): - """ - Test the str_to_bool function with a string set to "True" - """ - # GIVEN: A string set to "True" - true_string = 'True' - # WHEN: we convert it to a bool - true_result = str_to_bool(true_string) +def test_str_to_bool_with_string_no(): + """ + Test the str_to_bool function with a string saying "NO" + """ + # GIVEN: An string set to "NO" + no_string = 'NO' - # THEN: we should get back a true - assert true_result is True, 'The result should be True' + # WHEN: we convert it to a bool + str_result = str_to_bool(no_string) - def test_str_to_bool_with_yes_string_value(self): - """ - Test the str_to_bool function with a string set to "yes" - """ - # GIVEN: An string set to "yes" - yes_string = 'yes' + # THEN: we should get back a false + assert str_result is False, 'The result should be False' - # WHEN: we convert it to a bool - str_result = str_to_bool(yes_string) - # THEN: we should get back a true - assert str_result is True, 'The result should be True' +def test_str_to_bool_with_true_string_value(): + """ + Test the str_to_bool function with a string set to "True" + """ + # GIVEN: A string set to "True" + true_string = 'True' - def test_get_text_file_string_no_file(self): - """ - Test the get_text_file_string() function when a file does not exist - """ - # GIVEN: A patched is_file which returns False, and a file path - with patch.object(Path, 'is_file', return_value=False): - file_path = Path('testfile.txt') + # WHEN: we convert it to a bool + true_result = str_to_bool(true_string) - # WHEN: get_text_file_string is called - result = get_text_file_string(file_path) + # THEN: we should get back a true + assert true_result is True, 'The result should be True' - # THEN: The result should be False - file_path.is_file.assert_called_with() - assert result is False, 'False should be returned if no file exists' - def test_get_text_file_string_read_error(self): - """ - Test the get_text_file_string() method when a read error happens - """ - # GIVEN: A patched open which raises an exception and is_file which returns True - with patch.object(Path, 'is_file'), \ - patch.object(Path, 'open'): - file_path = Path('testfile.txt') - file_path.is_file.return_value = True - file_path.open.side_effect = OSError() +def test_str_to_bool_with_yes_string_value(): + """ + Test the str_to_bool function with a string set to "yes" + """ + # GIVEN: An string set to "yes" + yes_string = 'yes' - # WHEN: get_text_file_string is called - result = get_text_file_string(file_path) + # WHEN: we convert it to a bool + str_result = str_to_bool(yes_string) - # THEN: None should be returned - file_path.is_file.assert_called_once_with() - file_path.open.assert_called_once_with('r', encoding='utf-8') - assert result is None, 'None should be returned if the file cannot be opened' + # THEN: we should get back a true + assert str_result is True, 'The result should be True' - def test_get_text_file_string_decode_error(self): - """ - Test the get_text_file_string() method when the contents cannot be decoded - """ - self.skipTest('Impossible to test due to conflicts when mocking out the "open" function') - def test_build_icon_with_qicon(self): - """ - Test the build_icon() function with a QIcon instance - """ - # GIVEN: An icon QIcon - icon = QtGui.QIcon() +def test_get_text_file_string_no_file(): + """ + Test the get_text_file_string() function when a file does not exist + """ + # GIVEN: A patched is_file which returns False, and a file path + with patch.object(Path, 'is_file', return_value=False): + file_path = Path('testfile.txt') + + # WHEN: get_text_file_string is called + result = get_text_file_string(file_path) + + # THEN: The result should be False + file_path.is_file.assert_called_with() + assert result is False, 'False should be returned if no file exists' + + +def test_get_text_file_string_read_error(): + """ + Test the get_text_file_string() method when a read error happens + """ + # GIVEN: A patched open which raises an exception and is_file which returns True + with patch.object(Path, 'is_file'), \ + patch.object(Path, 'open'): + file_path = Path('testfile.txt') + file_path.is_file.return_value = True + file_path.open.side_effect = OSError() + + # WHEN: get_text_file_string is called + result = get_text_file_string(file_path) + + # THEN: None should be returned + file_path.is_file.assert_called_once_with() + file_path.open.assert_called_once_with('r', encoding='utf-8') + assert result is None, 'None should be returned if the file cannot be opened' + + +def test_build_icon_with_qicon(): + """ + Test the build_icon() function with a QIcon instance + """ + # GIVEN: An icon QIcon + icon = QtGui.QIcon() + + # WHEN: We pass a QIcon instance in + result = build_icon(icon) + + # THEN: The result should be the same icon as we passed in + assert icon is result, 'The result should be the same icon as we passed in' + + +def test_build_icon_with_resource(): + """ + Test the build_icon() function with a resource URI + """ + with patch('openlp.core.lib.QtGui') as MockedQtGui, \ + patch('openlp.core.lib.QtGui.QPixmap') as MockedQPixmap: + # GIVEN: A mocked QIcon and a mocked QPixmap + MockedQtGui.QIcon = MagicMock + MockedQtGui.QIcon.Normal = 1 + MockedQtGui.QIcon.Off = 2 + MockedQPixmap.return_value = 'mocked_pixmap' + resource_uri = ':/resource/uri' # WHEN: We pass a QIcon instance in - result = build_icon(icon) + result = build_icon(resource_uri) - # THEN: The result should be the same icon as we passed in - assert icon is result, 'The result should be the same icon as we passed in' - - def test_build_icon_with_resource(self): - """ - Test the build_icon() function with a resource URI - """ - with patch('openlp.core.lib.QtGui') as MockedQtGui, \ - patch('openlp.core.lib.QtGui.QPixmap') as MockedQPixmap: - # GIVEN: A mocked QIcon and a mocked QPixmap - MockedQtGui.QIcon = MagicMock - MockedQtGui.QIcon.Normal = 1 - MockedQtGui.QIcon.Off = 2 - MockedQPixmap.return_value = 'mocked_pixmap' - resource_uri = ':/resource/uri' - - # WHEN: We pass a QIcon instance in - result = build_icon(resource_uri) - - # THEN: The result should be our mocked QIcon - MockedQPixmap.assert_called_with(resource_uri) - # There really should be more assert statements here but due to type checking and things they all break. The - # best we can do is to assert that we get back a MagicMock object. - assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out' + # THEN: The result should be our mocked QIcon + MockedQPixmap.assert_called_with(resource_uri) + # There really should be more assert statements here but due to type checking and things they all break. The + # best we can do is to assert that we get back a MagicMock object. + assert isinstance(result, MagicMock), 'The result should be a MagicMock, because we mocked it out' def test_image_to_byte(self): """ @@ -241,260 +244,289 @@ class TestLib(TestCase): assert mocked_byte_array.toBase64.called is False assert mocked_byte_array == result, 'The mocked out byte array should be returned' - def test_image_to_byte_base_64(self): - """ - Test the image_to_byte() function - """ - with patch('openlp.core.lib.QtCore') as MockedQtCore: - # GIVEN: A set of mocked-out Qt classes - mocked_byte_array = MagicMock() - MockedQtCore.QByteArray.return_value = mocked_byte_array - mocked_byte_array.toBase64.return_value = QtCore.QByteArray(b'base64mock') - mocked_buffer = MagicMock() - MockedQtCore.QBuffer.return_value = mocked_buffer - MockedQtCore.QIODevice.WriteOnly = 'writeonly' - mocked_image = MagicMock() - # WHEN: We convert an image to a byte array - result = image_to_byte(mocked_image) +def test_image_to_byte_base_64(): + """ + Test the image_to_byte() function + """ + with patch('openlp.core.lib.QtCore') as MockedQtCore: + # GIVEN: A set of mocked-out Qt classes + mocked_byte_array = MagicMock() + MockedQtCore.QByteArray.return_value = mocked_byte_array + mocked_byte_array.toBase64.return_value = QtCore.QByteArray(b'base64mock') + mocked_buffer = MagicMock() + MockedQtCore.QBuffer.return_value = mocked_buffer + MockedQtCore.QIODevice.WriteOnly = 'writeonly' + mocked_image = MagicMock() - # THEN: We should receive a value of 'base64mock' - MockedQtCore.QByteArray.assert_called_with() - MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) - mocked_buffer.open.assert_called_with('writeonly') - mocked_image.save.assert_called_with(mocked_buffer, "PNG") - mocked_byte_array.toBase64.assert_called_with() - assert 'base64mock' == result, 'The result should be the return value of the mocked out base64 method' + # WHEN: We convert an image to a byte array + result = image_to_byte(mocked_image) - def test_create_thumb_with_size(self): - """ - Test the create_thumb() function with a given size. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - thumb_size = QtCore.QSize(10, 20) + # THEN: We should receive a value of 'base64mock' + MockedQtCore.QByteArray.assert_called_with() + MockedQtCore.QBuffer.assert_called_with(mocked_byte_array) + mocked_buffer.open.assert_called_with('writeonly') + mocked_image.save.assert_called_with(mocked_buffer, "PNG") + mocked_byte_array.toBase64.assert_called_with() + assert 'base64mock' == result, 'The result should be the return value of the mocked out base64 method' - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' +def test_create_thumb_with_size(): + """ + Test the create_thumb() function with a given size. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + thumb_size = QtCore.QSize(10, 20) - # WHEN: Create the thumb. + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created and scaled to the given size. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert thumb_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass + + +def test_create_thumb_no_size(): + """ + Test the create_thumb() function with no size specified. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + expected_size = QtCore.QSize(63, 88) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass + + +def test_create_thumb_invalid_size(): + """ + Test the create_thumb() function with invalid size specified. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + thumb_size = QtCore.QSize(-1, -1) + expected_size = QtCore.QSize(63, 88) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass + + +def test_create_thumb_width_only(): + """ + Test the create_thumb() function with a size of only width specified. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + thumb_size = QtCore.QSize(100, -1) + expected_size = QtCore.QSize(100, 137) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass + + +def test_create_thumb_height_only(): + """ + Test the create_thumb() function with a size of only height specified. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + thumb_size = QtCore.QSize(-1, 100) + expected_size = QtCore.QSize(72, 100) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + icon = create_thumb(image_path, thumb_path, size=thumb_size) + + # THEN: Check if the thumb was created, retaining its aspect ratio. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass + + +def test_create_thumb_empty_img(): + """ + Test the create_thumb() function with a size of only height specified. + """ + # GIVEN: An image to create a thumb of. + image_path = RESOURCE_PATH / 'church.jpg' + thumb_path = RESOURCE_PATH / 'church_thumb.jpg' + thumb_size = QtCore.QSize(-1, 100) + expected_size_1 = QtCore.QSize(88, 88) + expected_size_2 = QtCore.QSize(100, 100) + + # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the + # last test. + try: + thumb_path.unlink() + except Exception: + pass + + # Only continue when the thumb does not exist. + assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + + # WHEN: Create the thumb. + with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size: + mocked_size.return_value = QtCore.QSize(0, 0) + icon = create_thumb(image_path, thumb_path, size=None) + + # THEN: Check if the thumb was created with aspect ratio of 1. + assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size_1 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + + # WHEN: Create the thumb. + with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size: + mocked_size.return_value = QtCore.QSize(0, 0) icon = create_thumb(image_path, thumb_path, size=thumb_size) - # THEN: Check if the thumb was created and scaled to the given size. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert thumb_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' + # THEN: Check if the thumb was created with aspect ratio of 1. + assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' + assert icon.isNull() is False, 'The icon should not be null' + assert expected_size_2 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass + # Remove the thumb so that the test actually tests if the thumb will be created. + try: + thumb_path.unlink() + except Exception: + pass - def test_create_thumb_no_size(self): - """ - Test the create_thumb() function with no size specified. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - expected_size = QtCore.QSize(63, 88) - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass +@patch('openlp.core.lib.QtWidgets', MagicMock()) +def test_check_item_selected_true(): + """ + Test that the check_item_selected() function returns True when there are selected indexes + """ + # GIVEN: A mocked out QtWidgets module and a list widget with selected indexes + mocked_list_widget = MagicMock() + mocked_list_widget.selectedIndexes.return_value = True + message = 'message' - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' + # WHEN: We check if there are selected items + result = check_item_selected(mocked_list_widget, message) - # WHEN: Create the thumb. - icon = create_thumb(image_path, thumb_path) + # THEN: The selectedIndexes function should have been called and the result should be true + mocked_list_widget.selectedIndexes.assert_called_with() + assert result is True, 'The result should be True' - # THEN: Check if the thumb was created, retaining its aspect ratio. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass - - def test_create_thumb_invalid_size(self): - """ - Test the create_thumb() function with invalid size specified. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - thumb_size = QtCore.QSize(-1, -1) - expected_size = QtCore.QSize(63, 88) - - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass - - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' - - # WHEN: Create the thumb. - icon = create_thumb(image_path, thumb_path, size=thumb_size) - - # THEN: Check if the thumb was created, retaining its aspect ratio. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass - - def test_create_thumb_width_only(self): - """ - Test the create_thumb() function with a size of only width specified. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - thumb_size = QtCore.QSize(100, -1) - expected_size = QtCore.QSize(100, 137) - - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass - - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' - - # WHEN: Create the thumb. - icon = create_thumb(image_path, thumb_path, size=thumb_size) - - # THEN: Check if the thumb was created, retaining its aspect ratio. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass - - def test_create_thumb_height_only(self): - """ - Test the create_thumb() function with a size of only height specified. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - thumb_size = QtCore.QSize(-1, 100) - expected_size = QtCore.QSize(72, 100) - - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass - - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' - - # WHEN: Create the thumb. - icon = create_thumb(image_path, thumb_path, size=thumb_size) - - # THEN: Check if the thumb was created, retaining its aspect ratio. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass - - def test_create_thumb_empty_img(self): - """ - Test the create_thumb() function with a size of only height specified. - """ - # GIVEN: An image to create a thumb of. - image_path = RESOURCE_PATH / 'church.jpg' - thumb_path = RESOURCE_PATH / 'church_thumb.jpg' - thumb_size = QtCore.QSize(-1, 100) - expected_size_1 = QtCore.QSize(88, 88) - expected_size_2 = QtCore.QSize(100, 100) - - # Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the - # last test. - try: - thumb_path.unlink() - except Exception: - pass - - # Only continue when the thumb does not exist. - assert thumb_path.exists() is False, 'Test was not run, because the thumb already exists.' - - # WHEN: Create the thumb. - with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size: - mocked_size.return_value = QtCore.QSize(0, 0) - icon = create_thumb(image_path, thumb_path, size=None) - - # THEN: Check if the thumb was created with aspect ratio of 1. - assert thumb_path.exists() is True, 'Test was not ran, because the thumb already exists' - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size_1 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - - # WHEN: Create the thumb. - with patch('openlp.core.lib.QtGui.QImageReader.size') as mocked_size: - mocked_size.return_value = QtCore.QSize(0, 0) - icon = create_thumb(image_path, thumb_path, size=thumb_size) - - # THEN: Check if the thumb was created with aspect ratio of 1. - assert isinstance(icon, QtGui.QIcon), 'The icon should be a QIcon' - assert icon.isNull() is False, 'The icon should not be null' - assert expected_size_2 == QtGui.QImageReader(str(thumb_path)).size(), 'The thumb should have the given size' - - # Remove the thumb so that the test actually tests if the thumb will be created. - try: - thumb_path.unlink() - except Exception: - pass - - @patch('openlp.core.lib.QtWidgets', MagicMock()) - def test_check_item_selected_true(self): - """ - Test that the check_item_selected() function returns True when there are selected indexes - """ - # GIVEN: A mocked out QtWidgets module and a list widget with selected indexes +def test_check_item_selected_false(): + """ + Test that the check_item_selected() function returns False when there are no selected indexes. + """ + # GIVEN: A mocked out QtWidgets module and a list widget with selected indexes + with patch('openlp.core.lib.QtWidgets') as MockedQtWidgets, \ + patch('openlp.core.lib.translate') as mocked_translate: + mocked_translate.return_value = 'mocked translate' mocked_list_widget = MagicMock() - mocked_list_widget.selectedIndexes.return_value = True + mocked_list_widget.selectedIndexes.return_value = False + mocked_list_widget.parent.return_value = 'parent' message = 'message' # WHEN: We check if there are selected items @@ -502,358 +534,362 @@ class TestLib(TestCase): # THEN: The selectedIndexes function should have been called and the result should be true mocked_list_widget.selectedIndexes.assert_called_with() - assert result is True, 'The result should be True' + MockedQtWidgets.QMessageBox.information.assert_called_with('parent', 'mocked translate', 'message') + assert result is False, 'The result should be False' - def test_check_item_selected_false(self): - """ - Test that the check_item_selected() function returns False when there are no selected indexes. - """ - # GIVEN: A mocked out QtWidgets module and a list widget with selected indexes - with patch('openlp.core.lib.QtWidgets') as MockedQtWidgets, \ - patch('openlp.core.lib.translate') as mocked_translate: - mocked_translate.return_value = 'mocked translate' - mocked_list_widget = MagicMock() - mocked_list_widget.selectedIndexes.return_value = False - mocked_list_widget.parent.return_value = 'parent' - message = 'message' - # WHEN: We check if there are selected items - result = check_item_selected(mocked_list_widget, message) - - # THEN: The selectedIndexes function should have been called and the result should be true - mocked_list_widget.selectedIndexes.assert_called_with() - MockedQtWidgets.QMessageBox.information.assert_called_with('parent', 'mocked translate', 'message') - assert result is False, 'The result should be False' - - def test_validate_thumb_file_does_not_exist(self): - """ - Test the validate_thumb() function when the thumbnail does not exist - """ - # GIVEN: A mocked out os module, with path.exists returning False, and fake paths to a file and a thumb - with patch.object(Path, 'exists', return_value=False): - file_path = Path('path', 'to', 'file') - thumb_path = Path('path', 'to', 'thumb') - - # WHEN: we run the validate_thumb() function - result = validate_thumb(file_path, thumb_path) - - # THEN: we should have called a few functions, and the result should be False - thumb_path.exists.assert_called_once_with() - assert result is False, 'The result should be False' - - def test_validate_thumb_file_exists_and_newer(self): - """ - Test the validate_thumb() function when the thumbnail exists and has a newer timestamp than the file - """ - with patch.object(Path, 'exists'), patch.object(Path, 'stat'): - # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times - file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)}) - thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=11)}) - - # WHEN: we run the validate_thumb() function - result = validate_thumb(file_path, thumb_path) - - # THEN: `validate_thumb` should return True - assert result is True - - def test_validate_thumb_file_exists_and_older(self): - """ - Test the validate_thumb() function when the thumbnail exists but is older than the file - """ - # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times - file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)}) - thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=9)}) +def test_validate_thumb_file_does_not_exist(): + """ + Test the validate_thumb() function when the thumbnail does not exist + """ + # GIVEN: A mocked out os module, with path.exists returning False, and fake paths to a file and a thumb + with patch.object(Path, 'exists', return_value=False): + file_path = Path('path', 'to', 'file') + thumb_path = Path('path', 'to', 'thumb') # WHEN: we run the validate_thumb() function result = validate_thumb(file_path, thumb_path) - # THEN: `validate_thumb` should return False - thumb_path.stat.assert_called_once_with() + # THEN: we should have called a few functions, and the result should be False + thumb_path.exists.assert_called_once_with() assert result is False, 'The result should be False' - def test_resize_thumb(self): - """ - Test the resize_thumb() function - """ - # GIVEN: A path to an image. - image_path = str(RESOURCE_PATH / 'church.jpg') - wanted_width = 777 - wanted_height = 72 - # We want the background to be white. - wanted_background_hex = '#FFFFFF' - wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb() - # WHEN: Resize the image and add a background. - image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex) +def test_validate_thumb_file_exists_and_newer(): + """ + Test the validate_thumb() function when the thumbnail exists and has a newer timestamp than the file + """ + with patch.object(Path, 'exists'), patch.object(Path, 'stat'): + # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times + file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)}) + thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=11)}) - # THEN: Check if the size is correct and the background was set. - result_size = image.size() - assert wanted_height == result_size.height(), 'The image should have the requested height.' - assert wanted_width == result_size.width(), 'The image should have the requested width.' - assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' + # WHEN: we run the validate_thumb() function + result = validate_thumb(file_path, thumb_path) - def test_resize_thumb_ignoring_aspect_ratio(self): - """ - Test the resize_thumb() function ignoring aspect ratio - """ - # GIVEN: A path to an image. - image_path = str(RESOURCE_PATH / 'church.jpg') - wanted_width = 1000 - wanted_height = 1000 - # We want the background to be white. - wanted_background_hex = '#FFFFFF' - wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb() + # THEN: `validate_thumb` should return True + assert result is True - # WHEN: Resize the image and add a background. - image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex, True) - # THEN: Check if the size is correct and the background was set. - result_size = image.size() - assert wanted_height == result_size.height(), 'The image should have the requested height.' - assert wanted_width == result_size.width(), 'The image should have the requested width.' - assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' +def test_validate_thumb_file_exists_and_older(): + """ + Test the validate_thumb() function when the thumbnail exists but is older than the file + """ + # GIVEN: Mocked file_path and thumb_path which return different values fo the modified times + file_path = MagicMock(**{'stat.return_value': MagicMock(st_mtime=10)}) + thumb_path = MagicMock(**{'exists.return_value': True, 'stat.return_value': MagicMock(st_mtime=9)}) - @patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') - def test_create_separated_list_qlocate(self, mocked_createSeparatedList): - """ - Test the create_separated_list function using the Qt provided method - """ - # GIVEN: A list of strings and the mocked Qt module. - mocked_createSeparatedList.return_value = 'Author 1, Author 2, and Author 3' - string_list = ['Author 1', 'Author 2', 'Author 3'] + # WHEN: we run the validate_thumb() function + result = validate_thumb(file_path, thumb_path) - # WHEN: We get a string build from the entries it the list and a separator. - string_result = create_separated_list(string_list) + # THEN: `validate_thumb` should return False + thumb_path.stat.assert_called_once_with() + assert result is False, 'The result should be False' - # THEN: We should have "Author 1, Author 2, and Author 3" - assert string_result == 'Author 1, Author 2 and Author 3', \ - 'The string should be "Author 1, Author 2, and Author 3".' - def test_create_separated_list_empty_list(self): - """ - Test the create_separated_list function with an empty list - """ - # GIVEN: An empty list - string_list = [] +def test_resize_thumb(): + """ + Test the resize_thumb() function + """ + # GIVEN: A path to an image. + image_path = str(RESOURCE_PATH / 'church.jpg') + wanted_width = 777 + wanted_height = 72 + # We want the background to be white. + wanted_background_hex = '#FFFFFF' + wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb() - # WHEN: We get a string build from the entries it the list and a separator. - string_result = create_separated_list(string_list) + # WHEN: Resize the image and add a background. + image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex) - # THEN: We shoud have an emptry string. - assert string_result == '', 'The string sould be empty.' + # THEN: Check if the size is correct and the background was set. + result_size = image.size() + assert wanted_height == result_size.height(), 'The image should have the requested height.' + assert wanted_width == result_size.width(), 'The image should have the requested width.' + assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' - def test_create_separated_list_with_one_item(self): - """ - Test the create_separated_list function with a list consisting of only one entry - """ - # GIVEN: A list with a string. - string_list = ['Author 1'] - # WHEN: We get a string build from the entries it the list and a separator. - string_result = create_separated_list(string_list) +def test_resize_thumb_ignoring_aspect_ratio(): + """ + Test the resize_thumb() function ignoring aspect ratio + """ + # GIVEN: A path to an image. + image_path = str(RESOURCE_PATH / 'church.jpg') + wanted_width = 1000 + wanted_height = 1000 + # We want the background to be white. + wanted_background_hex = '#FFFFFF' + wanted_background_rgb = QtGui.QColor(wanted_background_hex).rgb() - # THEN: We should have "Author 1" - assert string_result == 'Author 1', 'The string should be "Author 1".' + # WHEN: Resize the image and add a background. + image = resize_image(image_path, wanted_width, wanted_height, wanted_background_hex, True) - def test_create_separated_list_with_two_items(self): - """ - Test the create_separated_list function with a list of two entries - """ - # GIVEN: A list with two strings. - string_list = ['Author 1', 'Author 2'] + # THEN: Check if the size is correct and the background was set. + result_size = image.size() + assert wanted_height == result_size.height(), 'The image should have the requested height.' + assert wanted_width == result_size.width(), 'The image should have the requested width.' + assert image.pixel(0, 0) == wanted_background_rgb, 'The background should be white.' - # WHEN: We get a string build from the entries it the list and a seperator. - string_result = create_separated_list(string_list) - # THEN: We should have "Author 1 and Author 2" - assert string_result == 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".' +@patch('openlp.core.lib.QtCore.QLocale.createSeparatedList') +def test_create_separated_list_qlocate(mocked_createSeparatedList): + """ + Test the create_separated_list function using the Qt provided method + """ + # GIVEN: A list of strings and the mocked Qt module. + mocked_createSeparatedList.return_value = 'Author 1, Author 2, and Author 3' + string_list = ['Author 1', 'Author 2', 'Author 3'] - def test_create_separated_list_with_three_items(self): - """ - Test the create_separated_list function with a list of three items - """ - # GIVEN: A list with three strings. - string_list = ['Author 1', 'Author 2', 'Author 3'] + # WHEN: We get a string build from the entries it the list and a separator. + string_result = create_separated_list(string_list) - # WHEN: We get a string build from the entries it the list and a seperator. - string_result = create_separated_list(string_list) + # THEN: We should have "Author 1, Author 2, and Author 3" + assert string_result == 'Author 1, Author 2 and Author 3', \ + 'The string should be "Author 1, Author 2, and Author 3".' - # THEN: We should have "Author 1, Author 2 and Author 3" - assert string_result == 'Author 1, Author 2 and Author 3', \ - 'The string should be "Author 1, Author 2, and Author 3".' - def test_read_or_fail_fail(self): - """ - Test the :func:`read_or_fail` function when attempting to read more data than the buffer contains. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'test data') +def test_create_separated_list_empty_list(): + """ + Test the create_separated_list function with an empty list + """ + # GIVEN: An empty list + string_list = [] - # WHEN: Attempting to read past the end of the buffer - # THEN: An OSError should be raised. - with self.assertRaises(OSError): - read_or_fail(test_data, 15) + # WHEN: We get a string build from the entries it the list and a separator. + string_result = create_separated_list(string_list) - def test_read_or_fail_success(self): - """ - Test the :func:`read_or_fail` function when reading data that is in the buffer. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'test data') + # THEN: We shoud have an emptry string. + assert string_result == '', 'The string sould be empty.' - # WHEN: Attempting to read data that should exist. - result = read_or_fail(test_data, 4) - # THEN: The data of the requested length should be returned - assert result == b'test' +def test_create_separated_list_with_one_item(): + """ + Test the create_separated_list function with a list consisting of only one entry + """ + # GIVEN: A list with a string. + string_list = ['Author 1'] - def test_read_int_u8_big(self): - """ - Test the :func:`read_int` function when reading an unsigned 8-bit int using 'big' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + # WHEN: We get a string build from the entries it the list and a separator. + string_result = create_separated_list(string_list) - # WHEN: Reading a an unsigned 8-bit int - result = read_int(test_data, DataType.U8, 'big') + # THEN: We should have "Author 1" + assert string_result == 'Author 1', 'The string should be "Author 1".' - # THEN: The an int should have been returned of the expected value - assert result == 15 - def test_read_int_u8_little(self): - """ - Test the :func:`read_int` function when reading an unsigned 8-bit int using 'little' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') +def test_create_separated_list_with_two_items(): + """ + Test the create_separated_list function with a list of two entries + """ + # GIVEN: A list with two strings. + string_list = ['Author 1', 'Author 2'] - # WHEN: Reading a an unsigned 8-bit int - result = read_int(test_data, DataType.U8, 'little') + # WHEN: We get a string build from the entries it the list and a seperator. + string_result = create_separated_list(string_list) - # THEN: The an int should have been returned of the expected value - assert result == 15 + # THEN: We should have "Author 1 and Author 2" + assert string_result == 'Author 1 and Author 2', 'The string should be "Author 1 and Author 2".' - def test_read_int_u16_big(self): - """ - Test the :func:`read_int` function when reading an unsigned 16-bit int using 'big' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') - # WHEN: Reading a an unsigned 16-bit int - result = read_int(test_data, DataType.U16, 'big') +def test_create_separated_list_with_three_items(): + """ + Test the create_separated_list function with a list of three items + """ + # GIVEN: A list with three strings. + string_list = ['Author 1', 'Author 2', 'Author 3'] - # THEN: The an int should have been returned of the expected value - assert result == 4080 + # WHEN: We get a string build from the entries it the list and a seperator. + string_result = create_separated_list(string_list) - def test_read_int_u16_little(self): - """ - Test the :func:`read_int` function when reading an unsigned 16-bit int using 'little' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + # THEN: We should have "Author 1, Author 2 and Author 3" + assert string_result == 'Author 1, Author 2 and Author 3', \ + 'The string should be "Author 1, Author 2, and Author 3".' - # WHEN: Reading a an unsigned 16-bit int - result = read_int(test_data, DataType.U16, 'little') - # THEN: The an int should have been returned of the expected value - assert result == 61455 +def test_read_or_fail_fail(): + """ + Test the :func:`read_or_fail` function when attempting to read more data than the buffer contains. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'test data') - def test_read_int_u32_big(self): - """ - Test the :func:`read_int` function when reading an unsigned 32-bit int using 'big' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + # WHEN: Attempting to read past the end of the buffer + # THEN: An OSError should be raised. + with pytest.raises(OSError): + read_or_fail(test_data, 15) - # WHEN: Reading a an unsigned 32-bit int - result = read_int(test_data, DataType.U32, 'big') - # THEN: The an int should have been returned of the expected value - assert result == 267390960 +def test_read_or_fail_success(): + """ + Test the :func:`read_or_fail` function when reading data that is in the buffer. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'test data') - def test_read_int_u32_little(self): - """ - Test the :func:`read_int` function when reading an unsigned 32-bit int using 'little' endianness. - """ - # GIVEN: Some test data - test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + # WHEN: Attempting to read data that should exist. + result = read_or_fail(test_data, 4) - # WHEN: Reading a an unsigned 32-bit int - result = read_int(test_data, DataType.U32, 'little') + # THEN: The data of the requested length should be returned + assert result == b'test' - # THEN: The an int should have been returned of the expected value - assert result == 4027576335 - def test_seek_or_fail_default_method(self): - """ - Test the :func:`seek_or_fail` function when using the default value for the :arg:`how` - """ - # GIVEN: A mocked_file_like_object - mocked_file_like_object = MagicMock(**{'seek.return_value': 5, 'tell.return_value': 0}) +def test_read_int_u8_big(): + """ + Test the :func:`read_int` function when reading an unsigned 8-bit int using 'big' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') - # WHEN: Calling seek_or_fail with out the how arg set - seek_or_fail(mocked_file_like_object, 5) + # WHEN: Reading a an unsigned 8-bit int + result = read_int(test_data, DataType.U8, 'big') - # THEN: seek should be called using the os.SEEK_SET constant - mocked_file_like_object.seek.assert_called_once_with(5, os.SEEK_SET) + # THEN: The an int should have been returned of the expected value + assert result == 15 - def test_seek_or_fail_os_end(self): - """ - Test the :func:`seek_or_fail` function when called with an unsupported seek operation. - """ - # GIVEN: A Mocked object - # WHEN: Attempting to seek relative to the end - # THEN: An NotImplementedError should have been raised - with self.assertRaises(NotImplementedError): - seek_or_fail(MagicMock(), 1, os.SEEK_END) - def test_seek_or_fail_valid_seek_set(self): - """ - Test that :func:`seek_or_fail` successfully seeks to the correct position. - """ - # GIVEN: A mocked file-like object - mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 5}) +def test_read_int_u8_little(): + """ + Test the :func:`read_int` function when reading an unsigned 8-bit int using 'little' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') - # WHEN: Attempting to seek from the beginning - result = seek_or_fail(mocked_file_like_object, 5, os.SEEK_SET) + # WHEN: Reading a an unsigned 8-bit int + result = read_int(test_data, DataType.U8, 'little') - # THEN: The new position should be 5 from the beginning - assert result == 5 + # THEN: The an int should have been returned of the expected value + assert result == 15 - def test_seek_or_fail_invalid_seek_set(self): - """ - Test that :func:`seek_or_fail` raises an exception when seeking past the end. - """ - # GIVEN: A Mocked file-like object - mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 10}) - # WHEN: Attempting to seek from the beginning past the end - # THEN: An OSError should have been raised - with self.assertRaises(OSError): - seek_or_fail(mocked_file_like_object, 15, os.SEEK_SET) +def test_read_int_u16_big(): + """ + Test the :func:`read_int` function when reading an unsigned 16-bit int using 'big' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') - def test_seek_or_fail_valid_seek_cur(self): - """ - Test that :func:`seek_or_fail` successfully seeks to the correct position. - """ - # GIVEN: A mocked file_like object - mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 8}) + # WHEN: Reading a an unsigned 16-bit int + result = read_int(test_data, DataType.U16, 'big') - # WHEN: Attempting to seek from the current position - result = seek_or_fail(mocked_file_like_object, 5, os.SEEK_CUR) + # THEN: The an int should have been returned of the expected value + assert result == 4080 - # THEN: The new position should be 8 (5 from its starting position) - assert result == 8 - def test_seek_or_fail_invalid_seek_cur(self): - """ - Test that :func:`seek_or_fail` raises an exception when seeking past the end. - """ - # GIVEN: A mocked file_like object - mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 10}) +def test_read_int_u16_little(): + """ + Test the :func:`read_int` function when reading an unsigned 16-bit int using 'little' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') - # WHEN: Attempting to seek from the current position pas the end. - # THEN: An OSError should have been raised - with self.assertRaises(OSError): - seek_or_fail(mocked_file_like_object, 15, os.SEEK_CUR) + # WHEN: Reading a an unsigned 16-bit int + result = read_int(test_data, DataType.U16, 'little') + + # THEN: The an int should have been returned of the expected value + assert result == 61455 + + +def test_read_int_u32_big(): + """ + Test the :func:`read_int` function when reading an unsigned 32-bit int using 'big' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + + # WHEN: Reading a an unsigned 32-bit int + result = read_int(test_data, DataType.U32, 'big') + + # THEN: The an int should have been returned of the expected value + assert result == 267390960 + + +def test_read_int_u32_little(): + """ + Test the :func:`read_int` function when reading an unsigned 32-bit int using 'little' endianness. + """ + # GIVEN: Some test data + test_data = io.BytesIO(b'\x0f\xf0\x0f\xf0') + + # WHEN: Reading a an unsigned 32-bit int + result = read_int(test_data, DataType.U32, 'little') + + # THEN: The an int should have been returned of the expected value + assert result == 4027576335 + + +def test_seek_or_fail_default_method(): + """ + Test the :func:`seek_or_fail` function when using the default value for the :arg:`how` + """ + # GIVEN: A mocked_file_like_object + mocked_file_like_object = MagicMock(**{'seek.return_value': 5, 'tell.return_value': 0}) + + # WHEN: Calling seek_or_fail with out the how arg set + seek_or_fail(mocked_file_like_object, 5) + + # THEN: seek should be called using the os.SEEK_SET constant + mocked_file_like_object.seek.assert_called_once_with(5, os.SEEK_SET) + + +def test_seek_or_fail_os_end(): + """ + Test the :func:`seek_or_fail` function when called with an unsupported seek operation. + """ + # GIVEN: A Mocked object + # WHEN: Attempting to seek relative to the end + # THEN: An NotImplementedError should have been raised + with pytest.raises(NotImplementedError): + seek_or_fail(MagicMock(), 1, os.SEEK_END) + + +def test_seek_or_fail_valid_seek_set(): + """ + Test that :func:`seek_or_fail` successfully seeks to the correct position. + """ + # GIVEN: A mocked file-like object + mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 5}) + + # WHEN: Attempting to seek from the beginning + result = seek_or_fail(mocked_file_like_object, 5, os.SEEK_SET) + + # THEN: The new position should be 5 from the beginning + assert result == 5 + + +def test_seek_or_fail_invalid_seek_set(): + """ + Test that :func:`seek_or_fail` raises an exception when seeking past the end. + """ + # GIVEN: A Mocked file-like object + mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 10}) + + # WHEN: Attempting to seek from the beginning past the end + # THEN: An OSError should have been raised + with pytest.raises(OSError): + seek_or_fail(mocked_file_like_object, 15, os.SEEK_SET) + + +def test_seek_or_fail_valid_seek_cur(): + """ + Test that :func:`seek_or_fail` successfully seeks to the correct position. + """ + # GIVEN: A mocked file_like object + mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 8}) + + # WHEN: Attempting to seek from the current position + result = seek_or_fail(mocked_file_like_object, 5, os.SEEK_CUR) + + # THEN: The new position should be 8 (5 from its starting position) + assert result == 8 + + +def test_seek_or_fail_invalid_seek_cur(): + """ + Test that :func:`seek_or_fail` raises an exception when seeking past the end. + """ + # GIVEN: A mocked file_like object + mocked_file_like_object = MagicMock(**{'tell.return_value': 3, 'seek.return_value': 10}) + + # WHEN: Attempting to seek from the current position pas the end. + # THEN: An OSError should have been raised + with pytest.raises(OSError): + seek_or_fail(mocked_file_like_object, 15, os.SEEK_CUR) diff --git a/tests/functional/openlp_core/lib/test_mediamanageritem.py b/tests/functional/openlp_core/lib/test_mediamanageritem.py index b240a572d..a991e26f9 100644 --- a/tests/functional/openlp_core/lib/test_mediamanageritem.py +++ b/tests/functional/openlp_core/lib/test_mediamanageritem.py @@ -21,103 +21,100 @@ """ Package to test the openlp.core.lib.mediamanageritem package. """ -from unittest import TestCase +import pytest from unittest.mock import MagicMock, patch from openlp.core.common.registry import Registry from openlp.core.lib.mediamanageritem import MediaManagerItem -from tests.helpers.testmixin import TestMixin -class TestMediaManagerItem(TestCase, TestMixin): +@pytest.yield_fixture +def media_env(): + setup_patcher = patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup') + setup_patcher.start() + yield + setup_patcher.stop + + +@patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_preview_click') +def test_on_double_clicked(mocked_on_preview_click, media_env, registry): """ - Test the MediaManagerItem class + Test that when an item is double-clicked then the item is previewed """ - def setUp(self): - """ - Mock out stuff for all the tests - """ - Registry.create() - self.setup_patcher = patch('openlp.core.lib.mediamanageritem.MediaManagerItem._setup') - self.mocked_setup = self.setup_patcher.start() - self.addCleanup(self.setup_patcher.stop) + # GIVEN: A setting to enable "Double-click to go live" and a media manager item + mocked_settings = MagicMock() + mocked_settings.value.return_value = False + Registry().register('settings', mocked_settings) + mmi = MediaManagerItem(None) + mmi.can_preview = True + mmi.can_make_live = True + mmi.can_add_to_service = True - @patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_preview_click') - def test_on_double_clicked(self, mocked_on_preview_click): - """ - Test that when an item is double-clicked then the item is previewed - """ - # GIVEN: A setting to enable "Double-click to go live" and a media manager item - mocked_settings = MagicMock() - mocked_settings.value.return_value = False - Registry().register('settings', mocked_settings) - mmi = MediaManagerItem(None) - mmi.can_preview = True - mmi.can_make_live = True - mmi.can_add_to_service = True + # WHEN: on_double_clicked() is called + mmi.on_double_clicked() - # WHEN: on_double_clicked() is called - mmi.on_double_clicked() + # THEN: on_preview_click() should have been called + mocked_on_preview_click.assert_called_with() - # THEN: on_preview_click() should have been called - mocked_on_preview_click.assert_called_with() - def test_required_icons(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 - assert mmi.has_import_icon is False, 'There should be no import icon by default' - assert mmi.has_new_icon is True, 'By default a new icon should be present' - assert mmi.has_file_icon is False, 'There should be no file icon by default' - assert mmi.has_delete_icon is True, 'By default a delete icon should be present' - assert mmi.add_to_service_item is False, 'There should be no add_to_service icon by default' - assert mmi.can_preview is True, 'There should be a preview icon by default' - assert mmi.can_make_live is True, 'There should be a make live by default' - assert mmi.can_add_to_service is True, 'There should be a add to service icon by default' +def test_required_icons(media_env): + """ + 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 + assert mmi.has_import_icon is False, 'There should be no import icon by default' + assert mmi.has_new_icon is True, 'By default a new icon should be present' + assert mmi.has_file_icon is False, 'There should be no file icon by default' + assert mmi.has_delete_icon is True, 'By default a delete icon should be present' + assert mmi.add_to_service_item is False, 'There should be no add_to_service icon by default' + assert mmi.can_preview is True, 'There should be a preview icon by default' + assert mmi.can_make_live is True, 'There should be a make live by default' + assert mmi.can_add_to_service is True, 'There should be a add to service icon by default' - @patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click') - def test_on_double_clicked_go_live(self, mocked_on_live_click): - """ - Test that when "Double-click to go live" is enabled that the item goes live - """ - # GIVEN: A setting to enable "Double-click to go live" and a media manager item - mocked_settings = MagicMock() - mocked_settings.value.side_effect = lambda x: x == 'advanced/double click live' - Registry().register('settings', mocked_settings) - mmi = MediaManagerItem(None) - mmi.can_preview = True - mmi.can_make_live = True - mmi.can_add_to_service = True - # WHEN: on_double_clicked() is called - mmi.on_double_clicked() +@patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click') +def test_on_double_clicked_go_live(mocked_on_live_click, media_env, registry): + """ + Test that when "Double-click to go live" is enabled that the item goes live + """ + # GIVEN: A setting to enable "Double-click to go live" and a media manager item + mocked_settings = MagicMock() + mocked_settings.value.side_effect = lambda x: x == 'advanced/double click live' + Registry().register('settings', mocked_settings) + mmi = MediaManagerItem(None) + mmi.can_preview = True + mmi.can_make_live = True + mmi.can_add_to_service = True - # THEN: on_live_click() should have been called - mocked_on_live_click.assert_called_with() + # WHEN: on_double_clicked() is called + mmi.on_double_clicked() - @patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click') - @patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_preview_click') - def test_on_double_clicked_single_click_preview(self, mocked_on_preview_click, mocked_on_live_click): - """ - Test that when "Single-click preview" is enabled then nothing happens on double-click - """ - # GIVEN: A setting to enable "Double-click to go live" and a media manager item - mocked_settings = MagicMock() - mocked_settings.value.side_effect = lambda x: x == 'advanced/single click preview' - Registry().register('settings', mocked_settings) - mmi = MediaManagerItem(None) - mmi.can_preview = True - mmi.can_make_live = True - mmi.can_add_to_service = True + # THEN: on_live_click() should have been called + mocked_on_live_click.assert_called_with() - # WHEN: on_double_clicked() is called - mmi.on_double_clicked() - # THEN: on_live_click() should have been called - assert 0 == mocked_on_live_click.call_count, 'on_live_click() should not have been called' - assert 0 == mocked_on_preview_click.call_count, 'on_preview_click() should not have been called' +@patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_live_click') +@patch('openlp.core.lib.mediamanageritem.MediaManagerItem.on_preview_click') +def test_on_double_clicked_single_click_preview(mocked_on_preview_click, mocked_on_live_click, media_env, registry): + """ + Test that when "Single-click preview" is enabled then nothing happens on double-click + """ + # GIVEN: A setting to enable "Double-click to go live" and a media manager item + mocked_settings = MagicMock() + mocked_settings.value.side_effect = lambda x: x == 'advanced/single click preview' + Registry().register('settings', mocked_settings) + mmi = MediaManagerItem(None) + mmi.can_preview = True + mmi.can_make_live = True + mmi.can_add_to_service = True + + # WHEN: on_double_clicked() is called + mmi.on_double_clicked() + + # THEN: on_live_click() should have been called + assert 0 == mocked_on_live_click.call_count, 'on_live_click() should not have been called' + assert 0 == mocked_on_preview_click.call_count, 'on_preview_click() should not have been called' diff --git a/tests/functional/openlp_core/lib/test_pluginmanager.py b/tests/functional/openlp_core/lib/test_pluginmanager.py index a508bd36b..536e0c07d 100644 --- a/tests/functional/openlp_core/lib/test_pluginmanager.py +++ b/tests/functional/openlp_core/lib/test_pluginmanager.py @@ -21,7 +21,7 @@ """ Package to test the openlp.core.lib.pluginmanager package. """ -from unittest import TestCase +import pytest from unittest.mock import MagicMock, patch from openlp.core.state import State @@ -31,477 +31,491 @@ from openlp.core.lib.plugin import PluginStatus from openlp.core.lib.pluginmanager import PluginManager -class TestPluginManager(TestCase): +@pytest.fixture() +def plugin_manager_env(registry, state): + mocked_main_window = MagicMock() + mocked_main_window.file_import_menu.return_value = None + mocked_main_window.file_export_menu.return_value = None + mocked_main_window.file_export_menu.return_value = None + mocked_settings_form = MagicMock() + Registry().register('service_list', MagicMock()) + Registry().register('main_window', mocked_main_window) + Registry().register('settings_form', mocked_settings_form) + Registry().register('settings', MagicMock()) + + +def test_bootstrap_initialise(settings, state): """ - Test the PluginManager class + Test the PluginManager.bootstrap_initialise() method """ - - def setUp(self): - """ - Some pre-test setup required. - """ - self.mocked_main_window = MagicMock() - self.mocked_main_window.file_import_menu.return_value = None - self.mocked_main_window.file_export_menu.return_value = None - self.mocked_main_window.file_export_menu.return_value = None - self.mocked_settings_form = MagicMock() - Registry.create() - State().load_settings() - Registry().register('service_list', MagicMock()) - Registry().register('main_window', self.mocked_main_window) - Registry().register('settings_form', self.mocked_settings_form) - Registry().register('settings', MagicMock()) - - def test_bootstrap_initialise(self): - """ - Test the PluginManager.bootstrap_initialise() method - """ - # GIVEN: A plugin manager with some mocked out methods - manager = PluginManager() - - with patch.object(manager, 'hook_settings_tabs') as mocked_hook_settings_tabs, \ - patch.object(manager, 'hook_media_manager') as mocked_hook_media_manager, \ - patch.object(manager, 'hook_import_menu') as mocked_hook_import_menu, \ - patch.object(manager, 'hook_export_menu') as mocked_hook_export_menu, \ - patch.object(manager, 'hook_tools_menu') as mocked_hook_tools_menu, \ - patch.object(manager, 'initialise_plugins') as mocked_initialise_plugins: - # WHEN: bootstrap_initialise() is called - manager.bootstrap_initialise() - manager.bootstrap_post_set_up() - - # THEN: The hook methods should have been called - mocked_hook_settings_tabs.assert_called_with() - mocked_hook_media_manager.assert_called_with() - mocked_hook_import_menu.assert_called_with() - mocked_hook_export_menu.assert_called_with() - mocked_hook_tools_menu.assert_called_with() - mocked_initialise_plugins.assert_called_with() - - def test_hook_media_manager_with_disabled_plugin(self): - """ - Test running the hook_media_manager() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Disabled) - State().flush_preconditions() - - # WHEN: We run hook_media_manager() - plugin_manager.hook_media_manager() - - # THEN: The create_media_manager_item() method should have been called - assert 0 == mocked_plugin.create_media_manager_item.call_count, \ - 'The create_media_manager_item() method should not have been called.' - - def test_hook_media_manager_with_active_plugin(self): - """ - Test running the hook_media_manager() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_media_manager() - plugin_manager.hook_media_manager() - - # THEN: The create_media_manager_item() method should have been called - mocked_plugin.create_media_manager_item.assert_called_with() - - def test_hook_settings_tabs_with_disabled_plugin_and_no_form(self): - """ - Test running the hook_settings_tabs() method with a disabled plugin and no form - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_settings_tabs() - plugin_manager.hook_settings_tabs() - - # THEN: The hook_settings_tabs() method should have been called - assert 0 == mocked_plugin.create_media_manager_item.call_count, \ - 'The create_media_manager_item() method should not have been called.' - - def test_hook_settings_tabs_with_disabled_plugin_and_mocked_form(self): - """ - Test running the hook_settings_tabs() method with a disabled plugin and a mocked form - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - mocked_settings_form = MagicMock() - # Replace the autoloaded plugin with the version for testing in real code this would error - mocked_settings_form.plugin_manager = plugin_manager - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_settings_tabs() - plugin_manager.hook_settings_tabs() - - # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same - assert 0 == mocked_plugin.create_settings_tab.call_count, \ - 'The create_media_manager_item() method should not have been called.' - assert mocked_settings_form.plugin_manager.plugins == plugin_manager.plugins, \ - 'The plugins on the settings form should be the same as the plugins in the plugin manager' - - def test_hook_settings_tabs_with_active_plugin_and_mocked_form(self): - """ - Test running the hook_settings_tabs() method with an active plugin and a mocked settings form - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - mocked_settings_form = MagicMock() - # Replace the autoloaded plugin with the version for testing in real code this would error - mocked_settings_form.plugin_manager = plugin_manager - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_settings_tabs() - plugin_manager.hook_settings_tabs() - - # THEN: The create_media_manager_item() method should have been called with the mocked settings form - assert 1 == mocked_plugin.create_settings_tab.call_count, \ - 'The create_media_manager_item() method should have been called once.' - assert plugin_manager.plugins == mocked_settings_form.plugin_manager.plugins, \ - 'The plugins on the settings form should be the same as the plugins in the plugin manager' - - def test_hook_settings_tabs_with_active_plugin_and_no_form(self): - """ - Test running the hook_settings_tabs() method with an active plugin and no settings form - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_settings_tabs() - plugin_manager.hook_settings_tabs() - - # THEN: The create_settings_tab() method should have been called - mocked_plugin.create_settings_tab.assert_called_with(self.mocked_settings_form) - - def test_hook_import_menu_with_disabled_plugin(self): - """ - Test running the hook_import_menu() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_import_menu() - plugin_manager.hook_import_menu() - - # THEN: The create_media_manager_item() method should have been called - assert 0 == mocked_plugin.add_import_menu_item.call_count, \ - 'The add_import_menu_item() method should not have been called.' - - def test_hook_import_menu_with_active_plugin(self): - """ - Test running the hook_import_menu() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_import_menu() - plugin_manager.hook_import_menu() - - # THEN: The add_import_menu_item() method should have been called - mocked_plugin.add_import_menu_item.assert_called_with(self.mocked_main_window.file_import_menu) - - def test_hook_export_menu_with_disabled_plugin(self): - """ - Test running the hook_export_menu() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_export_menu() - plugin_manager.hook_export_menu() - - # THEN: The add_export_menu_item() method should not have been called - assert 0 == mocked_plugin.add_export_menu_item.call_count, \ - 'The add_export_menu_item() method should not have been called.' - - def test_hook_export_menu_with_active_plugin(self): - """ - Test running the hook_export_menu() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_export_menu() - plugin_manager.hook_export_menu() - - # THEN: The add_export_menu_item() method should have been called - mocked_plugin.add_export_menu_item.assert_called_with(self.mocked_main_window.file_export_menu) - - def test_hook_upgrade_plugin_settings_with_disabled_plugin(self): - """ - Test running the hook_upgrade_plugin_settings() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - settings = Settings() - - # WHEN: We run hook_upgrade_plugin_settings() - plugin_manager.hook_upgrade_plugin_settings(settings) - - # THEN: The upgrade_settings() method should not have been called - assert 0 == mocked_plugin.upgrade_settings.call_count, \ - 'The upgrade_settings() method should not have been called.' - - def test_hook_upgrade_plugin_settings_with_active_plugin(self): - """ - Test running the hook_upgrade_plugin_settings() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - settings = Settings() - - # WHEN: We run hook_upgrade_plugin_settings() - plugin_manager.hook_upgrade_plugin_settings(settings) - - # THEN: The add_export_menu_item() method should have been called - mocked_plugin.upgrade_settings.assert_called_with(settings) - - def test_hook_tools_menu_with_disabled_plugin(self): - """ - Test running the hook_tools_menu() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_tools_menu() - plugin_manager.hook_tools_menu() - - # THEN: The add_tools_menu_item() method should have been called - assert 0 == mocked_plugin.add_tools_menu_item.call_count, \ - 'The add_tools_menu_item() method should not have been called.' - - def test_hook_tools_menu_with_active_plugin(self): - """ - Test running the hook_tools_menu() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run hook_tools_menu() - plugin_manager.hook_tools_menu() - - # THEN: The add_tools_menu_item() method should have been called - mocked_plugin.add_tools_menu_item.assert_called_with(self.mocked_main_window.tools_menu) - - def test_initialise_plugins_with_disabled_plugin(self): - """ - Test running the initialise_plugins() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - mocked_plugin.is_active.return_value = False - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run initialise_plugins() - plugin_manager.initialise_plugins() - - # THEN: The is_active() method should have been called, and initialise() method should NOT have been called - mocked_plugin.is_active.assert_called_with() - assert 0 == mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.' - - def test_initialise_plugins_with_active_plugin(self): - """ - Test running the initialise_plugins() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - mocked_plugin.is_active.return_value = True - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run initialise_plugins() - plugin_manager.initialise_plugins() - - # THEN: The is_active() and initialise() methods should have been called - mocked_plugin.is_active.assert_called_with() - mocked_plugin.initialise.assert_called_with() - - def test_finalise_plugins_with_disabled_plugin(self): - """ - Test running the finalise_plugins() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - mocked_plugin.is_active.return_value = False - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run finalise_plugins() - plugin_manager.finalise_plugins() - - # THEN: The is_active() method should have been called, and initialise() method should NOT have been called - mocked_plugin.is_active.assert_called_with() - assert 0 == mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.' - - def test_finalise_plugins_with_active_plugin(self): - """ - Test running the finalise_plugins() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - mocked_plugin.is_active.return_value = True - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run finalise_plugins() - plugin_manager.finalise_plugins() - - # THEN: The is_active() and finalise() methods should have been called - mocked_plugin.is_active.assert_called_with() - mocked_plugin.finalise.assert_called_with() - - def test_get_plugin_by_name_does_not_exist(self): - """ - Test running the get_plugin_by_name() method to find a plugin that does not exist - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.name = 'Mocked Plugin' - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run finalise_plugins() - result = plugin_manager.get_plugin_by_name('Missing Plugin') - - # THEN: The is_active() and finalise() methods should have been called - assert result is None, 'The result for get_plugin_by_name should be None' - - def test_get_plugin_by_name_exists(self): - """ - Test running the get_plugin_by_name() method to find a plugin that exists - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.name = 'Mocked Plugin' - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run finalise_plugins() - result = plugin_manager.get_plugin_by_name('Mocked Plugin') - - # THEN: The is_active() and finalise() methods should have been called - assert result == mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin' - - def test_new_service_created_with_disabled_plugin(self): - """ - Test running the new_service_created() method with a disabled plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Disabled - mocked_plugin.is_active.return_value = False - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run finalise_plugins() - plugin_manager.new_service_created() - - # THEN: The isActive() method should have been called, and initialise() method should NOT have been called - mocked_plugin.is_active.assert_called_with() - assert 0 == mocked_plugin.new_service_created.call_count, \ - 'The new_service_created() method should not have been called.' - - def test_new_service_created_with_active_plugin(self): - """ - Test running the new_service_created() method with an active plugin - """ - # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active - mocked_plugin = MagicMock() - mocked_plugin.status = PluginStatus.Active - mocked_plugin.is_active.return_value = True - plugin_manager = PluginManager() - Registry().register('mock_plugin', mocked_plugin) - State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) - State().flush_preconditions() - - # WHEN: We run new_service_created() - plugin_manager.new_service_created() - - # THEN: The is_active() and finalise() methods should have been called - mocked_plugin.is_active.assert_called_with() - mocked_plugin.new_service_created.assert_called_with() + # GIVEN: A plugin manager with some mocked out methods + manager = PluginManager() + + with patch.object(manager, 'hook_settings_tabs') as mocked_hook_settings_tabs, \ + patch.object(manager, 'hook_media_manager') as mocked_hook_media_manager, \ + patch.object(manager, 'hook_import_menu') as mocked_hook_import_menu, \ + patch.object(manager, 'hook_export_menu') as mocked_hook_export_menu, \ + patch.object(manager, 'hook_tools_menu') as mocked_hook_tools_menu, \ + patch.object(manager, 'initialise_plugins') as mocked_initialise_plugins: + # WHEN: bootstrap_initialise() is called + manager.bootstrap_initialise() + manager.bootstrap_post_set_up() + + # THEN: The hook methods should have been called + mocked_hook_settings_tabs.assert_called_with() + mocked_hook_media_manager.assert_called_with() + mocked_hook_import_menu.assert_called_with() + mocked_hook_export_menu.assert_called_with() + mocked_hook_tools_menu.assert_called_with() + mocked_initialise_plugins.assert_called_with() + + +def test_hook_media_manager_with_disabled_plugin(registry, state): + """ + Test running the hook_media_manager() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Disabled) + State().flush_preconditions() + + # WHEN: We run hook_media_manager() + plugin_manager.hook_media_manager() + + # THEN: The create_media_manager_item() method should have been called + assert 0 == mocked_plugin.create_media_manager_item.call_count, \ + 'The create_media_manager_item() method should not have been called.' + + +def test_hook_media_manager_with_active_plugin(registry, state): + """ + Test running the hook_media_manager() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_media_manager() + plugin_manager.hook_media_manager() + + # THEN: The create_media_manager_item() method should have been called + mocked_plugin.create_media_manager_item.assert_called_with() + + +def test_hook_settings_tabs_with_disabled_plugin_and_no_form(registry, state): + """ + Test running the hook_settings_tabs() method with a disabled plugin and no form + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_settings_tabs() + plugin_manager.hook_settings_tabs() + + # THEN: The hook_settings_tabs() method should have been called + assert 0 == mocked_plugin.create_media_manager_item.call_count, \ + 'The create_media_manager_item() method should not have been called.' + + +def test_hook_settings_tabs_with_disabled_plugin_and_mocked_form(registry, state): + """ + Test running the hook_settings_tabs() method with a disabled plugin and a mocked form + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + mocked_settings_form = MagicMock() + # Replace the autoloaded plugin with the version for testing in real code this would error + mocked_settings_form.plugin_manager = plugin_manager + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_settings_tabs() + plugin_manager.hook_settings_tabs() + + # THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same + assert 0 == mocked_plugin.create_settings_tab.call_count, \ + 'The create_media_manager_item() method should not have been called.' + assert mocked_settings_form.plugin_manager.plugins == plugin_manager.plugins, \ + 'The plugins on the settings form should be the same as the plugins in the plugin manager' + + +def test_hook_settings_tabs_with_active_plugin_and_mocked_form(registry, state): + """ + Test running the hook_settings_tabs() method with an active plugin and a mocked settings form + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + mocked_settings_form = MagicMock() + # Replace the autoloaded plugin with the version for testing in real code this would error + mocked_settings_form.plugin_manager = plugin_manager + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_settings_tabs() + plugin_manager.hook_settings_tabs() + + # THEN: The create_media_manager_item() method should have been called with the mocked settings form + assert 1 == mocked_plugin.create_settings_tab.call_count, \ + 'The create_media_manager_item() method should have been called once.' + assert plugin_manager.plugins == mocked_settings_form.plugin_manager.plugins, \ + 'The plugins on the settings form should be the same as the plugins in the plugin manager' + + +def test_hook_settings_tabs_with_active_plugin_and_no_form(plugin_manager_env): + """ + Test running the hook_settings_tabs() method with an active plugin and no settings form + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_settings_tabs() + plugin_manager.hook_settings_tabs() + + # THEN: The create_settings_tab() method should have been called + mocked_plugin.create_settings_tab.assert_called_with(Registry().get('settings_form')) + + +def test_hook_import_menu_with_disabled_plugin(registry, state): + """ + Test running the hook_import_menu() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_import_menu() + plugin_manager.hook_import_menu() + + # THEN: The create_media_manager_item() method should have been called + assert 0 == mocked_plugin.add_import_menu_item.call_count, \ + 'The add_import_menu_item() method should not have been called.' + + +def test_hook_import_menu_with_active_plugin(plugin_manager_env): + """ + Test running the hook_import_menu() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_import_menu() + plugin_manager.hook_import_menu() + + # THEN: The add_import_menu_item() method should have been called + mocked_plugin.add_import_menu_item.assert_called_with(Registry().get('main_window').file_import_menu) + + +def test_hook_export_menu_with_disabled_plugin(registry, state): + """ + Test running the hook_export_menu() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_export_menu() + plugin_manager.hook_export_menu() + + # THEN: The add_export_menu_item() method should not have been called + assert 0 == mocked_plugin.add_export_menu_item.call_count, \ + 'The add_export_menu_item() method should not have been called.' + + +def test_hook_export_menu_with_active_plugin(plugin_manager_env): + """ + Test running the hook_export_menu() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_export_menu() + plugin_manager.hook_export_menu() + + # THEN: The add_export_menu_item() method should have been called + mocked_plugin.add_export_menu_item.assert_called_with(Registry().get('main_window').file_export_menu) + + +def test_hook_upgrade_plugin_settings_with_disabled_plugin(registry, state): + """ + Test running the hook_upgrade_plugin_settings() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + settings = Settings() + + # WHEN: We run hook_upgrade_plugin_settings() + plugin_manager.hook_upgrade_plugin_settings(settings) + + # THEN: The upgrade_settings() method should not have been called + assert 0 == mocked_plugin.upgrade_settings.call_count, \ + 'The upgrade_settings() method should not have been called.' + + +def test_hook_upgrade_plugin_settings_with_active_plugin(registry, state): + """ + Test running the hook_upgrade_plugin_settings() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + settings = Settings() + + # WHEN: We run hook_upgrade_plugin_settings() + plugin_manager.hook_upgrade_plugin_settings(settings) + + # THEN: The add_export_menu_item() method should have been called + mocked_plugin.upgrade_settings.assert_called_with(settings) + + +def test_hook_tools_menu_with_disabled_plugin(registry, state): + """ + Test running the hook_tools_menu() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_tools_menu() + plugin_manager.hook_tools_menu() + + # THEN: The add_tools_menu_item() method should have been called + assert 0 == mocked_plugin.add_tools_menu_item.call_count, \ + 'The add_tools_menu_item() method should not have been called.' + + +def test_hook_tools_menu_with_active_plugin(plugin_manager_env): + """ + Test running the hook_tools_menu() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run hook_tools_menu() + plugin_manager.hook_tools_menu() + + # THEN: The add_tools_menu_item() method should have been called + mocked_plugin.add_tools_menu_item.assert_called_with(Registry().get('main_window').tools_menu) + + +def test_initialise_plugins_with_disabled_plugin(registry, state): + """ + Test running the initialise_plugins() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + mocked_plugin.is_active.return_value = False + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run initialise_plugins() + plugin_manager.initialise_plugins() + + # THEN: The is_active() method should have been called, and initialise() method should NOT have been called + mocked_plugin.is_active.assert_called_with() + assert 0 == mocked_plugin.initialise.call_count, 'The initialise() method should not have been called.' + + +def test_initialise_plugins_with_active_plugin(registry, state): + """ + Test running the initialise_plugins() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + mocked_plugin.is_active.return_value = True + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run initialise_plugins() + plugin_manager.initialise_plugins() + + # THEN: The is_active() and initialise() methods should have been called + mocked_plugin.is_active.assert_called_with() + mocked_plugin.initialise.assert_called_with() + + +def test_finalise_plugins_with_disabled_plugin(registry, state): + """ + Test running the finalise_plugins() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + mocked_plugin.is_active.return_value = False + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run finalise_plugins() + plugin_manager.finalise_plugins() + + # THEN: The is_active() method should have been called, and initialise() method should NOT have been called + mocked_plugin.is_active.assert_called_with() + assert 0 == mocked_plugin.finalise.call_count, 'The finalise() method should not have been called.' + + +def test_finalise_plugins_with_active_plugin(registry, state): + """ + Test running the finalise_plugins() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + mocked_plugin.is_active.return_value = True + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run finalise_plugins() + plugin_manager.finalise_plugins() + + # THEN: The is_active() and finalise() methods should have been called + mocked_plugin.is_active.assert_called_with() + mocked_plugin.finalise.assert_called_with() + + +def test_get_plugin_by_name_does_not_exist(registry, state): + """ + Test running the get_plugin_by_name() method to find a plugin that does not exist + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.name = 'Mocked Plugin' + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run finalise_plugins() + result = plugin_manager.get_plugin_by_name('Missing Plugin') + + # THEN: The is_active() and finalise() methods should have been called + assert result is None, 'The result for get_plugin_by_name should be None' + + +def test_get_plugin_by_name_exists(registry, state): + """ + Test running the get_plugin_by_name() method to find a plugin that exists + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.name = 'Mocked Plugin' + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run finalise_plugins() + result = plugin_manager.get_plugin_by_name('Mocked Plugin') + + # THEN: The is_active() and finalise() methods should have been called + assert result == mocked_plugin, 'The result for get_plugin_by_name should be the mocked plugin' + + +def test_new_service_created_with_disabled_plugin(registry, state): + """ + Test running the new_service_created() method with a disabled plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Disabled + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Disabled + mocked_plugin.is_active.return_value = False + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run finalise_plugins() + plugin_manager.new_service_created() + + # THEN: The isActive() method should have been called, and initialise() method should NOT have been called + mocked_plugin.is_active.assert_called_with() + assert 0 == mocked_plugin.new_service_created.call_count, \ + 'The new_service_created() method should not have been called.' + + +def test_new_service_created_with_active_plugin(registry, state): + """ + Test running the new_service_created() method with an active plugin + """ + # GIVEN: A PluginManager instance and a list with a mocked up plugin whose status is set to Active + mocked_plugin = MagicMock() + mocked_plugin.status = PluginStatus.Active + mocked_plugin.is_active.return_value = True + plugin_manager = PluginManager() + Registry().register('mock_plugin', mocked_plugin) + State().add_service("mock", 1, is_plugin=True, status=PluginStatus.Active) + State().flush_preconditions() + + # WHEN: We run new_service_created() + plugin_manager.new_service_created() + + # THEN: The is_active() and finalise() methods should have been called + mocked_plugin.is_active.assert_called_with() + mocked_plugin.new_service_created.assert_called_with() diff --git a/tests/functional/openlp_core/lib/test_serviceitem.py b/tests/functional/openlp_core/lib/test_serviceitem.py index d56283c36..4d547621c 100644 --- a/tests/functional/openlp_core/lib/test_serviceitem.py +++ b/tests/functional/openlp_core/lib/test_serviceitem.py @@ -22,18 +22,16 @@ Package to test the openlp.core.lib package. """ import os +import pytest from pathlib import Path -from unittest import TestCase from unittest.mock import Mock, MagicMock, patch from openlp.core.state import State from openlp.core.common import ThemeLevel, md5_hash from openlp.core.common.enum import ServiceItemType from openlp.core.common.registry import Registry -from openlp.core.common.settings import Settings from openlp.core.lib.formattingtags import FormattingTags from openlp.core.lib.serviceitem import ItemCapabilities, ServiceItem -from tests.helpers.testmixin import TestMixin from tests.utils import convert_file_service_item from tests.utils.constants import RESOURCE_PATH @@ -63,407 +61,411 @@ RENDERED_VERSE = 'The Lord said to Noa FOOTER = ['Arky Arky (Unknown)', 'Public Domain', 'CCLI 123456'] TEST_PATH = RESOURCE_PATH / 'service' -__default_settings__ = { - 'songs/enable chords': True, -} + +@pytest.fixture() +def state_env(state): + State().add_service("media", 0) + State().update_pre_conditions("media", True) + State().flush_preconditions() -class TestServiceItem(TestCase, TestMixin): +@pytest.fixture() +def service_item_env(state): + # Mock the renderer and its format_slide method + mocked_renderer = MagicMock() - def setUp(self): - """ - Set up the Registry - """ - self.build_settings() - self.setting.extend_default_settings(__default_settings__) - self.setting.setValue('songs/chord notation', 'english') - Registry.create() - Registry().register('settings', self.setting) - # Mock the renderer and its format_slide method - mocked_renderer = MagicMock() + def side_effect_return_arg(arg1, arg2): + return [arg1] - def side_effect_return_arg(arg1, arg2): - return [arg1] - mocked_slide_formater = MagicMock(side_effect=side_effect_return_arg) - mocked_renderer.format_slide = mocked_slide_formater - Registry().register('renderer', mocked_renderer) - Registry().register('image_manager', MagicMock()) + mocked_slide_formater = MagicMock(side_effect=side_effect_return_arg) + mocked_renderer.format_slide = mocked_slide_formater + Registry().register('renderer', mocked_renderer) + Registry().register('image_manager', MagicMock()) - def tearDown(self): - """ - Clean up - """ - self.destroy_settings() - def test_service_item_basic(self): - """ - Test the Service Item - basic test - """ - # GIVEN: A new service item +def test_service_item_basic(): + """ + Test the Service Item - basic test + """ + # GIVEN: A new service item - # WHEN: A service item is created (without a plugin) - service_item = ServiceItem(None) + # WHEN: A service item is created (without a plugin) + service_item = ServiceItem(None) - # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert service_item.missing_frames() is True, 'There should not be any frames in the service item' + # THEN: We should get back a valid service item + assert service_item.is_valid is True, 'The new service item should be valid' + assert service_item.missing_frames() is True, 'There should not be any frames in the service item' - def test_service_item_load_custom_from_service(self): - """ - Test the Service Item - adding a custom slide from a saved service - """ - # GIVEN: A new service item and a mocked add icon function - service_item = ServiceItem(None) - service_item.add_icon = MagicMock() - FormattingTags.load_tags() - # WHEN: We add a custom from a saved serviceand set the media state - line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj') - State().add_service("media", 0) - State().update_pre_conditions("media", True) - State().flush_preconditions() - service_item.set_from_service(line) +def test_service_item_load_custom_from_service(state_env, settings, service_item_env): + """ + Test the Service Item - adding a custom slide from a saved service + """ + # GIVEN: A new service item and a mocked add icon function + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() + FormattingTags.load_tags() - # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert len(service_item.get_frames()) == 2, 'The service item should have 2 display frames' - assert len(service_item.capabilities) == 5, 'There should be 5 default custom item capabilities' + # WHEN: We add a custom from a saved serviceand set the media state + line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj') + State().add_service("media", 0) + State().update_pre_conditions("media", True) + State().flush_preconditions() + service_item.set_from_service(line) - # THEN: The frames should also be valid - assert 'Test Custom' == service_item.get_display_title(), 'The title should be "Test Custom"' - assert 'Slide 1' == service_item.get_frames()[0]['text'] - assert 'Slide 2' == service_item.get_rendered_frame(1) - assert 'Slide 1' == service_item.get_frame_title(0), '"Slide 1" has been returned as the title' - assert 'Slide 2' == service_item.get_frame_title(1), '"Slide 2" has been returned as the title' - assert '' == service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3' + # THEN: We should get back a valid service item + assert service_item.is_valid is True, 'The new service item should be valid' + assert len(service_item.get_frames()) == 2, 'The service item should have 2 display frames' + assert len(service_item.capabilities) == 5, 'There should be 5 default custom item capabilities' - def test_service_item_load_image_from_service(self): - """ - Test the Service Item - adding an image from a saved service - """ - # GIVEN: A new service item and a mocked add icon function - image_name = 'image_1.jpg' - test_file = TEST_PATH / image_name - frame_array = {'path': test_file, 'title': image_name} + # THEN: The frames should also be valid + assert 'Test Custom' == service_item.get_display_title(), 'The title should be "Test Custom"' + assert 'Slide 1' == service_item.get_frames()[0]['text'] + assert 'Slide 2' == service_item.get_rendered_frame(1) + assert 'Slide 1' == service_item.get_frame_title(0), '"Slide 1" has been returned as the title' + assert 'Slide 2' == service_item.get_frame_title(1), '"Slide 2" has been returned as the title' + assert '' == service_item.get_frame_title(2), 'Blank has been returned as the title of slide 3' - service_item = ServiceItem(None) - service_item.add_icon = MagicMock() - # WHEN: adding an image from a saved Service and mocked exists - line = convert_file_service_item(TEST_PATH, 'serviceitem_image_1.osj') - with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists,\ - patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') as \ - mocked_get_section_data_path: - mocked_exists.return_value = True - mocked_get_section_data_path.return_value = Path('/path/') - service_item.set_from_service(line, TEST_PATH) +def test_service_item_load_image_from_service(state_env, settings): + """ + Test the Service Item - adding an image from a saved service + """ + # GIVEN: A new service item and a mocked add icon function + image_name = 'image_1.jpg' + test_file = TEST_PATH / image_name + frame_array = {'path': test_file, 'title': image_name} - # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image' - assert frame_array == service_item.get_frames()[0], 'The return should match frame array1' - assert test_file == service_item.get_frame_path(0), \ - 'The frame path should match the full path to the image' - assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name' - assert image_name == service_item.get_display_title(), 'The display title should match the first image name' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() - @patch('openlp.core.lib.serviceitem.os.path.exists') - @patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') - def test_service_item_load_image_from_local_service(self, mocked_get_section_data_path, mocked_exists): - """ - Test the Service Item - adding an image from a saved local service - """ - # GIVEN: A new service item and a mocked add icon function - mocked_get_section_data_path.return_value = Path('/path') + # WHEN: adding an image from a saved Service and mocked exists + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_1.osj') + with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists,\ + patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') as \ + mocked_get_section_data_path: mocked_exists.return_value = True - image_name1 = 'image_1.jpg' - image_name2 = 'image_2.jpg' - test_file1 = os.path.join('/home', 'openlp', image_name1) - test_file2 = os.path.join('/home', 'openlp', image_name2) - frame_array1 = {'path': test_file1, 'title': image_name1} - frame_array2 = {'path': test_file2, 'title': image_name2} - service_item = ServiceItem(None) - service_item.add_icon = MagicMock() - service_item2 = ServiceItem(None) - service_item2.add_icon = MagicMock() + mocked_get_section_data_path.return_value = Path('/path/') + service_item.set_from_service(line, TEST_PATH) - # WHEN: adding an image from a saved Service and mocked exists - line = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj') - line2 = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj', 1) - service_item2.set_from_service(line2) + # THEN: We should get back a valid service item + assert service_item.is_valid is True, 'The new service item should be valid' + assert test_file == service_item.get_rendered_frame(0), 'The first frame should match the path to the image' + assert frame_array == service_item.get_frames()[0], 'The return should match frame array1' + assert test_file == service_item.get_frame_path(0), \ + 'The frame path should match the full path to the image' + assert image_name == service_item.get_frame_title(0), 'The frame title should match the image name' + assert image_name == service_item.get_display_title(), 'The display title should match the first image name' + assert service_item.is_image() is True, 'This service item should be of an "image" type' + assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ + 'This service item should be able to be Maintained' + assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ + 'This service item should be able to be be Previewed' + assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ + 'This service item should be able to be run in a can be made to Loop' + assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ + 'This service item should be able to have new items added to it' + + +@patch('openlp.core.lib.serviceitem.os.path.exists') +@patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') +def test_service_item_load_image_from_local_service(mocked_get_section_data_path, mocked_exists, settings, state_env): + """ + Test the Service Item - adding an image from a saved local service + """ + # GIVEN: A new service item and a mocked add icon function + mocked_get_section_data_path.return_value = Path('/path') + mocked_exists.return_value = True + image_name1 = 'image_1.jpg' + image_name2 = 'image_2.jpg' + test_file1 = os.path.join('/home', 'openlp', image_name1) + test_file2 = os.path.join('/home', 'openlp', image_name2) + frame_array1 = {'path': test_file1, 'title': image_name1} + frame_array2 = {'path': test_file2, 'title': image_name2} + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() + service_item2 = ServiceItem(None) + service_item2.add_icon = MagicMock() + + # WHEN: adding an image from a saved Service and mocked exists + line = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj') + line2 = convert_file_service_item(TEST_PATH, 'serviceitem_image_2.osj', 1) + service_item2.set_from_service(line2) + service_item.set_from_service(line) + + # THEN: We should get back a valid service item + assert service_item.is_valid is True, 'The first service item should be valid' + assert service_item2.is_valid is True, 'The second service item should be valid' + # These test will fail on windows due to the difference in folder seperators + if os.name != 'nt': + assert test_file1 == service_item.get_rendered_frame(0), \ + 'The first frame should match the path to the image' + assert test_file2 == service_item2.get_rendered_frame(0), \ + 'The Second frame should match the path to the image' + assert frame_array1 == service_item.get_frames()[0], 'The return should match the frame array1' + assert frame_array2 == service_item2.get_frames()[0], 'The return should match the frame array2' + assert test_file1 == str(service_item.get_frame_path(0)), \ + 'The frame path should match the full path to the image' + assert test_file2 == str(service_item2.get_frame_path(0)), \ + 'The frame path should match the full path to the image' + assert image_name1 == service_item.get_frame_title(0), 'The 1st frame title should match the image name' + assert image_name2 == service_item2.get_frame_title(0), 'The 2nd frame title should match the image name' + assert service_item.name == service_item.title.lower(), \ + 'The plugin name should match the display title, as there are > 1 Images' + assert service_item.is_image() is True, 'This service item should be of an "image" type' + assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ + 'This service item should be able to be Maintained' + assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ + 'This service item should be able to be be Previewed' + assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ + 'This service item should be able to be run in a can be made to Loop' + assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ + 'This service item should be able to have new items added to it' + + +def test_add_from_command_for_a_presentation(): + """ + Test the Service Item - adding a presentation + """ + # GIVEN: A service item, a mocked icon and presentation data + service_item = ServiceItem(None) + presentation_name = 'test.pptx' + image = MagicMock() + display_title = 'DisplayTitle' + notes = 'Note1\nNote2\n' + frame = {'title': presentation_name, 'image': image, 'path': TEST_PATH, + 'display_title': display_title, 'notes': notes, 'thumbnail': image} + + # WHEN: adding presentation to service_item + service_item.add_from_command(TEST_PATH, presentation_name, image, display_title, notes) + + # THEN: verify that it is setup as a Command and that the frame data matches + assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' + assert service_item.get_frames()[0] == frame, 'Frames should match' + + +def test_add_from_command_without_display_title_and_notes(): + """ + Test the Service Item - add from command, but not presentation + """ + # GIVEN: A new service item, a mocked icon and image data + service_item = ServiceItem(None) + image_name = 'test.img' + image = MagicMock() + frame = {'title': image_name, 'image': image, 'path': TEST_PATH, + 'display_title': None, 'notes': None, 'thumbnail': image} + + # WHEN: adding image to service_item + service_item.add_from_command(TEST_PATH, image_name, image) + + # THEN: verify that it is setup as a Command and that the frame data matches + assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' + assert service_item.get_frames()[0] == frame, 'Frames should match' + + +@patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager') +@patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') +def test_add_from_command_for_a_presentation_thumb(mocked_get_section_data_path, mocked_image_manager): + """ + Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager + """ + # GIVEN: A service item, a mocked AppLocation and presentation data + mocked_get_section_data_path.return_value = Path('mocked') / 'section' / 'path' + service_item = ServiceItem(None) + service_item.add_capability(ItemCapabilities.HasThumbnails) + service_item.has_original_files = False + service_item.name = 'presentations' + presentation_name = 'test.pptx' + thumb = Path('tmp') / 'test' / 'thumb.png' + display_title = 'DisplayTitle' + notes = 'Note1\nNote2\n' + expected_thumb_path = Path('mocked') / 'section' / 'path' / 'thumbnails' / \ + md5_hash(str(TEST_PATH / presentation_name).encode('utf8')) / 'thumb.png' + frame = {'title': presentation_name, 'image': str(expected_thumb_path), 'path': str(TEST_PATH), + 'display_title': display_title, 'notes': notes, 'thumbnail': str(expected_thumb_path)} + + # WHEN: adding presentation to service_item + service_item.add_from_command(str(TEST_PATH), presentation_name, thumb, display_title, notes) + + # THEN: verify that it is setup as a Command and that the frame data matches + assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' + assert service_item.get_frames()[0] == frame, 'Frames should match' + # assert 1 == mocked_image_manager.add_image.call_count, 'image_manager should be used' + + +def test_service_item_load_optical_media_from_service(state_env): + """ + Test the Service Item - load an optical media item + """ + # GIVEN: A new service item and a mocked add icon function + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() + + # WHEN: We load a serviceitem with optical media + line = convert_file_service_item(TEST_PATH, 'serviceitem-dvd.osj') + with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: + mocked_exists.return_value = True service_item.set_from_service(line) - # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The first service item should be valid' - assert service_item2.is_valid is True, 'The second service item should be valid' - # These test will fail on windows due to the difference in folder seperators - if os.name != 'nt': - assert test_file1 == service_item.get_rendered_frame(0), \ - 'The first frame should match the path to the image' - assert test_file2 == service_item2.get_rendered_frame(0), \ - 'The Second frame should match the path to the image' - assert frame_array1 == service_item.get_frames()[0], 'The return should match the frame array1' - assert frame_array2 == service_item2.get_frames()[0], 'The return should match the frame array2' - assert test_file1 == str(service_item.get_frame_path(0)), \ - 'The frame path should match the full path to the image' - assert test_file2 == str(service_item2.get_frame_path(0)), \ - 'The frame path should match the full path to the image' - assert image_name1 == service_item.get_frame_title(0), 'The 1st frame title should match the image name' - assert image_name2 == service_item2.get_frame_title(0), 'The 2nd frame title should match the image name' - assert service_item.name == service_item.title.lower(), \ - 'The plugin name should match the display title, as there are > 1 Images' - assert service_item.is_image() is True, 'This service item should be of an "image" type' - assert service_item.is_capable(ItemCapabilities.CanMaintain) is True, \ - 'This service item should be able to be Maintained' - assert service_item.is_capable(ItemCapabilities.CanPreview) is True, \ - 'This service item should be able to be be Previewed' - assert service_item.is_capable(ItemCapabilities.CanLoop) is True, \ - 'This service item should be able to be run in a can be made to Loop' - assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ - 'This service item should be able to have new items added to it' + # THEN: We should get back a valid service item with optical media info + assert service_item.is_valid is True, 'The service item should be valid' + assert service_item.is_capable(ItemCapabilities.IsOptical) is True, 'The item should be Optical' + assert service_item.start_time == 654.375, 'Start time should be 654.375' + assert service_item.end_time == 672.069, 'End time should be 672.069' + assert service_item.media_length == 17.694, 'Media length should be 17.694' - def test_add_from_command_for_a_presentation(self): - """ - Test the Service Item - adding a presentation - """ - # GIVEN: A service item, a mocked icon and presentation data - service_item = ServiceItem(None) - presentation_name = 'test.pptx' - image = MagicMock() - display_title = 'DisplayTitle' - notes = 'Note1\nNote2\n' - frame = {'title': presentation_name, 'image': image, 'path': TEST_PATH, - 'display_title': display_title, 'notes': notes, 'thumbnail': image} - # WHEN: adding presentation to service_item - service_item.add_from_command(TEST_PATH, presentation_name, image, display_title, notes) +def test_service_item_load_song_and_audio_from_service(state_env, settings, service_item_env): + """ + Test the Service Item - adding a song slide from a saved service + """ + # GIVEN: A new service item and a mocked add icon function + service_item = ServiceItem(None) + service_item.add_icon = MagicMock() + FormattingTags.load_tags() - # THEN: verify that it is setup as a Command and that the frame data matches - assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' - assert service_item.get_frames()[0] == frame, 'Frames should match' + # WHEN: We add a custom from a saved service + line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj') + service_item.set_from_service(line, Path('/test/')) - def test_add_from_command_without_display_title_and_notes(self): - """ - Test the Service Item - add from command, but not presentation - """ - # GIVEN: A new service item, a mocked icon and image data - service_item = ServiceItem(None) - image_name = 'test.img' - image = MagicMock() - frame = {'title': image_name, 'image': image, 'path': TEST_PATH, - 'display_title': None, 'notes': None, 'thumbnail': image} + # THEN: We should get back a valid service item + assert service_item.is_valid is True, 'The new service item should be valid' + assert len(service_item.display_slides) == 6, 'The service item should have 6 display slides' + assert len(service_item.capabilities) == 7, 'There should be 7 default custom item capabilities' + assert 'Amazing Grace' == service_item.get_display_title(), 'The title should be "Amazing Grace"' + assert CLEANED_VERSE[:-1] == service_item.get_frames()[0]['text'], \ + 'The returned text matches the input, except the last line feed' + assert 'Amazing Grace! how sweet the s' == service_item.get_frame_title(0), \ + '"Amazing Grace! how sweet the s" has been returned as the title' + assert '’Twas grace that taught my hea' == service_item.get_frame_title(1), \ + '"’Twas grace that taught my hea" has been returned as the title' + assert Path('/test/amazing_grace.mp3') == service_item.background_audio[0], \ + '"/test/amazing_grace.mp3" should be in the background_audio list' - # WHEN: adding image to service_item - service_item.add_from_command(TEST_PATH, image_name, image) - # THEN: verify that it is setup as a Command and that the frame data matches - assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' - assert service_item.get_frames()[0] == frame, 'Frames should match' +def test_service_item_get_theme_data_global_level(settings): + """ + Test the service item - get theme data when set to global theme level + """ + # GIVEN: A service item with a theme and theme level set to global + service_item = ServiceItem(None) + service_item.theme = 'song_theme' + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Global) - @patch(u'openlp.core.lib.serviceitem.ServiceItem.image_manager') - @patch('openlp.core.lib.serviceitem.AppLocation.get_section_data_path') - def test_add_from_command_for_a_presentation_thumb(self, mocked_get_section_data_path, mocked_image_manager): - """ - Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager - """ - # GIVEN: A service item, a mocked AppLocation and presentation data - mocked_get_section_data_path.return_value = Path('mocked') / 'section' / 'path' - service_item = ServiceItem(None) - service_item.add_capability(ItemCapabilities.HasThumbnails) - service_item.has_original_files = False - service_item.name = 'presentations' - presentation_name = 'test.pptx' - thumb = Path('tmp') / 'test' / 'thumb.png' - display_title = 'DisplayTitle' - notes = 'Note1\nNote2\n' - expected_thumb_path = Path('mocked') / 'section' / 'path' / 'thumbnails' / \ - md5_hash(str(TEST_PATH / presentation_name).encode('utf8')) / 'thumb.png' - frame = {'title': presentation_name, 'image': str(expected_thumb_path), 'path': str(TEST_PATH), - 'display_title': display_title, 'notes': notes, 'thumbnail': str(expected_thumb_path)} + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - # WHEN: adding presentation to service_item - service_item.add_from_command(str(TEST_PATH), presentation_name, thumb, display_title, notes) + # THEN: theme should be the global theme + assert theme == mocked_theme_manager.global_theme - # THEN: verify that it is setup as a Command and that the frame data matches - assert service_item.service_item_type == ServiceItemType.Command, 'It should be a Command' - assert service_item.get_frames()[0] == frame, 'Frames should match' - # assert 1 == mocked_image_manager.add_image.call_count, 'image_manager should be used' - def test_service_item_load_optical_media_from_service(self): - """ - Test the Service Item - load an optical media item - """ - # GIVEN: A new service item and a mocked add icon function - service_item = ServiceItem(None) - service_item.add_icon = MagicMock() +def test_service_item_get_theme_data_service_level_service_undefined(settings): + """ + Test the service item - get theme data when set to service theme level + """ + # GIVEN: A service item with a theme and theme level set to service + service_item = ServiceItem(None) + service_item.theme = 'song_theme' + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Service) - # WHEN: We load a serviceitem with optical media - line = convert_file_service_item(TEST_PATH, 'serviceitem-dvd.osj') - with patch('openlp.core.ui.servicemanager.os.path.exists') as mocked_exists: - mocked_exists.return_value = True - service_item.set_from_service(line) + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - # THEN: We should get back a valid service item with optical media info - assert service_item.is_valid is True, 'The service item should be valid' - assert service_item.is_capable(ItemCapabilities.IsOptical) is True, 'The item should be Optical' - assert service_item.start_time == 654.375, 'Start time should be 654.375' - assert service_item.end_time == 672.069, 'End time should be 672.069' - assert service_item.media_length == 17.694, 'Media length should be 17.694' + # THEN: theme should be the global theme + assert theme == mocked_theme_manager.global_theme - def test_service_item_load_song_and_audio_from_service(self): - """ - Test the Service Item - adding a song slide from a saved service - """ - # GIVEN: A new service item and a mocked add icon function - service_item = ServiceItem(None) - service_item.add_icon = MagicMock() - FormattingTags.load_tags() - # WHEN: We add a custom from a saved service - line = convert_file_service_item(TEST_PATH, 'serviceitem-song-linked-audio.osj') - service_item.set_from_service(line, Path('/test/')) +def test_service_item_get_theme_data_service_level_service_defined(settings): + """ + Test the service item - get theme data when set to service theme level + """ + # GIVEN: A service item with a theme and theme level set to service + service_item = ServiceItem(None) + service_item.theme = 'song_theme' + service_item.from_service = True + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Service) - # THEN: We should get back a valid service item - assert service_item.is_valid is True, 'The new service item should be valid' - assert len(service_item.display_slides) == 6, 'The service item should have 6 display slides' - assert len(service_item.capabilities) == 7, 'There should be 7 default custom item capabilities' - assert 'Amazing Grace' == service_item.get_display_title(), 'The title should be "Amazing Grace"' - assert CLEANED_VERSE[:-1] == service_item.get_frames()[0]['text'], \ - 'The returned text matches the input, except the last line feed' - assert 'Amazing Grace! how sweet the s' == service_item.get_frame_title(0), \ - '"Amazing Grace! how sweet the s" has been returned as the title' - assert '’Twas grace that taught my hea' == service_item.get_frame_title(1), \ - '"’Twas grace that taught my hea" has been returned as the title' - assert Path('/test/amazing_grace.mp3') == service_item.background_audio[0], \ - '"/test/amazing_grace.mp3" should be in the background_audio list' + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - def test_service_item_get_theme_data_global_level(self): - """ - Test the service item - get theme data when set to global theme level - """ - # GIVEN: A service item with a theme and theme level set to global - service_item = ServiceItem(None) - service_item.theme = 'song_theme' - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Global) + # THEN: theme should be the service theme + assert theme == settings.value('servicemanager/service theme') - # WHEN: Get theme data is run - theme = service_item.get_theme_data() - # THEN: theme should be the global theme - assert theme == mocked_theme_manager.global_theme +def test_service_item_get_theme_data_song_level(settings): + """ + Test the service item - get theme data when set to song theme level + """ + # GIVEN: A service item with a theme and theme level set to song + service_item = ServiceItem(None) + service_item.theme = 'song_theme' + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Song) - def test_service_item_get_theme_data_service_level_service_undefined(self): - """ - Test the service item - get theme data when set to service theme level - """ - # GIVEN: A service item with a theme and theme level set to service - service_item = ServiceItem(None) - service_item.theme = 'song_theme' - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Service) + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - # WHEN: Get theme data is run - theme = service_item.get_theme_data() + # THEN: theme should be the song theme + assert theme == service_item.theme - # THEN: theme should be the global theme - assert theme == mocked_theme_manager.global_theme - def test_service_item_get_theme_data_service_level_service_defined(self): - """ - Test the service item - get theme data when set to service theme level - """ - # GIVEN: A service item with a theme and theme level set to service - service_item = ServiceItem(None) - service_item.theme = 'song_theme' - service_item.from_service = True - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Service) +def test_service_item_get_theme_data_song_level_service_fallback(settings): + """ + Test the service item - get theme data when set to song theme level + but the song theme doesn't exist + """ + # GIVEN: A service item with a theme and theme level set to song + service_item = ServiceItem(None) + service_item.from_service = True + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Song) - # WHEN: Get theme data is run - theme = service_item.get_theme_data() + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - # THEN: theme should be the service theme - assert theme == Settings().value('servicemanager/service theme') + # THEN: theme should be the serice theme + assert theme == settings.value('servicemanager/service theme') - def test_service_item_get_theme_data_song_level(self): - """ - Test the service item - get theme data when set to song theme level - """ - # GIVEN: A service item with a theme and theme level set to song - service_item = ServiceItem(None) - service_item.theme = 'song_theme' - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Song) - # WHEN: Get theme data is run - theme = service_item.get_theme_data() +def test_service_item_get_theme_data_song_level_global_fallback(settings): + """ + Test the service item - get theme data when set to song theme level + but the song and service theme don't exist + """ + # GIVEN: A service item with a theme and theme level set to song + service_item = ServiceItem(None) + mocked_theme_manager = MagicMock() + mocked_theme_manager.global_theme = 'global_theme' + mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) + Registry().register('theme_manager', mocked_theme_manager) + settings.setValue('servicemanager/service theme', 'service_theme') + settings.setValue('themes/theme level', ThemeLevel.Song) - # THEN: theme should be the song theme - assert theme == service_item.theme + # WHEN: Get theme data is run + theme = service_item.get_theme_data() - def test_service_item_get_theme_data_song_level_service_fallback(self): - """ - Test the service item - get theme data when set to song theme level - but the song theme doesn't exist - """ - # GIVEN: A service item with a theme and theme level set to song - service_item = ServiceItem(None) - service_item.from_service = True - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Song) - - # WHEN: Get theme data is run - theme = service_item.get_theme_data() - - # THEN: theme should be the serice theme - assert theme == Settings().value('servicemanager/service theme') - - def test_service_item_get_theme_data_song_level_global_fallback(self): - """ - Test the service item - get theme data when set to song theme level - but the song and service theme don't exist - """ - # GIVEN: A service item with a theme and theme level set to song - service_item = ServiceItem(None) - mocked_theme_manager = MagicMock() - mocked_theme_manager.global_theme = 'global_theme' - mocked_theme_manager.get_theme_data = Mock(side_effect=lambda value: value) - Registry().register('theme_manager', mocked_theme_manager) - Settings().setValue('servicemanager/service theme', 'service_theme') - Settings().setValue('themes/theme level', ThemeLevel.Song) - - # WHEN: Get theme data is run - theme = service_item.get_theme_data() - - # THEN: theme should be the global theme - assert theme == mocked_theme_manager.global_theme + # THEN: theme should be the global theme + assert theme == mocked_theme_manager.global_theme diff --git a/tests/functional/openlp_core/lib/test_theme.py b/tests/functional/openlp_core/lib/test_theme.py index 0a18daf30..17fe7974c 100644 --- a/tests/functional/openlp_core/lib/test_theme.py +++ b/tests/functional/openlp_core/lib/test_theme.py @@ -22,319 +22,324 @@ Package to test the openlp.core.lib.theme package. """ from pathlib import Path -from unittest import TestCase from unittest.mock import MagicMock, patch from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, TransitionType, TransitionSpeed, Theme -class ThemeEnumerationTypes(TestCase): +def test_background_type_to_string(): """ - Test the theme enum methods. + Test the to_string method of :class:`BackgroundType` """ - def test_background_type_to_string(self): - """ - Test the to_string method of :class:`BackgroundType` - """ - # GIVEN: The BackgroundType members - background_type_solid = BackgroundType.Solid - background_type_gradient = BackgroundType.Gradient - background_type_image = BackgroundType.Image - background_type_transparent = BackgroundType.Transparent - background_type_video = BackgroundType.Video - background_type_stream = BackgroundType.Stream + # GIVEN: The BackgroundType members + background_type_solid = BackgroundType.Solid + background_type_gradient = BackgroundType.Gradient + background_type_image = BackgroundType.Image + background_type_transparent = BackgroundType.Transparent + background_type_video = BackgroundType.Video + background_type_stream = BackgroundType.Stream - # WHEN: Calling BackgroundType.to_string - # THEN: The string equivalents should be returned - assert BackgroundType.to_string(background_type_solid) == 'solid' - assert BackgroundType.to_string(background_type_gradient) == 'gradient' - assert BackgroundType.to_string(background_type_image) == 'image' - assert BackgroundType.to_string(background_type_transparent) == 'transparent' - assert BackgroundType.to_string(background_type_video) == 'video' - assert BackgroundType.to_string(background_type_stream) == 'stream' - - def test_background_type_from_string(self): - """ - Test the from_string method of :class:`BackgroundType` - """ - # GIVEN: The BackgroundType strings - background_type_solid = 'solid' - background_type_gradient = 'gradient' - background_type_image = 'image' - background_type_transparent = 'transparent' - background_type_video = 'video' - background_type_stream = 'stream' - - # WHEN: Calling BackgroundType.from_string - # THEN: The enum equivalents should be returned - assert BackgroundType.from_string(background_type_solid) == BackgroundType.Solid - assert BackgroundType.from_string(background_type_gradient) == BackgroundType.Gradient - assert BackgroundType.from_string(background_type_image) == BackgroundType.Image - assert BackgroundType.from_string(background_type_transparent) == BackgroundType.Transparent - assert BackgroundType.from_string(background_type_video) == BackgroundType.Video - assert BackgroundType.from_string(background_type_stream) == BackgroundType.Stream - - def test_background_gradient_type_to_string(self): - """ - Test the to_string method of :class:`BackgroundGradientType` - """ - # GIVEN: The BackgroundGradientType member - background_gradient_horizontal = BackgroundGradientType.Horizontal - background_gradient_vertical = BackgroundGradientType.Vertical - background_gradient_circular = BackgroundGradientType.Circular - background_gradient_left_top = BackgroundGradientType.LeftTop - background_gradient_left_bottom = BackgroundGradientType.LeftBottom - - # WHEN: Calling BackgroundGradientType.to_string - # THEN: The string equivalents should be returned - assert BackgroundGradientType.to_string(background_gradient_horizontal) == 'horizontal' - assert BackgroundGradientType.to_string(background_gradient_vertical) == 'vertical' - assert BackgroundGradientType.to_string(background_gradient_circular) == 'circular' - assert BackgroundGradientType.to_string(background_gradient_left_top) == 'leftTop' - assert BackgroundGradientType.to_string(background_gradient_left_bottom) == 'leftBottom' - - def test_background_gradient_type_from_string(self): - """ - Test the from_string method of :class:`BackgroundGradientType` - """ - # GIVEN: The BackgroundGradientType strings - background_gradient_horizontal = 'horizontal' - background_gradient_vertical = 'vertical' - background_gradient_circular = 'circular' - background_gradient_left_top = 'leftTop' - background_gradient_left_bottom = 'leftBottom' - - # WHEN: Calling BackgroundGradientType.from_string - # THEN: The enum equivalents should be returned - assert BackgroundGradientType.from_string(background_gradient_horizontal) == BackgroundGradientType.Horizontal - assert BackgroundGradientType.from_string(background_gradient_vertical) == BackgroundGradientType.Vertical - assert BackgroundGradientType.from_string(background_gradient_circular) == BackgroundGradientType.Circular - assert BackgroundGradientType.from_string(background_gradient_left_top) == BackgroundGradientType.LeftTop - assert BackgroundGradientType.from_string(background_gradient_left_bottom) == BackgroundGradientType.LeftBottom - - def test_transition_type_to_string(self): - """ - Test the to_string method of :class:`TransitionType` - """ - # GIVEN: The TransitionType member - transition_type_fade = TransitionType.Fade - transition_type_slide = TransitionType.Slide - transition_type_convex = TransitionType.Convex - transition_type_concave = TransitionType.Concave - transition_type_zoom = TransitionType.Zoom - - # WHEN: Calling TransitionType.to_string - # THEN: The string equivalents should be returned - assert TransitionType.to_string(transition_type_fade) == 'fade' - assert TransitionType.to_string(transition_type_slide) == 'slide' - assert TransitionType.to_string(transition_type_convex) == 'convex' - assert TransitionType.to_string(transition_type_concave) == 'concave' - assert TransitionType.to_string(transition_type_zoom) == 'zoom' - - def test_transition_type_from_string(self): - """ - Test the from_string method of :class:`TransitionType` - """ - # GIVEN: The TransitionType strings - transition_type_fade = 'fade' - transition_type_slide = 'slide' - transition_type_convex = 'convex' - transition_type_concave = 'concave' - transition_type_zoom = 'zoom' - - # WHEN: Calling TransitionType.from_string - # THEN: The enum equivalents should be returned - assert TransitionType.from_string(transition_type_fade) == TransitionType.Fade - assert TransitionType.from_string(transition_type_slide) == TransitionType.Slide - assert TransitionType.from_string(transition_type_convex) == TransitionType.Convex - assert TransitionType.from_string(transition_type_concave) == TransitionType.Concave - assert TransitionType.from_string(transition_type_zoom) == TransitionType.Zoom - - def test_transition_speed_to_string(self): - """ - Test the to_string method of :class:`TransitionSpeed` - """ - # GIVEN: The TransitionSpeed member - transition_speed_normal = TransitionSpeed.Normal - transition_speed_fast = TransitionSpeed.Fast - transition_speed_slow = TransitionSpeed.Slow - - # WHEN: Calling TransitionSpeed.to_string - # THEN: The string equivalents should be returned - assert TransitionSpeed.to_string(transition_speed_normal) == 'normal' - assert TransitionSpeed.to_string(transition_speed_fast) == 'fast' - assert TransitionSpeed.to_string(transition_speed_slow) == 'slow' - - def test_transition_speed_from_string(self): - """ - Test the from_string method of :class:`TransitionSpeed` - """ - # GIVEN: The TransitionSpeed strings - transition_speed_normal = 'normal' - transition_speed_fast = 'fast' - transition_speed_slow = 'slow' - - # WHEN: Calling TransitionSpeed.from_string - # THEN: The enum equivalents should be returned - assert TransitionSpeed.from_string(transition_speed_normal) == TransitionSpeed.Normal - assert TransitionSpeed.from_string(transition_speed_fast) == TransitionSpeed.Fast - assert TransitionSpeed.from_string(transition_speed_slow) == TransitionSpeed.Slow + # WHEN: Calling BackgroundType.to_string + # THEN: The string equivalents should be returned + assert BackgroundType.to_string(background_type_solid) == 'solid' + assert BackgroundType.to_string(background_type_gradient) == 'gradient' + assert BackgroundType.to_string(background_type_image) == 'image' + assert BackgroundType.to_string(background_type_transparent) == 'transparent' + assert BackgroundType.to_string(background_type_video) == 'video' + assert BackgroundType.to_string(background_type_stream) == 'stream' -class TestTheme(TestCase): +def test_background_type_from_string(): """ - Test the Theme class + Test the from_string method of :class:`BackgroundType` """ - def test_new_theme(self): - """ - Test the Theme constructor - """ - # GIVEN: The Theme class - # WHEN: A theme object is created - default_theme = Theme() + # GIVEN: The BackgroundType strings + background_type_solid = 'solid' + background_type_gradient = 'gradient' + background_type_image = 'image' + background_type_transparent = 'transparent' + background_type_video = 'video' + background_type_stream = 'stream' - # THEN: The default values should be correct - self.check_theme(default_theme) + # WHEN: Calling BackgroundType.from_string + # THEN: The enum equivalents should be returned + assert BackgroundType.from_string(background_type_solid) == BackgroundType.Solid + assert BackgroundType.from_string(background_type_gradient) == BackgroundType.Gradient + assert BackgroundType.from_string(background_type_image) == BackgroundType.Image + assert BackgroundType.from_string(background_type_transparent) == BackgroundType.Transparent + assert BackgroundType.from_string(background_type_video) == BackgroundType.Video + assert BackgroundType.from_string(background_type_stream) == BackgroundType.Stream - def test_expand_json(self): - """ - Test the expand_json method - """ - # GIVEN: A Theme object and some JSON to "expand" - theme = Theme() - theme_json = { - 'background': { - 'border_color': '#000000', - 'type': 'solid' + +def test_background_gradient_type_to_string(): + """ + Test the to_string method of :class:`BackgroundGradientType` + """ + # GIVEN: The BackgroundGradientType member + background_gradient_horizontal = BackgroundGradientType.Horizontal + background_gradient_vertical = BackgroundGradientType.Vertical + background_gradient_circular = BackgroundGradientType.Circular + background_gradient_left_top = BackgroundGradientType.LeftTop + background_gradient_left_bottom = BackgroundGradientType.LeftBottom + + # WHEN: Calling BackgroundGradientType.to_string + # THEN: The string equivalents should be returned + assert BackgroundGradientType.to_string(background_gradient_horizontal) == 'horizontal' + assert BackgroundGradientType.to_string(background_gradient_vertical) == 'vertical' + assert BackgroundGradientType.to_string(background_gradient_circular) == 'circular' + assert BackgroundGradientType.to_string(background_gradient_left_top) == 'leftTop' + assert BackgroundGradientType.to_string(background_gradient_left_bottom) == 'leftBottom' + + +def test_background_gradient_type_from_string(): + """ + Test the from_string method of :class:`BackgroundGradientType` + """ + # GIVEN: The BackgroundGradientType strings + background_gradient_horizontal = 'horizontal' + background_gradient_vertical = 'vertical' + background_gradient_circular = 'circular' + background_gradient_left_top = 'leftTop' + background_gradient_left_bottom = 'leftBottom' + + # WHEN: Calling BackgroundGradientType.from_string + # THEN: The enum equivalents should be returned + assert BackgroundGradientType.from_string(background_gradient_horizontal) == BackgroundGradientType.Horizontal + assert BackgroundGradientType.from_string(background_gradient_vertical) == BackgroundGradientType.Vertical + assert BackgroundGradientType.from_string(background_gradient_circular) == BackgroundGradientType.Circular + assert BackgroundGradientType.from_string(background_gradient_left_top) == BackgroundGradientType.LeftTop + assert BackgroundGradientType.from_string(background_gradient_left_bottom) == BackgroundGradientType.LeftBottom + + +def test_transition_type_to_string(): + """ + Test the to_string method of :class:`TransitionType` + """ + # GIVEN: The TransitionType member + transition_type_fade = TransitionType.Fade + transition_type_slide = TransitionType.Slide + transition_type_convex = TransitionType.Convex + transition_type_concave = TransitionType.Concave + transition_type_zoom = TransitionType.Zoom + + # WHEN: Calling TransitionType.to_string + # THEN: The string equivalents should be returned + assert TransitionType.to_string(transition_type_fade) == 'fade' + assert TransitionType.to_string(transition_type_slide) == 'slide' + assert TransitionType.to_string(transition_type_convex) == 'convex' + assert TransitionType.to_string(transition_type_concave) == 'concave' + assert TransitionType.to_string(transition_type_zoom) == 'zoom' + + +def test_transition_type_from_string(): + """ + Test the from_string method of :class:`TransitionType` + """ + # GIVEN: The TransitionType strings + transition_type_fade = 'fade' + transition_type_slide = 'slide' + transition_type_convex = 'convex' + transition_type_concave = 'concave' + transition_type_zoom = 'zoom' + + # WHEN: Calling TransitionType.from_string + # THEN: The enum equivalents should be returned + assert TransitionType.from_string(transition_type_fade) == TransitionType.Fade + assert TransitionType.from_string(transition_type_slide) == TransitionType.Slide + assert TransitionType.from_string(transition_type_convex) == TransitionType.Convex + assert TransitionType.from_string(transition_type_concave) == TransitionType.Concave + assert TransitionType.from_string(transition_type_zoom) == TransitionType.Zoom + + +def test_transition_speed_to_string(): + """ + Test the to_string method of :class:`TransitionSpeed` + """ + # GIVEN: The TransitionSpeed member + transition_speed_normal = TransitionSpeed.Normal + transition_speed_fast = TransitionSpeed.Fast + transition_speed_slow = TransitionSpeed.Slow + + # WHEN: Calling TransitionSpeed.to_string + # THEN: The string equivalents should be returned + assert TransitionSpeed.to_string(transition_speed_normal) == 'normal' + assert TransitionSpeed.to_string(transition_speed_fast) == 'fast' + assert TransitionSpeed.to_string(transition_speed_slow) == 'slow' + + +def test_transition_speed_from_string(): + """ + Test the from_string method of :class:`TransitionSpeed` + """ + # GIVEN: The TransitionSpeed strings + transition_speed_normal = 'normal' + transition_speed_fast = 'fast' + transition_speed_slow = 'slow' + + # WHEN: Calling TransitionSpeed.from_string + # THEN: The enum equivalents should be returned + assert TransitionSpeed.from_string(transition_speed_normal) == TransitionSpeed.Normal + assert TransitionSpeed.from_string(transition_speed_fast) == TransitionSpeed.Fast + assert TransitionSpeed.from_string(transition_speed_slow) == TransitionSpeed.Slow + + +def test_new_theme(): + """ + Test the Theme constructor + """ + # GIVEN: The Theme class + # WHEN: A theme object is created + default_theme = Theme() + + # THEN: The default values should be correct + check_theme(default_theme) + + +def test_expand_json(): + """ + Test the expand_json method + """ + # GIVEN: A Theme object and some JSON to "expand" + theme = Theme() + theme_json = { + 'background': { + 'border_color': '#000000', + 'type': 'solid' + }, + 'display': { + 'vertical_align': 0 + }, + 'font': { + 'footer': { + 'bold': False }, - 'display': { - 'vertical_align': 0 - }, - 'font': { - 'footer': { - 'bold': False - }, - 'main': { - 'name': 'Arial' - } + 'main': { + 'name': 'Arial' } } + } - # WHEN: Theme.expand_json() is run - theme.expand_json(theme_json) + # WHEN: Theme.expand_json() is run + theme.expand_json(theme_json) - # THEN: The attributes should be set on the object - self.check_theme(theme) + # THEN: The attributes should be set on the object + check_theme(theme) - def test_extend_image_filename(self): - """ - Test the extend_image_filename method - """ - # GIVEN: A theme object - theme = Theme() - theme.theme_name = 'MyBeautifulTheme' - theme.background_filename = Path('video.mp4') - theme.background_type = 'video' - path = Path.home() - # WHEN: Theme.extend_image_filename is run - theme.extend_image_filename(path) +def test_extend_image_filename(): + """ + Test the extend_image_filename method + """ + # GIVEN: A theme object + theme = Theme() + theme.theme_name = 'MyBeautifulTheme' + theme.background_filename = Path('video.mp4') + theme.background_type = 'video' + path = Path.home() - # THEN: The filename of the background should be correct - expected_filename = path / 'MyBeautifulTheme' / 'video.mp4' - assert expected_filename == theme.background_filename - assert 'MyBeautifulTheme' == theme.theme_name + # WHEN: Theme.extend_image_filename is run + theme.extend_image_filename(path) - def test_save_retrieve(self): - """ - Load a dummy theme, save it and reload it - """ - # GIVEN: The default Theme class - # WHEN: A theme object is created - default_theme = Theme() - # THEN: The default values should be correct - save_theme_json = default_theme.export_theme() - lt = Theme() - lt.load_theme(save_theme_json) - self.check_theme(lt) + # THEN: The filename of the background should be correct + expected_filename = path / 'MyBeautifulTheme' / 'video.mp4' + assert expected_filename == theme.background_filename + assert 'MyBeautifulTheme' == theme.theme_name - @patch('openlp.core.display.screens.ScreenList.current') - def test_set_default_footer(self, mock_geometry): - """ - Test the set_default_footer function sets the footer back to default - (reletive to the screen) - """ - # GIVEN: A screen geometry object and a Theme footer with a strange area - mock_geometry.display_geometry = MagicMock() - mock_geometry.display_geometry.height.return_value = 600 - mock_geometry.display_geometry.width.return_value = 400 - theme = Theme() - theme.font_main_x = 20 - theme.font_footer_x = 207 - theme.font_footer_y = 25 - theme.font_footer_width = 4253 - theme.font_footer_height = 5423 - # WHEN: set_default_footer is called - theme.set_default_footer() +def test_save_retrieve(): + """ + Load a dummy theme, save it and reload it + """ + # GIVEN: The default Theme class + # WHEN: A theme object is created + default_theme = Theme() + # THEN: The default values should be correct + save_theme_json = default_theme.export_theme() + lt = Theme() + lt.load_theme(save_theme_json) + check_theme(lt) - # THEN: footer should be set, header should not have changed - assert theme.font_main_x == 20, 'header should not have been changed' - assert theme.font_footer_x == 10, 'x pos should be reset to default of 10' - assert theme.font_footer_y == 540, 'y pos should be reset to (screen_size_height * 9 / 10)' - assert theme.font_footer_width == 380, 'width should have been reset to (screen_size_width - 20)' - assert theme.font_footer_height == 60, 'height should have been reset to (screen_size_height / 10)' - @patch('openlp.core.display.screens.ScreenList.current') - def test_set_default_header(self, mock_geometry): - """ - Test the set_default_header function sets the header back to default - (reletive to the screen) - """ - # GIVEN: A screen geometry object and a Theme header with a strange area - mock_geometry.display_geometry = MagicMock() - mock_geometry.display_geometry.height.return_value = 600 - mock_geometry.display_geometry.width.return_value = 400 - theme = Theme() - theme.font_footer_x = 200 - theme.font_main_x = 687 - theme.font_main_y = 546 - theme.font_main_width = 345 - theme.font_main_height = 653 +@patch('openlp.core.display.screens.ScreenList.current') +def test_set_default_footer(mock_geometry): + """ + Test the set_default_footer function sets the footer back to default + (reletive to the screen) + """ + # GIVEN: A screen geometry object and a Theme footer with a strange area + mock_geometry.display_geometry = MagicMock() + mock_geometry.display_geometry.height.return_value = 600 + mock_geometry.display_geometry.width.return_value = 400 + theme = Theme() + theme.font_main_x = 20 + theme.font_footer_x = 207 + theme.font_footer_y = 25 + theme.font_footer_width = 4253 + theme.font_footer_height = 5423 - # WHEN: set_default_header is called - theme.set_default_header() + # WHEN: set_default_footer is called + theme.set_default_footer() - # THEN: footer should be set, header should not have changed - assert theme.font_footer_x == 200, 'footer should not have been changed' - assert theme.font_main_x == 10, 'x pos should be reset to default of 10' - assert theme.font_main_y == 0, 'y pos should be reset to 0' - assert theme.font_main_width == 380, 'width should have been reset to (screen_size_width - 20)' - assert theme.font_main_height == 540, 'height should have been reset to (screen_size_height * 9 / 10)' + # THEN: footer should be set, header should not have changed + assert theme.font_main_x == 20, 'header should not have been changed' + assert theme.font_footer_x == 10, 'x pos should be reset to default of 10' + assert theme.font_footer_y == 540, 'y pos should be reset to (screen_size_height * 9 / 10)' + assert theme.font_footer_width == 380, 'width should have been reset to (screen_size_width - 20)' + assert theme.font_footer_height == 60, 'height should have been reset to (screen_size_height / 10)' - @patch('openlp.core.display.screens.ScreenList.current') - def test_set_default_header_footer(self, mock_geometry): - """ - Test the set_default_header_footer function sets the header and footer back to default - (reletive to the screen) - """ - # GIVEN: A screen geometry object and a Theme header with a strange area - mock_geometry.display_geometry = MagicMock() - theme = Theme() - theme.font_footer_x = 200 - theme.font_main_x = 687 - # WHEN: set_default_header is called - theme.set_default_header_footer() +@patch('openlp.core.display.screens.ScreenList.current') +def test_set_default_header(mock_geometry): + """ + Test the set_default_header function sets the header back to default + (reletive to the screen) + """ + # GIVEN: A screen geometry object and a Theme header with a strange area + mock_geometry.display_geometry = MagicMock() + mock_geometry.display_geometry.height.return_value = 600 + mock_geometry.display_geometry.width.return_value = 400 + theme = Theme() + theme.font_footer_x = 200 + theme.font_main_x = 687 + theme.font_main_y = 546 + theme.font_main_width = 345 + theme.font_main_height = 653 - # THEN: footer should be set, header should not have changed - assert theme.font_footer_x == 10, 'footer x pos should be reset to default of 10' - assert theme.font_main_x == 10, 'header x pos should be reset to default of 10' + # WHEN: set_default_header is called + theme.set_default_header() - def check_theme(self, theme): - assert '#000000' == theme.background_border_color, 'background_border_color should be "#000000"' - assert 'solid' == theme.background_type, 'background_type should be "solid"' - assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0' - assert theme.font_footer_bold is False, 'font_footer_bold should be False' - assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"' - assert 53 == len(theme.__dict__), 'The theme should have 53 attributes' + # THEN: footer should be set, header should not have changed + assert theme.font_footer_x == 200, 'footer should not have been changed' + assert theme.font_main_x == 10, 'x pos should be reset to default of 10' + assert theme.font_main_y == 0, 'y pos should be reset to 0' + assert theme.font_main_width == 380, 'width should have been reset to (screen_size_width - 20)' + assert theme.font_main_height == 540, 'height should have been reset to (screen_size_height * 9 / 10)' + + +@patch('openlp.core.display.screens.ScreenList.current') +def test_set_default_header_footer(mock_geometry): + """ + Test the set_default_header_footer function sets the header and footer back to default + (reletive to the screen) + """ + # GIVEN: A screen geometry object and a Theme header with a strange area + mock_geometry.display_geometry = MagicMock() + theme = Theme() + theme.font_footer_x = 200 + theme.font_main_x = 687 + + # WHEN: set_default_header is called + theme.set_default_header_footer() + + # THEN: footer should be set, header should not have changed + assert theme.font_footer_x == 10, 'footer x pos should be reset to default of 10' + assert theme.font_main_x == 10, 'header x pos should be reset to default of 10' + + +def check_theme(theme): + assert '#000000' == theme.background_border_color, 'background_border_color should be "#000000"' + assert 'solid' == theme.background_type, 'background_type should be "solid"' + assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0' + assert theme.font_footer_bold is False, 'font_footer_bold should be False' + assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"' + assert 53 == len(theme.__dict__), 'The theme should have 53 attributes' diff --git a/tests/functional/openlp_core/lib/test_ui.py b/tests/functional/openlp_core/lib/test_ui.py index 00b08e7cd..cbcc1b7a0 100644 --- a/tests/functional/openlp_core/lib/test_ui.py +++ b/tests/functional/openlp_core/lib/test_ui.py @@ -21,7 +21,6 @@ """ Package to test the openlp.core.lib.ui package. """ -from unittest import TestCase from unittest.mock import MagicMock, call, patch from PyQt5 import QtCore, QtGui, QtWidgets @@ -32,287 +31,297 @@ from openlp.core.lib.ui import add_welcome_page, create_action, create_button, c critical_error_message_box, find_and_set_in_combo_box, set_case_insensitive_completer -class TestUi(TestCase): +def test_add_welcome_page(): """ - Test the functions in the ui module + Test appending a welcome page to a wizard """ + # GIVEN: A wizard + wizard = QtWidgets.QWizard() - def test_add_welcome_page(self): - """ - Test appending a welcome page to a wizard - """ - # GIVEN: A wizard - wizard = QtWidgets.QWizard() + # WHEN: A welcome page has been added to the wizard + add_welcome_page(wizard, ':/wizards/wizard_firsttime.bmp') - # WHEN: A welcome page has been added to the wizard - add_welcome_page(wizard, ':/wizards/wizard_firsttime.bmp') + # THEN: The wizard should have one page with a pixmap. + assert 1 == len(wizard.pageIds()), 'The wizard should have one page.' + assert isinstance(wizard.page(0).pixmap(QtWidgets.QWizard.WatermarkPixmap), QtGui.QPixmap) - # THEN: The wizard should have one page with a pixmap. - assert 1 == len(wizard.pageIds()), 'The wizard should have one page.' - assert isinstance(wizard.page(0).pixmap(QtWidgets.QWizard.WatermarkPixmap), QtGui.QPixmap) - def test_create_button_box(self): - """ - Test creating a button box for a dialog - """ - # GIVEN: A dialog +def test_create_button_box(): + """ + Test creating a button box for a dialog + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() + + # WHEN: We create the button box with five buttons + btnbox = create_button_box(dialog, 'my_btns', ['ok', 'save', 'cancel', 'close', 'defaults']) + + # THEN: We should get a QDialogButtonBox with five buttons + assert isinstance(btnbox, QtWidgets.QDialogButtonBox) + assert 5 == len(btnbox.buttons()) + + # WHEN: We create the button box with a custom button + btnbox = create_button_box(dialog, 'my_btns', None, [QtWidgets.QPushButton('Custom')]) + # THEN: We should get a QDialogButtonBox with one button + assert isinstance(btnbox, QtWidgets.QDialogButtonBox) + assert 1 == len(btnbox.buttons()) + + # WHEN: We create the button box with a custom button and a custom role + btnbox = create_button_box(dialog, 'my_btns', None, + [(QtWidgets.QPushButton('Help'), QtWidgets.QDialogButtonBox.HelpRole)]) + # THEN: We should get a QDialogButtonBox with one button with a certain role + assert isinstance(btnbox, QtWidgets.QDialogButtonBox) + assert 1 == len(btnbox.buttons()) + assert QtWidgets.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]) + + +@patch('openlp.core.lib.ui.Registry') +def test_critical_error_message_box(MockRegistry): + """ + Test the critical_error_message_box() function + """ + # GIVEN: A mocked Registry + # WHEN: critical_error_message_box() is called + critical_error_message_box('Error', 'This is an error') + + # THEN: The error_message() method on the main window should be called + MockRegistry.return_value.get.return_value.error_message.assert_called_once_with('Error', 'This is an error') + + +@patch('openlp.core.lib.ui.QtWidgets.QMessageBox.critical') +def test_critical_error_question(mocked_critical): + """ + Test the critical_error_message_box() function + """ + # GIVEN: A mocked critical() method and some other mocks + mocked_parent = MagicMock() + + # WHEN: critical_error_message_box() is called + critical_error_message_box(None, 'This is a question', mocked_parent, True) + + # THEN: The error_message() method on the main window should be called + mocked_critical.assert_called_once_with(mocked_parent, 'Error', 'This is a question', + QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | + QtWidgets.QMessageBox.No)) + + +def test_create_horizontal_adjusting_combo_box(): + """ + Test creating a horizontal adjusting combo box + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() + + # WHEN: We create the combobox + combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') + + # THEN: We should get a ComboBox + assert isinstance(combo, QtWidgets.QComboBox) + assert combo.objectName() == 'combo1' + assert QtWidgets.QComboBox.AdjustToMinimumContentsLength == combo.sizeAdjustPolicy() + + +@patch('openlp.core.lib.ui.log') +def test_create_button(mocked_log): + """ + Test creating a button + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() + + # WHEN: We create a button with some attributes + btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False, role='test', test=1) + + # THEN: We should get a button with those attributes + assert isinstance(btn, QtWidgets.QPushButton) + assert btn.objectName() == 'my_btn' + assert btn.text() == 'Hello' + assert btn.toolTip() == 'How are you?' + assert btn.isEnabled() is False + assert mocked_log.warning.call_args_list == [call('The role "test" is not defined in create_push_button().'), + call('Parameter test was not consumed in create_button().')] + + +def test_create_tool_button(): + """ + Test creating a toolbutton + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() + + # WHEN: We create a toolbutton + btn = create_button(dialog, 'my_btn', btn_class='toolbutton') + + # THEN: We should get a toolbutton + assert isinstance(btn, QtWidgets.QToolButton) + assert btn.objectName() == 'my_btn' + assert btn.isEnabled() is True + + +@patch('openlp.core.lib.ui.log') +def test_create_action(mocked_log): + """ + Test creating an action + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() + + # WHEN: We create an action with some properties + action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp', + tooltip='my tooltip', statustip='my statustip', test=1) + + # THEN: These properties should be set + assert isinstance(action, QtWidgets.QAction) + assert action.objectName() == 'my_action' + assert action.text() == 'my text' + assert isinstance(action.icon(), QtGui.QIcon) + assert action.toolTip() == 'my tooltip' + assert action.statusTip() == 'my statustip' + mocked_log.warning.assert_called_once_with('Parameter test was not consumed in create_action().') + + +def test_create_action_on_mac_osx(): + """ + Test creating an action on OS X calls the correct method + """ + # GIVEN: A dialog and a mocked out is_macosx() method to always return True + with patch('openlp.core.lib.ui.is_macosx') as mocked_is_macosx, \ + patch('openlp.core.lib.ui.QtWidgets.QAction') as MockedQAction: + mocked_is_macosx.return_value = True + mocked_action = MagicMock() + MockedQAction.return_value = mocked_action dialog = QtWidgets.QDialog() - # WHEN: We create the button box with five buttons - btnbox = create_button_box(dialog, 'my_btns', ['ok', 'save', 'cancel', 'close', 'defaults']) + # WHEN: An action is created + create_action(dialog, 'my_action') - # THEN: We should get a QDialogButtonBox with five buttons - assert isinstance(btnbox, QtWidgets.QDialogButtonBox) - assert 5 == len(btnbox.buttons()) + # THEN: setIconVisibleInMenu should be called + mocked_action.setIconVisibleInMenu.assert_called_with(False) - # WHEN: We create the button box with a custom button - btnbox = create_button_box(dialog, 'my_btns', None, [QtWidgets.QPushButton('Custom')]) - # THEN: We should get a QDialogButtonBox with one button - assert isinstance(btnbox, QtWidgets.QDialogButtonBox) - assert 1 == len(btnbox.buttons()) - # WHEN: We create the button box with a custom button and a custom role - btnbox = create_button_box(dialog, 'my_btns', None, - [(QtWidgets.QPushButton('Help'), QtWidgets.QDialogButtonBox.HelpRole)]) - # THEN: We should get a QDialogButtonBox with one button with a certain role - assert isinstance(btnbox, QtWidgets.QDialogButtonBox) - assert 1 == len(btnbox.buttons()) - assert QtWidgets.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]) - - @patch('openlp.core.lib.ui.Registry') - def test_critical_error_message_box(self, MockRegistry): - """ - Test the critical_error_message_box() function - """ - # GIVEN: A mocked Registry - # WHEN: critical_error_message_box() is called - critical_error_message_box('Error', 'This is an error') - - # THEN: The error_message() method on the main window should be called - MockRegistry.return_value.get.return_value.error_message.assert_called_once_with('Error', 'This is an error') - - @patch('openlp.core.lib.ui.QtWidgets.QMessageBox.critical') - def test_critical_error_question(self, mocked_critical): - """ - Test the critical_error_message_box() function - """ - # GIVEN: A mocked critical() method and some other mocks - mocked_parent = MagicMock() - - # WHEN: critical_error_message_box() is called - critical_error_message_box(None, 'This is a question', mocked_parent, True) - - # THEN: The error_message() method on the main window should be called - mocked_critical.assert_called_once_with(mocked_parent, 'Error', 'This is a question', - QtWidgets.QMessageBox.StandardButtons(QtWidgets.QMessageBox.Yes | - QtWidgets.QMessageBox.No)) - - def test_create_horizontal_adjusting_combo_box(self): - """ - Test creating a horizontal adjusting combo box - """ - # GIVEN: A dialog +def test_create_action_not_on_mac_osx(): + """ + Test creating an action on something other than OS X doesn't call the method + """ + # GIVEN: A dialog and a mocked out is_macosx() method to always return True + with patch('openlp.core.lib.ui.is_macosx') as mocked_is_macosx, \ + patch('openlp.core.lib.ui.QtWidgets.QAction') as MockedQAction: + mocked_is_macosx.return_value = False + mocked_action = MagicMock() + MockedQAction.return_value = mocked_action dialog = QtWidgets.QDialog() - # WHEN: We create the combobox - combo = create_horizontal_adjusting_combo_box(dialog, 'combo1') + # WHEN: An action is created + create_action(dialog, 'my_action') - # THEN: We should get a ComboBox - assert isinstance(combo, QtWidgets.QComboBox) - assert combo.objectName() == 'combo1' - assert QtWidgets.QComboBox.AdjustToMinimumContentsLength == combo.sizeAdjustPolicy() + # THEN: setIconVisibleInMenu should not be called + assert 0 == mocked_action.setIconVisibleInMenu.call_count, \ + 'setIconVisibleInMenu should not have been called' - @patch('openlp.core.lib.ui.log') - def test_create_button(self, mocked_log): - """ - Test creating a button - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() - # WHEN: We create a button with some attributes - btn = create_button(dialog, 'my_btn', text='Hello', tooltip='How are you?', enabled=False, role='test', test=1) +def test_create_checked_disabled_invisible_action(): + """ + Test that an invisible, disabled, checked action is created correctly + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() - # THEN: We should get a button with those attributes - assert isinstance(btn, QtWidgets.QPushButton) - assert btn.objectName() == 'my_btn' - assert btn.text() == 'Hello' - assert btn.toolTip() == 'How are you?' - assert btn.isEnabled() is False - assert mocked_log.warning.call_args_list == [call('The role "test" is not defined in create_push_button().'), - call('Parameter test was not consumed in create_button().')] + # WHEN: We create an action with some properties + action = create_action(dialog, 'my_action', checked=True, enabled=False, visible=False) - def test_create_tool_button(self): - """ - Test creating a toolbutton - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() + # THEN: These properties should be set + assert action.isChecked() is True, 'The action should be checked' + assert action.isEnabled() is False, 'The action should be disabled' + assert action.isVisible() is False, 'The action should be invisble' - # WHEN: We create a toolbutton - btn = create_button(dialog, 'my_btn', btn_class='toolbutton') - # THEN: We should get a toolbutton - assert isinstance(btn, QtWidgets.QToolButton) - assert btn.objectName() == 'my_btn' - assert btn.isEnabled() is True +def test_create_action_separator(): + """ + Test creating an action as separator + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() - @patch('openlp.core.lib.ui.log') - def test_create_action(self, mocked_log): - """ - Test creating an action - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() + # WHEN: We create an action as a separator + action = create_action(dialog, 'my_action', separator=True) - # WHEN: We create an action with some properties - action = create_action(dialog, 'my_action', text='my text', icon=':/wizards/wizard_firsttime.bmp', - tooltip='my tooltip', statustip='my statustip', test=1) + # THEN: The action should be a separator + assert action.isSeparator() is True, 'The action should be a separator' - # THEN: These properties should be set - assert isinstance(action, QtWidgets.QAction) - assert action.objectName() == 'my_action' - assert action.text() == 'my text' - assert isinstance(action.icon(), QtGui.QIcon) - assert action.toolTip() == 'my tooltip' - assert action.statusTip() == 'my statustip' - mocked_log.warning.assert_called_once_with('Parameter test was not consumed in create_action().') - def test_create_action_on_mac_osx(self): - """ - Test creating an action on OS X calls the correct method - """ - # GIVEN: A dialog and a mocked out is_macosx() method to always return True - with patch('openlp.core.lib.ui.is_macosx') as mocked_is_macosx, \ - patch('openlp.core.lib.ui.QtWidgets.QAction') as MockedQAction: - mocked_is_macosx.return_value = True - mocked_action = MagicMock() - MockedQAction.return_value = mocked_action - dialog = QtWidgets.QDialog() +def test_create_valign_selection_widgets(): + """ + Test creating a combo box for valign selection + """ + # GIVEN: A dialog + dialog = QtWidgets.QDialog() - # WHEN: An action is created - create_action(dialog, 'my_action') + # WHEN: We create the widgets + label, combo = create_valign_selection_widgets(dialog) - # THEN: setIconVisibleInMenu should be called - mocked_action.setIconVisibleInMenu.assert_called_with(False) + # THEN: We should get a label and a combobox. + assert translate('OpenLP.Ui', '&Vertical Align:') == label.text() + assert isinstance(combo, QtWidgets.QComboBox) + assert combo == label.buddy() + for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: + assert combo.findText(text) >= 0 - def test_create_action_not_on_mac_osx(self): - """ - Test creating an action on something other than OS X doesn't call the method - """ - # GIVEN: A dialog and a mocked out is_macosx() method to always return True - with patch('openlp.core.lib.ui.is_macosx') as mocked_is_macosx, \ - patch('openlp.core.lib.ui.QtWidgets.QAction') as MockedQAction: - mocked_is_macosx.return_value = False - mocked_action = MagicMock() - MockedQAction.return_value = mocked_action - dialog = QtWidgets.QDialog() - # WHEN: An action is created - create_action(dialog, 'my_action') +def test_find_and_set_in_combo_box(): + """ + Test finding a string in a combo box and setting it as the selected item if present + """ + # GIVEN: A ComboBox + combo = QtWidgets.QComboBox() + combo.addItems(['One', 'Two', 'Three']) + combo.setCurrentIndex(1) - # THEN: setIconVisibleInMenu should not be called - assert 0 == mocked_action.setIconVisibleInMenu.call_count, \ - 'setIconVisibleInMenu should not have been called' + # WHEN: We call the method with a non-existing value and set_missing=False + find_and_set_in_combo_box(combo, 'Four', set_missing=False) - def test_create_checked_disabled_invisible_action(self): - """ - Test that an invisible, disabled, checked action is created correctly - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() + # THEN: The index should not have changed + assert 1 == combo.currentIndex() - # WHEN: We create an action with some properties - action = create_action(dialog, 'my_action', checked=True, enabled=False, visible=False) + # WHEN: We call the method with a non-existing value + find_and_set_in_combo_box(combo, 'Four') - # THEN: These properties should be set - assert action.isChecked() is True, 'The action should be checked' - assert action.isEnabled() is False, 'The action should be disabled' - assert action.isVisible() is False, 'The action should be invisble' + # THEN: The index should have been reset + assert 0 == combo.currentIndex() - def test_create_action_separator(self): - """ - Test creating an action as separator - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() + # WHEN: We call the method with the default behavior + find_and_set_in_combo_box(combo, 'Three') - # WHEN: We create an action as a separator - action = create_action(dialog, 'my_action', separator=True) + # THEN: The index should have changed + assert 2 == combo.currentIndex() - # THEN: The action should be a separator - assert action.isSeparator() is True, 'The action should be a separator' - def test_create_valign_selection_widgets(self): - """ - Test creating a combo box for valign selection - """ - # GIVEN: A dialog - dialog = QtWidgets.QDialog() +def test_create_widget_action(): + """ + Test creating an action for a widget + """ + # GIVEN: A button + button = QtWidgets.QPushButton() - # WHEN: We create the widgets - label, combo = create_valign_selection_widgets(dialog) + # WHEN: We call the function + action = create_widget_action(button, 'some action') - # THEN: We should get a label and a combobox. - assert translate('OpenLP.Ui', '&Vertical Align:') == label.text() - assert isinstance(combo, QtWidgets.QComboBox) - assert combo == label.buddy() - for text in [UiStrings().Top, UiStrings().Middle, UiStrings().Bottom]: - assert combo.findText(text) >= 0 + # THEN: The action should be returned + assert isinstance(action, QtWidgets.QAction) + assert action.objectName() == 'some action' - def test_find_and_set_in_combo_box(self): - """ - Test finding a string in a combo box and setting it as the selected item if present - """ - # GIVEN: A ComboBox - combo = QtWidgets.QComboBox() - combo.addItems(['One', 'Two', 'Three']) - combo.setCurrentIndex(1) - # WHEN: We call the method with a non-existing value and set_missing=False - find_and_set_in_combo_box(combo, 'Four', set_missing=False) +def test_set_case_insensitive_completer(): + """ + Test setting a case insensitive completer on a widget + """ + # GIVEN: A QComboBox and a list of completion items + line_edit = QtWidgets.QLineEdit() + suggestions = ['one', 'Two', 'THRee', 'FOUR'] - # THEN: The index should not have changed - assert 1 == combo.currentIndex() + # WHEN: We call the function + set_case_insensitive_completer(suggestions, line_edit) - # WHEN: We call the method with a non-existing value - find_and_set_in_combo_box(combo, 'Four') - - # THEN: The index should have been reset - assert 0 == combo.currentIndex() - - # WHEN: We call the method with the default behavior - find_and_set_in_combo_box(combo, 'Three') - - # THEN: The index should have changed - assert 2 == combo.currentIndex() - - def test_create_widget_action(self): - """ - Test creating an action for a widget - """ - # GIVEN: A button - button = QtWidgets.QPushButton() - - # WHEN: We call the function - action = create_widget_action(button, 'some action') - - # THEN: The action should be returned - assert isinstance(action, QtWidgets.QAction) - assert action.objectName() == 'some action' - - def test_set_case_insensitive_completer(self): - """ - Test setting a case insensitive completer on a widget - """ - # GIVEN: A QComboBox and a list of completion items - line_edit = QtWidgets.QLineEdit() - suggestions = ['one', 'Two', 'THRee', 'FOUR'] - - # WHEN: We call the function - set_case_insensitive_completer(suggestions, line_edit) - - # THEN: The Combobox should have a completer which is case insensitive - completer = line_edit.completer() - assert isinstance(completer, QtWidgets.QCompleter) - assert completer.caseSensitivity() == QtCore.Qt.CaseInsensitive + # THEN: The Combobox should have a completer which is case insensitive + completer = line_edit.completer() + assert isinstance(completer, QtWidgets.QCompleter) + assert completer.caseSensitivity() == QtCore.Qt.CaseInsensitive diff --git a/tests/functional/openlp_core/widgets/test_buttons.py b/tests/functional/openlp_core/widgets/test_buttons.py index 0d12a1157..87645a0f9 100644 --- a/tests/functional/openlp_core/widgets/test_buttons.py +++ b/tests/functional/openlp_core/widgets/test_buttons.py @@ -21,156 +21,165 @@ """ This module contains tests for the openlp.core.widgets.buttons module """ -from unittest import TestCase +import pytest from unittest.mock import MagicMock, call, patch from openlp.core.widgets.buttons import ColorButton -class TestColorDialog(TestCase): +@pytest.yield_fixture() +def buttons_env(): + change_color_patcher = patch('openlp.core.widgets.buttons.ColorButton.change_color') + clicked_patcher = patch('openlp.core.widgets.buttons.ColorButton.clicked') + color_changed_patcher = patch('openlp.core.widgets.buttons.ColorButton.colorChanged') + qt_gui_patcher = patch('openlp.core.widgets.buttons.QtWidgets') + translate_patcher = patch('openlp.core.widgets.buttons.translate', **{'return_value': 'Tool Tip Text'}) + mocked_change_color = change_color_patcher.start() + mocked_clicked = clicked_patcher.start() + mocked_color_changed = color_changed_patcher.start() + mocked_qt_widgets = qt_gui_patcher.start() + translate_patcher.start() + yield mocked_clicked, mocked_color_changed, mocked_qt_widgets, mocked_change_color + change_color_patcher.stop() + clicked_patcher.stop() + color_changed_patcher.stop() + qt_gui_patcher.stop() + translate_patcher.stop() + + +@patch('openlp.core.widgets.buttons.ColorButton.setToolTip') +def test_constructor(mocked_set_tool_tip, buttons_env): """ - Test the :class:`~openlp.core.lib.colorbutton.ColorButton` class + Test that constructing a ColorButton object works correctly """ - def setUp(self): - self.change_color_patcher = patch('openlp.core.widgets.buttons.ColorButton.change_color') - self.clicked_patcher = patch('openlp.core.widgets.buttons.ColorButton.clicked') - self.color_changed_patcher = patch('openlp.core.widgets.buttons.ColorButton.colorChanged') - self.qt_gui_patcher = patch('openlp.core.widgets.buttons.QtWidgets') - self.translate_patcher = patch('openlp.core.widgets.buttons.translate', **{'return_value': 'Tool Tip Text'}) - self.addCleanup(self.change_color_patcher.stop) - self.addCleanup(self.clicked_patcher.stop) - self.addCleanup(self.color_changed_patcher.stop) - self.addCleanup(self.qt_gui_patcher.stop) - self.addCleanup(self.translate_patcher.stop) - self.mocked_change_color = self.change_color_patcher.start() - self.mocked_clicked = self.clicked_patcher.start() - self.mocked_color_changed = self.color_changed_patcher.start() - self.mocked_qt_widgets = self.qt_gui_patcher.start() - self.mocked_translate = self.translate_patcher.start() - @patch('openlp.core.widgets.buttons.ColorButton.setToolTip') - def test_constructor(self, mocked_set_tool_tip): - """ - Test that constructing a ColorButton object works correctly - """ + # GIVEN: The ColorButton class, a mocked change_color, setToolTip methods and clicked signal + # WHEN: The ColorButton object is instantiated + mocked_clicked = buttons_env[0] + mocked_change_color = buttons_env[3] + widget = ColorButton() - # GIVEN: The ColorButton class, a mocked change_color, setToolTip methods and clicked signal - # WHEN: The ColorButton object is instantiated - widget = ColorButton() + # THEN: The widget __init__ method should have the correct properties and methods called + assert widget.parent is None, 'The parent should be the same as the one that the class was instianted with' + mocked_change_color.assert_called_once_with('#ffffff') + mocked_set_tool_tip.assert_called_once_with('Tool Tip Text') + mocked_clicked.connect.assert_called_once_with(widget.on_clicked) - # THEN: The widget __init__ method should have the correct properties and methods called - assert widget.parent is None, 'The parent should be the same as the one that the class was instianted with' - self.mocked_change_color.assert_called_once_with('#ffffff') - mocked_set_tool_tip.assert_called_once_with('Tool Tip Text') - self.mocked_clicked.connect.assert_called_once_with(widget.on_clicked) - @patch('openlp.core.widgets.buttons.ColorButton.setStyleSheet') - def test_change_color(self, mocked_set_style_sheet): - """ - Test that change_color sets the new color and the stylesheet - """ - self.change_color_patcher.stop() +@patch('openlp.core.widgets.buttons.ColorButton.setStyleSheet') +def test_change_color(mocked_set_style_sheet): + """ + Test that change_color sets the new color and the stylesheet + """ + # GIVEN: An instance of the ColorButton object, and a mocked out setStyleSheet + widget = ColorButton() - # GIVEN: An instance of the ColorButton object, and a mocked out setStyleSheet - widget = ColorButton() + # WHEN: Changing the color + widget.change_color('#000000') - # WHEN: Changing the color - widget.change_color('#000000') + # THEN: The _color attribute should be set to #000000 and setStyleSheet should have been called twice + assert widget._color == '#000000', '_color should have been set to #000000' + mocked_set_style_sheet.assert_has_calls( + [call('background-color: #ffffff'), call('background-color: #000000')]) - # THEN: The _color attribute should be set to #000000 and setStyleSheet should have been called twice - assert widget._color == '#000000', '_color should have been set to #000000' - mocked_set_style_sheet.assert_has_calls( - [call('background-color: #ffffff'), call('background-color: #000000')]) - self.mocked_change_color = self.change_color_patcher.start() +def test_color(): + """ + Test that the color property method returns the set color + """ + # GIVEN: An instance of ColorButton, with a set _color attribute + widget = ColorButton() + widget._color = '#000000' - def test_color(self): - """ - Test that the color property method returns the set color - """ - # GIVEN: An instance of ColorButton, with a set _color attribute - widget = ColorButton() - widget._color = '#000000' + # WHEN: Accesing the color property + value = widget.color - # WHEN: Accesing the color property - value = widget.color + # THEN: The value set in _color should be returned + assert value == '#000000', 'The value returned should be equal to the one we set' - # THEN: The value set in _color should be returned - assert value == '#000000', 'The value returned should be equal to the one we set' - # @patch('openlp.core.widgets.buttons.ColorButton.__init__', **{'return_value': None}) - def test_color_setter(self): - """ - Test that the color property setter method sets the color - """ - # GIVEN: An instance of ColorButton, with a mocked __init__ - widget = ColorButton() +def test_color_setter(buttons_env): + """ + Test that the color property setter method sets the color + """ + # GIVEN: An instance of ColorButton, with a mocked __init__ + mocked_change_color = buttons_env[3] + widget = ColorButton() - # WHEN: Setting the color property - widget.color = '#000000' + # WHEN: Setting the color property + widget.color = '#000000' - # THEN: Then change_color should have been called with the value we set - self.mocked_change_color.assert_called_with('#000000') + # THEN: Then change_color should have been called with the value we set + mocked_change_color.assert_called_with('#000000') - def test_on_clicked_invalid_color(self): - """ - Test the on_click method when an invalid color has been supplied - """ - # GIVEN: An instance of ColorButton, and a set _color attribute - widget = ColorButton() - self.mocked_change_color.reset_mock() - self.mocked_color_changed.reset_mock() - widget._color = '#000000' +def test_on_clicked_invalid_color(buttons_env): + """ + Test the on_click method when an invalid color has been supplied + """ + # GIVEN: An instance of ColorButton, and a set _color attribute + mocked_color_changed = buttons_env[1] + mocked_qt_widgets = buttons_env[2] + mocked_change_color = buttons_env[3] + widget = ColorButton() + mocked_change_color.reset_mock() + mocked_color_changed.reset_mock() + widget._color = '#000000' - # WHEN: The on_clicked method is called, and the color is invalid - self.mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock(**{'isValid.return_value': False}) - widget.on_clicked() + # WHEN: The on_clicked method is called, and the color is invalid + mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock(**{'isValid.return_value': False}) + widget.on_clicked() - # THEN: change_color should not have been called and the colorChanged signal should not have been emitted - assert self.mocked_change_color.called is False, \ - 'change_color should not have been called with an invalid color' - assert self.mocked_color_changed.emit.called is False, \ - 'colorChange signal should not have been emitted with an invalid color' + # THEN: change_color should not have been called and the colorChanged signal should not have been emitted + assert mocked_change_color.called is False, \ + 'change_color should not have been called with an invalid color' + assert mocked_color_changed.emit.called is False, \ + 'colorChange signal should not have been emitted with an invalid color' - def test_on_clicked_same_color(self): - """ - Test the on_click method when a new color has not been chosen - """ - # GIVEN: An instance of ColorButton, and a set _color attribute - widget = ColorButton() - self.mocked_change_color.reset_mock() - self.mocked_color_changed.reset_mock() - widget._color = '#000000' +def test_on_clicked_same_color(buttons_env): + """ + Test the on_click method when a new color has not been chosen + """ + # GIVEN: An instance of ColorButton, and a set _color attribute + mocked_color_changed = buttons_env[1] + mocked_qt_widgets = buttons_env[2] + mocked_change_color = buttons_env[3] + widget = ColorButton() + mocked_change_color.reset_mock() + mocked_color_changed.reset_mock() + widget._color = '#000000' - # WHEN: The on_clicked method is called, and the color is valid, but the same as the existing color - self.mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock( - **{'isValid.return_value': True, 'name.return_value': '#000000'}) - widget.on_clicked() + # WHEN: The on_clicked method is called, and the color is valid, but the same as the existing color + mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock( + **{'isValid.return_value': True, 'name.return_value': '#000000'}) + widget.on_clicked() - # THEN: change_color should not have been called and the colorChanged signal should not have been emitted - assert self.mocked_change_color.called is False, \ - 'change_color should not have been called when the color has not changed' - assert self.mocked_color_changed.emit.called is False, \ - 'colorChange signal should not have been emitted when the color has not changed' + # THEN: change_color should not have been called and the colorChanged signal should not have been emitted + assert mocked_change_color.called is False, \ + 'change_color should not have been called when the color has not changed' + assert mocked_color_changed.emit.called is False, \ + 'colorChange signal should not have been emitted when the color has not changed' - def test_on_clicked_new_color(self): - """ - Test the on_click method when a new color has been chosen and is valid - """ - # GIVEN: An instance of ColorButton, and a set _color attribute - widget = ColorButton() - self.mocked_change_color.reset_mock() - self.mocked_color_changed.reset_mock() - widget._color = '#000000' +def test_on_clicked_new_color(buttons_env): + """ + Test the on_click method when a new color has been chosen and is valid + """ + # GIVEN: An instance of ColorButton, and a set _color attribute + mocked_color_changed = buttons_env[1] + mocked_qt_widgets = buttons_env[2] + mocked_change_color = buttons_env[3] + widget = ColorButton() + mocked_change_color.reset_mock() + mocked_color_changed.reset_mock() + widget._color = '#000000' - # WHEN: The on_clicked method is called, and the color is valid, and different to the existing color - self.mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock( - **{'isValid.return_value': True, 'name.return_value': '#ffffff'}) - widget.on_clicked() + # WHEN: The on_clicked method is called, and the color is valid, and different to the existing color + mocked_qt_widgets.QColorDialog.getColor.return_value = MagicMock( + **{'isValid.return_value': True, 'name.return_value': '#ffffff'}) + widget.on_clicked() - # THEN: change_color should have been called and the colorChanged signal should have been emitted - self.mocked_change_color.assert_called_once_with('#ffffff') - self.mocked_color_changed.emit.assert_called_once_with('#ffffff') + # THEN: change_color should have been called and the colorChanged signal should have been emitted + mocked_change_color.assert_called_once_with('#ffffff') + mocked_color_changed.emit.assert_called_once_with('#ffffff') diff --git a/tests/functional/openlp_core/widgets/test_dialogs.py b/tests/functional/openlp_core/widgets/test_dialogs.py index 5e8791c26..fb4c95b45 100755 --- a/tests/functional/openlp_core/widgets/test_dialogs.py +++ b/tests/functional/openlp_core/widgets/test_dialogs.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- - ########################################################################## # OpenLP - Open Source Lyrics Projection # # ---------------------------------------------------------------------- # @@ -20,7 +19,6 @@ ########################################################################## import os from pathlib import Path -from unittest import TestCase from unittest.mock import patch from PyQt5 import QtWidgets @@ -28,181 +26,188 @@ from PyQt5 import QtWidgets from openlp.core.widgets.dialogs import FileDialog -class TestFileDialogPatches(TestCase): +def test_file_dialog(): """ - Tests for the :mod:`openlp.core.widgets.dialogs` module + Test that the :class:`FileDialog` instantiates correctly """ + # GIVEN: The FileDialog class + # WHEN: Creating an instance + instance = FileDialog() - def test_file_dialog(self): - """ - Test that the :class:`FileDialog` instantiates correctly - """ - # GIVEN: The FileDialog class - # WHEN: Creating an instance - instance = FileDialog() + # THEN: The instance should be an instance of QFileDialog + assert isinstance(instance, QtWidgets.QFileDialog) - # THEN: The instance should be an instance of QFileDialog - assert isinstance(instance, QtWidgets.QFileDialog) - def test_get_existing_directory_user_abort(self): - """ - Test that `getExistingDirectory` handles the case when the user cancels the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method - # WHEN: Calling FileDialog.getExistingDirectory and the user cancels the dialog returns a empty string - with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=''): - result = FileDialog.getExistingDirectory() +def test_get_existing_directory_user_abort(): + """ + Test that `getExistingDirectory` handles the case when the user cancels the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method + # WHEN: Calling FileDialog.getExistingDirectory and the user cancels the dialog returns a empty string + with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=''): + result = FileDialog.getExistingDirectory() - # THEN: The result should be None - assert result is None + # THEN: The result should be None + assert result is None - def test_get_existing_directory_user_accepts(self): - """ - Test that `getExistingDirectory` handles the case when the user accepts the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method - # WHEN: Calling FileDialog.getExistingDirectory, the user chooses a file and accepts the dialog (it returns a - # string pointing to the directory) - with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=os.path.join('test', 'dir')): - result = FileDialog.getExistingDirectory() - # THEN: getExistingDirectory() should return a Path object pointing to the chosen file - assert result == Path('test', 'dir') +def test_get_existing_directory_user_accepts(): + """ + Test that `getExistingDirectory` handles the case when the user accepts the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getExistingDirectory method + # WHEN: Calling FileDialog.getExistingDirectory, the user chooses a file and accepts the dialog (it returns a + # string pointing to the directory) + with patch('PyQt5.QtWidgets.QFileDialog.getExistingDirectory', return_value=os.path.join('test', 'dir')): + result = FileDialog.getExistingDirectory() - def test_get_existing_directory_param_order(self): - """ - Test that `getExistingDirectory` passes the parameters to `QFileDialog.getExistingDirectory` in the correct - order - """ - # GIVEN: FileDialog - with patch('openlp.core.widgets.dialogs.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \ - as mocked_get_existing_directory: + # THEN: getExistingDirectory() should return a Path object pointing to the chosen file + assert result == Path('test', 'dir') - # WHEN: Calling the getExistingDirectory method with all parameters set - FileDialog.getExistingDirectory('Parent', 'Caption', Path('test', 'dir'), 'Options') - # THEN: The `QFileDialog.getExistingDirectory` should have been called with the parameters in the correct - # order - mocked_get_existing_directory.assert_called_once_with('Parent', 'Caption', os.path.join('test', 'dir'), - 'Options') +def test_get_existing_directory_param_order(): + """ + Test that `getExistingDirectory` passes the parameters to `QFileDialog.getExistingDirectory` in the correct + order + """ + # GIVEN: FileDialog + with patch('openlp.core.widgets.dialogs.QtWidgets.QFileDialog.getExistingDirectory', return_value='') \ + as mocked_get_existing_directory: - def test_get_open_file_name_user_abort(self): - """ - Test that `getOpenFileName` handles the case when the user cancels the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method - # WHEN: Calling FileDialog.getOpenFileName and the user cancels the dialog (it returns a tuple with the first - # value set as an empty string) - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')): - result = FileDialog.getOpenFileName() + # WHEN: Calling the getExistingDirectory method with all parameters set + FileDialog.getExistingDirectory('Parent', 'Caption', Path('test', 'dir'), 'Options') - # THEN: First value should be None - assert result[0] is None + # THEN: The `QFileDialog.getExistingDirectory` should have been called with the parameters in the correct + # order + mocked_get_existing_directory.assert_called_once_with('Parent', 'Caption', os.path.join('test', 'dir'), + 'Options') - def test_get_open_file_name_user_accepts(self): - """ - Test that `getOpenFileName` handles the case when the user accepts the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method - # WHEN: Calling FileDialog.getOpenFileName, the user chooses a file and accepts the dialog (it returns a - # tuple with the first value set as an string pointing to the file) - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', - return_value=(os.path.join('test', 'chosen.file'), '')): - result = FileDialog.getOpenFileName() - # THEN: getOpenFileName() should return a tuple with the first value set to a Path object pointing to the - # chosen file - assert result[0] == Path('test', 'chosen.file') +def test_get_open_file_name_user_abort(): + """ + Test that `getOpenFileName` handles the case when the user cancels the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method + # WHEN: Calling FileDialog.getOpenFileName and the user cancels the dialog (it returns a tuple with the first + # value set as an empty string) + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', '')): + result = FileDialog.getOpenFileName() - def test_get_open_file_name_selected_filter(self): - """ - Test that `getOpenFileName` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileName` - """ - # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method - # WHEN: Calling FileDialog.getOpenFileName, and `QFileDialog.getOpenFileName` returns a known `selectedFilter` - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', 'selected filter')): - result = FileDialog.getOpenFileName() + # THEN: First value should be None + assert result[0] is None - # THEN: getOpenFileName() should return a tuple with the second value set to a the selected filter - assert result[1] == 'selected filter' - def test_get_open_file_names_user_abort(self): - """ - Test that `getOpenFileNames` handles the case when the user cancels the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method - # WHEN: Calling FileDialog.getOpenFileNames and the user cancels the dialog (it returns a tuple with the first - # value set as an empty list) - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], '')): - result = FileDialog.getOpenFileNames() +def test_get_open_file_name_user_accepts(): + """ + Test that `getOpenFileName` handles the case when the user accepts the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getOpenFileName method + # WHEN: Calling FileDialog.getOpenFileName, the user chooses a file and accepts the dialog (it returns a + # tuple with the first value set as an string pointing to the file) + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', + return_value=(os.path.join('test', 'chosen.file'), '')): + result = FileDialog.getOpenFileName() - # THEN: First value should be an empty list - assert result[0] == [] + # THEN: getOpenFileName() should return a tuple with the first value set to a Path object pointing to the + # chosen file + assert result[0] == Path('test', 'chosen.file') - def test_get_open_file_names_user_accepts(self): - """ - Test that `getOpenFileNames` handles the case when the user accepts the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method - # WHEN: Calling FileDialog.getOpenFileNames, the user chooses some files and accepts the dialog (it returns a - # tuple with the first value set as a list of strings pointing to the file) - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', - return_value=([os.path.join('test', 'chosen.file1'), os.path.join('test', 'chosen.file2')], '')): - result = FileDialog.getOpenFileNames() - # THEN: getOpenFileNames() should return a tuple with the first value set to a list of Path objects pointing - # to the chosen file - assert result[0] == [Path('test', 'chosen.file1'), Path('test', 'chosen.file2')] +def test_get_open_file_name_selected_filter(): + """ + Test that `getOpenFileName` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileName` + """ + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method + # WHEN: Calling FileDialog.getOpenFileName, and `QFileDialog.getOpenFileName` returns a known `selectedFilter` + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileName', return_value=('', 'selected filter')): + result = FileDialog.getOpenFileName() - def test_get_open_file_names_selected_filter(self): - """ - Test that `getOpenFileNames` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileNames` - """ - # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method - # WHEN: Calling FileDialog.getOpenFileNames, and `QFileDialog.getOpenFileNames` returns a known - # `selectedFilter` - with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], 'selected filter')): - result = FileDialog.getOpenFileNames() + # THEN: getOpenFileName() should return a tuple with the second value set to a the selected filter + assert result[1] == 'selected filter' - # THEN: getOpenFileNames() should return a tuple with the second value set to a the selected filter - assert result[1] == 'selected filter' - def test_get_save_file_name_user_abort(self): - """ - Test that `getSaveFileName` handles the case when the user cancels the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method - # WHEN: Calling FileDialog.getSaveFileName and the user cancels the dialog (it returns a tuple with the first - # value set as an empty string) - with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', '')): - result = FileDialog.getSaveFileName() +def test_get_open_file_names_user_abort(): + """ + Test that `getOpenFileNames` handles the case when the user cancels the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method + # WHEN: Calling FileDialog.getOpenFileNames and the user cancels the dialog (it returns a tuple with the first + # value set as an empty list) + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], '')): + result = FileDialog.getOpenFileNames() - # THEN: First value should be None - assert result[0] is None + # THEN: First value should be an empty list + assert result[0] == [] - def test_get_save_file_name_user_accepts(self): - """ - Test that `getSaveFileName` handles the case when the user accepts the dialog - """ - # GIVEN: FileDialog with a mocked QDialog.getSaveFileName method - # WHEN: Calling FileDialog.getSaveFileName, the user chooses a file and accepts the dialog (it returns a - # tuple with the first value set as an string pointing to the file) - with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', - return_value=(os.path.join('test', 'chosen.file'), '')): - result = FileDialog.getSaveFileName() - # THEN: getSaveFileName() should return a tuple with the first value set to a Path object pointing to the - # chosen file - assert result[0] == Path('test', 'chosen.file') +def test_get_open_file_names_user_accepts(): + """ + Test that `getOpenFileNames` handles the case when the user accepts the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method + # WHEN: Calling FileDialog.getOpenFileNames, the user chooses some files and accepts the dialog (it returns a + # tuple with the first value set as a list of strings pointing to the file) + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', + return_value=([os.path.join('test', 'chosen.file1'), os.path.join('test', 'chosen.file2')], '')): + result = FileDialog.getOpenFileNames() - def test_get_save_file_name_selected_filter(self): - """ - Test that `getSaveFileName` does not modify the selectedFilter as returned by `QFileDialog.getSaveFileName` - """ - # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method - # WHEN: Calling FileDialog.getSaveFileName, and `QFileDialog.getSaveFileName` returns a known `selectedFilter` - with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', 'selected filter')): - result = FileDialog.getSaveFileName() + # THEN: getOpenFileNames() should return a tuple with the first value set to a list of Path objects pointing + # to the chosen file + assert result[0] == [Path('test', 'chosen.file1'), Path('test', 'chosen.file2')] - # THEN: getSaveFileName() should return a tuple with the second value set to a the selected filter - assert result[1] == 'selected filter' + +def test_get_open_file_names_selected_filter(): + """ + Test that `getOpenFileNames` does not modify the selectedFilter as returned by `QFileDialog.getOpenFileNames` + """ + # GIVEN: FileDialog with a mocked QDialog.getOpenFileNames method + # WHEN: Calling FileDialog.getOpenFileNames, and `QFileDialog.getOpenFileNames` returns a known + # `selectedFilter` + with patch('PyQt5.QtWidgets.QFileDialog.getOpenFileNames', return_value=([], 'selected filter')): + result = FileDialog.getOpenFileNames() + + # THEN: getOpenFileNames() should return a tuple with the second value set to a the selected filter + assert result[1] == 'selected filter' + + +def test_get_save_file_name_user_abort(): + """ + Test that `getSaveFileName` handles the case when the user cancels the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method + # WHEN: Calling FileDialog.getSaveFileName and the user cancels the dialog (it returns a tuple with the first + # value set as an empty string) + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', '')): + result = FileDialog.getSaveFileName() + + # THEN: First value should be None + assert result[0] is None + + +def test_get_save_file_name_user_accepts(): + """ + Test that `getSaveFileName` handles the case when the user accepts the dialog + """ + # GIVEN: FileDialog with a mocked QDialog.getSaveFileName method + # WHEN: Calling FileDialog.getSaveFileName, the user chooses a file and accepts the dialog (it returns a + # tuple with the first value set as an string pointing to the file) + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', + return_value=(os.path.join('test', 'chosen.file'), '')): + result = FileDialog.getSaveFileName() + + # THEN: getSaveFileName() should return a tuple with the first value set to a Path object pointing to the + # chosen file + assert result[0] == Path('test', 'chosen.file') + + +def test_get_save_file_name_selected_filter(): + """ + Test that `getSaveFileName` does not modify the selectedFilter as returned by `QFileDialog.getSaveFileName` + """ + # GIVEN: FileDialog with a mocked QDialog.get_save_file_name method + # WHEN: Calling FileDialog.getSaveFileName, and `QFileDialog.getSaveFileName` returns a known `selectedFilter` + with patch('PyQt5.QtWidgets.QFileDialog.getSaveFileName', return_value=('', 'selected filter')): + result = FileDialog.getSaveFileName() + + # THEN: getSaveFileName() should return a tuple with the second value set to a the selected filter + assert result[1] == 'selected filter' diff --git a/tests/functional/openlp_core/widgets/test_edits.py b/tests/functional/openlp_core/widgets/test_edits.py index d3a5432ff..b50bb0a15 100755 --- a/tests/functional/openlp_core/widgets/test_edits.py +++ b/tests/functional/openlp_core/widgets/test_edits.py @@ -22,8 +22,8 @@ This module contains tests for the openlp.core.widgets.edits module """ import os +import pytest from pathlib import Path -from unittest import TestCase from unittest.mock import MagicMock, PropertyMock, patch from openlp.core.widgets.dialogs import FileDialog @@ -31,267 +31,280 @@ from openlp.core.widgets.edits import PathEdit from openlp.core.widgets.enums import PathEditType -class TestPathEdit(TestCase): +@pytest.fixture() +def widget(): + with patch('openlp.core.widgets.edits.PathEdit._setup'): + return PathEdit() + + +def test_path_getter(widget): """ - Test the :class:`~openlp.core.widgets.edits.PathEdit` class + Test the `path` property getter. """ - def setUp(self): - with patch('openlp.core.widgets.edits.PathEdit._setup'): - self.widget = PathEdit() + # GIVEN: An instance of PathEdit with the `_path` instance variable set + widget._path = Path('getter', 'test', 'pat.h') - def test_path_getter(self): - """ - Test the `path` property getter. - """ - # GIVEN: An instance of PathEdit with the `_path` instance variable set - self.widget._path = Path('getter', 'test', 'pat.h') + # WHEN: Reading the `path` property + # THEN: The value that we set should be returned + assert widget.path == Path('getter', 'test', 'pat.h') - # WHEN: Reading the `path` property - # THEN: The value that we set should be returned - assert self.widget.path == Path('getter', 'test', 'pat.h') - def test_path_setter(self): - """ - Test the `path` property setter. - """ - # GIVEN: An instance of the PathEdit object and a mocked `line_edit` - self.widget.line_edit = MagicMock() +def test_path_setter(widget): + """ + Test the `path` property setter. + """ + # GIVEN: An instance of the PathEdit object and a mocked `line_edit` + widget.line_edit = MagicMock() - # WHEN: Writing to the `path` property - self.widget.path = Path('setter', 'test', 'pat.h') + # WHEN: Writing to the `path` property + widget.path = Path('setter', 'test', 'pat.h') - # THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip - # should have also been set. - assert self.widget._path == Path('setter', 'test', 'pat.h') - self.widget.line_edit.setToolTip.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) - self.widget.line_edit.setText.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) + # THEN: The `_path` instance variable should be set with the test data. The `line_edit` text and tooltip + # should have also been set. + assert widget._path == Path('setter', 'test', 'pat.h') + widget.line_edit.setToolTip.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) + widget.line_edit.setText.assert_called_once_with(os.path.join('setter', 'test', 'pat.h')) - def test_path_type_getter(self): - """ - Test the `path_type` property getter. - """ - # GIVEN: An instance of PathEdit - # WHEN: Reading the `path` property - # THEN: The default value should be returned - assert self.widget.path_type == PathEditType.Files - def test_path_type_setter(self): - """ - Test the `path_type` property setter. - """ - # GIVEN: An instance of the PathEdit object and a mocked `update_button_tool_tips` method. - with patch.object(self.widget, 'update_button_tool_tips') as mocked_update_button_tool_tips: +def test_path_type_getter(widget): + """ + Test the `path_type` property getter. + """ + # GIVEN: An instance of PathEdit + # WHEN: Reading the `path` property + # THEN: The default value should be returned + assert widget.path_type == PathEditType.Files - # WHEN: Writing to a different value than default to the `path_type` property - self.widget.path_type = PathEditType.Directories - # THEN: The `_path_type` instance variable should be set with the test data and not the default. The - # update_button_tool_tips should have been called. - assert self.widget._path_type == PathEditType.Directories - mocked_update_button_tool_tips.assert_called_once_with() +def test_path_type_setter(widget): + """ + Test the `path_type` property setter. + """ + # GIVEN: An instance of the PathEdit object and a mocked `update_button_tool_tips` method. + with patch.object(widget, 'update_button_tool_tips') as mocked_update_button_tool_tips: - def test_update_button_tool_tips_directories(self): - """ - Test the `update_button_tool_tips` method. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` - self.widget.browse_button = MagicMock() - self.widget.revert_button = MagicMock() - self.widget._path_type = PathEditType.Directories + # WHEN: Writing to a different value than default to the `path_type` property + widget.path_type = PathEditType.Directories - # WHEN: Calling update_button_tool_tips - self.widget.update_button_tool_tips() + # THEN: The `_path_type` instance variable should be set with the test data and not the default. The + # update_button_tool_tips should have been called. + assert widget._path_type == PathEditType.Directories + mocked_update_button_tool_tips.assert_called_once_with() - self.widget.browse_button.setToolTip.assert_called_once_with('Browse for directory.') - self.widget.revert_button.setToolTip.assert_called_once_with('Revert to default directory.') - def test_update_button_tool_tips_files(self): - """ - Test the `update_button_tool_tips` method. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Files` - self.widget.browse_button = MagicMock() - self.widget.revert_button = MagicMock() - self.widget._path_type = PathEditType.Files +def test_update_button_tool_tips_directories(widget): + """ + Test the `update_button_tool_tips` method. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` + widget.browse_button = MagicMock() + widget.revert_button = MagicMock() + widget._path_type = PathEditType.Directories - # WHEN: Calling update_button_tool_tips - self.widget.update_button_tool_tips() + # WHEN: Calling update_button_tool_tips + widget.update_button_tool_tips() - self.widget.browse_button.setToolTip.assert_called_once_with('Browse for file.') - self.widget.revert_button.setToolTip.assert_called_once_with('Revert to default file.') + widget.browse_button.setToolTip.assert_called_once_with('Browse for directory.') + widget.revert_button.setToolTip.assert_called_once_with('Revert to default directory.') - @patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None) - @patch('openlp.core.widgets.edits.FileDialog.getOpenFileName') - def test_on_browse_button_clicked_directory(self, mocked_get_open_file_name, mocked_get_existing_directory): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked - # QFileDialog.getExistingDirectory - self.widget._path_type = PathEditType.Directories - self.widget._path = Path('test', 'path') + +def test_update_button_tool_tips_files(widget): + """ + Test the `update_button_tool_tips` method. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Files` + widget.browse_button = MagicMock() + widget.revert_button = MagicMock() + widget._path_type = PathEditType.Files + + # WHEN: Calling update_button_tool_tips + widget.update_button_tool_tips() + + widget.browse_button.setToolTip.assert_called_once_with('Browse for file.') + widget.revert_button.setToolTip.assert_called_once_with('Revert to default file.') + + +@patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None) +@patch('openlp.core.widgets.edits.FileDialog.getOpenFileName') +def test_on_browse_button_clicked_directory(mocked_get_open_file_name, mocked_get_existing_directory, widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked + # QFileDialog.getExistingDirectory + widget._path_type = PathEditType.Directories + widget._path = Path('test', 'path') + + # WHEN: Calling on_browse_button_clicked + widget.on_browse_button_clicked() + + # THEN: The FileDialog.getExistingDirectory should have been called with the default caption + mocked_get_existing_directory.assert_called_once_with(widget, 'Select Directory', + Path('test', 'path'), + FileDialog.ShowDirsOnly) + assert mocked_get_open_file_name.called is False + + +def test_on_browse_button_clicked_directory_custom_caption(widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories, + and `dialog_caption` is set. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked + # QFileDialog.getExistingDirectory with `default_caption` set. + with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None) as \ + mocked_get_existing_directory, \ + patch('openlp.core.widgets.edits.FileDialog.getOpenFileName') as mocked_get_open_file_name: + widget._path_type = PathEditType.Directories + widget._path = Path('test', 'path') + widget.dialog_caption = 'Directory Caption' # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() + widget.on_browse_button_clicked() - # THEN: The FileDialog.getExistingDirectory should have been called with the default caption - mocked_get_existing_directory.assert_called_once_with(self.widget, 'Select Directory', + # THEN: The FileDialog.getExistingDirectory should have been called with the custom caption + mocked_get_existing_directory.assert_called_once_with(widget, 'Directory Caption', Path('test', 'path'), FileDialog.ShowDirsOnly) assert mocked_get_open_file_name.called is False - def test_on_browse_button_clicked_directory_custom_caption(self): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Directories, - and `dialog_caption` is set. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Directories` and a mocked - # QFileDialog.getExistingDirectory with `default_caption` set. - with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory', return_value=None) as \ - mocked_get_existing_directory, \ - patch('openlp.core.widgets.edits.FileDialog.getOpenFileName') as mocked_get_open_file_name: - self.widget._path_type = PathEditType.Directories - self.widget._path = Path('test', 'path') - self.widget.dialog_caption = 'Directory Caption' - # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() +def test_on_browse_button_clicked_file(widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName + with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ + patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ + mocked_get_open_file_name: + widget._path_type = PathEditType.Files + widget._path = Path('test', 'pat.h') - # THEN: The FileDialog.getExistingDirectory should have been called with the custom caption - mocked_get_existing_directory.assert_called_once_with(self.widget, 'Directory Caption', - Path('test', 'path'), - FileDialog.ShowDirsOnly) - assert mocked_get_open_file_name.called is False + # WHEN: Calling on_browse_button_clicked + widget.on_browse_button_clicked() - def test_on_browse_button_clicked_file(self): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName - with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ - patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ - mocked_get_open_file_name: - self.widget._path_type = PathEditType.Files - self.widget._path = Path('test', 'pat.h') + # THEN: The FileDialog.getOpenFileName should have been called with the default caption + mocked_get_open_file_name.assert_called_once_with(widget, 'Select File', Path('test', 'pat.h'), + widget.filters) + assert mocked_get_existing_directory.called is False - # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() - # THEN: The FileDialog.getOpenFileName should have been called with the default caption - mocked_get_open_file_name.assert_called_once_with(self.widget, 'Select File', Path('test', 'pat.h'), - self.widget.filters) - assert mocked_get_existing_directory.called is False +def test_on_browse_button_clicked_file_custom_caption(widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files and + `dialog_caption` is set. + """ + # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName + # with `default_caption` set. + with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ + patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ + mocked_get_open_file_name: + widget._path_type = PathEditType.Files + widget._path = Path('test', 'pat.h') + widget.dialog_caption = 'File Caption' - def test_on_browse_button_clicked_file_custom_caption(self): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the `path_type` is set to Files and - `dialog_caption` is set. - """ - # GIVEN: An instance of PathEdit with the `path_type` set to `Files` and a mocked QFileDialog.getOpenFileName - # with `default_caption` set. - with patch('openlp.core.widgets.edits.FileDialog.getExistingDirectory') as mocked_get_existing_directory, \ - patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ - mocked_get_open_file_name: - self.widget._path_type = PathEditType.Files - self.widget._path = Path('test', 'pat.h') - self.widget.dialog_caption = 'File Caption' + # WHEN: Calling on_browse_button_clicked + widget.on_browse_button_clicked() - # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() + # THEN: The FileDialog.getOpenFileName should have been called with the custom caption + mocked_get_open_file_name.assert_called_once_with(widget, 'File Caption', Path('test', 'pat.h'), + widget.filters) + assert mocked_get_existing_directory.called is False - # THEN: The FileDialog.getOpenFileName should have been called with the custom caption - mocked_get_open_file_name.assert_called_once_with(self.widget, 'File Caption', Path('test', 'pat.h'), - self.widget.filters) - assert mocked_get_existing_directory.called is False - def test_on_browse_button_clicked_user_cancels(self): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the user cancels the FileDialog (an - empty str is returned) - """ - # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the - # file path. - with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ - mocked_get_open_file_name: +def test_on_browse_button_clicked_user_cancels(widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the user cancels the FileDialog (an + empty str is returned) + """ + # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns an empty str for the + # file path. + with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', return_value=(None, '')) as \ + mocked_get_open_file_name: - # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() + # WHEN: Calling on_browse_button_clicked + widget.on_browse_button_clicked() - # THEN: normpath should not have been called - assert mocked_get_open_file_name.called is True + # THEN: normpath should not have been called + assert mocked_get_open_file_name.called is True - def test_on_browse_button_clicked_user_accepts(self): - """ - Test the `browse_button` `clicked` handler on_browse_button_clicked when the user accepts the FileDialog (a path - is returned) - """ - # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file - # path. - with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', - return_value=(Path('test', 'pat.h'), '')) as mocked_get_open_file_name, \ - patch.object(self.widget, 'on_new_path'): - # WHEN: Calling on_browse_button_clicked - self.widget.on_browse_button_clicked() +def test_on_browse_button_clicked_user_accepts(widget): + """ + Test the `browse_button` `clicked` handler on_browse_button_clicked when the user accepts the FileDialog (a path + is returned) + """ + # GIVEN: An instance of PathEdit with a mocked QFileDialog.getOpenFileName which returns a str for the file + # path. + with patch('openlp.core.widgets.edits.FileDialog.getOpenFileName', + return_value=(Path('test', 'pat.h'), '')) as mocked_get_open_file_name, \ + patch.object(widget, 'on_new_path'): - # THEN: normpath and `on_new_path` should have been called - assert mocked_get_open_file_name.called is True - assert self.widget.on_new_path.called is True + # WHEN: Calling on_browse_button_clicked + widget.on_browse_button_clicked() - def test_on_revert_button_clicked(self): - """ - Test that the default path is set as the path when the `revert_button.clicked` handler is called. - """ - # GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set. - with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: - self.widget.default_path = Path('default', 'pat.h') + # THEN: normpath and `on_new_path` should have been called + assert mocked_get_open_file_name.called is True + assert widget.on_new_path.called is True - # WHEN: Calling `on_revert_button_clicked` - self.widget.on_revert_button_clicked() - # THEN: on_new_path should have been called with the default path - mocked_on_new_path.assert_called_once_with(Path('default', 'pat.h')) +def test_on_revert_button_clicked(widget): + """ + Test that the default path is set as the path when the `revert_button.clicked` handler is called. + """ + # GIVEN: An instance of PathEdit with a mocked `on_new_path`, and the `default_path` set. + with patch.object(widget, 'on_new_path') as mocked_on_new_path: + widget.default_path = Path('default', 'pat.h') - def test_on_line_edit_editing_finished(self): - """ - Test that the new path is set as the path when the `line_edit.editingFinished` handler is called. - """ - # GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`. - with patch.object(self.widget, 'on_new_path') as mocked_on_new_path: - self.widget.line_edit = MagicMock(**{'text.return_value': 'test/pat.h'}) + # WHEN: Calling `on_revert_button_clicked` + widget.on_revert_button_clicked() - # WHEN: Calling `on_line_edit_editing_finished` - self.widget.on_line_edit_editing_finished() + # THEN: on_new_path should have been called with the default path + mocked_on_new_path.assert_called_once_with(Path('default', 'pat.h')) - # THEN: on_new_path should have been called with the path enetered in `line_edit` - mocked_on_new_path.assert_called_once_with(Path('test', 'pat.h')) - def test_on_new_path_no_change(self): - """ - Test `on_new_path` when called with a path that is the same as the existing path. - """ - # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal - with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock): - self.widget._path = Path('/old', 'test', 'pat.h') - self.widget.pathChanged = MagicMock() +def test_on_line_edit_editing_finished(widget): + """ + Test that the new path is set as the path when the `line_edit.editingFinished` handler is called. + """ + # GIVEN: An instance of PathEdit with a mocked `line_edit` and `on_new_path`. + with patch.object(widget, 'on_new_path') as mocked_on_new_path: + widget.line_edit = MagicMock(**{'text.return_value': 'test/pat.h'}) - # WHEN: Calling `on_new_path` with the same path as the existing path - self.widget.on_new_path(Path('/old', 'test', 'pat.h')) + # WHEN: Calling `on_line_edit_editing_finished` + widget.on_line_edit_editing_finished() - # THEN: The `pathChanged` signal should not be emitted - assert self.widget.pathChanged.emit.called is False + # THEN: on_new_path should have been called with the path enetered in `line_edit` + mocked_on_new_path.assert_called_once_with(Path('test', 'pat.h')) - def test_on_new_path_change(self): - """ - Test `on_new_path` when called with a path that is the different to the existing path. - """ - # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal - with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock): - self.widget._path = Path('/old', 'test', 'pat.h') - self.widget.pathChanged = MagicMock() - # WHEN: Calling `on_new_path` with the a new path - self.widget.on_new_path(Path('/new', 'test', 'pat.h')) +def test_on_new_path_no_change(widget): + """ + Test `on_new_path` when called with a path that is the same as the existing path. + """ + # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal + with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock): + widget._path = Path('/old', 'test', 'pat.h') + widget.pathChanged = MagicMock() - # THEN: The `pathChanged` signal should be emitted - self.widget.pathChanged.emit.assert_called_once_with(Path('/new', 'test', 'pat.h')) + # WHEN: Calling `on_new_path` with the same path as the existing path + widget.on_new_path(Path('/old', 'test', 'pat.h')) + + # THEN: The `pathChanged` signal should not be emitted + assert widget.pathChanged.emit.called is False + + +def test_on_new_path_change(widget): + """ + Test `on_new_path` when called with a path that is the different to the existing path. + """ + # GIVEN: An instance of PathEdit with a test path and mocked `pathChanged` signal + with patch('openlp.core.widgets.edits.PathEdit.path', new_callable=PropertyMock): + widget._path = Path('/old', 'test', 'pat.h') + widget.pathChanged = MagicMock() + + # WHEN: Calling `on_new_path` with the a new path + widget.on_new_path(Path('/new', 'test', 'pat.h')) + + # THEN: The `pathChanged` signal should be emitted + widget.pathChanged.emit.assert_called_once_with(Path('/new', 'test', 'pat.h')) diff --git a/tests/functional/openlp_core/widgets/test_views.py b/tests/functional/openlp_core/widgets/test_views.py index 16023a2e3..cab80674a 100644 --- a/tests/functional/openlp_core/widgets/test_views.py +++ b/tests/functional/openlp_core/widgets/test_views.py @@ -24,7 +24,6 @@ Package to test the openlp.core.widgets.views package. import os import pytest from types import GeneratorType -from unittest import TestCase from unittest.mock import MagicMock, call, patch from PyQt5 import QtGui @@ -54,56 +53,53 @@ def preview_widget_env(): viewport_patcher.stop() -class TestHandleMimeDataUrls(TestCase): +def test_files(): """ - Test the :func:`openlp.core.widgets.views.handle_mime_data_urls` function. + Test handle_mime_data_urls when the data points to some files. """ - def test_files(self): - """ - Test handle_mime_data_urls when the data points to some files. - """ - # GIVEN: Some mocked objects that return True when is_file is called, and some mocked mime data - mocked_path_instance_1 = MagicMock(**{'is_file.return_value': True}) - mocked_path_instance_2 = MagicMock(**{'is_file.return_value': True}) - with patch('openlp.core.widgets.views.Path', - side_effect=[mocked_path_instance_1, mocked_path_instance_2]) as mocked_path: - mocked_q_url_1 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path', '1.ext')}) - mocked_q_url_2 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path', '2.ext')}) - mocked_q_mime_data = MagicMock(**{'urls.return_value': [mocked_q_url_1, mocked_q_url_2]}) + # GIVEN: Some mocked objects that return True when is_file is called, and some mocked mime data + mocked_path_instance_1 = MagicMock(**{'is_file.return_value': True}) + mocked_path_instance_2 = MagicMock(**{'is_file.return_value': True}) + with patch('openlp.core.widgets.views.Path', + side_effect=[mocked_path_instance_1, mocked_path_instance_2]) as mocked_path: + mocked_q_url_1 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path', '1.ext')}) + mocked_q_url_2 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path', '2.ext')}) + mocked_q_mime_data = MagicMock(**{'urls.return_value': [mocked_q_url_1, mocked_q_url_2]}) - # WHEN: Calling handle_mime_data_urls with the mocked mime data - result = handle_mime_data_urls(mocked_q_mime_data) + # WHEN: Calling handle_mime_data_urls with the mocked mime data + result = handle_mime_data_urls(mocked_q_mime_data) - # THEN: Both mocked Path objects should be returned in the list - mocked_path.assert_has_calls([call(os.path.join('file', 'test', 'path', '1.ext')), - call(os.path.join('file', 'test', 'path', '2.ext'))]) - assert result == [mocked_path_instance_1, mocked_path_instance_2] + # THEN: Both mocked Path objects should be returned in the list + mocked_path.assert_has_calls([call(os.path.join('file', 'test', 'path', '1.ext')), + call(os.path.join('file', 'test', 'path', '2.ext'))]) + assert result == [mocked_path_instance_1, mocked_path_instance_2] - def test_directory(self): - """ - Test handle_mime_data_urls when the data points to some directories. - """ - # GIVEN: Some mocked objects that return True when is_dir is called, and some mocked mime data - mocked_path_instance_1 = MagicMock() - mocked_path_instance_2 = MagicMock() - mocked_path_instance_3 = MagicMock() - mocked_path_instance_4 = MagicMock(**{'is_file.return_value': False, 'is_directory.return_value': True, - 'iterdir.return_value': [mocked_path_instance_1, mocked_path_instance_2]}) - mocked_path_instance_5 = MagicMock(**{'is_file.return_value': False, 'is_directory.return_value': True, - 'iterdir.return_value': [mocked_path_instance_3]}) - with patch('openlp.core.widgets.views.Path', - side_effect=[mocked_path_instance_4, mocked_path_instance_5]) as mocked_path: - mocked_q_url_1 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path')}) - mocked_q_url_2 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path')}) - mocked_q_mime_data = MagicMock(**{'urls.return_value': [mocked_q_url_1, mocked_q_url_2]}) - # WHEN: Calling handle_mime_data_urls with the mocked mime data - result = handle_mime_data_urls(mocked_q_mime_data) +def test_directory(): + """ + Test handle_mime_data_urls when the data points to some directories. + """ + # GIVEN: Some mocked objects that return True when is_dir is called, and some mocked mime data + mocked_path_instance_1 = MagicMock() + mocked_path_instance_2 = MagicMock() + mocked_path_instance_3 = MagicMock() + mocked_path_instance_4 = MagicMock(**{'is_file.return_value': False, 'is_directory.return_value': True, + 'iterdir.return_value': [mocked_path_instance_1, mocked_path_instance_2]}) + mocked_path_instance_5 = MagicMock(**{'is_file.return_value': False, 'is_directory.return_value': True, + 'iterdir.return_value': [mocked_path_instance_3]}) + with patch('openlp.core.widgets.views.Path', + side_effect=[mocked_path_instance_4, mocked_path_instance_5]) as mocked_path: + mocked_q_url_1 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path')}) + mocked_q_url_2 = MagicMock(**{'toLocalFile.return_value': os.path.join('file', 'test', 'path')}) + mocked_q_mime_data = MagicMock(**{'urls.return_value': [mocked_q_url_1, mocked_q_url_2]}) - # THEN: The three mocked Path file objects should be returned in the list - mocked_path.assert_has_calls([call(os.path.join('file', 'test', 'path')), - call(os.path.join('file', 'test', 'path'))]) - assert result == [mocked_path_instance_1, mocked_path_instance_2, mocked_path_instance_3] + # WHEN: Calling handle_mime_data_urls with the mocked mime data + result = handle_mime_data_urls(mocked_q_mime_data) + + # THEN: The three mocked Path file objects should be returned in the list + mocked_path.assert_has_calls([call(os.path.join('file', 'test', 'path')), + call(os.path.join('file', 'test', 'path'))]) + assert result == [mocked_path_instance_1, mocked_path_instance_2, mocked_path_instance_3] def test_new_list_preview_widget(preview_widget_env, mock_settings): @@ -578,127 +574,124 @@ def test_autoscroll_normal(mocked_slide_count, mocked_item, mocked_scrollToItem, mocked_item.assert_has_calls(calls) -class TestListWidgetWithDnD(TestCase): +def test_treewidgetwithdnd_clear(): """ - Test the :class:`~openlp.core.widgets.views.ListWidgetWithDnD` class + Test the clear method when called without any arguments. """ - def test_clear(self): - """ - Test the clear method when called without any arguments. - """ - # GIVEN: An instance of ListWidgetWithDnD - widget = ListWidgetWithDnD() + # GIVEN: An instance of ListWidgetWithDnD + widget = ListWidgetWithDnD() - # WHEN: Calling clear with out any arguments - widget.clear() + # WHEN: Calling clear with out any arguments + widget.clear() - # THEN: The results text should be the standard 'no results' text. - assert widget.no_results_text == UiStrings().NoResults - - def test_clear_search_while_typing(self): - """ - Test the clear method when called with the search_while_typing argument set to True - """ - # GIVEN: An instance of ListWidgetWithDnD - widget = ListWidgetWithDnD() - - # WHEN: Calling clear with search_while_typing set to True - widget.clear(search_while_typing=True) - - # THEN: The results text should be the 'short results' text. - assert widget.no_results_text == UiStrings().ShortResults - - def test_all_items_no_list_items(self): - """ - Test allItems when there are no items in the list widget - """ - # GIVEN: An instance of ListWidgetWithDnD - widget = ListWidgetWithDnD() - with patch.object(widget, 'count', return_value=0), \ - patch.object(widget, 'item', side_effect=lambda x: [][x]): - - # WHEN: Calling allItems - result = widget.allItems() - - # THEN: An instance of a Generator object should be returned. The generator should not yeild any results - assert isinstance(result, GeneratorType) - assert list(result) == [] - - def test_all_items_list_items(self): - """ - Test allItems when the list widget contains some items. - """ - # GIVEN: An instance of ListWidgetWithDnD - widget = ListWidgetWithDnD() - with patch.object(widget, 'count', return_value=2), \ - patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]): - - # WHEN: Calling allItems - result = widget.allItems() - - # THEN: An instance of a Generator object should be returned. The generator should not yeild any results - assert isinstance(result, GeneratorType) - assert list(result) == [5, 3] - - def test_paint_event(self): - """ - Test the paintEvent method when the list is not empty - """ - # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 1 - # (i.e the list has an item) - widget = ListWidgetWithDnD() - with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \ - patch.object(widget, 'count', return_value=1), \ - patch.object(widget, 'viewport') as mocked_viewport: - mocked_event = MagicMock() - - # WHEN: Calling paintEvent - widget.paintEvent(mocked_event) - - # THEN: The overridden paintEvnet should have been called - mocked_paint_event.assert_called_once_with(mocked_event) - assert mocked_viewport.called is False - - def test_paint_event_no_items(self): - """ - Test the paintEvent method when the list is empty - """ - # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 0 - # (i.e the list is empty) - widget = ListWidgetWithDnD() - mocked_painter_instance = MagicMock() - mocked_qrect = MagicMock() - with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \ - patch.object(widget, 'count', return_value=0), \ - patch.object(widget, 'viewport'), \ - patch('openlp.core.widgets.views.QtGui.QPainter', - return_value=mocked_painter_instance) as mocked_qpainter, \ - patch('openlp.core.widgets.views.QtCore.QRect', return_value=mocked_qrect): - mocked_event = MagicMock() - - # WHEN: Calling paintEvent - widget.paintEvent(mocked_event) - - # THEN: The overridden paintEvnet should have been called, and some text should be drawn. - mocked_paint_event.assert_called_once_with(mocked_event) - mocked_qpainter.assert_called_once_with(widget.viewport()) - mocked_painter_instance.drawText.assert_called_once_with(mocked_qrect, 4100, 'No Search Results') + # THEN: The results text should be the standard 'no results' text. + assert widget.no_results_text == UiStrings().NoResults -class TestTreeWidgetWithDnD(TestCase): +def test_clear_search_while_typing(): """ - Test the :class:`~openlp.core.widgets.views.TreeWidgetWithDnD` class + Test the clear method when called with the search_while_typing argument set to True """ - def test_constructor(self): - """ - Test the constructor - """ - # GIVEN: A TreeWidgetWithDnD - # WHEN: An instance is created - widget = TreeWidgetWithDnD(name='Test') + # GIVEN: An instance of ListWidgetWithDnD + widget = ListWidgetWithDnD() - # THEN: It should be initialised correctly - assert widget.mime_data_text == 'Test' - assert widget.allow_internal_dnd is False - assert widget.indentation() == 0 - assert widget.isAnimated() is True + # WHEN: Calling clear with search_while_typing set to True + widget.clear(search_while_typing=True) + + # THEN: The results text should be the 'short results' text. + assert widget.no_results_text == UiStrings().ShortResults + + +def test_all_items_no_list_items(): + """ + Test allItems when there are no items in the list widget + """ + # GIVEN: An instance of ListWidgetWithDnD + widget = ListWidgetWithDnD() + with patch.object(widget, 'count', return_value=0), \ + patch.object(widget, 'item', side_effect=lambda x: [][x]): + + # WHEN: Calling allItems + result = widget.allItems() + + # THEN: An instance of a Generator object should be returned. The generator should not yeild any results + assert isinstance(result, GeneratorType) + assert list(result) == [] + + +def test_all_items_list_items(): + """ + Test allItems when the list widget contains some items. + """ + # GIVEN: An instance of ListWidgetWithDnD + widget = ListWidgetWithDnD() + with patch.object(widget, 'count', return_value=2), \ + patch.object(widget, 'item', side_effect=lambda x: [5, 3][x]): + + # WHEN: Calling allItems + result = widget.allItems() + + # THEN: An instance of a Generator object should be returned. The generator should not yeild any results + assert isinstance(result, GeneratorType) + assert list(result) == [5, 3] + + +def test_paint_event(): + """ + Test the paintEvent method when the list is not empty + """ + # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 1 + # (i.e the list has an item) + widget = ListWidgetWithDnD() + with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \ + patch.object(widget, 'count', return_value=1), \ + patch.object(widget, 'viewport') as mocked_viewport: + mocked_event = MagicMock() + + # WHEN: Calling paintEvent + widget.paintEvent(mocked_event) + + # THEN: The overridden paintEvnet should have been called + mocked_paint_event.assert_called_once_with(mocked_event) + assert mocked_viewport.called is False + + +def test_paint_event_no_items(): + """ + Test the paintEvent method when the list is empty + """ + # GIVEN: An instance of ListWidgetWithDnD with a mocked out count methode which returns 0 + # (i.e the list is empty) + widget = ListWidgetWithDnD() + mocked_painter_instance = MagicMock() + mocked_qrect = MagicMock() + with patch('openlp.core.widgets.views.QtWidgets.QListWidget.paintEvent') as mocked_paint_event, \ + patch.object(widget, 'count', return_value=0), \ + patch.object(widget, 'viewport'), \ + patch('openlp.core.widgets.views.QtGui.QPainter', + return_value=mocked_painter_instance) as mocked_qpainter, \ + patch('openlp.core.widgets.views.QtCore.QRect', return_value=mocked_qrect): + mocked_event = MagicMock() + + # WHEN: Calling paintEvent + widget.paintEvent(mocked_event) + + # THEN: The overridden paintEvnet should have been called, and some text should be drawn. + mocked_paint_event.assert_called_once_with(mocked_event) + mocked_qpainter.assert_called_once_with(widget.viewport()) + mocked_painter_instance.drawText.assert_called_once_with(mocked_qrect, 4100, 'No Search Results') + + +def test_treewidgetwithdnd_constructor(): + """ + Test the constructor + """ + # GIVEN: A TreeWidgetWithDnD + # WHEN: An instance is created + widget = TreeWidgetWithDnD(name='Test') + + # THEN: It should be initialised correctly + assert widget.mime_data_text == 'Test' + assert widget.allow_internal_dnd is False + assert widget.indentation() == 0 + assert widget.isAnimated() is True