mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-15 00:04:00 -04:00
268 lines
9.4 KiB
Python
268 lines
9.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""QGIS Unit tests for QgsProject.
|
|
|
|
.. note:: 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; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
"""
|
|
from builtins import chr
|
|
from builtins import range
|
|
__author__ = 'Sebastian Dietrich'
|
|
__date__ = '19/11/2015'
|
|
__copyright__ = 'Copyright 2015, The QGIS Project'
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
__revision__ = '$Format:%H$'
|
|
|
|
import os
|
|
|
|
import qgis # NOQA
|
|
|
|
from qgis.core import (QgsProject,
|
|
QgsApplication,
|
|
QgsUnitTypes,
|
|
QgsCoordinateReferenceSystem,
|
|
QgsVectorLayer)
|
|
from qgis.gui import (QgsLayerTreeMapCanvasBridge,
|
|
QgsMapCanvas)
|
|
from qgis.testing import start_app, unittest
|
|
from utilities import (unitTestDataPath)
|
|
from qgis.PyQt.QtCore import QDir
|
|
from qgis.PyQt.QtTest import QSignalSpy
|
|
|
|
app = start_app()
|
|
TEST_DATA_DIR = unitTestDataPath()
|
|
|
|
|
|
class TestQgsProject(unittest.TestCase):
|
|
|
|
def __init__(self, methodName):
|
|
"""Run once on class initialization."""
|
|
unittest.TestCase.__init__(self, methodName)
|
|
self.messageCaught = False
|
|
|
|
def test_makeKeyTokens_(self):
|
|
# see http://www.w3.org/TR/REC-xml/#d0e804 for a list of valid characters
|
|
|
|
invalidTokens = []
|
|
validTokens = []
|
|
|
|
# all test tokens will be generated by prepending or inserting characters to this token
|
|
validBase = "valid"
|
|
|
|
# some invalid characters, not allowed anywhere in a token
|
|
# note that '/' must not be added here because it is taken as a separator by makeKeyTokens_()
|
|
invalidChars = "+*,;<>|!$%()=?#\x01"
|
|
|
|
# generate the characters that are allowed at the start of a token (and at every other position)
|
|
validStartChars = ":_"
|
|
charRanges = [
|
|
(ord('a'), ord('z')),
|
|
(ord('A'), ord('Z')),
|
|
(0x00F8, 0x02FF),
|
|
(0x0370, 0x037D),
|
|
(0x037F, 0x1FFF),
|
|
(0x200C, 0x200D),
|
|
(0x2070, 0x218F),
|
|
(0x2C00, 0x2FEF),
|
|
(0x3001, 0xD7FF),
|
|
(0xF900, 0xFDCF),
|
|
(0xFDF0, 0xFFFD),
|
|
# (0x10000, 0xEFFFF), while actually valid, these are not yet accepted by makeKeyTokens_()
|
|
]
|
|
for r in charRanges:
|
|
for c in range(r[0], r[1]):
|
|
validStartChars += chr(c)
|
|
|
|
# generate the characters that are only allowed inside a token, not at the start
|
|
validInlineChars = "-.\xB7"
|
|
charRanges = [
|
|
(ord('0'), ord('9')),
|
|
(0x0300, 0x036F),
|
|
(0x203F, 0x2040),
|
|
]
|
|
for r in charRanges:
|
|
for c in range(r[0], r[1]):
|
|
validInlineChars += chr(c)
|
|
|
|
# test forbidden start characters
|
|
for c in invalidChars + validInlineChars:
|
|
invalidTokens.append(c + validBase)
|
|
|
|
# test forbidden inline characters
|
|
for c in invalidChars:
|
|
invalidTokens.append(validBase[:4] + c + validBase[4:])
|
|
|
|
# test each allowed start character
|
|
for c in validStartChars:
|
|
validTokens.append(c + validBase)
|
|
|
|
# test each allowed inline character
|
|
for c in validInlineChars:
|
|
validTokens.append(validBase[:4] + c + validBase[4:])
|
|
|
|
logger = QgsApplication.messageLog()
|
|
logger.messageReceived.connect(self.catchMessage)
|
|
prj = QgsProject.instance()
|
|
|
|
for token in validTokens:
|
|
self.messageCaught = False
|
|
prj.readEntry("test", token)
|
|
myMessage = "valid token '%s' not accepted" % (token)
|
|
assert not self.messageCaught, myMessage
|
|
|
|
for token in invalidTokens:
|
|
self.messageCaught = False
|
|
prj.readEntry("test", token)
|
|
myMessage = "invalid token '%s' accepted" % (token)
|
|
assert self.messageCaught, myMessage
|
|
|
|
logger.messageReceived.disconnect(self.catchMessage)
|
|
|
|
def catchMessage(self):
|
|
self.messageCaught = True
|
|
|
|
def testCrs(self):
|
|
prj = QgsProject.instance()
|
|
prj.clear()
|
|
|
|
self.assertFalse(prj.crs().isValid())
|
|
prj.setCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3111'))
|
|
self.assertEqual(prj.crs().authid(), 'EPSG:3111')
|
|
|
|
def testEllipsoid(self):
|
|
prj = QgsProject.instance()
|
|
prj.clear()
|
|
|
|
prj.setCrs(QgsCoordinateReferenceSystem.fromOgcWmsCrs('EPSG:3111'))
|
|
prj.setEllipsoid('WGS84')
|
|
self.assertEqual(prj.ellipsoid(), 'WGS84')
|
|
|
|
# if project has NO crs, then ellipsoid should always be none
|
|
prj.setCrs(QgsCoordinateReferenceSystem())
|
|
self.assertEqual(prj.ellipsoid(), 'NONE')
|
|
|
|
def testDistanceUnits(self):
|
|
prj = QgsProject.instance()
|
|
prj.clear()
|
|
|
|
prj.setDistanceUnits(QgsUnitTypes.DistanceFeet)
|
|
self.assertEqual(prj.distanceUnits(), QgsUnitTypes.DistanceFeet)
|
|
|
|
def testAreaUnits(self):
|
|
prj = QgsProject.instance()
|
|
prj.clear()
|
|
|
|
prj.setAreaUnits(QgsUnitTypes.AreaSquareFeet)
|
|
self.assertEqual(prj.areaUnits(), QgsUnitTypes.AreaSquareFeet)
|
|
|
|
def testReadEntry(self):
|
|
prj = QgsProject.instance()
|
|
prj.read(os.path.join(TEST_DATA_DIR, 'labeling/test-labeling.qgs'))
|
|
|
|
# valid key, valid int value
|
|
self.assertEqual(prj.readNumEntry("SpatialRefSys", "/ProjectionsEnabled", -1)[0], 0)
|
|
# invalid key
|
|
self.assertEqual(prj.readNumEntry("SpatialRefSys", "/InvalidKey", -1)[0], -1)
|
|
|
|
def testEmbeddedGroup(self):
|
|
testdata_path = unitTestDataPath('embedded_groups') + '/'
|
|
|
|
prj_path = os.path.join(testdata_path, "project2.qgs")
|
|
prj = QgsProject()
|
|
prj.read(prj_path)
|
|
|
|
layer_tree_group = prj.layerTreeRoot()
|
|
layers_ids = layer_tree_group.findLayerIds()
|
|
|
|
layers_names = []
|
|
for layer_id in layers_ids:
|
|
name = prj.mapLayer(layer_id).name()
|
|
layers_names.append(name)
|
|
|
|
expected = ['polys', 'lines']
|
|
self.assertEqual(sorted(layers_names), sorted(expected))
|
|
|
|
def testLayerOrder(self):
|
|
""" test project layer order"""
|
|
prj = QgsProject()
|
|
layer = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer1", "memory")
|
|
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer2", "memory")
|
|
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer3", "memory")
|
|
prj.addMapLayers([layer, layer2, layer3])
|
|
|
|
layer_order_changed_spy = QSignalSpy(prj.layerOrderChanged)
|
|
prj.setLayerOrder([layer2, layer])
|
|
self.assertEqual(len(layer_order_changed_spy), 1)
|
|
prj.setLayerOrder([layer2, layer])
|
|
self.assertEqual(len(layer_order_changed_spy), 1) # no signal, order not changed
|
|
|
|
self.assertEqual(prj.layerOrder(), [layer2, layer])
|
|
prj.setLayerOrder([layer])
|
|
self.assertEqual(prj.layerOrder(), [layer])
|
|
self.assertEqual(len(layer_order_changed_spy), 2)
|
|
|
|
# remove a layer
|
|
prj.setLayerOrder([layer2, layer, layer3])
|
|
self.assertEqual(len(layer_order_changed_spy), 3)
|
|
prj.removeMapLayer(layer)
|
|
self.assertEqual(prj.layerOrder(), [layer2, layer3])
|
|
self.assertEqual(len(layer_order_changed_spy), 4)
|
|
|
|
# save and restore
|
|
file_name = os.path.join(str(QDir.tempPath()), 'proj.qgs')
|
|
prj.setFileName(file_name)
|
|
prj.write()
|
|
prj2 = QgsProject()
|
|
prj2.setFileName(file_name)
|
|
prj2.read()
|
|
self.assertEqual([l.id() for l in prj2.layerOrder()], [layer2.id(), layer3.id()])
|
|
|
|
# clear project
|
|
prj.clear()
|
|
self.assertEqual(prj.layerOrder(), [])
|
|
|
|
def testLayerOrderUpdatedThroughBridge(self):
|
|
""" test that project layer order is updated when layer tree changes """
|
|
|
|
prj = QgsProject.instance()
|
|
layer = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer1", "memory")
|
|
layer2 = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer2", "memory")
|
|
layer3 = QgsVectorLayer("Point?field=fldtxt:string",
|
|
"layer3", "memory")
|
|
prj.addMapLayers([layer, layer2, layer3])
|
|
|
|
canvas = QgsMapCanvas()
|
|
bridge = QgsLayerTreeMapCanvasBridge(prj.layerTreeRoot(), canvas)
|
|
|
|
#custom layer order
|
|
bridge.setHasCustomLayerOrder(True)
|
|
bridge.setCustomLayerOrder([layer3.id(), layer.id(), layer2.id()])
|
|
app.processEvents()
|
|
self.assertEqual([l.id() for l in prj.layerOrder()], [layer3.id(), layer.id(), layer2.id()])
|
|
|
|
# no custom layer order
|
|
bridge.setHasCustomLayerOrder(False)
|
|
app.processEvents()
|
|
self.assertEqual([l.id() for l in prj.layerOrder()], [layer.id(), layer2.id(), layer3.id()])
|
|
|
|
# mess around with the layer tree order
|
|
root = prj.layerTreeRoot()
|
|
layer_node = root.findLayer(layer2.id())
|
|
cloned_node = layer_node.clone()
|
|
parent = layer_node.parent()
|
|
parent.insertChildNode(0, cloned_node)
|
|
parent.removeChildNode(layer_node)
|
|
app.processEvents()
|
|
# make sure project respects this
|
|
self.assertEqual([l.id() for l in prj.layerOrder()], [layer2.id(), layer.id(), layer3.id()])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|