Skip to content

Commit d10e564

Browse files
committedOct 18, 2016
[DBManager] Add tests for GPKG plugin
1 parent 3f2866c commit d10e564

File tree

2 files changed

+394
-0
lines changed

2 files changed

+394
-0
lines changed
 

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ ADD_PYTHON_TEST(PyQgsWFSProviderGUI test_provider_wfs_gui.py)
118118
ADD_PYTHON_TEST(PyQgsConsole test_console.py)
119119
ADD_PYTHON_TEST(PyQgsLayerDependencies test_layer_dependencies.py)
120120
ADD_PYTHON_TEST(PyQgsVersionCompare test_versioncompare.py)
121+
ADD_PYTHON_TEST(PyQgsDBManagerGpkg test_db_manager_gpkg.py)
121122

122123
IF (NOT WIN32)
123124
ADD_PYTHON_TEST(PyQgsLogger test_qgslogger.py)
Lines changed: 393 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,393 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for the DBManager GPKG plugin
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Even Rouault'
10+
__date__ = '2016-10-17'
11+
__copyright__ = 'Copyright 2016, Even Rouault'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
import os
18+
import tempfile
19+
import shutil
20+
from osgeo import gdal, ogr, osr
21+
22+
from qgis.core import QgsDataSourceUri
23+
from qgis.PyQt.QtCore import QCoreApplication, QSettings
24+
from qgis.testing import start_app, unittest
25+
26+
from plugins.db_manager.db_plugins import supportedDbTypes, createDbPlugin
27+
from plugins.db_manager.db_plugins.plugin import TableField
28+
29+
30+
def GDAL_COMPUTE_VERSION(maj, min, rev):
31+
return ((maj) * 1000000 + (min) * 10000 + (rev) * 100)
32+
33+
34+
class TestPyQgsDBManagerGpkg(unittest.TestCase):
35+
36+
@classmethod
37+
def setUpClass(cls):
38+
"""Run before all tests"""
39+
40+
QCoreApplication.setOrganizationName("QGIS_Test")
41+
QCoreApplication.setOrganizationDomain("TestPyQgsDBManagerGpkg.com")
42+
QCoreApplication.setApplicationName("TestPyQgsDBManagerGpkg")
43+
QSettings().clear()
44+
start_app()
45+
46+
cls.basetestpath = tempfile.mkdtemp()
47+
48+
cls.test_gpkg = os.path.join(cls.basetestpath, 'TestPyQgsDBManagerGpkg.gpkg')
49+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(cls.test_gpkg)
50+
lyr = ds.CreateLayer('testLayer', geom_type=ogr.wkbLineString, options=['SPATIAL_INDEX=NO'])
51+
cls.supportsAlterFieldDefn = lyr.TestCapability(ogr.OLCAlterFieldDefn) == 1
52+
lyr.CreateField(ogr.FieldDefn('text_field', ogr.OFTString))
53+
f = ogr.Feature(lyr.GetLayerDefn())
54+
f['text_field'] = 'foo'
55+
f.SetGeometry(ogr.CreateGeometryFromWkt('LINESTRING(1 2,3 4)'))
56+
lyr.CreateFeature(f)
57+
f = None
58+
ds = None
59+
60+
@classmethod
61+
def tearDownClass(cls):
62+
"""Run after all tests"""
63+
64+
QSettings().clear()
65+
shutil.rmtree(cls.basetestpath, True)
66+
67+
def testSupportedDbTypes(self):
68+
self.assertIn('gpkg', supportedDbTypes())
69+
70+
def testCreateDbPlugin(self):
71+
plugin = createDbPlugin('gpkg')
72+
self.assertIsNotNone(plugin)
73+
74+
def testConnect(self):
75+
connection_name = 'testConnect'
76+
plugin = createDbPlugin('gpkg')
77+
78+
uri = QgsDataSourceUri()
79+
uri.setDatabase(self.test_gpkg)
80+
self.assertTrue(plugin.addConnection(connection_name, uri))
81+
82+
connections = plugin.connections()
83+
self.assertEqual(len(connections), 1)
84+
85+
connection = createDbPlugin('gpkg', connection_name + '_does_not_exist')
86+
connection_succeeded = False
87+
try:
88+
connection.connect()
89+
connection_succeeded = True
90+
except:
91+
pass
92+
self.assertFalse(connection_succeeded, 'exception should have been raised')
93+
94+
connection = connections[0]
95+
connection.connect()
96+
97+
connection.reconnect()
98+
99+
connection.remove()
100+
101+
self.assertEqual(len(plugin.connections()), 0)
102+
103+
connection = createDbPlugin('gpkg', connection_name)
104+
connection_succeeded = False
105+
try:
106+
connection.connect()
107+
connection_succeeded = True
108+
except:
109+
pass
110+
self.assertFalse(connection_succeeded, 'exception should have been raised')
111+
112+
def testListLayer(self):
113+
connection_name = 'testListLayer'
114+
plugin = createDbPlugin('gpkg')
115+
uri = QgsDataSourceUri()
116+
uri.setDatabase(self.test_gpkg)
117+
self.assertTrue(plugin.addConnection(connection_name, uri))
118+
119+
connection = createDbPlugin('gpkg', connection_name)
120+
connection.connect()
121+
122+
db = connection.database()
123+
self.assertIsNotNone(db)
124+
125+
tables = db.tables()
126+
self.assertEqual(len(tables), 1)
127+
table = tables[0]
128+
self.assertEqual(table.name, 'testLayer')
129+
info = table.info()
130+
expected_html = """<div class="section"><h2>General info</h2><div><table><tr><td>Relation type:&nbsp;</td><td>Table&nbsp;</td></tr><tr><td>Rows:&nbsp;</td><td>1&nbsp;</td></tr></table></div></div><div class="section"><h2>GeoPackage</h2><div><table><tr><td>Column:&nbsp;</td><td>geom&nbsp;</td></tr><tr><td>Geometry:&nbsp;</td><td>LINE STRING&nbsp;</td></tr><tr><td>Dimension:&nbsp;</td><td>XY&nbsp;</td></tr><tr><td>Spatial ref:&nbsp;</td><td>Undefined (-1)&nbsp;</td></tr><tr><td>Extent:&nbsp;</td><td>1.00000, 2.00000 - 3.00000, 4.00000&nbsp;</td></tr></table><p><warning> No spatial index defined (<a href="action:spatialindex/create">create it</a>)</p></div></div><div class="section"><h2>Fields</h2><div><table class="header"><tr><th>#&nbsp;</th><th>Name&nbsp;</th><th>Type&nbsp;</th><th>Null&nbsp;</th><th>Default&nbsp;</th></tr><tr><td>0&nbsp;</td><td class="underline">fid&nbsp;</td><td>INTEGER&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr><tr><td>1&nbsp;</td><td>geom&nbsp;</td><td>LINESTRING&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr><tr><td>2&nbsp;</td><td>text_field&nbsp;</td><td>TEXT&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr></table></div></div>"""
131+
self.assertEqual(info.toHtml(), expected_html, info.toHtml())
132+
133+
connection.remove()
134+
135+
def testCreateRenameDeleteTable(self):
136+
connection_name = 'testCreateRenameDeleteTable'
137+
plugin = createDbPlugin('gpkg')
138+
uri = QgsDataSourceUri()
139+
140+
test_gpkg_new = os.path.join(self.basetestpath, 'testCreateRenameDeleteTable.gpkg')
141+
shutil.copy(self.test_gpkg, test_gpkg_new)
142+
143+
uri.setDatabase(test_gpkg_new)
144+
self.assertTrue(plugin.addConnection(connection_name, uri))
145+
146+
connection = createDbPlugin('gpkg', connection_name)
147+
connection.connect()
148+
149+
db = connection.database()
150+
self.assertIsNotNone(db)
151+
152+
tables = db.tables()
153+
self.assertEqual(len(tables), 1)
154+
table = tables[0]
155+
self.assertTrue(table.rename('newName'))
156+
self.assertEqual(table.name, 'newName')
157+
158+
connection.reconnect()
159+
160+
db = connection.database()
161+
tables = db.tables()
162+
self.assertEqual(len(tables), 1)
163+
table = tables[0]
164+
self.assertEqual(table.name, 'newName')
165+
166+
fields = []
167+
geom = ['geometry', 'POINT', 4326, 3]
168+
field1 = TableField(table)
169+
field1.name = 'fid'
170+
field1.dataType = 'INTEGER'
171+
field1.notNull = True
172+
field1.primaryKey = True
173+
174+
field2 = TableField(table)
175+
field2.name = 'str_field'
176+
field2.dataType = 'TEXT'
177+
field2.modifier = 20
178+
179+
fields = [field1, field2]
180+
self.assertTrue(db.createVectorTable('newName2', fields, geom))
181+
182+
tables = db.tables()
183+
self.assertEqual(len(tables), 2)
184+
new_table = tables[1]
185+
self.assertEqual(new_table.name, 'newName2')
186+
fields = new_table.fields()
187+
self.assertEqual(len(fields), 3)
188+
self.assertFalse(new_table.hasSpatialIndex())
189+
190+
self.assertTrue(new_table.createSpatialIndex())
191+
self.assertTrue(new_table.hasSpatialIndex())
192+
193+
self.assertTrue(new_table.delete())
194+
195+
tables = db.tables()
196+
self.assertEqual(len(tables), 1)
197+
198+
connection.remove()
199+
200+
def testCreateRenameDeleteFields(self):
201+
202+
if not self.supportsAlterFieldDefn:
203+
return
204+
205+
connection_name = 'testCreateRenameDeleteFields'
206+
plugin = createDbPlugin('gpkg')
207+
uri = QgsDataSourceUri()
208+
209+
test_gpkg_new = os.path.join(self.basetestpath, 'testCreateRenameDeleteFields.gpkg')
210+
shutil.copy(self.test_gpkg, test_gpkg_new)
211+
212+
uri.setDatabase(test_gpkg_new)
213+
self.assertTrue(plugin.addConnection(connection_name, uri))
214+
215+
connection = createDbPlugin('gpkg', connection_name)
216+
connection.connect()
217+
218+
db = connection.database()
219+
self.assertIsNotNone(db)
220+
221+
tables = db.tables()
222+
self.assertEqual(len(tables), 1)
223+
table = tables[0]
224+
225+
field_before_count = len(table.fields())
226+
227+
field = TableField(table)
228+
field.name = 'real_field'
229+
field.dataType = 'DOUBLE'
230+
self.assertTrue(table.addField(field))
231+
232+
self.assertEqual(len(table.fields()), field_before_count + 1)
233+
234+
self.assertTrue(field.update('real_field2', new_type_str='TEXT (30)', new_not_null=True, new_default_str='foo'))
235+
236+
field = table.fields()[field_before_count]
237+
self.assertEqual(field.name, 'real_field2')
238+
self.assertEqual(field.dataType, 'TEXT(30)')
239+
self.assertEqual(field.notNull, 1)
240+
self.assertEqual(field.default, "'foo'")
241+
242+
self.assertTrue(table.deleteField(field))
243+
244+
self.assertEqual(len(table.fields()), field_before_count)
245+
246+
connection.remove()
247+
248+
def testTableDataModel(self):
249+
connection_name = 'testTableDataModel'
250+
plugin = createDbPlugin('gpkg')
251+
uri = QgsDataSourceUri()
252+
uri.setDatabase(self.test_gpkg)
253+
self.assertTrue(plugin.addConnection(connection_name, uri))
254+
255+
connection = createDbPlugin('gpkg', connection_name)
256+
connection.connect()
257+
258+
db = connection.database()
259+
self.assertIsNotNone(db)
260+
261+
tables = db.tables()
262+
self.assertEqual(len(tables), 1)
263+
table = tables[0]
264+
self.assertEqual(table.name, 'testLayer')
265+
model = table.tableDataModel(None)
266+
self.assertEqual(model.rowCount(), 1)
267+
self.assertEqual(model.getData(0, 0), 1) # fid
268+
self.assertEqual(model.getData(0, 1), 'LINESTRING (1 2,3 4)')
269+
self.assertEqual(model.getData(0, 2), 'foo')
270+
271+
connection.remove()
272+
273+
def testRaster(self):
274+
275+
if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 2):
276+
return
277+
278+
connection_name = 'testRaster'
279+
plugin = createDbPlugin('gpkg')
280+
uri = QgsDataSourceUri()
281+
282+
test_gpkg_new = os.path.join(self.basetestpath, 'testRaster.gpkg')
283+
shutil.copy(self.test_gpkg, test_gpkg_new)
284+
mem_ds = gdal.GetDriverByName('MEM').Create('', 20, 20)
285+
mem_ds.SetGeoTransform([2, 0.01, 0, 49, 0, -0.01])
286+
sr = osr.SpatialReference()
287+
sr.ImportFromEPSG(4326)
288+
mem_ds.SetProjection(sr.ExportToWkt())
289+
mem_ds.GetRasterBand(1).Fill(255)
290+
gdal.GetDriverByName('GPKG').CreateCopy(test_gpkg_new, mem_ds, options=['APPEND_SUBDATASET=YES', 'RASTER_TABLE=raster_table'])
291+
mem_ds = None
292+
293+
uri.setDatabase(test_gpkg_new)
294+
self.assertTrue(plugin.addConnection(connection_name, uri))
295+
296+
connection = createDbPlugin('gpkg', connection_name)
297+
connection.connect()
298+
299+
db = connection.database()
300+
self.assertIsNotNone(db)
301+
302+
tables = db.tables()
303+
self.assertEqual(len(tables), 2)
304+
table = None
305+
for i in range(2):
306+
if tables[i].name == 'raster_table':
307+
table = tables[i]
308+
break
309+
self.assertIsNotNone(table)
310+
info = table.info()
311+
expected_html = """<div class="section"><h2>General info</h2><div><table><tr><td>Relation type:&nbsp;</td><td>Table&nbsp;</td></tr><tr><td>Rows:&nbsp;</td><td>Unknown (<a href="action:rows/count">find out</a>)&nbsp;</td></tr></table></div></div><div class="section"><h2>GeoPackage</h2><div><table><tr><td>Column:&nbsp;</td><td>&nbsp;</td></tr><tr><td>Geometry:&nbsp;</td><td>RASTER&nbsp;</td></tr><tr><td>Spatial ref:&nbsp;</td><td>WGS 84 geodetic (4326)&nbsp;</td></tr><tr><td>Extent:&nbsp;</td><td>2.00000, 48.80000 - 2.20000, 49.00000&nbsp;</td></tr></table></div></div><div class="section"><h2>Fields</h2><div><table class="header"><tr><th>#&nbsp;</th><th>Name&nbsp;</th><th>Type&nbsp;</th><th>Null&nbsp;</th><th>Default&nbsp;</th></tr><tr><td>0&nbsp;</td><td class="underline">id&nbsp;</td><td>INTEGER&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr><tr><td>1&nbsp;</td><td>zoom_level&nbsp;</td><td>INTEGER&nbsp;</td><td>N&nbsp;</td><td>&nbsp;</td></tr><tr><td>2&nbsp;</td><td>tile_column&nbsp;</td><td>INTEGER&nbsp;</td><td>N&nbsp;</td><td>&nbsp;</td></tr><tr><td>3&nbsp;</td><td>tile_row&nbsp;</td><td>INTEGER&nbsp;</td><td>N&nbsp;</td><td>&nbsp;</td></tr><tr><td>4&nbsp;</td><td>tile_data&nbsp;</td><td>BLOB&nbsp;</td><td>N&nbsp;</td><td>&nbsp;</td></tr></table></div></div><div class="section"><h2>Indexes</h2><div><table class="header"><tr><th>Name&nbsp;</th><th>Column(s)&nbsp;</th></tr><tr><td>sqlite_autoindex_raster_table_1&nbsp;</td><td>zoom_level<br>tile_column<br>tile_row&nbsp;</td></tr></table></div></div>"""
312+
self.assertEqual(info.toHtml(), expected_html, info.toHtml())
313+
314+
connection.remove()
315+
316+
def testTwoRaster(self):
317+
318+
if int(gdal.VersionInfo('VERSION_NUM')) < GDAL_COMPUTE_VERSION(2, 0, 2):
319+
return
320+
321+
connection_name = 'testTwoRaster'
322+
plugin = createDbPlugin('gpkg')
323+
uri = QgsDataSourceUri()
324+
325+
test_gpkg_new = os.path.join(self.basetestpath, 'testTwoRaster.gpkg')
326+
shutil.copy(self.test_gpkg, test_gpkg_new)
327+
mem_ds = gdal.GetDriverByName('MEM').Create('', 20, 20)
328+
mem_ds.SetGeoTransform([2, 0.01, 0, 49, 0, -0.01])
329+
sr = osr.SpatialReference()
330+
sr.ImportFromEPSG(4326)
331+
mem_ds.SetProjection(sr.ExportToWkt())
332+
mem_ds.GetRasterBand(1).Fill(255)
333+
for i in range(2):
334+
gdal.GetDriverByName('GPKG').CreateCopy(test_gpkg_new, mem_ds, options=['APPEND_SUBDATASET=YES', 'RASTER_TABLE=raster_table%d' % (i + 1)])
335+
mem_ds = None
336+
337+
uri.setDatabase(test_gpkg_new)
338+
self.assertTrue(plugin.addConnection(connection_name, uri))
339+
340+
connection = createDbPlugin('gpkg', connection_name)
341+
connection.connect()
342+
343+
db = connection.database()
344+
self.assertIsNotNone(db)
345+
346+
tables = db.tables()
347+
self.assertEqual(len(tables), 3)
348+
table = None
349+
for i in range(2):
350+
if tables[i].name.startswith('raster_table'):
351+
table = tables[i]
352+
info = table.info()
353+
info.toHtml()
354+
355+
connection.remove()
356+
357+
def testNonSpatial(self):
358+
359+
connection_name = 'testNonSpatial'
360+
plugin = createDbPlugin('gpkg')
361+
uri = QgsDataSourceUri()
362+
363+
test_gpkg = os.path.join(self.basetestpath, 'testNonSpatial.gpkg')
364+
ds = ogr.GetDriverByName('GPKG').CreateDataSource(test_gpkg)
365+
lyr = ds.CreateLayer('testNonSpatial', geom_type=ogr.wkbNone)
366+
lyr.CreateField(ogr.FieldDefn('text_field', ogr.OFTString))
367+
f = ogr.Feature(lyr.GetLayerDefn())
368+
f['text_field'] = 'foo'
369+
lyr.CreateFeature(f)
370+
f = None
371+
ds = None
372+
373+
uri.setDatabase(test_gpkg)
374+
self.assertTrue(plugin.addConnection(connection_name, uri))
375+
376+
connection = createDbPlugin('gpkg', connection_name)
377+
connection.connect()
378+
379+
db = connection.database()
380+
self.assertIsNotNone(db)
381+
382+
tables = db.tables()
383+
self.assertEqual(len(tables), 1)
384+
table = tables[0]
385+
self.assertEqual(table.name, 'testNonSpatial')
386+
info = table.info()
387+
expected_html = """<div class="section"><h2>General info</h2><div><table><tr><td>Relation type:&nbsp;</td><td>Table&nbsp;</td></tr><tr><td>Rows:&nbsp;</td><td>1&nbsp;</td></tr></table></div></div><div class="section"><h2>Fields</h2><div><table class="header"><tr><th>#&nbsp;</th><th>Name&nbsp;</th><th>Type&nbsp;</th><th>Null&nbsp;</th><th>Default&nbsp;</th></tr><tr><td>0&nbsp;</td><td class="underline">fid&nbsp;</td><td>INTEGER&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr><tr><td>1&nbsp;</td><td>text_field&nbsp;</td><td>TEXT&nbsp;</td><td>Y&nbsp;</td><td>&nbsp;</td></tr></table></div></div>"""
388+
self.assertEqual(info.toHtml(), expected_html, info.toHtml())
389+
390+
connection.remove()
391+
392+
if __name__ == '__main__':
393+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.