27
27
QgsMapLayer ,
28
28
QgsRectangle ,
29
29
QgsDataProvider ,
30
+ QgsReadWriteContext ,
30
31
QgsCoordinateReferenceSystem ,
31
32
)
32
33
from qgis .gui import (QgsLayerTreeMapCanvasBridge ,
35
36
from qgis .PyQt .QtGui import QFont , QColor
36
37
from qgis .PyQt .QtTest import QSignalSpy
37
38
from qgis .PyQt .QtCore import QT_VERSION_STR , QTemporaryDir , QSize
39
+ from qgis .PyQt .QtXml import QDomDocument , QDomNode
38
40
39
41
from qgis .testing import start_app , unittest
40
42
from utilities import (unitTestDataPath , renderMapToImage )
@@ -68,6 +70,34 @@ def getBaseMapSettings(cls):
68
70
ms .setDestinationCrs (crs )
69
71
return ms
70
72
73
+ def _change_data_source (self , layer , datasource , provider_key ):
74
+ """Due to the fact that a project r/w context is not available inside
75
+ the map layers classes, the original style and subset string restore
76
+ happens in app, this function replicates app behavior"""
77
+
78
+ options = QgsDataProvider .ProviderOptions ()
79
+
80
+ subset_string = ''
81
+ if not layer .isValid ():
82
+ try :
83
+ subset_string = layer .dataProvider ().subsetString ()
84
+ except :
85
+ pass
86
+
87
+ layer .setDataSource (datasource , layer .name (), provider_key , options )
88
+
89
+ if subset_string :
90
+ layer .setSubsetString (subset_string )
91
+
92
+ self .assertTrue (layer .originalXmlProperties (), layer .name ())
93
+ context = QgsReadWriteContext ()
94
+ context .setPathResolver (QgsProject .instance ().pathResolver ())
95
+ errorMsg = ''
96
+ doc = QDomDocument ()
97
+ self .assertTrue (doc .setContent (layer .originalXmlProperties ()))
98
+ layer_node = QDomNode (doc .firstChild ())
99
+ self .assertTrue (layer .readSymbology (layer_node , errorMsg , context ))
100
+
71
101
def test_project_roundtrip (self ):
72
102
"""Tests that a project with bad layers can be saved and restored"""
73
103
@@ -91,6 +121,7 @@ def test_project_roundtrip(self):
91
121
self .assertTrue (p .write (project_path ))
92
122
93
123
# Re-load the project, checking for the XML properties
124
+ p .removeAllMapLayers ()
94
125
self .assertTrue (p .read (project_path ))
95
126
vector = list (p .mapLayersByName ('lines' ))[0 ]
96
127
raster = list (p .mapLayersByName ('raster' ))[0 ]
@@ -109,6 +140,7 @@ def test_project_roundtrip(self):
109
140
outfile .write (infile .read ().replace ('./lines.shp' , './lines-BAD_SOURCE.shp' ).replace ('band1_byte_ct_epsg4326_copy.tif' , 'band1_byte_ct_epsg4326_copy-BAD_SOURCE.tif' ))
110
141
111
142
# Load the bad project
143
+ p .removeAllMapLayers ()
112
144
self .assertTrue (p .read (bad_project_path ))
113
145
# Check layer is invalid
114
146
vector = list (p .mapLayersByName ('lines' ))[0 ]
@@ -134,6 +166,7 @@ def test_project_roundtrip(self):
134
166
outfile .write (infile .read ().replace ('./lines-BAD_SOURCE.shp' , './lines.shp' ).replace ('band1_byte_ct_epsg4326_copy-BAD_SOURCE.tif' , 'band1_byte_ct_epsg4326_copy.tif' ))
135
167
136
168
# Load the good project
169
+ p .removeAllMapLayers ()
137
170
self .assertTrue (p .read (good_project_path ))
138
171
# Check layer is valid
139
172
vector = list (p .mapLayersByName ('lines' ))[0 ]
@@ -153,6 +186,7 @@ def test_project_relations(self):
153
186
154
187
# Load the good project
155
188
project_path = os .path .join (temp_dir .path (), 'relation_reference_test.qgs' )
189
+ p .removeAllMapLayers ()
156
190
self .assertTrue (p .read (project_path ))
157
191
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
158
192
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
@@ -177,6 +211,7 @@ def _check_relations():
177
211
outfile .write (infile .read ().replace ('./relation_reference_test.gpkg' , './relation_reference_test-BAD_SOURCE.gpkg' ))
178
212
179
213
# Load the bad project
214
+ p .removeAllMapLayers ()
180
215
self .assertTrue (p .read (bad_project_path ))
181
216
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
182
217
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
@@ -197,6 +232,7 @@ def _check_relations():
197
232
_check_relations ()
198
233
199
234
# Reload the bad project
235
+ p .removeAllMapLayers ()
200
236
self .assertTrue (p .read (bad_project_path ))
201
237
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
202
238
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
@@ -218,6 +254,7 @@ def _check_relations():
218
254
outfile .write (infile .read ().replace ('./relation_reference_test-BAD_SOURCE.gpkg' , './relation_reference_test.gpkg' ))
219
255
220
256
# Load the fixed project
257
+ p .removeAllMapLayers ()
221
258
self .assertTrue (p .read (bad_project_path_fixed ))
222
259
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
223
260
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
@@ -230,7 +267,6 @@ def _check_relations():
230
267
def testStyles (self ):
231
268
"""Test that styles for rasters and vectors are kept when setDataSource is called"""
232
269
233
- options = QgsDataProvider .ProviderOptions ()
234
270
temp_dir = QTemporaryDir ()
235
271
p = QgsProject .instance ()
236
272
for f in (
@@ -243,50 +279,71 @@ def testStyles(self):
243
279
244
280
project_path = os .path .join (temp_dir .path (), 'good_layers_test.qgs' )
245
281
p = QgsProject ().instance ()
282
+ p .removeAllMapLayers ()
246
283
self .assertTrue (p .read (project_path ))
247
- self .assertEqual (p .count (), 3 )
284
+ self .assertEqual (p .count (), 4 )
248
285
249
286
ms = self .getBaseMapSettings ()
287
+ point_a_copy = list (p .mapLayersByName ('point_a copy' ))[0 ]
250
288
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
251
289
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
252
290
raster = list (p .mapLayersByName ('bad_layer_raster_test' ))[0 ]
291
+ self .assertTrue (point_a_copy .isValid ())
253
292
self .assertTrue (point_a .isValid ())
254
293
self .assertTrue (point_b .isValid ())
255
294
self .assertTrue (raster .isValid ())
256
295
ms .setExtent (QgsRectangle (2.81861 , 41.98138 , 2.81952 , 41.9816 ))
257
- ms .setLayers ([point_a , point_b , raster ])
296
+ ms .setLayers ([point_a_copy , point_a , point_b , raster ])
258
297
image = renderMapToImage (ms )
259
- print (os .path .join (temp_dir .path (), 'expected.png' ))
260
298
self .assertTrue (image .save (os .path .join (temp_dir .path (), 'expected.png' ), 'PNG' ))
261
299
262
300
point_a_source = point_a .publicSource ()
263
301
point_b_source = point_b .publicSource ()
264
302
raster_source = raster .publicSource ()
265
- point_a .setDataSource (point_a_source , point_a .name (), 'ogr' , options )
266
- point_b .setDataSource (point_b_source , point_b .name (), 'ogr' , options )
267
- raster .setDataSource (raster_source , raster .name (), 'gdal' , options )
303
+ self ._change_data_source (point_a , point_a_source , 'ogr' )
304
+ # Attention: we are not passing the subset string here:
305
+ self ._change_data_source (point_a_copy , point_a_source , 'ogr' )
306
+ self ._change_data_source (point_b , point_b_source , 'ogr' )
307
+ self ._change_data_source (raster , raster_source , 'gdal' )
268
308
self .assertTrue (image .save (os .path .join (temp_dir .path (), 'actual.png' ), 'PNG' ))
269
309
270
310
self .assertTrue (filecmp .cmp (os .path .join (temp_dir .path (), 'actual.png' ), os .path .join (temp_dir .path (), 'expected.png' )), False )
271
311
272
312
# Now build a bad project
313
+ p .removeAllMapLayers ()
273
314
bad_project_path = os .path .join (temp_dir .path (), 'bad_layers_test.qgs' )
274
315
with open (project_path , 'r' ) as infile :
275
316
with open (bad_project_path , 'w+' ) as outfile :
276
317
outfile .write (infile .read ().replace ('./bad_layers_test.' , './bad_layers_test-BAD_SOURCE.' ).replace ('bad_layer_raster_test.tiff' , 'bad_layer_raster_test-BAD_SOURCE.tiff' ))
277
318
319
+ p .removeAllMapLayers ()
278
320
self .assertTrue (p .read (bad_project_path ))
279
- self .assertEqual (p .count (), 3 )
321
+ self .assertEqual (p .count (), 4 )
322
+ point_a_copy = list (p .mapLayersByName ('point_a copy' ))[0 ]
280
323
point_a = list (p .mapLayersByName ('point_a' ))[0 ]
281
324
point_b = list (p .mapLayersByName ('point_b' ))[0 ]
282
325
raster = list (p .mapLayersByName ('bad_layer_raster_test' ))[0 ]
283
326
self .assertFalse (point_a .isValid ())
327
+ self .assertFalse (point_a_copy .isValid ())
284
328
self .assertFalse (point_b .isValid ())
285
329
self .assertFalse (raster .isValid ())
330
+ ms .setLayers ([point_a_copy , point_a , point_b , raster ])
331
+ image = renderMapToImage (ms )
332
+ self .assertTrue (image .save (os .path .join (temp_dir .path (), 'bad.png' ), 'PNG' ))
333
+ self .assertFalse (filecmp .cmp (os .path .join (temp_dir .path (), 'bad.png' ), os .path .join (temp_dir .path (), 'expected.png' )), False )
334
+
335
+ self ._change_data_source (point_a , point_a_source , 'ogr' )
336
+ # We are not passing the subset string!!
337
+ self ._change_data_source (point_a_copy , point_a_source , 'ogr' )
338
+ self ._change_data_source (point_b , point_b_source , 'ogr' )
339
+ self ._change_data_source (raster , raster_source , 'gdal' )
340
+ self .assertTrue (point_a .isValid ())
341
+ self .assertTrue (point_a_copy .isValid ())
342
+ self .assertTrue (point_b .isValid ())
343
+ self .assertTrue (raster .isValid ())
286
344
287
- point_a .setDataSource (point_a_source , point_a .name (), 'ogr' , options )
288
- point_b .setDataSource (point_b_source , point_b .name (), 'ogr' , options )
289
- raster .setDataSource (raster_source , raster .name (), 'gdal' , options )
345
+ ms .setLayers ([point_a_copy , point_a , point_b , raster ])
346
+ image = renderMapToImage (ms )
290
347
self .assertTrue (image .save (os .path .join (temp_dir .path (), 'actual_fixed.png' ), 'PNG' ))
291
348
292
349
self .assertTrue (filecmp .cmp (os .path .join (temp_dir .path (), 'actual_fixed.png' ), os .path .join (temp_dir .path (), 'expected.png' )), False )
0 commit comments