From 79263bfea4e46d9717df4fb59669956f299cf011 Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 18 Mar 2014 22:33:05 +0200 Subject: [PATCH 01/21] Try to fix bug #1136278 by detecting if the upgrade has already been run Fixes: https://launchpad.net/bugs/1136278 --- openlp/plugins/bibles/lib/upgrade.py | 138 +----------------------- openlp/plugins/songs/lib/upgrade.py | 46 +++++--- openlp/plugins/songusage/lib/upgrade.py | 18 +++- 3 files changed, 46 insertions(+), 156 deletions(-) diff --git a/openlp/plugins/bibles/lib/upgrade.py b/openlp/plugins/bibles/lib/upgrade.py index 3e58686d1..bf1caf025 100644 --- a/openlp/plugins/bibles/lib/upgrade.py +++ b/openlp/plugins/bibles/lib/upgrade.py @@ -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') diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index ee01fb8b0..adb7d8af5 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -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,14 +53,20 @@ 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: """ - op = get_upgrade_op(session) - op.drop_table('media_files_songs') - op.add_column('media_files', Column('song_id', types.Integer(), server_default=null())) - op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) - 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']) + 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())) + op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) + 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 """ - 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())) + 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,9 +89,11 @@ def upgrade_3(session, metadata): This upgrade adds a temporary song flag to the songs table """ - 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())) - + 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') diff --git a/openlp/plugins/songusage/lib/upgrade.py b/openlp/plugins/songusage/lib/upgrade.py index 08096423d..24f264824 100644 --- a/openlp/plugins/songusage/lib/upgrade.py +++ b/openlp/plugins/songusage/lib/upgrade.py @@ -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 """ - 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='')) + 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') From 35e3564655ba8daa80f7d6df74d3bc3a783b2e6b Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Tue, 18 Mar 2014 23:03:53 +0200 Subject: [PATCH 02/21] Tests! --- openlp/core/lib/db.py | 10 ++--- tests/functional/openlp_core_lib/test_db.py | 44 ++++++++++++++++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/openlp/core/lib/db.py b/openlp/core/lib/db.py index 36bfa24ef..d43938afe 100644 --- a/openlp/core/lib/db.py +++ b/openlp/core/lib/db.py @@ -93,10 +93,11 @@ def upgrade_db(url, upgrade): """ pass - metadata_table = Table('metadata', metadata, - Column('key', types.Unicode(64), primary_key=True), - Column('value', types.UnicodeText(), default=None) - ) + metadata_table = Table( + 'metadata', metadata, + Column('key', types.Unicode(64), primary_key=True), + Column('value', types.UnicodeText(), default=None) + ) metadata_table.create(checkfirst=True) mapper(Metadata, metadata_table) version_meta = session.query(Metadata).get('version') @@ -137,7 +138,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: diff --git a/tests/functional/openlp_core_lib/test_db.py b/tests/functional/openlp_core_lib/test_db.py index 8a2f21ec3..470bd0636 100644 --- a/tests/functional/openlp_core_lib/test_db.py +++ b/tests/functional/openlp_core_lib/test_db.py @@ -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)') From 20533624e0aef3ea3822e1991409ca9d367e32d1 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Sun, 23 Mar 2014 23:27:07 +0100 Subject: [PATCH 03/21] Fix method calls (broken with rev. 2329) --- openlp/plugins/songs/forms/editsongform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/plugins/songs/forms/editsongform.py b/openlp/plugins/songs/forms/editsongform.py index 8b1e3a897..b655c0f73 100644 --- a/openlp/plugins/songs/forms/editsongform.py +++ b/openlp/plugins/songs/forms/editsongform.py @@ -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) From 78e7cb58732da6ed9928b62419d12161f4285eeb Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 24 Mar 2014 14:32:28 +0100 Subject: [PATCH 04/21] Test for add_welcome_page --- tests/functional/openlp_core_lib/test_ui.py | 55 +++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/functional/openlp_core_lib/test_ui.py diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py new file mode 100644 index 000000000..747005bd8 --- /dev/null +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -0,0 +1,55 @@ +# -*- 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) From 8ad47cb888c4700271db911d09ed2291fa60d38b Mon Sep 17 00:00:00 2001 From: Tomas Groth Date: Mon, 24 Mar 2014 17:49:50 +0100 Subject: [PATCH 05/21] Fixes for windows tests and for starting OpenLP on windows. --- .../presentations/lib/pptviewcontroller.py | 5 +- .../presentations/test_pptviewcontroller.py | 65 ++++++++++++++++++- tests/helpers/testmixin.py | 5 +- .../openlp_core_lib/test_pluginmanager.py | 4 ++ 4 files changed, 71 insertions(+), 8 deletions(-) diff --git a/openlp/plugins/presentations/lib/pptviewcontroller.py b/openlp/plugins/presentations/lib/pptviewcontroller.py index 46195478d..16008f7e9 100644 --- a/openlp/plugins/presentations/lib/pptviewcontroller.py +++ b/openlp/plugins/presentations/lib/pptviewcontroller.py @@ -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) diff --git a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py index 5f25c4d3a..1b43615f3 100644 --- a/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py +++ b/tests/functional/openlp_plugins/presentations/test_pptviewcontroller.py @@ -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): """ diff --git a/tests/helpers/testmixin.py b/tests/helpers/testmixin.py index bda6d7291..b4e8a5c59 100644 --- a/tests/helpers/testmixin.py +++ b/tests/helpers/testmixin.py @@ -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.close(self.fd) os.unlink(Settings().fileName()) diff --git a/tests/interfaces/openlp_core_lib/test_pluginmanager.py b/tests/interfaces/openlp_core_lib/test_pluginmanager.py index 2949a46fb..054aa6dc1 100644 --- a/tests/interfaces/openlp_core_lib/test_pluginmanager.py +++ b/tests/interfaces/openlp_core_lib/test_pluginmanager.py @@ -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): From a4829c62ba1f408065ffb48b89239ab019e440b3 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 26 Mar 2014 12:26:16 +0100 Subject: [PATCH 06/21] Add missing arguments for debug mode Fixes: https://launchpad.net/bugs/1296574 --- openlp/core/ui/slidecontroller.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index eb1f13fa6..7faf10ca2 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -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 """ From bf32fe164a380cf18c3915976d57254f4a6f4cbe Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Wed, 26 Mar 2014 13:06:48 +0100 Subject: [PATCH 07/21] Test --- openlp/core/lib/ui.py | 6 +++-- tests/functional/openlp_core_lib/test_ui.py | 28 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/openlp/core/lib/ui.py b/openlp/core/lib/ui.py index 6d9e9e8f8..3126d1a56 100644 --- a/openlp/core/lib/ui.py +++ b/openlp/core/lib/ui.py @@ -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 diff --git a/tests/functional/openlp_core_lib/test_ui.py b/tests/functional/openlp_core_lib/test_ui.py index 747005bd8..babc94a81 100644 --- a/tests/functional/openlp_core_lib/test_ui.py +++ b/tests/functional/openlp_core_lib/test_ui.py @@ -53,3 +53,31 @@ class TestUi(TestCase): # 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])) From e4c3f08a2a82a70eb9b4bb0e20f2308b63d102eb Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Sat, 29 Mar 2014 15:31:28 +0200 Subject: [PATCH 08/21] Fix some of the tests which were failing in Python 3.4 --- openlp/plugins/songs/lib/easyslidesimport.py | 2 +- openlp/plugins/songs/lib/ewimport.py | 2 +- openlp/plugins/songs/lib/songbeamerimport.py | 2 +- .../plugins/songs/lib/songshowplusimport.py | 2 +- .../openlp_plugins/songs/test_ewimport.py | 22 +++++++++---------- .../songs/test_songbeamerimport.py | 8 +++---- .../songs/test_songshowplusimport.py | 10 ++++----- tests/helpers/songfileimport.py | 2 +- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/openlp/plugins/songs/lib/easyslidesimport.py b/openlp/plugins/songs/lib/easyslidesimport.py index 628d64880..e28e7bf97 100644 --- a/openlp/plugins/songs/lib/easyslidesimport.py +++ b/openlp/plugins/songs/lib/easyslidesimport.py @@ -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) diff --git a/openlp/plugins/songs/lib/ewimport.py b/openlp/plugins/songs/lib/ewimport.py index 9e3ae566e..cde0b1692 100644 --- a/openlp/plugins/songs/lib/ewimport.py +++ b/openlp/plugins/songs/lib/ewimport.py @@ -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): """ diff --git a/openlp/plugins/songs/lib/songbeamerimport.py b/openlp/plugins/songs/lib/songbeamerimport.py index 4de763554..0106ca687 100644 --- a/openlp/plugins/songs/lib/songbeamerimport.py +++ b/openlp/plugins/songs/lib/songbeamerimport.py @@ -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): """ diff --git a/openlp/plugins/songs/lib/songshowplusimport.py b/openlp/plugins/songs/lib/songshowplusimport.py index 485a8f047..0a8dc4650 100644 --- a/openlp/plugins/songs/lib/songshowplusimport.py +++ b/openlp/plugins/songs/lib/songshowplusimport.py @@ -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): """ diff --git a/tests/functional/openlp_plugins/songs/test_ewimport.py b/tests/functional/openlp_plugins/songs/test_ewimport.py index a22519bec..f98ba57d2 100644 --- a/tests/functional/openlp_plugins/songs/test_ewimport.py +++ b/tests/functional/openlp_plugins/songs/test_ewimport.py @@ -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' diff --git a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py index 72901eb47..a968975b3 100644 --- a/tests/functional/openlp_plugins/songs/test_songbeamerimport.py +++ b/tests/functional/openlp_plugins/songs/test_songbeamerimport.py @@ -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 diff --git a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py index 5c945efb3..16af347b2 100644 --- a/tests/functional/openlp_plugins/songs/test_songshowplusimport.py +++ b/tests/functional/openlp_plugins/songs/test_songshowplusimport.py @@ -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'), diff --git a/tests/helpers/songfileimport.py b/tests/helpers/songfileimport.py index 2ffecb11a..12754f55a 100644 --- a/tests/helpers/songfileimport.py +++ b/tests/helpers/songfileimport.py @@ -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 = [] From 80284f6f8d7f6e69b9e80fa6cc4c63380a1682d9 Mon Sep 17 00:00:00 2001 From: Samuel Mehrbrodt Date: Mon, 31 Mar 2014 10:08:07 +0200 Subject: [PATCH 09/21] =?UTF-8?q?Added=20'Neue=20evangelistische=20=C3=9Cb?= =?UTF-8?q?ersetzung'=20to=20the=20list=20of=20Online=20bible=20translatio?= =?UTF-8?q?ns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bibles/resources/bibles_resources.sqlite | Bin 104448 -> 104448 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/openlp/plugins/bibles/resources/bibles_resources.sqlite b/openlp/plugins/bibles/resources/bibles_resources.sqlite index c0fa931d1bb88b6e6c0224feff9cbc5aa61c7312..8f1777124032ee11b547fd6e1897f7e9b8b717a9 100644 GIT binary patch delta 140 zcmZqJz}B#VZGtr8lZi6UtWOwppD}GrvEyc2pv27V$vF9Sk=*8E+;$J-cp0@sn0GQy zV$NjtVEV;$nrRkOJQF+PE5_Z7HH=yt8#x#a{Tmh785(&d85#W;QW;8tSOJL37!n!s n7}9~P9EMDWVulhhnaq&EkUIIpOKG-LhC2)z%$sZ8wyXjGKRhSf delta 78 zcmV-U0I~moum*sz29O&8*oM;vcbOJ_`n)A_D_S0h8-;9<#;^KHwY-0eln# kxdV~|X#+?C`U1-WngU+}2Lb8 Date: Mon, 31 Mar 2014 18:08:42 +0200 Subject: [PATCH 10/21] added jenkins script --- tests/utils/jenkins-script.py | 188 ++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 tests/utils/jenkins-script.py diff --git a/tests/utils/jenkins-script.py b/tests/utils/jenkins-script.py new file mode 100644 index 000000000..3a6f34a13 --- /dev/null +++ b/tests/utils/jenkins-script.py @@ -0,0 +1,188 @@ +#!/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 isntall jenkins-webapi + +You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login: + + alias ci="python PATH-TO-THIS-SCRIPT TOKEN" + +If you do not know/have the token, then please ask for it (e. g. IRC). +""" + +from optparse import OptionParser +from subprocess import Popen, PIPE +import sys +import time + +from jenkins import Jenkins +from PyQt4 import QtCore, QtGui + + +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' + + Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows] + + +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 + # Check if trunk. Note, bzr info can return both 'push branch' as well as 'checkout of branch'. The later can + # occur before the first. So we do not want to break here to make sure we find any orrucances of + elif 'checkout of branch' in line: + repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/+branch/', 'lp:') + 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='Print output for the merge request.') + 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) + jenkins_trigger.trigger_build() + # Open the browser before printing the output. + if options.open_browser: + jenkins_trigger.open_browser() + if options.enable_output: + jenkins_trigger.print_output() + else: + raise Exception() + +if __name__ == '__main__': + main() From 5f0b40fe3017a41222f85056d6be65d8c2c07b5a Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:27:26 +0200 Subject: [PATCH 11/21] Moved script --- tests/utils/jenkins-script.py => scripts/jenkins_script.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/utils/jenkins-script.py => scripts/jenkins_script.py (100%) diff --git a/tests/utils/jenkins-script.py b/scripts/jenkins_script.py similarity index 100% rename from tests/utils/jenkins-script.py rename to scripts/jenkins_script.py From 4bed39869a7846720cf60d564f44585e8d9ca6e0 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:30:07 +0200 Subject: [PATCH 12/21] updated check_dependencies.py sciprt --- scripts/check_dependencies.py | 1 + scripts/jenkins_script.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index adb6a47f2..bab1c6419 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -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: enkins-webapi)', True), ] w = sys.stdout.write diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 3a6f34a13..eefe74150 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -45,7 +45,6 @@ import sys import time from jenkins import Jenkins -from PyQt4 import QtCore, QtGui JENKINS_URL = 'http://ci.openlp.org/' From 716a79bcc576547512982d7dc24cd5e5dc9fd894 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:37:43 +0200 Subject: [PATCH 13/21] cleaned doc --- scripts/jenkins_script.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index eefe74150..d275463c3 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -40,6 +40,7 @@ If you do not know/have the token, then please ask for it (e. g. IRC). """ from optparse import OptionParser +from requests.exceptions import HTTPError from subprocess import Popen, PIPE import sys import time @@ -58,8 +59,9 @@ class OpenLPJobs(object): 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] + Jobs = [Branch_Pull, Branch_Functional, Branch_Interface, Branch_Windows, Branch_PEP] class JenkinsTrigger(object): @@ -113,9 +115,9 @@ class JenkinsTrigger(object): 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) + #if result_string == "FAILURE": + # url += 'console' + # Popen(('xdg-open', url), stderr=PIPE) def get_repo_name(): @@ -162,7 +164,7 @@ def main(): parser = OptionParser(usage=usage) parser.add_option('-d', '--disable-output', dest='enable_output', action="store_false", default=True, - help='Print output for the merge request.') + 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, @@ -174,14 +176,18 @@ def main(): return token = args[-1] jenkins_trigger = JenkinsTrigger(token) - jenkins_trigger.trigger_build() + 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: - raise Exception() + parser.print_help() if __name__ == '__main__': main() From 6204454bfbe70fffbe51eb531ebc60c48c09554c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 18:40:36 +0200 Subject: [PATCH 14/21] updated doc --- scripts/jenkins_script.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index d275463c3..1a655799b 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -32,11 +32,11 @@ This script helps to trigger builds of branches. To use it you have to install t pip3 isntall jenkins-webapi -You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login: +You probably want to create an alias. Add this to your ~/.bashrc file and then logout and login (to apply the alias): - alias ci="python PATH-TO-THIS-SCRIPT TOKEN" + alias ci="python3 ~/Projects/OpenLP/trunk/script/jenkins_script.py TOKEN" -If you do not know/have the token, then please ask for it (e. g. IRC). +You can look up the token in the Branch-01-Pull job configuration or ask in IRC. """ from optparse import OptionParser From c451a8eafa71d7c3ed67b393551f7e20159188f8 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:01:09 +0200 Subject: [PATCH 15/21] added test --- openlp/core/common/registry.py | 1 + scripts/jenkins_script.py | 2 +- .../functional/openlp_core_common/test_registry.py | 14 ++++++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 014a534f7..33eb64880 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -151,6 +151,7 @@ class Registry(object): if result: results.append(result) except TypeError: + print("sdf") # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function %s', function) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 1a655799b..0e29271a2 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -30,7 +30,7 @@ """ This script helps to trigger builds of branches. To use it you have to install the jenkins-webapi package: - pip3 isntall jenkins-webapi + 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): diff --git a/tests/functional/openlp_core_common/test_registry.py b/tests/functional/openlp_core_common/test_registry.py index a57d7ea85..e27b69d10 100644 --- a/tests/functional/openlp_core_common/test_registry.py +++ b/tests/functional/openlp_core_common/test_registry.py @@ -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" From 52279248462f191b9aeb0f103c39c0803db2b25c Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:29:14 +0200 Subject: [PATCH 16/21] fix: removed stray print --- openlp/core/common/registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 33eb64880..014a534f7 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -151,7 +151,6 @@ class Registry(object): if result: results.append(result) except TypeError: - print("sdf") # Who has called me can help in debugging trace_error_handler(log) log.exception('Exception for function %s', function) From 20e7517b033c82f8b9ebcee157fd86e9cf969737 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:30:14 +0200 Subject: [PATCH 17/21] doc: shortened path --- scripts/jenkins_script.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 0e29271a2..cd75bbc6e 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -34,7 +34,7 @@ This script helps to trigger builds of branches. To use it you have to install t 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 ~/Projects/OpenLP/trunk/script/jenkins_script.py TOKEN" + 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 3db984a846290345b589b294a18d9dfc13cee05e Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:32:49 +0200 Subject: [PATCH 18/21] clean up: simplify code --- openlp/core/common/registry.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/openlp/core/common/registry.py b/openlp/core/common/registry.py index 014a534f7..218325823 100644 --- a/openlp/core/common/registry.py +++ b/openlp/core/common/registry.py @@ -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) From 6235fa19a912e57e4a64c0b3841670bc91af77ca Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:52:49 +0200 Subject: [PATCH 19/21] long line --- scripts/jenkins_script.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index cd75bbc6e..75d4f6376 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -167,8 +167,8 @@ def main(): 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.') + #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: From ab4cbfd85ee86295a3bf65508b27a98735fdab49 Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 19:57:15 +0200 Subject: [PATCH 20/21] fix: spelling mistake --- scripts/check_dependencies.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/check_dependencies.py b/scripts/check_dependencies.py index bab1c6419..5298139be 100755 --- a/scripts/check_dependencies.py +++ b/scripts/check_dependencies.py @@ -94,7 +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: enkins-webapi)', True), + ('jenkins', '(access jenkins api - package name: jenkins-webapi)', True), ] w = sys.stdout.write From 9d9a8145f64fc59ac98c3dc8637f4c04cb5ed4ce Mon Sep 17 00:00:00 2001 From: Andreas Preikschat Date: Mon, 31 Mar 2014 20:10:14 +0200 Subject: [PATCH 21/21] fixed dectection --- scripts/jenkins_script.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/jenkins_script.py b/scripts/jenkins_script.py index 75d4f6376..386ab69ef 100644 --- a/scripts/jenkins_script.py +++ b/scripts/jenkins_script.py @@ -141,10 +141,9 @@ def get_repo_name(): if 'push branch' in line: repo_name = line.replace('push branch: bzr+ssh://bazaar.launchpad.net/', 'lp:') break - # Check if trunk. Note, bzr info can return both 'push branch' as well as 'checkout of branch'. The later can - # occur before the first. So we do not want to break here to make sure we find any orrucances of elif 'checkout of branch' in line: - repo_name = line.replace('checkout of branch: bzr+ssh://bazaar.launchpad.net/+branch/', 'lp:') + 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?