1
1
# -*- coding: utf-8 -*-
2
2
"""QGIS Unit test utils for offline editing tests.
3
3
4
+ There are three layers referenced through the code:
5
+
6
+ - the "online_layer" is the layer being edited online (WFS or PostGIS) layer inside
7
+ QGIS client
8
+ - the "offline_layer" (SQLite)
9
+ - the "layer", is the shapefile layer that is served by QGIS Server WFS, in case of
10
+ PostGIS, this will be the same layer referenced by online_layer
11
+
12
+ Each test simulates one working session.
13
+
14
+ When testing on PostGIS, the first two layers will be exactly the same object.
15
+
4
16
.. note:: This program is free software; you can redistribute it and/or modify
5
17
it under the terms of the GNU General Public License as published by
6
18
the Free Software Foundation; either version 2 of the License, or
16
28
__revision__ = '$Format:%H$'
17
29
18
30
from time import sleep
31
+ import os
19
32
20
33
from qgis .core import (
21
34
QgsFeature ,
37
50
(4 , 'name 4' , QgsPoint (10 , 46.5 )),
38
51
]
39
52
53
+ # Additional features for insert test
54
+ TEST_FEATURES_INSERT = [
55
+ (5 , 'name 5' , QgsPoint (9.7 , 45.7 )),
56
+ (6 , 'name 6' , QgsPoint (10.6 , 46.6 )),
57
+ ]
58
+
40
59
41
60
class OfflineTestBase (object ):
42
61
@@ -47,22 +66,25 @@ def _setUp(self):
47
66
# Setup: create some features for the test layer
48
67
features = []
49
68
layer = self ._getLayer ('test_point' )
69
+ assert layer .startEditing ()
50
70
for id , name , geom in TEST_FEATURES :
51
71
f = QgsFeature (layer .pendingFields ())
52
72
f ['id' ] = id
53
73
f ['name' ] = name
54
74
f .setGeometry (QgsGeometry .fromPoint (geom ))
55
75
features .append (f )
56
- layer .dataProvider ().addFeatures (features )
57
- # Add the remote layer
76
+ layer .addFeatures (features )
77
+ assert layer .commitChanges ()
78
+ # Add the online layer
58
79
self .registry = QgsMapLayerRegistry .instance ()
59
80
self .registry .removeAllMapLayers ()
60
81
assert self .registry .addMapLayer (self ._getOnlineLayer ('test_point' )) is not None
61
82
62
83
def _tearDown (self ):
63
84
"""Called by tearDown: run after each test."""
64
- # Clear test layers
65
- self ._clearLayer ('test_point' )
85
+ # Delete the sqlite db
86
+ #os.unlink(os.path.join(self.temp_path, 'offlineDbFile.sqlite'))
87
+ pass
66
88
67
89
@classmethod
68
90
def _compareFeature (cls , layer , attributes ):
@@ -71,11 +93,10 @@ def _compareFeature(cls, layer, attributes):
71
93
return f ['name' ] == attributes [1 ] and f .geometry ().asPoint ().toString () == attributes [2 ].toString ()
72
94
73
95
@classmethod
74
- def _clearLayer (cls , layer_name ):
96
+ def _clearLayer (cls , layer ):
75
97
"""
76
- Delete all features from the backend layer
98
+ Delete all features from the given layer
77
99
"""
78
- layer = cls ._getLayer (layer_name )
79
100
layer .startEditing ()
80
101
layer .deleteFeatures ([f .id () for f in layer .getFeatures ()])
81
102
layer .commitChanges ()
@@ -108,19 +129,26 @@ def _getFeatureByAttribute(cls, layer, attr_name, attr_value):
108
129
raise Exception ("Wrong attributes in WFS layer %s" %
109
130
layer .name ())
110
131
111
- def test_offlineConversion (self ):
132
+ def _testInit (self ):
133
+ """
134
+ Preliminary checks for each test
135
+ """
112
136
# goes offline
113
137
ol = QgsOfflineEditing ()
114
138
online_layer = list (self .registry .mapLayers ().values ())[0 ]
115
139
self .assertTrue (online_layer .hasGeometryType ())
116
- # Check we have 3 features
140
+ # Check we have features
117
141
self .assertEqual (len ([f for f in online_layer .getFeatures ()]), len (TEST_FEATURES ))
118
142
self .assertTrue (ol .convertToOfflineProject (self .temp_path , 'offlineDbFile.sqlite' , [online_layer .id ()]))
119
143
offline_layer = list (self .registry .mapLayers ().values ())[0 ]
120
144
self .assertTrue (offline_layer .hasGeometryType ())
121
145
self .assertTrue (offline_layer .isValid ())
122
146
self .assertTrue (offline_layer .name ().find ('(offline)' ) > - 1 )
123
147
self .assertEqual (len ([f for f in offline_layer .getFeatures ()]), len (TEST_FEATURES ))
148
+ return ol , offline_layer
149
+
150
+ def test_updateFeatures (self ):
151
+ ol , offline_layer = self ._testInit ()
124
152
# Edit feature 2
125
153
feat2 = self ._getFeatureByAttribute (offline_layer , 'name' , "'name 2'" )
126
154
self .assertTrue (offline_layer .startEditing ())
@@ -131,8 +159,8 @@ def test_offlineConversion(self):
131
159
self .assertTrue (ol .isOfflineProject ())
132
160
# Sync
133
161
ol .synchronize ()
162
+ sleep (2 )
134
163
# Does anybody know why the sleep is needed? Is that a threaded WFS consequence?
135
- sleep (1 )
136
164
online_layer = list (self .registry .mapLayers ().values ())[0 ]
137
165
self .assertTrue (online_layer .isValid ())
138
166
self .assertFalse (online_layer .name ().find ('(offline)' ) > - 1 )
@@ -176,3 +204,98 @@ def test_offlineConversion(self):
176
204
self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [1 - 1 ]))
177
205
self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [2 - 1 ]))
178
206
self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [3 - 1 ]))
207
+
208
+ def test_deleteOneFeature (self ):
209
+ """
210
+ Delete a single feature
211
+ """
212
+ ol , offline_layer = self ._testInit ()
213
+ # Delete feature 3
214
+ feat3 = self ._getFeatureByAttribute (offline_layer , 'name' , "'name 3'" )
215
+ self .assertTrue (offline_layer .startEditing ())
216
+ self .assertTrue (offline_layer .deleteFeatures ([feat3 .id ()]))
217
+ self .assertTrue (offline_layer .commitChanges ())
218
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (offline_layer , 'name' , "'name 3'" ))
219
+ self .assertTrue (ol .isOfflineProject ())
220
+ # Sync
221
+ ol .synchronize ()
222
+ # Does anybody know why the sleep is needed? Is that a threaded WFS consequence?
223
+ sleep (1 )
224
+ online_layer = list (self .registry .mapLayers ().values ())[0 ]
225
+ self .assertTrue (online_layer .isValid ())
226
+ self .assertFalse (online_layer .name ().find ('(offline)' ) > - 1 )
227
+ self .assertEqual (len ([f for f in online_layer .getFeatures ()]), len (TEST_FEATURES ) - 1 )
228
+ # Check that data have changed in the backend (raise exception if not found)
229
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (online_layer , 'name' , "'name 3'" ))
230
+ # Check that all other features have not changed
231
+ layer = self ._getLayer ('test_point' )
232
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [1 - 1 ]))
233
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [2 - 1 ]))
234
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [4 - 1 ]))
235
+
236
+ def test_deleteMultipleFeatures (self ):
237
+ """
238
+ Delete a multiple features
239
+ """
240
+ ol , offline_layer = self ._testInit ()
241
+ # Delete feature 1 and 3
242
+ feat1 = self ._getFeatureByAttribute (offline_layer , 'name' , "'name 1'" )
243
+ feat3 = self ._getFeatureByAttribute (offline_layer , 'name' , "'name 3'" )
244
+ self .assertTrue (offline_layer .startEditing ())
245
+ self .assertTrue (offline_layer .deleteFeatures ([feat3 .id (), feat1 .id ()]))
246
+ self .assertTrue (offline_layer .commitChanges ())
247
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (offline_layer , 'name' , "'name 3'" ))
248
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (offline_layer , 'name' , "'name 1'" ))
249
+ self .assertTrue (ol .isOfflineProject ())
250
+ # Sync
251
+ ol .synchronize ()
252
+ # Does anybody know why the sleep is needed? Is that a threaded WFS consequence?
253
+ sleep (1 )
254
+ online_layer = list (self .registry .mapLayers ().values ())[0 ]
255
+ self .assertTrue (online_layer .isValid ())
256
+ self .assertFalse (online_layer .name ().find ('(offline)' ) > - 1 )
257
+ self .assertEqual (len ([f for f in online_layer .getFeatures ()]), len (TEST_FEATURES ) - 2 )
258
+ # Check that data have changed in the backend (raise exception if not found)
259
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (online_layer , 'name' , "'name 3'" ))
260
+ self .assertRaises (Exception , lambda : self ._getFeatureByAttribute (online_layer , 'name' , "'name 1'" ))
261
+ # Check that all other features have not changed
262
+ layer = self ._getLayer ('test_point' )
263
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [2 - 1 ]))
264
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [4 - 1 ]))
265
+
266
+ def test_InsertFeatures (self ):
267
+ """
268
+ Insert multiple features
269
+ """
270
+ ol , offline_layer = self ._testInit ()
271
+ # Insert feature 5 and 6
272
+ self .assertTrue (offline_layer .startEditing ())
273
+ features = []
274
+ for id , name , geom in TEST_FEATURES_INSERT :
275
+ f = QgsFeature (offline_layer .pendingFields ())
276
+ f ['id' ] = id
277
+ f ['name' ] = name
278
+ f .setGeometry (QgsGeometry .fromPoint (geom ))
279
+ features .append (f )
280
+ offline_layer .addFeatures (features )
281
+ self .assertTrue (offline_layer .commitChanges ())
282
+ self ._getFeatureByAttribute (offline_layer , 'name' , "'name 5'" )
283
+ self ._getFeatureByAttribute (offline_layer , 'name' , "'name 6'" )
284
+ self .assertTrue (ol .isOfflineProject ())
285
+ # Sync
286
+ ol .synchronize ()
287
+ # Does anybody know why the sleep is needed? Is that a threaded WFS consequence?
288
+ sleep (1 )
289
+ online_layer = list (self .registry .mapLayers ().values ())[0 ]
290
+ self .assertTrue (online_layer .isValid ())
291
+ self .assertFalse (online_layer .name ().find ('(offline)' ) > - 1 )
292
+ self .assertEqual (len ([f for f in online_layer .getFeatures ()]), len (TEST_FEATURES ) + 2 )
293
+ # Check that data have changed in the backend (raise exception if not found)
294
+ self ._getFeatureByAttribute (online_layer , 'name' , "'name 5'" )
295
+ self ._getFeatureByAttribute (online_layer , 'name' , "'name 6'" )
296
+ # Check that all other features have not changed
297
+ layer = self ._getLayer ('test_point' )
298
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [1 - 1 ]))
299
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [2 - 1 ]))
300
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [3 - 1 ]))
301
+ self .assertTrue (self ._compareFeature (layer , TEST_FEATURES [4 - 1 ]))
0 commit comments