This commit is contained in:
Tim Bentley 2014-04-01 18:33:13 +01:00
commit 3e7370c656
26 changed files with 493 additions and 207 deletions

View File

@ -62,11 +62,9 @@ class Registry(object):
registry = cls()
registry.service_list = {}
registry.functions_list = {}
registry.running_under_test = False
registry.initialising = True
# Allow the tests to remove Registry entries but not the live system
if 'nose' in sys.argv[0]:
registry.running_under_test = True
registry.running_under_test = 'nose' in sys.argv[0]
registry.initialising = True
return registry
def get(self, key):
@ -128,7 +126,7 @@ class Registry(object):
:param event: The function description..
:param function: The function to be called when the event happens.
"""
if self.running_under_test is False:
if not self.running_under_test:
trace_error_handler(log)
log.error('Invalid Method call for key %s' % event)
raise KeyError('Invalid Method call for key %s' % event)

View File

@ -87,7 +87,8 @@ def upgrade_db(url, upgrade):
"""
pass
metadata_table = Table('metadata', metadata,
metadata_table = Table(
'metadata', metadata,
Column('key', types.Unicode(64), primary_key=True),
Column('value', types.UnicodeText(), default=None)
)
@ -131,7 +132,6 @@ def delete_database(plugin_name, db_file_name=None):
:param plugin_name: The name of the plugin to remove the database for
:param db_file_name: The database file name. Defaults to None resulting in the plugin_name being used.
"""
db_file_path = None
if db_file_name:
db_file_path = os.path.join(AppLocation.get_section_data_path(plugin_name), db_file_name)
else:

View File

@ -74,11 +74,13 @@ def create_button_box(dialog, name, standard_buttons, custom_buttons=None):
:param name: A string which is set as object name.
:param standard_buttons: A list of strings for the used buttons. It might contain: ``ok``, ``save``, ``cancel``,
``close``, and ``defaults``.
:param custom_buttons: A list of additional buttons. If a item is a instance of QtGui.QAbstractButton it is added
with QDialogButtonBox.ActionRole. Other wise the item has to be a tuple of a button and a ButtonRole.
:param custom_buttons: A list of additional buttons. If an item is an instance of QtGui.QAbstractButton it is added
with QDialogButtonBox.ActionRole. Otherwise the item has to be a tuple of a Button and a ButtonRole.
"""
if custom_buttons is None:
custom_buttons = []
if standard_buttons is None:
standard_buttons = []
buttons = QtGui.QDialogButtonBox.NoButton
if 'ok' in standard_buttons:
buttons |= QtGui.QDialogButtonBox.Ok

View File

@ -471,7 +471,7 @@ class SlideController(DisplayController, RegistryProperties):
category=self.category,
triggers=self.live_escape)
def live_escape(self):
def live_escape(self, field=None):
"""
If you press ESC on the live screen it should close the display temporarily.
"""
@ -1243,7 +1243,7 @@ class SlideController(DisplayController, RegistryProperties):
if self.service_item:
self.service_manager.add_service_item(self.service_item)
def on_go_live_click(self):
def on_go_live_click(self, field=None):
"""
triggered by clicking the Preview slide items
"""
@ -1256,7 +1256,7 @@ class SlideController(DisplayController, RegistryProperties):
self.on_media_close()
self.on_go_live()
def on_go_live(self):
def on_go_live(self, field=None):
"""
If preview copy slide item to live controller from Preview Controller
"""

View File

@ -31,10 +31,8 @@ The :mod:`upgrade` module provides a way for the database and schema that is the
"""
import logging
from sqlalchemy import Table, func, select, insert
__version__ = 1
log = logging.getLogger(__name__)
__version__ = 1
def upgrade_1(session, metadata):
@ -43,136 +41,4 @@ def upgrade_1(session, metadata):
This upgrade renames a number of keys to a single naming convention.
"""
metadata_table = Table('metadata', metadata, autoload=True)
# Copy "Version" to "name" ("version" used by upgrade system)
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
session.execute(insert(metadata_table).values(
key='name',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'Version'
).as_scalar()
))
# Copy "Copyright" to "copyright"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
session.execute(insert(metadata_table).values(
key='copyright',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'Copyright'
).as_scalar()
))
# Copy "Permissions" to "permissions"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
session.execute(insert(metadata_table).values(
key='permissions',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'Permissions'
).as_scalar()
))
# Copy "Bookname language" to "book_name_language"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'Bookname language'
)
).scalar()
if value_count > 0:
session.execute(insert(metadata_table).values(
key='book_name_language',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'Bookname language'
).as_scalar()
))
# Copy "download source" to "download_source"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'download source'
)
).scalar()
log.debug('download source: %s', value_count)
if value_count > 0:
session.execute(insert(metadata_table).values(
key='download_source',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'download source'
).as_scalar()
))
# Copy "download name" to "download_name"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'download name'
)
).scalar()
log.debug('download name: %s', value_count)
if value_count > 0:
session.execute(insert(metadata_table).values(
key='download_name',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'download name'
).as_scalar()
))
# Copy "proxy server" to "proxy_server"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'proxy server'
)
).scalar()
log.debug('proxy server: %s', value_count)
if value_count > 0:
session.execute(insert(metadata_table).values(
key='proxy_server',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'proxy server'
).as_scalar()
))
# Copy "proxy username" to "proxy_username"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'proxy username'
)
).scalar()
log.debug('proxy username: %s', value_count)
if value_count > 0:
session.execute(insert(metadata_table).values(
key='proxy_username',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'proxy username'
).as_scalar()
))
# Copy "proxy password" to "proxy_password"
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
value_count = session.execute(
select(
[func.count(metadata_table.c.value)],
metadata_table.c.key == 'proxy password'
)
).scalar()
log.debug('proxy password: %s', value_count)
if value_count > 0:
session.execute(insert(metadata_table).values(
key='proxy_password',
value=select(
[metadata_table.c.value],
metadata_table.c.key == 'proxy password'
).as_scalar()
))
# TODO: Clean up in a subsequent release of OpenLP (like 2.0 final)
#session.execute(delete(metadata_table)\
# .where(metadata_table.c.key == u'dbversion'))
session.commit()
log.info('No upgrades to perform')

View File

@ -34,6 +34,7 @@ if os.name == 'nt':
from ctypes import cdll
from ctypes.wintypes import RECT
from openlp.core.utils import AppLocation
from openlp.core.lib import ScreenList
from .presentationcontroller import PresentationController, PresentationDocument
@ -85,8 +86,8 @@ class PptviewController(PresentationController):
if self.process:
return
log.debug('start PPTView')
dll_path = os.path.join(
self.plugin_manager.base_path, 'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll')
dll_path = os.path.join(AppLocation.get_directory(AppLocation.AppDir),
'presentations', 'lib', 'pptviewlib', 'pptviewlib.dll')
self.process = cdll.LoadLibrary(dll_path)
if log.isEnabledFor(logging.DEBUG):
self.process.SetDebug(1)

View File

@ -601,7 +601,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
def on_verse_add_button_clicked(self):
self.verse_form.set_verse('', True)
if self.verse_form.exec_():
after_text, verse_tag, verse_num = self.verse_form.get_verse
after_text, verse_tag, verse_num = self.verse_form.get_verse()
verse_def = '%s%s' % (verse_tag, verse_num)
item = QtGui.QTableWidgetItem(after_text)
item.setData(QtCore.Qt.UserRole, verse_def)
@ -619,7 +619,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
verse_id = item.data(QtCore.Qt.UserRole)
self.verse_form.set_verse(temp_text, True, verse_id)
if self.verse_form.exec_():
after_text, verse_tag, verse_num = self.verse_form.get_verse
after_text, verse_tag, verse_num = self.verse_form.get_verse()
verse_def = '%s%s' % (verse_tag, verse_num)
item.setData(QtCore.Qt.UserRole, verse_def)
item.setText(after_text)
@ -661,7 +661,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
self.verse_form.set_verse('')
if not self.verse_form.exec_():
return
verse_list = self.verse_form.get_all_verses
verse_list = self.verse_form.get_all_verses()
verse_list = str(verse_list.replace('\r\n', '\n'))
self.verse_list_widget.clear()
self.verse_list_widget.setRowCount(0)

View File

@ -49,7 +49,7 @@ class EasySlidesImport(SongImport):
"""
Initialise the class.
"""
SongImport.__init__(self, manager, **kwargs)
super(EasySlidesImport, self).__init__(manager, **kwargs)
def do_import(self):
log.info('Importing EasySlides XML file %s', self.import_source)

View File

@ -73,7 +73,7 @@ class EasyWorshipSongImport(SongImport):
ability to import EasyWorship song files.
"""
def __init__(self, manager, **kwargs):
SongImport.__init__(self, manager, **kwargs)
super(EasyWorshipSongImport, self).__init__(manager, **kwargs)
def do_import(self):
"""

View File

@ -97,7 +97,7 @@ class SongBeamerImport(SongImport):
"""
Initialise the Song Beamer importer.
"""
SongImport.__init__(self, manager, **kwargs)
super(SongBeamerImport, self).__init__(manager, **kwargs)
def do_import(self):
"""

View File

@ -92,7 +92,7 @@ class SongShowPlusImport(SongImport):
"""
Initialise the SongShow Plus importer.
"""
SongImport.__init__(self, manager, **kwargs)
super(SongShowPlusImport, self).__init__(manager, **kwargs)
def do_import(self):
"""

View File

@ -30,12 +30,15 @@
The :mod:`upgrade` module provides a way for the database and schema that is the
backend for the Songs plugin
"""
import logging
from sqlalchemy import Column, types
from sqlalchemy.exc import OperationalError
from sqlalchemy.sql.expression import func, false, null, text
from openlp.core.lib.db import get_upgrade_op
log = logging.getLogger(__name__)
__version__ = 3
@ -50,7 +53,11 @@ def upgrade_1(session, metadata):
In order to facilitate this one-to-many relationship, a song_id column is
added to the media_files table, and a weight column so that the media
files can be ordered.
:param session:
:param metadata:
"""
try:
op = get_upgrade_op(session)
op.drop_table('media_files_songs')
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
@ -58,6 +65,8 @@ def upgrade_1(session, metadata):
if metadata.bind.url.get_dialect().name != 'sqlite':
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
except OperationalError:
log.info('Upgrade 1 has already been run')
def upgrade_2(session, metadata):
@ -66,9 +75,12 @@ def upgrade_2(session, metadata):
This upgrade adds a create_date and last_modified date to the songs table
"""
try:
op = get_upgrade_op(session)
op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
except OperationalError:
log.info('Upgrade 2 has already been run')
def upgrade_3(session, metadata):
@ -77,8 +89,11 @@ def upgrade_3(session, metadata):
This upgrade adds a temporary song flag to the songs table
"""
try:
op = get_upgrade_op(session)
if metadata.bind.url.get_dialect().name == 'sqlite':
op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
else:
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
except OperationalError:
log.info('Upgrade 3 has already been run')

View File

@ -30,10 +30,14 @@
The :mod:`upgrade` module provides a way for the database and schema that is the
backend for the SongsUsage plugin
"""
from openlp.core.lib.db import get_upgrade_op
import logging
from sqlalchemy.exc import OperationalError
from sqlalchemy import Column, types
from openlp.core.lib.db import get_upgrade_op
log = logging.getLogger(__name__)
__version__ = 1
@ -42,7 +46,13 @@ def upgrade_1(session, metadata):
Version 1 upgrade.
This upgrade adds two new fields to the songusage database
:param session: SQLAlchemy Session object
:param metadata: SQLAlchemy MetaData object
"""
try:
op = get_upgrade_op(session)
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
except OperationalError:
log.info('Upgrade 1 has already taken place')

View File

@ -94,6 +94,7 @@ OPTIONAL_MODULES = [
('psycopg2', '(PostgreSQL support)', True),
('nose', '(testing framework)', True),
('mock', '(testing module)', sys.version_info[1] < 3),
('jenkins', '(access jenkins api - package name: jenkins-webapi)', True),
]
w = sys.stdout.write

192
scripts/jenkins_script.py Normal file
View File

@ -0,0 +1,192 @@
#!/usr/bin/env python3
# -*- 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 script helps to trigger builds of branches. To use it you have to install the jenkins-webapi package:
pip3 install jenkins-webapi
You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias):
alias ci="python3 ./scripts/jenkins_script.py TOKEN"
You can look up the token in the Branch-01-Pull job configuration or ask in IRC.
"""
from optparse import OptionParser
from requests.exceptions import HTTPError
from subprocess import Popen, PIPE
import sys
import time
from jenkins import Jenkins
JENKINS_URL = 'http://ci.openlp.org/'
class OpenLPJobs(object):
"""
This class holds any jobs we have on jenkins and we actually need in this script.
"""
Branch_Pull = 'Branch-01-Pull'
Branch_Functional = 'Branch-02-Functional-Tests'
Branch_Interface = 'Branch-03-Interface-Tests'
Branch_Windows = 'Branch-04-Windows_Tests'
Branch_PEP = 'Branch-05-Code-Analysis'
Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP]
class JenkinsTrigger(object):
def __init__(self, token):
"""
Create the JenkinsTrigger instance.
:param token: The token we need to trigger the build. If you do not have this token, ask in IRC.
"""
self.token = token
self.repo_name = get_repo_name()
self.jenkins_instance = Jenkins(JENKINS_URL)
def trigger_build(self):
"""
Ask our jenkins server to build the "Branch-01-Pull" job.
"""
self.jenkins_instance.job(OpenLPJobs.Branch_Pull).build({'BRANCH_NAME': self.repo_name}, token=self.token)
def print_output(self):
"""
Print the status information of the build tirggered.
"""
print("Add this to your merge proposal:")
print("--------------------------------")
for job in OpenLPJobs.Jobs:
self.__print_build_info(job)
def open_browser(self):
"""
Opens the browser.
"""
url = self.jenkins_instance.job(OpenLPJobs.Branch_Pull).info['url']
# Open the url
Popen(('xdg-open', url), stderr=PIPE)
def __print_build_info(self, job_name):
"""
This helper method prints the job information of the given ``job_name``
:param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class
variables from the :class:`OpenLPJobs` class.
"""
job = self.jenkins_instance.job(job_name)
while job.info['inQueue']:
# Give other processes the possibility to take over. Like Thread.yield().
time.sleep(0)
build = job.last_build
build.wait()
result_string = build.info['result']
url = build.info['url']
print('[%s] %s' % (result_string, url))
# On failure open the browser.
#if result_string == "FAILURE":
# url += 'console'
# Popen(('xdg-open', url), stderr=PIPE)
def get_repo_name():
"""
This returns the name of branch of the wokring directory. For example it returns *lp:~googol/openlp/render*.
"""
# Run the bzr command.
bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE)
raw_output, error = bzr.communicate()
# Detect any errors
if error:
print('This is not a branch.')
return
# Clean the output.
raw_output = raw_output.decode()
output_list = list(map(str.strip, raw_output.split('\n')))
# Determine the branch's name
repo_name = ''
for line in output_list:
# Check if it is remote branch.
if 'push branch' in line:
repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
break
elif 'checkout of branch' in line:
repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/', 'lp:')
break
repo_name = repo_name.strip('/')
# Did we find the branch name?
if not repo_name:
for line in output_list:
# Check if the branch was pushed.
if 'Shared repository with trees (format: 2a)' in line:
print('Not a branch. cd to a branch.')
return
print('Not a branch. Have you pushed it to launchpad?')
return
return repo_name
def main():
usage = 'Usage: python %prog TOKEN [options]'
parser = OptionParser(usage=usage)
parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True,
help='Disable output.')
parser.add_option('-b', '--open-browser', dest='open_browser', action="store_true", default=False,
help='Opens the jenkins page in your browser.')
#parser.add_option('-e', '--open-browser-on-error', dest='open_browser_on_error', action="store_true",
# default=False, help='Opens the jenkins page in your browser in case a test fails.')
options, args = parser.parse_args(sys.argv)
if len(args) == 2:
if not get_repo_name():
return
token = args[-1]
jenkins_trigger = JenkinsTrigger(token)
try:
jenkins_trigger.trigger_build()
except HTTPError as e:
print("Wrong token.")
return
# Open the browser before printing the output.
if options.open_browser:
jenkins_trigger.open_browser()
if options.enable_output:
jenkins_trigger.print_output()
else:
parser.print_help()
if __name__ == '__main__':
main()

View File

@ -100,6 +100,20 @@ class TestRegistry(TestCase):
# THEN: I expect then function to have been called and a return given
self.assertEqual(return_value[0], 'function_2', 'A return value is provided and matches')
def remove_function_test(self):
"""
Test the remove_function() method
"""
# GIVEN: An existing registry register a function
Registry.create()
Registry().register_function('test1', self.dummy_function_1)
# WHEN: Remove the function.
Registry().remove_function('test1', self.dummy_function_1)
# THEN: The method should not be available.
assert not Registry().functions_list['test1'], 'The function should not be in the dict anymore.'
def dummy_function_1(self):
return "function_1"

View File

@ -29,13 +29,14 @@
"""
Package to test the openlp.core.lib package.
"""
import os
from unittest import TestCase
from sqlalchemy.pool import NullPool
from sqlalchemy.orm.scoping import ScopedSession
from sqlalchemy import MetaData
from openlp.core.lib.db import init_db, get_upgrade_op
from openlp.core.lib.db import init_db, get_upgrade_op, delete_database
from tests.functional import patch, MagicMock
@ -110,3 +111,44 @@ class TestDB(TestCase):
mocked_session.bind.connect.assert_called_with()
MockedMigrationContext.configure.assert_called_with(mocked_connection)
MockedOperations.assert_called_with(mocked_context)
def delete_database_without_db_file_name_test(self):
"""
Test that the ``delete_database`` function removes a database file, without the file name parameter
"""
# GIVEN: Mocked out AppLocation class and delete_file method, a test plugin name and a db location
with patch('openlp.core.lib.db.AppLocation') as MockedAppLocation, \
patch('openlp.core.lib.db.delete_file') as mocked_delete_file:
MockedAppLocation.get_section_data_path.return_value = 'test-dir'
mocked_delete_file.return_value = True
test_plugin = 'test'
test_location = os.path.join('test-dir', test_plugin)
# WHEN: delete_database is run without a database file
result = delete_database(test_plugin)
# THEN: The AppLocation.get_section_data_path and delete_file methods should have been called
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
mocked_delete_file.assert_called_with(test_location)
self.assertTrue(result, 'The result of delete_file should be True (was rigged that way)')
def delete_database_with_db_file_name_test(self):
"""
Test that the ``delete_database`` function removes a database file, with the file name supplied
"""
# GIVEN: Mocked out AppLocation class and delete_file method, a test plugin name and a db location
with patch('openlp.core.lib.db.AppLocation') as MockedAppLocation, \
patch('openlp.core.lib.db.delete_file') as mocked_delete_file:
MockedAppLocation.get_section_data_path.return_value = 'test-dir'
mocked_delete_file.return_value = False
test_plugin = 'test'
test_db_file = 'mydb.sqlite'
test_location = os.path.join('test-dir', test_db_file)
# WHEN: delete_database is run without a database file
result = delete_database(test_plugin, test_db_file)
# THEN: The AppLocation.get_section_data_path and delete_file methods should have been called
MockedAppLocation.get_section_data_path.assert_called_with(test_plugin)
mocked_delete_file.assert_called_with(test_location)
self.assertFalse(result, 'The result of delete_file should be False (was rigged that way)')

View File

@ -0,0 +1,83 @@
# -*- 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 #
###############################################################################
"""
Package to test the openlp.core.lib.ui package.
"""
from PyQt4 import QtGui
from unittest import TestCase
from openlp.core.lib.ui import *
class TestUi(TestCase):
"""
Test the functions in the ui module
"""
def test_add_welcome_page(self):
"""
Test appending a welcome page to a wizard
"""
# GIVEN: A wizard
wizard = QtGui.QWizard()
# WHEN: A welcome page has been added to the wizard
add_welcome_page(wizard, ':/wizards/wizard_firsttime.bmp')
# THEN: The wizard should have one page with a pixmap.
self.assertEqual(1, len(wizard.pageIds()), 'The wizard should have one page.')
self.assertIsInstance(wizard.page(0).pixmap(QtGui.QWizard.WatermarkPixmap), QtGui.QPixmap)
def test_create_button_box(self):
"""
Test creating a button box for a dialog
"""
# GIVEN: A dialog
dialog = QtGui.QDialog()
# WHEN: We create the button box with five buttons
btnbox = create_button_box(dialog, 'my_btns', ['ok', 'save', 'cancel', 'close', 'defaults'])
# THEN: We should get a QDialogButtonBox with five buttons
self.assertIsInstance(btnbox, QtGui.QDialogButtonBox)
self.assertEqual(5, len(btnbox.buttons()))
# WHEN: We create the button box with a custom button
btnbox = create_button_box(dialog, 'my_btns', None, [QtGui.QPushButton('Custom')])
# THEN: We should get a QDialogButtonBox with one button
self.assertIsInstance(btnbox, QtGui.QDialogButtonBox)
self.assertEqual(1, len(btnbox.buttons()))
# WHEN: We create the button box with a custom button and a custom role
btnbox = create_button_box(dialog, 'my_btns', None,
[(QtGui.QPushButton('Help'), QtGui.QDialogButtonBox.HelpRole)])
# THEN: We should get a QDialogButtonBox with one button with a certain role
self.assertIsInstance(btnbox, QtGui.QDialogButtonBox)
self.assertEqual(1, len(btnbox.buttons()))
self.assertEqual(QtGui.QDialogButtonBox.HelpRole, btnbox.buttonRole(btnbox.buttons()[0]))

View File

@ -30,19 +30,78 @@
This module contains tests for the pptviewcontroller module of the Presentations plugin.
"""
import os
import shutil
if os.name == 'nt':
from ctypes import cdll
from tempfile import mkdtemp
from unittest import TestCase
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin
from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument
from openlp.plugins.presentations.lib.pptviewcontroller import PptviewDocument, PptviewController
class TestPptviewController(TestCase, TestMixin):
"""
Test the PptviewController Class
"""
#TODO: Items left to test
# PptviewController
# __init__
# check_availablecheck_installed
# start_process(self)
# kill
def setUp(self):
"""
Set up the patches and mocks need for all tests.
"""
self.get_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)
def constructor_test(self):
"""
Test the Constructor from the PptViewController
"""
# GIVEN: No presentation controller
controller = None
# WHEN: The presentation controller object is created
controller = PptviewController(plugin=self.mock_plugin)
# THEN: The name of the presentation controller should be correct
self.assertEqual('Powerpoint Viewer', controller.name, 'The name of the presentation controller should be correct')
def check_available_test(self):
"""
Test check_available / check_installed
"""
# GIVEN: A mocked dll loader and a controller
with patch('ctypes.cdll.LoadLibrary') as mocked_load_library:
mocked_process = MagicMock()
mocked_process.CheckInstalled.return_value = True
mocked_load_library.return_value = mocked_process
controller = PptviewController(plugin=self.mock_plugin)
# WHEN: check_available is called
available = controller.check_available()
# THEN: On windows it should return True, on other platforms False
if os.name == 'nt':
self.assertTrue(available, 'check_available should return True on windows.')
else:
self.assertFalse(available, 'check_available should return False when not on windows.')
class TestPptviewDocument(TestCase):
"""

View File

@ -77,7 +77,7 @@ class EasyWorshipSongImportLogger(EasyWorshipSongImport):
_title_assignment_list = []
def __init__(self, manager):
EasyWorshipSongImport.__init__(self, manager)
EasyWorshipSongImport.__init__(self, manager, filenames=[])
@property
def title(self):
@ -147,7 +147,7 @@ class TestEasyWorshipSongImport(TestCase):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
# THEN: The importer object should not be None
self.assertIsNotNone(importer, 'Import should not be none')
@ -159,7 +159,7 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions.
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.field_descriptions = TEST_FIELD_DESCS
# WHEN: Called with a field name that exists
@ -177,7 +177,7 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: A mocked out SongImport class, a mocked out "manager" and a list of field descriptions
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.field_descriptions = TEST_FIELD_DESCS
# WHEN: Called with a field name that does not exist
@ -196,7 +196,7 @@ class TestEasyWorshipSongImport(TestCase):
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
# WHEN: set_record_struct is called with a list of field descriptions
return_value = importer.set_record_struct(TEST_FIELD_DESCS)
@ -213,7 +213,7 @@ class TestEasyWorshipSongImport(TestCase):
# GIVEN: A mocked out SongImport class, a mocked out "manager", an encoding and some test data and known results
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.encoding = TEST_DATA_ENCODING
importer.fields = TEST_FIELDS
importer.field_descriptions = TEST_FIELD_DESCS
@ -236,7 +236,7 @@ class TestEasyWorshipSongImport(TestCase):
with patch('openlp.plugins.songs.lib.ewimport.SongImport'):
mocked_manager = MagicMock()
mocked_memo_file = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
importer.memo_file = mocked_memo_file
importer.encoding = TEST_DATA_ENCODING
@ -267,7 +267,7 @@ class TestEasyWorshipSongImport(TestCase):
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.side_effect = [True, False]
# WHEN: Supplied with an import source
@ -286,7 +286,7 @@ class TestEasyWorshipSongImport(TestCase):
with patch('openlp.plugins.songs.lib.ewimport.SongImport'), \
patch('openlp.plugins.songs.lib.ewimport.os.path') as mocked_os_path:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.return_value = True
importer.import_source = 'Songs.DB'
@ -307,7 +307,7 @@ class TestEasyWorshipSongImport(TestCase):
patch('builtins.open') as mocked_open, \
patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.return_value = True
mocked_os_path.getsize.return_value = 0x800
importer.import_source = 'Songs.DB'
@ -334,7 +334,7 @@ class TestEasyWorshipSongImport(TestCase):
patch('builtins.open'), patch('openlp.plugins.songs.lib.ewimport.struct') as mocked_struct, \
patch('openlp.plugins.songs.lib.ewimport.retrieve_windows_encoding') as mocked_retrieve_windows_encoding:
mocked_manager = MagicMock()
importer = EasyWorshipSongImport(mocked_manager)
importer = EasyWorshipSongImport(mocked_manager, filenames=[])
mocked_os_path.isfile.return_value = True
mocked_os_path.getsize.return_value = 0x800
importer.import_source = 'Songs.DB'

View File

@ -66,7 +66,7 @@ class TestSongBeamerImport(TestCase):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = SongBeamerImport(mocked_manager)
importer = SongBeamerImport(mocked_manager, filenames=[])
# THEN: The importer object should not be None
self.assertIsNotNone(importer, 'Import should not be none')
@ -79,7 +79,7 @@ class TestSongBeamerImport(TestCase):
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongBeamerImport(mocked_manager)
importer = SongBeamerImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
@ -100,7 +100,7 @@ class TestSongBeamerImport(TestCase):
with patch('openlp.plugins.songs.lib.songbeamerimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongBeamerImport(mocked_manager)
importer = SongBeamerImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
@ -127,7 +127,7 @@ class TestSongBeamerImport(TestCase):
mocked_add_verse = MagicMock()
mocked_finish = MagicMock()
mocked_finish.return_value = True
importer = SongBeamerImport(mocked_manager)
importer = SongBeamerImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = False
importer.add_verse = mocked_add_verse

View File

@ -72,7 +72,7 @@ class TestSongShowPlusImport(TestCase):
mocked_manager = MagicMock()
# WHEN: An importer object is created
importer = SongShowPlusImport(mocked_manager)
importer = SongShowPlusImport(mocked_manager, filenames=[])
# THEN: The importer object should not be None
self.assertIsNotNone(importer, 'Import should not be none')
@ -85,7 +85,7 @@ class TestSongShowPlusImport(TestCase):
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongShowPlusImport(mocked_manager)
importer = SongShowPlusImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
@ -106,7 +106,7 @@ class TestSongShowPlusImport(TestCase):
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
mocked_manager = MagicMock()
mocked_import_wizard = MagicMock()
importer = SongShowPlusImport(mocked_manager)
importer = SongShowPlusImport(mocked_manager, filenames=[])
importer.import_wizard = mocked_import_wizard
importer.stop_import_flag = True
@ -126,7 +126,7 @@ class TestSongShowPlusImport(TestCase):
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
mocked_manager = MagicMock()
importer = SongShowPlusImport(mocked_manager)
importer = SongShowPlusImport(mocked_manager, filenames=[])
# WHEN: Supplied with the following arguments replicating verses being added
test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'),
@ -153,7 +153,7 @@ class TestSongShowPlusImport(TestCase):
# GIVEN: A mocked out SongImport class, and a mocked out "manager"
with patch('openlp.plugins.songs.lib.songshowplusimport.SongImport'):
mocked_manager = MagicMock()
importer = SongShowPlusImport(mocked_manager)
importer = SongShowPlusImport(mocked_manager, filenames=[])
# WHEN: Supplied with the following arguments replicating a verse order being added
test_values = [('Verse 1', VerseType.tags[VerseType.Verse] + '1'),

View File

@ -89,7 +89,7 @@ class SongImportTestHelper(TestCase):
"""
Import the given file and check that it has imported correctly
"""
importer = self.importer_class(self.mocked_manager)
importer = self.importer_class(self.mocked_manager, filenames=[source_file_name])
importer.import_wizard = self.mocked_import_wizard
importer.stop_import_flag = False
importer.topics = []

View File

@ -53,13 +53,12 @@ class TestMixin(object):
Build the settings Object and initialise it
"""
Settings.setDefaultFormat(Settings.IniFormat)
fd, self.ini_file = mkstemp('.ini')
self.fd, self.ini_file = mkstemp('.ini')
Settings().set_filename(self.ini_file)
def destroy_settings(self):
"""
Destroy the Settings Object
"""
if hasattr(self, 'fd'):
os.close(self.fd)
os.unlink(Settings().fileName())

View File

@ -31,6 +31,7 @@ Package to test the openlp.core.lib.pluginmanager package.
"""
import sys
import shutil
import gc
from tempfile import mkdtemp
from unittest import TestCase
@ -65,6 +66,9 @@ class TestPluginManager(TestCase, TestMixin):
del self.main_window
Settings().remove('advanced/data path')
self.destroy_settings()
# On windows we need to manually garbage collect to close sqlalchemy files
# to avoid errors when temporary files are deleted.
gc.collect()
shutil.rmtree(self.temp_dir)
def find_plugins_test(self):