From 7dd341721712e96ac503e86e27dc847b0794f0e8 Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Sun, 29 Dec 2013 19:46:04 +0000 Subject: [PATCH] Added tests for the PdfController. --- .../presentations/lib/pdfcontroller.py | 14 ++- .../presentations/test_mediaitem.py | 11 +- .../presentations/test_pdfcontroller.py | 108 ++++++++++++++++++ tests/resources/presentations/pdf_test1.pdf | Bin 0 -> 12471 bytes 4 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 tests/functional/openlp_plugins/presentations/test_pdfcontroller.py create mode 100644 tests/resources/presentations/pdf_test1.pdf diff --git a/openlp/plugins/presentations/lib/pdfcontroller.py b/openlp/plugins/presentations/lib/pdfcontroller.py index 781bd0af4..ef7e0dd1c 100644 --- a/openlp/plugins/presentations/lib/pdfcontroller.py +++ b/openlp/plugins/presentations/lib/pdfcontroller.py @@ -4,8 +4,8 @@ ############################################################################### # OpenLP - Open Source Lyrics Projection # # --------------------------------------------------------------------------- # -# Copyright (c) 2008-2013 Raoul Snyman # -# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # # Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # # Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # # Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # @@ -104,7 +104,7 @@ class PdfController(PresentationController): # Use the user defined program if given if (Settings().value('presentations/enable_pdf_program')): pdf_program = Settings().value('presentations/pdf_program') - program_type = check_binary(pdf_program) + program_type = self.check_binary(pdf_program) if program_type == 'gs': self.gsbin = pdf_program return True @@ -152,6 +152,9 @@ class PdfController(PresentationController): class PdfDocument(PresentationDocument): """ Class which holds information of a single presentation. + This class is not actually used to present the PDF, instead we convert to + image-serviceitem on the fly and present as such. Therefore some of the 'playback' + functions is not implemented. """ def __init__(self, controller, presentation): """ @@ -291,3 +294,8 @@ quit \n\ log.debug('is_active pdf') return self.is_loaded() and not self.hidden + def get_slide_count(self): + """ + Returns total number of slides + """ + return self.num_pages \ No newline at end of file diff --git a/tests/functional/openlp_plugins/presentations/test_mediaitem.py b/tests/functional/openlp_plugins/presentations/test_mediaitem.py index 5b5c99e78..c9c8fbbf6 100644 --- a/tests/functional/openlp_plugins/presentations/test_mediaitem.py +++ b/tests/functional/openlp_plugins/presentations/test_mediaitem.py @@ -75,11 +75,16 @@ class TestMediaItem(TestCase): presentation_controller.also_supports = [] presentation_viewer_controller = MagicMock() presentation_viewer_controller.enabled.return_value = False + pdf_controller = MagicMock() + pdf_controller.enabled.return_value = True + pdf_controller.supports = ['pdf'] + pdf_controller.also_supports = ['xps'] # Mock the controllers. self.media_item.controllers = { 'Impress': impress_controller, 'Powerpoint': presentation_controller, - 'Powerpoint Viewer': presentation_viewer_controller + 'Powerpoint Viewer': presentation_viewer_controller, + 'Pdf': pdf_controller } # WHEN: Build the file mask. @@ -92,3 +97,7 @@ class TestMediaItem(TestCase): 'The file mask should contain the odp extension') self.assertIn('*.ppt', self.media_item.on_new_file_masks, 'The file mask should contain the ppt extension') + self.assertIn('*.pdf', self.media_item.on_new_file_masks, + 'The file mask should contain the pdf extension') + self.assertIn('*.xps', self.media_item.on_new_file_masks, + 'The file mask should contain the xps extension') diff --git a/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py new file mode 100644 index 000000000..7739af3c8 --- /dev/null +++ b/tests/functional/openlp_plugins/presentations/test_pdfcontroller.py @@ -0,0 +1,108 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2014 Raoul Snyman # +# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan # +# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, # +# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. # +# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, # +# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, # +# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, # +# Frode Woldsund, Martin Zibricky, Patrick Zimmermann # +# --------------------------------------------------------------------------- # +# 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 contains tests for the PdfController +""" +import os +import shutil +from unittest import TestCase, SkipTest +from tempfile import mkstemp, mkdtemp + +from PyQt4 import QtGui + +from openlp.plugins.presentations.lib.pdfcontroller import PdfController, PdfDocument +from tests.functional import MagicMock +from openlp.core.common import Settings +from openlp.core.lib import ScreenList +from tests.utils.constants import TEST_RESOURCES_PATH + +__default_settings__ = { + 'presentations/enable_pdf_program': False +} + +class TestPdfController(TestCase): + """ + Test the PdfController. + """ + def setUp(self): + """ + Set up the components need for all tests. + """ + self.fd, self.ini_file = mkstemp('.ini') + Settings().set_filename(self.ini_file) + self.application = QtGui.QApplication.instance() + ScreenList.create(self.application.desktop()) + Settings().extend_default_settings(__default_settings__) + self.temp_folder = mkdtemp() + self.thumbnail_folder = mkdtemp() + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + del self.application + try: + os.unlink(self.ini_file) + shutil.rmtree(self.thumbnail_folder) + shutil.rmtree(self.temp_folder) + except OSError: + pass + + def constructor_test(self): + """ + Test the Constructor + """ + # GIVEN: No presentation controller + controller = None + + # WHEN: The presentation controller object is created + controller = PdfController(plugin = MagicMock()) + + # THEN: The name of the presentation controller should be correct + self.assertEqual('Pdf', controller.name, 'The name of the presentation controller should be correct') + + def load_pdf_test(self): + """ + Test loading of a Pdf + """ + # GIVEN: A Pdf-file + test_file = os.path.join(TEST_RESOURCES_PATH, 'presentations', 'pdf_test1.pdf') + + # WHEN: The Pdf is loaded + controller = PdfController(plugin = MagicMock()) + if not controller.check_available(): + raise SkipTest('Could not detect mudraw or ghostscript, so skipping PDF test') + controller.temp_folder = self.temp_folder + controller.thumbnail_folder = self.thumbnail_folder + document = PdfDocument(controller, test_file) + loaded = document.load_presentation() + + # THEN: The load should succeed and we should be able to get a pagecount + self.assertTrue(loaded, 'The loading of the PDF should succeed.') + self.assertEqual(3, document.get_slide_count(), 'The pagecount of the PDF should be 3.') diff --git a/tests/resources/presentations/pdf_test1.pdf b/tests/resources/presentations/pdf_test1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..012dc95483300fd0338cb0fb303e7b0e7270d346 GIT binary patch literal 12471 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 literal 0 HcmV?d00001