Merge pull request #8223 from signedav/json_tests

QgsServer Tests for JSON fields
This commit is contained in:
Matthias Kuhn 2018-10-25 16:41:40 +02:00 committed by GitHub
commit dc334ac028
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 395 additions and 13 deletions

View File

@ -48,15 +48,18 @@ class TestQgsServerWMSTestBase(QgsServerTestBase):
# Set to True to re-generate reference files for this class
regenerate_reference = False
def wms_request_compare(self, request, extra=None, reference_file=None, project='test_project.qgs', version='1.3.0'):
def wms_request(self, request, extra=None, project='test_project.qgs', version='1.3.0'):
project = self.testdata_path + project
assert os.path.exists(project), "Project file not found: " + project
query_string = 'https://www.qgis.org/?MAP=%s&SERVICE=WMS&VERSION=%s&REQUEST=%s' % (urllib.parse.quote(project), version, request)
if extra is not None:
query_string += extra
header, body = self._execute_request(query_string)
response = header + body
return (header, body, query_string)
def wms_request_compare(self, request, extra=None, reference_file=None, project='test_project.qgs', version='1.3.0'):
response_header, response_body, query_string = self.wms_request(request, extra, project, version)
response = response_header + response_body
reference_path = self.testdata_path + (request.lower() if not reference_file else reference_file) + '.txt'
self.store_reference(reference_path, response)
f = open(reference_path, 'rb')

View File

@ -18,7 +18,8 @@ __revision__ = '$Format:%H$'
import os
# Needed on Qt 5 so that the serialization of XML is consistent among all executions
# Needed on Qt 5 so that the serialization of XML is consistent among all
# executions
os.environ['QT_HASH_SEED'] = '1'
import re
@ -26,6 +27,9 @@ import urllib.request
import urllib.parse
import urllib.error
import xml.etree.ElementTree as ET
import json
from qgis.testing import unittest
from qgis.PyQt.QtCore import QSize
@ -194,7 +198,8 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'wms_getfeatureinfo_notvisible',
'test_project_scalevisibility.qgs')
# Test GetFeatureInfo resolves "value map" widget values but also Server usage of qgs and gpkg file
# Test GetFeatureInfo resolves "value map" widget values but also
# Server usage of qgs and gpkg file
mypath = self.testdata_path + "test_project_values.qgz"
self.wms_request_compare('GetFeatureInfo',
'&layers=layer0&styles=&' +
@ -240,7 +245,8 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'wms_getfeatureinfo-values3-text-xml',
'test_project_values.qgz')
# TODO make GetFeatureInfo show what's in the display expression and enable test
# TODO make GetFeatureInfo show what's in the display expression and
# enable test
@unittest.expectedFailure
def testGetFeatureInfoRelationReference(self):
"""Test GetFeatureInfo solves "relation reference" widget "display expression" values"""
@ -267,7 +273,8 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'INFO_FORMAT=text%2Fxml&' +
'width=600&height=400&srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(':"NAME" = \'two\''),
'wms_getfeatureinfo_filter_gpkg',
'test_project.qgz')
@ -281,7 +288,8 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'INFO_FORMAT=text%2Fxml&' +
'width=600&height=400&srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(':"NAME" = \'two\''),
'wms_getfeatureinfo_filter')
# Test a filter with NO condition results
@ -290,7 +298,9 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'INFO_FORMAT=text%2Fxml&' +
'width=600&height=400&srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\' AND "utf8nameè" = \'no-results\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(
':"NAME" = \'two\' AND "utf8nameè" = \'no-results\''),
'wms_getfeatureinfo_filter_no_results')
# Test a filter with OR condition results
@ -299,7 +309,9 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'INFO_FORMAT=text%2Fxml&' +
'width=600&height=400&srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\' OR "NAME" = \'three\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(
':"NAME" = \'two\' OR "NAME" = \'three\''),
'wms_getfeatureinfo_filter_or')
# Test a filter with OR condition and UTF results
@ -310,16 +322,20 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'INFO_FORMAT=text%2Fxml&' +
'width=600&height=400&srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\' OR "utf8nameè" = \'three èé↓\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(
':"NAME" = \'two\' OR "utf8nameè" = \'three èé↓\''),
'wms_getfeatureinfo_filter_or_utf8')
# Regression #18292 Server GetFeatureInfo FILTER search fails when WIDTH, HEIGHT are not specified
# Regression #18292 Server GetFeatureInfo FILTER search fails when
# WIDTH, HEIGHT are not specified
self.wms_request_compare('GetFeatureInfo',
'&layers=testlayer%20%C3%A8%C3%A9&' +
'INFO_FORMAT=text%2Fxml&' +
'srs=EPSG%3A3857&' +
'query_layers=testlayer%20%C3%A8%C3%A9&' +
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' + urllib.parse.quote(':"NAME" = \'two\''),
'FEATURE_COUNT=10&FILTER=testlayer%20%C3%A8%C3%A9' +
urllib.parse.quote(':"NAME" = \'two\''),
'wms_getfeatureinfo_filter_no_width')
def testGetFeatureInfoTolerance(self):
@ -407,6 +423,89 @@ class TestQgsServerWMSGetFeatureInfo(TestQgsServerWMSTestBase):
'wms_getfeatureinfo_polygon_tolerance_20_text_xml',
'test_project_values.qgz')
def testGetFeatureInfoPostgresTypes(self):
# compare json list output with file
self.wms_request_compare('GetFeatureInfo',
'&layers=json' +
'&info_format=text%2Fxml' +
'&srs=EPSG%3A3857' +
'&QUERY_LAYERS=json' +
'&FILTER=json' +
urllib.parse.quote(':"pk" = 1'),
'get_postgres_types_json_list',
'test_project_postgres_types.qgs')
# compare dict output with file
self.wms_request_compare('GetFeatureInfo',
'&layers=json' +
'&info_format=text%2Fxml' +
'&srs=EPSG%3A3857' +
'&QUERY_LAYERS=json' +
'&FILTER=json' +
urllib.parse.quote(':"pk" = 2'),
'get_postgres_types_json_dict',
'test_project_postgres_types.qgs')
# compare decoded json field list
response_header, response_body, query_string = self.wms_request('GetFeatureInfo',
'&layers=json' +
'&info_format=text%2Fxml' +
'&srs=EPSG%3A3857' +
'&QUERY_LAYERS=json' +
'&FILTER=json' +
urllib.parse.quote(
':"pk" = 1'),
'test_project_postgres_types.qgs')
root = ET.fromstring(response_body)
for attribute in root.iter('Attribute'):
if attribute.get('name') == 'jvalue':
self.assertIsInstance(json.loads(attribute.get('value')), list)
self.assertEqual(json.loads(attribute.get('value')), [1, 2, 3])
self.assertEqual(
json.loads(
attribute.get('value')), [
1.0, 2.0, 3.0])
if attribute.get('name') == 'jbvalue':
self.assertIsInstance(json.loads(attribute.get('value')), list)
self.assertEqual(json.loads(attribute.get('value')), [4, 5, 6])
self.assertEqual(
json.loads(
attribute.get('value')), [
4.0, 5.0, 6.0])
# compare decoded json field dict
response_header, response_body, query_string = self.wms_request('GetFeatureInfo',
'&layers=json' +
'&info_format=text%2Fxml' +
'&srs=EPSG%3A3857' +
'&QUERY_LAYERS=json' +
'&FILTER=json' +
urllib.parse.quote(
':"pk" = 2'),
'test_project_postgres_types.qgs')
root = ET.fromstring(response_body)
for attribute in root.iter('Attribute'):
if attribute.get('name') == 'jvalue':
self.assertIsInstance(json.loads(attribute.get('value')), dict)
self.assertEqual(
json.loads(
attribute.get('value')), {
'a': 1, 'b': 2})
self.assertEqual(
json.loads(
attribute.get('value')), {
'a': 1.0, 'b': 2.0})
if attribute.get('name') == 'jbvalue':
self.assertIsInstance(json.loads(attribute.get('value')), dict)
self.assertEqual(
json.loads(
attribute.get('value')), {
'c': 4, 'd': 5})
self.assertEqual(
json.loads(
attribute.get('value')), {
'c': 4.0, 'd': 5.0})
if __name__ == '__main__':
unittest.main()

Binary file not shown.

View File

@ -0,0 +1,13 @@
*****
Content-Type: text/xml; charset=utf-8
<GetFeatureInfoResponse>
<BoundingBox maxy="0" maxx="0" miny="0" CRS="EPSG:3857" minx="0"/>
<Layer name="json">
<Feature id="2">
<Attribute value="2" name="pk"/>
<Attribute value="{&#xa; &quot;a&quot;: 1,&#xa; &quot;b&quot;: 2&#xa;}&#xa;" name="jvalue"/>
<Attribute value="{&#xa; &quot;c&quot;: 4,&#xa; &quot;d&quot;: 5&#xa;}&#xa;" name="jbvalue"/>
</Feature>
</Layer>
</GetFeatureInfoResponse>

View File

@ -0,0 +1,13 @@
*****
Content-Type: text/xml; charset=utf-8
<GetFeatureInfoResponse>
<BoundingBox maxy="0" maxx="0" miny="0" CRS="EPSG:3857" minx="0"/>
<Layer name="json">
<Feature id="1">
<Attribute value="1" name="pk"/>
<Attribute value="[&#xa; 1,&#xa; 2,&#xa; 3&#xa;]&#xa;" name="jvalue"/>
<Attribute value="[&#xa; 4,&#xa; 5,&#xa; 6&#xa;]&#xa;" name="jbvalue"/>
</Feature>
</Layer>
</GetFeatureInfoResponse>

View File

@ -0,0 +1,254 @@
<!DOCTYPE qgis PUBLIC 'http://mrcc.com/qgis.dtd' 'SYSTEM'>
<qgis projectname="" version="3.3.0-Master">
<homePath path=""/>
<title></title>
<autotransaction active="0"/>
<evaluateDefaultValues active="0"/>
<trust active="0"/>
<projectCrs>
<spatialrefsys>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</projectCrs>
<layer-tree-group>
<customproperties/>
<layer-tree-layer expanded="1" checked="Qt::Checked" source="dbname='qgis_test' port=5432 user='postgres' sslmode=disable key='pk' table=&quot;qgis_test&quot;.&quot;json&quot; sql=" providerKey="postgres" id="json_b2af056d_ba24_4fb7_805b_5a7720e242f3" name="json">
<customproperties/>
</layer-tree-layer>
<custom-order enabled="0"/>
</layer-tree-group>
<snapping-settings enabled="0" type="1" intersection-snapping="0" unit="2" tolerance="0" mode="2">
<individual-layer-settings/>
</snapping-settings>
<relations/>
<mapcanvas annotationsVisible="1" name="theMapCanvas">
<units>degrees</units>
<extent>
<xmin>8.20313020806923632</xmin>
<ymin>44.90118727027074641</ymin>
<xmax>8.20717330488028196</xmax>
<ymax>44.90224039332306205</ymax>
</extent>
<rotation>0</rotation>
<destinationsrs>
<spatialrefsys>
<proj4>+proj=longlat +datum=WGS84 +no_defs</proj4>
<srsid>3452</srsid>
<srid>4326</srid>
<authid>EPSG:4326</authid>
<description>WGS 84</description>
<projectionacronym>longlat</projectionacronym>
<ellipsoidacronym>WGS84</ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</destinationsrs>
<rendermaptile>0</rendermaptile>
</mapcanvas>
<projectModels/>
<legend updateDrawingOrder="true">
<legendlayer showFeatureCount="0" drawingOrder="-1" open="true" checked="Qt::Checked" name="json">
<filegroup open="true" hidden="false">
<legendlayerfile visible="1" layerid="json_b2af056d_ba24_4fb7_805b_5a7720e242f3" isInOverview="0"/>
</filegroup>
</legendlayer>
</legend>
<mapViewDocks/>
<projectlayers>
<maplayer maxScale="0" autoRefreshEnabled="0" hasScaleBasedVisibilityFlag="0" type="vector" refreshOnNotifyMessage="" autoRefreshTime="0" geometry="No geometry" styleCategories="AllStyleCategories" refreshOnNotifyEnabled="0" minScale="1e+8" readOnly="0">
<id>json_b2af056d_ba24_4fb7_805b_5a7720e242f3</id>
<datasource>dbname='qgis_test' port=5432 sslmode=disable key='pk' table="qgis_test"."json" sql=</datasource>
<keywordList>
<value></value>
</keywordList>
<layername>json</layername>
<srs>
<spatialrefsys>
<proj4></proj4>
<srsid>0</srsid>
<srid>0</srid>
<authid></authid>
<description></description>
<projectionacronym></projectionacronym>
<ellipsoidacronym></ellipsoidacronym>
<geographicflag>true</geographicflag>
</spatialrefsys>
</srs>
<resourceMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type>dataset</type>
<title></title>
<abstract></abstract>
<links/>
<fees></fees>
<encoding></encoding>
<crs>
<spatialrefsys>
<proj4></proj4>
<srsid>0</srsid>
<srid>0</srid>
<authid></authid>
<description></description>
<projectionacronym></projectionacronym>
<ellipsoidacronym></ellipsoidacronym>
<geographicflag>false</geographicflag>
</spatialrefsys>
</crs>
<extent/>
</resourceMetadata>
<provider encoding="ISO-8859-5">postgres</provider>
<vectorjoins/>
<layerDependencies/>
<dataDependencies/>
<legend type="default-vector"/>
<expressionfields/>
<map-layer-style-manager current="default">
<map-layer-style name="default"/>
</map-layer-style-manager>
<auxiliaryLayer/>
<flags>
<Identifiable>1</Identifiable>
<Removable>1</Removable>
<Searchable>1</Searchable>
</flags>
<customproperties>
<property key="dualview/previewExpressions" value="&quot;pk&quot;"/>
</customproperties>
<geometryOptions removeDuplicateNodes="0" geometryPrecision="0"/>
<fieldConfiguration>
<field name="pk">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="jvalue">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
<field name="jbvalue">
<editWidget type="">
<config>
<Option/>
</config>
</editWidget>
</field>
</fieldConfiguration>
<aliases>
<alias field="pk" index="0" name=""/>
<alias field="jvalue" index="1" name=""/>
<alias field="jbvalue" index="2" name=""/>
</aliases>
<excludeAttributesWMS/>
<excludeAttributesWFS/>
<defaults>
<default field="pk" applyOnUpdate="0" expression=""/>
<default field="jvalue" applyOnUpdate="0" expression=""/>
<default field="jbvalue" applyOnUpdate="0" expression=""/>
</defaults>
<constraints>
<constraint constraints="3" field="pk" notnull_strength="1" exp_strength="0" unique_strength="1"/>
<constraint constraints="0" field="jvalue" notnull_strength="0" exp_strength="0" unique_strength="0"/>
<constraint constraints="0" field="jbvalue" notnull_strength="0" exp_strength="0" unique_strength="0"/>
</constraints>
<constraintExpressions>
<constraint field="pk" exp="" desc=""/>
<constraint field="jvalue" exp="" desc=""/>
<constraint field="jbvalue" exp="" desc=""/>
</constraintExpressions>
<expressionfields/>
<attributeactions>
<defaultAction key="Canvas" value="{00000000-0000-0000-0000-000000000000}"/>
</attributeactions>
<attributetableconfig actionWidgetStyle="dropDown" sortOrder="0" sortExpression="">
<columns/>
</attributetableconfig>
<conditionalstyles>
<rowstyles/>
<fieldstyles/>
</conditionalstyles>
<editform tolerant="1"></editform>
<editforminit/>
<editforminitcodesource>0</editforminitcodesource>
<editforminitfilepath></editforminitfilepath>
<editforminitcode><![CDATA[]]></editforminitcode>
<featformsuppress>0</featformsuppress>
<editorlayout>generatedlayout</editorlayout>
<editable/>
<labelOnTop/>
<widgets/>
<previewExpression>"pk"</previewExpression>
<mapTip></mapTip>
</maplayer>
</projectlayers>
<layerorder/>
<properties>
<Measure>
<Ellipsoid type="QString">WGS84</Ellipsoid>
</Measure>
<SpatialRefSys>
<ProjectionsEnabled type="int">1</ProjectionsEnabled>
</SpatialRefSys>
<Gui>
<CanvasColorBluePart type="int">255</CanvasColorBluePart>
<CanvasColorGreenPart type="int">255</CanvasColorGreenPart>
<SelectionColorGreenPart type="int">255</SelectionColorGreenPart>
<SelectionColorBluePart type="int">0</SelectionColorBluePart>
<CanvasColorRedPart type="int">255</CanvasColorRedPart>
<SelectionColorAlphaPart type="int">255</SelectionColorAlphaPart>
<SelectionColorRedPart type="int">255</SelectionColorRedPart>
</Gui>
<PositionPrecision>
<DecimalPlaces type="int">2</DecimalPlaces>
<Automatic type="bool">true</Automatic>
</PositionPrecision>
<Legend>
<filterByMap type="bool">false</filterByMap>
</Legend>
<PAL>
<CandidatesPoint type="int">16</CandidatesPoint>
<CandidatesLine type="int">50</CandidatesLine>
<ShowingAllLabels type="bool">false</ShowingAllLabels>
<SearchMethod type="int">0</SearchMethod>
<DrawRectOnly type="bool">false</DrawRectOnly>
<CandidatesPolygon type="int">30</CandidatesPolygon>
<ShowingCandidates type="bool">false</ShowingCandidates>
<ShowingPartialsLabels type="bool">true</ShowingPartialsLabels>
<DrawOutlineLabels type="bool">true</DrawOutlineLabels>
</PAL>
<Paths>
<Absolute type="bool">false</Absolute>
</Paths>
<Measurement>
<DistanceUnits type="QString">meters</DistanceUnits>
<AreaUnits type="QString">m2</AreaUnits>
</Measurement>
</properties>
<visibility-presets/>
<transformContext/>
<projectMetadata>
<identifier></identifier>
<parentidentifier></parentidentifier>
<language></language>
<type></type>
<title></title>
<abstract></abstract>
<links/>
<author>David</author>
<creation>2018-10-17T15:28:25</creation>
</projectMetadata>
<Annotations/>
<Layouts/>
</qgis>