openlp/tests/openlp_plugins/images/test_mediaitem.py

381 lines
16 KiB
Python

# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2021 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
This module contains tests for the lib submodule of the Images plugin.
"""
import pytest
from pathlib import Path
from unittest.mock import ANY, MagicMock, patch
from PyQt5 import QtCore, QtWidgets
from openlp.core.common.registry import Registry
from openlp.core.common.enum import ImageThemeMode
from openlp.core.lib.serviceitem import ItemCapabilities
from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
from openlp.plugins.images.lib.mediaitem import ImageMediaItem
@pytest.fixture
def media_item(mock_settings):
"""Local test setup"""
mocked_main_window = MagicMock()
Registry().register('service_list', MagicMock())
Registry().register('main_window', mocked_main_window)
Registry().register('live_controller', MagicMock())
mocked_plugin = MagicMock()
with patch('openlp.plugins.images.lib.mediaitem.MediaManagerItem._setup'), \
patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.setup_item'):
m_item = ImageMediaItem(None, mocked_plugin)
m_item.settings_section = 'images'
return m_item
def _recursively_delete_group_side_effect(*args, **kwargs):
"""
Side effect method that creates custom return values for the recursively_delete_group method
"""
if args[0] == ImageFilenames and args[1]:
# Create some fake objects that should be removed
returned_object1 = ImageFilenames()
returned_object1.id = 1
returned_object1.file_path = Path('/', 'tmp', 'test_file_1.jpg')
returned_object1.file_hash = 'abcd1'
returned_object2 = ImageFilenames()
returned_object2.id = 2
returned_object2.file_path = Path('/', 'tmp', 'test_file_2.jpg')
returned_object2.file_hash = 'abcd2'
returned_object3 = ImageFilenames()
returned_object3.id = 3
returned_object3.file_path = Path('/', 'tmp', 'test_file_3.jpg')
returned_object3.file_hash = 'abcd3'
return [returned_object1, returned_object2, returned_object3]
if args[0] == ImageGroups and args[1]:
# Change the parent_id that is matched so we don't get into an endless loop
ImageGroups.parent_id = 0
# Create a fake group that will be used in the next run
returned_object1 = ImageGroups()
returned_object1.id = 1
return [returned_object1]
return []
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
def test_save_new_images_list_empty_list(mocked_load_full_list, media_item):
"""
Test that the save_new_images_list() method handles empty lists gracefully
"""
# GIVEN: An empty image_list
image_list = []
media_item.manager = MagicMock()
# WHEN: We run save_new_images_list with the empty list
media_item.save_new_images_list(image_list)
# THEN: The save_object() method should not have been called
assert media_item.manager.save_object.call_count == 0, \
'The save_object() method should not have been called'
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
@patch('openlp.plugins.images.lib.mediaitem.sha256_file_hash')
def test_save_new_images_list_single_image_with_reload(mocked_sha256_file_hash, mocked_load_full_list, media_item):
"""
Test that the save_new_images_list() calls load_full_list() when reload_list is set to True
"""
# GIVEN: A list with 1 image and a mocked out manager
image_list = [Path('test_image.jpg')]
ImageFilenames.file_path = None
media_item.manager = MagicMock()
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: We run save_new_images_list with reload_list=True
media_item.save_new_images_list(image_list, reload_list=True)
# THEN: load_full_list() should have been called
assert mocked_load_full_list.call_count == 1, 'load_full_list() should have been called'
# CLEANUP: Remove added attribute from ImageFilenames
delattr(ImageFilenames, 'file_path')
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
@patch('openlp.plugins.images.lib.mediaitem.sha256_file_hash')
def test_save_new_images_list_single_image_without_reload(mocked_sha256_file_hash, mocked_load_full_list, media_item):
"""
Test that the save_new_images_list() doesn't call load_full_list() when reload_list is set to False
"""
# GIVEN: A list with 1 image and a mocked out manager
image_list = [Path('test_image.jpg')]
media_item.manager = MagicMock()
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: We run save_new_images_list with reload_list=False
media_item.save_new_images_list(image_list, reload_list=False)
# THEN: load_full_list() should not have been called
assert mocked_load_full_list.call_count == 0, 'load_full_list() should not have been called'
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
@patch('openlp.plugins.images.lib.mediaitem.sha256_file_hash')
def test_save_new_images_list_multiple_images(mocked_sha256_file_hash, mocked_load_full_list, media_item):
"""
Test that the save_new_images_list() saves all images in the list
"""
# GIVEN: A list with 3 images
image_list = [Path('test_image_1.jpg'), Path('test_image_2.jpg'), Path('test_image_3.jpg')]
media_item.manager = MagicMock()
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: We run save_new_images_list with the list of 3 images
media_item.save_new_images_list(image_list, reload_list=False)
# THEN: load_full_list() should not have been called
assert media_item.manager.save_object.call_count == 3, \
'load_full_list() should have been called three times'
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list')
@patch('openlp.plugins.images.lib.mediaitem.sha256_file_hash')
def test_save_new_images_list_other_objects_in_list(mocked_sha256_file_hash, mocked_load_full_list, media_item):
"""
Test that the save_new_images_list() ignores everything in the provided list except strings
"""
# GIVEN: A list with images and objects
image_list = [Path('test_image_1.jpg'), None, True, ImageFilenames(), Path('test_image_2.jpg')]
media_item.manager = MagicMock()
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: We run save_new_images_list with the list of images and objects
media_item.save_new_images_list(image_list, reload_list=False)
# THEN: load_full_list() should not have been called
assert media_item.manager.save_object.call_count == 2, 'load_full_list() should have been called only once'
def test_on_reset_click(media_item):
"""
Test that on_reset_click() actually resets the background
"""
# GIVEN: A mocked version of reset_action
media_item.reset_action = MagicMock()
media_item.reset_action_context = MagicMock()
# WHEN: on_reset_click is called
media_item.on_reset_click()
# THEN: the reset_action should be set invisible, and the image should be reset
media_item.reset_action.setVisible.assert_called_with(False)
media_item.reset_action_context.setVisible.assert_called_with(False)
media_item.live_controller.reload_theme.assert_called_with()
def test_on_display_changed(media_item):
"""
Test that on_display_changed() hides the reset background button
"""
# GIVEN: A mocked version of reset_action
media_item.reset_action = MagicMock()
media_item.reset_action_context = MagicMock()
# WHEN: on_display_changed is called
media_item.on_display_changed()
# THEN: the reset_action should be set invisible
media_item.reset_action.setVisible.assert_called_with(False)
media_item.reset_action_context.setVisible.assert_called_with(False)
def test_generate_slide_data_default_theme(media_item):
"""
Test that the generated service item provides the corect theme
"""
# GIVEN: A mocked service item and settings
mocked_service_item = MagicMock()
media_item.list_view = MagicMock()
Registry().get('settings').value.side_effect = [ImageThemeMode.Black]
# WHEN: generate_slide_data is called
media_item.generate_slide_data(mocked_service_item)
# THEN: The service item should force the theme, and use the default theme
mocked_service_item.add_capability.assert_any_call(ItemCapabilities.ProvidesOwnTheme)
assert mocked_service_item.theme == -1
def test_generate_slide_data_custom_theme(media_item):
"""
Test that the generated service item provides the corect theme
"""
# GIVEN: A mocked service item and settings
mocked_service_item = MagicMock()
media_item.list_view = MagicMock()
Registry().get('settings').value.side_effect = [ImageThemeMode.CustomTheme, 'theme_name']
# WHEN: generate_slide_data is called
media_item.generate_slide_data(mocked_service_item)
# THEN: The service item should force the theme, and use the theme in the settings
mocked_service_item.add_capability.assert_any_call(ItemCapabilities.ProvidesOwnTheme)
assert mocked_service_item.theme == 'theme_name'
@patch('openlp.plugins.images.lib.mediaitem.check_item_selected')
@patch('openlp.plugins.images.lib.mediaitem.isinstance')
@patch('openlp.plugins.images.lib.mediaitem.Path.exists')
def test_on_replace_click(mocked_exists, mocked_isinstance, mocked_check_item_selected, media_item):
"""
Test that on_replace_click() actually sets the background
"""
# GIVEN: A mocked version of reset_action, and a (faked) existing selected image file
media_item.reset_action = MagicMock()
media_item.reset_action_context = MagicMock()
media_item.list_view = MagicMock()
mocked_check_item_selected.return_value = True
mocked_isinstance.return_value = True
mocked_exists.return_value = True
# WHEN: on_replace_click is called
media_item.on_replace_click()
# THEN: the reset_action should be set visible, and the image should be set
media_item.reset_action.setVisible.assert_called_with(True)
media_item.reset_action_context.setVisible.assert_called_with(True)
media_item.live_controller.set_background_image.assert_called_with(ANY)
@patch('openlp.plugins.images.lib.mediaitem.delete_file')
@patch('openlp.core.lib.serviceitem.sha256_file_hash')
def test_recursively_delete_group(mocked_sha256_file_hash, mocked_delete_file, media_item):
"""
Test that recursively_delete_group() works
"""
# GIVEN: An ImageGroups object and mocked functions
ImageFilenames.group_id = 1
ImageGroups.parent_id = 1
media_item.manager = MagicMock()
media_item.manager.get_all_objects.side_effect = _recursively_delete_group_side_effect
media_item.service_path = Path()
test_group = ImageGroups()
test_group.id = 1
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: recursively_delete_group() is called
media_item.recursively_delete_group(test_group)
# THEN: delete_file() should have been called 12 times and manager.delete_object() 7 times.
assert mocked_delete_file.call_count == 12, 'delete_file() should have been called 12 times'
assert media_item.manager.delete_object.call_count == 7, \
'manager.delete_object() should be called exactly 7 times'
# CLEANUP: Remove added attribute from Image Filenames and ImageGroups
delattr(ImageFilenames, 'group_id')
delattr(ImageGroups, 'parent_id')
@patch('openlp.plugins.images.lib.mediaitem.delete_file')
@patch('openlp.plugins.images.lib.mediaitem.check_item_selected')
@patch('openlp.core.lib.serviceitem.sha256_file_hash')
def test_on_delete_click(mocked_sha256_file_hash, mocked_check_item_selected, mocked_delete_file, media_item):
"""
Test that on_delete_click() works
"""
# GIVEN: An ImageGroups object and mocked functions
mocked_check_item_selected.return_value = True
test_image = ImageFilenames()
test_image.id = 1
test_image.group_id = 1
test_image.file_path = Path('imagefile.png')
test_image.file_hash = 'abcd'
media_item.manager = MagicMock()
media_item.service_path = Path()
media_item.list_view = MagicMock()
mocked_row_item = MagicMock()
mocked_row_item.data.return_value = test_image
mocked_row_item.text.return_value = ''
media_item.list_view.selectedItems.return_value = [mocked_row_item]
mocked_sha256_file_hash.return_value = 'abcd'
# WHEN: Calling on_delete_click
media_item.on_delete_click()
# THEN: delete_file should have been called twice
assert mocked_delete_file.call_count == 2, 'delete_file() should have been called twice'
def test_create_item_from_id(media_item):
"""
Test that the create_item_from_id() method returns a valid QTreeWidgetItem with a pre-created ImageFilenames
"""
# GIVEN: An ImageFilenames that already exists in the database
image_file = ImageFilenames()
image_file.id = 1
image_file.file_path = Path('/', 'tmp', 'test_file_1.jpg')
media_item.manager = MagicMock()
media_item.manager.get_object_filtered.return_value = image_file
ImageFilenames.file_path = None
# WHEN: create_item_from_id() is called
item = media_item.create_item_from_id('1')
# THEN: A QTreeWidgetItem should be created with the above model object as it's data
assert isinstance(item, QtWidgets.QTreeWidgetItem)
assert 'test_file_1.jpg' == item.text(0)
item_data = item.data(0, QtCore.Qt.UserRole)
assert isinstance(item_data, ImageFilenames)
assert 1 == item_data.id
assert Path('/', 'tmp', 'test_file_1.jpg') == item_data.file_path
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_list')
def test_validate_and_load(mocked_load_list, media_item):
"""
Test that the validate_and_load_test() method when called without a group
"""
# GIVEN: A list of files
file_list = [Path('path1', 'image1.jpg'), Path('path2', 'image2.jpg')]
# WHEN: Calling validate_and_load with the list of files
media_item.validate_and_load(file_list)
# THEN: load_list should have been called with the file list and None,
# the directory should have been saved to the settings
mocked_load_list.assert_called_once_with(file_list, None)
Registry().get('settings').setValue.assert_called_once_with(ANY, Path('path1'))
@patch('openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_list')
def test_validate_and_load_group(mocked_load_list, media_item):
"""
Test that the validate_and_load_test() method when called with a group
"""
# GIVEN: A list of files
file_list = [Path('path1', 'image1.jpg'), Path('path2', 'image2.jpg')]
# WHEN: Calling validate_and_load with the list of files and a group
media_item.validate_and_load(file_list, 'group')
# THEN: load_list should have been called with the file list and the group name,
# the directory should have been saved to the settings
mocked_load_list.assert_called_once_with(file_list, 'group')
Registry().get('settings').setValue.assert_called_once_with(ANY, Path('path1'))