26
26
__revision__ = '$Format:%H$'
27
27
28
28
import os
29
+ import yaml
30
+ import hashlib
31
+
29
32
from osgeo import gdal
30
33
from osgeo .gdalconst import GA_ReadOnly
31
34
32
35
from PyQt4 .QtCore import QCoreApplication , QMetaObject
33
36
from PyQt4 .QtGui import QMessageBox , QDialog , QVBoxLayout , QTextEdit
34
37
35
38
from processing .core .Processing import Processing
36
- from processing .core .outputs import OutputNumber
37
- from processing .core .outputs import OutputString
38
- from processing .core .outputs import OutputRaster
39
- from processing .core .outputs import OutputVector
40
- from processing .tools import vector , dataobjects
39
+ from processing .core .outputs import (
40
+ OutputNumber ,
41
+ OutputString ,
42
+ OutputRaster ,
43
+ OutputVector
44
+ )
45
+
46
+ from processing .core .parameters import (
47
+ ParameterRaster ,
48
+ ParameterVector ,
49
+ ParameterMultipleInput
50
+ )
51
+
52
+
53
+ def extractSchemaPath (filepath ):
54
+ """
55
+ Trys to find where the file is relative to the QGIS source code directory.
56
+ If it is already placed in the processing or QGIS testdata directory it will
57
+ return an appropriate schema and relative filepath
58
+
59
+ Args:
60
+ filepath: The path of the file to examine
61
+
62
+ Returns:
63
+ A tuple (schema, relative_file_path) where the schema is 'qgs' or 'proc'
64
+ if we can assume that the file is in this testdata directory.
65
+ """
66
+ parts = []
67
+ schema = None
68
+ localpath = ''
69
+ path = filepath
70
+ part = True
71
+
72
+ while part :
73
+ (path , part ) = os .path .split (path )
74
+ if part == 'testdata' and not localpath :
75
+ localparts = parts
76
+ localparts .reverse ()
77
+ localpath = os .path .join (* localparts )
78
+
79
+ parts .append (part )
80
+
81
+ parts .reverse ()
82
+
83
+ try :
84
+ testsindex = parts .index ('tests' )
85
+ except ValueError :
86
+ return '' , filepath
87
+
88
+ if parts [testsindex - 1 ] == 'processing' :
89
+ schema = 'proc'
90
+
91
+ return schema , localpath
41
92
42
93
43
94
def createTest (text ):
44
- s = ''
95
+ definition = {}
96
+
45
97
tokens = text [len ('processing.runalg(' ):- 1 ].split (',' )
46
98
cmdname = (tokens [0 ])[1 :- 1 ]
47
- methodname = 'test_' + cmdname .replace (':' , '' )
48
- s += 'def ' + methodname + '(self):\n '
49
99
alg = Processing .getAlgorithm (cmdname )
50
- execcommand = 'processing.runalg('
100
+
101
+ definition ['name' ] = 'Test ({})' .format (cmdname )
102
+ definition ['algorithm' ] = cmdname
103
+
104
+ params = []
105
+ results = {}
106
+
51
107
i = 0
52
- for token in tokens :
53
- if i < alg .getVisibleParametersCount () + 1 :
54
- if os .path .exists (token [1 :- 1 ]):
55
- token = os .path .basename (token [1 :- 1 ])[:- 4 ] + '()'
56
- execcommand += token + ','
57
- else :
58
- execcommand += 'None,'
108
+ for param in alg .parameters :
109
+ if param .hidden :
110
+ continue
111
+
59
112
i += 1
60
- s += '\t outputs=' + execcommand [:- 1 ] + ')\n '
61
-
62
- i = - 1 * len (alg .outputs )
63
- for out in alg .outputs :
64
- filename = (tokens [i ])[1 :- 1 ]
65
- if tokens [i ] == unicode (None ):
66
- QMessageBox .critical (None , tr ('Error' ),
67
- tr ('Cannot create unit test for that algorithm execution. The '
68
- 'output cannot be a temporary file' ))
69
- return
70
- s += "\t output=outputs['" + out .name + "']\n "
113
+ token = tokens [i ]
114
+
115
+ if isinstance (param , ParameterVector ):
116
+ filename = token [1 :- 1 ]
117
+ schema , filepath = extractSchemaPath (filename )
118
+ p = {
119
+ 'type' : 'vector' ,
120
+ 'name' : filepath
121
+ }
122
+ if not schema :
123
+ p ['location' ] = '[The source data is not in the testdata directory. Please use data in the processing/tests/testdata folder.]'
124
+
125
+ params .append (p )
126
+ elif isinstance (param , ParameterRaster ):
127
+ filename = token [1 :- 1 ]
128
+ schema , filepath = extractSchemaPath (filename )
129
+ p = {
130
+ 'type' : 'raster' ,
131
+ 'name' : filepath
132
+ }
133
+ if not schema :
134
+ p ['location' ] = '[The source data is not in the testdata directory. Please use data in the processing/tests/testdata folder.]'
135
+
136
+ params .append (p )
137
+ elif isinstance (param , ParameterMultipleInput ):
138
+ multiparams = token [1 :- 1 ].split (';' )
139
+ newparam = []
140
+ for mp in multiparams :
141
+ schema , filepath = extractSchemaPath (mp )
142
+ newparam .append ({
143
+ 'type' : 'vector' ,
144
+ 'name' : filepath
145
+ })
146
+ p = {
147
+ 'type' : 'multi' ,
148
+ 'params' : newparam
149
+ }
150
+ if not schema :
151
+ p ['location' ] = '[The source data is not in the testdata directory. Please use data in the processing/tests/testdata folder.]'
152
+
153
+ params .append (p )
154
+ else :
155
+ params .append (token )
156
+
157
+ definition ['params' ] = params
158
+
159
+ for i , out in enumerate (alg .outputs ):
160
+ token = tokens [i - len (alg .outputs )]
161
+
71
162
if isinstance (out , (OutputNumber , OutputString )):
72
- s += 'self.assertTrue(' + unicode (out ) + ', output.value)\n '
73
- if isinstance (out , OutputRaster ):
163
+ results [out .name ] = unicode (out )
164
+ elif isinstance (out , OutputRaster ):
165
+ filename = token [1 :- 1 ]
74
166
dataset = gdal .Open (filename , GA_ReadOnly )
75
- strhash = hash (unicode (dataset .ReadAsArray (0 ).tolist ()))
76
- s += '\t self.assertTrue(os.path.isfile(output))\n '
77
- s += '\t dataset=gdal.Open(output, GA_ReadOnly)\n '
78
- s += '\t strhash=hash(unicode(dataset.ReadAsArray(0).tolist()))\n '
79
- s += '\t self.assertEqual(strhash,' + unicode (strhash ) + ')\n '
80
- if isinstance (out , OutputVector ):
81
- layer = dataobjects .getObject (filename )
82
- fields = layer .pendingFields ()
83
- s += '\t layer=dataobjects.getObjectFromUri(output, True)\n '
84
- s += '\t fields=layer.pendingFields()\n '
85
- s += '\t expectednames=[' + ',' .join (["'" + unicode (f .name ()) + "'"
86
- for f in fields ]) + ']\n '
87
- s += '\t expectedtypes=[' + ',' .join (["'" + unicode (f .typeName ()) + "'"
88
- for f in fields ]) + ']\n '
89
- s += '\t names=[unicode(f.name()) for f in fields]\n '
90
- s += '\t types=[unicode(f.typeName()) for f in fields]\n '
91
- s += '\t self.assertEqual(expectednames, names)\n '
92
- s += '\t self.assertEqual(expectedtypes, types)\n '
93
- features = vector .features (layer )
94
- numfeat = len (features )
95
- s += '\t features=processing.features(layer)\n '
96
- s += '\t self.assertEqual(' + unicode (numfeat ) + ', len(features))\n '
97
- if numfeat > 0 :
98
- feature = features .next ()
99
- attrs = feature .attributes ()
100
- s += '\t feature=features.next()\n '
101
- s += '\t attrs=feature.attributes()\n '
102
- s += '\t expectedvalues=[' + ',' .join (['"' + unicode (attr ) + '"'
103
- for attr in attrs ]) + ']\n '
104
- s += '\t values=[unicode(attr) for attr in attrs]\n '
105
- s += '\t self.assertEqual(expectedvalues, values)\n '
106
- s += "\t wkt='" + unicode (feature .geometry ().exportToWkt ()) + "'\n "
107
- s += '\t self.assertEqual(wkt, \
108
- unicode(feature.geometry().exportToWkt()))'
109
-
110
- dlg = ShowTestDialog (s )
167
+ strhash = hashlib .sha224 (dataset .ReadAsArray (0 ).data ).hexdigest ()
168
+
169
+ results [out .name ] = {
170
+ 'type' : 'rasterhash' ,
171
+ 'hash' : strhash
172
+ }
173
+ elif isinstance (out , OutputVector ):
174
+ filename = token [1 :- 1 ]
175
+ schema , filepath = extractSchemaPath (filename )
176
+ results [out .name ] = {
177
+ 'type' : 'vector' ,
178
+ 'name' : filepath
179
+ }
180
+ if not schema :
181
+ results [out .name ]['location' ] = '[The expected result data is not in the testdata directory. Please write it to processing/tests/testdata/expected. Prefer gml files.]'
182
+
183
+ definition ['results' ] = results
184
+
185
+ dlg = ShowTestDialog (yaml .dump ([definition ], default_flow_style = False ))
111
186
dlg .exec_ ()
112
187
113
188
@@ -124,6 +199,7 @@ def __init__(self, s):
124
199
self .setWindowTitle (self .tr ('Unit test' ))
125
200
layout = QVBoxLayout ()
126
201
self .text = QTextEdit ()
202
+ self .text .setFontFamily ("monospace" )
127
203
self .text .setEnabled (True )
128
204
self .text .setText (s )
129
205
layout .addWidget (self .text )
0 commit comments