Skip to content

Commit 287e0a0

Browse files
committed
[labeling] Add local preconfigured test server unit tests class
- Add control images for server output - Add class for comparison against canvas output
1 parent dd26e61 commit 287e0a0

File tree

7 files changed

+176
-14
lines changed

7 files changed

+176
-14
lines changed

tests/src/python/CMakeLists.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ ADD_PYTHON_TEST(PyQgsExpression test_qgsexpression.py)
2525
ADD_PYTHON_TEST(PyQgsPalLabelingBase test_qgspallabeling_base.py)
2626
ADD_PYTHON_TEST(PyQgsPalLabelingCanvas test_qgspallabeling_canvas.py)
2727
#ADD_PYTHON_TEST(PyQgsPalLabelingComposer test_qgspallabeling_composer.py)
28-
#ADD_PYTHON_TEST(PyQgsPalLabelingServer test_qgspallabeling_server.py)
28+
ADD_PYTHON_TEST(PyQgsPalLabelingServer test_qgspallabeling_server.py)
2929
ADD_PYTHON_TEST(PyQgsVectorFileWriter test_qgsvectorfilewriter.py)
3030
ADD_PYTHON_TEST(PyQgsSpatialiteProvider test_qgsspatialiteprovider.py)
3131
ADD_PYTHON_TEST(PyQgsZonalStatistics test_qgszonalstatistics.py)

tests/src/python/test_qgspallabeling_base.py

+63-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import sys
2424
import datetime
2525
import glob
26+
import shutil
2627
import StringIO
2728
import tempfile
2829
from PyQt4.QtCore import *
@@ -36,6 +37,7 @@
3637
QgsMapRenderer,
3738
QgsPalLabeling,
3839
QgsPalLayerSettings,
40+
QgsProject,
3941
QgsProviderRegistry,
4042
QgsVectorLayer,
4143
QgsRenderChecker
@@ -64,6 +66,7 @@ class TestQgsPalLabeling(TestCase):
6466
_PalDataDir = os.path.join(_TestDataDir, 'labeling')
6567
_PalFeaturesDb = os.path.join(_PalDataDir, 'pal_features_v3.sqlite')
6668
_TestFont = TESTFONT
69+
_TestProj = None
6770
_MapRegistry = None
6871
_MapRenderer = None
6972
_Canvas = None
@@ -77,8 +80,7 @@ def setUpClass(cls):
7780
QGISAPP, CANVAS, IFACE, PARENT
7881

7982
# verify that spatialite provider is available
80-
msg = ('\nSpatialite provider not found, '
81-
'SKIPPING TEST SUITE')
83+
msg = '\nSpatialite provider not found, SKIPPING TEST SUITE'
8284
res = 'spatialite' in QgsProviderRegistry.instance().providerList()
8385
assert res, msg
8486

@@ -90,6 +92,7 @@ def setUpClass(cls):
9092
cls._TestGroup = ''
9193
cls._TestGroupPrefix = ''
9294
cls._TestGroupAbbr = ''
95+
cls._TestImage = ''
9396

9497
# initialize class MapRegistry, Canvas, MapRenderer, Map and PAL
9598
cls._MapRegistry = QgsMapLayerRegistry.instance()
@@ -98,10 +101,10 @@ def setUpClass(cls):
98101
cls._Map = cls._Canvas.map()
99102
cls._Map.resize(QSize(600, 400))
100103
cls._MapRenderer = cls._Canvas.mapRenderer()
101-
crs = QgsCoordinateReferenceSystem()
104+
cls._CRS = QgsCoordinateReferenceSystem()
102105
# default for labeling test data sources: WGS 84 / UTM zone 13N
103-
crs.createFromSrid(32613)
104-
cls._MapRenderer.setDestinationCrs(crs)
106+
cls._CRS.createFromSrid(32613)
107+
cls._MapRenderer.setDestinationCrs(cls._CRS)
105108
# use platform's native logical output dpi for QgsMapRenderer on launch
106109

107110
cls._Pal = QgsPalLabeling()
@@ -195,7 +198,7 @@ def settingsDict(lyr):
195198
res[attr] = value
196199
return res
197200

198-
def saveContolImage(self):
201+
def saveContolImage(self, tmpimg=''):
199202
if 'PAL_CONTROL_IMAGE' not in os.environ:
200203
return
201204
testgrpdir = 'expected_' + self._TestGroupPrefix
@@ -208,21 +211,69 @@ def saveContolImage(self):
208211
for f in glob.glob(imgbasepath + '.*'):
209212
if os.path.exists(f):
210213
os.remove(f)
211-
self._Map.render()
212-
self._Canvas.saveAsImage(imgpath)
213-
214-
def renderCheck(self, mismatch=0):
214+
if tmpimg:
215+
if os.path.exists(tmpimg):
216+
shutil.copyfile(tmpimg, imgpath)
217+
else:
218+
self._Map.render()
219+
self._Canvas.saveAsImage(imgpath)
220+
221+
def renderCheck(self, mismatch=0, imgpath='', grpprefix=''):
222+
"""Check rendered map canvas or existing image against control image
223+
224+
mismatch: number of pixels different from control, and still valid check
225+
imgpath: existing image; if present, skips rendering canvas
226+
grpprefix: compare test image/rendering against different test group
227+
"""
228+
if not grpprefix:
229+
grpprefix = self._TestGroupPrefix
215230
chk = QgsRenderChecker()
216-
chk.setControlPathPrefix('expected_' + self._TestGroupPrefix)
231+
chk.setControlPathPrefix('expected_' + grpprefix)
217232
chk.setControlName(self._Test)
218233
chk.setMapRenderer(self._MapRenderer)
219-
res = chk.runTest(self._Test, mismatch)
234+
res = False
235+
if imgpath:
236+
res = chk.compareImages(self._Test, mismatch, str(imgpath))
237+
else:
238+
res = chk.runTest(self._Test, mismatch)
220239
if PALREPORT and not res: # don't report ok checks
221240
testname = self._TestGroup + ' . ' + self._Test
222241
PALREPORTS[testname] = str(chk.report().toLocal8Bit())
223242
msg = '\nRender check failed for "{0}"'.format(self._Test)
224243
return res, msg
225244

245+
def defaultWmsParams(self, projpath, layername):
246+
return {
247+
'SERVICE': 'WMS',
248+
'VERSION': '1.3.0',
249+
'REQUEST': 'GetMap',
250+
'MAP': str(projpath).strip(),
251+
# layer stacking order for rendering: bottom,to,top
252+
'LAYERS': ['background', str(layername).strip()], # or 'name,name'
253+
'STYLES': ',',
254+
'CRS': 'EPSG:32613', # self._CRS, # authid str or QgsCoordinateReferenceSystem obj
255+
'BBOX': '606510,4823130,612510,4827130', # self.aoiExtent(),
256+
'FORMAT': 'image/png', # or: 'image/png; mode=8bit'
257+
'WIDTH': '600',
258+
'HEIGHT': '400',
259+
'DPI': '72',
260+
'MAP_RESOLUTION': '72',
261+
'FORMAT_OPTIONS': 'dpi:72',
262+
'TRANSPARENT': 'FALSE',
263+
'IgnoreGetMapUrl': '1'
264+
}
265+
266+
@classmethod
267+
def setUpServerProjectAndDir(cls, testprojpath, testdir):
268+
cls._TestProj = QgsProject.instance()
269+
cls._TestProj.setFileName(testprojpath)
270+
try:
271+
shutil.copy(cls._PalFeaturesDb, testdir)
272+
for qml in glob.glob(cls._PalDataDir + os.sep + '*.qml'):
273+
shutil.copy(qml, testdir)
274+
except IOError, e:
275+
raise IOError(str(e) + '\nCould not set up test server directory')
276+
226277

227278
class TestPALConfig(TestQgsPalLabeling):
228279

tests/src/python/test_qgspallabeling_canvas.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def tearDown(self):
5252
"""Run after each test."""
5353
pass
5454

55-
def checkTest(self):
55+
def checkTest(self, **kwargs):
5656
self.lyr.writeToLayer(self.layer)
5757
self.saveContolImage()
5858
self.assertTrue(*self.renderCheck())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS unit tests for QgsPalLabeling: label rendering via QGIS Server
3+
4+
From build dir: ctest -R PyQgsPalLabelingServer -V
5+
Set the following env variables when manually running tests:
6+
PAL_SUITE to run specific tests (define in __main__)
7+
PAL_VERBOSE to output individual test summary
8+
PAL_CONTROL_IMAGE to trigger building of new control images
9+
PAL_REPORT to open any failed image check reports in web browser
10+
11+
.. note:: This program is free software; you can redistribute it and/or modify
12+
it under the terms of the GNU General Public License as published by
13+
the Free Software Foundation; either version 2 of the License, or
14+
(at your option) any later version.
15+
"""
16+
17+
__author__ = 'Larry Shaffer'
18+
__date__ = '07/12/2013'
19+
__copyright__ = 'Copyright 2013, The QGIS Project'
20+
# This will get replaced with a git SHA1 when you do a git archive
21+
__revision__ = '$Format:%H$'
22+
23+
import sys
24+
import os
25+
from PyQt4.QtCore import *
26+
from PyQt4.QtGui import *
27+
28+
from qgis.core import *
29+
30+
from utilities import (
31+
unittest,
32+
expectedFailure,
33+
)
34+
35+
from test_qgspallabeling_base import TestQgsPalLabeling, runSuite
36+
from test_qgspallabeling_tests import TestPointBase
37+
from qgis_local_server import (QgisLocalServerConfig,
38+
ServerConfigNotAccessibleError)
39+
40+
SERVER = None
41+
TESTPROJDIR = ''
42+
TESTPROJPATH = ''
43+
try:
44+
SERVER = \
45+
QgisLocalServerConfig(str(QgsApplication.qgisSettingsDirPath()), True)
46+
TESTPROJDIR = SERVER.projectDir()
47+
TESTPROJPATH = os.path.join(TESTPROJDIR, 'pal_test.qgs')
48+
except ServerConfigNotAccessibleError, e:
49+
print e
50+
51+
52+
def skipUnlessHasServer(): # skip test class decorator
53+
if SERVER is not None:
54+
return lambda func: func
55+
return unittest.skip('\nConfigured local QGIS Server is not accessible\n\n')
56+
57+
58+
@skipUnlessHasServer()
59+
class TestServerPoint(TestQgsPalLabeling, TestPointBase):
60+
61+
@classmethod
62+
def setUpClass(cls):
63+
TestQgsPalLabeling.setUpClass()
64+
cls.setUpServerProjectAndDir(TESTPROJPATH, TESTPROJDIR)
65+
cls.layer = TestQgsPalLabeling.loadFeatureLayer('background')
66+
cls.layer = TestQgsPalLabeling.loadFeatureLayer('point')
67+
cls.checkmismatch = 1000
68+
cls.checkgroup = ''
69+
70+
def setUp(self):
71+
"""Run before each test."""
72+
self.configTest('pal_server', 'sp')
73+
self.lyr = self.defaultSettings()
74+
self.params = self.defaultWmsParams(TESTPROJPATH, 'point')
75+
self._TestImage = ''
76+
77+
def tearDown(self):
78+
"""Run after each test."""
79+
pass
80+
81+
def checkTest(self, **kwargs):
82+
self.lyr.writeToLayer(self.layer)
83+
# save project file
84+
self._TestProj.write()
85+
# get server results
86+
res, self._TestImage = SERVER.getMap(self.params, False)
87+
self.saveContolImage(self._TestImage)
88+
self.assertTrue(res, 'Failed to retrieve/save image from test server')
89+
# gp = kwargs['grpprefix'] if 'grpprefix' in kwargs else ''
90+
self.assertTrue(*self.renderCheck(mismatch=self.checkmismatch,
91+
imgpath=self._TestImage,
92+
grpprefix=self.checkgroup))
93+
94+
95+
@skipUnlessHasServer()
96+
class TestServerVsCanvasPoint(TestServerPoint):
97+
98+
@classmethod
99+
def setUpClass(cls):
100+
TestServerPoint.setUpClass()
101+
cls.checkgroup = 'pal_canvas'
102+
103+
104+
if __name__ == '__main__':
105+
# NOTE: unless PAL_SUITE env var is set all test class methods will be run
106+
# ex: 'TestGroup(Point|Line|Curved|Polygon|Feature).test_method'
107+
suite = [
108+
'TestServerVsCanvasPoint.test_text_size_map_unit'
109+
]
110+
res = runSuite(sys.modules[__name__], suite)
111+
sys.exit(not res.wasSuccessful())
Loading
Loading

0 commit comments

Comments
 (0)