# -*- coding: utf-8 -*- ########################################################################## # OpenLP - Open Source Lyrics Projection # # ---------------------------------------------------------------------- # # Copyright (c) 2008-2022 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 . # ########################################################################## """ Test the ThemeForm class and related methods. """ from pathlib import Path from unittest.mock import patch, MagicMock import pytest from openlp.core.common.registry import Registry from openlp.core.lib.theme import BackgroundType from openlp.core.ui.themeform import ThemeForm from openlp.core.ui.themelayoutform import ThemeLayoutForm def _make_path(s): return MagicMock(**{'__str__.return_value': s, 'exists.return_value': True}) THEME_BACKGROUNDS = { 'solid': [ ('color', 'background_color', '#ff0') ], 'gradient': [ ('gradient_type', 'background_direction', 'horizontal'), ('gradient_start', 'background_start_color', '#fff'), ('gradient_end', 'background_end_color', '#000') ], 'image': [ ('image_color', 'background_border_color', '#f0f0f0'), ('image_path', 'background_source', '/path/to/image.png'), ('image_path', 'background_filename', '/path/to/image.png') ], 'video': [ ('video_color', 'background_border_color', '#222'), ('video_path', 'background_source', '/path/to/video.mkv'), ('video_path', 'background_filename', '/path/to/video.mkv') ], 'stream': [ ('stream_color', 'background_border_color', '#222'), ('stream_mrl', 'background_source', 'http:/127.0.0.1/stream.mkv'), ('stream_mrl', 'background_filename', 'http:/127.0.0.1/stream.mkv') ] } @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_create_theme_wizard(mocked_setup, settings): """ Test creating a ThemeForm instance """ # GIVEN: A ThemeForm class # WHEN: An object is created # THEN: There should be no problems ThemeForm(None) def test_setup(settings): """ Test the _setup method """ # GIVEN: A ThemeForm instance with patch('openlp.core.ui.themeform.ThemeForm._setup'): theme_form = ThemeForm(None) theme_form.setup_ui = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() # WHEN: _setup() is called theme_form._setup() # THEN: The right calls should have been made theme_form.setup_ui.assert_called_once_with(theme_form) assert theme_form.can_update_theme is True assert theme_form.temp_background_filename is None assert isinstance(theme_form.theme_layout_form, ThemeLayoutForm) theme_form.main_area_page.font_name_changed.connect.assert_called_once_with(theme_form.calculate_lines) theme_form.main_area_page.font_size_changed.connect.assert_called_once_with(theme_form.calculate_lines) theme_form.main_area_page.line_spacing_changed.connect.assert_called_once_with(theme_form.calculate_lines) theme_form.main_area_page.is_outline_enabled_changed.connect.assert_called_once_with( theme_form.on_outline_toggled) theme_form.main_area_page.outline_size_changed.connect.assert_called_once_with(theme_form.calculate_lines) theme_form.main_area_page.is_shadow_enabled_changed.connect.assert_called_once_with( theme_form.on_shadow_toggled) theme_form.main_area_page.shadow_size_changed.connect.assert_called_once_with(theme_form.calculate_lines) theme_form.footer_area_page.font_name_changed.connect.assert_called_once_with(theme_form.update_theme) theme_form.footer_area_page.font_size_changed.connect.assert_called_once_with(theme_form.update_theme) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_set_defaults(mocked_setup, settings): """ Test that the right methods are called by the set_defaults() method """ # GIVEN: A ThemeForm instance with mocked methods theme_form = ThemeForm(None) theme_form.restart = MagicMock() theme_form.set_background_page_values = MagicMock() theme_form.set_main_area_page_values = MagicMock() theme_form.set_footer_area_page_values = MagicMock() theme_form.set_alignment_page_values = MagicMock() theme_form.set_position_page_values = MagicMock() theme_form.set_preview_page_values = MagicMock() # WHEN: set_defaults() is called theme_form.set_defaults() # THEN: all the mocks are called theme_form.restart.assert_called_once() theme_form.set_background_page_values.assert_called_once() theme_form.set_main_area_page_values.assert_called_once() theme_form.set_footer_area_page_values.assert_called_once() theme_form.set_alignment_page_values.assert_called_once() theme_form.set_position_page_values.assert_called_once() theme_form.set_preview_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_calculate_lines(mocked_setup, settings): """ Test the calculate_lines() method """ # GIVEN: A ThemeForm instance with some mocked methods theme_form = ThemeForm(None) theme_form.theme = None theme_form.welcome_page = None theme_form.currentPage = MagicMock() theme_form.update_theme = MagicMock() mocked_theme_manager = MagicMock() Registry().register('theme_manager', mocked_theme_manager) # WHEN: calculate_lines() is called theme_form.calculate_lines() # THEN: The mocks should have been called correctly theme_form.currentPage.assert_called_once() theme_form.update_theme.assert_called_once() mocked_theme_manager.generate_image.assert_called_once_with(None, True) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_update_lines_text(mocked_setup, settings): """ Test the update_lines_text() method """ # GIVEN: A ThemeForm instance with some mocked methods theme_form = ThemeForm(None) theme_form.main_line_count_label = MagicMock() # WHEN: calculate_lines() is called theme_form.update_lines_text(10) # THEN: The mocks should have been called correctly theme_form.main_line_count_label.setText.assert_called_once_with('(approximately 10 lines per slide)') @patch('openlp.core.ui.themeform.QtWidgets.QWizard.resizeEvent') @patch('openlp.core.ui.themeform.QtGui.QResizeEvent') @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_resize_event(mocked_setup, MockResizeEvent, mocked_resizeEvent, settings): """ Test that the resizeEvent method handles resizing correctly """ # GIVEN: A ThemeForm instance with a number of mocked methods mocked_event = MagicMock() MockResizeEvent.return_value = mocked_event theme_form = ThemeForm(None) theme_form.size = MagicMock(return_value=1920) theme_form.preview_area_layout = MagicMock() theme_form.preview_box = MagicMock(**{'width.return_value': 300}) mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 1080}) Registry().remove('renderer') Registry().register('renderer', mocked_renderer) # WHEN: resizeEvent() is called theme_form.resizeEvent() # THEN: The correct calls should have been made MockResizeEvent.assert_called_once_with(1920, 1920) mocked_resizeEvent.assert_called_once_with(theme_form, mocked_event) assert mocked_renderer.width.call_count == 2 mocked_renderer.height.assert_called_once() theme_form.preview_area_layout.set_aspect_ratio.assert_called_once_with(16 / 9) theme_form.preview_box.set_scale.assert_called_once_with(float(300 / 1920)) @patch('openlp.core.ui.themeform.QtWidgets.QWizard.resizeEvent') @patch('openlp.core.ui.themeform.QtGui.QResizeEvent') @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_resize_event_dbze(mocked_setup, MockResizeEvent, mocked_resizeEvent, settings): """ Test that the resizeEvent method handles a divide by zero exception correctly """ # GIVEN: A ThemeForm instance with a number of mocked methods mocked_event = MagicMock() MockResizeEvent.return_value = mocked_event theme_form = ThemeForm(None) theme_form.size = MagicMock(return_value=1920) theme_form.preview_area_layout = MagicMock() theme_form.preview_box = MagicMock(**{'width.return_value': 300}) mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 0}) Registry().remove('renderer') Registry().register('renderer', mocked_renderer) # WHEN: resizeEvent() is called theme_form.resizeEvent() # THEN: The correct calls should have been made MockResizeEvent.assert_called_once_with(1920, 1920) mocked_resizeEvent.assert_called_once_with(theme_form, mocked_event) assert mocked_renderer.width.call_count == 2 mocked_renderer.height.assert_called_once() theme_form.preview_area_layout.set_aspect_ratio.assert_called_once_with(1) theme_form.preview_box.set_scale.assert_called_once_with(float(300 / 1920)) @patch('openlp.core.ui.themeform.QtWidgets.QMessageBox.critical') @patch('openlp.core.ui.themeform.is_not_image_file') @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_validate_current_page_with_image(mocked_setup, mocked_is_not_image_file, mocked_critical, settings): """ Test the validateCurrentPage() method """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock(background_type=BackgroundType.to_string(BackgroundType.Image), image_path=Path('picture.jpg')) theme_form.page = MagicMock(return_value=theme_form.background_page) mocked_is_not_image_file.return_value = True # WHEN: validateCurrentPage() is called result = theme_form.validateCurrentPage() # THEN: The right methods were called, and the result is False mocked_critical.assert_called_once_with(theme_form, 'Background Image Empty', 'You have not selected a background image. ' 'Please select one before continuing.') assert result is False @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_validate_current_page(mocked_setup, settings): """ Test the validateCurrentPage() method """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.page = MagicMock() # WHEN: validateCurrentPage() is called result = theme_form.validateCurrentPage() # THEN: The right methods were called, and the result is False assert result is True @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_on_current_id_changed_preview(mocked_setup, settings): """ Test the on_current_id_changed() method """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.theme = 'my fake theme' theme_form.area_position_page = MagicMock() theme_form.preview_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.preview_page) theme_form.update_theme = MagicMock() theme_form.preview_box = MagicMock(**{'width.return_value': 300}) theme_form.preview_area_layout = MagicMock() theme_form.resizeEvent = MagicMock() mocked_renderer = MagicMock(**{'width.return_value': 1920, 'height.return_value': 0}) Registry().remove('renderer') Registry().register('renderer', mocked_renderer) # WHEN: on_current_id_changed() is called theme_form.on_current_id_changed(1) # THEN: The right options should have been set theme_form.update_theme.assert_called_once() theme_form.resizeEvent.assert_called_once() theme_form.preview_box.clear_slides.assert_called_once() theme_form.preview_box.show.assert_called_once() theme_form.preview_box.generate_preview.assert_called_once_with('my fake theme', False, False) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_on_outline_toggled(mocked_setup, settings): """ Test the on_outline_toggled() method """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.can_update_theme = True theme_form.theme = MagicMock() theme_form.calculate_lines = MagicMock() # WHEN: on_outline_toggled is called theme_form.on_outline_toggled(True) # THEN: Everything is working right assert theme_form.theme.font_main_outline is True theme_form.calculate_lines.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_on_shadow_toggled(mocked_setup, settings): """ Test the on_shadow_toggled() method """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.can_update_theme = True theme_form.theme = MagicMock() theme_form.calculate_lines = MagicMock() # WHEN: on_shadow_toggled is called theme_form.on_shadow_toggled(True) # THEN: Everything is working right assert theme_form.theme.font_main_shadow is True theme_form.calculate_lines.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_initialise_page_background(mocked_setup, settings): """ Test the initializePage() method with the background page """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.background_page) theme_form.set_background_page_values = MagicMock() # WHEN: on_shadow_toggled is called theme_form.initializePage(0) # THEN: Everything is working right theme_form.set_background_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_initialise_page_main_area(mocked_setup, settings): """ Test the initializePage() method with the main_area page """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.main_area_page) theme_form.set_main_area_page_values = MagicMock() # WHEN: on_shadow_toggled is called theme_form.initializePage(0) # THEN: Everything is working right theme_form.set_main_area_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_initialise_page_footer_area(mocked_setup, settings): """ Test the initializePage() method with the footer_area page """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.footer_area_page) theme_form.set_footer_area_page_values = MagicMock() # WHEN: on_shadow_toggled is called theme_form.initializePage(0) # THEN: Everything is working right theme_form.set_footer_area_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_initialise_page_alignment(mocked_setup, settings): """ Test the initializePage() method with the alignment page """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.alignment_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.alignment_page) theme_form.set_alignment_page_values = MagicMock() # WHEN: on_shadow_toggled is called theme_form.initializePage(0) # THEN: Everything is working right theme_form.set_alignment_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_initialise_page_area_position(mocked_setup, settings): """ Test the initializePage() method with the area_position page """ # GIVEN: An instance of ThemeForm with some mocks theme_form = ThemeForm(None) theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.alignment_page = MagicMock() theme_form.area_position_page = MagicMock() theme_form.page = MagicMock(return_value=theme_form.area_position_page) theme_form.set_position_page_values = MagicMock() # WHEN: on_shadow_toggled is called theme_form.initializePage(0) # THEN: Everything is working right theme_form.set_position_page_values.assert_called_once() @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_update_theme_static(mocked_setup, settings): """ Test that the update_theme() method correctly sets all the "static" theme variables """ # GIVEN: An instance of a ThemeForm with some mocked out pages which return certain values theme_form = ThemeForm(None) theme_form.can_update_theme = True theme_form.theme = MagicMock() theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock(font_name='Montserrat', font_color='#f00', font_size=50, line_spacing=12, is_outline_enabled=True, outline_color='#00f', outline_size=3, is_shadow_enabled=True, shadow_color='#111', shadow_size=5, is_bold=True, is_italic=False) theme_form.footer_area_page = MagicMock(font_name='Oxygen', font_color='#fff', font_size=20, is_bold=False, is_italic=True) theme_form.alignment_page = MagicMock(horizontal_align='left', vertical_align='top', is_transition_enabled=True, transition_type='fade', transition_speed='normal', transition_direction='horizontal', is_transition_reverse_enabled=False) theme_form.area_position_page = MagicMock(use_main_default_location=True, use_footer_default_location=True) # WHEN: ThemeForm.update_theme() is called theme_form.update_theme() # THEN: The theme should be correct # Main area assert theme_form.theme.font_main_name == 'Montserrat' assert theme_form.theme.font_main_color == '#f00' assert theme_form.theme.font_main_size == 50 assert theme_form.theme.font_main_line_adjustment == 12 assert theme_form.theme.font_main_outline is True assert theme_form.theme.font_main_outline_color == '#00f' assert theme_form.theme.font_main_outline_size == 3 assert theme_form.theme.font_main_shadow is True assert theme_form.theme.font_main_shadow_color == '#111' assert theme_form.theme.font_main_shadow_size == 5 assert theme_form.theme.font_main_bold is True assert theme_form.theme.font_main_italics is False assert theme_form.theme.font_main_override is False theme_form.theme.set_default_header.assert_called_once_with() # Footer assert theme_form.theme.font_footer_name == 'Oxygen' assert theme_form.theme.font_footer_color == '#fff' assert theme_form.theme.font_footer_size == 20 assert theme_form.theme.font_footer_bold is False assert theme_form.theme.font_footer_italics is True assert theme_form.theme.font_footer_override is False theme_form.theme.set_default_footer.assert_called_once_with() # Alignment assert theme_form.theme.display_horizontal_align == 'left' assert theme_form.theme.display_vertical_align == 'top' # Transitions assert theme_form.theme.display_slide_transition is True assert theme_form.theme.display_slide_transition_type == 'fade' assert theme_form.theme.display_slide_transition_direction == 'horizontal' assert theme_form.theme.display_slide_transition_speed == 'normal' assert theme_form.theme.display_slide_transition_reverse is False @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_update_theme_overridden_areas(mocked_setup, settings): """ Test that the update_theme() method correctly sets all the positioning information for a custom position """ # GIVEN: An instance of a ThemeForm with some mocked out pages which return certain values theme_form = ThemeForm(None) theme_form.can_update_theme = True theme_form.theme = MagicMock() theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.alignment_page = MagicMock() theme_form.area_position_page = MagicMock(use_main_default_location=False, use_footer_default_location=False, main_x=20, main_y=50, main_height=900, main_width=1880, footer_x=20, footer_y=910, footer_height=70, footer_width=1880) # WHEN: ThemeForm.update_theme() is called theme_form.update_theme() # THEN: The theme should be correct assert theme_form.theme.font_main_override is True assert theme_form.theme.font_main_x == 20 assert theme_form.theme.font_main_y == 50 assert theme_form.theme.font_main_height == 900 assert theme_form.theme.font_main_width == 1880 assert theme_form.theme.font_footer_override is True assert theme_form.theme.font_footer_x == 20 assert theme_form.theme.font_footer_y == 910 assert theme_form.theme.font_footer_height == 70 assert theme_form.theme.font_footer_width == 1880 @pytest.mark.parametrize('background_type', THEME_BACKGROUNDS.keys()) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_update_theme_background(mocked_setup, background_type, settings): """ Test that the update_theme() method correctly sets all the theme background variables for each background type """ # GIVEN: An instance of a ThemeForm with some mocked out pages which return certain values page_props = {page_prop: value for page_prop, _, value in THEME_BACKGROUNDS[background_type]} theme_form = ThemeForm(None) theme_form.can_update_theme = True theme_form.theme = MagicMock() theme_form.background_page = MagicMock(background_type=background_type, **page_props) theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.alignment_page = MagicMock() theme_form.area_position_page = MagicMock() # WHEN: ThemeForm.update_theme() is called theme_form.update_theme() # THEN: The theme should be correct for _, theme_prop, value in THEME_BACKGROUNDS[background_type]: assert getattr(theme_form.theme, theme_prop) == value, f'{theme_prop} should have been {value}' @pytest.mark.skip('Being a bit problematic right now') @pytest.mark.parametrize('background_type', THEME_BACKGROUNDS.keys()) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_set_background_page_values(mocked_setup, background_type, settings): """ Test that the set_background_page_values() method sets the background page values correctly """ # GIVEN: An instance of a ThemeForm with some mocked out pages and a mocked theme with values theme_props = {theme_prop: value for _, theme_prop, value in THEME_BACKGROUNDS[background_type]} theme_form = ThemeForm(None) theme_form.theme = MagicMock(background_type=background_type, **theme_props) theme_form.background_page = MagicMock() theme_form.main_area_page = MagicMock() theme_form.footer_area_page = MagicMock() theme_form.alignment_page = MagicMock() theme_form.area_position_page = MagicMock() # WHEN: set_background_page_values() is called theme_form.set_background_page_values() # THEN: The correct values are set on the page for page_prop, _, value in THEME_BACKGROUNDS[background_type]: assert getattr(theme_form.background_page, page_prop) == value, ( f'{page_prop} should have been {value} but was {getattr(theme_form.background_page, page_prop)}' ) @patch('openlp.core.ui.themeform.ThemeForm._setup') def test_update_theme_cannot_update(mocked_setup, settings): """ Test that the update_theme() method skips out early when the theme cannot be updated """ # GIVEN: An instance of a ThemeForm with some mocked out pages which return certain values theme_form = ThemeForm(None) theme_form.can_update_theme = False # WHEN: ThemeForm.update_theme() is called theme_form.update_theme() # THEN: The theme should be correct # TODO: Figure out a way to check this