From 90a91aa7888007f1119f851b6b51181cc289c98a Mon Sep 17 00:00:00 2001 From: Martin Thompson Date: Thu, 12 Mar 2009 20:19:24 +0000 Subject: [PATCH] Updated tests to pass again. Found some device dependent problems in the tests of the theme rendering (when monitor resolution changes, so does font size (in pixels)). This meant that the tests failed (as they are comparisons with golden-bitmaps). Have updated the tests to do a more "approximate comparison", with a low tolerance. This is slow currently, could do with optimising. bzr-revno: 411 --- openlp/core/lib/toolbar.py | 8 +- openlp/core/pluginmanager.py | 2 +- openlp/core/render.py | 9 +- .../core/test/data_for_tests/render_theme.xml | 3 +- openlp/core/test/test_mediamanageritem.py | 18 +-- openlp/core/test/test_plugin_manager.py | 10 +- openlp/core/test/test_render.py | 1 + openlp/core/test/test_render_theme.py | 61 ++++++---- openlp/core/theme/test/test_theme.py | 30 ++--- openlp/core/theme/test/test_theme.xml | 1 + openlp/core/theme/theme.py | 107 +++++++++++------- 11 files changed, 143 insertions(+), 107 deletions(-) diff --git a/openlp/core/lib/toolbar.py b/openlp/core/lib/toolbar.py index 78d469e73..95be246f3 100644 --- a/openlp/core/lib/toolbar.py +++ b/openlp/core/lib/toolbar.py @@ -36,6 +36,7 @@ class OpenLPToolbar(QtGui.QToolBar): """ A method to help developers easily add a button to the toolbar. """ + ButtonIcon = None if type(icon) is QtGui.QIcon: ButtonIcon = icon elif type(icon) is types.StringType: @@ -46,9 +47,10 @@ class OpenLPToolbar(QtGui.QToolBar): else: ButtonIcon.addPixmap(QtGui.QPixmap.fromImage(QtGui.QImage(icon)), QtGui.QIcon.Normal, QtGui.QIcon.Off) - ToolbarButton = self.addAction(ButtonIcon, title) - if tooltip is not None: - ToolbarButton.setToolTip(tooltip) + if ButtonIcon is not None: + ToolbarButton = self.addAction(ButtonIcon, title) + if tooltip is not None: + ToolbarButton.setToolTip(tooltip) if slot is not None: QtCore.QObject.connect(ToolbarButton, QtCore.SIGNAL('triggered()'), slot) self.icons[title]=ButtonIcon diff --git a/openlp/core/pluginmanager.py b/openlp/core/pluginmanager.py index c6d7c1542..fa3c33442 100644 --- a/openlp/core/pluginmanager.py +++ b/openlp/core/pluginmanager.py @@ -45,7 +45,7 @@ class PluginManager(object): self.basepath = os.path.abspath(dir) log.debug("Base path %s ", self.basepath) self.plugins = [] - # this has to happen after the UI is sroted self.find_plugins(dir) + # this has to happen after the UI is sorted self.find_plugins(dir) log.info("Plugin manager done init") def find_plugins(self, dir, preview_controller, live_controller): # xxx shouldn't dir come from self.basepath diff --git a/openlp/core/render.py b/openlp/core/render.py index 22b944752..41a1643ab 100644 --- a/openlp/core/render.py +++ b/openlp/core/render.py @@ -375,14 +375,19 @@ class Renderer: print "_get_extent_and_render", [line], tlcorner, dodraw p=QtGui.QPainter() p.begin(self._paint) - # use this to scale for rendering in "operators view" xxx -# p.SetUserScale(0.5,0.5) # 'twould be more efficient to set this once when theme changes # or p changes font=QtGui.QFont(self._theme.FontName, self._theme.FontProportion, # size QtGui.QFont.Normal, # weight 0)# italic + # to make the unit tests monitor independent, we have to be able to + # specify whether a font proportion is in pixels or points + if self._theme.FontUnits.lower() == "pixels": + print "pixels" + font.setPixelSize(self._theme.FontProportion) + print self._theme.FontName, self._theme.FontProportion + print font.family(), font.pointSize() p.setFont(font) if color == None: p.setPen(self._theme.FontColor) diff --git a/openlp/core/test/data_for_tests/render_theme.xml b/openlp/core/test/data_for_tests/render_theme.xml index 445a7eba1..0958cc5c6 100644 --- a/openlp/core/test/data_for_tests/render_theme.xml +++ b/openlp/core/test/data_for_tests/render_theme.xml @@ -7,7 +7,8 @@ Tahoma $00007F - 32 + 53 + pixels 0 $000000 0 diff --git a/openlp/core/test/test_mediamanageritem.py b/openlp/core/test/test_mediamanageritem.py index 22113707b..96d0fd460 100644 --- a/openlp/core/test/test_mediamanageritem.py +++ b/openlp/core/test/test_mediamanageritem.py @@ -19,16 +19,13 @@ import os, sys mypath=os.path.split(os.path.abspath(__file__))[0] sys.path.insert(0,(os.path.join(mypath, '..' ,'..', '..'))) -from openlp.plugins import MediaManagerItem +from openlp.core.lib import MediaManagerItem class TestMediaManager: def setup_class(self): self.app = QtGui.QApplication([]) logging.info ("App is " + str(self.app)) self.main_window = QtGui.QMainWindow() self.main_window.resize(200, 600) -# self.StatusBar = QtGui.QStatusBar(self.main_window) -# self.StatusBar.setObjectName("StatusBar") -# self.main_window.setStatusBar(self.StatusBar) self.MediaManagerDock = QtGui.QDockWidget(self.main_window) sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) @@ -39,34 +36,29 @@ class TestMediaManager: icon.addPixmap(QtGui.QPixmap(":/system/system_mediamanager.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off) self.MediaManagerDock.setWindowIcon(icon) self.MediaManagerDock.setFloating(False) -# self.MediaManagerDock.setObjectName("MediaManagerDock") self.MediaManagerContents = QtGui.QWidget() sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) sizePolicy.setHorizontalStretch(0) sizePolicy.setVerticalStretch(0) sizePolicy.setHeightForWidth(self.MediaManagerContents.sizePolicy().hasHeightForWidth()) self.MediaManagerContents.setSizePolicy(sizePolicy) -# self.MediaManagerContents.setObjectName("MediaManagerContents") self.MediaManagerLayout = QtGui.QHBoxLayout(self.MediaManagerContents) self.MediaManagerLayout.setContentsMargins(0, 2, 0, 0) -# self.MediaManagerLayout.setObjectName("MediaManagerLayout") self.MediaToolBox = QtGui.QToolBox(self.MediaManagerContents) -# self.MediaToolBox.setObjectName("MediaToolBox") self.MediaManagerDock.setWidget(self.MediaManagerContents) self.main_window.addDockWidget(QtCore.Qt.DockWidgetArea(1), self.MediaManagerDock) self.MediaManagerLayout.addWidget(self.MediaToolBox) def test1(self): log=logging.getLogger("test1") log.info("Start") -# i1=MediaManagerItem() -# i2=MediaManagerItem() i1=MediaManagerItem(self.MediaToolBox) i2=MediaManagerItem(self.MediaToolBox) - i2.choose_area.text="More Stuff" log.info("i1"+str(i1)) log.info("i2"+str(i2)) - self.MediaToolBox.addItem(i1, i1.icon, "Test1") - self.MediaToolBox.addItem(i2, i2.icon, "Test2") + i1.addToolbar() + i1.addToolbarButton("Test1", "Test1", None) + i2.addToolbar() + i2.addToolbarButton("Test2", "Test2", None) self.MediaToolBox.setItemText(self.MediaToolBox.indexOf(i1), QtGui.QApplication.translate("main_window", "Item1", None, QtGui.QApplication.UnicodeUTF8)) self.MediaToolBox.setItemText(self.MediaToolBox.indexOf(i2), QtGui.QApplication.translate("main_window", "Item2", None, QtGui.QApplication.UnicodeUTF8)) log.info("Show window") diff --git a/openlp/core/test/test_plugin_manager.py b/openlp/core/test/test_plugin_manager.py index bf04ec269..1736d67fb 100644 --- a/openlp/core/test/test_plugin_manager.py +++ b/openlp/core/test/test_plugin_manager.py @@ -23,7 +23,9 @@ from openlp.core.pluginmanager import PluginManager # test the plugin manager with some plugins in the test_plugins directory class TestPluginManager: def test_init(self): - p=PluginManager("./testplugins") + self.p=PluginManager("./testplugins") + p=self.p + p.find_plugins('./testplugins', None, None) assert (len(p.plugins)==2); # get list of the names of the plugins names=[plugin.name for plugin in p.plugins] @@ -37,6 +39,8 @@ class TestPluginManager: assert p.plugins[1].name=="testplugin1" if __name__=="__main__": log.debug("Starting") - p=PluginManager("./testplugins") - for plugin in p.plugins: + t=TestPluginManager() + t.test_init() + log.debug("List of plugins found:") + for plugin in t.p.plugins: log.debug("Plugin %s, name=%s (version=%d)"%(str(plugin), plugin.name, plugin.version)) diff --git a/openlp/core/test/test_render.py b/openlp/core/test/test_render.py index f843b113b..18a480de0 100644 --- a/openlp/core/test/test_render.py +++ b/openlp/core/test/test_render.py @@ -213,6 +213,7 @@ if __name__=="__main__": t=TestRender() t.setup_class() t.setup_method(None) + t.test_easy() t.test_splits() t.teardown_method(None) diff --git a/openlp/core/test/test_render_theme.py b/openlp/core/test/test_render_theme.py index c4828ab68..9bd66a1ae 100644 --- a/openlp/core/test/test_render_theme.py +++ b/openlp/core/test/test_render_theme.py @@ -21,10 +21,33 @@ import sys import os mypath=os.path.split(os.path.abspath(__file__))[0] sys.path.insert(0,(os.path.join(mypath, '..', '..','..'))) -from openlp.theme import Theme +from openlp.core.theme import Theme from openlp.core import Renderer from PyQt4 import QtGui, QtCore + +def compare_images(goldenim, testim, threshold=0.01): + # easy test first + if goldenim == testim: + return 1 + # how close are they? Calculated the sum of absolute differences in + # each channel of each pixel and divide by the number of pixels in the image + # if this sum is < threshold, the images are deemed to be "close enough" + sad=0; + for x in range(goldenim.width()): + for y in range(goldenim.height()): + p1=goldenim.pixel(x,y) + p2=testim.pixel(x,y) + sad += abs((p1&0xFF)-(p2&0xFF)) + sad += abs((p1>>8&0xFF)-(p2>>8&0xFF)) + sad += abs((p1>>16&0xFF)-(p2>>16&0xFF)) + sad /= float(goldenim.width()*goldenim.height()) + if (sad < threshold): + return 1 + + return 0 + + class TestRenderTheme(TestRender_base): # {{{ Basics @@ -34,7 +57,7 @@ class TestRenderTheme(TestRender_base): TestRender_base.setup_method(self, method) print "Theme setup", method # print "setup theme" - self.r.set_theme(Theme()) # set "blank" theme + self.r.set_theme(Theme('blank_theme.xml')) # set "blank" theme self.r.set_text_rectangle(QtCore.QRect(0,0, self.size.width(), self.size.height())) words="""How sweet the name of Jesus sounds In a believer's ear! @@ -70,7 +93,7 @@ And drives away his fear. else: print "File", goldenfilename, "not found" return False - if (goldenim == im): + if (compare_images(goldenim, im)): print name, "Images match" return True else: @@ -80,6 +103,7 @@ And drives away his fear. def test_theme_basic(self): self.answer=self.r.render_screen(0) self.bmpname=whoami() + print self.r._theme.FontProportion print self.answer, self.expected_answer, self.answer==self.expected_answer # self.msg=self.bmpname @@ -105,14 +129,12 @@ And drives away his fear. # }}} # {{{ backgrounds def test_bg_stretch_y(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 2 t.BackgroundParameter1 = os.path.join('data_for_tests', "snowsmall.jpg"); t.BackgroundParameter2 = QtGui.QColor(0,0,64); t.BackgroundParameter3 = 0 t.Name="stretch y" - print t - print "set theme" self.r.set_theme(t) print "render" self.answer=self.r.render_screen(0) @@ -120,7 +142,7 @@ And drives away his fear. self.bmpname=whoami() print "fone" def test_bg_shrink_y(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 2 t.BackgroundParameter1 = os.path.join('data_for_tests', "snowbig.jpg"); t.BackgroundParameter2 = QtGui.QColor(0,0,64); @@ -131,7 +153,7 @@ And drives away his fear. self.bmpname=whoami() def test_bg_stretch_x(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 2 t.BackgroundParameter1 = os.path.join('data_for_tests', "treessmall.jpg"); t.BackgroundParameter2 = QtGui.QColor(0,0,64); @@ -144,7 +166,7 @@ And drives away his fear. self.bmpname=whoami() def test_bg_shrink_x(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 2 t.BackgroundParameter1 = os.path.join('data_for_tests', "treesbig.jpg"); t.BackgroundParameter2 = QtGui.QColor(0,0,64); @@ -158,7 +180,7 @@ And drives away his fear. # }}} # {{{ Vertical alignment def test_theme_vertical_align_top(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 0 @@ -168,7 +190,7 @@ And drives away his fear. self.bmpname=whoami() def test_theme_vertical_align_bot(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 1 @@ -179,7 +201,7 @@ And drives away his fear. self.bmpname=whoami() def test_theme_vertical_align_cen(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 2 @@ -191,7 +213,7 @@ And drives away his fear. # }}} # {{{ Horzontal alignment def test_theme_horizontal_align_left(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 0 @@ -202,7 +224,7 @@ And drives away his fear. self.bmpname=whoami() def test_theme_horizontal_align_right(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 0 @@ -214,7 +236,7 @@ And drives away his fear. self.bmpname=whoami() def test_theme_horizontal_align_centre(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 0 @@ -226,7 +248,7 @@ And drives away his fear. self.bmpname=whoami() def test_theme_horizontal_align_left_lyric(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.VerticalAlign = 0 @@ -241,7 +263,8 @@ And drives away his fear. # }}} # {{{ Shadows and outlines def test_theme_shadow_outline(self): - t=Theme() + t=Theme('blank_theme.xml') + t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,0); t.Name="shadow/outline" @@ -259,7 +282,7 @@ And drives away his fear. self.bmpname=whoami() # }}} def test_theme_font(self): - t=Theme() + t=Theme('blank_theme.xml') t.BackgroundType = 0 t.BackgroundParameter1 = QtGui.QColor(0,0,64); t.Name="font" @@ -274,5 +297,5 @@ if __name__=="__main__": t=TestRenderTheme() t.setup_class() t.setup_method(None) - t.test_theme_font() + t.test_bg_stretch_y() t.teardown_method(None) diff --git a/openlp/core/theme/test/test_theme.py b/openlp/core/theme/test/test_theme.py index 5ab7791ec..f4b760d4e 100644 --- a/openlp/core/theme/test/test_theme.py +++ b/openlp/core/theme/test/test_theme.py @@ -1,26 +1,8 @@ -""" -OpenLP - Open Source Lyrics Projection -Copyright (c) 2008 Raoul Snyman -Portions copyright (c) 2008 Martin Thompson, Tim Bentley - -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 os import sys mypath=os.path.split(os.path.abspath(__file__))[0] -sys.path.insert(0,(os.path.join(mypath, '..' ,'..', '..','..'))) +sys.path.insert(0,(os.path.join(mypath, '..' ,'..', '..', '..'))) print sys.path from openlp.core.theme import Theme @@ -38,6 +20,7 @@ def test_read_theme(): assert(t.FontColor == QtGui.QColor(255,255,255)) assert(t.FontName == "Tahoma") assert(t.FontProportion == 16) + assert(t.FontUnits == "pixels") assert(t.HorizontalAlign == 2) assert(t.Name == "openlp.org Packaged Theme") assert(t.Outline == -1) @@ -45,6 +28,7 @@ def test_read_theme(): assert(t.Shadow == -1) assert(t.ShadowColor == QtGui.QColor(0,0,1)) assert(t.VerticalAlign == 0) +def test_theme(): # test we create a "blank" theme correctly t=Theme() print t @@ -56,16 +40,16 @@ def test_read_theme(): assert(t.FontName == "Arial") assert(t.FontProportion == 30) assert(t.HorizontalAlign == 0) + assert(t.FontUnits == "pixels") assert(t.Name == "BlankStyle") assert(t.Outline == 0) assert(t.Shadow == 0) assert(t.VerticalAlign == 0) - + print "Tests passed" -def test_theme(): - test_read_theme() - + if __name__=="__main__": test_read_theme() + test_theme() diff --git a/openlp/core/theme/test/test_theme.xml b/openlp/core/theme/test/test_theme.xml index 63dfc1519..0e2e98284 100644 --- a/openlp/core/theme/test/test_theme.xml +++ b/openlp/core/theme/test/test_theme.xml @@ -8,6 +8,7 @@ Tahoma clWhite 16 + pixels -1 $00000001 -1 diff --git a/openlp/core/theme/theme.py b/openlp/core/theme/theme.py index a56e67e3b..81f94a9ce 100644 --- a/openlp/core/theme/theme.py +++ b/openlp/core/theme/theme.py @@ -1,30 +1,15 @@ -# -*- coding: utf-8 -*- -# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4 -""" -OpenLP - Open Source Lyrics Projection -Copyright (c) 2008 Raoul Snyman -Portions copyright (c) 2008 Martin Thompson, Tim Bentley +import platform +ver = platform.python_version() +if ver >= '2.5': + from xml.etree.ElementTree import ElementTree, XML +else: + from elementtree import ElementTree, XML -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 -import os from PyQt4 import QtGui -sys.path.append(os.path.abspath("./../..")) -from openlp.core.lib import XmlRootClass +DelphiColors={"clRed":0xFF0000, + "clBlack":0x000000, + "clWhite":0xFFFFFF} blankstylexml=\ ''' @@ -37,6 +22,7 @@ blankstylexml=\ Arial clWhite 30 + pixels 0 0 0 @@ -45,16 +31,12 @@ blankstylexml=\ ''' -DelphiColors={"clRed":0xFF0000, - "clBlack":0x000000, - "clWhite":0xFFFFFF} - -class Theme(XmlRootClass): +class Theme: def __init__(self, xmlfile=None): """ stores the info about a theme attributes: name : theme name - + BackgroundType : 0 - solid color 1 - gradient color 2 - image @@ -67,10 +49,11 @@ class Theme(XmlRootClass): for solid - N/A BackgroundParameter3 : for image - N/A for gradient - 0 -> vertical, 1 -> horizontal - + FontName : name of font to use FontColor : color for main font - FontProportion : point size of font + FontProportion : size of font + FontUnits : whether size of font is in or Shadow : 0 - no shadow, non-zero use shadow ShadowColor : color for drop shadow @@ -87,19 +70,59 @@ class Theme(XmlRootClass): 1 - lyrics """ # init to defaults - self._setFromXml(blankstylexml, 'Theme') + self._set_from_XML(blankstylexml) if xmlfile != None: # init from xmlfile file=open(xmlfile) t=''.join(file.readlines()) # read the file and change list to a string - self._setFromXml(t, 'Theme') + self._set_from_XML(t) - def post_tag_hook(self, tag, val): - if DelphiColors.has_key(val): - val=DelphiColors[val] - if (tag.find("Color") > 0 or - (tag.find("BackgroundParameter") == 0 and type(val) == type(0))): - # convert to a QtGui.Color - val= QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF) + def _get_as_string(self): + s="" + keys=dir(self) + keys.sort() + for k in keys: + if k[0:1] != "_": + s+= "_%s_" %(getattr(self,k)) + return s + def _set_from_XML(self, xml): + root=ElementTree(element=XML(xml)) + iter=root.getiterator() + for element in iter: + if element.tag != "Theme": + t=element.text +# print element.tag, t, type(t) + if type(t) == type(None): # easy! + val=t + if type(t) == type(" "): # strings need special handling to sort the colours out +# print "str", + if t[0] == "$": # might be a hex number +# print "hex", + try: + val=int(t[1:], 16) + except ValueError: # nope +# print "nope", + pass + elif DelphiColors.has_key(t): +# print "colour", + val=DelphiColors[t] + else: + try: + val=int(t) + except ValueError: + val=t + if (element.tag.find("Color") > 0 or + (element.tag.find("BackgroundParameter") == 0 and type(val) == type(0))): + # convert to a wx.Colour + val= QtGui.QColor((val>>16) & 0xFF, (val>>8)&0xFF, val&0xFF) + # print [val] + setattr(self,element.tag, val) + + + def __str__(self): + s="" + for k in dir(self): + if k[0:1] != "_": + s+= "%30s : %s\n" %(k,getattr(self,k)) + return s - return (tag, val)