First merge for 2.4.

A Number of small bug fixes which could be done in 2.2.
Clean up all the 2.0 to 2.2 migrations stuff.
Create a 2.2 to 2.4 migration for settings.
Fix problems with the Tag test so you do not need to restart a branch each time we do a release.

lp:~trb143/openlp/bugs-2_4b (revision 2578)
[SUCCESS] https//ci.openlp.io/job/Branch-01-Pull/1164/
[SUCCESS] https//ci.openlp.io/job/Branch-02-Functional-Tests/1087/
[SUCCESS] https//ci.openlp.io/job/Branch-03-Interface-Tests/1028/...

bzr-revno: 2566
Fixes: https://launchpad.net/bugs/913508, https://launchpad.net/bugs/1168778, https://launchpad.net/bugs/1281100, https://launchpad.net/bugs/1390699, https://launchpad.net/bugs/1390706, https://launchpad.net/bugs/1413217, https://launchpad.net/bugs/1420276, https://launchpad.net/bugs/1439304, https://launchpad.net/bugs/1487014
This commit is contained in:
tim.bentley@gmail.com 2015-11-05 19:05:19 +00:00 committed by Tim Bentley
commit 7c5add7fac
31 changed files with 351 additions and 312 deletions

View File

@ -30,7 +30,7 @@ logging and a plugin framework are contained within the openlp.core module.
import os
import sys
import logging
from optparse import OptionParser
import argparse
from traceback import format_exception
import shutil
import time
@ -282,17 +282,18 @@ def parse_options(args):
:return: a tuple of parsed options of type optparse.Value and a list of remaining argsZ
"""
# Set up command line options.
usage = 'Usage: %prog [options] [qt-options]'
parser = OptionParser(usage=usage)
parser.add_option('-e', '--no-error-form', dest='no_error_form', action='store_true',
parser = argparse.ArgumentParser(prog='openlp.py')
parser.add_argument('-e', '--no-error-form', dest='no_error_form', action='store_true',
help='Disable the error notification form.')
parser.add_option('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL',
parser.add_argument('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL',
help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".')
parser.add_option('-p', '--portable', dest='portable', action='store_true',
help='Specify if this should be run as a portable app, off a USB flash drive (not implemented).')
parser.add_option('-d', '--dev-version', dest='dev_version', action='store_true',
parser.add_argument('-p', '--portable', dest='portable', action='store_true',
help='Specify if this should be run as a portable app, '
'off a USB flash drive (not implemented).')
parser.add_argument('-d', '--dev-version', dest='dev_version', action='store_true',
help='Ignore the version file and pull the version directly from Bazaar')
parser.add_option('-s', '--style', dest='style', help='Set the Qt4 style (passed directly to Qt4).')
parser.add_argument('-s', '--style', dest='style', help='Set the Qt4 style (passed directly to Qt4).')
parser.add_argument('rargs', nargs='?', default=[])
# Parse command line options and deal with them. Use args supplied pragmatically if possible.
return parser.parse_args(args) if args else parser.parse_args()
@ -318,18 +319,18 @@ def main(args=None):
:param args: Some args
"""
(options, args) = parse_options(args)
args = parse_options(args)
qt_args = []
if options.loglevel.lower() in ['d', 'debug']:
if args and args.loglevel.lower() in ['d', 'debug']:
log.setLevel(logging.DEBUG)
elif options.loglevel.lower() in ['w', 'warning']:
elif args and args.loglevel.lower() in ['w', 'warning']:
log.setLevel(logging.WARNING)
else:
log.setLevel(logging.INFO)
if options.style:
qt_args.extend(['-style', options.style])
if args and args.style:
qt_args.extend(['-style', args.style])
# Throw the rest of the arguments at Qt, just in case.
qt_args.extend(args)
qt_args.extend(args.rargs)
# Bug #1018855: Set the WM_CLASS property in X11
if not is_win() and not is_macosx():
qt_args.append('OpenLP')
@ -339,7 +340,7 @@ def main(args=None):
application = OpenLP(qt_args)
application.setOrganizationName('OpenLP')
application.setOrganizationDomain('openlp.org')
if options.portable:
if args and args.portable:
application.setApplicationName('OpenLPPortable')
Settings.setDefaultFormat(Settings.IniFormat)
# Get location OpenLPPortable.ini
@ -383,6 +384,6 @@ def main(args=None):
application.installTranslator(default_translator)
else:
log.debug('Could not find default_translator.')
if not options.no_error_form:
if args and not args.no_error_form:
sys.excepthook = application.hook_exception
sys.exit(application.run(qt_args))

View File

@ -118,6 +118,7 @@ class Settings(QtCore.QSettings):
'advanced/slide limits': SlideLimits.End,
'advanced/single click preview': False,
'advanced/x11 bypass wm': X11_BYPASS_DEFAULT,
'advanced/search as type': True,
'crashreport/last directory': '',
'formattingTags/html_tags': '',
'core/audio repeat list': False,
@ -321,48 +322,10 @@ class Settings(QtCore.QSettings):
}
__file_path__ = ''
__obsolete_settings__ = [
# Changed during 1.9.x development.
('bibles/bookname language', 'bibles/book name language', []),
('general/enable slide loop', 'advanced/slide limits', [(SlideLimits.Wrap, True), (SlideLimits.End, False)]),
('songs/ccli number', 'core/ccli number', []),
('media/use phonon', '', []),
# Changed during 2.1.x development.
('advanced/stylesheet fix', '', []),
('bibles/last directory 1', 'bibles/last directory import', []),
('media/background color', 'players/background color', []),
('themes/last directory', 'themes/last directory import', []),
('themes/last directory 1', 'themes/last directory export', []),
('songs/last directory 1', 'songs/last directory import', []),
('songusage/last directory 1', 'songusage/last directory export', []),
('user interface/mainwindow splitter geometry', 'user interface/main window splitter geometry', []),
('shortcuts/makeLive', 'shortcuts/make_live', []),
('general/audio repeat list', 'core/audio repeat list', []),
('general/auto open', 'core/auto open', []),
('general/auto preview', 'core/auto preview', []),
('general/audio start paused', 'core/audio start paused', []),
('general/auto unblank', 'core/auto unblank', []),
('general/blank warning', 'core/blank warning', []),
('general/ccli number', 'core/ccli number', []),
('general/has run wizard', 'core/has run wizard', []),
('general/language', 'core/language', []),
('general/last version test', 'core/last version test', []),
('general/loop delay', 'core/loop delay', []),
('general/recent files', 'core/recent files', [(recent_files_conv, None)]),
('general/save prompt', 'core/save prompt', []),
('general/screen blank', 'core/screen blank', []),
('general/show splash', 'core/show splash', []),
('general/songselect password', 'core/songselect password', []),
('general/songselect username', 'core/songselect username', []),
('general/update check', 'core/update check', []),
('general/view mode', 'core/view mode', []),
('general/display on monitor', 'core/display on monitor', []),
('general/override position', 'core/override position', []),
('general/x position', 'core/x position', []),
('general/y position', 'core/y position', []),
('general/monitor', 'core/monitor', []),
('general/height', 'core/height', []),
('general/monitor', 'core/monitor', []),
('general/width', 'core/width', [])
# Changed during 2.2.x development.
# ('advanced/stylesheet fix', '', []),
# ('general/recent files', 'core/recent files', [(recent_files_conv, None)]),
('songs/search as type', 'advanced/search as type', [])
]
@staticmethod

View File

@ -108,8 +108,9 @@ class UiStrings(object):
self.NFSp = translate('OpenLP.Ui', 'No Files Selected', 'Plural')
self.NISs = translate('OpenLP.Ui', 'No Item Selected', 'Singular')
self.NISp = translate('OpenLP.Ui', 'No Items Selected', 'Plural')
self.OLPV2 = translate('OpenLP.Ui', 'OpenLP 2')
self.OLPV2x = translate('OpenLP.Ui', 'OpenLP 2.2')
self.OLP = translate('OpenLP.Ui', 'OpenLP')
self.OLPV2 = "%s %s" % (self.OLP, "2")
self.OLPV2x = "%s %s" % (self.OLP, "2.4")
self.OpenLPStart = translate('OpenLP.Ui', 'OpenLP is already running. Do you wish to continue?')
self.OpenService = translate('OpenLP.Ui', 'Open service.')
self.PlaySlidesInLoop = translate('OpenLP.Ui', 'Play Slides in Loop')

View File

@ -288,13 +288,7 @@ class Plugin(QtCore.QObject, RegistryProperties):
"""
Perform tasks on application startup
"""
# FIXME: Remove after 2.2 release.
# This is needed to load the list of media/presentation from the config saved before the settings rewrite.
if self.media_item_class is not None and self.name != 'images':
loaded_list = Settings().get_files_from_config(self)
# Now save the list to the config using our Settings class.
if loaded_list:
Settings().setValue('%s/%s files' % (self.settings_section, self.name), loaded_list)
pass
def uses_theme(self, theme):
"""

View File

@ -129,7 +129,7 @@ class ItemCapabilities(object):
OnLoadUpdate = 8
AddIfNewItem = 9
ProvidesOwnDisplay = 10
HasDetailedTitleDisplay = 11
# HasDetailedTitleDisplay = 11
HasVariableStartTime = 12
CanSoftBreak = 13
CanWordSplit = 14
@ -415,11 +415,6 @@ class ServiceItem(RegistryProperties):
self.will_auto_start = header.get('will_auto_start', False)
self.processor = header.get('processor', None)
self.has_original_files = True
# TODO: Remove me in 2,3 build phase
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
self.processor = self.title
self.title = None
if 'background_audio' in header:
self.background_audio = []
for filename in header['background_audio']:

View File

@ -80,6 +80,9 @@ class AdvancedTab(SettingsTab):
self.expand_service_item_check_box = QtGui.QCheckBox(self.ui_group_box)
self.expand_service_item_check_box.setObjectName('expand_service_item_check_box')
self.ui_layout.addRow(self.expand_service_item_check_box)
self.search_as_type_check_box = QtGui.QCheckBox(self.ui_group_box)
self.search_as_type_check_box.setObjectName('SearchAsType_check_box')
self.ui_layout.addRow(self.search_as_type_check_box)
self.enable_auto_close_check_box = QtGui.QCheckBox(self.ui_group_box)
self.enable_auto_close_check_box.setObjectName('enable_auto_close_check_box')
self.ui_layout.addRow(self.enable_auto_close_check_box)
@ -251,6 +254,7 @@ class AdvancedTab(SettingsTab):
self.end_slide_radio_button.clicked.connect(self.on_end_slide_button_clicked)
self.wrap_slide_radio_button.clicked.connect(self.on_wrap_slide_button_clicked)
self.next_item_radio_button.clicked.connect(self.on_next_item_button_clicked)
self.search_as_type_check_box.stateChanged.connect(self.on_search_as_type_check_box_changed)
def retranslateUi(self):
"""
@ -319,6 +323,7 @@ class AdvancedTab(SettingsTab):
self.end_slide_radio_button.setText(translate('OpenLP.GeneralTab', '&Remain on Slide'))
self.wrap_slide_radio_button.setText(translate('OpenLP.GeneralTab', '&Wrap around'))
self.next_item_radio_button.setText(translate('OpenLP.GeneralTab', '&Move to next/previous service item'))
self.search_as_type_check_box.setText(translate('SongsPlugin.GeneralTab', 'Enable search as you type'))
def load(self):
"""
@ -349,6 +354,8 @@ class AdvancedTab(SettingsTab):
self.default_color = settings.value('default color')
self.default_file_edit.setText(settings.value('default image'))
self.slide_limits = settings.value('slide limits')
self.is_search_as_you_type_enabled = settings.value('search as type')
self.search_as_type_check_box.setChecked(self.is_search_as_you_type_enabled)
# Prevent the dialog displayed by the alternate_rows_check_box to display.
self.alternate_rows_check_box.blockSignals(True)
self.alternate_rows_check_box.setChecked(settings.value('alternate rows'))
@ -424,8 +431,14 @@ class AdvancedTab(SettingsTab):
settings.setValue('x11 bypass wm', self.x11_bypass_check_box.isChecked())
self.settings_form.register_post_process('config_screen_changed')
self.settings_form.register_post_process('slidecontroller_update_slide_limits')
settings.setValue('search as type', self.is_search_as_you_type_enabled)
settings.endGroup()
def on_search_as_type_check_box_changed(self, check_state):
self.is_search_as_you_type_enabled = (check_state == QtCore.Qt.Checked)
self.settings_form.register_post_process('songs_config_updated')
self.settings_form.register_post_process('custom_config_updated')
def cancel(self):
"""
Dialogue was cancelled, remove any pending data path change.

View File

@ -389,7 +389,7 @@ class Ui_MainWindow(object):
self.file_menu.setTitle(translate('OpenLP.MainWindow', '&File'))
self.file_import_menu.setTitle(translate('OpenLP.MainWindow', '&Import'))
self.file_export_menu.setTitle(translate('OpenLP.MainWindow', '&Export'))
self.recent_files_menu.setTitle(translate('OpenLP.MainWindow', '&Recent Files'))
self.recent_files_menu.setTitle(translate('OpenLP.MainWindow', '&Recent Services'))
self.view_menu.setTitle(translate('OpenLP.MainWindow', '&View'))
self.view_mode_menu.setTitle(translate('OpenLP.MainWindow', 'M&ode'))
self.tools_menu.setTitle(translate('OpenLP.MainWindow', '&Tools'))
@ -400,16 +400,16 @@ class Ui_MainWindow(object):
self.service_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Service Manager'))
self.theme_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Theme Manager'))
self.projector_manager_dock.setWindowTitle(translate('OpenLP.MainWindow', 'Projector Manager'))
self.file_new_item.setText(translate('OpenLP.MainWindow', '&New'))
self.file_new_item.setText(translate('OpenLP.MainWindow', '&New Service'))
self.file_new_item.setToolTip(UiStrings().NewService)
self.file_new_item.setStatusTip(UiStrings().CreateService)
self.file_open_item.setText(translate('OpenLP.MainWindow', '&Open'))
self.file_open_item.setText(translate('OpenLP.MainWindow', '&Open Service'))
self.file_open_item.setToolTip(UiStrings().OpenService)
self.file_open_item.setStatusTip(translate('OpenLP.MainWindow', 'Open an existing service.'))
self.file_save_item.setText(translate('OpenLP.MainWindow', '&Save'))
self.file_save_item.setText(translate('OpenLP.MainWindow', '&Save Service'))
self.file_save_item.setToolTip(UiStrings().SaveService)
self.file_save_item.setStatusTip(translate('OpenLP.MainWindow', 'Save the current service to disk.'))
self.file_save_as_item.setText(translate('OpenLP.MainWindow', 'Save &As...'))
self.file_save_as_item.setText(translate('OpenLP.MainWindow', 'Save Service &As...'))
self.file_save_as_item.setToolTip(translate('OpenLP.MainWindow', 'Save Service As'))
self.file_save_as_item.setStatusTip(translate('OpenLP.MainWindow',
'Save the current service under a new name.'))
@ -456,7 +456,7 @@ class Ui_MainWindow(object):
self.lock_panel.setText(translate('OpenLP.MainWindow', 'L&ock Panels'))
self.lock_panel.setStatusTip(translate('OpenLP.MainWindow', 'Prevent the panels being moved.'))
self.view_live_panel.setStatusTip(translate('OpenLP.MainWindow', 'Toggle the visibility of the live panel.'))
self.settings_plugin_list_item.setText(translate('OpenLP.MainWindow', '&Plugin List'))
self.settings_plugin_list_item.setText(translate('OpenLP.MainWindow', '&Manage Plugins'))
self.settings_plugin_list_item.setStatusTip(translate('OpenLP.MainWindow', 'List the Plugins'))
self.about_item.setText(translate('OpenLP.MainWindow', '&About'))
self.about_item.setStatusTip(translate('OpenLP.MainWindow', 'More information about OpenLP'))
@ -505,7 +505,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
super(MainWindow, self).__init__()
Registry().register('main_window', self)
self.clipboard = self.application.clipboard()
self.arguments = self.application.args
self.arguments = ''.join(self.application.args)
# Set up settings sections for the main application (not for use by plugins).
self.ui_settings_section = 'user interface'
self.general_settings_section = 'core'
@ -634,7 +634,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
self.live_controller.display.setFocus()
self.activateWindow()
if self.arguments:
self.open_cmd_line_files()
self.open_cmd_line_files(self.arguments)
elif Settings().value(self.general_settings_section + '/auto open'):
self.service_manager_contents.load_last_file()
view_mode = Settings().value('%s/view mode' % self.general_settings_section)
@ -1416,14 +1416,10 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
settings.remove('advanced/data path')
self.application.set_normal_cursor()
def open_cmd_line_files(self):
def open_cmd_line_files(self, filename):
"""
Open files passed in through command line arguments
"""
args = []
for a in self.arguments:
args.extend([a])
for filename in args:
if not isinstance(filename, str):
filename = str(filename, sys.getfilesystemencoding())
if filename.endswith(('.osz', '.oszl')):

View File

@ -33,21 +33,21 @@ class Ui_PluginViewDialog(object):
"""
The UI of the plugin view dialog
"""
def setupUi(self, pluginViewDialog):
def setupUi(self, plugin_view_dialog):
"""
Set up the UI
"""
pluginViewDialog.setObjectName('pluginViewDialog')
pluginViewDialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
pluginViewDialog.setWindowModality(QtCore.Qt.ApplicationModal)
self.plugin_layout = QtGui.QVBoxLayout(pluginViewDialog)
plugin_view_dialog.setObjectName('plugin_view_dialog')
plugin_view_dialog.setWindowIcon(build_icon(u':/icon/openlp-logo.svg'))
plugin_view_dialog.setWindowModality(QtCore.Qt.ApplicationModal)
self.plugin_layout = QtGui.QVBoxLayout(plugin_view_dialog)
self.plugin_layout.setObjectName('plugin_layout')
self.list_layout = QtGui.QHBoxLayout()
self.list_layout.setObjectName('list_layout')
self.plugin_list_widget = QtGui.QListWidget(pluginViewDialog)
self.plugin_list_widget = QtGui.QListWidget(plugin_view_dialog)
self.plugin_list_widget.setObjectName('plugin_list_widget')
self.list_layout.addWidget(self.plugin_list_widget)
self.plugin_info_group_box = QtGui.QGroupBox(pluginViewDialog)
self.plugin_info_group_box = QtGui.QGroupBox(plugin_view_dialog)
self.plugin_info_group_box.setObjectName('plugin_info_group_box')
self.plugin_info_layout = QtGui.QFormLayout(self.plugin_info_group_box)
self.plugin_info_layout.setObjectName('plugin_info_layout')
@ -70,15 +70,15 @@ class Ui_PluginViewDialog(object):
self.plugin_info_layout.addRow(self.about_label, self.about_text_browser)
self.list_layout.addWidget(self.plugin_info_group_box)
self.plugin_layout.addLayout(self.list_layout)
self.button_box = create_button_box(pluginViewDialog, 'button_box', ['ok'])
self.button_box = create_button_box(plugin_view_dialog, 'button_box', ['ok'])
self.plugin_layout.addWidget(self.button_box)
self.retranslateUi(pluginViewDialog)
self.retranslateUi(plugin_view_dialog)
def retranslateUi(self, pluginViewDialog):
def retranslateUi(self, plugin_view_dialog):
"""
Translate the UI on the fly
"""
pluginViewDialog.setWindowTitle(translate('OpenLP.PluginForm', 'Plugin List'))
plugin_view_dialog.setWindowTitle(translate('OpenLP.PluginForm', 'Manage Plugins'))
self.plugin_info_group_box.setTitle(translate('OpenLP.PluginForm', 'Plugin Details'))
self.version_label.setText('%s:' % UiStrings().Version)
self.about_label.setText('%s:' % UiStrings().About)

View File

@ -162,7 +162,7 @@ class PrintServiceForm(QtGui.QDialog, Ui_PrintServiceDialog, RegistryProperties)
html_data = self._add_element('html')
self._add_element('head', parent=html_data)
self._add_element('title', self.title_line_edit.text(), html_data.head)
css_path = os.path.join(AppLocation.get_data_path(), 'service_print.css')
css_path = os.path.join(AppLocation.get_data_path(), 'serviceprint', 'service_print.css')
custom_css = get_text_file_string(css_path)
if not custom_css:
custom_css = DEFAULT_CSS

View File

@ -755,12 +755,19 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
return False
# check for use in the system else where.
if test_plugin:
plugin_usage = ""
for plugin in self.plugin_manager.plugins:
if plugin.uses_theme(theme):
critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
translate('OpenLP.ThemeManager',
'Theme %s is used in the %s plugin.')
% (theme, plugin.name))
used_count = plugin.uses_theme(theme)
if used_count:
plugin_usage = "%s%s" % (plugin_usage, (translate('OpenLP.ThemeManager',
'%s time(s) by %s') %
(used_count, plugin.name)))
plugin_usage = "%s\n" % plugin_usage
if plugin_usage:
critical_error_message_box(translate('OpenLP.ThemeManager', 'Unable to delete theme'),
translate('OpenLP.ThemeManager', 'Theme is currently used \n\n%s') %
plugin_usage)
return False
return True
return False

View File

@ -178,12 +178,14 @@ class BiblePlugin(Plugin):
def uses_theme(self, theme):
"""
Called to find out if the bible plugin is currently using a theme. Returns ``True`` if the theme is being used,
otherwise returns ``False``.
Called to find out if the bible plugin is currently using a theme. Returns ``1`` if the theme is being used,
otherwise returns ``0``.
:param theme: The theme
"""
return str(self.settings_tab.bible_theme) == theme
if str(self.settings_tab.bible_theme) == theme:
return 1
return 0
def rename_theme(self, old_theme, new_theme):
"""

View File

@ -476,16 +476,6 @@ class BibleDB(QtCore.QObject, Manager, RegistryProperties):
self.save_meta('language_id', language_id)
return language_id
def is_old_database(self):
"""
Returns ``True`` if it is a bible database, which has been created prior to 1.9.6.
"""
try:
self.session.query(Book).all()
except:
return True
return False
def dump_bible(self):
"""
Utility debugging method to dump the contents of a bible.

View File

@ -129,11 +129,6 @@ class BibleManager(RegistryProperties):
bible.session.close()
delete_file(os.path.join(self.path, filename))
continue
# Find old database versions.
if bible.is_old_database():
self.old_bible_databases.append([filename, name])
bible.session.close()
continue
log.debug('Bible Name: "%s"', name)
self.db_cache[name] = bible
# Look to see if lazy load bible exists and get create getter.

View File

@ -72,11 +72,9 @@ class CustomPlugin(Plugin):
"""
Called to find out if the custom plugin is currently using a theme.
Returns True if the theme is being used, otherwise returns False.
Returns count of the times the theme is used.
"""
if self.db_manager.get_all_objects(CustomSlide, CustomSlide.theme_name == theme):
return True
return False
return len(self.db_manager.get_all_objects(CustomSlide, CustomSlide.theme_name == theme))
def rename_theme(self, old_theme, new_theme):
"""

View File

@ -85,6 +85,7 @@ class CustomMediaItem(MediaManagerItem):
"""
log.debug('Config loaded')
self.add_custom_from_service = Settings().value(self.settings_section + '/add custom from service')
self.is_search_as_you_type_enabled = Settings().value('advanced/search as type')
def retranslateUi(self):
"""
@ -269,6 +270,7 @@ class CustomMediaItem(MediaManagerItem):
:param text: The search text
"""
if self.is_search_as_you_type_enabled:
search_length = 2
if len(text) > search_length:
self.on_search_text_button_clicked()

View File

@ -67,36 +67,13 @@ class ImagePlugin(Plugin):
'provided by the theme.')
return about_text
def app_startup(self):
"""
Perform tasks on application startup.
"""
# TODO: Can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
Plugin.app_startup(self)
# Convert old settings-based image list to the database.
files_from_config = Settings().get_files_from_config(self)
if files_from_config:
for file in files_from_config:
filename = os.path.split(file)[1]
thumb = os.path.join(self.media_item.service_path, filename)
try:
os.remove(thumb)
except:
pass
log.debug('Importing images list from old config: %s' % files_from_config)
self.media_item.save_new_images_list(files_from_config)
def upgrade_settings(self, settings):
"""
Upgrade the settings of this plugin.
:param settings: The Settings object containing the old settings.
"""
# TODO: Can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
files_from_config = settings.get_files_from_config(self)
if files_from_config:
log.debug('Importing images list from old config: %s' % files_from_config)
self.media_item.save_new_images_list(files_from_config)
pass
def set_plugin_text_strings(self):
"""

View File

@ -119,14 +119,6 @@ class ImageMediaItem(MediaManagerItem):
icon=':/general/general_edit.png',
triggers=self.on_edit_click)
create_widget_action(self.list_view, separator=True)
if self.has_delete_icon:
create_widget_action(
self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
text=self.plugin.get_string(StringContent.Delete)['title'],
icon=':/general/general_delete.png',
can_shortcuts=True, triggers=self.on_delete_click)
create_widget_action(self.list_view, separator=True)
create_widget_action(
self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
@ -155,6 +147,14 @@ class ImageMediaItem(MediaManagerItem):
text=translate('OpenLP.MediaManagerItem', '&Add to selected Service Item'),
icon=':/general/general_add.png',
triggers=self.on_add_edit_click)
create_widget_action(self.list_view, separator=True)
if self.has_delete_icon:
create_widget_action(
self.list_view,
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
text=self.plugin.get_string(StringContent.Delete)['title'],
icon=':/general/general_delete.png',
can_shortcuts=True, triggers=self.on_delete_click)
self.add_custom_context_actions()
# Create the context menu and add all actions from the list_view.
self.menu = QtGui.QMenu()

View File

@ -137,22 +137,6 @@ class PresentationPlugin(Plugin):
self.register_controllers(controller)
return bool(self.controllers)
def app_startup(self):
"""
Perform tasks on application startup.
"""
# TODO: Can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
super().app_startup()
files_from_config = Settings().value('presentations/presentations files')
for file in files_from_config:
try:
self.media_item.clean_up_thumbnails(file, True)
except AttributeError:
pass
self.media_item.list_view.clear()
Settings().setValue('presentations/thumbnail_scheme', 'md5')
self.media_item.validate_and_load(files_from_config)
def about(self):
"""
Return information about this plugin.

View File

@ -271,9 +271,15 @@ window.OpenLP = {
if (typeof value[0] !== "number"){
value[0] = OpenLP.escapeString(value[0])
}
var txt = "";
if (value[2].length > 0) {
txt = value[1] + " ( " + value[2] + " )";
} else {
txt = value[1];
}
ul.append($("<li>").append($("<a>").attr("href", "#options")
.attr("data-rel", "dialog").attr("value", value[0])
.click(OpenLP.showOptions).text(value[1])));
.click(OpenLP.showOptions).text(txt)));
});
}
ul.listview("refresh");

View File

@ -309,10 +309,13 @@ class HttpRouter(RegistryProperties):
"""
Translate various strings in the mobile app.
"""
remote = translate('RemotePlugin.Mobile', 'Remote')
stage = translate('RemotePlugin.Mobile', 'Stage View')
live = translate('RemotePlugin.Mobile', 'Live View')
self.template_vars = {
'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Remote'),
'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Stage View'),
'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Live View'),
'app_title': "%s %s" % (UiStrings().OLPV2x, remote),
'stage_title': "%s %s" % (UiStrings().OLPV2x, stage),
'live_title': "%s %s" % (UiStrings().OLPV2x, live),
'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
'alerts': translate('RemotePlugin.Mobile', 'Alerts'),

View File

@ -115,7 +115,7 @@ class SongMediaItem(MediaManagerItem):
Is triggered when the songs config is updated
"""
log.debug('config_updated')
self.search_as_you_type = Settings().value(self.settings_section + '/search as type')
self.is_search_as_you_type_enabled = Settings().value('advanced/search as type')
self.update_service_on_edit = Settings().value(self.settings_section + '/update service on edit')
self.add_song_from_service = Settings().value(self.settings_section + '/add song from service')
self.display_songbook = Settings().value(self.settings_section + '/display songbook')
@ -279,7 +279,7 @@ class SongMediaItem(MediaManagerItem):
If search as type enabled invoke the search on each key press. If the Lyrics are being searched do not start
till 7 characters have been entered.
"""
if self.search_as_you_type:
if self.is_search_as_you_type_enabled:
search_length = 1
if self.search_text_edit.current_search_type() == SongSearch.Entire:
search_length = 4
@ -590,4 +590,4 @@ class SongMediaItem(MediaManagerItem):
:param show_error: Is this an error?
"""
search_results = self.search_entire(string)
return [[song.id, song.title] for song in search_results]
return [[song.id, song.title, song.alternate_title] for song in search_results]

View File

@ -121,17 +121,7 @@ class SongXML(object):
"""
self.song_xml = None
verse_list = []
if not xml.startswith('<?xml') and not xml.startswith('<song'):
# This is an old style song, without XML. Let's handle it correctly by iterating through the verses, and
# then recreating the internal xml object as well.
self.song_xml = objectify.fromstring('<song version="1.0" />')
self.lyrics = etree.SubElement(self.song_xml, 'lyrics')
verses = xml.split('\n\n')
for count, verse in enumerate(verses):
verse_list.append([{'type': 'v', 'label': str(count)}, str(verse)])
self.add_verse_to_lyrics('v', str(count), verse)
return verse_list
elif xml.startswith('<?xml'):
if xml.startswith('<?xml'):
xml = xml[38:]
try:
self.song_xml = objectify.fromstring(xml)

View File

@ -41,9 +41,6 @@ class SongsTab(SettingsTab):
self.mode_group_box.setObjectName('mode_group_box')
self.mode_layout = QtGui.QVBoxLayout(self.mode_group_box)
self.mode_layout.setObjectName('mode_layout')
self.search_as_type_check_box = QtGui.QCheckBox(self.mode_group_box)
self.search_as_type_check_box.setObjectName('SearchAsType_check_box')
self.mode_layout.addWidget(self.search_as_type_check_box)
self.tool_bar_active_check_box = QtGui.QCheckBox(self.mode_group_box)
self.tool_bar_active_check_box.setObjectName('tool_bar_active_check_box')
self.mode_layout.addWidget(self.tool_bar_active_check_box)
@ -62,7 +59,6 @@ class SongsTab(SettingsTab):
self.left_layout.addWidget(self.mode_group_box)
self.left_layout.addStretch()
self.right_layout.addStretch()
self.search_as_type_check_box.stateChanged.connect(self.on_search_as_type_check_box_changed)
self.tool_bar_active_check_box.stateChanged.connect(self.on_tool_bar_active_check_box_changed)
self.update_on_edit_check_box.stateChanged.connect(self.on_update_on_edit_check_box_changed)
self.add_from_service_check_box.stateChanged.connect(self.on_add_from_service_check_box_changed)
@ -71,7 +67,6 @@ class SongsTab(SettingsTab):
def retranslateUi(self):
self.mode_group_box.setTitle(translate('SongsPlugin.SongsTab', 'Songs Mode'))
self.search_as_type_check_box.setText(translate('SongsPlugin.SongsTab', 'Enable search as you type'))
self.tool_bar_active_check_box.setText(translate('SongsPlugin.SongsTab',
'Display verses on live tool bar'))
self.update_on_edit_check_box.setText(translate('SongsPlugin.SongsTab', 'Update service from song edit'))
@ -103,13 +98,11 @@ class SongsTab(SettingsTab):
def load(self):
settings = Settings()
settings.beginGroup(self.settings_section)
self.song_search = settings.value('search as type')
self.tool_bar = settings.value('display songbar')
self.update_edit = settings.value('update service on edit')
self.update_load = settings.value('add song from service')
self.display_songbook = settings.value('display songbook')
self.display_copyright_symbol = settings.value('display copyright symbol')
self.search_as_type_check_box.setChecked(self.song_search)
self.tool_bar_active_check_box.setChecked(self.tool_bar)
self.update_on_edit_check_box.setChecked(self.update_edit)
self.add_from_service_check_box.setChecked(self.update_load)
@ -120,7 +113,6 @@ class SongsTab(SettingsTab):
def save(self):
settings = Settings()
settings.beginGroup(self.settings_section)
settings.setValue('search as type', self.song_search)
settings.setValue('display songbar', self.tool_bar)
settings.setValue('update service on edit', self.update_edit)
settings.setValue('add song from service', self.update_load)

View File

@ -57,7 +57,6 @@ __default_settings__ = {
'songs/last search type': SongSearch.Entire,
'songs/last import type': SongFormat.OpenLyrics,
'songs/update service on edit': False,
'songs/search as type': True,
'songs/add song from service': True,
'songs/display songbar': True,
'songs/display songbook': False,
@ -226,11 +225,9 @@ class SongsPlugin(Plugin):
Called to find out if the song plugin is currently using a theme.
:param theme: The theme to check for usage
:return: True if the theme is being used, otherwise returns False
:return: count of the number of times the theme is used.
"""
if self.manager.get_all_objects(Song, Song.theme_name == theme):
return True
return False
return len(self.manager.get_all_objects(Song, Song.theme_name == theme))
def rename_theme(self, old_theme, new_theme):
"""

View File

@ -0,0 +1,144 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2015 OpenLP Developers #
# --------------------------------------------------------------------------- #
# This program is free software; you can redistribute it and/or modify it #
# under the terms of the GNU General Public License as published by the Free #
# Software Foundation; version 2 of the License. #
# #
# This program is distributed in the hope that it will be useful, but WITHOUT #
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
# more details. #
# #
# You should have received a copy of the GNU General Public License along #
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
###############################################################################
import sys
from unittest import TestCase
from openlp.core import parse_options
from tests.helpers.testmixin import TestMixin
class TestInitFunctions(TestMixin, TestCase):
def parse_options_basic_test(self):
"""
Test the parse options process works
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = []
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertFalse(args.dev_version, 'The dev_version flag should be False')
self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertFalse(args.portable, 'The portable flag should be set to false')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_debug_test(self):
"""
Test the parse options process works for debug only
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['-l debug']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertFalse(args.dev_version, 'The dev_version flag should be False')
self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertFalse(args.portable, 'The portable flag should be set to false')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_debug_and_portable_test(self):
"""
Test the parse options process works for debug and portable
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['--portable']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertFalse(args.dev_version, 'The dev_version flag should be False')
self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertTrue(args.portable, 'The portable flag should be set to true')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_all_no_file_test(self):
"""
Test the parse options process works with two options
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['-l debug', '-d']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertTrue(args.dev_version, 'The dev_version flag should be True')
self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertFalse(args.portable, 'The portable flag should be set to false')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, [], 'The service file should be blank')
def parse_options_file_test(self):
"""
Test the parse options process works with a file
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['dummy_temp']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertFalse(args.dev_version, 'The dev_version flag should be False')
self.assertEquals(args.loglevel, 'warning', 'The log level should be set to warning')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertFalse(args.portable, 'The portable flag should be set to false')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank')
def parse_options_file_and_debug_test(self):
"""
Test the parse options process works with a file
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['-l debug', 'dummy_temp']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertFalse(args.dev_version, 'The dev_version flag should be False')
self.assertEquals(args.loglevel, ' debug', 'The log level should be set to debug')
self.assertFalse(args.no_error_form, 'The no_error_form should be set to False')
self.assertFalse(args.portable, 'The portable flag should be set to false')
self.assertEquals(args.style, None, 'There are no style flags to be processed')
self.assertEquals(args.rargs, 'dummy_temp', 'The service file should not be blank')
def parse_options_two_files_test(self):
"""
Test the parse options process works with a file
"""
# GIVEN: a a set of system arguments.
sys.argv[1:] = ['dummy_temp', 'dummy_temp2']
# WHEN: We we parse them to expand to options
args = parse_options()
# THEN: the following fields will have been extracted.
self.assertEquals(args, None, 'The args should be None')

View File

@ -0,0 +1,69 @@
# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
###############################################################################
# OpenLP - Open Source Lyrics Projection #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2015 OpenLP Developers #
# --------------------------------------------------------------------------- #
# 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.ui.advancedtab package.
"""
from unittest import TestCase
from openlp.core.common import Registry
from openlp.core.ui.advancedtab import AdvancedTab
from openlp.core.ui.settingsform import SettingsForm
from tests.helpers.testmixin import TestMixin
class TestAdvancedTab(TestCase, TestMixin):
def setUp(self):
"""
Set up a few things for the tests
"""
Registry.create()
def test_creation(self):
"""
Test that Advanced Tab is created.
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
# WHEN: I create an advanced tab
advanced_tab = AdvancedTab(settings_form)
# THEN:
self.assertEqual("Advanced", advanced_tab.tab_title, 'The tab title should be Advanced')
def test_change_search_as_type(self):
"""
Test that when search as type is changed custom and song configs are updated
"""
# GIVEN: A new Advanced Tab
settings_form = SettingsForm(None)
advanced_tab = AdvancedTab(settings_form)
# WHEN: I change search as type check box
advanced_tab.on_search_as_type_check_box_changed(True)
# THEN: we should have two post save processed to run
self.assertEqual(2, len(settings_form.processes), 'Two post save processes should be created')
self.assertTrue("songs_config_updated" in settings_form.processes, 'The songs plugin should be called')
self.assertTrue("custom_config_updated" in settings_form.processes, 'The custom plugin should be called')

View File

@ -71,7 +71,7 @@ class TestMainWindow(TestCase, TestMixin):
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
# WHEN the argument is processed
self.main_window.open_cmd_line_files()
self.main_window.open_cmd_line_files(service)
# THEN the service from the arguments is loaded
mocked_load_path.assert_called_with(service), 'load_path should have been called with the service\'s path'
@ -86,7 +86,7 @@ class TestMainWindow(TestCase, TestMixin):
with patch('openlp.core.ui.servicemanager.ServiceManager.load_file') as mocked_load_path:
# WHEN the argument is processed
self.main_window.open_cmd_line_files()
self.main_window.open_cmd_line_files("")
# THEN the file should not be opened
assert not mocked_load_path.called, 'load_path should not have been called'

View File

@ -25,7 +25,7 @@ Package to test the openlp.core.utils.actions package.
from unittest import TestCase
from openlp.core.common.settings import Settings
from openlp.core.utils import VersionThread, get_application_version, get_uno_command
from openlp.core.utils import VersionThread, get_uno_command
from tests.functional import MagicMock, patch
from tests.helpers.testmixin import TestMixin

View File

@ -33,7 +33,6 @@ from openlp.core.common import Settings
from tests.helpers.testmixin import TestMixin
from tests.functional import MagicMock, patch, call
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resources'))
@ -132,60 +131,3 @@ class TestInit(TestCase, TestMixin):
# THEN: It should ask if we want to create a backup
self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!')
self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!')
@patch(u'openlp.core.OptionParser')
def parse_options_test(self, MockedOptionParser):
"""
Test that parse_options sets up OptionParser correctly and parses the options given
"""
# GIVEN: A list of valid options and a mocked out OptionParser object
options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args']
mocked_parser = MagicMock()
MockedOptionParser.return_value = mocked_parser
expected_calls = [
call('-e', '--no-error-form', dest='no_error_form', action='store_true',
help='Disable the error notification form.'),
call('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL',
help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".'),
call('-p', '--portable', dest='portable', action='store_true',
help='Specify if this should be run as a portable app, off a USB flash drive (not implemented).'),
call('-d', '--dev-version', dest='dev_version', action='store_true',
help='Ignore the version file and pull the version directly from Bazaar'),
call('-s', '--style', dest='style', help='Set the Qt4 style (passed directly to Qt4).')
]
# WHEN: Calling parse_options
parse_options(options)
# THEN: A tuple should be returned with the parsed options and left over options
MockedOptionParser.assert_called_with(usage='Usage: %prog [options] [qt-options]')
self.assertEquals(expected_calls, mocked_parser.add_option.call_args_list)
mocked_parser.parse_args.assert_called_with(options)
@patch(u'openlp.core.OptionParser')
def parse_options_from_sys_argv_test(self, MockedOptionParser):
"""
Test that parse_options sets up OptionParser correctly and parses sys.argv
"""
# GIVEN: A list of valid options and a mocked out OptionParser object
mocked_parser = MagicMock()
MockedOptionParser.return_value = mocked_parser
expected_calls = [
call('-e', '--no-error-form', dest='no_error_form', action='store_true',
help='Disable the error notification form.'),
call('-l', '--log-level', dest='loglevel', default='warning', metavar='LEVEL',
help='Set logging to LEVEL level. Valid values are "debug", "info", "warning".'),
call('-p', '--portable', dest='portable', action='store_true',
help='Specify if this should be run as a portable app, off a USB flash drive (not implemented).'),
call('-d', '--dev-version', dest='dev_version', action='store_true',
help='Ignore the version file and pull the version directly from Bazaar'),
call('-s', '--style', dest='style', help='Set the Qt4 style (passed directly to Qt4).')
]
# WHEN: Calling parse_options
parse_options([])
# THEN: A tuple should be returned with the parsed options and left over options
MockedOptionParser.assert_called_with(usage='Usage: %prog [options] [qt-options]')
self.assertEquals(expected_calls, mocked_parser.add_option.call_args_list)
mocked_parser.parse_args.assert_called_with()

View File

@ -23,39 +23,13 @@
Package to test for proper bzr tags.
"""
import os
import re
from unittest import TestCase
from subprocess import Popen, PIPE
TAGS = [
['1.9.0', '1'],
['1.9.1', '775'],
['1.9.2', '890'],
['1.9.3', '1063'],
['1.9.4', '1196'],
['1.9.5', '1421'],
['1.9.6', '1657'],
['1.9.7', '1761'],
['1.9.8', '1856'],
['1.9.9', '1917'],
['1.9.10', '2003'],
['1.9.11', '2039'],
['1.9.12', '2063'],
['2.0', '2118'],
['2.1.0', '2119'],
['2.1.1', '2438'],
['2.1.2', '2488'],
['2.1.3', '2513'],
['2.1.4', '2532'],
['2.1.5', '2543'],
['2.1.6', '2550'],
['2.2', '2562']
]
# Depending on the repository, we sometimes have the 2.0.x tags in the repo too. They come up with a revision number of
# "?", which I suspect is due to the fact that we're using shared repositories. This regular expression matches all
# 2.0.x tags.
TAG_SEARCH = re.compile('2\.0\.\d')
TAGS1 = {'1.9.0', '1.9.1', '1.9.2', '1.9.3', '1.9.4', '1.9.5', '1.9.6', '1.9.7', '1.9.8', '1.9.9', '1.9.10',
'1.9.11', '1.9.12', '2.0', '2.1.0', '2.1.1', '2.1.2', '2.1.3', '2.1.4', '2.1.5', '2.1.6', '2.2'
}
class TestBzrTags(TestCase):
@ -70,8 +44,12 @@ class TestBzrTags(TestCase):
# WHEN getting the branches tags
bzr = Popen(('bzr', 'tags', '--directory=' + path), stdout=PIPE)
std_out = bzr.communicate()[0]
tags = [line.decode('utf-8').split() for line in std_out.splitlines()]
tags = [t_r for t_r in tags if t_r[1] != '?' or not (t_r[1] == '?' and TAG_SEARCH.search(t_r[0]))]
count = len(TAGS1)
tags = [line.decode('utf-8').split()[0] for line in std_out.splitlines()]
count1 = 0
for t in tags:
if t in TAGS1:
count1 += 1
# THEN the tags should match the accepted tags
self.assertEqual(TAGS, tags, 'List of tags should match')
self.assertEqual(count, count1, 'List of tags should match')