diff --git a/openlp/core/common/uistrings.py b/openlp/core/common/uistrings.py index d7a4c1c42..7368e5011 100644 --- a/openlp/core/common/uistrings.py +++ b/openlp/core/common/uistrings.py @@ -83,6 +83,8 @@ class UiStrings(object): self.Error = translate('OpenLP.Ui', 'Error') self.Export = translate('OpenLP.Ui', 'Export') self.File = translate('OpenLP.Ui', 'File') + self.FileNotFound = translate('OpenLP.Ui', 'File Not Found') + self.FileNotFoundMessage = translate('OpenLP.Ui', 'File %s not found.\nPlease try selecting it individually.') self.FontSizePtUnit = translate('OpenLP.Ui', 'pt', 'Abbreviated font pointsize unit') self.Help = translate('OpenLP.Ui', 'Help') self.Hours = translate('OpenLP.Ui', 'h', 'The abbreviated unit for hours') diff --git a/openlp/core/lib/__init__.py b/openlp/core/lib/__init__.py index 712e3a0fb..a1ef5bcc1 100644 --- a/openlp/core/lib/__init__.py +++ b/openlp/core/lib/__init__.py @@ -336,6 +336,7 @@ def create_separated_list(string_list): from .registry import Registry +from .filedialog import FileDialog from .screen import ScreenList from .listwidgetwithdnd import ListWidgetWithDnD from .treewidgetwithdnd import TreeWidgetWithDnD @@ -351,4 +352,3 @@ from .dockwidget import OpenLPDockWidget from .imagemanager import ImageManager from .renderer import Renderer from .mediamanageritem import MediaManagerItem - diff --git a/openlp/core/lib/filedialog.py b/openlp/core/lib/filedialog.py new file mode 100644 index 000000000..bac1b5ce2 --- /dev/null +++ b/openlp/core/lib/filedialog.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 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 # +# --------------------------------------------------------------------------- # +# 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 # +############################################################################### + +""" +Provide a work around for a bug in QFileDialog +""" +import logging +import os +from urllib import parse + +from PyQt4 import QtGui + +from openlp.core.common import UiStrings + +log = logging.getLogger(__name__) + +class FileDialog(QtGui.QFileDialog): + """ + Subclass QFileDialog to work round a bug + """ + @staticmethod + def getOpenFileNames(parent, *args, **kwargs): + """ + Reimplement getOpenFileNames to fix the way it returns some file names that url encoded when selecting multiple + files + """ + files = QtGui.QFileDialog.getOpenFileNames(parent, *args, **kwargs) + file_list = [] + for file in files: + if not os.path.exists(file): + log.info('File %s not found. Attempting to unquote.') + file = parse.unquote(file) + if not os.path.exists(file): + log.error('File %s not found.' % file) + QtGui.QMessageBox.information(parent, UiStrings().FileNotFound, + UiStrings().FileNotFoundMessage % file) + continue + log.info('File %s found.') + file_list.append(file) + return file_list \ No newline at end of file diff --git a/openlp/core/lib/mediamanageritem.py b/openlp/core/lib/mediamanageritem.py index 01e16eef3..22f03355f 100644 --- a/openlp/core/lib/mediamanageritem.py +++ b/openlp/core/lib/mediamanageritem.py @@ -36,7 +36,7 @@ import re from PyQt4 import QtCore, QtGui from openlp.core.common import Settings, UiStrings, translate -from openlp.core.lib import OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \ +from openlp.core.lib import FileDialog, OpenLPToolbar, ServiceItem, StringContent, ListWidgetWithDnD, \ ServiceItemContext, Registry from openlp.core.lib.searchedit import SearchEdit from openlp.core.lib.ui import create_widget_action, critical_error_message_box @@ -319,7 +319,7 @@ class MediaManagerItem(QtGui.QWidget): """ Add a file to the list widget to make it available for showing """ - files = QtGui.QFileDialog.getOpenFileNames(self, self.on_new_prompt, + files = FileDialog.getOpenFileNames(self, self.on_new_prompt, Settings().value(self.settings_section + '/last directory'), self.on_new_file_masks) log.info('New files(s) %s', files) if files: diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py index e48395000..cd8c26f7b 100644 --- a/openlp/core/ui/firsttimeform.py +++ b/openlp/core/ui/firsttimeform.py @@ -37,7 +37,7 @@ import urllib.request import urllib.parse import urllib.error from tempfile import gettempdir -from configparser import SafeConfigParser +from configparser import ConfigParser from PyQt4 import QtCore, QtGui @@ -68,7 +68,7 @@ class ThemeScreenshotThread(QtCore.QThread): filename = config.get('theme_%s' % theme, 'filename') screenshot = config.get('theme_%s' % theme, 'screenshot') urllib.request.urlretrieve('%s%s' % (self.parent().web, screenshot), - os.path.join(gettempdir(), 'openlp', screenshot)) + os.path.join(gettempdir(), 'openlp', screenshot)) item = QtGui.QListWidgetItem(title, self.parent().themes_list_widget) item.setData(QtCore.Qt.UserRole, filename) item.setCheckState(QtCore.Qt.Unchecked) @@ -90,14 +90,16 @@ class FirstTimeForm(QtGui.QWizard, Ui_FirstTimeWizard): self.screens = screens # check to see if we have web access self.web = 'http://openlp.org/files/frw/' - self.config = SafeConfigParser() - self.web_access = get_web_page('%s%s' % (self.web, 'download.cfg')) + self.config = ConfigParser() + user_agent = 'OpenLP/' + Registry().get('application').applicationVersion() + self.web_access = get_web_page('%s%s' % (self.web, 'download.cfg'), header=('User-Agent', user_agent)) if self.web_access: files = self.web_access.read() self.config.read_string(files.decode()) self.update_screen_list_combo() self.was_download_cancelled = False self.theme_screenshot_thread = None + self.has_run_wizard = False self.downloading = translate('OpenLP.FirstTimeWizard', 'Downloading %s...') self.cancel_button.clicked.connect(self.on_cancel_button_clicked) self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked) diff --git a/openlp/core/ui/shortcutlistform.py b/openlp/core/ui/shortcutlistform.py index efe876e3e..fb459824d 100644 --- a/openlp/core/ui/shortcutlistform.py +++ b/openlp/core/ui/shortcutlistform.py @@ -74,7 +74,7 @@ class ShortcutListForm(QtGui.QDialog, Ui_ShortcutListDialog): if event.key() == QtCore.Qt.Key_Space: self.keyReleaseEvent(event) elif self.primaryPushButton.isChecked() or self.alternatePushButton.isChecked(): - event.ignore() + self.keyReleaseEvent(event) elif event.key() == QtCore.Qt.Key_Escape: event.accept() self.close() diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py index 8e1838d5d..68f5acceb 100644 --- a/openlp/core/ui/thememanager.py +++ b/openlp/core/ui/thememanager.py @@ -33,13 +33,12 @@ import os import zipfile import shutil import logging -import re from xml.etree.ElementTree import ElementTree, XML from PyQt4 import QtCore, QtGui from openlp.core.common import AppLocation, Settings, check_directory_exists, UiStrings, translate -from openlp.core.lib import ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \ +from openlp.core.lib import FileDialog, ImageSource, OpenLPToolbar, Registry, get_text_file_string, build_icon, \ check_item_selected, create_thumb, validate_thumb from openlp.core.lib.theme import ThemeXML, BackgroundType from openlp.core.lib.ui import critical_error_message_box, create_widget_action @@ -59,7 +58,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): """ super(ThemeManager, self).__init__(parent) Registry().register('theme_manager', self) - Registry().register_function('bootstrap_initialise', self.load_first_time_themes) + Registry().register_function('bootstrap_initialise', self.initialise) Registry().register_function('bootstrap_post_set_up', self._push_themes) self.settings_section = 'themes' self.theme_form = ThemeForm(self) @@ -135,15 +134,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): Registry().register_function('theme_update_global', self.change_global_from_tab) # Variables self.theme_list = [] - self.path = AppLocation.get_section_data_path(self.settings_section) - check_directory_exists(self.path) - self.thumb_path = os.path.join(self.path, 'thumbnails') - check_directory_exists(self.thumb_path) - self.theme_form.path = self.path self.old_background_image = None - self.bad_v1_name_chars = re.compile(r'[%+\[\]]') - # Last little bits of setting up - self.global_theme = Settings().value(self.settings_section + '/global theme') def check_list_state(self, item): """ @@ -374,7 +365,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from those files. This process will load both OpenLP version 1 and version 2 themes. """ - files = QtGui.QFileDialog.getOpenFileNames(self, + files = FileDialog.getOpenFileNames(self, translate('OpenLP.ThemeManager', 'Select Theme Import File'), Settings().value(self.settings_section + '/last directory import'), translate('OpenLP.ThemeManager', 'OpenLP Themes (*.theme *.otz)')) @@ -392,6 +383,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): """ Imports any themes on start up and makes sure there is at least one theme """ + log.debug('load_first_time_themes called') self.application.set_busy_cursor() files = AppLocation.get_files(self.settings_section, '.otz') for theme_file in files: @@ -410,8 +402,8 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): def load_themes(self): """ - Loads the theme lists and triggers updates across the whole system - using direct calls or core functions and events for the plugins. + Loads the theme lists and triggers updates across the whole system using direct calls or core functions and + events for the plugins. The plugins will call back in to get the real list if they want it. """ log.debug('Load themes from dir') @@ -636,18 +628,18 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): self.main_window.finished_progress_bar() self.load_themes() - def generate_image(self, theme_data, forcePage=False): + def generate_image(self, theme_data, force_page=False): """ Call the renderer to build a Sample Image ``theme_data`` The theme to generated a preview for. - ``forcePage`` + ``force_page`` Flag to tell message lines per page need to be generated. """ log.debug('generate_image \n%s ', theme_data) - return self.renderer.generate_preview(theme_data, forcePage) + return self.renderer.generate_preview(theme_data, force_page) def get_preview_image(self, theme): """ @@ -672,7 +664,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): theme.extend_image_filename(path) return theme - def _validate_theme_action(self, select_text, confirm_title, confirm_text, testPlugin=True, confirm=True): + def _validate_theme_action(self, select_text, confirm_title, confirm_text, test_plugin=True, confirm=True): """ Check to see if theme has been selected and the destructive action is allowed. @@ -694,7 +686,7 @@ class ThemeManager(QtGui.QWidget, ThemeManagerHelper): message=translate('OpenLP.ThemeManager', 'You are unable to delete the default theme.')) return False # check for use in the system else where. - if testPlugin: + if test_plugin: for plugin in self.plugin_manager.plugins: if plugin.uses_theme(theme): critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'), diff --git a/openlp/core/ui/thememanagerhelper.py b/openlp/core/ui/thememanagerhelper.py index 8fa02bde5..bc04b3bd9 100644 --- a/openlp/core/ui/thememanagerhelper.py +++ b/openlp/core/ui/thememanagerhelper.py @@ -29,10 +29,34 @@ """ The Theme Controller helps manages adding, deleteing and modifying of themes. """ +import logging +import os + +from openlp.core.common import AppLocation, Settings, check_directory_exists + +log = logging.getLogger(__name__) class ThemeManagerHelper(object): """ Manages the non ui theme functions. """ - pass \ No newline at end of file + def initialise(self): + """ + Setup the manager + """ + log.debug('initialise called') + self.global_theme = Settings().value(self.settings_section + '/global theme') + self.build_theme_path() + self.load_first_time_themes() + + def build_theme_path(self): + """ + Set up the theme path variables + """ + log.debug('build theme path called') + self.path = AppLocation.get_section_data_path(self.settings_section) + check_directory_exists(self.path) + self.thumb_path = os.path.join(self.path, 'thumbnails') + check_directory_exists(self.thumb_path) + self.theme_form.path = self.path \ No newline at end of file diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py index 6ceb592f4..f19d88ba9 100644 --- a/openlp/core/utils/__init__.py +++ b/openlp/core/utils/__init__.py @@ -40,6 +40,7 @@ import sys import urllib.request import urllib.error import urllib.parse +from random import randint from PyQt4 import QtGui, QtCore @@ -61,10 +62,29 @@ APPLICATION_VERSION = {} IMAGES_FILTER = None ICU_COLLATOR = None UNO_CONNECTION_TYPE = 'pipe' -#UNO_CONNECTION_TYPE = u'socket' CONTROL_CHARS = re.compile(r'[\x00-\x1F\x7F-\x9F]', re.UNICODE) INVALID_FILE_CHARS = re.compile(r'[\\/:\*\?"<>\|\+\[\]%]', re.UNICODE) DIGITS_OR_NONDIGITS = re.compile(r'\d+|\D+', re.UNICODE) +USER_AGENTS = { + 'win32': [ + 'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36', + 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36' + ], + 'darwin': [ + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_3) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.43 Safari/537.31', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11', + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_8) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11', + ], + 'linux2': [ + 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.22 (KHTML, like Gecko) Ubuntu Chromium/25.0.1364.160 Chrome/25.0.1364.160 Safari/537.22', + 'Mozilla/5.0 (X11; CrOS armv7l 2913.260.0) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.99 Safari/537.11', + 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.27 (KHTML, like Gecko) Chrome/26.0.1389.0 Safari/537.27' + ], + 'default': [ + 'Mozilla/5.0 (X11; NetBSD amd64; rv:18.0) Gecko/20130120 Firefox/18.0' + ] +} class VersionThread(QtCore.QThread): @@ -298,6 +318,17 @@ def delete_file(file_path_name): return False +def _get_user_agent(): + """ + Return a user agent customised for the platform the user is on. + """ + browser_list = USER_AGENTS.get(sys.platform, None) + if not browser_list: + browser_list = USER_AGENTS['default'] + random_index = randint(0, len(browser_list) - 1) + return browser_list[random_index] + + def get_web_page(url, header=None, update_openlp=False): """ Attempts to download the webpage at url and returns that page or None. @@ -318,6 +349,9 @@ def get_web_page(url, header=None, update_openlp=False): if not url: return None req = urllib.request.Request(url) + if not header or header[0].lower() != 'user-agent': + user_agent = _get_user_agent() + req.add_header('User-Agent', user_agent) if header: req.add_header(header[0], header[1]) page = None diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py index 22ff6185a..7c9c9808f 100644 --- a/openlp/plugins/bibles/lib/http.py +++ b/openlp/plugins/bibles/lib/http.py @@ -379,7 +379,7 @@ class BSExtract(object): send_error_message('parse') return None content = content.find_all('li') - return [book.contents[0].contents[0] for book in content] + return [book.contents[0].contents[0] for book in content if len(book.contents[0].contents)] def _get_application(self): """ diff --git a/openlp/plugins/presentations/lib/messagelistener.py b/openlp/plugins/presentations/lib/messagelistener.py index 43f93cf2c..259e65281 100644 --- a/openlp/plugins/presentations/lib/messagelistener.py +++ b/openlp/plugins/presentations/lib/messagelistener.py @@ -281,7 +281,7 @@ class Controller(object): class MessageListener(object): """ - This is the Presentation listener who acts on events from the slide controller and passes the messages on the the + This is the Presentation listener who acts on events from the slide controller and passes the messages on the correct presentation handlers """ log.info('Message Listener loaded') diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py index 5a037ec48..a5e06a599 100644 --- a/openlp/plugins/presentations/lib/presentationcontroller.py +++ b/openlp/plugins/presentations/lib/presentationcontroller.py @@ -337,8 +337,8 @@ class PresentationDocument(object): class PresentationController(object): """ - This class is used to control interactions with presentation applications by creating a runtime environment. This is - a base class for presentation controllers to inherit from. + This class is used to control interactions with presentation applications by creating a runtime environment. + This is a base class for presentation controllers to inherit from. To create a new controller, take a copy of this file and name it so it ends with ``controller.py``, i.e. ``foobarcontroller.py``. Make sure it inherits @@ -386,8 +386,7 @@ class PresentationController(object): """ log.info('PresentationController loaded') - def __init__(self, plugin=None, name='PresentationController', - document_class=PresentationDocument): + def __init__(self, plugin=None, name='PresentationController', document_class=PresentationDocument): """ This is the constructor for the presentationcontroller object. This provides an easy way for descendent plugins to populate common data. This method *must* be overridden, like so:: diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index ed4676929..e14652765 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -39,7 +39,7 @@ import shutil from PyQt4 import QtCore, QtGui from openlp.core.common import AppLocation, UiStrings, check_directory_exists, translate -from openlp.core.lib import Registry, PluginStatus, MediaType, create_separated_list +from openlp.core.lib import FileDialog, Registry, PluginStatus, MediaType, create_separated_list from openlp.core.lib.ui import set_case_insensitive_completer, critical_error_message_box, find_and_set_in_combo_box from openlp.plugins.songs.lib import VerseType, clean_song from openlp.plugins.songs.lib.db import Book, Song, Author, Topic, MediaFile @@ -758,7 +758,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog): Loads file(s) from the filesystem. """ filters = '%s (*)' % UiStrings().AllFiles - filenames = QtGui.QFileDialog.getOpenFileNames(self, + filenames = FileDialog.getOpenFileNames(self, translate('SongsPlugin.EditSongForm', 'Open File(s)'), '', filters) for filename in filenames: item = QtGui.QListWidgetItem(os.path.split(str(filename))[1]) diff --git a/openlp/plugins/songs/forms/songimportform.py b/openlp/plugins/songs/forms/songimportform.py index 2105e5e35..5976b41bc 100644 --- a/openlp/plugins/songs/forms/songimportform.py +++ b/openlp/plugins/songs/forms/songimportform.py @@ -37,7 +37,7 @@ from PyQt4 import QtCore, QtGui from openlp.core.common import UiStrings, translate from openlp.core.common import Settings -from openlp.core.lib import Registry +from openlp.core.lib import FileDialog, Registry from openlp.core.lib.ui import critical_error_message_box from openlp.core.ui.wizard import OpenLPWizard, WizardStrings from openlp.plugins.songs.lib.importer import SongFormat, SongFormatSelect @@ -246,7 +246,7 @@ class SongImportForm(OpenLPWizard): if filters: filters += ';;' filters += '%s (*)' % UiStrings().AllFiles - filenames = QtGui.QFileDialog.getOpenFileNames(self, title, + filenames = FileDialog.getOpenFileNames(self, title, Settings().value(self.plugin.settings_section + '/last directory import'), filters) if filenames: listbox.addItems(filenames) diff --git a/tests/functional/__init__.py b/tests/functional/__init__.py index 3f471cf61..f3b85b9b3 100644 --- a/tests/functional/__init__.py +++ b/tests/functional/__init__.py @@ -11,9 +11,9 @@ import sys from PyQt4 import QtGui if sys.version_info[1] >= 3: - from unittest.mock import patch, MagicMock + from unittest.mock import MagicMock, patch, mock_open else: - from mock import patch, MagicMock + from mock import MagicMock, patch, mock_open # Only one QApplication can be created. Use QtGui.QApplication.instance() when you need to "create" a QApplication. application = QtGui.QApplication([]) diff --git a/tests/functional/openlp_core_common/__init__.py b/tests/functional/openlp_core_common/__init__.py index f87606f07..64d028205 100644 --- a/tests/functional/openlp_core_common/__init__.py +++ b/tests/functional/openlp_core_common/__init__.py @@ -1 +1,28 @@ -__author__ = 'tim' +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 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 # +############################################################################### diff --git a/tests/functional/openlp_core_common/test_applocation.py b/tests/functional/openlp_core_common/test_applocation.py index 0dcb2e6b1..0683bb050 100644 --- a/tests/functional/openlp_core_common/test_applocation.py +++ b/tests/functional/openlp_core_common/test_applocation.py @@ -59,7 +59,6 @@ class TestAppLocation(TestCase): # WHEN: we call AppLocation.get_data_path() data_path = AppLocation.get_data_path() - print(data_path) # THEN: check that all the correct methods were called, and the result is correct mocked_settings.contains.assert_called_with('advanced/data path') diff --git a/tests/functional/openlp_core_lib/test_file_dialog.py b/tests/functional/openlp_core_lib/test_file_dialog.py new file mode 100644 index 000000000..f42a865d7 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_file_dialog.py @@ -0,0 +1,73 @@ +""" +Package to test the openlp.core.lib.filedialog package. +""" +from unittest import TestCase + +from openlp.core.common import UiStrings +from openlp.core.lib.filedialog import FileDialog +from tests.functional import MagicMock, patch + +class TestFileDialog(TestCase): + """ + Test the functions in the :mod:`filedialog` module. + """ + def setUp(self): + self.os_patcher = patch('openlp.core.lib.filedialog.os') + self.qt_gui_patcher = patch('openlp.core.lib.filedialog.QtGui') + self.ui_strings_patcher = patch('openlp.core.lib.filedialog.UiStrings') + self.mocked_os = self.os_patcher.start() + self.mocked_qt_gui = self.qt_gui_patcher.start() + self.mocked_ui_strings = self.ui_strings_patcher.start() + self.mocked_parent = MagicMock() + + def tearDown(self): + self.os_patcher.stop() + self.qt_gui_patcher.stop() + self.ui_strings_patcher.stop() + + def get_open_file_names_canceled_test(self): + """ + Test that FileDialog.getOpenFileNames() returns and empty QStringList when QFileDialog is canceled + (returns an empty QStringList) + """ + self.mocked_os.reset() + + # GIVEN: An empty QStringList as a return value from QFileDialog.getOpenFileNames + self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [] + + # WHEN: FileDialog.getOpenFileNames is called + result = FileDialog.getOpenFileNames(self.mocked_parent) + + # THEN: The returned value should be an empty QStringList and os.path.exists should not have been called + assert not self.mocked_os.path.exists.called + self.assertEqual(result, [], + 'FileDialog.getOpenFileNames should return and empty list when QFileDialog.getOpenFileNames is canceled') + + def returned_file_list_test(self): + """ + Test that FileDialog.getOpenFileNames handles a list of files properly when QFileList.getOpenFileNames + returns a good file name, a urlencoded file name and a non-existing file + """ + self.mocked_os.rest() + self.mocked_qt_gui.reset() + + # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid + # file names. + self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [ + '/Valid File', '/url%20encoded%20file%20%231', '/non-existing'] + self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ + '/Valid File', '/url encoded file #1'] + + # WHEN: FileDialog.getOpenFileNames is called + result = FileDialog.getOpenFileNames(self.mocked_parent) + + # THEN: os.path.exists should have been called with known args. QmessageBox.information should have been + # called. The returned result should corrilate with the input. + self.mocked_os.path.exists.assert_callde_with('/Valid File') + self.mocked_os.path.exists.assert_callde_with('/url%20encoded%20file%20%231') + self.mocked_os.path.exists.assert_callde_with('/url encoded file #1') + self.mocked_os.path.exists.assert_callde_with('/non-existing') + self.mocked_os.path.exists.assert_callde_with('/non-existing') + self.mocked_qt_gui.QmessageBox.information.called_with(self.mocked_parent, UiStrings().FileNotFound, + UiStrings().FileNotFoundMessage % '/non-existing') + self.assertEqual(result, ['/Valid File', '/url encoded file #1'], 'The returned file list is incorrect') \ No newline at end of file diff --git a/tests/functional/openlp_core_lib/test_htmlbuilder.py b/tests/functional/openlp_core_lib/test_htmlbuilder.py index 0ffab5458..fafa277d7 100644 --- a/tests/functional/openlp_core_lib/test_htmlbuilder.py +++ b/tests/functional/openlp_core_lib/test_htmlbuilder.py @@ -3,13 +3,13 @@ Package to test the openlp.core.lib.htmlbuilder module. """ from unittest import TestCase -from mock import MagicMock, patch from PyQt4 import QtCore from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \ build_lyrics_format_css, build_footer_css from openlp.core.lib.theme import HorizontalType, VerticalType +from tests.functional import MagicMock, patch HTML = """ diff --git a/tests/functional/openlp_core_lib/test_theme.py b/tests/functional/openlp_core_lib/test_theme.py index 1ece64b34..abedc230b 100644 --- a/tests/functional/openlp_core_lib/test_theme.py +++ b/tests/functional/openlp_core_lib/test_theme.py @@ -62,9 +62,11 @@ class TestTheme(TestCase): # THEN: We should get some default behaviours self.assertTrue(default_theme.background_border_color == '#000000', 'The theme should have a black border') - self.assertTrue(default_theme.background_type == 'solid', 'There theme should have a solid backgrounds') + self.assertTrue(default_theme.background_type == 'solid', 'The theme should have a solid backgrounds') self.assertTrue(default_theme.display_vertical_align == 0, - 'There theme should have display_vertical_align of 0') + 'The theme should have a display_vertical_align of 0') self.assertTrue(default_theme.font_footer_name == "Arial", - 'There theme should has font_footer_name of Arial') - self.assertTrue(default_theme.font_main_bold is False, 'There theme should has font_main_bold of false') \ No newline at end of file + 'The theme should have a font_footer_name of Arial') + self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false') + self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables') + diff --git a/tests/functional/openlp_core_utils/test_utils.py b/tests/functional/openlp_core_utils/test_utils.py index dfc31b245..d9cdd9582 100644 --- a/tests/functional/openlp_core_utils/test_utils.py +++ b/tests/functional/openlp_core_utils/test_utils.py @@ -32,14 +32,74 @@ Functional tests to test the AppLocation class and related methods. from unittest import TestCase from openlp.core.utils import clean_filename, get_filesystem_encoding, get_locale_key, \ - get_natural_key, split_filename -from tests.functional import patch + get_natural_key, split_filename, _get_user_agent, get_web_page, get_uno_instance, add_actions +from tests.functional import MagicMock, patch class TestUtils(TestCase): """ A test suite to test out various methods around the AppLocation class. """ + def add_actions_empty_list_test(self): + """ + Test that no actions are added when the list is empty + """ + # GIVEN: a mocked action list, and an empty list + mocked_target = MagicMock() + empty_list = [] + + # WHEN: The empty list is added to the mocked target + add_actions(mocked_target, empty_list) + + # THEN: The add method on the mocked target is never called + self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called') + self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called') + + def add_actions_none_action_test(self): + """ + Test that a separator is added when a None action is in the list + """ + # GIVEN: a mocked action list, and a list with None in it + mocked_target = MagicMock() + separator_list = [None] + + # WHEN: The list is added to the mocked target + add_actions(mocked_target, separator_list) + + # THEN: The addSeparator method is called, but the addAction method is never called + mocked_target.addSeparator.assert_called_with() + self.assertEqual(0, mocked_target.addAction.call_count, 'addAction method should not have been called') + + def add_actions_add_action_test(self): + """ + Test that an action is added when a valid action is in the list + """ + # GIVEN: a mocked action list, and a list with an action in it + mocked_target = MagicMock() + action_list = ['action'] + + # WHEN: The list is added to the mocked target + add_actions(mocked_target, action_list) + + # THEN: The addSeparator method is not called, and the addAction method is called + self.assertEqual(0, mocked_target.addSeparator.call_count, 'addSeparator method should not have been called') + mocked_target.addAction.assert_called_with('action') + + def add_actions_action_and_none_test(self): + """ + Test that an action and a separator are added when a valid action and None are in the list + """ + # GIVEN: a mocked action list, and a list with an action and None in it + mocked_target = MagicMock() + action_list = ['action', None] + + # WHEN: The list is added to the mocked target + add_actions(mocked_target, action_list) + + # THEN: The addSeparator method is called, and the addAction method is called + mocked_target.addSeparator.assert_called_with() + mocked_target.addAction.assert_called_with('action') + def get_filesystem_encoding_sys_function_not_called_test(self): """ Test the get_filesystem_encoding() function does not call the sys.getdefaultencoding() function @@ -153,3 +213,211 @@ class TestUtils(TestCase): # THEN: We get a properly sorted list self.assertEqual(['1st item', 'item 3b', 'item 10a'], sorted_list, 'Numbers should be sorted naturally') + + def get_uno_instance_pipe_test(self): + """ + Test that when the UNO connection type is "pipe" the resolver is given the "pipe" URI + """ + # GIVEN: A mock resolver object and UNO_CONNECTION_TYPE is "pipe" + mock_resolver = MagicMock() + + # WHEN: get_uno_instance() is called + get_uno_instance(mock_resolver) + + # THEN: the resolve method is called with the correct argument + mock_resolver.resolve.assert_called_with('uno:pipe,name=openlp_pipe;urp;StarOffice.ComponentContext') + + def get_user_agent_linux_test(self): + """ + Test that getting a user agent on Linux returns a user agent suitable for Linux + """ + with patch('openlp.core.utils.sys') as mocked_sys: + + # GIVEN: The system is Linux + mocked_sys.platform = 'linux2' + + # WHEN: We call _get_user_agent() + user_agent = _get_user_agent() + + # THEN: The user agent is a Linux (or ChromeOS) user agent + result = 'Linux' in user_agent or 'CrOS' in user_agent + self.assertTrue(result, u'The user agent should be a valid Linux user agent') + + def get_user_agent_windows_test(self): + """ + Test that getting a user agent on Windows returns a user agent suitable for Windows + """ + with patch('openlp.core.utils.sys') as mocked_sys: + + # GIVEN: The system is Linux + mocked_sys.platform = 'win32' + + # WHEN: We call _get_user_agent() + user_agent = _get_user_agent() + + # THEN: The user agent is a Linux (or ChromeOS) user agent + self.assertIn('Windows', user_agent, u'The user agent should be a valid Windows user agent') + + def get_user_agent_macos_test(self): + """ + Test that getting a user agent on OS X returns a user agent suitable for OS X + """ + with patch('openlp.core.utils.sys') as mocked_sys: + + # GIVEN: The system is Linux + mocked_sys.platform = 'darwin' + + # WHEN: We call _get_user_agent() + user_agent = _get_user_agent() + + # THEN: The user agent is a Linux (or ChromeOS) user agent + self.assertIn('Mac OS X', user_agent, u'The user agent should be a valid OS X user agent') + + def get_user_agent_default_test(self): + """ + Test that getting a user agent on a non-Linux/Windows/OS X platform returns the default user agent + """ + with patch('openlp.core.utils.sys') as mocked_sys: + + # GIVEN: The system is Linux + mocked_sys.platform = 'freebsd' + + # WHEN: We call _get_user_agent() + user_agent = _get_user_agent() + + # THEN: The user agent is a Linux (or ChromeOS) user agent + self.assertIn('NetBSD', user_agent, u'The user agent should be the default user agent') + + def get_web_page_no_url_test(self): + """ + Test that sending a URL of None to the get_web_page method returns None + """ + # GIVEN: A None url + test_url = None + + # WHEN: We try to get the test URL + result = get_web_page(test_url) + + # THEN: None should be returned + self.assertIsNone(result, 'The return value of get_web_page should be None') + + def get_web_page_test(self): + """ + Test that the get_web_page method works correctly + """ + with patch('openlp.core.utils.urllib.request.Request') as MockRequest, \ + patch('openlp.core.utils.urllib.request.urlopen') as mock_urlopen, \ + patch('openlp.core.utils._get_user_agent') as mock_get_user_agent, \ + patch('openlp.core.utils.Registry') as MockRegistry: + # GIVEN: Mocked out objects and a fake URL + mocked_request_object = MagicMock() + MockRequest.return_value = mocked_request_object + mocked_page_object = MagicMock() + mock_urlopen.return_value = mocked_page_object + mock_get_user_agent.return_value = 'user_agent' + fake_url = 'this://is.a.fake/url' + + # WHEN: The get_web_page() method is called + returned_page = get_web_page(fake_url) + + # THEN: The correct methods are called with the correct arguments and a web page is returned + MockRequest.assert_called_with(fake_url) + mocked_request_object.add_header.assert_called_with('User-Agent', 'user_agent') + self.assertEqual(1, mocked_request_object.add_header.call_count, + 'There should only be 1 call to add_header') + mock_get_user_agent.assert_called_with() + mock_urlopen.assert_called_with(mocked_request_object) + mocked_page_object.geturl.assert_called_with() + self.assertEqual(0, MockRegistry.call_count, 'The Registry() object should have never been called') + self.assertEqual(mocked_page_object, returned_page, 'The returned page should be the mock object') + + def get_web_page_with_header_test(self): + """ + Test that adding a header to the call to get_web_page() adds the header to the request + """ + with patch('openlp.core.utils.urllib.request.Request') as MockRequest, \ + patch('openlp.core.utils.urllib.request.urlopen') as mock_urlopen, \ + patch('openlp.core.utils._get_user_agent') as mock_get_user_agent: + # GIVEN: Mocked out objects, a fake URL and a fake header + mocked_request_object = MagicMock() + MockRequest.return_value = mocked_request_object + mocked_page_object = MagicMock() + mock_urlopen.return_value = mocked_page_object + mock_get_user_agent.return_value = 'user_agent' + fake_url = 'this://is.a.fake/url' + fake_header = ('Fake-Header', 'fake value') + + # WHEN: The get_web_page() method is called + returned_page = get_web_page(fake_url, header=fake_header) + + # THEN: The correct methods are called with the correct arguments and a web page is returned + MockRequest.assert_called_with(fake_url) + mocked_request_object.add_header.assert_called_with(fake_header[0], fake_header[1]) + self.assertEqual(2, mocked_request_object.add_header.call_count, + 'There should only be 2 calls to add_header') + mock_get_user_agent.assert_called_with() + mock_urlopen.assert_called_with(mocked_request_object) + mocked_page_object.geturl.assert_called_with() + self.assertEqual(mocked_page_object, returned_page, 'The returned page should be the mock object') + + def get_web_page_with_user_agent_in_headers_test(self): + """ + Test that adding a user agent in the header when calling get_web_page() adds that user agent to the request + """ + with patch('openlp.core.utils.urllib.request.Request') as MockRequest, \ + patch('openlp.core.utils.urllib.request.urlopen') as mock_urlopen, \ + patch('openlp.core.utils._get_user_agent') as mock_get_user_agent: + # GIVEN: Mocked out objects, a fake URL and a fake header + mocked_request_object = MagicMock() + MockRequest.return_value = mocked_request_object + mocked_page_object = MagicMock() + mock_urlopen.return_value = mocked_page_object + fake_url = 'this://is.a.fake/url' + user_agent_header = ('User-Agent', 'OpenLP/2.1.0') + + # WHEN: The get_web_page() method is called + returned_page = get_web_page(fake_url, header=user_agent_header) + + # THEN: The correct methods are called with the correct arguments and a web page is returned + MockRequest.assert_called_with(fake_url) + mocked_request_object.add_header.assert_called_with(user_agent_header[0], user_agent_header[1]) + self.assertEqual(1, mocked_request_object.add_header.call_count, + 'There should only be 1 call to add_header') + self.assertEqual(0, mock_get_user_agent.call_count, '_get_user_agent should not have been called') + mock_urlopen.assert_called_with(mocked_request_object) + mocked_page_object.geturl.assert_called_with() + self.assertEqual(mocked_page_object, returned_page, 'The returned page should be the mock object') + + def get_web_page_update_openlp_test(self): + """ + Test that passing "update_openlp" as true to get_web_page calls Registry().get('app').process_events() + """ + with patch('openlp.core.utils.urllib.request.Request') as MockRequest, \ + patch('openlp.core.utils.urllib.request.urlopen') as mock_urlopen, \ + patch('openlp.core.utils._get_user_agent') as mock_get_user_agent, \ + patch('openlp.core.utils.Registry') as MockRegistry: + # GIVEN: Mocked out objects, a fake URL + mocked_request_object = MagicMock() + MockRequest.return_value = mocked_request_object + mocked_page_object = MagicMock() + mock_urlopen.return_value = mocked_page_object + mock_get_user_agent.return_value = 'user_agent' + mocked_registry_object = MagicMock() + mocked_application_object = MagicMock() + mocked_registry_object.get.return_value = mocked_application_object + MockRegistry.return_value = mocked_registry_object + fake_url = 'this://is.a.fake/url' + + # WHEN: The get_web_page() method is called + returned_page = get_web_page(fake_url, update_openlp=True) + + # THEN: The correct methods are called with the correct arguments and a web page is returned + MockRequest.assert_called_with(fake_url) + mocked_request_object.add_header.assert_called_with('User-Agent', 'user_agent') + self.assertEqual(1, mocked_request_object.add_header.call_count, + 'There should only be 1 call to add_header') + mock_urlopen.assert_called_with(mocked_request_object) + mocked_page_object.geturl.assert_called_with() + mocked_registry_object.get.assert_called_with('application') + mocked_application_object.process_events.assert_called_with() + self.assertEqual(mocked_page_object, returned_page, 'The returned page should be the mock object') diff --git a/tests/functional/openlp_plugins/bibles/test_http.py b/tests/functional/openlp_plugins/bibles/test_http.py new file mode 100644 index 000000000..c156fad22 --- /dev/null +++ b/tests/functional/openlp_plugins/bibles/test_http.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 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 http module of the Bibles plugin. +""" +from unittest import TestCase +from bs4 import BeautifulSoup + +from tests.functional import patch, MagicMock +from openlp.plugins.bibles.lib.http import BSExtract + +#TODO: Items left to test +# BGExtract +# __init__ +# _remove_elements +# _extract_verse +# _clean_soup +# _extract_verses +# _extract_verses_old +# get_bible_chapter +# get_books_from_http +# _get_application +# CWExtract +# __init__ +# get_bible_chapter +# get_books_from_http +# _get_application +# HTTPBible +# __init__ +# do_import +# get_verses +# get_chapter +# get_books +# get_chapter_count +# get_verse_count +# _get_application +# get_soup_for_bible_ref +# send_error_message + +class TestBSExtract(TestCase): + """ + Test the BSExtractClass + """ + #TODO: Items left to test + # BSExtract + # __init__ + # get_bible_chapter + # get_books_from_http + # _get_application + def setUp(self): + self.get_soup_for_bible_ref_patcher = patch('openlp.plugins.bibles.lib.http.get_soup_for_bible_ref') + self.log_patcher = patch('openlp.plugins.bibles.lib.http.log') + self.send_error_message_patcher = patch('openlp.plugins.bibles.lib.http.send_error_message') + self.socket_patcher = patch('openlp.plugins.bibles.lib.http.socket') + self.urllib_patcher = patch('openlp.plugins.bibles.lib.http.urllib') + + self.mock_get_soup_for_bible_ref = self.get_soup_for_bible_ref_patcher.start() + self.mock_log = self.log_patcher.start() + self.mock_send_error_message = self.send_error_message_patcher.start() + self.mock_socket = self.socket_patcher.start() + self.mock_soup = MagicMock() + self.mock_urllib = self.urllib_patcher.start() + + def tearDown(self): + self.get_soup_for_bible_ref_patcher.stop() + self.log_patcher.stop() + self.send_error_message_patcher.stop() + self.socket_patcher.stop() + self.urllib_patcher.stop() + + def get_books_from_http_no_soup_test(self): + """ + Test the get_books_from_http method when get_soup_for_bible_ref returns a falsey value + """ + # GIVEN: An instance of BSExtract, and reset log, urllib & get_soup_for_bible_ref mocks + instance = BSExtract() + self.mock_log.debug.reset_mock() + self.mock_urllib.reset_mock() + self.mock_get_soup_for_bible_ref.reset_mock() + + # WHEN: get_books_from_http is called with 'NIV' and get_soup_for_bible_ref returns a None value + self.mock_urllib.parse.quote.return_value = 'NIV' + self.mock_get_soup_for_bible_ref.return_value = None + result = instance.get_books_from_http('NIV') + + # THEN: The rest mocks should be called with known values and get_books_from_http should return None + self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV') + self.mock_urllib.parse.quote.assert_called_once_with(b'NIV') + self.mock_get_soup_for_bible_ref.assert_called_once_with( + 'http://m.bibleserver.com/overlay/selectBook?translation=NIV') + self.assertIsNone(result, + 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value') + + def get_books_from_http_no_content_test(self): + """ + Test the get_books_from_http method when the specified element cannot be found in the tag object returned from + get_soup_for_bible_ref + """ + # GIVEN: An instance of BSExtract, and reset log, urllib, get_soup_for_bible_ref & soup mocks + instance = BSExtract() + self.mock_log.reset_mock() + self.mock_urllib.reset_mock() + self.mock_get_soup_for_bible_ref.reset_mock() + self.mock_soup.reset_mock() + + # WHEN: get_books_from_http is called with 'NIV', get_soup_for_bible_ref returns a mocked_soup object and + # mocked_soup.find returns None + self.mock_urllib.parse.quote.return_value = 'NIV' + self.mock_soup.find.return_value = None + self.mock_get_soup_for_bible_ref.return_value = self.mock_soup + result = instance.get_books_from_http('NIV') + + # THEN: The rest mocks should be called with known values and get_books_from_http should return None + self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV') + self.mock_urllib.parse.quote.assert_called_once_with(b'NIV') + self.mock_get_soup_for_bible_ref.assert_called_once_with( + 'http://m.bibleserver.com/overlay/selectBook?translation=NIV') + self.mock_soup.find.assert_called_once_with('ul') + self.mock_log.error.assert_called_once_with('No books found in the Bibleserver response.') + self.mock_send_error_message.assert_called_once_with('parse') + self.assertIsNone(result, + 'BSExtract.get_books_from_http should return None when get_soup_for_bible_ref returns a false value') + + def get_books_from_http_content_test(self): + """ + Test the get_books_from_http method with sample HTML + Also a regression test for bug #1184869. (The anchor tag in the second list item is empty) + """ + # GIVEN: An instance of BSExtract, and reset log, urllib & get_soup_for_bible_ref mocks and sample HTML data + self.test_html = '' + self.test_soup = BeautifulSoup(self.test_html) + instance = BSExtract() + self.mock_log.reset_mock() + self.mock_urllib.reset_mock() + self.mock_get_soup_for_bible_ref.reset_mock() + self.mock_send_error_message.reset_mock() + + # WHEN: get_books_from_http is called with 'NIV' and get_soup_for_bible_ref returns tag object based on the + # supplied test data. + self.mock_urllib.parse.quote.return_value = 'NIV' + self.mock_get_soup_for_bible_ref.return_value = self.test_soup + result = instance.get_books_from_http('NIV') + + # THEN: The rest mocks should be called with known values and get_books_from_http should return the two books + # in the test data + self.mock_log.debug.assert_called_once_with('BSExtract.get_books_from_http("%s")', 'NIV') + self.mock_urllib.parse.quote.assert_called_once_with(b'NIV') + self.mock_get_soup_for_bible_ref.assert_called_once_with( + 'http://m.bibleserver.com/overlay/selectBook?translation=NIV') + self.assertFalse(self.mock_log.error.called, 'log.error should not have been called') + self.assertFalse(self.mock_send_error_message.called, 'send_error_message should not have been called') + self.assertEquals(result, ['Genesis', 'Leviticus']) diff --git a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py index 1ee4c49f9..40d785f5d 100644 --- a/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_presentationcontroller.py @@ -1,165 +1,181 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 - -############################################################################### -# OpenLP - Open Source Lyrics Projection # -# --------------------------------------------------------------------------- # -# Copyright (c) 2008-2013 Raoul Snyman # -# Portions copyright (c) 2008-2013 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 # -############################################################################### -""" -Functional tests to test the PresentationController and PresentationDocument -classes and related methods. -""" -from unittest import TestCase -import os -from mock import MagicMock, patch, mock_open -from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument - -class TestLibModule(TestCase): - - def setUp(self): - mocked_plugin = MagicMock() - mocked_plugin.settings_section = 'presentations' - self.presentation = PresentationController(mocked_plugin) - self.document = PresentationDocument(self.presentation, '') - - def save_titles_and_notes_test(self): - """ - Test PresentationDocument.save_titles_and_notes method with two valid lists - """ - # GIVEN: two lists of length==2 and a mocked open and get_thumbnail_folder - mocked_open = mock_open() - with patch('builtins.open', mocked_open), \ - patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ - as mocked_get_thumbnail_folder: - titles = ['uno', 'dos'] - notes = ['one', 'two'] - # WHEN: calling save_titles_and_notes - mocked_get_thumbnail_folder.return_value = 'test' - self.document.save_titles_and_notes(titles, notes) - # THEN: the last call to open should have been for slideNotes2.txt - mocked_open.assert_any_call( - os.path.join('test', 'titles.txt'), mode='w') - mocked_open.assert_any_call( - os.path.join('test', 'slideNotes1.txt'), mode='w') - mocked_open.assert_any_call( - os.path.join('test', 'slideNotes2.txt'), mode='w') - self.assertEqual(mocked_open.call_count, 3, - 'There should be exactly three files opened') - mocked_open().writelines.assert_called_once_with(['uno', 'dos']) - mocked_open().write.assert_called_any('one') - mocked_open().write.assert_called_any('two') - - def save_titles_and_notes_with_None_test(self): - """ - Test PresentationDocument.save_titles_and_notes method with no data - """ - # GIVEN: None and an empty list and a mocked open and get_thumbnail_folder - with patch('builtins.open') as mocked_open, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ - as mocked_get_thumbnail_folder: - titles = None - notes = None - # WHEN: calling save_titles_and_notes - mocked_get_thumbnail_folder.return_value = 'test' - self.document.save_titles_and_notes(titles, notes) - # THEN: No file should have been created - self.assertEqual(mocked_open.call_count, 0, - 'No file should be created') - - - def get_titles_and_notes_test(self): - """ - Test PresentationDocument.get_titles_and_notes method - """ - # GIVEN: A mocked open, get_thumbnail_folder and exists - with patch('builtins.open', mock_open(read_data='uno\ndos\n')) as mocked_open, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ - as mocked_get_thumbnail_folder, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: - mocked_get_thumbnail_folder.return_value = 'test' - mocked_exists.return_value = True - # WHEN: calling get_titles_and_notes - result_titles, result_notes = self.document.get_titles_and_notes() - # THEN: it should return two items for the titles and two empty strings for the notes - self.assertIs(type(result_titles), list, - 'result_titles should be of type list') - self.assertEqual(len(result_titles), 2, - 'There should be two items in the titles') - self.assertIs(type(result_notes), list, - 'result_notes should be of type list') - self.assertEqual(len(result_notes), 2, - 'There should be two items in the notes') - self.assertEqual(mocked_open.call_count, 3, - 'Three files should be opened') - mocked_open.assert_any_call(os.path.join('test', 'titles.txt')) - mocked_open.assert_any_call(os.path.join('test', 'slideNotes1.txt')) - mocked_open.assert_any_call(os.path.join('test', 'slideNotes2.txt')) - self.assertEqual(mocked_exists.call_count, 3, - 'Three files should have been checked') - - def get_titles_and_notes_with_file_not_found_test(self): - """ - Test PresentationDocument.get_titles_and_notes method with file not found - """ - # GIVEN: A mocked open, get_thumbnail_folder and exists - with patch('builtins.open') as mocked_open, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ - as mocked_get_thumbnail_folder, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: - mocked_get_thumbnail_folder.return_value = 'test' - mocked_exists.return_value = False - #WHEN: calling get_titles_and_notes - result_titles, result_notes = self.document.get_titles_and_notes() - # THEN: it should return two empty lists - self.assertIs(type(result_titles), list, - 'result_titles should be of type list') - self.assertEqual(len(result_titles), 0, - 'there be no titles') - self.assertIs(type(result_notes), list, - 'result_notes should be a list') - self.assertEqual(len(result_notes), 0, - 'but the list should be empty') - self.assertEqual(mocked_open.call_count, 0, - 'No calls to open files') - self.assertEqual(mocked_exists.call_count, 1, - 'There should be one call to file exists') - - def get_titles_and_notes_with_file_error_test(self): - """ - Test PresentationDocument.get_titles_and_notes method with file errors - """ - # GIVEN: A mocked open, get_thumbnail_folder and exists - with patch('builtins.open') as mocked_open, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ - as mocked_get_thumbnail_folder, \ - patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: - mocked_get_thumbnail_folder.return_value = 'test' - mocked_exists.return_value = True - mocked_open.side_effect = IOError() - # WHEN: calling get_titles_and_notes - result_titles, result_notes = self.document.get_titles_and_notes() - # THEN: it should return two empty lists - self.assertIs(type(result_titles), list, - 'result_titles should be a list') - +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 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 # +############################################################################### +""" +Functional tests to test the PresentationController and PresentationDocument +classes and related methods. +""" +from unittest import TestCase +import os +from openlp.plugins.presentations.lib.presentationcontroller import PresentationController, PresentationDocument +from tests.functional import MagicMock, patch, mock_open + +class TestPresentationController(TestCase): + + def setUp(self): + mocked_plugin = MagicMock() + mocked_plugin.settings_section = 'presentations' + self.presentation = PresentationController(mocked_plugin) + self.document = PresentationDocument(self.presentation, '') + + + """ + Test the PresentationController. + """ + def constructor_test(self): + """ + Test the Constructor + """ + # GIVEN: A mocked plugin + + # WHEN: The PresentationController is created + + # THEN: The name of the presentation controller should be correct + self.assertEqual('PresentationController', self.presentation.name, + 'The name of the presentation controller should be correct') + + def save_titles_and_notes_test(self): + """ + Test PresentationDocument.save_titles_and_notes method with two valid lists + """ + # GIVEN: two lists of length==2 and a mocked open and get_thumbnail_folder + mocked_open = mock_open() + with patch('builtins.open', mocked_open), \ + patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ + as mocked_get_thumbnail_folder: + titles = ['uno', 'dos'] + notes = ['one', 'two'] + # WHEN: calling save_titles_and_notes + mocked_get_thumbnail_folder.return_value = 'test' + self.document.save_titles_and_notes(titles, notes) + # THEN: the last call to open should have been for slideNotes2.txt + mocked_open.assert_any_call( + os.path.join('test', 'titles.txt'), mode='w') + mocked_open.assert_any_call( + os.path.join('test', 'slideNotes1.txt'), mode='w') + mocked_open.assert_any_call( + os.path.join('test', 'slideNotes2.txt'), mode='w') + self.assertEqual(mocked_open.call_count, 3, + 'There should be exactly three files opened') + mocked_open().writelines.assert_called_once_with(['uno', 'dos']) + mocked_open().write.assert_called_any('one') + mocked_open().write.assert_called_any('two') + + def save_titles_and_notes_with_None_test(self): + """ + Test PresentationDocument.save_titles_and_notes method with no data + """ + # GIVEN: None and an empty list and a mocked open and get_thumbnail_folder + with patch('builtins.open') as mocked_open, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ + as mocked_get_thumbnail_folder: + titles = None + notes = None + # WHEN: calling save_titles_and_notes + mocked_get_thumbnail_folder.return_value = 'test' + self.document.save_titles_and_notes(titles, notes) + # THEN: No file should have been created + self.assertEqual(mocked_open.call_count, 0, + 'No file should be created') + + + def get_titles_and_notes_test(self): + """ + Test PresentationDocument.get_titles_and_notes method + """ + # GIVEN: A mocked open, get_thumbnail_folder and exists + with patch('builtins.open', mock_open(read_data='uno\ndos\n')) as mocked_open, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ + as mocked_get_thumbnail_folder, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: + mocked_get_thumbnail_folder.return_value = 'test' + mocked_exists.return_value = True + # WHEN: calling get_titles_and_notes + result_titles, result_notes = self.document.get_titles_and_notes() + # THEN: it should return two items for the titles and two empty strings for the notes + self.assertIs(type(result_titles), list, + 'result_titles should be of type list') + self.assertEqual(len(result_titles), 2, + 'There should be two items in the titles') + self.assertIs(type(result_notes), list, + 'result_notes should be of type list') + self.assertEqual(len(result_notes), 2, + 'There should be two items in the notes') + self.assertEqual(mocked_open.call_count, 3, + 'Three files should be opened') + mocked_open.assert_any_call(os.path.join('test', 'titles.txt')) + mocked_open.assert_any_call(os.path.join('test', 'slideNotes1.txt')) + mocked_open.assert_any_call(os.path.join('test', 'slideNotes2.txt')) + self.assertEqual(mocked_exists.call_count, 3, + 'Three files should have been checked') + + def get_titles_and_notes_with_file_not_found_test(self): + """ + Test PresentationDocument.get_titles_and_notes method with file not found + """ + # GIVEN: A mocked open, get_thumbnail_folder and exists + with patch('builtins.open') as mocked_open, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ + as mocked_get_thumbnail_folder, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: + mocked_get_thumbnail_folder.return_value = 'test' + mocked_exists.return_value = False + #WHEN: calling get_titles_and_notes + result_titles, result_notes = self.document.get_titles_and_notes() + # THEN: it should return two empty lists + self.assertIs(type(result_titles), list, + 'result_titles should be of type list') + self.assertEqual(len(result_titles), 0, + 'there be no titles') + self.assertIs(type(result_notes), list, + 'result_notes should be a list') + self.assertEqual(len(result_notes), 0, + 'but the list should be empty') + self.assertEqual(mocked_open.call_count, 0, + 'No calls to open files') + self.assertEqual(mocked_exists.call_count, 1, + 'There should be one call to file exists') + + def get_titles_and_notes_with_file_error_test(self): + """ + Test PresentationDocument.get_titles_and_notes method with file errors + """ + # GIVEN: A mocked open, get_thumbnail_folder and exists + with patch('builtins.open') as mocked_open, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.PresentationDocument.get_thumbnail_folder') \ + as mocked_get_thumbnail_folder, \ + patch('openlp.plugins.presentations.lib.presentationcontroller.os.path.exists') as mocked_exists: + mocked_get_thumbnail_folder.return_value = 'test' + mocked_exists.return_value = True + mocked_open.side_effect = IOError() + # WHEN: calling get_titles_and_notes + result_titles, result_notes = self.document.get_titles_and_notes() + # THEN: it should return two empty lists + self.assertIs(type(result_titles), list, + 'result_titles should be a list') + diff --git a/tests/functional/openlp_plugins/remotes/test_router.py b/tests/functional/openlp_plugins/remotes/test_router.py index 88d4b848f..5eb46a0b6 100644 --- a/tests/functional/openlp_plugins/remotes/test_router.py +++ b/tests/functional/openlp_plugins/remotes/test_router.py @@ -39,9 +39,8 @@ from PyQt4 import QtGui from openlp.core.lib import Registry from openlp.core.common import Settings from openlp.plugins.remotes.lib.httpserver import HttpRouter -from tests.functional import MagicMock, patch -from mock import mock_open from urllib.parse import urlparse +from tests.functional import MagicMock, patch, mock_open __default_settings__ = { 'remotes/twelve hour': True, @@ -56,6 +55,7 @@ __default_settings__ = { TEST_PATH = os.path.abspath(os.path.dirname(__file__)) + class TestRouter(TestCase): """ Test the functions in the :mod:`lib` module. @@ -94,7 +94,7 @@ class TestRouter(TestCase): # THEN: the function should return the correct password self.assertEqual(router.auth, test_value, - 'The result for make_sha_hash should return the correct encrypted password') + 'The result for make_sha_hash should return the correct encrypted password') def process_http_request_test(self): """ @@ -112,10 +112,8 @@ class TestRouter(TestCase): function, args = router.process_http_request('/stage/api/poll', None) # THEN: the function should have been called only once - assert function['function'] == mocked_function, \ - 'The mocked function should match defined value.' - assert function['secure'] == False, \ - 'The mocked function should not require any security.' + self.assertEqual(mocked_function, function['function'], 'The mocked function should match defined value.') + self.assertFalse(function['secure'], 'The mocked function should not require any security.') def get_content_type_test(self): """ @@ -127,10 +125,12 @@ class TestRouter(TestCase): ['test.gif', 'image/gif'], ['test.ico', 'image/x-icon'], ['test.png', 'image/png'], ['test.whatever', 'text/plain'], ['test', 'text/plain'], ['', 'text/plain'], - [os.path.join(TEST_PATH,'test.html'), 'text/html']] + [os.path.join(TEST_PATH, 'test.html'), 'text/html']] + # WHEN: calling each file type for header in headers: ext, content_type = self.router.get_content_type(header[0]) + # THEN: all types should match self.assertEqual(content_type, header[1], 'Mismatch of content type') @@ -145,13 +145,14 @@ class TestRouter(TestCase): self.router.wfile = MagicMock() self.router.html_dir = os.path.normpath('test/dir') self.router.template_vars = MagicMock() + # WHEN: call serve_file with no file_name self.router.serve_file() + # THEN: it should return a 404 self.router.send_response.assert_called_once_with(404) self.router.send_header.assert_called_once_with('Content-type','text/html') - self.assertEqual(self.router.end_headers.call_count, 1, - 'end_headers called once') + self.assertEqual(self.router.end_headers.call_count, 1, 'end_headers called once') def serve_file_with_valid_params_test(self): """ @@ -165,16 +166,16 @@ class TestRouter(TestCase): self.router.html_dir = os.path.normpath('test/dir') self.router.template_vars = MagicMock() with patch('openlp.core.lib.os.path.exists') as mocked_exists, \ - patch('builtins.open', mock_open(read_data='123')): + patch('builtins.open', mock_open(read_data='123')): mocked_exists.return_value = True + # WHEN: call serve_file with an existing html file self.router.serve_file(os.path.normpath('test/dir/test.html')) + # THEN: it should return a 200 and the file self.router.send_response.assert_called_once_with(200) - self.router.send_header.assert_called_once_with( - 'Content-type','text/html') - self.assertEqual(self.router.end_headers.call_count, 1, - 'end_headers called once') + self.router.send_header.assert_called_once_with('Content-type', 'text/html') + self.assertEqual(self.router.end_headers.call_count, 1, 'end_headers called once') def serve_thumbnail_without_params_test(self): """ diff --git a/tests/interfaces/openlp_core_ui/test_thememanagerhelper.py b/tests/interfaces/openlp_core_ui/test_thememanagerhelper.py new file mode 100644 index 000000000..aa70edd51 --- /dev/null +++ b/tests/interfaces/openlp_core_ui/test_thememanagerhelper.py @@ -0,0 +1,99 @@ +# -*- coding: utf-8 -*- +# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4 + +############################################################################### +# OpenLP - Open Source Lyrics Projection # +# --------------------------------------------------------------------------- # +# Copyright (c) 2008-2013 Raoul Snyman # +# Portions copyright (c) 2008-2013 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 # +############################################################################### +""" +Interface tests to test the thememanagerhelper class and related methods. +""" +import os +from unittest import TestCase +from tempfile import mkstemp + +from openlp.core.common import Settings +from openlp.core.ui import ThemeManagerHelper +from tests.functional import patch, MagicMock + + +class TestThemeManagerHelper(TestCase): + """ + Test the functions in the ThemeManagerHelp[er module + """ + def setUp(self): + """ + Create the UI + """ + fd, self.ini_file = mkstemp('.ini') + Settings().set_filename(self.ini_file) + self.helper = ThemeManagerHelper() + self.helper.settings_section = "themes" + + def tearDown(self): + """ + Delete all the C++ objects at the end so that we don't have a segfault + """ + os.unlink(self.ini_file) + os.unlink(Settings().fileName()) + + def test_initialise(self): + """ + Test the thememanagerhelper initialise - basic test + """ + # GIVEN: A new a call to initialise + Settings().setValue('themes/global theme', 'my_theme') + self.helper.build_theme_path = MagicMock() + self.helper.load_first_time_themes = MagicMock() + + # WHEN: the initialistion is run + self.helper.initialise() + + # THEN: + self.assertEqual(1, self.helper.build_theme_path.call_count, + 'The function build_theme_path should have been called') + self.assertEqual(1, self.helper.load_first_time_themes.call_count, + 'The function load_first_time_themes should have been called only once') + self.assertEqual(self.helper.global_theme, 'my_theme', + 'The global theme should have been set to my_theme') + + def test_build_theme_path(self): + """ + Test the thememanagerhelper build_theme_path - basic test + """ + # GIVEN: A new a call to initialise + with patch('openlp.core.common.applocation.check_directory_exists') as mocked_check_directory_exists: + # GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory() + mocked_check_directory_exists.return_value = True + Settings().setValue('themes/global theme', 'my_theme') + + self.helper.theme_form = MagicMock() + #self.helper.load_first_time_themes = MagicMock() + + # WHEN: the build_theme_path is run + self.helper.build_theme_path() + + # THEN: + self.assertEqual(self.helper.path, self.helper.theme_form.path, + 'The theme path and the main path should be the same value') \ No newline at end of file