openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #34048
Re: [Merge] lp:~raoul-snyman/openlp/pyro-impress into lp:openlp
Review: Needs Fixing
Headers needs update - see inline.
I tried to test but the MacBook at hand was simply to slow to be of value....
Diff comments:
>
> === added file 'openlp/plugins/presentations/lib/libreofficeserver.py'
> --- openlp/plugins/presentations/lib/libreofficeserver.py 1970-01-01 00:00:00 +0000
> +++ openlp/plugins/presentations/lib/libreofficeserver.py 2019-05-22 16:50:27 +0000
> @@ -0,0 +1,431 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2016 OpenLP Developers #
GPL3 License
> +# --------------------------------------------------------------------------- #
> +# 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; version 2 of the License. #
> +# #
> +# 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, write to the Free Software Foundation, Inc., 59 #
> +# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> +###############################################################################
> +"""
> +This module runs a Pyro4 server using LibreOffice's version of Python
> +
> +Please Note: This intentionally uses os.path over pathlib because we don't know which version of Python is shipped with
> +the version of LibreOffice on the user's computer.
> +"""
> +from subprocess import Popen
> +import sys
> +import os
> +import logging
> +import time
> +
> +
> +if sys.platform.startswith('darwin'):
> + # Only make the log file on OS X when running as a server
> + logfile = os.path.join(str(os.getenv('HOME')), 'Library', 'Application Support', 'openlp', 'libreofficeserver.log')
> + print('Setting up log file: {logfile}'.format(logfile=logfile))
> + logging.basicConfig(filename=logfile, level=logging.INFO)
> +
> +
> +# Add the current directory to sys.path so that we can load the serializers
> +sys.path.append(os.path.join(os.path.dirname(__file__)))
> +# Add the vendor directory to sys.path so that we can load Pyro4
> +sys.path.append(os.path.join(os.path.dirname(__file__), 'vendor'))
> +
> +from serializers import register_classes
> +from Pyro4 import Daemon, expose
> +
> +try:
> + # Wrap these imports in a try so that we can run the tests on macOS
> + import uno
> + from com.sun.star.beans import PropertyValue
> + from com.sun.star.task import ErrorCodeIOException
> +except ImportError:
> + # But they need to be defined for mocking
> + uno = None
> + PropertyValue = None
> + ErrorCodeIOException = Exception
> +
> +
> +log = logging.getLogger(__name__)
> +register_classes()
> +
> +
> +class TextType(object):
> + """
> + Type Enumeration for Types of Text to request
> + """
> + Title = 0
> + SlideText = 1
> + Notes = 2
> +
> +
> +class LibreOfficeException(Exception):
> + """
> + A specific exception for LO
> + """
> + pass
> +
> +
> +@expose
> +class LibreOfficeServer(object):
> + """
> + A Pyro4 server which controls LibreOffice
> + """
> + def __init__(self):
> + """
> + Set up the server
> + """
> + self._desktop = None
> + self._control = None
> + self._document = None
> + self._presentation = None
> + self._process = None
> + self._manager = None
> +
> + def _create_property(self, name, value):
> + """
> + Create an OOo style property object which are passed into some Uno methods.
> + """
> + log.debug('create property')
> + property_object = PropertyValue()
> + property_object.Name = name
> + property_object.Value = value
> + return property_object
> +
> + def _get_text_from_page(self, slide_no, text_type=TextType.SlideText):
> + """
> + Return any text extracted from the presentation page.
> +
> + :param slide_no: The slide the notes are required for, starting at 1
> + :param notes: A boolean. If set the method searches the notes of the slide.
> + :param text_type: A TextType. Enumeration of the types of supported text.
> + """
> + text = ''
> + if TextType.Title <= text_type <= TextType.Notes:
> + pages = self._document.getDrawPages()
> + if 0 < slide_no <= pages.getCount():
> + page = pages.getByIndex(slide_no - 1)
> + if text_type == TextType.Notes:
> + page = page.getNotesPage()
> + for index in range(page.getCount()):
> + shape = page.getByIndex(index)
> + shape_type = shape.getShapeType()
> + if shape.supportsService('com.sun.star.drawing.Text'):
> + # if they requested title, make sure it is the title
> + if text_type != TextType.Title or shape_type == 'com.sun.star.presentation.TitleTextShape':
> + text += shape.getString() + '\n'
> + return text
> +
> + def start_process(self):
> + """
> + Initialise Impress
> + """
> + uno_command = [
> + '/Applications/LibreOffice.app/Contents/MacOS/soffice',
> + '--nologo',
> + '--norestore',
> + '--minimized',
> + '--nodefault',
> + '--nofirststartwizard',
> + '--accept=pipe,name=openlp_maclo;urp;StarOffice.ServiceManager'
> + ]
> + self._process = Popen(uno_command)
> +
> + @property
> + def desktop(self):
> + """
> + Set up an UNO desktop instance
> + """
> + if self._desktop is not None:
> + return self._desktop
> + uno_instance = None
> + context = uno.getComponentContext()
> + resolver = context.ServiceManager.createInstanceWithContext('com.sun.star.bridge.UnoUrlResolver', context)
> + loop = 0
> + while uno_instance is None and loop < 3:
> + try:
> + uno_instance = resolver.resolve('uno:pipe,name=openlp_maclo;urp;StarOffice.ComponentContext')
> + except Exception:
> + log.exception('Unable to find running instance, retrying...')
> + loop += 1
> + try:
> + self._manager = uno_instance.ServiceManager
> + log.debug('get UNO Desktop Openoffice - createInstanceWithContext - Desktop')
> + desktop = self._manager.createInstanceWithContext('com.sun.star.frame.Desktop', uno_instance)
> + if not desktop:
> + raise Exception('Failed to get UNO desktop')
> + self._desktop = desktop
> + return desktop
> + except Exception:
> + log.exception('Failed to get UNO desktop')
> + return None
> +
> + def shutdown(self):
> + """
> + Shut down the server
> + """
> + can_kill = True
> + if hasattr(self, '_docs'):
> + while self._docs:
> + self._docs[0].close_presentation()
> + docs = self.desktop.getComponents()
> + count = 0
> + if docs.hasElements():
> + list_elements = docs.createEnumeration()
> + while list_elements.hasMoreElements():
> + doc = list_elements.nextElement()
> + if doc.getImplementationName() != 'com.sun.star.comp.framework.BackingComp':
> + count += 1
> + if count > 0:
> + log.debug('LibreOffice not terminated as docs are still open')
> + can_kill = False
> + else:
> + try:
> + self.desktop.terminate()
> + log.debug('LibreOffice killed')
> + except Exception:
> + log.exception('Failed to terminate LibreOffice')
> + if getattr(self, '_process') and can_kill:
> + self._process.kill()
> +
> + def load_presentation(self, file_path, screen_number):
> + """
> + Load a presentation
> + """
> + self._file_path = file_path
> + url = uno.systemPathToFileUrl(file_path)
> + properties = (self._create_property('Hidden', True),)
> + self._document = None
> + loop_count = 0
> + while loop_count < 3:
> + try:
> + self._document = self.desktop.loadComponentFromURL(url, '_blank', 0, properties)
> + except Exception:
> + log.exception('Failed to load presentation {url}'.format(url=url))
> + if self._document:
> + break
> + time.sleep(0.5)
> + loop_count += 1
> + if loop_count == 3:
> + log.error('Looped too many times')
> + return False
> + self._presentation = self._document.getPresentation()
> + self._presentation.Display = screen_number
> + self._control = None
> + return True
> +
> + def extract_thumbnails(self, temp_folder):
> + """
> + Create thumbnails for the presentation
> + """
> + thumbnails = []
> + thumb_dir_url = uno.systemPathToFileUrl(temp_folder)
> + properties = (self._create_property('FilterName', 'impress_png_Export'),)
> + pages = self._document.getDrawPages()
> + if not pages:
> + return []
> + if not os.path.isdir(temp_folder):
> + os.makedirs(temp_folder)
> + for index in range(pages.getCount()):
> + page = pages.getByIndex(index)
> + self._document.getCurrentController().setCurrentPage(page)
> + url_path = '{path}/{name}.png'.format(path=thumb_dir_url, name=str(index + 1))
> + path = os.path.join(temp_folder, str(index + 1) + '.png')
> + try:
> + self._document.storeToURL(url_path, properties)
> + thumbnails.append(path)
> + except ErrorCodeIOException as exception:
> + log.exception('ERROR! ErrorCodeIOException {error:d}'.format(error=exception.ErrCode))
> + except Exception:
> + log.exception('{path} - Unable to store openoffice preview'.format(path=path))
> + return thumbnails
> +
> + def get_titles_and_notes(self):
> + """
> + Extract the titles and the notes from the slides.
> + """
> + titles = []
> + notes = []
> + pages = self._document.getDrawPages()
> + for slide_no in range(1, pages.getCount() + 1):
> + titles.append(self._get_text_from_page(slide_no, TextType.Title).replace('\n', ' ') + '\n')
> + note = self._get_text_from_page(slide_no, TextType.Notes)
> + if len(note) == 0:
> + note = ' '
> + notes.append(note)
> + return titles, notes
> +
> + def close_presentation(self):
> + """
> + Close presentation and clean up objects.
> + """
> + log.debug('close Presentation LibreOffice')
> + if self._document:
> + if self._presentation:
> + try:
> + self._presentation.end()
> + self._presentation = None
> + self._document.dispose()
> + except Exception:
> + log.exception("Closing presentation failed")
> + self._document = None
> +
> + def is_loaded(self):
> + """
> + Returns true if a presentation is loaded.
> + """
> + log.debug('is loaded LibreOffice')
> + if self._presentation is None or self._document is None:
> + log.debug("is_loaded: no presentation or document")
> + return False
> + try:
> + if self._document.getPresentation() is None:
> + log.debug("getPresentation failed to find a presentation")
> + return False
> + except Exception:
> + log.exception("getPresentation failed to find a presentation")
> + return False
> + return True
> +
> + def is_active(self):
> + """
> + Returns true if a presentation is active and running.
> + """
> + log.debug('is active LibreOffice')
> + if not self.is_loaded():
> + return False
> + return self._control.isRunning() if self._control else False
> +
> + def unblank_screen(self):
> + """
> + Unblanks the screen.
> + """
> + log.debug('unblank screen LibreOffice')
> + return self._control.resume()
> +
> + def blank_screen(self):
> + """
> + Blanks the screen.
> + """
> + log.debug('blank screen LibreOffice')
> + self._control.blankScreen(0)
> +
> + def is_blank(self):
> + """
> + Returns true if screen is blank.
> + """
> + log.debug('is blank LibreOffice')
> + if self._control and self._control.isRunning():
> + return self._control.isPaused()
> + else:
> + return False
> +
> + def stop_presentation(self):
> + """
> + Stop the presentation, remove from screen.
> + """
> + log.debug('stop presentation LibreOffice')
> + self._presentation.end()
> + self._control = None
> +
> + def start_presentation(self):
> + """
> + Start the presentation from the beginning.
> + """
> + log.debug('start presentation LibreOffice')
> + if self._control is None or not self._control.isRunning():
> + window = self._document.getCurrentController().getFrame().getContainerWindow()
> + window.setVisible(True)
> + self._presentation.start()
> + self._control = self._presentation.getController()
> + # start() returns before the Component is ready. Try for 15 seconds.
> + sleep_count = 1
> + while not self._control and sleep_count < 150:
> + time.sleep(0.1)
> + sleep_count += 1
> + self._control = self._presentation.getController()
> + window.setVisible(False)
> + else:
> + self._control.activate()
> + self.goto_slide(1)
> +
> + def get_slide_number(self):
> + """
> + Return the current slide number on the screen, from 1.
> + """
> + return self._control.getCurrentSlideIndex() + 1
> +
> + def get_slide_count(self):
> + """
> + Return the total number of slides.
> + """
> + return self._document.getDrawPages().getCount()
> +
> + def goto_slide(self, slide_no):
> + """
> + Go to a specific slide (from 1).
> +
> + :param slide_no: The slide the text is required for, starting at 1
> + """
> + self._control.gotoSlideIndex(slide_no - 1)
> +
> + def next_step(self):
> + """
> + Triggers the next effect of slide on the running presentation.
> + """
> + is_paused = self._control.isPaused()
> + self._control.gotoNextEffect()
> + time.sleep(0.1)
> + if not is_paused and self._control.isPaused():
> + self._control.gotoPreviousEffect()
> +
> + def previous_step(self):
> + """
> + Triggers the previous slide on the running presentation.
> + """
> + self._control.gotoPreviousEffect()
> +
> + def get_slide_text(self, slide_no):
> + """
> + Returns the text on the slide.
> +
> + :param slide_no: The slide the text is required for, starting at 1
> + """
> + return self._get_text_from_page(slide_no)
> +
> + def get_slide_notes(self, slide_no):
> + """
> + Returns the text in the slide notes.
> +
> + :param slide_no: The slide the notes are required for, starting at 1
> + """
> + return self._get_text_from_page(slide_no, TextType.Notes)
> +
> +
> +def main():
> + """
> + The main function which runs the server
> + """
> + daemon = Daemon(host='localhost', port=4310)
> + daemon.register(LibreOfficeServer, 'openlp.libreofficeserver')
> + try:
> + daemon.requestLoop()
> + finally:
> + daemon.close()
> +
> +
> +if __name__ == '__main__':
> + main()
>
> === added file 'openlp/plugins/presentations/lib/maclocontroller.py'
> --- openlp/plugins/presentations/lib/maclocontroller.py 1970-01-01 00:00:00 +0000
> +++ openlp/plugins/presentations/lib/maclocontroller.py 2019-05-22 16:50:27 +0000
> @@ -0,0 +1,266 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2016 OpenLP Developers #
GPL3 license
> +# --------------------------------------------------------------------------- #
> +# 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; version 2 of the License. #
> +# #
> +# 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, write to the Free Software Foundation, Inc., 59 #
> +# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> +###############################################################################
> +
> +import logging
> +from subprocess import Popen
> +
> +from Pyro4 import Proxy
> +
> +from openlp.core.common import delete_file, is_macosx
> +from openlp.core.common.applocation import AppLocation
> +from openlp.core.common.path import Path
> +from openlp.core.common.registry import Registry
> +from openlp.core.display.screens import ScreenList
> +from openlp.plugins.presentations.lib.serializers import register_classes
> +from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument
> +
> +
> +LIBREOFFICE_PATH = Path('/Applications/LibreOffice.app')
> +LIBREOFFICE_PYTHON = LIBREOFFICE_PATH / 'Contents' / 'Resources' / 'python'
> +
> +if is_macosx() and LIBREOFFICE_PATH.exists():
> + macuno_available = True
> +else:
> + macuno_available = False
> +
> +
> +log = logging.getLogger(__name__)
> +register_classes()
> +
> +
> +class MacLOController(PresentationController):
> + """
> + Class to control interactions with MacLO presentations on Mac OS X via Pyro4. It starts the Pyro4 nameserver,
> + starts the LibreOfficeServer, and then controls MacLO via Pyro4.
> + """
> + log.info('MacLOController loaded')
> +
> + def __init__(self, plugin):
> + """
> + Initialise the class
> + """
> + log.debug('Initialising')
> + super(MacLOController, self).__init__(plugin, 'maclo', MacLODocument, 'Impress on macOS')
> + self.supports = ['odp']
> + self.also_supports = ['ppt', 'pps', 'pptx', 'ppsx', 'pptm']
> + self.server_process = None
> + self._client = None
> + self._start_server()
> +
> + def _start_server(self):
> + """
> + Start a LibreOfficeServer
> + """
> + libreoffice_python = Path('/Applications/LibreOffice.app/Contents/Resources/python')
> + libreoffice_server = AppLocation.get_directory(AppLocation.PluginsDir).joinpath('presentations', 'lib',
> + 'libreofficeserver.py')
> + if libreoffice_python.exists():
> + self.server_process = Popen([str(libreoffice_python), str(libreoffice_server)])
> +
> + @property
> + def client(self):
> + """
> + Set up a Pyro4 client so that we can talk to the LibreOfficeServer
> + """
> + if not self._client:
> + self._client = Proxy('PYRO:openlp.libreofficeserver@localhost:4310')
> + if not self._client._pyroConnection:
> + self._client._pyroReconnect()
> + return self._client
> +
> + def check_available(self):
> + """
> + MacLO is able to run on this machine.
> + """
> + log.debug('check_available')
> + return macuno_available
> +
> + def start_process(self):
> + """
> + Loads a running version of LibreOffice in the background. It is not displayed to the user but is available to
> + the UNO interface when required.
> + """
> + log.debug('Started automatically by the Pyro server')
> + self.client.start_process()
> +
> + def kill(self):
> + """
> + Called at system exit to clean up any running presentations.
> + """
> + log.debug('Kill LibreOffice')
> + self.client.shutdown()
> + self.server_process.kill()
> +
> +
> +class MacLODocument(PresentationDocument):
> + """
> + Class which holds information and controls a single presentation.
> + """
> +
> + def __init__(self, controller, presentation):
> + """
> + Constructor, store information about the file and initialise.
> + """
> + log.debug('Init Presentation LibreOffice')
> + super(MacLODocument, self).__init__(controller, presentation)
> + self.client = controller.client
> +
> + def load_presentation(self):
> + """
> + Tell the LibreOfficeServer to start the presentation.
> + """
> + log.debug('Load Presentation LibreOffice')
> + if not self.client.load_presentation(str(self.file_path), ScreenList().current.number + 1):
> + return False
> + self.create_thumbnails()
> + self.create_titles_and_notes()
> + return True
> +
> + def create_thumbnails(self):
> + """
> + Create thumbnail images for presentation.
> + """
> + log.debug('create thumbnails LibreOffice')
> + if self.check_thumbnails():
> + return
> + temp_thumbnails = self.client.extract_thumbnails(str(self.get_temp_folder()))
> + for index, temp_thumb in enumerate(temp_thumbnails):
> + temp_thumb = Path(temp_thumb)
> + self.convert_thumbnail(temp_thumb, index + 1)
> + delete_file(temp_thumb)
> +
> + def create_titles_and_notes(self):
> + """
> + Writes the list of titles (one per slide) to 'titles.txt' and the notes to 'slideNotes[x].txt'
> + in the thumbnails directory
> + """
> + titles, notes = self.client.get_titles_and_notes()
> + self.save_titles_and_notes(titles, notes)
> +
> + def close_presentation(self):
> + """
> + Close presentation and clean up objects. Triggered by new object being added to SlideController or OpenLP being
> + shutdown.
> + """
> + log.debug('close Presentation LibreOffice')
> + self.client.close_presentation()
> + self.controller.remove_doc(self)
> +
> + def is_loaded(self):
> + """
> + Returns true if a presentation is loaded.
> + """
> + log.debug('is loaded LibreOffice')
> + return self.client.is_loaded()
> +
> + def is_active(self):
> + """
> + Returns true if a presentation is active and running.
> + """
> + log.debug('is active LibreOffice')
> + return self.client.is_active()
> +
> + def unblank_screen(self):
> + """
> + Unblanks the screen.
> + """
> + log.debug('unblank screen LibreOffice')
> + return self.client.unblank_screen()
> +
> + def blank_screen(self):
> + """
> + Blanks the screen.
> + """
> + log.debug('blank screen LibreOffice')
> + self.client.blank_screen()
> +
> + def is_blank(self):
> + """
> + Returns true if screen is blank.
> + """
> + log.debug('is blank LibreOffice')
> + return self.client.is_blank()
> +
> + def stop_presentation(self):
> + """
> + Stop the presentation, remove from screen.
> + """
> + log.debug('stop presentation LibreOffice')
> + self.client.stop_presentation()
> +
> + def start_presentation(self):
> + """
> + Start the presentation from the beginning.
> + """
> + log.debug('start presentation LibreOffice')
> + self.client.start_presentation()
> + # Make sure impress doesn't steal focus, unless we're on a single screen setup
> + if len(ScreenList()) > 1:
> + Registry().get('main_window').activateWindow()
> +
> + def get_slide_number(self):
> + """
> + Return the current slide number on the screen, from 1.
> + """
> + return self.client.get_slide_number()
> +
> + def get_slide_count(self):
> + """
> + Return the total number of slides.
> + """
> + return self.client.get_slide_count()
> +
> + def goto_slide(self, slide_no):
> + """
> + Go to a specific slide (from 1).
> +
> + :param slide_no: The slide the text is required for, starting at 1
> + """
> + self.client.goto_slide(slide_no)
> +
> + def next_step(self):
> + """
> + Triggers the next effect of slide on the running presentation.
> + """
> + self.client.next_step()
> +
> + def previous_step(self):
> + """
> + Triggers the previous slide on the running presentation.
> + """
> + self.client.previous_step()
> +
> + def get_slide_text(self, slide_no):
> + """
> + Returns the text on the slide.
> +
> + :param slide_no: The slide the text is required for, starting at 1
> + """
> + return self.client.get_slide_text(slide_no)
> +
> + def get_slide_notes(self, slide_no):
> + """
> + Returns the text in the slide notes.
> +
> + :param slide_no: The slide the notes are required for, starting at 1
> + """
> + return self.client.get_slide_notes(slide_no)
>
> === added file 'openlp/plugins/presentations/lib/serializers.py'
> --- openlp/plugins/presentations/lib/serializers.py 1970-01-01 00:00:00 +0000
> +++ openlp/plugins/presentations/lib/serializers.py 2019-05-22 16:50:27 +0000
> @@ -0,0 +1,28 @@
> +try:
Should have a license header
> + from openlp.core.common.path import Path
> +except ImportError:
> + from pathlib import Path
> +
> +from Pyro4.util import SerializerBase
> +
> +
> +def path_class_to_dict(obj):
> + """
> + Serialize a Path object for Pyro4
> + """
> + return {
> + '__class__': 'Path',
> + 'parts': obj.parts
> + }
> +
> +
> +def path_dict_to_class(classname, d):
> + return Path(d['parts'])
> +
> +
> +def register_classes():
> + """
> + Register the serializers
> + """
> + SerializerBase.register_class_to_dict(Path, path_class_to_dict)
> + SerializerBase.register_dict_to_class('Path', path_dict_to_class)
>
> === added file 'tests/functional/openlp_plugins/presentations/test_libreofficeserver.py'
> --- tests/functional/openlp_plugins/presentations/test_libreofficeserver.py 1970-01-01 00:00:00 +0000
> +++ tests/functional/openlp_plugins/presentations/test_libreofficeserver.py 2019-05-22 16:50:27 +0000
> @@ -0,0 +1,948 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2016 OpenLP Developers #
GPL3 license
> +# --------------------------------------------------------------------------- #
> +# 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; version 2 of the License. #
> +# #
> +# 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, write to the Free Software Foundation, Inc., 59 #
> +# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> +###############################################################################
> +"""
> +Functional tests to test the LibreOffice Pyro server
> +"""
> +from unittest.mock import MagicMock, patch, call
> +
> +from openlp.plugins.presentations.lib.libreofficeserver import LibreOfficeServer, TextType, main
> +
> +
> +def test_constructor():
> + """
> + Test the Constructor from the server
> + """
> + # GIVEN: No server
> + # WHEN: The server object is created
> + server = LibreOfficeServer()
> +
> + # THEN: The server should have been set up correctly
> + assert server._control is None
> + # assert server._desktop is None
> + assert server._document is None
> + assert server._presentation is None
> + assert server._process is None
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.Popen')
> +def test_start_process(MockedPopen):
> + """
> + Test that the correct command is issued to run LibreOffice
> + """
> + # GIVEN: A LOServer
> + mocked_process = MagicMock()
> + MockedPopen.return_value = mocked_process
> + server = LibreOfficeServer()
> +
> + # WHEN: The start_process() method is run
> + server.start_process()
> +
> + # THEN: The correct command line should run and the process should have started
> + MockedPopen.assert_called_with([
> + '/Applications/LibreOffice.app/Contents/MacOS/soffice',
> + '--nologo',
> + '--norestore',
> + '--minimized',
> + '--nodefault',
> + '--nofirststartwizard',
> + '--accept=pipe,name=openlp_maclo;urp;StarOffice.ServiceManager'
> + ])
> + assert server._process is mocked_process
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_desktop_already_has_desktop(mocked_uno):
> + """
> + Test that setup_desktop() exits early when there's already a desktop
> + """
> + # GIVEN: A LibreOfficeServer instance
> + server = LibreOfficeServer()
> + server._desktop = MagicMock()
> +
> + # WHEN: the desktop property is called
> + desktop = server.desktop
> +
> + # THEN: setup_desktop() exits early
> + assert desktop is server._desktop
> + assert server._manager is None
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_desktop_exception(mocked_uno):
> + """
> + Test that setting up the desktop works correctly when an exception occurs
> + """
> + # GIVEN: A LibreOfficeServer instance
> + server = LibreOfficeServer()
> + mocked_context = MagicMock()
> + mocked_resolver = MagicMock()
> + mocked_uno_instance = MagicMock()
> + MockedServiceManager = MagicMock()
> + mocked_uno.getComponentContext.return_value = mocked_context
> + mocked_context.ServiceManager.createInstanceWithContext.return_value = mocked_resolver
> + mocked_resolver.resolve.side_effect = [Exception, mocked_uno_instance]
> + mocked_uno_instance.ServiceManager = MockedServiceManager
> + MockedServiceManager.createInstanceWithContext.side_effect = Exception()
> +
> + # WHEN: the desktop property is called
> + server.desktop
> +
> + # THEN: A desktop object was created
> + mocked_uno.getComponentContext.assert_called_once_with()
> + mocked_context.ServiceManager.createInstanceWithContext.assert_called_once_with(
> + 'com.sun.star.bridge.UnoUrlResolver', mocked_context)
> + expected_calls = [
> + call('uno:pipe,name=openlp_maclo;urp;StarOffice.ComponentContext'),
> + call('uno:pipe,name=openlp_maclo;urp;StarOffice.ComponentContext')
> + ]
> + assert mocked_resolver.resolve.call_args_list == expected_calls
> + MockedServiceManager.createInstanceWithContext.assert_called_once_with(
> + 'com.sun.star.frame.Desktop', mocked_uno_instance)
> + assert server._manager is MockedServiceManager
> + assert server._desktop is None
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_desktop(mocked_uno):
> + """
> + Test that setting up the desktop works correctly
> + """
> + # GIVEN: A LibreOfficeServer instance
> + server = LibreOfficeServer()
> + mocked_context = MagicMock()
> + mocked_resolver = MagicMock()
> + mocked_uno_instance = MagicMock()
> + MockedServiceManager = MagicMock()
> + mocked_desktop = MagicMock()
> + mocked_uno.getComponentContext.return_value = mocked_context
> + mocked_context.ServiceManager.createInstanceWithContext.return_value = mocked_resolver
> + mocked_resolver.resolve.side_effect = [Exception, mocked_uno_instance]
> + mocked_uno_instance.ServiceManager = MockedServiceManager
> + MockedServiceManager.createInstanceWithContext.return_value = mocked_desktop
> +
> + # WHEN: the desktop property is called
> + server.desktop
> +
> + # THEN: A desktop object was created
> + mocked_uno.getComponentContext.assert_called_once_with()
> + mocked_context.ServiceManager.createInstanceWithContext.assert_called_once_with(
> + 'com.sun.star.bridge.UnoUrlResolver', mocked_context)
> + expected_calls = [
> + call('uno:pipe,name=openlp_maclo;urp;StarOffice.ComponentContext'),
> + call('uno:pipe,name=openlp_maclo;urp;StarOffice.ComponentContext')
> + ]
> + assert mocked_resolver.resolve.call_args_list == expected_calls
> + MockedServiceManager.createInstanceWithContext.assert_called_once_with(
> + 'com.sun.star.frame.Desktop', mocked_uno_instance)
> + assert server._manager is MockedServiceManager
> + assert server._desktop is mocked_desktop
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.PropertyValue')
> +def test_create_property(MockedPropertyValue):
> + """
> + Test that the _create_property() method works correctly
> + """
> + # GIVEN: A server amnd property to set
> + server = LibreOfficeServer()
> + name = 'Hidden'
> + value = True
> +
> + # WHEN: The _create_property() method is called
> + prop = server._create_property(name, value)
> +
> + # THEN: The property should have the correct attributes
> + assert prop.Name == name
> + assert prop.Value == value
> +
> +
> +def test_get_text_from_page_slide_text():
> + """
> + Test that the _get_text_from_page() method gives us nothing for slide text
> + """
> + # GIVEN: A LibreOfficeServer object and some mocked objects
> + text_type = TextType.SlideText
> + slide_no = 1
> + server = LibreOfficeServer()
> + server._document = MagicMock()
> + mocked_pages = MagicMock()
> + mocked_page = MagicMock()
> + mocked_shape = MagicMock()
> + server._document.getDrawPages.return_value = mocked_pages
> + mocked_pages.getCount.return_value = 1
> + mocked_pages.getByIndex.return_value = mocked_page
> + mocked_page.getByIndex.return_value = mocked_shape
> + mocked_shape.getShapeType.return_value = 'com.sun.star.presentation.TitleTextShape'
> + mocked_shape.supportsService.return_value = True
> + mocked_shape.getString.return_value = 'Page Text'
> +
> + # WHEN: _get_text_from_page() is run for slide text
> + text = server._get_text_from_page(slide_no, text_type)
> +
> + # THE: The text is correct
> + assert text == 'Page Text\n'
> +
> +
> +def test_get_text_from_page_title():
> + """
> + Test that the _get_text_from_page() method gives us the text from the titles
> + """
> + # GIVEN: A LibreOfficeServer object and some mocked objects
> + text_type = TextType.Title
> + slide_no = 1
> + server = LibreOfficeServer()
> + server._document = MagicMock()
> + mocked_pages = MagicMock()
> + mocked_page = MagicMock()
> + mocked_shape = MagicMock()
> + server._document.getDrawPages.return_value = mocked_pages
> + mocked_pages.getCount.return_value = 1
> + mocked_pages.getByIndex.return_value = mocked_page
> + mocked_page.getByIndex.return_value = mocked_shape
> + mocked_shape.getShapeType.return_value = 'com.sun.star.presentation.TitleTextShape'
> + mocked_shape.supportsService.return_value = True
> + mocked_shape.getString.return_value = 'Page Title'
> +
> + # WHEN: _get_text_from_page() is run for titles
> + text = server._get_text_from_page(slide_no, text_type)
> +
> + # THEN: The text should be correct
> + assert text == 'Page Title\n'
> +
> +
> +def test_get_text_from_page_notes():
> + """
> + Test that the _get_text_from_page() method gives us the text from the notes
> + """
> + # GIVEN: A LibreOfficeServer object and some mocked objects
> + text_type = TextType.Notes
> + slide_no = 1
> + server = LibreOfficeServer()
> + server._document = MagicMock()
> + mocked_pages = MagicMock()
> + mocked_page = MagicMock()
> + mocked_notes_page = MagicMock()
> + mocked_shape = MagicMock()
> + server._document.getDrawPages.return_value = mocked_pages
> + mocked_pages.getCount.return_value = 1
> + mocked_pages.getByIndex.return_value = mocked_page
> + mocked_page.getNotesPage.return_value = mocked_notes_page
> + mocked_notes_page.getByIndex.return_value = mocked_shape
> + mocked_shape.getShapeType.return_value = 'com.sun.star.presentation.TitleTextShape'
> + mocked_shape.supportsService.return_value = True
> + mocked_shape.getString.return_value = 'Page Notes'
> +
> + # WHEN: _get_text_from_page() is run for titles
> + text = server._get_text_from_page(slide_no, text_type)
> +
> + # THEN: The text should be correct
> + assert text == 'Page Notes\n'
> +
> +
> +def test_shutdown_other_docs():
> + """
> + Test the shutdown method while other documents are open in LibreOffice
> + """
> + def close_docs():
> + server._docs = []
> +
> + # GIVEN: An up an running LibreOfficeServer
> + server = LibreOfficeServer()
> + mocked_doc = MagicMock()
> + mocked_desktop = MagicMock()
> + mocked_docs = MagicMock()
> + mocked_list = MagicMock()
> + mocked_element_doc = MagicMock()
> + server._docs = [mocked_doc]
> + server._desktop = mocked_desktop
> + server._process = MagicMock()
> + mocked_doc.close_presentation.side_effect = close_docs
> + mocked_desktop.getComponents.return_value = mocked_docs
> + mocked_docs.hasElements.return_value = True
> + mocked_docs.createEnumeration.return_value = mocked_list
> + mocked_list.hasMoreElements.side_effect = [True, False]
> + mocked_list.nextElement.return_value = mocked_element_doc
> + mocked_element_doc.getImplementationName.side_effect = [
> + 'org.openlp.Nothing',
> + 'com.sun.star.comp.framework.BackingComp'
> + ]
> +
> + # WHEN: shutdown() is called
> + server.shutdown()
> +
> + # THEN: The right methods are called and everything works
> + mocked_doc.close_presentation.assert_called_once_with()
> + mocked_desktop.getComponents.assert_called_once_with()
> + mocked_docs.hasElements.assert_called_once_with()
> + mocked_docs.createEnumeration.assert_called_once_with()
> + assert mocked_list.hasMoreElements.call_count == 2
> + mocked_list.nextElement.assert_called_once_with()
> + mocked_element_doc.getImplementationName.assert_called_once_with()
> + assert mocked_desktop.terminate.call_count == 0
> + assert server._process.kill.call_count == 0
> +
> +
> +def test_shutdown():
> + """
> + Test the shutdown method
> + """
> + def close_docs():
> + server._docs = []
> +
> + # GIVEN: An up an running LibreOfficeServer
> + server = LibreOfficeServer()
> + mocked_doc = MagicMock()
> + mocked_desktop = MagicMock()
> + mocked_docs = MagicMock()
> + mocked_list = MagicMock()
> + mocked_element_doc = MagicMock()
> + server._docs = [mocked_doc]
> + server._desktop = mocked_desktop
> + server._process = MagicMock()
> + mocked_doc.close_presentation.side_effect = close_docs
> + mocked_desktop.getComponents.return_value = mocked_docs
> + mocked_docs.hasElements.return_value = True
> + mocked_docs.createEnumeration.return_value = mocked_list
> + mocked_list.hasMoreElements.side_effect = [True, False]
> + mocked_list.nextElement.return_value = mocked_element_doc
> + mocked_element_doc.getImplementationName.return_value = 'com.sun.star.comp.framework.BackingComp'
> +
> + # WHEN: shutdown() is called
> + server.shutdown()
> +
> + # THEN: The right methods are called and everything works
> + mocked_doc.close_presentation.assert_called_once_with()
> + mocked_desktop.getComponents.assert_called_once_with()
> + mocked_docs.hasElements.assert_called_once_with()
> + mocked_docs.createEnumeration.assert_called_once_with()
> + assert mocked_list.hasMoreElements.call_count == 2
> + mocked_list.nextElement.assert_called_once_with()
> + mocked_element_doc.getImplementationName.assert_called_once_with()
> + mocked_desktop.terminate.assert_called_once_with()
> + server._process.kill.assert_called_once_with()
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_load_presentation_exception(mocked_uno):
> + """
> + Test the load_presentation() method when an exception occurs
> + """
> + # GIVEN: A LibreOfficeServer object
> + presentation_file = '/path/to/presentation.odp'
> + screen_number = 1
> + server = LibreOfficeServer()
> + mocked_desktop = MagicMock()
> + mocked_uno.systemPathToFileUrl.side_effect = lambda x: x
> + server._desktop = mocked_desktop
> + mocked_desktop.loadComponentFromURL.side_effect = Exception()
> +
> + # WHEN: load_presentation() is called
> + with patch.object(server, '_create_property') as mocked_create_property:
> + mocked_create_property.side_effect = lambda x, y: {x: y}
> + result = server.load_presentation(presentation_file, screen_number)
> +
> + # THEN: A presentation is loaded
> + assert result is False
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_load_presentation(mocked_uno):
> + """
> + Test the load_presentation() method
> + """
> + # GIVEN: A LibreOfficeServer object
> + presentation_file = '/path/to/presentation.odp'
> + screen_number = 1
> + server = LibreOfficeServer()
> + mocked_desktop = MagicMock()
> + mocked_document = MagicMock()
> + mocked_presentation = MagicMock()
> + mocked_uno.systemPathToFileUrl.side_effect = lambda x: x
> + server._desktop = mocked_desktop
> + mocked_desktop.loadComponentFromURL.return_value = mocked_document
> + mocked_document.getPresentation.return_value = mocked_presentation
> +
> + # WHEN: load_presentation() is called
> + with patch.object(server, '_create_property') as mocked_create_property:
> + mocked_create_property.side_effect = lambda x, y: {x: y}
> + result = server.load_presentation(presentation_file, screen_number)
> +
> + # THEN: A presentation is loaded
> + assert result is True
> + mocked_uno.systemPathToFileUrl.assert_called_once_with(presentation_file)
> + mocked_create_property.assert_called_once_with('Hidden', True)
> + mocked_desktop.loadComponentFromURL.assert_called_once_with(
> + presentation_file, '_blank', 0, ({'Hidden': True},))
> + assert server._document is mocked_document
> + mocked_document.getPresentation.assert_called_once_with()
> + assert server._presentation is mocked_presentation
> + assert server._presentation.Display == screen_number
> + assert server._control is None
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +def test_extract_thumbnails_no_pages(mocked_uno):
> + """
> + Test the extract_thumbnails() method when there are no pages
> + """
> + # GIVEN: A LibreOfficeServer instance
> + temp_folder = '/tmp'
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + server._document = mocked_document
> + mocked_uno.systemPathToFileUrl.side_effect = lambda x: x
> + mocked_document.getDrawPages.return_value = None
> +
> + # WHEN: The extract_thumbnails() method is called
> + with patch.object(server, '_create_property') as mocked_create_property:
> + mocked_create_property.side_effect = lambda x, y: {x: y}
> + thumbnails = server.extract_thumbnails(temp_folder)
> +
> + # THEN: Thumbnails have been extracted
> + mocked_uno.systemPathToFileUrl.assert_called_once_with(temp_folder)
> + mocked_create_property.assert_called_once_with('FilterName', 'impress_png_Export')
> + mocked_document.getDrawPages.assert_called_once_with()
> + assert thumbnails == []
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.uno')
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.os')
> +def test_extract_thumbnails(mocked_os, mocked_uno):
> + """
> + Test the extract_thumbnails() method
> + """
> + # GIVEN: A LibreOfficeServer instance
> + temp_folder = '/tmp'
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + mocked_pages = MagicMock()
> + mocked_page_1 = MagicMock()
> + mocked_page_2 = MagicMock()
> + mocked_controller = MagicMock()
> + server._document = mocked_document
> + mocked_uno.systemPathToFileUrl.side_effect = lambda x: x
> + mocked_document.getDrawPages.return_value = mocked_pages
> + mocked_os.path.isdir.return_value = False
> + mocked_pages.getCount.return_value = 2
> + mocked_pages.getByIndex.side_effect = [mocked_page_1, mocked_page_2]
> + mocked_document.getCurrentController.return_value = mocked_controller
> + mocked_os.path.join.side_effect = lambda *x: '/'.join(x)
> +
> + # WHEN: The extract_thumbnails() method is called
> + with patch.object(server, '_create_property') as mocked_create_property:
> + mocked_create_property.side_effect = lambda x, y: {x: y}
> + thumbnails = server.extract_thumbnails(temp_folder)
> +
> + # THEN: Thumbnails have been extracted
> + mocked_uno.systemPathToFileUrl.assert_called_once_with(temp_folder)
> + mocked_create_property.assert_called_once_with('FilterName', 'impress_png_Export')
> + mocked_document.getDrawPages.assert_called_once_with()
> + mocked_pages.getCount.assert_called_once_with()
> + assert mocked_pages.getByIndex.call_args_list == [call(0), call(1)]
> + assert mocked_controller.setCurrentPage.call_args_list == \
> + [call(mocked_page_1), call(mocked_page_2)]
> + assert mocked_document.storeToURL.call_args_list == \
> + [call('/tmp/1.png', ({'FilterName': 'impress_png_Export'},)),
> + call('/tmp/2.png', ({'FilterName': 'impress_png_Export'},))]
> + assert thumbnails == ['/tmp/1.png', '/tmp/2.png']
> +
> +
> +def test_get_titles_and_notes():
> + """
> + Test the get_titles_and_notes() method
> + """
> + # GIVEN: A LibreOfficeServer object and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + mocked_pages = MagicMock()
> + server._document = mocked_document
> + mocked_document.getDrawPages.return_value = mocked_pages
> + mocked_pages.getCount.return_value = 2
> +
> + # WHEN: get_titles_and_notes() is called
> + with patch.object(server, '_get_text_from_page') as mocked_get_text_from_page:
> + mocked_get_text_from_page.side_effect = [
> + 'OpenLP on Mac OS X',
> + '',
> + '',
> + 'Installing is a drag-and-drop affair'
> + ]
> + titles, notes = server.get_titles_and_notes()
> +
> + # THEN: The right calls are made and the right stuff returned
> + mocked_document.getDrawPages.assert_called_once_with()
> + mocked_pages.getCount.assert_called_once_with()
> + assert mocked_get_text_from_page.call_count == 4
> + expected_calls = [
> + call(1, TextType.Title), call(1, TextType.Notes),
> + call(2, TextType.Title), call(2, TextType.Notes),
> + ]
> + assert mocked_get_text_from_page.call_args_list == expected_calls
> + assert titles == ['OpenLP on Mac OS X\n', '\n'], titles
> + assert notes == [' ', 'Installing is a drag-and-drop affair'], notes
> +
> +
> +def test_close_presentation():
> + """
> + Test that closing the presentation cleans things up correctly
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + mocked_presentation = MagicMock()
> + server._document = mocked_document
> + server._presentation = mocked_presentation
> +
> + # WHEN: close_presentation() is called
> + server.close_presentation()
> +
> + # THEN: The presentation and document should be closed
> + mocked_presentation.end.assert_called_once_with()
> + mocked_document.dispose.assert_called_once_with()
> + assert server._document is None
> + assert server._presentation is None
> +
> +
> +def test_is_loaded_no_objects():
> + """
> + Test the is_loaded() method when there's no document or presentation
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> +
> + # WHEN: The is_loaded() method is called
> + result = server.is_loaded()
> +
> + # THEN: The result should be false
> + assert result is False
> +
> +
> +def test_is_loaded_no_presentation():
> + """
> + Test the is_loaded() method when there's no presentation
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + server._document = mocked_document
> + server._presentation = MagicMock()
> + mocked_document.getPresentation.return_value = None
> +
> + # WHEN: The is_loaded() method is called
> + result = server.is_loaded()
> +
> + # THEN: The result should be false
> + assert result is False
> + mocked_document.getPresentation.assert_called_once_with()
> +
> +
> +def test_is_loaded_exception():
> + """
> + Test the is_loaded() method when an exception is thrown
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + server._document = mocked_document
> + server._presentation = MagicMock()
> + mocked_document.getPresentation.side_effect = Exception()
> +
> + # WHEN: The is_loaded() method is called
> + result = server.is_loaded()
> +
> + # THEN: The result should be false
> + assert result is False
> + mocked_document.getPresentation.assert_called_once_with()
> +
> +
> +def test_is_loaded():
> + """
> + Test the is_loaded() method
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + mocked_presentation = MagicMock()
> + server._document = mocked_document
> + server._presentation = mocked_presentation
> + mocked_document.getPresentation.return_value = mocked_presentation
> +
> + # WHEN: The is_loaded() method is called
> + result = server.is_loaded()
> +
> + # THEN: The result should be false
> + assert result is True
> + mocked_document.getPresentation.assert_called_once_with()
> +
> +
> +def test_is_active_not_loaded():
> + """
> + Test is_active() when is_loaded() returns False
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> +
> + # WHEN: is_active() is called with is_loaded() returns False
> + with patch.object(server, 'is_loaded') as mocked_is_loaded:
> + mocked_is_loaded.return_value = False
> + result = server.is_active()
> +
> + # THEN: It should have returned False
> + assert result is False
> +
> +
> +def test_is_active_no_control():
> + """
> + Test is_active() when is_loaded() returns True but there's no control
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> +
> + # WHEN: is_active() is called with is_loaded() returns False
> + with patch.object(server, 'is_loaded') as mocked_is_loaded:
> + mocked_is_loaded.return_value = True
> + result = server.is_active()
> +
> + # THEN: The result should be False
> + assert result is False
> + mocked_is_loaded.assert_called_once_with()
> +
> +
> +def test_is_active():
> + """
> + Test is_active()
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> + mocked_control.isRunning.return_value = True
> +
> + # WHEN: is_active() is called with is_loaded() returns False
> + with patch.object(server, 'is_loaded') as mocked_is_loaded:
> + mocked_is_loaded.return_value = True
> + result = server.is_active()
> +
> + # THEN: The result should be False
> + assert result is True
> + mocked_is_loaded.assert_called_once_with()
> + mocked_control.isRunning.assert_called_once_with()
> +
> +
> +def test_unblank_screen():
> + """
> + Test the unblank_screen() method
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> +
> + # WHEN: unblank_screen() is run
> + server.unblank_screen()
> +
> + # THEN: The resume method should have been called
> + mocked_control.resume.assert_called_once_with()
> +
> +
> +def test_blank_screen():
> + """
> + Test the blank_screen() method
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> +
> + # WHEN: blank_screen() is run
> + server.blank_screen()
> +
> + # THEN: The resume method should have been called
> + mocked_control.blankScreen.assert_called_once_with(0)
> +
> +
> +def test_is_blank_no_control():
> + """
> + Test the is_blank() method when there's no control
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> +
> + # WHEN: is_blank() is called
> + result = server.is_blank()
> +
> + # THEN: It should have returned False
> + assert result is False
> +
> +
> +def test_is_blank_control_is_running():
> + """
> + Test the is_blank() method when the control is running
> + """
> + # GIVEN: A LibreOfficeServer instance and a bunch of mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> + mocked_control.isRunning.return_value = True
> + mocked_control.isPaused.return_value = True
> +
> + # WHEN: is_blank() is called
> + result = server.is_blank()
> +
> + # THEN: It should have returned False
> + assert result is True
> + mocked_control.isRunning.assert_called_once_with()
> + mocked_control.isPaused.assert_called_once_with()
> +
> +
> +def test_stop_presentation():
> + """
> + Test the stop_presentation() method
> + """
> + # GIVEN: A LibreOfficeServer instance and a mocked presentation
> + server = LibreOfficeServer()
> + mocked_presentation = MagicMock()
> + mocked_control = MagicMock()
> + server._presentation = mocked_presentation
> + server._control = mocked_control
> +
> + # WHEN: stop_presentation() is called
> + server.stop_presentation()
> +
> + # THEN: The presentation is ended and the control is removed
> + mocked_presentation.end.assert_called_once_with()
> + assert server._control is None
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.time.sleep')
> +def test_start_presentation_no_control(mocked_sleep):
> + """
> + Test the start_presentation() method when there's no control
> + """
> + # GIVEN: A LibreOfficeServer instance and some mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + mocked_document = MagicMock()
> + mocked_presentation = MagicMock()
> + mocked_controller = MagicMock()
> + mocked_frame = MagicMock()
> + mocked_window = MagicMock()
> + server._document = mocked_document
> + server._presentation = mocked_presentation
> + mocked_document.getCurrentController.return_value = mocked_controller
> + mocked_controller.getFrame.return_value = mocked_frame
> + mocked_frame.getContainerWindow.return_value = mocked_window
> + mocked_presentation.getController.side_effect = [None, mocked_control]
> +
> + # WHEN: start_presentation() is called
> + server.start_presentation()
> +
> + # THEN: The slide number should be correct
> + mocked_document.getCurrentController.assert_called_once_with()
> + mocked_controller.getFrame.assert_called_once_with()
> + mocked_frame.getContainerWindow.assert_called_once_with()
> + mocked_presentation.start.assert_called_once_with()
> + assert mocked_presentation.getController.call_count == 2
> + mocked_sleep.assert_called_once_with(0.1)
> + assert mocked_window.setVisible.call_args_list == [call(True), call(False)]
> + assert server._control is mocked_control
> +
> +
> +def test_start_presentation():
> + """
> + Test the start_presentation() method when there's a control
> + """
> + # GIVEN: A LibreOfficeServer instance and some mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> +
> + # WHEN: start_presentation() is called
> + with patch.object(server, 'goto_slide') as mocked_goto_slide:
> + server.start_presentation()
> +
> + # THEN: The control should have been activated and the first slide selected
> + mocked_control.activate.assert_called_once_with()
> + mocked_goto_slide.assert_called_once_with(1)
> +
> +
> +def test_get_slide_number():
> + """
> + Test the get_slide_number() method
> + """
> + # GIVEN: A LibreOfficeServer instance and some mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + mocked_control.getCurrentSlideIndex.return_value = 3
> + server._control = mocked_control
> +
> + # WHEN: get_slide_number() is called
> + result = server.get_slide_number()
> +
> + # THEN: The slide number should be correct
> + assert result == 4
> +
> +
> +def test_get_slide_count():
> + """
> + Test the get_slide_count() method
> + """
> + # GIVEN: A LibreOfficeServer instance and some mocks
> + server = LibreOfficeServer()
> + mocked_document = MagicMock()
> + mocked_pages = MagicMock()
> + server._document = mocked_document
> + mocked_document.getDrawPages.return_value = mocked_pages
> + mocked_pages.getCount.return_value = 2
> +
> + # WHEN: get_slide_count() is called
> + result = server.get_slide_count()
> +
> + # THEN: The slide count should be correct
> + assert result == 2
> +
> +
> +def test_goto_slide():
> + """
> + Test the goto_slide() method
> + """
> + # GIVEN: A LibreOfficeServer instance and some mocks
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> +
> + # WHEN: goto_slide() is called
> + server.goto_slide(1)
> +
> + # THEN: The slide number should be correct
> + mocked_control.gotoSlideIndex.assert_called_once_with(0)
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.time.sleep')
> +def test_next_step_when_paused(mocked_sleep):
> + """
> + Test the next_step() method when paused
> + """
> + # GIVEN: A LibreOfficeServer instance and a mocked control
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> + mocked_control.isPaused.side_effect = [False, True]
> +
> + # WHEN: next_step() is called
> + server.next_step()
> +
> + # THEN: The correct call should be made
> + mocked_control.gotoNextEffect.assert_called_once_with()
> + mocked_sleep.assert_called_once_with(0.1)
> + assert mocked_control.isPaused.call_count == 2
> + mocked_control.gotoPreviousEffect.assert_called_once_with()
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.time.sleep')
> +def test_next_step(mocked_sleep):
> + """
> + Test the next_step() method when paused
> + """
> + # GIVEN: A LibreOfficeServer instance and a mocked control
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> + mocked_control.isPaused.side_effect = [True, True]
> +
> + # WHEN: next_step() is called
> + server.next_step()
> +
> + # THEN: The correct call should be made
> + mocked_control.gotoNextEffect.assert_called_once_with()
> + mocked_sleep.assert_called_once_with(0.1)
> + assert mocked_control.isPaused.call_count == 1
> + assert mocked_control.gotoPreviousEffect.call_count == 0
> +
> +
> +def test_previous_step():
> + """
> + Test the previous_step() method
> + """
> + # GIVEN: A LibreOfficeServer instance and a mocked control
> + server = LibreOfficeServer()
> + mocked_control = MagicMock()
> + server._control = mocked_control
> +
> + # WHEN: previous_step() is called
> + server.previous_step()
> +
> + # THEN: The correct call should be made
> + mocked_control.gotoPreviousEffect.assert_called_once_with()
> +
> +
> +def test_get_slide_text():
> + """
> + Test the get_slide_text() method
> + """
> + # GIVEN: A LibreOfficeServer instance
> + server = LibreOfficeServer()
> +
> + # WHEN: get_slide_text() is called for a particular slide
> + with patch.object(server, '_get_text_from_page') as mocked_get_text_from_page:
> + mocked_get_text_from_page.return_value = 'OpenLP on Mac OS X'
> + result = server.get_slide_text(5)
> +
> + # THEN: The text should be returned
> + mocked_get_text_from_page.assert_called_once_with(5)
> + assert result == 'OpenLP on Mac OS X'
> +
> +
> +def test_get_slide_notes():
> + """
> + Test the get_slide_notes() method
> + """
> + # GIVEN: A LibreOfficeServer instance
> + server = LibreOfficeServer()
> +
> + # WHEN: get_slide_notes() is called for a particular slide
> + with patch.object(server, '_get_text_from_page') as mocked_get_text_from_page:
> + mocked_get_text_from_page.return_value = 'Installing is a drag-and-drop affair'
> + result = server.get_slide_notes(3)
> +
> + # THEN: The text should be returned
> + mocked_get_text_from_page.assert_called_once_with(3, TextType.Notes)
> + assert result == 'Installing is a drag-and-drop affair'
> +
> +
> +@patch('openlp.plugins.presentations.lib.libreofficeserver.Daemon')
> +def test_main(MockedDaemon):
> + """
> + Test the main() function
> + """
> + # GIVEN: Mocked out Pyro objects
> + mocked_daemon = MagicMock()
> + MockedDaemon.return_value = mocked_daemon
> +
> + # WHEN: main() is run
> + main()
> +
> + # THEN: The correct calls are made
> + MockedDaemon.assert_called_once_with(host='localhost', port=4310)
> + mocked_daemon.register.assert_called_once_with(LibreOfficeServer, 'openlp.libreofficeserver')
> + mocked_daemon.requestLoop.assert_called_once_with()
> + mocked_daemon.close.assert_called_once_with()
>
> === added file 'tests/functional/openlp_plugins/presentations/test_maclocontroller.py'
> --- tests/functional/openlp_plugins/presentations/test_maclocontroller.py 1970-01-01 00:00:00 +0000
> +++ tests/functional/openlp_plugins/presentations/test_maclocontroller.py 2019-05-22 16:50:27 +0000
> @@ -0,0 +1,453 @@
> +# -*- coding: utf-8 -*-
> +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
> +
> +###############################################################################
> +# OpenLP - Open Source Lyrics Projection #
> +# --------------------------------------------------------------------------- #
> +# Copyright (c) 2008-2016 OpenLP Developers #
GPL3 license
> +# --------------------------------------------------------------------------- #
> +# 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; version 2 of the License. #
> +# #
> +# 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, write to the Free Software Foundation, Inc., 59 #
> +# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
> +###############################################################################
> +"""
> +Functional tests to test the Mac LibreOffice class and related methods.
> +"""
> +import shutil
> +from tempfile import mkdtemp
> +from unittest import TestCase
> +from unittest.mock import MagicMock, patch, call
> +
> +from openlp.core.common.settings import Settings
> +from openlp.core.common.path import Path
> +from openlp.plugins.presentations.lib.maclocontroller import MacLOController, MacLODocument
> +from openlp.plugins.presentations.presentationplugin import __default_settings__
> +
> +from tests.helpers.testmixin import TestMixin
> +from tests.utils.constants import TEST_RESOURCES_PATH
> +
> +
> +class TestMacLOController(TestCase, TestMixin):
> + """
> + Test the MacLOController Class
> + """
> +
> + def setUp(self):
> + """
> + Set up the patches and mocks need for all tests.
> + """
> + self.setup_application()
> + self.build_settings()
> + self.mock_plugin = MagicMock()
> + self.temp_folder = mkdtemp()
> + self.mock_plugin.settings_section = self.temp_folder
> +
> + def tearDown(self):
> + """
> + Stop the patches
> + """
> + self.destroy_settings()
> + shutil.rmtree(self.temp_folder)
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server')
> + def test_constructor(self, mocked_start_server):
> + """
> + Test the Constructor from the MacLOController
> + """
> + # GIVEN: No presentation controller
> + controller = None
> +
> + # WHEN: The presentation controller object is created
> + controller = MacLOController(plugin=self.mock_plugin)
> +
> + # THEN: The name of the presentation controller should be correct
> + assert controller.name == 'maclo', \
> + 'The name of the presentation controller should be correct'
> + assert controller.display_name == 'Impress on macOS', \
> + 'The display name of the presentation controller should be correct'
> + mocked_start_server.assert_called_once_with()
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server')
> + @patch('openlp.plugins.presentations.lib.maclocontroller.Proxy')
> + def test_client(self, MockedProxy, mocked_start_server):
> + """
> + Test the client property of the Controller
> + """
> + # GIVEN: A controller without a client and a mocked out Pyro
> + controller = MacLOController(plugin=self.mock_plugin)
> + mocked_client = MagicMock()
> + MockedProxy.return_value = mocked_client
> + mocked_client._pyroConnection = None
> +
> + # WHEN: the client property is called the first time
> + client = controller.client
> +
> + # THEN: a client is created
> + assert client == mocked_client
> + MockedProxy.assert_called_once_with('PYRO:openlp.libreofficeserver@localhost:4310')
> + mocked_client._pyroReconnect.assert_called_once_with()
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server')
> + def test_check_available(self, mocked_start_server):
> + """
> + Test the check_available() method
> + """
> + from openlp.plugins.presentations.lib.maclocontroller import macuno_available
> +
> + # GIVEN: A controller
> + controller = MacLOController(plugin=self.mock_plugin)
> +
> + # WHEN: check_available() is run
> + result = controller.check_available()
> +
> + # THEN: it should return false
> + assert result == macuno_available
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server')
> + def test_start_process(self, mocked_start_server):
> + """
> + Test the start_process() method
> + """
> + # GIVEN: A controller and a client
> + controller = MacLOController(plugin=self.mock_plugin)
> + controller._client = MagicMock()
> +
> + # WHEN: start_process() is called
> + controller.start_process()
> +
> + # THEN: The client's start_process() should have been called
> + controller._client.start_process.assert_called_once_with()
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server')
> + def test_kill(self, mocked_start_server):
> + """
> + Test the kill() method
> + """
> + # GIVEN: A controller and a client
> + controller = MacLOController(plugin=self.mock_plugin)
> + controller._client = MagicMock()
> + controller.server_process = MagicMock()
> +
> + # WHEN: start_process() is called
> + controller.kill()
> +
> + # THEN: The client's start_process() should have been called
> + controller._client.shutdown.assert_called_once_with()
> + controller.server_process.kill.assert_called_once_with()
> +
> +
> +class TestMacLODocument(TestCase):
> + """
> + Test the MacLODocument Class
> + """
> + def setUp(self):
> + mocked_plugin = MagicMock()
> + mocked_plugin.settings_section = 'presentations'
> + Settings().extend_default_settings(__default_settings__)
> + self.file_name = Path(TEST_RESOURCES_PATH) / 'presentations' / 'test.odp'
> + self.mocked_client = MagicMock()
> + with patch('openlp.plugins.presentations.lib.maclocontroller.MacLOController._start_server'):
> + self.controller = MacLOController(mocked_plugin)
> + self.controller._client = self.mocked_client
> + self.document = MacLODocument(self.controller, self.file_name)
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.ScreenList')
> + def test_load_presentation_cannot_load(self, MockedScreenList):
> + """
> + Test the load_presentation() method when the server can't load the presentation
> + """
> + # GIVEN: A document and a mocked client
> + mocked_screen_list = MagicMock()
> + MockedScreenList.return_value = mocked_screen_list
> + mocked_screen_list.current.number = 0
> + self.mocked_client.load_presentation.return_value = False
> +
> + # WHEN: load_presentation() is called
> + result = self.document.load_presentation()
> +
> + # THEN: Stuff should work right
> + self.mocked_client.load_presentation.assert_called_once_with(str(self.file_name), 1)
> + assert result is False
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.ScreenList')
> + def test_load_presentation(self, MockedScreenList):
> + """
> + Test the load_presentation() method
> + """
> + # GIVEN: A document and a mocked client
> + mocked_screen_list = MagicMock()
> + MockedScreenList.return_value = mocked_screen_list
> + mocked_screen_list.current.number = 0
> + self.mocked_client.load_presentation.return_value = True
> +
> + # WHEN: load_presentation() is called
> + with patch.object(self.document, 'create_thumbnails') as mocked_create_thumbnails, \
> + patch.object(self.document, 'create_titles_and_notes') as mocked_create_titles_and_notes:
> + result = self.document.load_presentation()
> +
> + # THEN: Stuff should work right
> + self.mocked_client.load_presentation.assert_called_once_with(str(self.file_name), 1)
> + mocked_create_thumbnails.assert_called_once_with()
> + mocked_create_titles_and_notes.assert_called_once_with()
> + assert result is True
> +
> + def test_create_thumbnails_already_exist(self):
> + """
> + Test the create_thumbnails() method when thumbnails already exist
> + """
> + # GIVEN: thumbnails that exist and a mocked client
> + self.document.check_thumbnails = MagicMock(return_value=True)
> +
> + # WHEN: create_thumbnails() is called
> + self.document.create_thumbnails()
> +
> + # THEN: The method should exit early
> + assert self.mocked_client.extract_thumbnails.call_count == 0
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.delete_file')
> + def test_create_thumbnails(self, mocked_delete_file):
> + """
> + Test the create_thumbnails() method
> + """
> + # GIVEN: thumbnails that don't exist and a mocked client
> + self.document.check_thumbnails = MagicMock(return_value=False)
> + self.mocked_client.extract_thumbnails.return_value = ['thumb1.png', 'thumb2.png']
> +
> + # WHEN: create_thumbnails() is called
> + with patch.object(self.document, 'convert_thumbnail') as mocked_convert_thumbnail, \
> + patch.object(self.document, 'get_temp_folder') as mocked_get_temp_folder:
> + mocked_get_temp_folder.return_value = 'temp'
> + self.document.create_thumbnails()
> +
> + # THEN: The method should complete successfully
> + self.mocked_client.extract_thumbnails.assert_called_once_with('temp')
> + assert mocked_convert_thumbnail.call_args_list == [
> + call(Path('thumb1.png'), 1), call(Path('thumb2.png'), 2)]
> + assert mocked_delete_file.call_args_list == [call(Path('thumb1.png')), call(Path('thumb2.png'))]
> +
> + def test_create_titles_and_notes(self):
> + """
> + Test create_titles_and_notes() method
> + """
> + # GIVEN: mocked client and mocked save_titles_and_notes() method
> + self.mocked_client.get_titles_and_notes.return_value = ('OpenLP', 'This is a note')
> +
> + # WHEN: create_titles_and_notes() is called
> + with patch.object(self.document, 'save_titles_and_notes') as mocked_save_titles_and_notes:
> + self.document.create_titles_and_notes()
> +
> + # THEN save_titles_and_notes should have been called
> + self.mocked_client.get_titles_and_notes.assert_called_once_with()
> + mocked_save_titles_and_notes.assert_called_once_with('OpenLP', 'This is a note')
> +
> + def test_close_presentation(self):
> + """
> + Test the close_presentation() method
> + """
> + # GIVEN: A mocked client and mocked remove_doc() method
> + # WHEN: close_presentation() is called
> + with patch.object(self.controller, 'remove_doc') as mocked_remove_doc:
> + self.document.close_presentation()
> +
> + # THEN: The presentation should have been closed
> + self.mocked_client.close_presentation.assert_called_once_with()
> + mocked_remove_doc.assert_called_once_with(self.document)
> +
> + def test_is_loaded(self):
> + """
> + Test the is_loaded() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.is_loaded.return_value = True
> +
> + # WHEN: is_loaded() is called
> + result = self.document.is_loaded()
> +
> + # THEN: Then the result should be correct
> + assert result is True
> +
> + def test_is_active(self):
> + """
> + Test the is_active() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.is_active.return_value = True
> +
> + # WHEN: is_active() is called
> + result = self.document.is_active()
> +
> + # THEN: Then the result should be correct
> + assert result is True
> +
> + def test_unblank_screen(self):
> + """
> + Test the unblank_screen() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.unblank_screen.return_value = True
> +
> + # WHEN: unblank_screen() is called
> + result = self.document.unblank_screen()
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.unblank_screen.assert_called_once_with()
> + assert result is True
> +
> + def test_blank_screen(self):
> + """
> + Test the blank_screen() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.blank_screen.return_value = True
> +
> + # WHEN: blank_screen() is called
> + self.document.blank_screen()
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.blank_screen.assert_called_once_with()
> +
> + def test_is_blank(self):
> + """
> + Test the is_blank() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.is_blank.return_value = True
> +
> + # WHEN: is_blank() is called
> + result = self.document.is_blank()
> +
> + # THEN: Then the result should be correct
> + assert result is True
> +
> + def test_stop_presentation(self):
> + """
> + Test the stop_presentation() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.stop_presentation.return_value = True
> +
> + # WHEN: stop_presentation() is called
> + self.document.stop_presentation()
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.stop_presentation.assert_called_once_with()
> +
> + @patch('openlp.plugins.presentations.lib.maclocontroller.ScreenList')
> + @patch('openlp.plugins.presentations.lib.maclocontroller.Registry')
> + def test_start_presentation(self, MockedRegistry, MockedScreenList):
> + """
> + Test the start_presentation() method
> + """
> + # GIVEN: a mocked client, and multiple screens
> + mocked_screen_list = MagicMock()
> + mocked_screen_list.__len__.return_value = 2
> + mocked_registry = MagicMock()
> + mocked_main_window = MagicMock()
> + MockedScreenList.return_value = mocked_screen_list
> + MockedRegistry.return_value = mocked_registry
> + mocked_screen_list.screen_list = [0, 1]
> + mocked_registry.get.return_value = mocked_main_window
> +
> + # WHEN: start_presentation() is called
> + self.document.start_presentation()
> +
> + # THEN: The presentation should be started
> + self.mocked_client.start_presentation.assert_called_once_with()
> + mocked_registry.get.assert_called_once_with('main_window')
> + mocked_main_window.activateWindow.assert_called_once_with()
> +
> + def test_get_slide_number(self):
> + """
> + Test the get_slide_number() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.get_slide_number.return_value = 5
> +
> + # WHEN: get_slide_number() is called
> + result = self.document.get_slide_number()
> +
> + # THEN: Then the result should be correct
> + assert result == 5
> +
> + def test_get_slide_count(self):
> + """
> + Test the get_slide_count() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.get_slide_count.return_value = 8
> +
> + # WHEN: get_slide_count() is called
> + result = self.document.get_slide_count()
> +
> + # THEN: Then the result should be correct
> + assert result == 8
> +
> + def test_goto_slide(self):
> + """
> + Test the goto_slide() method
> + """
> + # GIVEN: A mocked client
> + # WHEN: goto_slide() is called
> + self.document.goto_slide(3)
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.goto_slide.assert_called_once_with(3)
> +
> + def test_next_step(self):
> + """
> + Test the next_step() method
> + """
> + # GIVEN: A mocked client
> + # WHEN: next_step() is called
> + self.document.next_step()
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.next_step.assert_called_once_with()
> +
> + def test_previous_step(self):
> + """
> + Test the previous_step() method
> + """
> + # GIVEN: A mocked client
> + # WHEN: previous_step() is called
> + self.document.previous_step()
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.previous_step.assert_called_once_with()
> +
> + def test_get_slide_text(self):
> + """
> + Test the get_slide_text() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.get_slide_text.return_value = 'Some slide text'
> +
> + # WHEN: get_slide_text() is called
> + result = self.document.get_slide_text(1)
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.get_slide_text.assert_called_once_with(1)
> + assert result == 'Some slide text'
> +
> + def test_get_slide_notes(self):
> + """
> + Test the get_slide_notes() method
> + """
> + # GIVEN: A mocked client
> + self.mocked_client.get_slide_notes.return_value = 'This is a note'
> +
> + # WHEN: get_slide_notes() is called
> + result = self.document.get_slide_notes(2)
> +
> + # THEN: Then the result should be correct
> + self.mocked_client.get_slide_notes.assert_called_once_with(2)
> + assert result == 'This is a note'
--
https://code.launchpad.net/~raoul-snyman/openlp/pyro-impress/+merge/367787
Your team OpenLP Core is subscribed to branch lp:openlp.
References