From 16027a43a979c8d4504ef778f60c65bfc4ca9234 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Mon, 12 Nov 2018 22:42:43 -0700 Subject: [PATCH] Fixed a couple tests --- openlp/core/api/endpoint/controller.py | 13 +- openlp/core/display/render.py | 17 +- openlp/core/lib/serviceitem.py | 39 ++-- .../api/endpoint/test_controller.py | 48 +++-- .../openlp_core/common/test_actions.py | 203 +++++++++--------- 5 files changed, 165 insertions(+), 155 deletions(-) diff --git a/openlp/core/api/endpoint/controller.py b/openlp/core/api/endpoint/controller.py index e59d207ae..7af852d91 100644 --- a/openlp/core/api/endpoint/controller.py +++ b/openlp/core/api/endpoint/controller.py @@ -49,7 +49,7 @@ def controller_text(request): :param request: the http request - not used """ - log.debug("controller_text ") + log.debug('controller_text') live_controller = Registry().get('live_controller') current_item = live_controller.service_item data = [] @@ -58,13 +58,14 @@ def controller_text(request): item = {} # Handle text (songs, custom, bibles) if current_item.is_text(): - if frame['verseTag']: - item['tag'] = str(frame['verseTag']) + if frame['verse']: + item['tag'] = str(frame['verse']) else: item['tag'] = str(index + 1) - item['chords_text'] = str(frame['chords_text']) - item['text'] = str(frame['text']) - item['html'] = str(frame['html']) + # TODO: Figure out rendering chords + item['chords_text'] = str(frame.get('chords_text', '')) + item['text'] = frame['text'] + item['html'] = current_item.get_rendered_frame(index) # Handle images, unless a custom thumbnail is given or if thumbnails is disabled elif current_item.is_image() and not frame.get('image', '') and Settings().value('api/thumbnails'): item['tag'] = str(index + 1) diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py index cb4a0639b..aaaa11136 100644 --- a/openlp/core/display/render.py +++ b/openlp/core/display/render.py @@ -25,6 +25,7 @@ The :mod:`~openlp.display.render` module contains functions for rendering. import html import logging import math +import os import re import time @@ -33,15 +34,15 @@ from PyQt5 import QtWidgets, QtGui from openlp.core.lib.formattingtags import FormattingTags from openlp.core.common.registry import Registry, RegistryBase from openlp.core.common.mixins import LogMixin, RegistryProperties -from openlp.core.display.window import DisplayWindow from openlp.core.display.screens import ScreenList +from openlp.core.display.window import DisplayWindow from openlp.core.lib import ItemCapabilities log = logging.getLogger(__name__) SLIM_CHARS = 'fiíIÍjlĺľrtť.,;/ ()|"\'!:\\' -CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' +CHORD_LINE_MATCH = re.compile(r'\[(.*?)\]([\u0080-\uFFFF,\w]*)' # noqa '([\u0080-\uFFFF,\w,\s,\.,\,,\!,\?,\;,\:,\|,\",\',\-,\_]*)(\Z)?') CHORD_TEMPLATE = '{chord}' FIRST_CHORD_TEMPLATE = '{chord}' @@ -438,11 +439,11 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow): self.setGeometry(screen.geometry.x(), screen.geometry.y(), screen.geometry.width(), screen.geometry.height()) break - # If the display is not show'ed and hidden like this webegine will not render + # If the display is not show'ed and hidden like this webegine will not render self.show() self.hide() self.theme_height = 0 - + def calculate_line_count(self): """ Calculate the number of lines that fits on one slide @@ -524,11 +525,11 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow): # If there are (at least) two occurrences of [---] we use the first two slides (and neglect the last # for now). if len(slides) == 3: - html_text = expand_tags('\n'.join(slides[:2])) + html_text = render_tags('\n'.join(slides[:2])) # We check both slides to determine if the optional split is needed (there is only one optional # split). else: - html_text = expand_tags('\n'.join(slides)) + html_text = render_tags('\n'.join(slides)) html_text = html_text.replace('\n', '
') if self._text_fits_on_slide(html_text): # The first two optional slides fit (as a whole) on one slide. Replace the first occurrence @@ -617,7 +618,7 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow): previous_raw = '' for line in lines: line = line.strip() - html_line = expand_tags(line) + html_line = render_tags(line) # Text too long so go to next page. if not self._text_fits_on_slide(previous_html + html_line): # Check if there was a verse before the current one and append it, when it fits on the page. @@ -634,7 +635,7 @@ class Renderer(RegistryBase, LogMixin, RegistryProperties, DisplayWindow): continue # Figure out how many words of the line will fit on screen as the line will not fit as a whole. raw_words = words_split(line) - html_words = list(map(expand_tags, raw_words)) + html_words = list(map(render_tags, raw_words)) previous_html, previous_raw = \ self._binary_chop(formatted, previous_html, previous_raw, html_words, raw_words, ' ', line_end) else: diff --git a/openlp/core/lib/serviceitem.py b/openlp/core/lib/serviceitem.py index 4827c1980..01304e035 100644 --- a/openlp/core/lib/serviceitem.py +++ b/openlp/core/lib/serviceitem.py @@ -165,24 +165,23 @@ class ServiceItem(RegistryProperties): previous_pages = {} for index, raw_slide in enumerate(self.slides): verse_tag = raw_slide['verse'] - if verse_tag in previous_pages and previous_pages[verse_tag][0] == raw_slide: - pages = previous_pages[verse_tag][1] + if verse_tag in previous_pages and previous_pages[verse_tag][1] == raw_slide: + page = previous_pages[verse_tag][1] else: - pages = self.renderer.format_slide(raw_slide['text'], self) - previous_pages[verse_tag] = (raw_slide, pages) - for page in pages: - rendered_slide = { - 'title': raw_slide['title'], - 'text': render_tags(page), - 'verse': index, - } - self._rendered_slides.append(rendered_slide) - display_slide = { - 'title': raw_slide['title'], - 'text': remove_tags(page), - 'verse': verse_tag, - } - self._display_slides.append(display_slide) + page = render_tags(raw_slide['text'], self) + previous_pages[verse_tag] = (raw_slide, page) + rendered_slide = { + 'title': raw_slide['title'], + 'text': page, + 'verse': index, + } + self._rendered_slides.append(rendered_slide) + display_slide = { + 'title': raw_slide['title'], + 'text': remove_tags(page), + 'verse': verse_tag, + } + self._display_slides.append(display_slide) @property def rendered_slides(self): @@ -362,6 +361,7 @@ class ServiceItem(RegistryProperties): if self.service_item_type == ServiceItemType.Text: for slide in service_item['serviceitem']['data']: 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(Settings().value(settings_section + '/background color')) @@ -485,7 +485,7 @@ class ServiceItem(RegistryProperties): Returns the frames for the ServiceItem """ if self.service_item_type == ServiceItemType.Text: - return self._display_slides + return self.display_slides else: return self.slides @@ -496,7 +496,8 @@ class ServiceItem(RegistryProperties): :param row: The service item slide to be returned """ if self.service_item_type == ServiceItemType.Text: - return self._display_frames[row]['html'].split('\n')[0] + # return self.display_frames[row]['html'].split('\n')[0] + return self.rendered_slides[row]['text'] elif self.service_item_type == ServiceItemType.Image: return self.slides[row]['path'] else: diff --git a/tests/functional/openlp_core/api/endpoint/test_controller.py b/tests/functional/openlp_core/api/endpoint/test_controller.py index ad9359739..9ff46e592 100644 --- a/tests/functional/openlp_core/api/endpoint/test_controller.py +++ b/tests/functional/openlp_core/api/endpoint/test_controller.py @@ -19,20 +19,17 @@ # with this program; if not, write to the Free Software Foundation, Inc., 59 # # Temple Place, Suite 330, Boston, MA 02111-1307 USA # ############################################################################### -import sys +# import sys from unittest import TestCase -# You should have received a copy of the GNU General Public License along # -# with this program; if not, write to the Free Software Foundation, Inc., 59 # -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch from PyQt5 import QtCore # Mock QtWebEngineWidgets -sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() +# sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock() from openlp.core.api.endpoint.controller import controller_direction, controller_text from openlp.core.common.registry import Registry -from openlp.core.display.render import Renderer from openlp.core.display.screens import ScreenList from openlp.core.lib.serviceitem import ServiceItem from tests.utils import convert_file_service_item @@ -64,9 +61,11 @@ class TestController(TestCase): self.desktop.primaryScreen.return_value = SCREEN['primary'] self.desktop.screenCount.return_value = SCREEN['number'] self.desktop.screenGeometry.return_value = SCREEN['size'] - self.screens = ScreenList.create(self.desktop) - renderer = Renderer() - renderer.empty_height = 1000 + with patch('openlp.core.display.screens.QtWidgets.QApplication.screens') as mocked_screens: + mocked_screens.return_value = [ + MagicMock(**{'geometry.return_value': SCREEN['size']}) + ] + self.screens = ScreenList.create(self.desktop) Registry().register('live_controller', self.mocked_live_controller) def test_controller_text_empty(self): @@ -74,13 +73,17 @@ class TestController(TestCase): Remote API Tests : test the controller text method can be called with empty service item """ # GIVEN: A mocked service with a dummy service item - self.mocked_live_controller.service_item = MagicMock() + mocked_service_item = MagicMock() + mocked_service_item.get_frames.return_value = [] + mocked_service_item.unique_identifier = 'mock-service-item' + self.mocked_live_controller.service_item = mocked_service_item + # WHEN: I trigger the method - ret = controller_text("SomeText") + ret = controller_text(MagicMock()) + # THEN: I get a basic set of results - results = ret['results'] - assert isinstance(results['item'], MagicMock) - assert len(results['slides']) == 0 + assert ret['results']['item'] == 'mock-service-item' + assert len(ret['results']['slides']) == 0 def test_controller_text(self): """ @@ -90,9 +93,10 @@ class TestController(TestCase): line = convert_file_service_item(TEST_PATH, 'serviceitem_custom_1.osj') self.mocked_live_controller.service_item = ServiceItem(None) self.mocked_live_controller.service_item.set_from_service(line) - self.mocked_live_controller.service_item.render(True) + # WHEN: I trigger the method - ret = controller_text("SomeText") + ret = controller_text(MagicMock()) + # THEN: I get a basic set of results results = ret['results'] assert isinstance(ret, dict) @@ -103,19 +107,27 @@ class TestController(TestCase): Text the live next method is triggered """ # GIVEN: A mocked service with a dummy service item + mocked_emit = MagicMock() + self.mocked_live_controller.slidecontroller_live_next.emit = mocked_emit self.mocked_live_controller.service_item = MagicMock() + # WHEN: I trigger the method controller_direction(None, 'live', 'next') + # THEN: The correct method is called - self.mocked_live_controller.slidecontroller_live_next.emit.assert_called_once_with() + mocked_emit.assert_called_once_with() def test_controller_direction_previous(self): """ Text the live next method is triggered """ # GIVEN: A mocked service with a dummy service item + mocked_emit = MagicMock() + self.mocked_live_controller.slidecontroller_live_previous.emit = mocked_emit self.mocked_live_controller.service_item = MagicMock() + # WHEN: I trigger the method controller_direction(None, 'live', 'previous') + # THEN: The correct method is called - self.mocked_live_controller.slidecontroller_live_previous.emit.assert_called_once_with() + mocked_emit.assert_called_once_with() diff --git a/tests/functional/openlp_core/common/test_actions.py b/tests/functional/openlp_core/common/test_actions.py index cf67957a9..4e9a5fba5 100644 --- a/tests/functional/openlp_core/common/test_actions.py +++ b/tests/functional/openlp_core/common/test_actions.py @@ -23,138 +23,133 @@ Package to test the openlp.core.common.actions package. """ from unittest import TestCase -from unittest.mock import MagicMock, call, patch +from unittest.mock import MagicMock from PyQt5 import QtCore, QtGui, QtWidgets -import openlp.core.common.actions from openlp.core.common.actions import ActionList, CategoryActionList from openlp.core.common.settings import Settings from tests.helpers.testmixin import TestMixin -class TestCategoryActionList(TestCase): - def setUp(self): - """ - Create an instance and a few example actions. - """ - self.action1 = MagicMock() - self.action1.text.return_value = 'first' - self.action2 = MagicMock() - self.action2.text.return_value = 'second' - self.list = CategoryActionList() +MOCK_ACTION1 = MagicMock(**{'text.return_value': 'first'}) +MOCK_ACTION2 = MagicMock(**{'text.return_value': 'second'}) - def tearDown(self): - """ - Clean up - """ - del self.list - def test_contains(self): - """ - Test the __contains__() method - """ - # GIVEN: The list. - # WHEN: Add an action - self.list.append(self.action1) +def test_action_list_contains(): + """ + Test the __contains__() method + """ + # GIVEN: The list and 2 actions + category_list = CategoryActionList() - # THEN: The actions should (not) be in the list. - assert self.action1 in self.list - assert self.action2 not in self.list + # WHEN: Add an action + category_list.append(MOCK_ACTION1) - def test_len(self): - """ - Test the __len__ method - """ - # GIVEN: The list. - # WHEN: Do nothing. - # THEN: Check the length. - assert len(self.list) == 0, "The length should be 0." + # THEN: The actions should (not) be in the list. + assert MOCK_ACTION1 in category_list + assert MOCK_ACTION2 not in category_list - # GIVEN: The list. - # WHEN: Append an action. - self.list.append(self.action1) - # THEN: Check the length. - assert len(self.list) == 1, "The length should be 1." +def test_action_list_empty_len(): + """ + Test the __len__ method when the list is empty + """ + # GIVEN: The list without any actions + category_list = CategoryActionList() - def test_append(self): - """ - Test the append() method - """ - # GIVEN: The list. - # WHEN: Append an action. - self.list.append(self.action1) - self.list.append(self.action2) + # WHEN: Do nothing. + list_len = len(category_list) - # THEN: Check if the actions are in the list and check if they have the correct weights. - assert self.action1 in self.list - assert self.action2 in self.list - assert self.list.actions[0] == (0, self.action1) - assert self.list.actions[1] == (1, self.action2) + # THEN: Check the length. + assert list_len == 0, 'The length should be 0.' - def test_add(self): - """ - Test the add() method - """ - # GIVEN: The list and weights. - action1_weight = 42 - action2_weight = 41 - # WHEN: Add actions and their weights. - self.list.add(self.action1, action1_weight) - self.list.add(self.action2, action2_weight) +def test_action_list_len(): + """ + Test the __len__ method when the list is not empty + """ + # GIVEN: The list with 2 items in it + category_list = CategoryActionList() + category_list.append(MOCK_ACTION1) + category_list.append(MOCK_ACTION2) - # THEN: Check if they were added and have the specified weights. - assert self.action1 in self.list - assert self.action2 in self.list - # Now check if action1 is second and action2 is first (due to their weights). - assert self.list.actions[0] == (41, self.action2) - assert self.list.actions[1] == (42, self.action1) + # WHEN: The length of the list is calculated + list_len = len(category_list) - def test_iterator(self): - """ - Test the __iter__ and __next__ methods - """ - # GIVEN: The list including two actions - self.list.add(self.action1) - self.list.add(self.action2) + # THEN: It should have 2 items + assert list_len == 2, 'The list should have 2 items in it' - # WHEN: Iterating over the list - local_list = [a for a in self.list] - # THEN: Make sure they are returned in correct order - assert len(self.list) == 2 - assert local_list[0] is self.action1 - assert local_list[1] is self.action2 - def test_remove(self): - """ - Test the remove() method - """ - # GIVEN: The list - self.list.append(self.action1) +def test_action_list_append(): + """ + Test the append() method + """ + # GIVEN: The list. + category_list = CategoryActionList() - # WHEN: Delete an item from the list. - self.list.remove(self.action1) + # WHEN: Append an action. + category_list.append(MOCK_ACTION1) + category_list.append(MOCK_ACTION2) - # THEN: Now the element should not be in the list anymore. - assert self.action1 not in self.list + # THEN: Check if the actions are in the list and check if they have the correct weights. + assert MOCK_ACTION1 in category_list + assert MOCK_ACTION2 in category_list + assert category_list.actions[0] == (0, MOCK_ACTION1) + assert category_list.actions[1] == (1, MOCK_ACTION2) - def test_remove_not_in_list(self): - """ - Test the remove() method when action not in list - """ - with patch.object(openlp.core.common.actions, 'log') as mock_log: - log_warn_calls = [call('Action "" does not exist.')] - # GIVEN: The list - self.list.append(self.action1) +def test_action_list_add(): + """ + Test the add() method + """ + # GIVEN: The list and weights. + action1_weight = 42 + action2_weight = 41 + category_list = CategoryActionList() - # WHEN: Delete an item not in the list. - self.list.remove('') + # WHEN: Add actions and their weights. + category_list.add(MOCK_ACTION1, action1_weight) + category_list.add(MOCK_ACTION2, action2_weight) - # THEN: Warning should be logged - mock_log.warning.assert_has_calls(log_warn_calls) + # THEN: Check if they were added and have the specified weights. + assert MOCK_ACTION1 in category_list + assert MOCK_ACTION2 in category_list + assert category_list.actions[0] == (41, MOCK_ACTION2) + assert category_list.actions[1] == (42, MOCK_ACTION1) + + +def test_action_list_iterator(): + """ + Test the __iter__ and __next__ methods + """ + # GIVEN: The list including two actions + category_list = CategoryActionList() + category_list.append(MOCK_ACTION1) + category_list.append(MOCK_ACTION2) + + # WHEN: Iterating over the list + local_list = [a for a in category_list] + + # THEN: Make sure they are returned in correct order + assert len(category_list) == 2 + assert local_list[0] is MOCK_ACTION1 + assert local_list[1] is MOCK_ACTION2 + + +def test_action_list_remove(): + """ + Test the remove() method + """ + # GIVEN: The list + category_list = CategoryActionList() + category_list.append(MOCK_ACTION1) + + # WHEN: Delete an item from the list. + category_list.remove(MOCK_ACTION1) + + # THEN: Now the element should not be in the list anymore. + assert MOCK_ACTION1 not in category_list class TestActionList(TestCase, TestMixin):