Skip to content

Commit f0629ea

Browse files
committedFeb 4, 2016
[processing] Allow test creation from history window
1 parent 8adc871 commit f0629ea

File tree

3 files changed

+141
-67
lines changed

3 files changed

+141
-67
lines changed
 

‎python/plugins/processing/gui/AlgorithmDialog.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ def __init__(self, alg):
8888

8989
def runAsBatch(self):
9090
dlg = BatchAlgorithmDialog(self.alg)
91-
dlg.show()
9291
dlg.exec_()
9392

9493
def setParamValues(self):

‎python/plugins/processing/gui/HistoryDialog.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@ def createTest(self):
128128
TestTools.createTest(item.entry.text)
129129

130130
def showPopupMenu(self, point):
131-
return
132131
item = self.tree.currentItem()
133132
if isinstance(item, TreeLogEntryItem):
134133
if item.isAlg:

‎python/plugins/processing/gui/TestTools.py

Lines changed: 141 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -26,88 +26,163 @@
2626
__revision__ = '$Format:%H$'
2727

2828
import os
29+
import yaml
30+
import hashlib
31+
2932
from osgeo import gdal
3033
from osgeo.gdalconst import GA_ReadOnly
3134

3235
from PyQt4.QtCore import QCoreApplication, QMetaObject
3336
from PyQt4.QtGui import QMessageBox, QDialog, QVBoxLayout, QTextEdit
3437

3538
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
4192

4293

4394
def createTest(text):
44-
s = ''
95+
definition = {}
96+
4597
tokens = text[len('processing.runalg('):-1].split(',')
4698
cmdname = (tokens[0])[1:-1]
47-
methodname = 'test_' + cmdname.replace(':', '')
48-
s += 'def ' + methodname + '(self):\n'
4999
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+
51107
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+
59112
i += 1
60-
s += '\toutputs=' + 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 += "\toutput=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+
71162
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]
74166
dataset = gdal.Open(filename, GA_ReadOnly)
75-
strhash = hash(unicode(dataset.ReadAsArray(0).tolist()))
76-
s += '\tself.assertTrue(os.path.isfile(output))\n'
77-
s += '\tdataset=gdal.Open(output, GA_ReadOnly)\n'
78-
s += '\tstrhash=hash(unicode(dataset.ReadAsArray(0).tolist()))\n'
79-
s += '\tself.assertEqual(strhash,' + unicode(strhash) + ')\n'
80-
if isinstance(out, OutputVector):
81-
layer = dataobjects.getObject(filename)
82-
fields = layer.pendingFields()
83-
s += '\tlayer=dataobjects.getObjectFromUri(output, True)\n'
84-
s += '\tfields=layer.pendingFields()\n'
85-
s += '\texpectednames=[' + ','.join(["'" + unicode(f.name()) + "'"
86-
for f in fields]) + ']\n'
87-
s += '\texpectedtypes=[' + ','.join(["'" + unicode(f.typeName()) + "'"
88-
for f in fields]) + ']\n'
89-
s += '\tnames=[unicode(f.name()) for f in fields]\n'
90-
s += '\ttypes=[unicode(f.typeName()) for f in fields]\n'
91-
s += '\tself.assertEqual(expectednames, names)\n'
92-
s += '\tself.assertEqual(expectedtypes, types)\n'
93-
features = vector.features(layer)
94-
numfeat = len(features)
95-
s += '\tfeatures=processing.features(layer)\n'
96-
s += '\tself.assertEqual(' + unicode(numfeat) + ', len(features))\n'
97-
if numfeat > 0:
98-
feature = features.next()
99-
attrs = feature.attributes()
100-
s += '\tfeature=features.next()\n'
101-
s += '\tattrs=feature.attributes()\n'
102-
s += '\texpectedvalues=[' + ','.join(['"' + unicode(attr) + '"'
103-
for attr in attrs]) + ']\n'
104-
s += '\tvalues=[unicode(attr) for attr in attrs]\n'
105-
s += '\tself.assertEqual(expectedvalues, values)\n'
106-
s += "\twkt='" + unicode(feature.geometry().exportToWkt()) + "'\n"
107-
s += '\tself.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))
111186
dlg.exec_()
112187

113188

@@ -124,6 +199,7 @@ def __init__(self, s):
124199
self.setWindowTitle(self.tr('Unit test'))
125200
layout = QVBoxLayout()
126201
self.text = QTextEdit()
202+
self.text.setFontFamily("monospace")
127203
self.text.setEnabled(True)
128204
self.text.setText(s)
129205
layout.addWidget(self.text)

0 commit comments

Comments
 (0)