From f8d747e2362db0c2be8212876c403065e355a364 Mon Sep 17 00:00:00 2001 From: Tim Bentley Date: Sat, 20 Jun 2020 06:06:31 +0000 Subject: [PATCH] Fix Image Backgounds and remove Image Manager Resolves ##480 --- openlp/core/api/versions/v1/controller.py | 2 - openlp/core/api/versions/v2/controller.py | 2 - openlp/core/common/mixins.py | 10 - openlp/core/display/html/display.js | 4 +- openlp/core/display/window.py | 4 +- openlp/core/lib/imagemanager.py | 353 ------------------ openlp/core/lib/pluginmanager.py | 1 - openlp/core/lib/serviceitem.py | 16 +- openlp/core/ui/mainwindow.py | 1 - openlp/plugins/images/lib/mediaitem.py | 3 +- openlp/plugins/songs/forms/songimportform.py | 8 +- .../openlp_core/lib/test_image_manager.py | 298 --------------- .../openlp_core/lib/test_pluginmanager.py | 4 - .../openlp_core/lib/test_serviceitem.py | 7 +- .../openlp_core/ui/test_slidecontroller.py | 33 +- .../openlp_core/widgets/test_views.py | 11 +- .../openlp_core/ui/test_settings_form.py | 60 --- 17 files changed, 26 insertions(+), 791 deletions(-) delete mode 100644 openlp/core/lib/imagemanager.py delete mode 100644 tests/functional/openlp_core/lib/test_image_manager.py diff --git a/openlp/core/api/versions/v1/controller.py b/openlp/core/api/versions/v1/controller.py index e6a58e232..b9d98e8b5 100644 --- a/openlp/core/api/versions/v1/controller.py +++ b/openlp/core/api/versions/v1/controller.py @@ -60,7 +60,6 @@ def controller_text_api(): full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path if not full_thumbnail_path.exists(): create_thumb(Path(current_item.get_frame_path(index)), full_thumbnail_path, False) - # Registry().get('image_manager').add_image(str(full_thumbnail_path), frame['title'], None, 88, 88) item['img'] = urllib.request.pathname2url(os.path.sep + str(thumbnail_path)) item['text'] = str(frame['title']) item['html'] = str(frame['title']) @@ -76,7 +75,6 @@ def controller_text_api(): data_path = str(AppLocation.get_data_path()) if frame['image'][0:len(data_path)] == data_path: item['img'] = urllib.request.pathname2url(frame['image'][len(data_path):]) - # Registry().get('image_manager').add_image(frame['image'], frame['title'], None, 88, 88) item['text'] = str(frame['title']) item['html'] = str(frame['title']) data.append(item) diff --git a/openlp/core/api/versions/v2/controller.py b/openlp/core/api/versions/v2/controller.py index dbcb442f4..3d4ff845f 100644 --- a/openlp/core/api/versions/v2/controller.py +++ b/openlp/core/api/versions/v2/controller.py @@ -60,7 +60,6 @@ def controller_text_api(): full_thumbnail_path = AppLocation.get_data_path() / thumbnail_path if not full_thumbnail_path.exists(): create_thumb(Path(current_item.get_frame_path(index)), full_thumbnail_path, False) - # Registry().get('image_manager').add_image(str(full_thumbnail_path), frame['title'], None, 88, 88) item['img'] = urllib.request.pathname2url(os.path.sep + str(thumbnail_path)) item['text'] = str(frame['title']) item['html'] = str(frame['title']) @@ -76,7 +75,6 @@ def controller_text_api(): data_path = str(AppLocation.get_data_path()) if frame['image'][0:len(data_path)] == data_path: item['img'] = urllib.request.pathname2url(frame['image'][len(data_path):]) - # Registry().get('image_manager').add_image(frame['image'], frame['title'], None, 88, 88) item['text'] = str(frame['title']) item['html'] = str(frame['title']) data.append(item) diff --git a/openlp/core/common/mixins.py b/openlp/core/common/mixins.py index feed2b481..02e58d8e1 100644 --- a/openlp/core/common/mixins.py +++ b/openlp/core/common/mixins.py @@ -114,7 +114,6 @@ class RegistryProperties(object): """ _application = None _plugin_manager = None - _image_manager = None _media_controller = None _service_manager = None _preview_controller = None @@ -149,15 +148,6 @@ class RegistryProperties(object): self._plugin_manager = Registry().get('plugin_manager') return self._plugin_manager - @property - def image_manager(self): - """ - Adds the image manager to the class dynamically - """ - if not hasattr(self, '_image_manager') or not self._image_manager: - self._image_manager = Registry().get('image_manager') - return self._image_manager - @property def media_controller(self): """ diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js index 81f421ac1..5ce47e6a4 100644 --- a/openlp/core/display/html/display.js +++ b/openlp/core/display/html/display.js @@ -756,13 +756,13 @@ var Display = { * Set image slides * @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}] */ - setImageSlides: function (slides) { + setImageSlides: function (slides, background) { Display._clearSlidesList(); var parentSection = document.createElement("section"); slides.forEach(function (slide, index) { var section = document.createElement("section"); section.setAttribute("id", index); - section.setAttribute("data-background", "#000"); + section.setAttribute("data-background", background); section.setAttribute("style", "height: 100%; width: 100%;"); var img = document.createElement('img'); img.src = slide.path; diff --git a/openlp/core/display/window.py b/openlp/core/display/window.py index acc00a9f6..e4216a5c1 100644 --- a/openlp/core/display/window.py +++ b/openlp/core/display/window.py @@ -326,7 +326,9 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin): else: image['thumbnail'] = image['path'] json_images = json.dumps(imagesr) - self.run_javascript('Display.setImageSlides({images});'.format(images=json_images)) + background = self.settings.value('images/background color') + self.run_javascript('Display.setImageSlides({images}, "{background}");'.format(images=json_images, + background=background)) def load_video(self, video): """ diff --git a/openlp/core/lib/imagemanager.py b/openlp/core/lib/imagemanager.py deleted file mode 100644 index 25b9da193..000000000 --- a/openlp/core/lib/imagemanager.py +++ /dev/null @@ -1,353 +0,0 @@ -# -*- coding: utf-8 -*- - -########################################################################## -# OpenLP - Open Source Lyrics Projection # -# ---------------------------------------------------------------------- # -# Copyright (c) 2008-2020 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 . # -########################################################################## -""" -Provides the store and management for Images automatically caching them and resizing them when needed. Only one copy of -each image is needed in the system. A Thread is used to convert the image to a byte array so the user does not need to -wait for the conversion to happen. -""" -import logging -import os -import queue -import time - -from PyQt5 import QtCore - -from openlp.core.common.registry import Registry -from openlp.core.display.screens import ScreenList -from openlp.core.lib import image_to_byte, resize_image -from openlp.core.threading import ThreadWorker, run_thread - - -log = logging.getLogger(__name__) - - -class ImageWorker(ThreadWorker): - """ - A thread worker class to speed up the display of images. This is threaded so it loads the frames and generates - byte stream in background. - """ - def __init__(self, manager): - """ - Constructor for the thread class. - - ``manager`` - The image manager. - """ - super().__init__() - self.image_manager = manager - - def start(self): - """ - Start the worker - """ - self.image_manager.process() - self.quit.emit() - - def stop(self): - """ - Stop the worker - """ - self.image_manager.stop_manager = True - - -class Priority(object): - """ - Enumeration class for different priorities. - - ``Lowest`` - Only the image's byte stream has to be generated. But neither the ``QImage`` nor the byte stream has been - requested yet. - - ``Low`` - Only the image's byte stream has to be generated. Because the image's ``QImage`` has been requested previously - it is reasonable to assume that the byte stream will be needed before the byte stream of other images whose - ``QImage`` were not generated due to a request. - - ``Normal`` - The image's byte stream as well as the image has to be generated. Neither the ``QImage`` nor the byte stream has - been requested yet. - - ``High`` - The image's byte stream as well as the image has to be generated. The ``QImage`` for this image has been - requested. **Note**, this priority is only set when the ``QImage`` has not been generated yet. - - ``Urgent`` - The image's byte stream as well as the image has to be generated. The byte stream for this image has been - requested. **Note**, this priority is only set when the byte stream has not been generated yet. - """ - Lowest = 4 - Low = 3 - Normal = 2 - High = 1 - Urgent = 0 - - -class Image(object): - """ - This class represents an image. To mark an image as *dirty* call the :class:`ImageManager`'s ``_reset_image`` method - with the Image instance as argument. - """ - secondary_priority = 0 - - def __init__(self, path, source, background, width=-1, height=-1): - """ - Create an image for the :class:`ImageManager`'s cache. - - :param path: The image's file path. This should be an existing file path. - :param source: The source describes the image's origin. Possible values are described in the - :class:`~openlp.core.lib.ImageSource` class. - :param background: A ``QtGui.QColor`` object specifying the colour to be used to fill the gabs if the image's - ratio does not match with the display ratio. - :param width: The width of the image, defaults to -1 meaning that the screen width will be used. - :param height: The height of the image, defaults to -1 meaning that the screen height will be used. - """ - if not os.path.exists(path): - raise FileNotFoundError('{path} not found'.format(path=path)) - self.path = path - self.image = None - self.image_bytes = None - self.priority = Priority.Normal - self.source = source - self.background = background - self.timestamp = 0 - self.width = width - self.height = height - self.timestamp = os.stat(path).st_mtime - self.secondary_priority = Image.secondary_priority - Image.secondary_priority += 1 - - -class PriorityQueue(queue.PriorityQueue): - """ - Customised ``queue.PriorityQueue``. - - Each item in the queue must be a tuple with three values. The first value is the :class:`Image`'s ``priority`` - attribute, the second value the :class:`Image`'s ``secondary_priority`` attribute. The last value the :class:`Image` - instance itself:: - - (image.priority, image.secondary_priority, image) - - Doing this, the :class:`Queue.PriorityQueue` will sort the images according to their priorities, but also according - to there number. However, the number only has an impact on the result if there are more images with the same - priority. In such case the image which has been added earlier is privileged. - """ - def modify_priority(self, image, new_priority): - """ - Modifies the priority of the given ``image``. - - :param image: The image to remove. This should be an :class:`Image` instance. - :param new_priority: The image's new priority. See the :class:`Priority` class for priorities. - """ - self.remove(image) - image.priority = new_priority - self.put((image.priority, image.secondary_priority, image)) - - def remove(self, image): - """ - Removes the given ``image`` from the queue. - - :param image: The image to remove. This should be an ``Image`` instance. - """ - if (image.priority, image.secondary_priority, image) in self.queue: - self.queue.remove((image.priority, image.secondary_priority, image)) - - -class ImageManager(QtCore.QObject): - """ - Image Manager handles the conversion and sizing of images. - """ - log.info('Image Manager loaded') - - def __init__(self): - """ - Constructor for the image manager. - """ - super(ImageManager, self).__init__() - Registry().register('image_manager', self) - current_screen = ScreenList().current - self.width = current_screen.display_geometry.width() - self.height = current_screen.display_geometry.height() - self._cache = {} - self._conversion_queue = PriorityQueue() - self.stop_manager = False - Registry().register_function('images_regenerate', self.process_updates) - - def update_display(self): - """ - Screen has changed size so rebuild the cache to new size. - """ - log.debug('update_display') - current_screen = ScreenList().current - self.width = current_screen.display_geometry.width() - self.height = current_screen.display_geometry.height() - # Mark the images as dirty for a rebuild by setting the image and byte stream to None. - for image in list(self._cache.values()): - self._reset_image(image) - - def update_images_border(self, source, background): - """ - Border has changed so update all the images affected. - """ - log.debug('update_images_border') - # Mark the images as dirty for a rebuild by setting the image and byte stream to None. - for image in list(self._cache.values()): - if image.source == source: - image.background = background - self._reset_image(image) - - def update_image_border(self, path, source, background, width=-1, height=-1): - """ - Border has changed so update the image affected. - """ - log.debug('update_image_border') - # Mark the image as dirty for a rebuild by setting the image and byte stream to None. - image = self._cache[(path, source, width, height)] - if image.source == source: - image.background = background - self._reset_image(image) - - def _reset_image(self, image): - """ - Mark the given :class:`Image` instance as dirty by setting its ``image`` and ``image_bytes`` attributes to None. - """ - image.image = None - image.image_bytes = None - self._conversion_queue.modify_priority(image, Priority.Normal) - - def process_updates(self): - """ - Flush the queue to updated any data to update - """ - try: - worker = ImageWorker(self) - run_thread(worker, 'image_manager') - except KeyError: - # run_thread() will throw a KeyError if this thread already exists, so ignore it so that we don't - # try to start another thread when one is already running - pass - - def get_image(self, path, source, width=-1, height=-1): - """ - Return the ``QImage`` from the cache. If not present wait for the background thread to process it. - - :param: path: The image path - :param: source: The source of the image - :param: background: The image background colour - :param: width: The processed image width - :param: height: The processed image height - """ - log.debug('get_image {path} {source} {width} {height}'.format(path=path, source=source, - width=width, height=height)) - image = self._cache[(path, source, width, height)] - if image.image is None: - self._conversion_queue.modify_priority(image, Priority.High) - # make sure we are running and if not give it a kick - self.process_updates() - while image.image is None: - log.debug('getImage - waiting') - time.sleep(0.1) - elif image.image_bytes is None: - # Set the priority to Low, because the image was requested but the byte stream was not generated yet. - # However, we only need to do this, when the image was generated before it was requested (otherwise this is - # already taken care of). - self._conversion_queue.modify_priority(image, Priority.Low) - return image.image - - def get_image_bytes(self, path, source, width=-1, height=-1): - """ - Returns the byte string for an image. If not present wait for the background thread to process it. - - :param: path: The image path - :param: source: The source of the image - :param: background: The image background colour - :param: width: The processed image width - :param: height: The processed image height - """ - log.debug('get_image_bytes {path} {source} {width} {height}'.format(path=path, source=source, - width=width, height=height)) - image = self._cache[(path, source, width, height)] - if image.image_bytes is None: - self._conversion_queue.modify_priority(image, Priority.Urgent) - # make sure we are running and if not give it a kick - self.process_updates() - while image.image_bytes is None: - log.debug('getImageBytes - waiting') - time.sleep(0.1) - return image.image_bytes - - def add_image(self, path, source, background, width=-1, height=-1): - """ - Add image to cache if it is not already there. - - :param: path: The image path - :param: source: The source of the image - :param: background: The image background colour - :param: width: The processed image width - :param: height: The processed image height - """ - log.debug('add_image {path} {source} {width} {height}'.format(path=path, source=source, - width=width, height=height)) - if not (path, source, width, height) in self._cache: - image = Image(path, source, background, width, height) - self._cache[(path, source, width, height)] = image - self._conversion_queue.put((image.priority, image.secondary_priority, image)) - # Check if the there are any images with the same path and check if the timestamp has changed. - for image in list(self._cache.values()): - if os.path.exists(path): - if image.path == path and image.timestamp != os.stat(path).st_mtime: - image.timestamp = os.stat(path).st_mtime - self._reset_image(image) - self.process_updates() - - def process(self): - """ - Controls the processing called from a ``QtCore.QThread``. - """ - log.debug('process - started') - while not self._conversion_queue.empty() and not self.stop_manager: - self._process_cache() - log.debug('_process - ended') - - def _process_cache(self): - """ - Actually does the work. - """ - log.debug('_processCache') - image = self._conversion_queue.get()[2] - # Generate the QImage for the image. - if image.image is None: - # Let's see if the image was requested with specific dimensions - width = self.width if image.width == -1 else image.width - height = self.height if image.height == -1 else image.height - image.image = resize_image(image.path, width, height, image.background, - Registry().get('settings').value('advanced/ignore aspect ratio')) - # Set the priority to Lowest and stop here as we need to process more important images first. - if image.priority == Priority.Normal: - self._conversion_queue.modify_priority(image, Priority.Lowest) - return - # For image with high priority we set the priority to Low, as the byte stream might be needed earlier the - # byte stream of image with Normal priority. We stop here as we need to process more important images first. - elif image.priority == Priority.High: - self._conversion_queue.modify_priority(image, Priority.Low) - return - # Generate the byte stream for the image. - if image.image_bytes is None: - image.image_bytes = image_to_byte(image.image) diff --git a/openlp/core/lib/pluginmanager.py b/openlp/core/lib/pluginmanager.py index d0cc97b8c..a45a8d501 100644 --- a/openlp/core/lib/pluginmanager.py +++ b/openlp/core/lib/pluginmanager.py @@ -47,7 +47,6 @@ class PluginManager(RegistryBase, LogMixin, RegistryProperties): super(PluginManager, self).__init__(parent) self.log_info('Plugin manager Initialising') self.log_debug('Base path {path}'.format(path=AppLocation.get_directory(AppLocation.PluginsDir))) - self.plugins = [] self.log_info('Plugin manager Initialised') def bootstrap_initialise(self): diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 4cdcbf826..af0a7f135 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -96,7 +96,6 @@ class ServiceItem(RegistryProperties): self.end_time = 0 self.media_length = 0 self.from_service = False - self.image_border = '#000000' self.background_audio = [] self.theme_overwritten = False self.temporary_edit = False @@ -279,17 +278,15 @@ class ServiceItem(RegistryProperties): self._print_slides.append(slide) return self._print_slides - def add_from_image(self, path, title, background=None, thumbnail=None, file_hash=None): + def add_from_image(self, path, title, thumbnail=None, file_hash=None): """ Add an image slide to the service item. :param path: The directory in which the image file is located. :param title: A title for the slide in the service item. - :param background: The background colour :param thumbnail: Optional alternative thumbnail, used for remote thumbnails. + :param file_hash: Unique Reference to file . """ - if background: - self.image_border = background self.service_item_type = ServiceItemType.Image if not file_hash: file_hash = sha256_file_hash(path) @@ -297,7 +294,6 @@ class ServiceItem(RegistryProperties): if thumbnail: slide['thumbnail'] = thumbnail self.slides.append(slide) - # self.image_manager.add_image(path, ImageSource.ImagePlugin, self.image_border) self._new_item() def add_from_text(self, text, verse_tag=None): @@ -346,8 +342,6 @@ class ServiceItem(RegistryProperties): ntpath.basename(image) self.slides.append({'title': file_name, 'image': image, 'path': path, 'display_title': display_title, 'notes': notes, 'thumbnail': image}) - # if self.is_capable(ItemCapabilities.HasThumbnails): - # self.image_manager.add_image(image, ImageSource.CommandPlugins, '#000000') self._new_item() def get_service_repr(self, lite_save): @@ -496,8 +490,6 @@ class ServiceItem(RegistryProperties): self.add_from_text(slide['raw_slide'], slide['verseTag']) self._create_slides() elif self.service_item_type == ServiceItemType.Image: - settings_section = service_item['serviceitem']['header']['name'] - background = QtGui.QColor(self.settings.value(settings_section + '/background color')) if path: self.has_original_file_path = False for text_image in service_item['serviceitem']['data']: @@ -522,7 +514,7 @@ class ServiceItem(RegistryProperties): test_thumb = AppLocation.get_section_data_path(self.name) / 'thumbnails' / new_file if test_thumb.exists(): thumbnail = test_thumb - self.add_from_image(file_path, text_image, background, thumbnail=thumbnail, file_hash=file_hash) + self.add_from_image(file_path, text_image, thumbnail=thumbnail, file_hash=file_hash) else: for text_image in service_item['serviceitem']['data']: file_hash = None @@ -540,7 +532,7 @@ class ServiceItem(RegistryProperties): test_thumb = AppLocation.get_section_data_path(self.name) / 'thumbnails' / new_file if test_thumb.exists(): thumbnail = test_thumb - self.add_from_image(file_path, text, background, thumbnail=thumbnail, file_hash=file_hash) + self.add_from_image(file_path, text, thumbnail=thumbnail, file_hash=file_hash) elif self.service_item_type == ServiceItemType.Command: if version < 3: # If this is an old servicefile with files included, we need to rename the bundled files to match diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py index b28abffe6..d5580c93c 100644 --- a/openlp/core/ui/mainwindow.py +++ b/openlp/core/ui/mainwindow.py @@ -991,7 +991,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert The screen has changed so we have to update components such as the renderer. """ self.application.set_busy_cursor() - # self.image_manager.update_display() self.renderer.resize(self.live_controller.screens.current.display_geometry.size()) self.preview_controller.screen_size_changed() self.live_controller.setup_displays() diff --git a/openlp/plugins/images/lib/mediaitem.py b/openlp/plugins/images/lib/mediaitem.py index 29d029cdd..59443100a 100644 --- a/openlp/plugins/images/lib/mediaitem.py +++ b/openlp/plugins/images/lib/mediaitem.py @@ -553,7 +553,6 @@ class ImageMediaItem(MediaManagerItem): :param context: Why is it being generated :param kwargs: Consume other unused args specified by the base implementation, but not use by this one. """ - background = QtGui.QColor(self.settings.value('images/background color')) if item: items = [item] else: @@ -611,7 +610,7 @@ class ImageMediaItem(MediaManagerItem): for image in images: name = image.file_path.name thumbnail_path = self.generate_thumbnail_path(image) - service_item.add_from_image(image.file_path, name, background, thumbnail_path) + service_item.add_from_image(image.file_path, name, thumbnail_path) return True def check_group_exists(self, new_group): diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 5947f35e1..551bfa291 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -231,13 +231,13 @@ class SongImportForm(OpenLPWizard, RegistryProperties): filters += '{text} (*)'.format(text=UiStrings().AllFiles) file_paths, filter_used = FileDialog.getOpenFileNames( self, title, - self.settings.value('presentations/last directory import'), filters) + self.settings.value('songs/last directory import'), filters) for file_path in file_paths: list_item = QtWidgets.QListWidgetItem(str(file_path)) list_item.setData(QtCore.Qt.UserRole, file_path) listbox.addItem(list_item) if file_paths: - self.settings.setValue('song/last directory import', file_paths[0].parent) + self.settings.setValue('songs/last directory import', file_paths[0].parent) def get_list_of_paths(self, list_box): """ @@ -348,7 +348,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): :rtype: None """ file_path, filter_used = FileDialog.getSaveFileName( - self, self.settings.value(self.plugin.settings_section + '/last directory import')) + self, self.settings.value('songs/last directory import')) if file_path is None: return file_path.write_text(self.error_report_text_edit.toPlainText(), encoding='utf-8') @@ -399,7 +399,7 @@ class SongImportForm(OpenLPWizard, RegistryProperties): path_edit.filters = filters + ';;' + path_edit.filters else: path_edit.filters = filters - path_edit.path = self.settings.value(self.plugin.settings_section + '/last directory import') + path_edit.path = self.settings.value('songs/last directory import') file_path_layout.addWidget(path_edit) import_layout.addLayout(file_path_layout) import_layout.addSpacerItem(self.stack_spacer) diff --git a/tests/functional/openlp_core/lib/test_image_manager.py b/tests/functional/openlp_core/lib/test_image_manager.py deleted file mode 100644 index 693380cb3..000000000 --- a/tests/functional/openlp_core/lib/test_image_manager.py +++ /dev/null @@ -1,298 +0,0 @@ -# -*- coding: utf-8 -*- - -########################################################################## -# OpenLP - Open Source Lyrics Projection # -# ---------------------------------------------------------------------- # -# Copyright (c) 2008-2020 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 . # -########################################################################## -""" -Package to test the openlp.core.ui package. -""" -import os -import time -from threading import Lock -from unittest import TestCase, skip -from unittest.mock import MagicMock, patch - -from PyQt5 import QtGui - -from openlp.core.common.registry import Registry -from openlp.core.display.screens import ScreenList -from openlp.core.lib.imagemanager import ImageManager, ImageWorker, Priority, PriorityQueue -from tests.helpers.testmixin import TestMixin -from tests.utils.constants import RESOURCE_PATH - - -TEST_PATH = str(RESOURCE_PATH) - - -@skip('Probably not going to use ImageManager') -class TestImageWorker(TestCase, TestMixin): - """ - Test all the methods in the ImageWorker class - """ - def test_init(self): - """ - Test the constructor of the ImageWorker - """ - # GIVEN: An ImageWorker class and a mocked ImageManager - mocked_image_manager = MagicMock() - - # WHEN: Creating the ImageWorker - worker = ImageWorker(mocked_image_manager) - - # THEN: The image_manager attribute should be set correctly - assert worker.image_manager is mocked_image_manager, \ - 'worker.image_manager should have been the mocked_image_manager' - - @patch('openlp.core.lib.imagemanager.ThreadWorker.quit') - def test_start(self, mocked_quit): - """ - Test that the start() method of the image worker calls the process method and then emits quit. - """ - # GIVEN: A mocked image_manager and a new image worker - mocked_image_manager = MagicMock() - worker = ImageWorker(mocked_image_manager) - - # WHEN: start() is called - worker.start() - - # THEN: process() should have been called and quit should have been emitted - mocked_image_manager.process.assert_called_once_with() - mocked_quit.emit.assert_called_once_with() - - def test_stop(self): - """ - Test that the stop method does the right thing - """ - # GIVEN: A mocked image_manager and a worker - mocked_image_manager = MagicMock() - worker = ImageWorker(mocked_image_manager) - - # WHEN: The stop() method is called - worker.stop() - - # THEN: The stop_manager attrivute should have been set to True - assert mocked_image_manager.stop_manager is True, 'mocked_image_manager.stop_manager should have been True' - - -@skip('Probably not going to use ImageManager') -class TestPriorityQueue(TestCase, TestMixin): - """ - Test the PriorityQueue class - """ - @patch('openlp.core.lib.imagemanager.PriorityQueue.remove') - @patch('openlp.core.lib.imagemanager.PriorityQueue.put') - def test_modify_priority(self, mocked_put, mocked_remove): - """ - Test the modify_priority() method of PriorityQueue - """ - # GIVEN: An instance of a PriorityQueue and a mocked image - mocked_image = MagicMock() - mocked_image.priority = Priority.Normal - mocked_image.secondary_priority = Priority.Low - queue = PriorityQueue() - - # WHEN: modify_priority is called with a mocked image and a new priority - queue.modify_priority(mocked_image, Priority.High) - - # THEN: The remove() method should have been called, image priority updated and put() called - mocked_remove.assert_called_once_with(mocked_image) - assert mocked_image.priority == Priority.High, 'The priority should have been Priority.High' - mocked_put.assert_called_once_with((Priority.High, Priority.Low, mocked_image)) - - def test_remove(self): - """ - Test the remove() method of PriorityQueue - """ - # GIVEN: A PriorityQueue instance with a mocked image and queue - mocked_image = MagicMock() - mocked_image.priority = Priority.High - mocked_image.secondary_priority = Priority.Normal - queue = PriorityQueue() - - # WHEN: An image is removed - with patch.object(queue, 'queue') as mocked_queue: - mocked_queue.__contains__.return_value = True - queue.remove(mocked_image) - - # THEN: The mocked queue.remove() method should have been called - mocked_queue.remove.assert_called_once_with((Priority.High, Priority.Normal, mocked_image)) - - -@skip('Probably not going to use ImageManager') -class TestImageManager(TestCase, TestMixin): - - def setUp(self): - """ - Create the UI - """ - Registry.create() - self.setup_application() - ScreenList.create(self.app.desktop()) - self.image_manager = ImageManager() - self.lock = Lock() - self.sleep_time = 0.1 - - def tearDown(self): - """ - Delete all the C++ objects at the end so that we don't have a segfault - """ - self.image_manager.stop_manager = True - del self.app - - @patch('openlp.core.lib.imagemanager.run_thread') - def test_basic_image_manager(self, mocked_run_thread): - """ - Test the Image Manager setup basic functionality - """ - # GIVEN: the an image add to the image manager - full_path = os.path.normpath(os.path.join(TEST_PATH, 'church.jpg')) - self.image_manager.add_image(full_path, 'church.jpg', None) - - # WHEN the image is retrieved - image = self.image_manager.get_image(full_path, 'church.jpg') - - # THEN returned record is a type of image - assert isinstance(image, QtGui.QImage), 'The returned object should be a QImage' - - # WHEN: The image bytes are requested. - byte_array = self.image_manager.get_image_bytes(full_path, 'church.jpg') - - # THEN: Type should be a str. - assert isinstance(byte_array, str), 'The returned object should be a str' - - # WHEN the image is retrieved has not been loaded - # THEN a KeyError is thrown - with self.assertRaises(KeyError) as context: - self.image_manager.get_image(TEST_PATH, 'church1.jpg') - assert context.exception != '', 'KeyError exception should have been thrown for missing image' - - @patch('openlp.core.lib.imagemanager.run_thread') - def test_different_dimension_image(self, mocked_run_thread): - """ - Test the Image Manager with dimensions - """ - # GIVEN: add an image with specific dimensions - full_path = os.path.normpath(os.path.join(TEST_PATH, 'church.jpg')) - self.image_manager.add_image(full_path, 'church.jpg', None, 80, 80) - - # WHEN: the image is retrieved - image = self.image_manager.get_image(full_path, 'church.jpg', 80, 80) - - # THEN: The return should be of type image - assert isinstance(image, QtGui.QImage), 'The returned object should be a QImage' - - # WHEN: adding the same image with different dimensions - self.image_manager.add_image(full_path, 'church.jpg', None, 100, 100) - - # THEN: the cache should contain two pictures - assert len(self.image_manager._cache) == 2, \ - 'Image manager should consider two dimensions of the same picture as different' - - # WHEN: adding the same image with first dimensions - self.image_manager.add_image(full_path, 'church.jpg', None, 80, 80) - - # THEN: the cache should still contain only two pictures - assert len(self.image_manager._cache) == 2, 'Same dimensions should not be added again' - - # WHEN: calling with correct image, but wrong dimensions - with self.assertRaises(KeyError) as context: - self.image_manager.get_image(full_path, 'church.jpg', 120, 120) - assert context.exception != '', 'KeyError exception should have been thrown for missing dimension' - - @patch('openlp.core.lib.imagemanager.resize_image') - @patch('openlp.core.lib.imagemanager.image_to_byte') - @patch('openlp.core.lib.imagemanager.run_thread') - def test_process_cache(self, mocked_run_thread, mocked_image_to_byte, mocked_resize_image): - """ - Test the process_cache method - """ - # GIVEN: Mocked functions - mocked_resize_image.side_effect = self.mocked_resize_image - mocked_image_to_byte.side_effect = self.mocked_image_to_byte - image1 = 'church.jpg' - image2 = 'church2.jpg' - image3 = 'church3.jpg' - image4 = 'church4.jpg' - - # WHEN: Add the images. Then get the lock (=queue can not be processed). - self.lock.acquire() - self.image_manager.add_image(TEST_PATH, image1, None) - self.image_manager.add_image(TEST_PATH, image2, None) - - # THEN: All images have been added to the queue, and only the first image is not be in the list anymore, but - # is being processed (see mocked methods/functions). - # Note: Priority.Normal means, that the resize_image() was not completed yet (because afterwards the # - # priority is adjusted to Priority.Lowest). - assert self.get_image_priority(image1) == Priority.Normal, "image1's priority should be 'Priority.Normal'" - assert self.get_image_priority(image2) == Priority.Normal, "image2's priority should be 'Priority.Normal'" - - # WHEN: Add more images. - self.image_manager.add_image(TEST_PATH, image3, None) - self.image_manager.add_image(TEST_PATH, image4, None) - # Allow the queue to process. - self.lock.release() - # Request some "data". - self.image_manager.get_image_bytes(TEST_PATH, image4) - self.image_manager.get_image(TEST_PATH, image3) - # Now the mocked methods/functions do not have to sleep anymore. - self.sleep_time = 0 - # Wait for the queue to finish. - while not self.image_manager._conversion_queue.empty(): - time.sleep(0.1) - # Because empty() is not reliable, wait a litte; just to make sure. - time.sleep(0.1) - # THEN: The images' priority reflect how they were processed. - assert self.image_manager._conversion_queue.qsize() == 0, "The queue should be empty." - assert self.get_image_priority(image1) == Priority.Lowest, \ - "The image should have not been requested (=Lowest)" - assert self.get_image_priority(image2) == Priority.Lowest, \ - "The image should have not been requested (=Lowest)" - assert self.get_image_priority(image3) == Priority.Low, \ - "Only the QImage should have been requested (=Low)." - assert self.get_image_priority(image4) == Priority.Urgent, \ - "The image bytes should have been requested (=Urgent)." - - def get_image_priority(self, image): - """ - This is a help method to get the priority of the given image out of the image_manager's cache. - - NOTE: This requires, that the image has been added to the image manager using the *TEST_PATH*. - - :param image: The name of the image. E. g. ``image1`` - """ - return self.image_manager._cache[(TEST_PATH, image, -1, -1)].priority - - def mocked_resize_image(self, *args): - """ - This is a mocked method, so that we can control the work flow of the image manager. - """ - self.lock.acquire() - self.lock.release() - # The sleep time is adjusted in the test case. - time.sleep(self.sleep_time) - return QtGui.QImage() - - def mocked_image_to_byte(self, *args): - """ - This is a mocked method, so that we can control the work flow of the image manager. - """ - self.lock.acquire() - self.lock.release() - # The sleep time is adjusted in the test case. - time.sleep(self.sleep_time) - return '' diff --git a/tests/functional/openlp_core/lib/test_pluginmanager.py b/tests/functional/openlp_core/lib/test_pluginmanager.py index 536e0c07d..14a4396a2 100644 --- a/tests/functional/openlp_core/lib/test_pluginmanager.py +++ b/tests/functional/openlp_core/lib/test_pluginmanager.py @@ -150,8 +150,6 @@ def test_hook_settings_tabs_with_disabled_plugin_and_mocked_form(registry, state # 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): @@ -175,8 +173,6 @@ def test_hook_settings_tabs_with_active_plugin_and_mocked_form(registry, state): # 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): diff --git a/tests/functional/openlp_core/lib/test_serviceitem.py b/tests/functional/openlp_core/lib/test_serviceitem.py index bf5083636..b71097e52 100644 --- a/tests/functional/openlp_core/lib/test_serviceitem.py +++ b/tests/functional/openlp_core/lib/test_serviceitem.py @@ -72,7 +72,6 @@ def service_item_env(state): 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 test_service_item_basic(settings): @@ -266,11 +265,10 @@ def test_add_from_command_without_display_title_and_notes(): assert service_item.get_frames()[0] == frame, 'Frames should match' -@patch('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): +def test_add_from_command_for_a_presentation_thumb(mocked_get_section_data_path): """ - Test the Service Item - adding a presentation, updating the thumb path & adding the thumb to image_manager + Test the Service Item - adding a presentation, updating the thumb path & adding the thumb """ # GIVEN: A service item, a mocked AppLocation and presentation data mocked_get_section_data_path.return_value = Path('mocked') / 'section' / 'path' @@ -294,7 +292,6 @@ def test_add_from_command_for_a_presentation_thumb(mocked_get_section_data_path, # 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_media): diff --git a/tests/functional/openlp_core/ui/test_slidecontroller.py b/tests/functional/openlp_core/ui/test_slidecontroller.py index a5938061a..0542e3f4a 100644 --- a/tests/functional/openlp_core/ui/test_slidecontroller.py +++ b/tests/functional/openlp_core/ui/test_slidecontroller.py @@ -815,21 +815,18 @@ def test_on_preview_double_click_add_to_service(mock_settings): assert 1 == slide_controller.on_preview_add_to_service.call_count, 'Should have been called once.' -@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') @patch(u'PyQt5.QtCore.QTimer.singleShot') -def test_update_preview_live(mocked_singleShot, mocked_image_manager, registry): +def test_update_preview_live(mocked_singleShot, registry): """ Test that the preview screen is updated with a screen grab for live service items """ - # GIVEN: A mocked live service item, a mocked image_manager, a mocked Registry, + # GIVEN: A mocked live service item, a mocked Registry, # and a slide controller with many mocks. # Mocked Live Item mocked_live_item = MagicMock() mocked_live_item.get_rendered_frame.return_value = '' mocked_live_item.is_capable = MagicMock() mocked_live_item.is_capable.side_effect = [True, True] - # Mock image_manager - mocked_image_manager.get_image.return_value = QtGui.QImage() mocked_main_window = MagicMock() Registry().register('main_window', mocked_main_window) # Mock SlideController @@ -853,24 +850,20 @@ def test_update_preview_live(mocked_singleShot, mocked_image_manager, registry): assert 0 == slide_controller.slide_preview.setPixmap.call_count, 'setPixmap should not be called' assert 0 == slide_controller.display.preview.call_count, 'display.preview() should not be called' assert 2 == mocked_singleShot.call_count, 'Timer to display_maindisplay should have been called 2 times' - assert 0 == mocked_image_manager.get_image.call_count, 'image_manager not be called' -@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') @patch(u'PyQt5.QtCore.QTimer.singleShot') -def test_update_preview_pres(mocked_singleShot, mocked_image_manager, registry): +def test_update_preview_pres(mocked_singleShot, registry): """ Test that the preview screen is updated with the correct preview for presentation service items """ - # GIVEN: A mocked presentation service item, a mocked image_manager, a mocked Registry, + # GIVEN: A mocked presentation service item, a mocked Registry, # and a slide controller with many mocks. # Mocked Presentation Item mocked_pres_item = MagicMock() mocked_pres_item.get_rendered_frame.return_value = '' mocked_pres_item.is_capable = MagicMock() mocked_pres_item.is_capable.side_effect = [True, True] - # Mock image_manager - mocked_image_manager.get_image.return_value = QtGui.QImage() mocked_main_window = MagicMock() Registry().register('main_window', mocked_main_window) # Mock SlideController @@ -891,26 +884,23 @@ def test_update_preview_pres(mocked_singleShot, mocked_image_manager, registry): # WHEN: update_preview is called slide_controller.update_preview() - # THEN: setPixmap and the image_manager should have been called + # THEN: setPixmap should have been called assert 1 == slide_controller.preview_display.set_single_image.call_count, 'set_single_image should be called' assert 0 == mocked_singleShot.call_count, 'Timer to display_maindisplay should not be called' -@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') @patch(u'PyQt5.QtCore.QTimer.singleShot') -def test_update_preview_media(mocked_singleShot, mocked_image_manager, registry): +def test_update_preview_media(mocked_singleShot, registry): """ Test that the preview screen is updated with the correct preview for media service items """ - # GIVEN: A mocked media service item, a mocked image_manager, a mocked Registry, + # GIVEN: A mocked media service item, a mocked Registry, # and a slide controller with many mocks. # Mocked Media Item mocked_media_item = MagicMock() mocked_media_item.get_rendered_frame.return_value = '' mocked_media_item.is_capable = MagicMock() mocked_media_item.is_capable.side_effect = [True, False] - # Mock image_manager - mocked_image_manager.get_image.return_value = QtGui.QImage() # Mock Registry mocked_main_window = MagicMock() Registry().register('main_window', mocked_main_window) @@ -935,24 +925,20 @@ def test_update_preview_media(mocked_singleShot, mocked_image_manager, registry) # THEN: setPixmap should have been called assert 1 == slide_controller.preview_display.set_single_image.call_count, 'set_single_image should be called' assert 0 == mocked_singleShot.call_count, 'Timer to display_maindisplay should not be called' - assert 0 == mocked_image_manager.get_image.call_count, 'image_manager should not be called' -@patch(u'openlp.core.ui.slidecontroller.SlideController.image_manager') @patch(u'PyQt5.QtCore.QTimer.singleShot') -def test_update_preview_image(mocked_singleShot, mocked_image_manager, registry): +def test_update_preview_image(mocked_singleShot, registry): """ Test that the preview screen is updated with the correct preview for image service items """ - # GIVEN: A mocked image service item, a mocked image_manager, a mocked Registry, + # GIVEN: A mocked image service item, a mocked Registry, # and a slide controller with many mocks. # Mocked Image Item mocked_img_item = MagicMock() mocked_img_item.get_rendered_frame.return_value = '' mocked_img_item.is_capable = MagicMock() mocked_img_item.is_capable.side_effect = [False, True] - # Mock image_manager - mocked_image_manager.get_image.return_value = QtGui.QImage() # Mock Registry mocked_main_window = MagicMock() Registry().register('main_window', mocked_main_window) @@ -976,7 +962,6 @@ def test_update_preview_image(mocked_singleShot, mocked_image_manager, registry) # THEN: setPixmap and display.preview should have been called assert 1 == slide_controller.preview_display.go_to_slide.call_count, 'go_to_slide should be called' assert 0 == mocked_singleShot.call_count, 'Timer to display_maindisplay should not be called' - assert 0 == mocked_image_manager.get_image.call_count, 'image_manager should not be called' @patch(u'openlp.core.ui.slidecontroller.image_to_byte') diff --git a/tests/functional/openlp_core/widgets/test_views.py b/tests/functional/openlp_core/widgets/test_views.py index 0168b7bea..fffc8962a 100644 --- a/tests/functional/openlp_core/widgets/test_views.py +++ b/tests/functional/openlp_core/widgets/test_views.py @@ -26,7 +26,6 @@ import pytest from types import GeneratorType from unittest.mock import MagicMock, call, patch -from PyQt5 import QtGui from openlp.core.common.i18n import UiStrings from openlp.core.widgets.views import ListPreviewWidget, ListWidgetWithDnD, TreeWidgetWithDnD, handle_mime_data_urls @@ -119,10 +118,9 @@ def test_new_list_preview_widget(preview_widget_env, mock_settings): assert list_preview_widget.screen_ratio == 1, 'Should not be called' -@patch(u'openlp.core.widgets.views.ListPreviewWidget.image_manager') @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents') @patch(u'openlp.core.widgets.views.ListPreviewWidget.setRowHeight') -def test_replace_service_item_thumbs(mocked_setRowHeight, mocked_resizeRowsToContents, mocked_image_manager, +def test_replace_service_item_thumbs(mocked_setRowHeight, mocked_resizeRowsToContents, preview_widget_env, mock_settings): """ Test that thubmails for different slides are loaded properly in replace_service_item. @@ -151,8 +149,6 @@ def test_replace_service_item_thumbs(mocked_setRowHeight, mocked_resizeRowsToCon mocked_cmd_service_item.is_capable.return_value = True mocked_cmd_service_item.get_frames.return_value = [{'title': None, 'path': 'FAIL', 'image': 'TEST3'}, {'title': None, 'path': 'FAIL', 'image': 'TEST4'}] - # Mock image_manager - mocked_image_manager.get_image.return_value = QtGui.QImage() # init ListPreviewWidget and load service item list_preview_widget = ListPreviewWidget(None, 1) @@ -160,11 +156,6 @@ def test_replace_service_item_thumbs(mocked_setRowHeight, mocked_resizeRowsToCon # WHEN: replace_service_item is called list_preview_widget.replace_service_item(mocked_img_service_item, 200, 0) list_preview_widget.replace_service_item(mocked_cmd_service_item, 200, 0) - # THEN: The ImageManager should be called in the appriopriate manner for each service item. - # assert mocked_image_manager.get_image.call_count == 4, 'Should be called once for each slide' - # calls = [call('TEST1', ImageSource.ImagePlugin), call('TEST2', ImageSource.ImagePlugin), - # call('TEST3', ImageSource.CommandPlugins), call('TEST4', ImageSource.CommandPlugins)] - # mocked_image_manager.get_image.assert_has_calls(calls) @patch(u'openlp.core.widgets.views.ListPreviewWidget.resizeRowsToContents') diff --git a/tests/interfaces/openlp_core/ui/test_settings_form.py b/tests/interfaces/openlp_core/ui/test_settings_form.py index 9f9ef9fae..b57476a5d 100644 --- a/tests/interfaces/openlp_core/ui/test_settings_form.py +++ b/tests/interfaces/openlp_core/ui/test_settings_form.py @@ -26,7 +26,6 @@ from unittest.mock import MagicMock, patch from PyQt5 import QtCore, QtTest -from openlp.core.common.registry import Registry from openlp.core.ui.settingsform import SettingsForm @@ -111,62 +110,3 @@ def test_register_multiple_functions(form): # WHEN testing the processing stack # THEN the processing stack should still have two items assert len(form.processes) == 2, 'No new processes should have been added to the stack' - - -def test_register_image_manager_trigger_one(form, dummy): - """ - Test the triggering of the image manager rebuild event from image background change - """ - # GIVEN: Three functions registered to be call - Registry().register_function('images_config_updated', dummy[0]) - Registry().register_function('config_screen_changed', dummy[1]) - Registry().register_function('images_regenerate', dummy[2]) - - # WHEN: The Images have been changed and the form submitted - form.register_post_process('images_config_updated') - form.accept() - - # THEN: images_regenerate should have been added. - assert dummy[0].call_count == 1, 'dummy1 should have been called once' - assert dummy[1].call_count == 0, 'dummy2 should not have been called at all' - assert dummy[2].call_count == 1, 'dummy3 should have been called once' - - -def test_register_image_manager_trigger_two(form, dummy): - """ - Test the triggering of the image manager rebuild event from screen dimension change - """ - # GIVEN: Three functions registered to be call - Registry().register_function('images_config_updated', dummy[0]) - Registry().register_function('config_screen_changed', dummy[1]) - Registry().register_function('images_regenerate', dummy[2]) - - # WHEN: The Images have been changed and the form submitted - form.register_post_process('config_screen_changed') - form.accept() - - # THEN: images_regenerate should have been added. - assert dummy[0].call_count == 0, 'dummy1 should not have been called at all' - assert dummy[1].call_count == 1, 'dummy2 should have been called once' - assert dummy[2].call_count == 1, 'dummy3 should have been called once' - - -def test_register_image_manager_trigger_three(form, dummy): - """ - Test the triggering of the image manager rebuild event from image background change and a change to the - screen dimension. - """ - # GIVEN: Three functions registered to be call - Registry().register_function('images_config_updated', dummy[0]) - Registry().register_function('config_screen_changed', dummy[1]) - Registry().register_function('images_regenerate', dummy[2]) - - # WHEN: The Images have been changed and the form submitted - form.register_post_process('config_screen_changed') - form.register_post_process('images_config_updated') - form.accept() - - # THEN: Images_regenerate should have been added. - assert dummy[0].call_count == 1, 'dummy1 should have been called once' - assert dummy[1].call_count == 1, 'dummy2 should have been called once' - assert dummy[2].call_count == 1, 'dummy3 should have been called once'