1 /***************************************************************************
2 testqgsprocessing.cpp
3 ---------------------
4 begin : January 2017
5 copyright : (C) 2017 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgsprocessingregistry.h"
19 #include "qgsprocessingprovider.h"
20 #include "qgsprocessingutils.h"
21 #include "qgsprocessingalgorithm.h"
22 #include "qgsprocessingcontext.h"
23 #include "qgsprocessingparametertype.h"
24 #include "qgsprocessingmodelalgorithm.h"
25 #include "qgsprocessingmodelgroupbox.h"
26 #include "qgsnativealgorithms.h"
27 #include <QObject>
28 #include <QtTest/QSignalSpy>
29 #include <QList>
30 #include <QFileInfo>
31 #include "qgis.h"
32 #include "qgstest.h"
33 #include "qgsrasterlayer.h"
34 #include "qgsmeshlayer.h"
35 #include "qgspluginlayer.h"
36 #include "qgsproject.h"
37 #include "qgspoint.h"
38 #include "qgsgeometry.h"
39 #include "qgsvectorfilewriter.h"
40 #include "qgsexpressioncontext.h"
41 #include "qgsxmlutils.h"
42 #include "qgsreferencedgeometry.h"
43 #include "qgssettings.h"
44 #include "qgsmessagelog.h"
45 #include "qgsvectorlayer.h"
46 #include "qgsexpressioncontextutils.h"
47 #include "qgsprintlayout.h"
48 #include "qgslayoutmanager.h"
49 #include "qgslayoutitemlabel.h"
50 #include "qgscoordinatetransformcontext.h"
51 #include "qgsrasterfilewriter.h"
52 #include "qgsprocessingparameterfieldmap.h"
53 #include "qgsprocessingparameteraggregate.h"
54 #include "qgsprocessingparametertininputlayers.h"
55 #include "qgsprocessingparameterdxflayers.h"
56 #include "qgsprocessingparametermeshdataset.h"
57 #include "qgsdxfexport.h"
58 #include "qgspointcloudlayer.h"
59 #include "qgsannotationlayer.h"
60 #include "qgsconfig.h"
61
62 class DummyAlgorithm : public QgsProcessingAlgorithm
63 {
64 public:
65
DummyAlgorithm(const QString & name)66 DummyAlgorithm( const QString &name ) : mName( name ) { mFlags = QgsProcessingAlgorithm::flags(); }
67
initAlgorithm(const QVariantMap &=QVariantMap ())68 void initAlgorithm( const QVariantMap & = QVariantMap() ) override {}
name() const69 QString name() const override { return mName; }
displayName() const70 QString displayName() const override { return mName; }
processAlgorithm(const QVariantMap &,QgsProcessingContext &,QgsProcessingFeedback *)71 QVariantMap processAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * ) override { return QVariantMap(); }
72
flags() const73 Flags flags() const override { return mFlags; }
createInstance() const74 DummyAlgorithm *createInstance() const override { return new DummyAlgorithm( name() ); }
75
76 QString mName;
77
78 Flags mFlags;
79
checkParameterVals()80 void checkParameterVals()
81 {
82 addParameter( new QgsProcessingParameterString( "p1" ) );
83 QVariantMap params;
84 QgsProcessingContext context;
85
86 QVERIFY( !checkParameterValues( params, context ) );
87 params.insert( "p1", "a" );
88 QVERIFY( checkParameterValues( params, context ) );
89 // optional param
90 addParameter( new QgsProcessingParameterString( "p2", QString(), QVariant(), false, true ) );
91 QVERIFY( checkParameterValues( params, context ) );
92 params.insert( "p2", "a" );
93 QVERIFY( checkParameterValues( params, context ) );
94 }
95
runParameterChecks()96 void runParameterChecks()
97 {
98 QVERIFY( parameterDefinitions().isEmpty() );
99 QVERIFY( addParameter( new QgsProcessingParameterBoolean( "p1" ) ) );
100 QCOMPARE( parameterDefinitions().count(), 1 );
101 QCOMPARE( parameterDefinitions().at( 0 )->name(), QString( "p1" ) );
102 QCOMPARE( parameterDefinitions().at( 0 )->algorithm(), this );
103
104 QVERIFY( !addParameter( nullptr ) );
105 QCOMPARE( parameterDefinitions().count(), 1 );
106 // duplicate name!
107 QgsProcessingParameterBoolean *p2 = new QgsProcessingParameterBoolean( "p1" );
108 QVERIFY( !addParameter( p2 ) );
109 QCOMPARE( parameterDefinitions().count(), 1 );
110
111 QCOMPARE( parameterDefinition( "p1" ), parameterDefinitions().at( 0 ) );
112 // parameterDefinition should be case insensitive
113 QCOMPARE( parameterDefinition( "P1" ), parameterDefinitions().at( 0 ) );
114 QVERIFY( !parameterDefinition( "invalid" ) );
115
116 QCOMPARE( countVisibleParameters(), 1 );
117 QgsProcessingParameterBoolean *p3 = new QgsProcessingParameterBoolean( "p3" );
118 QVERIFY( addParameter( p3 ) );
119 QCOMPARE( countVisibleParameters(), 2 );
120 QgsProcessingParameterBoolean *p4 = new QgsProcessingParameterBoolean( "p4" );
121 p4->setFlags( QgsProcessingParameterDefinition::FlagHidden );
122 QVERIFY( addParameter( p4 ) );
123 QCOMPARE( countVisibleParameters(), 2 );
124
125
126 //destination styleparameters
127 QVERIFY( destinationParameterDefinitions().isEmpty() );
128 QgsProcessingParameterFeatureSink *p5 = new QgsProcessingParameterFeatureSink( "p5" );
129 QVERIFY( addParameter( p5, false ) );
130 QCOMPARE( destinationParameterDefinitions(), QgsProcessingParameterDefinitions() << p5 );
131 QgsProcessingParameterFeatureSink *p6 = new QgsProcessingParameterFeatureSink( "p6" );
132 QVERIFY( addParameter( p6, false ) );
133 QCOMPARE( destinationParameterDefinitions(), QgsProcessingParameterDefinitions() << p5 << p6 );
134
135 // remove parameter
136 removeParameter( "non existent" );
137 removeParameter( "p6" );
138 QCOMPARE( destinationParameterDefinitions(), QgsProcessingParameterDefinitions() << p5 );
139 removeParameter( "p5" );
140 QVERIFY( destinationParameterDefinitions().isEmpty() );
141
142 // try with auto output creation
143 QgsProcessingParameterVectorDestination *p7 = new QgsProcessingParameterVectorDestination( "p7", "my output" );
144 QVERIFY( addParameter( p7 ) );
145 QCOMPARE( destinationParameterDefinitions(), QgsProcessingParameterDefinitions() << p7 );
146 QVERIFY( outputDefinition( "p7" ) );
147 QCOMPARE( outputDefinition( "p7" )->name(), QStringLiteral( "p7" ) );
148 QCOMPARE( outputDefinition( "p7" )->type(), QStringLiteral( "outputVector" ) );
149 QCOMPARE( outputDefinition( "p7" )->description(), QStringLiteral( "my output" ) );
150
151 // duplicate output name
152 QVERIFY( addOutput( new QgsProcessingOutputVectorLayer( "p8" ) ) );
153 QgsProcessingParameterVectorDestination *p8 = new QgsProcessingParameterVectorDestination( "p8" );
154 // this should fail - it would result in a duplicate output name
155 QVERIFY( !addParameter( p8 ) );
156
157 // default vector format extension
158 QgsProcessingParameterFeatureSink *sinkParam = new QgsProcessingParameterFeatureSink( "sink" );
159 QCOMPARE( sinkParam->defaultFileExtension(), QStringLiteral( "gpkg" ) ); // before alg is accessible
160 QVERIFY( !sinkParam->algorithm() );
161 QVERIFY( !sinkParam->provider() );
162 QVERIFY( addParameter( sinkParam ) );
163 QCOMPARE( sinkParam->defaultFileExtension(), QStringLiteral( "gpkg" ) );
164 QCOMPARE( sinkParam->algorithm(), this );
165 QVERIFY( !sinkParam->provider() );
166
167 // default raster format extension
168 QgsProcessingParameterRasterDestination *rasterParam = new QgsProcessingParameterRasterDestination( "raster" );
169 QCOMPARE( rasterParam->defaultFileExtension(), QStringLiteral( "tif" ) ); // before alg is accessible
170 QVERIFY( addParameter( rasterParam ) );
171 QCOMPARE( rasterParam->defaultFileExtension(), QStringLiteral( "tif" ) );
172
173 // should allow parameters with same name but different case (required for grass provider)
174 QgsProcessingParameterBoolean *p1C = new QgsProcessingParameterBoolean( "P1" );
175 QVERIFY( addParameter( p1C ) );
176 QCOMPARE( parameterDefinitions().count(), 8 );
177
178 // remove parameter and auto created output
179 QgsProcessingParameterVectorDestination *p9 = new QgsProcessingParameterVectorDestination( "p9", "output" );
180 QVERIFY( addParameter( p9 ) );
181 QVERIFY( outputDefinition( "p9" ) );
182 QCOMPARE( outputDefinition( "p9" )->name(), QStringLiteral( "p9" ) );
183 QCOMPARE( outputDefinition( "p9" )->type(), QStringLiteral( "outputVector" ) );
184 removeParameter( "p9" );
185 QVERIFY( !outputDefinition( "p9" ) );
186
187 // remove parameter and check manually added output isn't removed
188 QVERIFY( addParameter( new QgsProcessingParameterVectorDestination( "p10", "output" ), false ) );
189 QVERIFY( addOutput( new QgsProcessingOutputVectorLayer( "p10" ) ) );
190 QCOMPARE( outputDefinition( "p10" )->name(), QStringLiteral( "p10" ) );
191 QCOMPARE( outputDefinition( "p10" )->type(), QStringLiteral( "outputVector" ) );
192 removeParameter( "p10" );
193 QVERIFY( outputDefinition( "p10" ) );
194
195 // parameterDefinition should be case insensitive, but prioritize correct case matches
196 QCOMPARE( parameterDefinition( "p1" ), parameterDefinitions().at( 0 ) );
197 QCOMPARE( parameterDefinition( "P1" ), parameterDefinitions().at( 7 ) );
198 }
199
runParameterChecks2()200 void runParameterChecks2()
201 {
202 // default vector format extension, taken from provider
203 QgsProcessingParameterFeatureSink *sinkParam = new QgsProcessingParameterFeatureSink( "sink2" );
204 QCOMPARE( sinkParam->defaultFileExtension(), QStringLiteral( "gpkg" ) ); // before alg is accessible
205 QVERIFY( !sinkParam->algorithm() );
206 QVERIFY( !sinkParam->provider() );
207 QVERIFY( addParameter( sinkParam ) );
208 QCOMPARE( sinkParam->defaultFileExtension(), QStringLiteral( "xshp" ) );
209 QCOMPARE( sinkParam->algorithm(), this );
210 QCOMPARE( sinkParam->provider(), provider() );
211
212 // default raster format extension
213 QgsProcessingParameterRasterDestination *rasterParam = new QgsProcessingParameterRasterDestination( "raster2" );
214 QCOMPARE( rasterParam->defaultFileExtension(), QStringLiteral( "tif" ) ); // before alg is accessible
215 QVERIFY( addParameter( rasterParam ) );
216 QCOMPARE( rasterParam->defaultFileExtension(), QStringLiteral( "pcx" ) );
217 }
218
runOutputChecks()219 void runOutputChecks()
220 {
221 QVERIFY( outputDefinitions().isEmpty() );
222 QVERIFY( addOutput( new QgsProcessingOutputVectorLayer( "p1" ) ) );
223 QCOMPARE( outputDefinitions().count(), 1 );
224 QCOMPARE( outputDefinitions().at( 0 )->name(), QString( "p1" ) );
225
226 // make sure manually added outputs are not deleted by calling removeParameter
227 removeParameter( "p1" );
228 QCOMPARE( outputDefinitions().count(), 1 );
229
230 QVERIFY( !addOutput( nullptr ) );
231 QCOMPARE( outputDefinitions().count(), 1 );
232 // duplicate name!
233 QgsProcessingOutputVectorLayer *p2 = new QgsProcessingOutputVectorLayer( "p1" );
234 QVERIFY( !addOutput( p2 ) );
235 QCOMPARE( outputDefinitions().count(), 1 );
236
237 QCOMPARE( outputDefinition( "p1" ), outputDefinitions().at( 0 ) );
238 // parameterDefinition should be case insensitive
239 QCOMPARE( outputDefinition( "P1" ), outputDefinitions().at( 0 ) );
240 QVERIFY( !outputDefinition( "invalid" ) );
241
242 QVERIFY( !hasHtmlOutputs() );
243 QgsProcessingOutputHtml *p3 = new QgsProcessingOutputHtml( "p3" );
244 QVERIFY( addOutput( p3 ) );
245 QVERIFY( hasHtmlOutputs() );
246 }
247
runValidateInputCrsChecks()248 void runValidateInputCrsChecks()
249 {
250 addParameter( new QgsProcessingParameterMapLayer( "p1" ) );
251 addParameter( new QgsProcessingParameterMapLayer( "p2" ) );
252 QVariantMap parameters;
253
254 QgsVectorLayer *layer3111 = new QgsVectorLayer( "Point?crs=epsg:3111", "v1", "memory" );
255 QgsProject p;
256 p.addMapLayer( layer3111 );
257
258 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
259 const QString raster1 = testDataDir + "landsat_4326.tif";
260 const QFileInfo fi1( raster1 );
261 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
262 QVERIFY( r1->isValid() );
263 p.addMapLayer( r1 );
264
265 QgsVectorLayer *layer4326 = new QgsVectorLayer( "Point?crs=epsg:4326", "v1", "memory" );
266 p.addMapLayer( layer4326 );
267
268 QgsProcessingContext context;
269 context.setProject( &p );
270
271 // flag not set
272 mFlags = QgsProcessingAlgorithm::Flags();
273 parameters.insert( "p1", QVariant::fromValue( layer3111 ) );
274 QVERIFY( validateInputCrs( parameters, context ) );
275 mFlags = FlagRequiresMatchingCrs;
276 QVERIFY( validateInputCrs( parameters, context ) );
277
278 // two layers, different crs
279 parameters.insert( "p2", QVariant::fromValue( layer4326 ) );
280 // flag not set
281 mFlags = QgsProcessingAlgorithm::Flags();
282 QVERIFY( validateInputCrs( parameters, context ) );
283 mFlags = FlagRequiresMatchingCrs;
284 QVERIFY( !validateInputCrs( parameters, context ) );
285
286 // raster layer
287 parameters.remove( "p2" );
288 addParameter( new QgsProcessingParameterRasterLayer( "p3" ) );
289 parameters.insert( "p3", QVariant::fromValue( r1 ) );
290 QVERIFY( !validateInputCrs( parameters, context ) );
291
292 // feature source
293 parameters.remove( "p3" );
294 addParameter( new QgsProcessingParameterFeatureSource( "p4" ) );
295 parameters.insert( "p4", layer4326->id() );
296 QVERIFY( !validateInputCrs( parameters, context ) );
297
298 parameters.remove( "p4" );
299 addParameter( new QgsProcessingParameterMultipleLayers( "p5" ) );
300 parameters.insert( "p5", QVariantList() << layer4326->id() << r1->id() );
301 QVERIFY( !validateInputCrs( parameters, context ) );
302
303 // extent
304 parameters.clear();
305 parameters.insert( "p1", QVariant::fromValue( layer3111 ) );
306 addParameter( new QgsProcessingParameterExtent( "extent" ) );
307 parameters.insert( "extent", QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
308 QVERIFY( !validateInputCrs( parameters, context ) );
309 parameters.insert( "extent", QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ) );
310 QVERIFY( validateInputCrs( parameters, context ) );
311
312 // point
313 parameters.clear();
314 parameters.insert( "p1", QVariant::fromValue( layer3111 ) );
315 addParameter( new QgsProcessingParameterPoint( "point" ) );
316 parameters.insert( "point", QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:4326" ) ) ) );
317 QVERIFY( !validateInputCrs( parameters, context ) );
318 parameters.insert( "point", QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ) );
319 QVERIFY( validateInputCrs( parameters, context ) );
320 }
321
runAsPythonCommandChecks()322 void runAsPythonCommandChecks()
323 {
324 addParameter( new QgsProcessingParameterString( "p1" ) );
325 addParameter( new QgsProcessingParameterString( "p2" ) );
326 QgsProcessingParameterString *hidden = new QgsProcessingParameterString( "p3" );
327 hidden->setFlags( QgsProcessingParameterDefinition::FlagHidden );
328 addParameter( hidden );
329
330 QVariantMap params;
331 QgsProcessingContext context;
332
333 QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {})" ) );
334 params.insert( "p1", "a" );
335 QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a'})" ) );
336 params.insert( "p2", QVariant() );
337 QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':None})" ) );
338 params.insert( "p2", "b" );
339 QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) );
340
341 // hidden, shouldn't be shown
342 params.insert( "p3", "b" );
343 QCOMPARE( asPythonCommand( params, context ), QStringLiteral( "processing.run(\"test\", {'p1':'a','p2':'b'})" ) );
344 }
345
addDestParams()346 void addDestParams()
347 {
348 QgsProcessingParameterFeatureSink *sinkParam1 = new QgsProcessingParameterFeatureSink( "supports" );
349 sinkParam1->setSupportsNonFileBasedOutput( true );
350 addParameter( sinkParam1 );
351 QgsProcessingParameterFeatureSink *sinkParam2 = new QgsProcessingParameterFeatureSink( "non_supports" );
352 sinkParam2->setSupportsNonFileBasedOutput( false );
353 addParameter( sinkParam2 );
354 }
355
356 };
357
358 //dummy provider for testing
359 class DummyProvider : public QgsProcessingProvider // clazy:exclude=missing-qobject-macro
360 {
361 public:
362
DummyProvider(const QString & id)363 DummyProvider( const QString &id ) : mId( id ) {}
364
id() const365 QString id() const override { return mId; }
366
name() const367 QString name() const override { return "dummy"; }
368
unload()369 void unload() override { if ( unloaded ) { *unloaded = true; } }
370
defaultVectorFileExtension(bool) const371 QString defaultVectorFileExtension( bool ) const override
372 {
373 return "xshp"; // shape-X. Just like shapefiles, but to the max!
374 }
375
defaultRasterFileExtension() const376 QString defaultRasterFileExtension() const override
377 {
378 return "pcx"; // next-gen raster storage
379 }
380
supportsNonFileBasedOutput() const381 bool supportsNonFileBasedOutput() const override
382 {
383 return supportsNonFileOutputs;
384 }
385
isActive() const386 bool isActive() const override
387 {
388 return active;
389 }
390
391 bool active = true;
392 bool *unloaded = nullptr;
393 bool supportsNonFileOutputs = false;
394
395 protected:
396
loadAlgorithms()397 void loadAlgorithms() override
398 {
399 QVERIFY( addAlgorithm( new DummyAlgorithm( "alg1" ) ) );
400 QVERIFY( addAlgorithm( new DummyAlgorithm( "alg2" ) ) );
401
402 //dupe name
403 QgsProcessingAlgorithm *a = new DummyAlgorithm( "alg1" );
404 QVERIFY( !addAlgorithm( a ) );
405 delete a;
406
407 QVERIFY( !addAlgorithm( nullptr ) );
408 }
409
410 QString mId;
411
412 friend class TestQgsProcessing;
413 };
414
415 class DummyProviderNoLoad : public DummyProvider // clazy:exclude=missing-qobject-macro
416 {
417 public:
418
DummyProviderNoLoad(const QString & id)419 DummyProviderNoLoad( const QString &id ) : DummyProvider( id ) {}
420
load()421 bool load() override
422 {
423 return false;
424 }
425
426 };
427
428 class DummyAlgorithm2 : public QgsProcessingAlgorithm
429 {
430 public:
431
DummyAlgorithm2(const QString & name)432 DummyAlgorithm2( const QString &name ) : mName( name ) { mFlags = QgsProcessingAlgorithm::flags(); }
433
initAlgorithm(const QVariantMap &=QVariantMap ())434 void initAlgorithm( const QVariantMap & = QVariantMap() ) override
435 {
436 addParameter( new QgsProcessingParameterVectorDestination( QStringLiteral( "vector_dest" ) ) );
437 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "raster_dest" ) ) );
438 addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "sink" ) ) );
439 }
name() const440 QString name() const override { return mName; }
displayName() const441 QString displayName() const override { return mName; }
processAlgorithm(const QVariantMap &,QgsProcessingContext &,QgsProcessingFeedback *)442 QVariantMap processAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * ) override { return QVariantMap(); }
443
flags() const444 Flags flags() const override { return mFlags; }
createInstance() const445 DummyAlgorithm2 *createInstance() const override { return new DummyAlgorithm2( name() ); }
446
447 QString mName;
448
449 Flags mFlags;
450
451 };
452
453 class DummyProvider3 : public QgsProcessingProvider // clazy:exclude=missing-qobject-macro
454 {
455 public:
456
457 DummyProvider3() = default;
id() const458 QString id() const override { return QStringLiteral( "dummy3" ); }
name() const459 QString name() const override { return QStringLiteral( "dummy3" ); }
460
supportedOutputVectorLayerExtensions() const461 QStringList supportedOutputVectorLayerExtensions() const override
462 {
463 return QStringList() << QStringLiteral( "mif" ) << QStringLiteral( "tab" );
464 }
465
supportedOutputTableExtensions() const466 QStringList supportedOutputTableExtensions() const override
467 {
468 return QStringList() << QStringLiteral( "dbf" );
469 }
470
supportedOutputRasterLayerExtensions() const471 QStringList supportedOutputRasterLayerExtensions() const override
472 {
473 return QStringList() << QStringLiteral( "mig" ) << QStringLiteral( "sdat" );
474 }
475
loadAlgorithms()476 void loadAlgorithms() override
477 {
478 QVERIFY( addAlgorithm( new DummyAlgorithm2( "alg1" ) ) );
479 }
480
481 };
482
483 class DummyProvider4 : public QgsProcessingProvider // clazy:exclude=missing-qobject-macro
484 {
485 public:
486
487 DummyProvider4() = default;
id() const488 QString id() const override { return QStringLiteral( "dummy4" ); }
name() const489 QString name() const override { return QStringLiteral( "dummy4" ); }
490
supportsNonFileBasedOutput() const491 bool supportsNonFileBasedOutput() const override
492 {
493 return false;
494 }
495
supportedOutputVectorLayerExtensions() const496 QStringList supportedOutputVectorLayerExtensions() const override
497 {
498 return QStringList() << QStringLiteral( "mif" );
499 }
500
supportedOutputRasterLayerExtensions() const501 QStringList supportedOutputRasterLayerExtensions() const override
502 {
503 return QStringList() << QStringLiteral( "mig" );
504 }
505
loadAlgorithms()506 void loadAlgorithms() override
507 {
508 QVERIFY( addAlgorithm( new DummyAlgorithm2( "alg1" ) ) );
509 }
510
511 };
512
513 class DummyParameterType : public QgsProcessingParameterType
514 {
515
516
517 // QgsProcessingParameterType interface
518 public:
create(const QString & name) const519 QgsProcessingParameterDefinition *create( const QString &name ) const override
520 {
521 return new QgsProcessingParameterString( name );
522 }
523
description() const524 QString description() const override
525 {
526 return QStringLiteral( "Description" );
527 }
528
name() const529 QString name() const override
530 {
531 return QStringLiteral( "ParamType" );
532 }
533
id() const534 QString id() const override
535 {
536 return QStringLiteral( "paramType" );
537 }
538 };
539
540 class DummyPluginLayer: public QgsPluginLayer
541 {
542 public:
543
DummyPluginLayer(const QString & layerType,const QString & layerName)544 DummyPluginLayer( const QString &layerType, const QString &layerName ): QgsPluginLayer( layerType, layerName )
545 {
546 mValid = true;
547 };
548
clone() const549 DummyPluginLayer *clone() const override { return new DummyPluginLayer( "dummylayer", "test" ); };
550
createMapRenderer(QgsRenderContext & rendererContext)551 QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) override
552 {
553 Q_UNUSED( rendererContext );
554 return nullptr;
555 };
556
writeXml(QDomNode & layerNode,QDomDocument & doc,const QgsReadWriteContext & context) const557 bool writeXml( QDomNode &layerNode, QDomDocument &doc, const QgsReadWriteContext &context ) const override
558 {
559 Q_UNUSED( layerNode );
560 Q_UNUSED( doc );
561 Q_UNUSED( context );
562 return true;
563 };
readSymbology(const QDomNode & node,QString & errorMessage,QgsReadWriteContext & context,StyleCategories categories=AllStyleCategories)564 bool readSymbology( const QDomNode &node, QString &errorMessage,
565 QgsReadWriteContext &context, StyleCategories categories = AllStyleCategories ) override
566 {
567 Q_UNUSED( node );
568 Q_UNUSED( errorMessage );
569 Q_UNUSED( context );
570 Q_UNUSED( categories );
571 return true;
572 };
writeSymbology(QDomNode & node,QDomDocument & doc,QString & errorMessage,const QgsReadWriteContext & context,StyleCategories categories=AllStyleCategories) const573 bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context,
574 StyleCategories categories = AllStyleCategories ) const override
575 {
576 Q_UNUSED( node );
577 Q_UNUSED( doc );
578 Q_UNUSED( errorMessage );
579 Q_UNUSED( context );
580 Q_UNUSED( categories );
581 return true;
582 };
583
setTransformContext(const QgsCoordinateTransformContext & transformContext)584 void setTransformContext( const QgsCoordinateTransformContext &transformContext ) override { Q_UNUSED( transformContext ); };
585 };
586
587 class TestQgsProcessing: public QObject
588 {
589 Q_OBJECT
590
591 private slots:
592 void initTestCase();// will be called before the first testfunction is executed.
593 void cleanupTestCase(); // will be called after the last testfunction was executed.
init()594 void init() {} // will be called before each testfunction is executed.
cleanup()595 void cleanup() {} // will be called after every testfunction.
596 void instance();
597 void addProvider();
598 void providerById();
599 void removeProvider();
600 void compatibleLayers();
601 void encodeDecodeUriProvider();
602 void normalizeLayerSource();
603 void context();
604 void feedback();
605 void mapLayers();
606 void mapLayerFromStore();
607 void mapLayerFromString();
608 void algorithm();
609 void features();
610 void uniqueValues();
611 void createIndex();
612 void generateTemporaryDestination();
613 void parseDestinationString();
614 void createFeatureSink();
615 #ifdef ENABLE_PGTEST
616 void createFeatureSinkPostgres();
617 #endif
618 void source();
619 void parameters();
620 void algorithmParameters();
621 void algorithmOutputs();
622 void parameterGeneral();
623 void parameterBoolean();
624 void parameterCrs();
625 void parameterMapLayer();
626 void parameterExtent();
627 void parameterPoint();
628 void parameterGeometry();
629 void parameterFile();
630 void parameterMatrix();
631 void parameterLayerList();
632 void parameterNumber();
633 void parameterDistance();
634 void parameterDuration();
635 void parameterScale();
636 void parameterRange();
637 void parameterRasterLayer();
638 void parameterEnum();
639 void parameterString();
640 void parameterAuthConfig();
641 void parameterExpression();
642 void parameterField();
643 void parameterVectorLayer();
644 void parameterMeshLayer();
645 void parameterFeatureSource();
646 void parameterFeatureSink();
647 void parameterVectorOut();
648 void parameterRasterOut();
649 void parameterFileOut();
650 void parameterFolderOut();
651 void parameterBand();
652 void parameterLayout();
653 void parameterLayoutItem();
654 void parameterColor();
655 void parameterCoordinateOperation();
656 void parameterMapTheme();
657 void parameterDateTime();
658 void parameterProviderConnection();
659 void parameterDatabaseSchema();
660 void parameterDatabaseTable();
661 void parameterFieldMapping();
662 void parameterAggregate();
663 void parameterTinInputLayers();
664 void parameterMeshDatasetGroups();
665 void parameterMeshDatasetTime();
666 void parameterDxfLayers();
667 #ifdef HAVE_EPT
668 void parameterPointCloudLayer();
669 #endif
670 void parameterAnnotationLayer();
671 void checkParamValues();
672 void combineLayerExtent();
673 void processingFeatureSource();
674 void processingFeatureSink();
675 void algorithmScope();
676 void modelScope();
677 void validateInputCrs();
678 void generateIteratingDestination();
679 void asPythonCommand();
680 void modelerAlgorithm();
681 void modelExecution();
682 void modelBranchPruning();
683 void modelBranchPruningConditional();
684 void modelWithProviderWithLimitedTypes();
685 void modelVectorOutputIsCompatibleType();
686 void modelAcceptableValues();
687 void modelValidate();
688 void modelInputs();
689 void modelDependencies();
690 void tempUtils();
691 void convertCompatible();
692 void create();
693 void combineFields();
694 void fieldNamesToIndices();
695 void indicesToFields();
696 void variantToPythonLiteral();
697 void stringToPythonLiteral();
698 void defaultExtensionsForProvider();
699 void supportedExtensions();
700 void supportsNonFileBasedOutput();
701 void addParameterType();
702 void removeParameterType();
703 void parameterTypes();
704 void parameterType();
705 void sourceTypeToString_data();
706 void sourceTypeToString();
707 void modelSource();
708
709 private:
710
711 };
712
initTestCase()713 void TestQgsProcessing::initTestCase()
714 {
715 QgsApplication::init();
716 QgsApplication::initQgis();
717
718 // Set up the QgsSettings environment
719 QCoreApplication::setOrganizationName( QStringLiteral( "QGIS" ) );
720 QCoreApplication::setOrganizationDomain( QStringLiteral( "qgis.org" ) );
721 QCoreApplication::setApplicationName( QStringLiteral( "QGIS-TEST" ) );
722
723 QgsSettings settings;
724 settings.clear();
725
726 QgsApplication::processingRegistry()->addProvider( new QgsNativeAlgorithms( QgsApplication::processingRegistry() ) );
727 }
728
cleanupTestCase()729 void TestQgsProcessing::cleanupTestCase()
730 {
731 QFile::remove( QDir::tempPath() + "/create_feature_sink.tab" );
732 QgsVectorFileWriter::deleteShapeFile( QDir::tempPath() + "/create_feature_sink2.gpkg" );
733
734 QgsApplication::exitQgis();
735 }
736
instance()737 void TestQgsProcessing::instance()
738 {
739 // test that application has a registry instance
740 QVERIFY( QgsApplication::processingRegistry() );
741 }
742
addProvider()743 void TestQgsProcessing::addProvider()
744 {
745 QgsProcessingRegistry r;
746 QSignalSpy spyProviderAdded( &r, &QgsProcessingRegistry::providerAdded );
747
748 QVERIFY( r.providers().isEmpty() );
749
750 QVERIFY( !r.addProvider( nullptr ) );
751
752 // add a provider
753 DummyProvider *p = new DummyProvider( "p1" );
754 QVERIFY( r.addProvider( p ) );
755 QCOMPARE( r.providers(), QList< QgsProcessingProvider * >() << p );
756 QCOMPARE( spyProviderAdded.count(), 1 );
757 QCOMPARE( spyProviderAdded.last().at( 0 ).toString(), QString( "p1" ) );
758
759 //try adding another provider
760 DummyProvider *p2 = new DummyProvider( "p2" );
761 QVERIFY( r.addProvider( p2 ) );
762 QCOMPARE( qgis::listToSet( r.providers() ), QSet< QgsProcessingProvider * >() << p << p2 );
763 QCOMPARE( spyProviderAdded.count(), 2 );
764 QCOMPARE( spyProviderAdded.last().at( 0 ).toString(), QString( "p2" ) );
765
766 //try adding a provider with duplicate id
767 DummyProvider *p3 = new DummyProvider( "p2" );
768 QVERIFY( !r.addProvider( p3 ) );
769 QCOMPARE( qgis::listToSet( r.providers() ), QSet< QgsProcessingProvider * >() << p << p2 );
770 QCOMPARE( spyProviderAdded.count(), 2 );
771
772 // test that adding a provider which does not load means it is not added to registry
773 DummyProviderNoLoad *p4 = new DummyProviderNoLoad( "p4" );
774 QVERIFY( !r.addProvider( p4 ) );
775 QCOMPARE( qgis::listToSet( r.providers() ), QSet< QgsProcessingProvider * >() << p << p2 );
776 QCOMPARE( spyProviderAdded.count(), 2 );
777 }
778
providerById()779 void TestQgsProcessing::providerById()
780 {
781 QgsProcessingRegistry r;
782
783 // no providers
784 QVERIFY( !r.providerById( "p1" ) );
785
786 // add a provider
787 DummyProvider *p = new DummyProvider( "p1" );
788 QVERIFY( r.addProvider( p ) );
789 QCOMPARE( r.providerById( "p1" ), p );
790 QVERIFY( !r.providerById( "p2" ) );
791
792 //try adding another provider
793 DummyProvider *p2 = new DummyProvider( "p2" );
794 QVERIFY( r.addProvider( p2 ) );
795 QCOMPARE( r.providerById( "p1" ), p );
796 QCOMPARE( r.providerById( "p2" ), p2 );
797 QVERIFY( !r.providerById( "p3" ) );
798 }
799
removeProvider()800 void TestQgsProcessing::removeProvider()
801 {
802 QgsProcessingRegistry r;
803 QSignalSpy spyProviderRemoved( &r, &QgsProcessingRegistry::providerRemoved );
804
805 QVERIFY( !r.removeProvider( nullptr ) );
806 QVERIFY( !r.removeProvider( "p1" ) );
807 // provider not in registry
808 DummyProvider *p = new DummyProvider( "p1" );
809 QVERIFY( !r.removeProvider( p ) );
810 QCOMPARE( spyProviderRemoved.count(), 0 );
811
812 // add some providers
813 QVERIFY( r.addProvider( p ) );
814 DummyProvider *p2 = new DummyProvider( "p2" );
815 QVERIFY( r.addProvider( p2 ) );
816
817 // remove one by pointer
818 bool unloaded = false;
819 p->unloaded = &unloaded;
820 QVERIFY( r.removeProvider( p ) );
821 QCOMPARE( spyProviderRemoved.count(), 1 );
822 QCOMPARE( spyProviderRemoved.last().at( 0 ).toString(), QString( "p1" ) );
823 QCOMPARE( r.providers(), QList< QgsProcessingProvider * >() << p2 );
824
825 //test that provider was unloaded
826 QVERIFY( unloaded );
827
828 // should fail, already removed
829 QVERIFY( !r.removeProvider( "p1" ) );
830
831 // remove one by id
832 QVERIFY( r.removeProvider( "p2" ) );
833 QCOMPARE( spyProviderRemoved.count(), 2 );
834 QCOMPARE( spyProviderRemoved.last().at( 0 ).toString(), QString( "p2" ) );
835 QVERIFY( r.providers().isEmpty() );
836 }
837
compatibleLayers()838 void TestQgsProcessing::compatibleLayers()
839 {
840 QgsProject p;
841
842 // add a bunch of layers to a project
843 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
844 const QString raster1 = testDataDir + "tenbytenraster.asc";
845 const QString raster2 = testDataDir + "landsat.tif";
846 const QString raster3 = testDataDir + "/raster/band1_float32_noct_epsg4326.tif";
847 const QFileInfo fi1( raster1 );
848 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
849 QVERIFY( r1->isValid() );
850 const QFileInfo fi2( raster2 );
851 QgsRasterLayer *r2 = new QgsRasterLayer( fi2.filePath(), "ar2" );
852 QVERIFY( r2->isValid() );
853 const QFileInfo fi3( raster3 );
854 QgsRasterLayer *r3 = new QgsRasterLayer( fi3.filePath(), "zz" );
855 QVERIFY( r3->isValid() );
856
857 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V4", "memory" );
858 QgsVectorLayer *v2 = new QgsVectorLayer( "Point", "v1", "memory" );
859 QgsVectorLayer *v3 = new QgsVectorLayer( "LineString", "v3", "memory" );
860 QgsVectorLayer *v4 = new QgsVectorLayer( "none", "vvvv4", "memory" );
861
862 const QFileInfo fm( testDataDir + "/mesh/quad_and_triangle.2dm" );
863 QgsMeshLayer *m1 = new QgsMeshLayer( fm.filePath(), "MX", "mdal" );
864 QVERIFY( m1->isValid() );
865 QgsMeshLayer *m2 = new QgsMeshLayer( fm.filePath(), "mA", "mdal" );
866 QVERIFY( m2->isValid() );
867
868 #ifdef HAVE_EPT
869 QFileInfo fpc( testDataDir + "/point_clouds/ept/sunshine-coast/ept.json" );
870 QgsPointCloudLayer *pc1 = new QgsPointCloudLayer( fpc.filePath(), "PCX", "ept" );
871 QgsPointCloudLayer *pc2 = new QgsPointCloudLayer( fpc.filePath(), "pcA", "ept" );
872 #endif
873
874 DummyPluginLayer *pl1 = new DummyPluginLayer( "dummylayer", "PX" );
875 DummyPluginLayer *pl2 = new DummyPluginLayer( "dummylayer", "pA" );
876
877 QgsAnnotationLayer *al = new QgsAnnotationLayer( "secondary annotation layer", QgsAnnotationLayer::LayerOptions( p.transformContext() ) );
878
879 #ifdef HAVE_EPT
880 p.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << r3 << v1 << v2 << v3 << v4 << m1 << m2 << pl1 << pl2 << pc1 << pc2 << al );
881 #else
882 p.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << r3 << v1 << v2 << v3 << v4 << m1 << m2 << pl1 << pl2 << al );
883 #endif
884
885 // compatibleRasterLayers
886 QVERIFY( QgsProcessingUtils::compatibleRasterLayers( nullptr ).isEmpty() );
887
888 // sorted
889 QStringList lIds;
890 const QList<QgsRasterLayer *> layers = QgsProcessingUtils::compatibleRasterLayers( &p );
891 for ( QgsRasterLayer *rl : layers )
892 lIds << rl->name();
893 QCOMPARE( lIds, QStringList() << "ar2" << "R1" << "zz" );
894
895 // unsorted
896 lIds.clear();
897 for ( QgsRasterLayer *rl : QgsProcessingUtils::compatibleRasterLayers( &p, false ) )
898 lIds << rl->name();
899 QCOMPARE( lIds, QStringList() << "R1" << "ar2" << "zz" );
900
901 // compatibleMeshLayers
902 QVERIFY( QgsProcessingUtils::compatibleMeshLayers( nullptr ).isEmpty() );
903
904 // sorted
905 lIds.clear();
906 for ( QgsMeshLayer *rl : QgsProcessingUtils::compatibleMeshLayers( &p ) )
907 lIds << rl->name();
908 QCOMPARE( lIds, QStringList() << "mA" << "MX" );
909
910 // unsorted
911 lIds.clear();
912 for ( QgsMeshLayer *rl : QgsProcessingUtils::compatibleMeshLayers( &p, false ) )
913 lIds << rl->name();
914 QCOMPARE( lIds, QStringList() << "MX" << "mA" );
915
916 // compatibleVectorLayers
917 QVERIFY( QgsProcessingUtils::compatibleVectorLayers( nullptr ).isEmpty() );
918
919 // sorted
920 lIds.clear();
921 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p ) )
922 lIds << vl->name();
923 QCOMPARE( lIds, QStringList() << "v1" << "v3" << "V4" << "vvvv4" );
924
925 // unsorted
926 lIds.clear();
927 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>(), false ) )
928 lIds << vl->name();
929 QCOMPARE( lIds, QStringList() << "V4" << "v1" << "v3" << "vvvv4" );
930
931 // compatiblePluginLayers
932 QVERIFY( QgsProcessingUtils::compatiblePluginLayers( nullptr ).isEmpty() );
933
934 // sorted
935 lIds.clear();
936 for ( QgsPluginLayer *pl : QgsProcessingUtils::compatiblePluginLayers( &p ) )
937 lIds << pl->name();
938 QCOMPARE( lIds, QStringList() << "pA" << "PX" );
939
940 // unsorted
941 lIds.clear();
942 for ( QgsPluginLayer *pl : QgsProcessingUtils::compatiblePluginLayers( &p, false ) )
943 lIds << pl->name();
944 QCOMPARE( lIds, QStringList() << "PX" << "pA" );
945
946 #ifdef HAVE_EPT
947 // compatiblePointCloudLayers
948 QVERIFY( QgsProcessingUtils::compatiblePointCloudLayers( nullptr ).isEmpty() );
949
950 // sorted
951 lIds.clear();
952 for ( QgsPointCloudLayer *pcl : QgsProcessingUtils::compatiblePointCloudLayers( &p ) )
953 lIds << pcl->name();
954 QCOMPARE( lIds, QStringList() << "pcA" << "PCX" );
955
956 // unsorted
957 lIds.clear();
958 for ( QgsPointCloudLayer *pcl : QgsProcessingUtils::compatiblePointCloudLayers( &p, false ) )
959 lIds << pcl->name();
960 QCOMPARE( lIds, QStringList() << "PCX" << "pcA" );
961 #endif
962
963 // compatibleAnnotationLayers
964 QVERIFY( QgsProcessingUtils::compatibleAnnotationLayers( nullptr ).isEmpty() );
965
966 // sorted
967 lIds.clear();
968 for ( QgsAnnotationLayer *pcl : QgsProcessingUtils::compatibleAnnotationLayers( &p ) )
969 lIds << pcl->name();
970 QCOMPARE( lIds, QStringList() << QObject::tr( "Annotations" ) << "secondary annotation layer" );
971
972 // unsorted
973 lIds.clear();
974 for ( QgsAnnotationLayer *pcl : QgsProcessingUtils::compatibleAnnotationLayers( &p, false ) )
975 lIds << pcl->name();
976 QCOMPARE( lIds, QStringList() << "secondary annotation layer" << QObject::tr( "Annotations" ) );
977
978 // point only
979 lIds.clear();
980 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVectorPoint ) )
981 lIds << vl->name();
982 QCOMPARE( lIds, QStringList() << "v1" );
983
984 // polygon only
985 lIds.clear();
986 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVectorPolygon ) )
987 lIds << vl->name();
988 QCOMPARE( lIds, QStringList() << "V4" );
989
990 // line only
991 lIds.clear();
992 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVectorLine ) )
993 lIds << vl->name();
994 QCOMPARE( lIds, QStringList() << "v3" );
995
996 // point and line only
997 lIds.clear();
998 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorLine ) )
999 lIds << vl->name();
1000 QCOMPARE( lIds, QStringList() << "v1" << "v3" );
1001
1002 // any vector w geometry
1003 lIds.clear();
1004 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVectorAnyGeometry ) )
1005 lIds << vl->name();
1006 QCOMPARE( lIds, QStringList() << "v1" << "v3" << "V4" );
1007
1008 // any vector
1009 lIds.clear();
1010 for ( QgsVectorLayer *vl : QgsProcessingUtils::compatibleVectorLayers( &p, QList<int>() << QgsProcessing::TypeVector ) )
1011 lIds << vl->name();
1012 QCOMPARE( lIds, QStringList() << "v1" << "v3" << "V4" << "vvvv4" );
1013
1014 // all layers
1015 QVERIFY( QgsProcessingUtils::compatibleLayers( nullptr ).isEmpty() );
1016
1017 // sorted
1018 lIds.clear();
1019 for ( QgsMapLayer *l : QgsProcessingUtils::compatibleLayers( &p ) )
1020 lIds << l->name();
1021 #ifdef HAVE_EPT
1022 QCOMPARE( lIds, QStringList() << QObject::tr( "Annotations" ) << "ar2" << "mA" << "MX" << "pA" << "pcA" << "PCX" << "PX" << "R1" << "secondary annotation layer" << "v1" << "v3" << "V4" << "vvvv4" << "zz" );
1023 #else
1024 QCOMPARE( lIds, QStringList() << QObject::tr( "Annotations" ) << "ar2" << "mA" << "MX" << "pA" << "PX" << "R1" << "secondary annotation layer" << "v1" << "v3" << "V4" << "vvvv4" << "zz" );
1025 #endif
1026
1027 // unsorted
1028 lIds.clear();
1029 for ( QgsMapLayer *l : QgsProcessingUtils::compatibleLayers( &p, false ) )
1030 lIds << l->name();
1031 #ifdef HAVE_EPT
1032 QCOMPARE( lIds, QStringList() << "R1" << "ar2" << "zz" << "V4" << "v1" << "v3" << "vvvv4" << "MX" << "mA" << "PCX" << "pcA" << "secondary annotation layer" << QObject::tr( "Annotations" ) << "PX" << "pA" );
1033 #else
1034 QCOMPARE( lIds, QStringList() << "R1" << "ar2" << "zz" << "V4" << "v1" << "v3" << "vvvv4" << "MX" << "mA" << "secondary annotation layer" << QObject::tr( "Annotations" ) << "PX" << "pA" );
1035 #endif
1036 }
1037
encodeDecodeUriProvider()1038 void TestQgsProcessing::encodeDecodeUriProvider()
1039 {
1040 QString provider;
1041 QString uri;
1042 QCOMPARE( QgsProcessingUtils::encodeProviderKeyAndUri( QStringLiteral( "ogr" ), QStringLiteral( "/home/me/test.shp" ) ), QStringLiteral( "ogr:///home/me/test.shp" ) );
1043 QVERIFY( QgsProcessingUtils::decodeProviderKeyAndUri( QStringLiteral( "ogr:///home/me/test.shp" ), provider, uri ) );
1044 QCOMPARE( provider, QStringLiteral( "ogr" ) );
1045 QCOMPARE( uri, QStringLiteral( "/home/me/test.shp" ) );
1046 QCOMPARE( QgsProcessingUtils::encodeProviderKeyAndUri( QStringLiteral( "ogr" ), QStringLiteral( "http://mysourcem/a.json" ) ), QStringLiteral( "ogr://http://mysourcem/a.json" ) );
1047 QVERIFY( QgsProcessingUtils::decodeProviderKeyAndUri( QStringLiteral( "ogr://http://mysourcem/a.json" ), provider, uri ) );
1048 QCOMPARE( provider, QStringLiteral( "ogr" ) );
1049 QCOMPARE( uri, QStringLiteral( "http://mysourcem/a.json" ) );
1050 QCOMPARE( QgsProcessingUtils::encodeProviderKeyAndUri( QStringLiteral( "postgres" ), QStringLiteral( "host=blah blah etc" ) ), QStringLiteral( "postgres://host=blah blah etc" ) );
1051 QVERIFY( QgsProcessingUtils::decodeProviderKeyAndUri( QStringLiteral( "postgres://host=blah blah etc" ), provider, uri ) );
1052 QCOMPARE( provider, QStringLiteral( "postgres" ) );
1053 QCOMPARE( uri, QStringLiteral( "host=blah blah etc" ) );
1054
1055 // should reject non valid providers
1056 QVERIFY( !QgsProcessingUtils::decodeProviderKeyAndUri( QStringLiteral( "asdasda://host=blah blah etc" ), provider, uri ) );
1057 QVERIFY( !QgsProcessingUtils::decodeProviderKeyAndUri( QStringLiteral( "http://mysourcem/a.json" ), provider, uri ) );
1058 }
1059
normalizeLayerSource()1060 void TestQgsProcessing::normalizeLayerSource()
1061 {
1062 QCOMPARE( QgsProcessingUtils::normalizeLayerSource( "data\\layers\\test.shp" ), QString( "data/layers/test.shp" ) );
1063 QCOMPARE( QgsProcessingUtils::normalizeLayerSource( "data\\layers \"new\"\\test.shp" ), QString( "data/layers \"new\"/test.shp" ) );
1064 }
1065
1066
1067 class TestPostProcessor : public QgsProcessingLayerPostProcessorInterface
1068 {
1069 public:
1070
TestPostProcessor(bool * deleted)1071 TestPostProcessor( bool *deleted )
1072 : deleted( deleted )
1073 {}
1074
~TestPostProcessor()1075 ~TestPostProcessor() override
1076 {
1077 *deleted = true;
1078 }
1079
1080 bool *deleted = nullptr;
1081
postProcessLayer(QgsMapLayer *,QgsProcessingContext &,QgsProcessingFeedback *)1082 void postProcessLayer( QgsMapLayer *, QgsProcessingContext &, QgsProcessingFeedback * ) override
1083 {
1084 }
1085 };
1086
1087
context()1088 void TestQgsProcessing::context()
1089 {
1090 QgsProcessingContext context;
1091
1092 // simple tests for getters/setters
1093 context.setDefaultEncoding( "my_enc" );
1094 QCOMPARE( context.defaultEncoding(), QStringLiteral( "my_enc" ) );
1095
1096 context.setFlags( QgsProcessingContext::Flags() );
1097 QCOMPARE( context.flags(), QgsProcessingContext::Flags() );
1098
1099 QCOMPARE( context.ellipsoid(), QString() );
1100 QCOMPARE( context.distanceUnit(), QgsUnitTypes::DistanceUnknownUnit );
1101 QCOMPARE( context.areaUnit(), QgsUnitTypes::AreaUnknownUnit );
1102
1103 QgsProject p;
1104 p.setCrs( QgsCoordinateReferenceSystem( "EPSG:4536" ) );
1105 p.setEllipsoid( QStringLiteral( "WGS84" ) );
1106 p.setDistanceUnits( QgsUnitTypes::DistanceFeet );
1107 p.setAreaUnits( QgsUnitTypes::AreaHectares );
1108 context.setProject( &p );
1109 QCOMPARE( context.project(), &p );
1110 QCOMPARE( context.ellipsoid(), QStringLiteral( "WGS84" ) );
1111 QCOMPARE( context.distanceUnit(), QgsUnitTypes::DistanceFeet );
1112 QCOMPARE( context.areaUnit(), QgsUnitTypes::AreaHectares );
1113
1114 // if context ellipsoid/units are already set then setting the project shouldn't overwrite them
1115 p.setEllipsoid( QStringLiteral( "WGS84v2" ) );
1116 p.setDistanceUnits( QgsUnitTypes::DistanceMiles );
1117 p.setAreaUnits( QgsUnitTypes::AreaAcres );
1118 context.setProject( &p );
1119 QCOMPARE( context.ellipsoid(), QStringLiteral( "WGS84" ) );
1120 QCOMPARE( context.distanceUnit(), QgsUnitTypes::DistanceFeet );
1121 QCOMPARE( context.areaUnit(), QgsUnitTypes::AreaHectares );
1122
1123 context.setLogLevel( QgsProcessingContext::Verbose );
1124 QCOMPARE( static_cast< int >( context.logLevel() ), static_cast< int >( QgsProcessingContext::Verbose ) );
1125
1126 context.setInvalidGeometryCheck( QgsFeatureRequest::GeometrySkipInvalid );
1127 QCOMPARE( context.invalidGeometryCheck(), QgsFeatureRequest::GeometrySkipInvalid );
1128
1129 QgsVectorLayer *vector = new QgsVectorLayer( "Polygon", "vector", "memory" );
1130 context.temporaryLayerStore()->addMapLayer( vector );
1131 QCOMPARE( context.temporaryLayerStore()->mapLayer( vector->id() ), vector );
1132
1133 QgsProcessingContext context2;
1134 context2.copyThreadSafeSettings( context );
1135 QCOMPARE( context2.defaultEncoding(), context.defaultEncoding() );
1136 QCOMPARE( context2.invalidGeometryCheck(), context.invalidGeometryCheck() );
1137 QCOMPARE( context2.flags(), context.flags() );
1138 QCOMPARE( context2.project(), context.project() );
1139 QCOMPARE( static_cast< int >( context2.logLevel() ), static_cast< int >( QgsProcessingContext::Verbose ) );
1140 // layers from temporaryLayerStore must not be copied by copyThreadSafeSettings
1141 QVERIFY( context2.temporaryLayerStore()->mapLayers().isEmpty() );
1142
1143 // layers to load on completion
1144 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V1", "memory" );
1145 QgsVectorLayer *v2 = new QgsVectorLayer( "Polygon", "V2", "memory" );
1146 QVERIFY( context.layersToLoadOnCompletion().isEmpty() );
1147 QVERIFY( !context.willLoadLayerOnCompletion( v1->id() ) );
1148 QVERIFY( !context.willLoadLayerOnCompletion( v2->id() ) );
1149 QMap< QString, QgsProcessingContext::LayerDetails > layers;
1150 QgsProcessingContext::LayerDetails details( QStringLiteral( "v1" ), &p );
1151 bool ppDeleted = false;
1152 TestPostProcessor *pp = new TestPostProcessor( &ppDeleted );
1153 details.setPostProcessor( pp );
1154 layers.insert( v1->id(), details );
1155 context.setLayersToLoadOnCompletion( layers );
1156 QCOMPARE( context.layersToLoadOnCompletion().count(), 1 );
1157 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
1158 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v1" ) );
1159 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).postProcessor(), pp );
1160 QVERIFY( context.willLoadLayerOnCompletion( v1->id() ) );
1161 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).name, QStringLiteral( "v1" ) );
1162 QVERIFY( !context.willLoadLayerOnCompletion( v2->id() ) );
1163 context.addLayerToLoadOnCompletion( v2->id(), QgsProcessingContext::LayerDetails( QStringLiteral( "v2" ), &p ) );
1164 QCOMPARE( context.layersToLoadOnCompletion().count(), 2 );
1165 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
1166 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v1" ) );
1167 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).postProcessor(), pp );
1168 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 1 ), v2->id() );
1169 QCOMPARE( context.layersToLoadOnCompletion().values().at( 1 ).name, QStringLiteral( "v2" ) );
1170 QVERIFY( !context.layersToLoadOnCompletion().values().at( 1 ).postProcessor() );
1171 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).name, QStringLiteral( "v1" ) );
1172 QCOMPARE( context.layerToLoadOnCompletionDetails( v2->id() ).name, QStringLiteral( "v2" ) );
1173 QVERIFY( context.willLoadLayerOnCompletion( v1->id() ) );
1174 QVERIFY( context.willLoadLayerOnCompletion( v2->id() ) );
1175
1176 // ensure that copyThreadSafeSettings doesn't copy layersToLoadOnCompletion()
1177 context2.copyThreadSafeSettings( context );
1178 QVERIFY( context2.layersToLoadOnCompletion().isEmpty() );
1179
1180 layers.clear();
1181 layers.insert( v2->id(), QgsProcessingContext::LayerDetails( QStringLiteral( "v2" ), &p ) );
1182 context.setLayersToLoadOnCompletion( layers );
1183 QVERIFY( ppDeleted );
1184
1185 QCOMPARE( context.layersToLoadOnCompletion().count(), 1 );
1186 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v2->id() );
1187 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "v2" ) );
1188 context.addLayerToLoadOnCompletion( v1->id(), QgsProcessingContext::LayerDetails( QString(), &p ) );
1189 QCOMPARE( context.layersToLoadOnCompletion().count(), 2 );
1190 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
1191 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 1 ), v2->id() );
1192
1193 context.temporaryLayerStore()->addMapLayer( v1 );
1194 context.temporaryLayerStore()->addMapLayer( v2 );
1195
1196 // test takeResultsFrom
1197 context2.takeResultsFrom( context );
1198 QVERIFY( context.temporaryLayerStore()->mapLayers().isEmpty() );
1199 QVERIFY( context.layersToLoadOnCompletion().isEmpty() );
1200 // should now be in context2
1201 QCOMPARE( context2.temporaryLayerStore()->mapLayer( v1->id() ), v1 );
1202 QCOMPARE( context2.temporaryLayerStore()->mapLayer( v2->id() ), v2 );
1203 QCOMPARE( context2.layersToLoadOnCompletion().count(), 2 );
1204 QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), v1->id() );
1205 QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 1 ), v2->id() );
1206
1207 // make sure postprocessor is correctly deleted
1208 ppDeleted = false;
1209 pp = new TestPostProcessor( &ppDeleted );
1210 details = QgsProcessingContext::LayerDetails( QStringLiteral( "v1" ), &p );
1211 details.setPostProcessor( pp );
1212 layers.insert( v1->id(), details );
1213 context.setLayersToLoadOnCompletion( layers );
1214 // overwrite with existing
1215 context.setLayersToLoadOnCompletion( layers );
1216 QVERIFY( !ppDeleted );
1217 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).postProcessor(), pp );
1218 bool pp2Deleted = false;
1219 TestPostProcessor *pp2 = new TestPostProcessor( &pp2Deleted );
1220 details = QgsProcessingContext::LayerDetails( QStringLiteral( "v1" ), &p );
1221 details.setPostProcessor( pp2 );
1222 layers.insert( v1->id(), details );
1223 context.setLayersToLoadOnCompletion( layers );
1224 QVERIFY( ppDeleted );
1225 QVERIFY( !pp2Deleted );
1226 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).postProcessor(), pp2 );
1227 ppDeleted = false;
1228 pp = new TestPostProcessor( &ppDeleted );
1229 details = QgsProcessingContext::LayerDetails( QStringLiteral( "v1" ), &p );
1230 details.setPostProcessor( pp );
1231 context.addLayerToLoadOnCompletion( v1->id(), details );
1232 QVERIFY( !ppDeleted );
1233 QVERIFY( pp2Deleted );
1234 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).postProcessor(), pp );
1235 pp2Deleted = false;
1236 pp2 = new TestPostProcessor( &pp2Deleted );
1237 context.layerToLoadOnCompletionDetails( v1->id() ).setPostProcessor( pp2 );
1238 QVERIFY( ppDeleted );
1239 QVERIFY( !pp2Deleted );
1240 QCOMPARE( context.layerToLoadOnCompletionDetails( v1->id() ).postProcessor(), pp2 );
1241
1242 // take result layer
1243 QgsMapLayer *result = context2.takeResultLayer( v1->id() );
1244 QCOMPARE( result, v1 );
1245 QString id = v1->id();
1246 delete v1;
1247 QVERIFY( !context2.temporaryLayerStore()->mapLayer( id ) );
1248 QVERIFY( !context2.takeResultLayer( id ) );
1249 result = context2.takeResultLayer( v2->id() );
1250 QCOMPARE( result, v2 );
1251 id = v2->id();
1252 delete v2;
1253 QVERIFY( !context2.temporaryLayerStore()->mapLayer( id ) );
1254 }
1255
feedback()1256 void TestQgsProcessing::feedback()
1257 {
1258 QgsProcessingFeedback f;
1259 f.pushInfo( QStringLiteral( "info" ) );
1260 f.reportError( QStringLiteral( "error" ) );
1261 f.pushDebugInfo( QStringLiteral( "debug" ) );
1262 f.pushCommandInfo( QStringLiteral( "command" ) );
1263 f.pushConsoleInfo( QStringLiteral( "console" ) );
1264
1265 QCOMPARE( f.htmlLog(), QStringLiteral( "info<br/><span style=\"color:red\">error</span><br/><span style=\"color:#777\">debug</span><br/><code>command</code><br/><code style=\"color:#777\">console</code><br/>" ) );
1266 QCOMPARE( f.textLog(), QStringLiteral( "info\nerror\ndebug\ncommand\nconsole\n" ) );
1267 }
1268
mapLayers()1269 void TestQgsProcessing::mapLayers()
1270 {
1271 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
1272 const QString raster = testDataDir + "landsat.tif";
1273 const QString vector = testDataDir + "points.shp";
1274
1275 // test loadMapLayerFromString with raster
1276 QgsMapLayer *l = QgsProcessingUtils::loadMapLayerFromString( raster, QgsCoordinateTransformContext() );
1277 QVERIFY( l->isValid() );
1278 QCOMPARE( l->type(), QgsMapLayerType::RasterLayer );
1279 QCOMPARE( l->name(), QStringLiteral( "landsat" ) );
1280
1281 delete l;
1282
1283 // use encoded provider/uri string
1284 l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "gdal://%1" ).arg( raster ), QgsCoordinateTransformContext() );
1285 QVERIFY( l->isValid() );
1286 QCOMPARE( l->type(), QgsMapLayerType::RasterLayer );
1287 QCOMPARE( l->name(), QStringLiteral( "landsat" ) );
1288 delete l;
1289
1290 //test with vector
1291 l = QgsProcessingUtils::loadMapLayerFromString( vector, QgsCoordinateTransformContext() );
1292 QVERIFY( l->isValid() );
1293 QCOMPARE( l->type(), QgsMapLayerType::VectorLayer );
1294 QCOMPARE( l->name(), QStringLiteral( "points" ) );
1295 delete l;
1296
1297 // use encoded provider/uri string
1298 l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "ogr://%1" ).arg( vector ), QgsCoordinateTransformContext() );
1299 QVERIFY( l->isValid() );
1300 QCOMPARE( l->type(), QgsMapLayerType::VectorLayer );
1301 QCOMPARE( l->name(), QStringLiteral( "points" ) );
1302 delete l;
1303
1304 l = QgsProcessingUtils::loadMapLayerFromString( QString(), QgsCoordinateTransformContext() );
1305 QVERIFY( !l );
1306 l = QgsProcessingUtils::loadMapLayerFromString( QStringLiteral( "so much room for activities!" ), QgsCoordinateTransformContext() );
1307 QVERIFY( !l );
1308 l = QgsProcessingUtils::loadMapLayerFromString( testDataDir + "multipoint.shp", QgsCoordinateTransformContext() );
1309 QVERIFY( l->isValid() );
1310 QCOMPARE( l->type(), QgsMapLayerType::VectorLayer );
1311 QCOMPARE( l->name(), QStringLiteral( "multipoint" ) );
1312 delete l;
1313
1314 // Test layers from a string with parameters
1315 const QString osmFilePath = testDataDir + "openstreetmap/testdata.xml";
1316 std::unique_ptr< QgsVectorLayer > osm( qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::loadMapLayerFromString( osmFilePath, QgsCoordinateTransformContext() ) ) );
1317 QVERIFY( osm->isValid() );
1318 QCOMPARE( osm->geometryType(), QgsWkbTypes::PointGeometry );
1319
1320 osm.reset( qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::loadMapLayerFromString( osmFilePath + "|layerid=3", QgsCoordinateTransformContext() ) ) );
1321 QVERIFY( osm->isValid() );
1322 QCOMPARE( osm->geometryType(), QgsWkbTypes::PolygonGeometry );
1323
1324 osm.reset( qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::loadMapLayerFromString( osmFilePath + "|layerid=3|subset=\"building\" is not null", QgsCoordinateTransformContext() ) ) );
1325 QVERIFY( osm->isValid() );
1326 QCOMPARE( osm->geometryType(), QgsWkbTypes::PolygonGeometry );
1327 QCOMPARE( osm->subsetString(), QStringLiteral( "\"building\" is not null" ) );
1328 }
1329
mapLayerFromStore()1330 void TestQgsProcessing::mapLayerFromStore()
1331 {
1332 // test mapLayerFromStore
1333
1334 QgsMapLayerStore store;
1335
1336 // add a bunch of layers to a project
1337 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
1338 const QString raster1 = testDataDir + "tenbytenraster.asc";
1339 const QString raster2 = testDataDir + "landsat.tif";
1340 const QFileInfo fi1( raster1 );
1341 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
1342 QVERIFY( r1->isValid() );
1343 const QFileInfo fi2( raster2 );
1344 QgsRasterLayer *r2 = new QgsRasterLayer( fi2.filePath(), "ar2" );
1345 QVERIFY( r2->isValid() );
1346
1347 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V4", "memory" );
1348 QgsVectorLayer *v2 = new QgsVectorLayer( "Point", "v1", "memory" );
1349 store.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
1350
1351 QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), nullptr ) );
1352 QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QStringLiteral( "v1" ), nullptr ) );
1353 QVERIFY( ! QgsProcessingUtils::mapLayerFromStore( QString(), &store ) );
1354 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster1, &store ), r1 );
1355 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( raster2, &store ), r2 );
1356 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "R1", &store ), r1 );
1357 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "ar2", &store ), r2 );
1358 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "V4", &store ), v1 );
1359 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( "v1", &store ), v2 );
1360 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( r1->id(), &store ), r1 );
1361 QCOMPARE( QgsProcessingUtils::mapLayerFromStore( v1->id(), &store ), v1 );
1362 }
1363
mapLayerFromString()1364 void TestQgsProcessing::mapLayerFromString()
1365 {
1366 // test mapLayerFromString
1367
1368 QgsProcessingContext c;
1369 QgsProject p;
1370
1371 // add a bunch of layers to a project
1372 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
1373 const QString raster1 = testDataDir + "tenbytenraster.asc";
1374 const QString raster2 = testDataDir + "landsat.tif";
1375 const QFileInfo fi1( raster1 );
1376 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
1377 QVERIFY( r1->isValid() );
1378 const QFileInfo fi2( raster2 );
1379 QgsRasterLayer *r2 = new QgsRasterLayer( fi2.filePath(), "ar2" );
1380 QVERIFY( r2->isValid() );
1381
1382 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon", "V4", "memory" );
1383 QgsVectorLayer *v2 = new QgsVectorLayer( "Point", "v1", "memory" );
1384 p.addMapLayers( QList<QgsMapLayer *>() << r1 << r2 << v1 << v2 );
1385
1386 // no project set yet
1387 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QString(), c ) );
1388 QVERIFY( !c.getMapLayer( QString() ) );
1389 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QStringLiteral( "v1" ), c ) );
1390 QVERIFY( !c.getMapLayer( QStringLiteral( "v1" ) ) );
1391
1392 c.setProject( &p );
1393
1394 // layers from current project
1395 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( QString(), c ) );
1396 QVERIFY( !c.getMapLayer( QString() ) );
1397 QCOMPARE( QgsProcessingUtils::mapLayerFromString( raster1, c ), r1 );
1398 QCOMPARE( c.getMapLayer( raster1 ), r1 );
1399 QCOMPARE( QgsProcessingUtils::mapLayerFromString( raster1, c, true, QgsProcessingUtils::LayerHint::Raster ), r1 );
1400 QVERIFY( !QgsProcessingUtils::mapLayerFromString( raster1, c, true, QgsProcessingUtils::LayerHint::Vector ) );
1401 QCOMPARE( QgsProcessingUtils::mapLayerFromString( raster2, c ), r2 );
1402 QCOMPARE( c.getMapLayer( raster2 ), r2 );
1403 QVERIFY( !QgsProcessingUtils::mapLayerFromString( raster2, c, true, QgsProcessingUtils::LayerHint::Vector ) );
1404 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "R1", c ), r1 );
1405 QCOMPARE( c.getMapLayer( "R1" ), r1 );
1406 QVERIFY( !QgsProcessingUtils::mapLayerFromString( "R1", c, true, QgsProcessingUtils::LayerHint::Vector ) );
1407 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "ar2", c ), r2 );
1408 QCOMPARE( c.getMapLayer( "ar2" ), r2 );
1409 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V4", c ), v1 );
1410 QCOMPARE( c.getMapLayer( "V4" ), v1 );
1411 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V4", c, true, QgsProcessingUtils::LayerHint::Vector ), v1 );
1412 QVERIFY( !QgsProcessingUtils::mapLayerFromString( "V4", c, true, QgsProcessingUtils::LayerHint::Raster ) );
1413 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v1", c ), v2 );
1414 QCOMPARE( c.getMapLayer( "v1" ), v2 );
1415 QCOMPARE( QgsProcessingUtils::mapLayerFromString( r1->id(), c ), r1 );
1416 QCOMPARE( c.getMapLayer( r1->id() ), r1 );
1417 QCOMPARE( QgsProcessingUtils::mapLayerFromString( v1->id(), c ), v1 );
1418 QCOMPARE( c.getMapLayer( v1->id() ), v1 );
1419
1420 // check that layers in context temporary store are used
1421 QgsVectorLayer *v5 = new QgsVectorLayer( "Polygon", "V5", "memory" );
1422 QgsVectorLayer *v6 = new QgsVectorLayer( "Point", "v6", "memory" );
1423 c.temporaryLayerStore()->addMapLayers( QList<QgsMapLayer *>() << v5 << v6 );
1424 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V5", c ), v5 );
1425 QCOMPARE( c.getMapLayer( "V5" ), v5 );
1426 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "V5", c, true, QgsProcessingUtils::LayerHint::Vector ), v5 );
1427 QVERIFY( !QgsProcessingUtils::mapLayerFromString( "V5", c, true, QgsProcessingUtils::LayerHint::Raster ) );
1428 QCOMPARE( QgsProcessingUtils::mapLayerFromString( "v6", c ), v6 );
1429 QCOMPARE( c.getMapLayer( "v6" ), v6 );
1430 QCOMPARE( QgsProcessingUtils::mapLayerFromString( v5->id(), c ), v5 );
1431 QCOMPARE( c.getMapLayer( v5->id() ), v5 );
1432 QCOMPARE( QgsProcessingUtils::mapLayerFromString( v6->id(), c ), v6 );
1433 QCOMPARE( c.getMapLayer( v6->id() ), v6 );
1434 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( "aaaaa", c ) );
1435 QVERIFY( !c.getMapLayer( "aaaa" ) );
1436
1437 // if specified, check that layers can be loaded
1438 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( "aaaaa", c ) );
1439 const QString newRaster = testDataDir + "requires_warped_vrt.tif";
1440 // don't allow loading
1441 QVERIFY( ! QgsProcessingUtils::mapLayerFromString( newRaster, c, false ) );
1442 QVERIFY( !c.getMapLayer( newRaster ) );
1443 // allow loading
1444 QgsMapLayer *loadedLayer = QgsProcessingUtils::mapLayerFromString( newRaster, c, true );
1445 QVERIFY( loadedLayer->isValid() );
1446 QCOMPARE( loadedLayer->type(), QgsMapLayerType::RasterLayer );
1447 // should now be in temporary store
1448 QCOMPARE( c.temporaryLayerStore()->mapLayer( loadedLayer->id() ), loadedLayer );
1449
1450 // since it's now in temporary store, should be accessible even if we deny loading new layers
1451 QCOMPARE( QgsProcessingUtils::mapLayerFromString( newRaster, c, false ), loadedLayer );
1452 QCOMPARE( c.getMapLayer( newRaster ), loadedLayer );
1453 }
1454
algorithm()1455 void TestQgsProcessing::algorithm()
1456 {
1457 DummyAlgorithm alg( "test" );
1458 DummyProvider *p = new DummyProvider( "p1" );
1459 QCOMPARE( alg.id(), QString( "test" ) );
1460 alg.setProvider( p );
1461 QCOMPARE( alg.provider(), p );
1462 QCOMPARE( alg.id(), QString( "p1:test" ) );
1463
1464 QVERIFY( p->algorithms().isEmpty() );
1465
1466 const QSignalSpy providerRefreshed( p, &DummyProvider::algorithmsLoaded );
1467 p->refreshAlgorithms();
1468 QCOMPARE( providerRefreshed.count(), 1 );
1469
1470 for ( int i = 0; i < 2; ++i )
1471 {
1472 QCOMPARE( p->algorithms().size(), 2 );
1473 QCOMPARE( p->algorithm( "alg1" )->name(), QStringLiteral( "alg1" ) );
1474 QCOMPARE( p->algorithm( "alg1" )->provider(), p );
1475 QCOMPARE( p->algorithm( "alg2" )->provider(), p );
1476 QCOMPARE( p->algorithm( "alg2" )->name(), QStringLiteral( "alg2" ) );
1477 QVERIFY( !p->algorithm( "aaaa" ) );
1478 QVERIFY( p->algorithms().contains( p->algorithm( "alg1" ) ) );
1479 QVERIFY( p->algorithms().contains( p->algorithm( "alg2" ) ) );
1480
1481 // reload, then retest on next loop
1482 // must be safe for providers to reload their algorithms
1483 p->refreshAlgorithms();
1484 QCOMPARE( providerRefreshed.count(), 2 + i );
1485 }
1486
1487 // inactive provider, should not load algorithms
1488 p->active = false;
1489 p->refreshAlgorithms();
1490 QCOMPARE( providerRefreshed.count(), 3 );
1491 QVERIFY( p->algorithms().empty() );
1492 p->active = true;
1493 p->refreshAlgorithms();
1494 QCOMPARE( providerRefreshed.count(), 4 );
1495 QVERIFY( !p->algorithms().empty() );
1496
1497 QgsProcessingRegistry r;
1498 QVERIFY( r.addProvider( p ) );
1499 QCOMPARE( r.algorithms().size(), 2 );
1500 QVERIFY( r.algorithms().contains( p->algorithm( "alg1" ) ) );
1501 QVERIFY( r.algorithms().contains( p->algorithm( "alg2" ) ) );
1502
1503 // algorithmById
1504 QCOMPARE( r.algorithmById( "p1:alg1" ), p->algorithm( "alg1" ) );
1505 QCOMPARE( r.algorithmById( "p1:alg2" ), p->algorithm( "alg2" ) );
1506 QVERIFY( !r.algorithmById( "p1:alg3" ) );
1507 QVERIFY( !r.algorithmById( "px:alg1" ) );
1508
1509 // alias support
1510 QVERIFY( !r.algorithmById( QStringLiteral( "fake:fakealg" ) ) );
1511 r.addAlgorithmAlias( QStringLiteral( "fake:fakealg" ), QStringLiteral( "nope:none" ) );
1512 QVERIFY( !r.algorithmById( QStringLiteral( "fake:fakealg" ) ) );
1513 r.addAlgorithmAlias( QStringLiteral( "fake:fakealg" ), QStringLiteral( "p1:alg1" ) );
1514 QCOMPARE( r.algorithmById( QStringLiteral( "fake:fakealg" ) ), p->algorithm( "alg1" ) );
1515
1516 // test that algorithmById can transparently map 'qgis' algorithms across to matching 'native' algorithms
1517 // this allows us the freedom to convert qgis python algs to c++ without breaking api or existing models
1518 QCOMPARE( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "qgis:dissolve" ) )->id(), QStringLiteral( "native:dissolve" ) );
1519 QCOMPARE( QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "qgis:clip" ) )->id(), QStringLiteral( "native:clip" ) );
1520 QVERIFY( !QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "qgis:notanalg" ) ) );
1521
1522 // createAlgorithmById
1523 QVERIFY( !r.createAlgorithmById( "p1:alg3" ) );
1524 std::unique_ptr< QgsProcessingAlgorithm > creation( r.createAlgorithmById( "p1:alg1" ) );
1525 QVERIFY( creation.get() );
1526 QCOMPARE( creation->provider()->id(), QStringLiteral( "p1" ) );
1527 QCOMPARE( creation->id(), QStringLiteral( "p1:alg1" ) );
1528 creation.reset( r.createAlgorithmById( "p1:alg2" ) );
1529 QVERIFY( creation.get() );
1530 QCOMPARE( creation->provider()->id(), QStringLiteral( "p1" ) );
1531 QCOMPARE( creation->id(), QStringLiteral( "p1:alg2" ) );
1532
1533 //test that loading a provider triggers an algorithm refresh
1534 DummyProvider *p2 = new DummyProvider( "p2" );
1535 QVERIFY( p2->algorithms().isEmpty() );
1536 p2->load();
1537 QCOMPARE( p2->algorithms().size(), 2 );
1538 delete p2;
1539
1540 // test that adding a provider to the registry automatically refreshes algorithms (via load)
1541 DummyProvider *p3 = new DummyProvider( "p3" );
1542 QVERIFY( p3->algorithms().isEmpty() );
1543 QVERIFY( r.addProvider( p3 ) );
1544 QCOMPARE( p3->algorithms().size(), 2 );
1545 }
1546
features()1547 void TestQgsProcessing::features()
1548 {
1549 QgsVectorLayer *layer = new QgsVectorLayer( "Point", "v1", "memory" );
1550 for ( int i = 1; i < 6; ++i )
1551 {
1552 QgsFeature f( i );
1553 f.setGeometry( QgsGeometry( new QgsPoint( 1, 2 ) ) );
1554 layer->dataProvider()->addFeatures( QgsFeatureList() << f );
1555 }
1556
1557 QgsProject p;
1558 p.addMapLayer( layer );
1559
1560 QgsProcessingContext context;
1561 context.setProject( &p );
1562 // disable check for geometry validity
1563 context.setFlags( QgsProcessingContext::Flags() );
1564
1565 const std::function< QgsFeatureIds( QgsFeatureIterator it ) > getIds = []( QgsFeatureIterator it )
1566 {
1567 QgsFeature f;
1568 QgsFeatureIds ids;
1569 while ( it.nextFeature( f ) )
1570 {
1571 ids << f.id();
1572 }
1573 return ids;
1574 };
1575
1576 const std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterString( QStringLiteral( "layer" ) ) );
1577 QVariantMap params;
1578 params.insert( QStringLiteral( "layer" ), layer->id() );
1579
1580 std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1581
1582 // test with all features
1583 QgsFeatureIds ids = getIds( source->getFeatures() );
1584 QCOMPARE( ids, QgsFeatureIds() << 1 << 2 << 3 << 4 << 5 );
1585 QCOMPARE( source->featureCount(), 5L );
1586
1587 // test with selected features
1588 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), true ) ) );
1589 layer->selectByIds( QgsFeatureIds() << 2 << 4 );
1590 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1591 ids = getIds( source->getFeatures() );
1592 QCOMPARE( ids, QgsFeatureIds() << 2 << 4 );
1593 QCOMPARE( source->featureCount(), 2L );
1594
1595 // selection, but not using selected features
1596 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), false ) ) );
1597 layer->selectByIds( QgsFeatureIds() << 2 << 4 );
1598 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1599 ids = getIds( source->getFeatures() );
1600 QCOMPARE( ids, QgsFeatureIds() << 1 << 2 << 3 << 4 << 5 );
1601 QCOMPARE( source->featureCount(), 5L );
1602
1603 // using selected features, but no selection
1604 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), true ) ) );
1605 layer->removeSelection();
1606 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1607 ids = getIds( source->getFeatures() );
1608 QVERIFY( ids.isEmpty() );
1609 QCOMPARE( source->featureCount(), 0L );
1610
1611 // feature limit
1612 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), false, 3 ) ) );
1613 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1614 ids = getIds( source->getFeatures() );
1615 QCOMPARE( ids.size(), 3 );
1616 QCOMPARE( source->featureCount(), 3L );
1617
1618 // test that feature request is honored
1619 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), false ) ) );
1620 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1621 ids = getIds( source->getFeatures( QgsFeatureRequest().setFilterFids( QgsFeatureIds() << 1 << 3 << 5 ) ) );
1622 QCOMPARE( ids, QgsFeatureIds() << 1 << 3 << 5 );
1623
1624 // count is only rough - but we expect (for now) to see full layer count
1625 QCOMPARE( source->featureCount(), 5L );
1626
1627 //test that feature request is honored when using selections
1628 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), true ) ) );
1629 layer->selectByIds( QgsFeatureIds() << 2 << 4 );
1630 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1631 ids = getIds( source->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ) ) );
1632 QCOMPARE( ids, QgsFeatureIds() << 2 << 4 );
1633
1634 // test callback is hit when filtering invalid geoms
1635 bool encountered = false;
1636 const std::function< void( const QgsFeature & ) > callback = [ &encountered ]( const QgsFeature & )
1637 {
1638 encountered = true;
1639 };
1640
1641 context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryAbortOnInvalid );
1642 context.setInvalidGeometryCallback( callback );
1643 QgsVectorLayer *polyLayer = new QgsVectorLayer( "Polygon", "v2", "memory" );
1644 QgsFeature f;
1645 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 1 0, 0 1, 1 1, 0 0))" ) ) );
1646 polyLayer->dataProvider()->addFeatures( QgsFeatureList() << f );
1647 p.addMapLayer( polyLayer );
1648 params.insert( QStringLiteral( "layer" ), polyLayer->id() );
1649
1650 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1651 ids = getIds( source->getFeatures() );
1652 QVERIFY( encountered );
1653
1654 encountered = false;
1655 context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryNoCheck );
1656 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1657 ids = getIds( source->getFeatures() );
1658 QVERIFY( !encountered );
1659
1660 // context wants to filter, but filtering disabled on source definition
1661 context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryAbortOnInvalid );
1662 context.setInvalidGeometryCallback( callback );
1663 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( polyLayer->id(), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometryNoCheck ) ) );
1664
1665 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1666 ids = getIds( source->getFeatures() );
1667 QVERIFY( !encountered );
1668
1669 QgsProcessingContext context2;
1670 // context wants to skip, source wants to abort
1671 context2.setInvalidGeometryCheck( QgsFeatureRequest::GeometrySkipInvalid );
1672 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( polyLayer->id(), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometryAbortOnInvalid ) ) );
1673 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1674 try
1675 {
1676 ids = getIds( source->getFeatures() );
1677 QVERIFY( false );
1678 }
1679 catch ( QgsProcessingException & )
1680 {}
1681
1682 // equality operator
1683 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true ) == QgsProcessingFeatureSourceDefinition( layer->id(), true ) );
1684 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true ) != QgsProcessingFeatureSourceDefinition( "b", true ) );
1685 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true ) != QgsProcessingFeatureSourceDefinition( layer->id(), false ) );
1686 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true ) != QgsProcessingFeatureSourceDefinition( layer->id(), true, 5 ) );
1687 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true, 5, QgsProcessingFeatureSourceDefinition::Flags() ) != QgsProcessingFeatureSourceDefinition( layer->id(), true, 5, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck ) );
1688 QVERIFY( QgsProcessingFeatureSourceDefinition( layer->id(), true, 5, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid ) != QgsProcessingFeatureSourceDefinition( layer->id(), true, 5, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometryAbortOnInvalid ) );
1689 }
1690
uniqueValues()1691 void TestQgsProcessing::uniqueValues()
1692 {
1693 QgsVectorLayer *layer = new QgsVectorLayer( "Point?field=a:integer&field=b:string", "v1", "memory" );
1694 for ( int i = 0; i < 6; ++i )
1695 {
1696 QgsFeature f( i );
1697 f.setAttributes( QgsAttributes() << i % 3 + 1 << QString( QChar( ( i % 3 ) + 65 ) ) );
1698 layer->dataProvider()->addFeatures( QgsFeatureList() << f );
1699 }
1700
1701 QgsProcessingContext context;
1702 context.setFlags( QgsProcessingContext::Flags() );
1703
1704 QgsProject p;
1705 p.addMapLayer( layer );
1706 context.setProject( &p );
1707
1708 const std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterString( QStringLiteral( "layer" ) ) );
1709 QVariantMap params;
1710 params.insert( QStringLiteral( "layer" ), layer->id() );
1711
1712 std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1713
1714 // some bad checks
1715 QVERIFY( source->uniqueValues( -1 ).isEmpty() );
1716 QVERIFY( source->uniqueValues( 10001 ).isEmpty() );
1717
1718 // good checks
1719 QSet< QVariant > vals = source->uniqueValues( 0 );
1720 QCOMPARE( vals.count(), 3 );
1721 QVERIFY( vals.contains( 1 ) );
1722 QVERIFY( vals.contains( 2 ) );
1723 QVERIFY( vals.contains( 3 ) );
1724 vals = source->uniqueValues( 1 );
1725 QCOMPARE( vals.count(), 3 );
1726 QVERIFY( vals.contains( QString( "A" ) ) );
1727 QVERIFY( vals.contains( QString( "B" ) ) );
1728 QVERIFY( vals.contains( QString( "C" ) ) );
1729
1730 //using only selected features
1731 layer->selectByIds( QgsFeatureIds() << 1 << 2 << 4 );
1732 // but not using selection yet...
1733 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1734 vals = source->uniqueValues( 0 );
1735 QCOMPARE( vals.count(), 3 );
1736 QVERIFY( vals.contains( 1 ) );
1737 QVERIFY( vals.contains( 2 ) );
1738 QVERIFY( vals.contains( 3 ) );
1739 vals = source->uniqueValues( 1 );
1740 QCOMPARE( vals.count(), 3 );
1741 QVERIFY( vals.contains( QString( "A" ) ) );
1742 QVERIFY( vals.contains( QString( "B" ) ) );
1743 QVERIFY( vals.contains( QString( "C" ) ) );
1744
1745 // selection and using selection
1746 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), true ) ) );
1747 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1748 QVERIFY( source->uniqueValues( -1 ).isEmpty() );
1749 QVERIFY( source->uniqueValues( 10001 ).isEmpty() );
1750 vals = source->uniqueValues( 0 );
1751 QCOMPARE( vals.count(), 2 );
1752 QVERIFY( vals.contains( 1 ) );
1753 QVERIFY( vals.contains( 2 ) );
1754 vals = source->uniqueValues( 1 );
1755 QCOMPARE( vals.count(), 2 );
1756 QVERIFY( vals.contains( QString( "A" ) ) );
1757 QVERIFY( vals.contains( QString( "B" ) ) );
1758 }
1759
createIndex()1760 void TestQgsProcessing::createIndex()
1761 {
1762 QgsVectorLayer *layer = new QgsVectorLayer( "Point", "v1", "memory" );
1763 for ( int i = 1; i < 6; ++i )
1764 {
1765 QgsFeature f( i );
1766 f.setGeometry( QgsGeometry( new QgsPoint( i, 2 ) ) );
1767 layer->dataProvider()->addFeatures( QgsFeatureList() << f );
1768 }
1769
1770 QgsProcessingContext context;
1771 QgsProject p;
1772 p.addMapLayer( layer );
1773 context.setProject( &p );
1774
1775 const std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterString( QStringLiteral( "layer" ) ) );
1776 QVariantMap params;
1777 params.insert( QStringLiteral( "layer" ), layer->id() );
1778
1779 // disable selected features check
1780 std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1781 QVERIFY( source.get() );
1782 QgsSpatialIndex index( *source );
1783 QList<QgsFeatureId> ids = index.nearestNeighbor( QgsPointXY( 2.1, 2 ), 1 );
1784 QCOMPARE( ids, QList<QgsFeatureId>() << 2 );
1785
1786 // selected features check, but none selected
1787 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), true ) ) );
1788 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1789 index = QgsSpatialIndex( *source );
1790 ids = index.nearestNeighbor( QgsPointXY( 2.1, 2 ), 1 );
1791 QCOMPARE( ids, QList<QgsFeatureId>() );
1792
1793 // create selection
1794 layer->selectByIds( QgsFeatureIds() << 4 << 5 );
1795 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1796 index = QgsSpatialIndex( *source );
1797 ids = index.nearestNeighbor( QgsPointXY( 2.1, 2 ), 1 );
1798 QCOMPARE( ids, QList<QgsFeatureId>() << 4 );
1799
1800 // selection but not using selection mode
1801 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( QgsProcessingFeatureSourceDefinition( layer->id(), false ) ) );
1802 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
1803 index = QgsSpatialIndex( *source );
1804 ids = index.nearestNeighbor( QgsPointXY( 2.1, 2 ), 1 );
1805 QCOMPARE( ids, QList<QgsFeatureId>() << 2 );
1806 }
1807
generateTemporaryDestination()1808 void TestQgsProcessing::generateTemporaryDestination()
1809 {
1810 // setup a context
1811 QgsProject p;
1812 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
1813 QgsProcessingContext context;
1814 context.setProject( &p );
1815
1816 // destination vector with "." in it's name
1817 std::unique_ptr< QgsProcessingParameterVectorDestination > def( new QgsProcessingParameterVectorDestination( "with.inside", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
1818
1819 // check that temporary destination does not have dot at the end when there is no extension
1820 QVERIFY( !def->generateTemporaryDestination().endsWith( QLatin1Char( '.' ) ) );
1821 // check that temporary destination starts with tempFolder
1822 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
1823 // check that extension with QFileInfo::completeSuffix is "gpkg"
1824 QFileInfo f = QFileInfo( def->generateTemporaryDestination() );
1825 QCOMPARE( f.completeSuffix(), QString( "gpkg" ) );
1826
1827 // destination raster with "." in it's name
1828 std::unique_ptr< QgsProcessingParameterRasterDestination > def2( new QgsProcessingParameterRasterDestination( "with.inside", QString(), QString(), false ) );
1829
1830 // check that temporary destination does not have dot at the end when there is no extension
1831 QVERIFY( !def2->generateTemporaryDestination().endsWith( QLatin1Char( '.' ) ) );
1832 // check that temporary destination starts with tempFolder
1833 QVERIFY( def2->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
1834 // check that extension with QFileInfo::completeSuffix is "tif"
1835 f = QFileInfo( def2->generateTemporaryDestination() );
1836 QCOMPARE( f.completeSuffix(), QString( "tif" ) );
1837
1838 // destination vector without "." in it's name
1839 std::unique_ptr< QgsProcessingParameterVectorDestination > def3( new QgsProcessingParameterVectorDestination( "without_inside", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
1840
1841 // check that temporary destination does not have dot at the end when there is no extension
1842 QVERIFY( !def3->generateTemporaryDestination().endsWith( QLatin1Char( '.' ) ) );
1843 // check that temporary destination starts with tempFolder
1844 QVERIFY( def3->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
1845 // check that extension with QFileInfo::completeSuffix is "gpkg"
1846 f = QFileInfo( def3->generateTemporaryDestination() );
1847 QCOMPARE( f.completeSuffix(), QString( "gpkg" ) );
1848
1849 }
1850
parseDestinationString()1851 void TestQgsProcessing::parseDestinationString()
1852 {
1853 QString providerKey;
1854 QString uri;
1855 QString layerName;
1856 QString format;
1857 QVariantMap options;
1858 QString extension;
1859 bool useWriter = false;
1860
1861 // simple shapefile output
1862 QString destination = QStringLiteral( "d:/test.shp" );
1863 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1864 QCOMPARE( destination, QStringLiteral( "d:/test.shp" ) );
1865 QCOMPARE( providerKey, QStringLiteral( "ogr" ) );
1866 QCOMPARE( uri, QStringLiteral( "d:/test.shp" ) );
1867 QCOMPARE( format, QStringLiteral( "ESRI Shapefile" ) );
1868 QCOMPARE( extension, QStringLiteral( "shp" ) );
1869 QVERIFY( useWriter );
1870
1871 // postgis output
1872 destination = QStringLiteral( "postgis:dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" );
1873 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1874 QCOMPARE( providerKey, QStringLiteral( "postgres" ) );
1875 QCOMPARE( uri, QStringLiteral( "dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" ) );
1876 QVERIFY( !useWriter );
1877 QVERIFY( extension.isEmpty() );
1878 // postgres key should also work
1879 destination = QStringLiteral( "postgres:dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" );
1880 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1881 QCOMPARE( providerKey, QStringLiteral( "postgres" ) );
1882 QCOMPARE( uri, QStringLiteral( "dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" ) );
1883 QVERIFY( !useWriter );
1884 QVERIFY( extension.isEmpty() );
1885
1886 // newer format
1887 destination = QStringLiteral( "postgres://dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" );
1888 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1889 QCOMPARE( providerKey, QStringLiteral( "postgres" ) );
1890 QCOMPARE( uri, QStringLiteral( "dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" ) );
1891 QVERIFY( !useWriter );
1892 QVERIFY( extension.isEmpty() );
1893 //mssql
1894 destination = QStringLiteral( "mssql://dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" );
1895 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1896 QCOMPARE( providerKey, QStringLiteral( "mssql" ) );
1897 QCOMPARE( uri, QStringLiteral( "dbname='db' host=DBHOST port=5432 table=\"calcs\".\"output\" (geom) sql=" ) );
1898 QVERIFY( !useWriter );
1899 QVERIFY( extension.isEmpty() );
1900
1901 // full uri shp output
1902 options.clear();
1903 destination = QStringLiteral( "ogr:d:/test.shp" );
1904 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1905 QCOMPARE( providerKey, QStringLiteral( "ogr" ) );
1906 QCOMPARE( uri, QStringLiteral( "d:/test.shp" ) );
1907 QCOMPARE( options.value( QStringLiteral( "update" ) ).toBool(), true );
1908 QCOMPARE( options.value( QStringLiteral( "driverName" ) ).toString(), QStringLiteral( "ESRI Shapefile" ) );
1909 QVERIFY( !options.contains( QStringLiteral( "layerName" ) ) );
1910 QVERIFY( !useWriter );
1911 QCOMPARE( extension, QStringLiteral( "shp" ) );
1912
1913 // full uri geopackage output
1914 options.clear();
1915 destination = QStringLiteral( "ogr:d:/test.gpkg" );
1916 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1917 QCOMPARE( providerKey, QStringLiteral( "ogr" ) );
1918 QCOMPARE( uri, QStringLiteral( "d:/test.gpkg" ) );
1919 QCOMPARE( options.value( QStringLiteral( "update" ) ).toBool(), true );
1920 QVERIFY( !options.contains( QStringLiteral( "layerName" ) ) );
1921 QCOMPARE( options.value( QStringLiteral( "driverName" ) ).toString(), QStringLiteral( "GPKG" ) );
1922 QVERIFY( !useWriter );
1923 QCOMPARE( extension, QStringLiteral( "gpkg" ) );
1924
1925 // full uri geopackage table output with layer name
1926 options.clear();
1927 destination = QStringLiteral( "ogr:dbname='d:/package.gpkg' table=\"mylayer\" (geom) sql=" );
1928 QgsProcessingUtils::parseDestinationString( destination, providerKey, uri, layerName, format, options, useWriter, extension );
1929 QCOMPARE( providerKey, QStringLiteral( "ogr" ) );
1930 QCOMPARE( uri, QStringLiteral( "d:/package.gpkg" ) );
1931 QCOMPARE( options.value( QStringLiteral( "update" ) ).toBool(), true );
1932 QCOMPARE( options.value( QStringLiteral( "layerName" ) ).toString(), QStringLiteral( "mylayer" ) );
1933 QCOMPARE( options.value( QStringLiteral( "driverName" ) ).toString(), QStringLiteral( "GPKG" ) );
1934 QVERIFY( !useWriter );
1935 QCOMPARE( extension, QStringLiteral( "gpkg" ) );
1936 }
1937
createFeatureSink()1938 void TestQgsProcessing::createFeatureSink()
1939 {
1940 QgsProcessingContext context;
1941
1942 // empty destination
1943 QString destination;
1944 destination = QString();
1945 QgsVectorLayer *layer = nullptr;
1946
1947 // should create a memory layer
1948 std::unique_ptr< QgsFeatureSink > sink( QgsProcessingUtils::createFeatureSink( destination, context, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem() ) );
1949 QVERIFY( sink.get() );
1950 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
1951 QVERIFY( layer );
1952 QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink.get() )->destinationSink(), layer->dataProvider() );
1953 QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
1954 QCOMPARE( destination, layer->id() );
1955 QVERIFY( !layer->customProperty( QStringLiteral( "OnConvertFormatRegeneratePrimaryKey" ) ).toBool() );
1956 QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
1957 QgsFeature f;
1958 QCOMPARE( layer->featureCount(), 0L );
1959 QVERIFY( sink->addFeature( f ) );
1960 QCOMPARE( layer->featureCount(), 1L );
1961 context.temporaryLayerStore()->removeAllMapLayers();
1962 layer = nullptr;
1963
1964 // specific memory layer output
1965 destination = QStringLiteral( "memory:mylayer" );
1966 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem() ) );
1967 QVERIFY( sink.get() );
1968 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
1969 QVERIFY( layer );
1970 QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink.get() )->destinationSink(), layer->dataProvider() );
1971 QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
1972 QCOMPARE( layer->name(), QStringLiteral( "mylayer" ) );
1973 QCOMPARE( destination, layer->id() );
1974 QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
1975 QCOMPARE( layer->featureCount(), 0L );
1976 QVERIFY( sink->addFeature( f ) );
1977 QCOMPARE( layer->featureCount(), 1L );
1978 context.temporaryLayerStore()->removeAllMapLayers();
1979 layer = nullptr;
1980
1981 // nameless memory layer
1982 destination = QStringLiteral( "memory:" );
1983 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem() ) );
1984 QVERIFY( sink.get() );
1985 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
1986 QVERIFY( layer );
1987 QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink.get() )->destinationSink(), layer->dataProvider() );
1988 QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
1989 QCOMPARE( layer->name(), QString( "output" ) ); // should fall back to "output" name
1990 QCOMPARE( destination, layer->id() );
1991 QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
1992 QCOMPARE( layer->featureCount(), 0L );
1993 QVERIFY( sink->addFeature( f ) );
1994 QCOMPARE( layer->featureCount(), 1L );
1995 context.temporaryLayerStore()->removeAllMapLayers();
1996 layer = nullptr;
1997
1998 // memory layer parameters
1999 destination = QStringLiteral( "memory:mylayer" );
2000 QgsFields fields;
2001 fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) );
2002 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::PointZM, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ), QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::RegeneratePrimaryKey ) );
2003 QVERIFY( sink.get() );
2004 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, false ) );
2005 QVERIFY( layer );
2006 QCOMPARE( static_cast< QgsProxyFeatureSink *>( sink.get() )->destinationSink(), layer->dataProvider() );
2007 QCOMPARE( layer->dataProvider()->name(), QStringLiteral( "memory" ) );
2008 QCOMPARE( layer->name(), QStringLiteral( "mylayer" ) );
2009 QVERIFY( layer->customProperty( QStringLiteral( "OnConvertFormatRegeneratePrimaryKey" ) ).toBool() );
2010 QCOMPARE( layer->wkbType(), QgsWkbTypes::PointZM );
2011 QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
2012 QCOMPARE( layer->fields().size(), 1 );
2013 QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "my_field" ) );
2014 QCOMPARE( layer->fields().at( 0 ).type(), QVariant::String );
2015 QCOMPARE( destination, layer->id() );
2016 QCOMPARE( context.temporaryLayerStore()->mapLayer( layer->id() ), layer ); // layer should be in store
2017 QCOMPARE( layer->featureCount(), 0L );
2018 QVERIFY( sink->addFeature( f ) );
2019 QCOMPARE( layer->featureCount(), 1L );
2020 context.temporaryLayerStore()->removeAllMapLayers();
2021
2022 // non memory layer output
2023 destination = QDir::tempPath() + "/create_feature_sink.tab";
2024 QString prevDest = destination;
2025 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2026 QVERIFY( sink.get() );
2027 f = QgsFeature( fields );
2028 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
2029 f.setAttributes( QgsAttributes() << "val" );
2030 QVERIFY( sink->addFeature( f ) );
2031 QCOMPARE( destination, prevDest );
2032 sink.reset( nullptr );
2033 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
2034 QVERIFY( layer->isValid() );
2035 QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
2036 QCOMPARE( layer->fields().size(), 1 );
2037 QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "my_field" ) );
2038 QCOMPARE( layer->fields().at( 0 ).type(), QVariant::String );
2039 QCOMPARE( layer->featureCount(), 1L );
2040
2041 // no extension, should default to shp
2042 destination = QDir::tempPath() + "/create_feature_sink2";
2043 prevDest = QDir::tempPath() + "/create_feature_sink2.gpkg";
2044 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::PointZ, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2045 QVERIFY( sink.get() );
2046 f = QgsFeature( fields );
2047 f.setAttributes( QgsAttributes() << "val" );
2048 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(1 2 3)" ) ) );
2049 QVERIFY( sink->addFeature( f ) );
2050 QCOMPARE( destination, prevDest );
2051 sink.reset( nullptr );
2052 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
2053 QCOMPARE( layer->wkbType(), QgsWkbTypes::PointZ );
2054 QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
2055 QCOMPARE( layer->fields().size(), 2 );
2056 QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "fid" ) );
2057 QCOMPARE( layer->fields().at( 1 ).name(), QStringLiteral( "my_field" ) );
2058 QCOMPARE( layer->fields().at( 1 ).type(), QVariant::String );
2059 QCOMPARE( layer->featureCount(), 1L );
2060 // append to existing OGR layer
2061 QgsRemappingSinkDefinition remapDef;
2062 remapDef.setDestinationFields( layer->fields() );
2063 remapDef.setDestinationCrs( layer->crs() );
2064 remapDef.setSourceCrs( QgsCoordinateReferenceSystem( "EPSG:4326" ) );
2065 remapDef.setDestinationWkbType( QgsWkbTypes::Polygon );
2066 remapDef.addMappedField( QStringLiteral( "my_field" ), QgsProperty::fromExpression( QStringLiteral( "field2 || @extra" ) ) );
2067 QgsFields fields2;
2068 fields2.append( QgsField( "field2", QVariant::String ) );
2069 context.expressionContext().appendScope( new QgsExpressionContextScope() );
2070 context.expressionContext().scope( 0 )->setVariable( QStringLiteral( "extra" ), 2 );
2071 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields2, QgsWkbTypes::Point, QgsCoordinateReferenceSystem::fromEpsgId( 4326 ), QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::SinkFlags(), &remapDef ) );
2072 QVERIFY( sink.get() );
2073 f = QgsFeature( fields2 );
2074 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Point(10 0)" ) ) );
2075 f.setAttributes( QgsAttributes() << "val" );
2076 QVERIFY( sink->addFeature( f ) );
2077 QCOMPARE( destination, prevDest );
2078 sink.reset( nullptr );
2079 layer = new QgsVectorLayer( destination );
2080 QVERIFY( layer->isValid() );
2081 QCOMPARE( layer->featureCount(), 2L );
2082 QgsFeatureIterator it = layer->getFeatures();
2083 QVERIFY( it.nextFeature( f ) );
2084 QCOMPARE( f.attributes().at( 1 ).toString(), QStringLiteral( "val" ) );
2085 QCOMPARE( f.geometry().asWkt( 1 ), QStringLiteral( "PointZ (1 2 3)" ) );
2086 QVERIFY( it.nextFeature( f ) );
2087 QCOMPARE( f.attributes().at( 1 ).toString(), QStringLiteral( "val2" ) );
2088 QCOMPARE( f.geometry().asWkt( 0 ), QStringLiteral( "Point (-10199761 -4017774)" ) );
2089 delete layer;
2090 //windows style path
2091 destination = "d:\\temp\\create_feature_sink.tab";
2092 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2093 QVERIFY( sink.get() );
2094
2095 // save to geopackage
2096 const QString geopackagePath = QDir::tempPath() + "/packaged.gpkg";
2097 if ( QFileInfo::exists( geopackagePath ) )
2098 QFile::remove( geopackagePath );
2099 destination = QStringLiteral( "ogr:dbname='%1' table=\"polygons\" (geom) sql=" ).arg( geopackagePath );
2100 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2101 QVERIFY( sink.get() );
2102 QCOMPARE( destination, QStringLiteral( "%1|layername=polygons" ).arg( geopackagePath ) );
2103 f = QgsFeature( fields );
2104 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
2105 f.setAttributes( QgsAttributes() << "val" );
2106 QVERIFY( sink->addFeature( f ) );
2107 sink.reset( nullptr );
2108 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
2109 QVERIFY( layer->isValid() );
2110 QCOMPARE( layer->wkbType(), QgsWkbTypes::Polygon );
2111 QVERIFY( layer->getFeatures().nextFeature( f ) );
2112 QCOMPARE( f.attribute( "my_field" ).toString(), QStringLiteral( "val" ) );
2113
2114 // add another output to the same geopackage
2115 QString destination2 = QStringLiteral( "ogr:dbname='%1' table=\"points\" (geom) sql=" ).arg( geopackagePath );
2116 sink.reset( QgsProcessingUtils::createFeatureSink( destination2, context, fields, QgsWkbTypes::Point, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2117 QVERIFY( sink.get() );
2118 QCOMPARE( destination2, QStringLiteral( "%1|layername=points" ).arg( geopackagePath ) );
2119 f = QgsFeature( fields );
2120 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Point(0 0)" ) ) );
2121 f.setAttributes( QgsAttributes() << "val2" );
2122 QVERIFY( sink->addFeature( f ) );
2123 sink.reset( nullptr );
2124 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination2, context, true ) );
2125 QVERIFY( layer->isValid() );
2126 QCOMPARE( layer->wkbType(), QgsWkbTypes::Point );
2127 QVERIFY( layer->getFeatures().nextFeature( f ) );
2128 QCOMPARE( f.attribute( "my_field" ).toString(), QStringLiteral( "val2" ) );
2129
2130 // original polygon layer should remain
2131 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
2132 QVERIFY( layer->isValid() );
2133 QCOMPARE( layer->wkbType(), QgsWkbTypes::Polygon );
2134 QVERIFY( layer->getFeatures().nextFeature( f ) );
2135 QCOMPARE( f.attribute( "my_field" ).toString(), QStringLiteral( "val" ) );
2136
2137 // now append to that second layer
2138 remapDef.setDestinationFields( layer->fields() );
2139 remapDef.setDestinationCrs( layer->crs() );
2140
2141 remapDef.setSourceCrs( QgsCoordinateReferenceSystem( "EPSG:4326" ) );
2142 remapDef.setDestinationWkbType( QgsWkbTypes::Point );
2143 remapDef.addMappedField( QStringLiteral( "my_field" ), QgsProperty::fromExpression( QStringLiteral( "field2 || @extra" ) ) );
2144 destination2 = QStringLiteral( "ogr:dbname='%1' table=\"points\" (geom) sql=" ).arg( geopackagePath );
2145 sink.reset( QgsProcessingUtils::createFeatureSink( destination2, context, fields2, QgsWkbTypes::PointZ, QgsCoordinateReferenceSystem::fromEpsgId( 4326 ), QVariantMap(), QStringList(), QStringList(), QgsFeatureSink::SinkFlags(), &remapDef ) );
2146 QVERIFY( sink.get() );
2147 f = QgsFeature( fields );
2148 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "PointZ(3 4 5)" ) ) );
2149 f.setAttributes( QgsAttributes() << "v" );
2150 QVERIFY( sink->addFeature( f ) );
2151 sink.reset( nullptr );
2152 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination2, context, true ) );
2153 QVERIFY( layer->isValid() );
2154 QCOMPARE( layer->wkbType(), QgsWkbTypes::Point );
2155 QCOMPARE( layer->featureCount(), 2L );
2156 QVERIFY( layer->getFeatures().nextFeature( f ) );
2157 QCOMPARE( f.attribute( "my_field" ).toString(), QStringLiteral( "val2" ) );
2158 }
2159
2160 #ifdef ENABLE_PGTEST
createFeatureSinkPostgres()2161 void TestQgsProcessing::createFeatureSinkPostgres()
2162 {
2163 QgsProcessingContext context;
2164
2165 QgsFields fields;
2166 fields.append( QgsField( QStringLiteral( "my_field" ), QVariant::String, QString(), 100 ) );
2167
2168 // save to database
2169 QString destination = "postgres://dbname='qgis_test' service='qgis_test' table=\"public\".\"test_feature_sink\" (geom)";
2170 std::unique_ptr< QgsFeatureSink > sink;
2171 try
2172 {
2173 sink.reset( QgsProcessingUtils::createFeatureSink( destination, context, fields, QgsWkbTypes::Polygon, QgsCoordinateReferenceSystem::fromEpsgId( 3111 ) ) );
2174 }
2175 catch ( QgsProcessingException &e )
2176 {
2177 QFAIL( "could not create layer -- perhaps postgres test db is not accessible?" );
2178 }
2179 QVERIFY( sink.get() );
2180 QgsFeature f = QgsFeature( fields );
2181 f.setGeometry( QgsGeometry::fromWkt( QStringLiteral( "Polygon((0 0, 0 1, 1 1, 1 0, 0 0 ))" ) ) );
2182 f.setAttributes( QgsAttributes() << "val" );
2183 QVERIFY( sink->addFeature( f ) );
2184 sink.reset( nullptr );
2185 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destination, context, true ) );
2186 QVERIFY( layer && layer->isValid() );
2187 QCOMPARE( layer->wkbType(), QgsWkbTypes::Polygon );
2188 QVERIFY( layer->getFeatures().nextFeature( f ) );
2189 QCOMPARE( f.attribute( "my_field" ).toString(), QStringLiteral( "val" ) );
2190 }
2191 #endif
2192
source()2193 void TestQgsProcessing::source()
2194 {
2195 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
2196 QgsVectorLayer *invalidLayer = new QgsVectorLayer( testDataDir + "invalidgeometries.gml", QString(), "ogr" );
2197 QVERIFY( invalidLayer->isValid() );
2198
2199 QgsProcessingContext context;
2200 context.setInvalidGeometryCheck( QgsFeatureRequest::GeometryAbortOnInvalid );
2201 QgsProcessingFeatureSource source( invalidLayer, context );
2202 // expect an exception, we should be using the context's "abort on invalid" setting
2203 QgsFeatureIterator it = source.getFeatures();
2204 QgsFeature f;
2205 try
2206 {
2207 it.nextFeature( f );
2208 QVERIFY( false );
2209 }
2210 catch ( QgsProcessingException & )
2211 {
2212
2213 }
2214
2215 // now try with a source overriding the context's setting
2216 source.setInvalidGeometryCheck( QgsFeatureRequest::GeometryNoCheck );
2217 it = source.getFeatures();
2218 QVERIFY( it.nextFeature( f ) );
2219 QVERIFY( !f.geometry().isGeosValid() );
2220 // all good!
2221
2222 QgsVectorLayer *polygonLayer = new QgsVectorLayer( testDataDir + "polys.shp", QString(), "ogr" );
2223 QVERIFY( polygonLayer->isValid() );
2224
2225 const QgsProcessingFeatureSource source2( polygonLayer, context );
2226 QCOMPARE( source2.featureCount(), 10L );
2227 int i = 0;
2228 it = source2.getFeatures();
2229 while ( it.nextFeature( f ) )
2230 i++;
2231 QCOMPARE( i, 10 );
2232
2233 // now with a limit on features
2234 const QgsProcessingFeatureSource source3( polygonLayer, context, false, 5 );
2235 QCOMPARE( source3.featureCount(), 5L );
2236 i = 0;
2237 it = source3.getFeatures();
2238 while ( it.nextFeature( f ) )
2239 i++;
2240 QCOMPARE( i, 5 );
2241
2242 // feature request has a lower limit than source
2243 it = source3.getFeatures( QgsFeatureRequest().setLimit( 2 ) );
2244 i = 0;
2245 while ( it.nextFeature( f ) )
2246 i++;
2247 QCOMPARE( i, 2 );
2248
2249 // feature request has a higher limit than source
2250 it = source3.getFeatures( QgsFeatureRequest().setLimit( 12 ) );
2251 i = 0;
2252 while ( it.nextFeature( f ) )
2253 i++;
2254 QCOMPARE( i, 5 );
2255
2256 }
2257
parameters()2258 void TestQgsProcessing::parameters()
2259 {
2260 // test parameter utilities
2261
2262 std::unique_ptr< QgsProcessingParameterDefinition > def;
2263
2264 QVariantMap params;
2265 params.insert( QStringLiteral( "prop" ), QgsProperty::fromField( "a_field" ) );
2266 params.insert( QStringLiteral( "string" ), QStringLiteral( "a string" ) );
2267 params.insert( QStringLiteral( "double" ), 5.2 );
2268 params.insert( QStringLiteral( "int" ), 15 );
2269 params.insert( QStringLiteral( "ints" ), QVariant( QList<QVariant>() << 3 << 2 << 1 ) );
2270 params.insert( QStringLiteral( "bool" ), true );
2271
2272 QgsProcessingContext context;
2273
2274 // isDynamic
2275 QVERIFY( QgsProcessingParameters::isDynamic( params, QStringLiteral( "prop" ) ) );
2276 QVERIFY( !QgsProcessingParameters::isDynamic( params, QStringLiteral( "string" ) ) );
2277 QVERIFY( !QgsProcessingParameters::isDynamic( params, QStringLiteral( "bad" ) ) );
2278
2279 // parameterAsString
2280 def.reset( new QgsProcessingParameterString( QStringLiteral( "string" ), QStringLiteral( "desc" ) ) );
2281 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QStringLiteral( "a string" ) );
2282 def->setName( QStringLiteral( "double" ) );
2283 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ).left( 3 ), QStringLiteral( "5.2" ) );
2284 def->setName( QStringLiteral( "int" ) );
2285 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QStringLiteral( "15" ) );
2286 def->setName( QStringLiteral( "bool" ) );
2287 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QStringLiteral( "true" ) );
2288 def->setName( QStringLiteral( "bad" ) );
2289 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
2290
2291 def->setIsDynamic( true );
2292 QVERIFY( def->isDynamic() );
2293 def->setDynamicPropertyDefinition( QgsPropertyDefinition( QStringLiteral( "Distance" ), QObject::tr( "Buffer distance" ), QgsPropertyDefinition::Double ) );
2294 QCOMPARE( def->dynamicPropertyDefinition().name(), QStringLiteral( "Distance" ) );
2295 def->setDynamicLayerParameterName( "parent" );
2296 QCOMPARE( def->dynamicLayerParameterName(), QStringLiteral( "parent" ) );
2297
2298 // string with dynamic property (feature not set)
2299 def->setName( QStringLiteral( "prop" ) );
2300 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
2301
2302 // correctly setup feature
2303 QgsFields fields;
2304 fields.append( QgsField( "a_field", QVariant::String, QString(), 30 ) );
2305 QgsFeature f( fields );
2306 f.setAttribute( 0, QStringLiteral( "field value" ) );
2307 context.expressionContext().setFeature( f );
2308 context.expressionContext().setFields( fields );
2309 def->setName( QStringLiteral( "prop" ) );
2310 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QStringLiteral( "field value" ) );
2311
2312 // as double
2313 def->setName( QStringLiteral( "double" ) );
2314 QCOMPARE( QgsProcessingParameters::parameterAsDouble( def.get(), params, context ), 5.2 );
2315 def->setName( QStringLiteral( "int" ) );
2316 QCOMPARE( QgsProcessingParameters::parameterAsDouble( def.get(), params, context ), 15.0 );
2317 f.setAttribute( 0, QStringLiteral( "6.2" ) );
2318 context.expressionContext().setFeature( f );
2319 def->setName( QStringLiteral( "prop" ) );
2320 QCOMPARE( QgsProcessingParameters::parameterAsDouble( def.get(), params, context ), 6.2 );
2321
2322 // as int
2323 def->setName( QStringLiteral( "double" ) );
2324 QCOMPARE( QgsProcessingParameters::parameterAsInt( def.get(), params, context ), 5 );
2325 def->setName( QStringLiteral( "int" ) );
2326 QCOMPARE( QgsProcessingParameters::parameterAsInt( def.get(), params, context ), 15 );
2327 def->setName( QStringLiteral( "prop" ) );
2328 QCOMPARE( QgsProcessingParameters::parameterAsInt( def.get(), params, context ), 6 );
2329
2330 // as ints
2331 def->setName( QStringLiteral( "int" ) );
2332 QCOMPARE( QgsProcessingParameters::parameterAsInts( def.get(), params, context ), QList<int>() << 15 );
2333 def->setName( QStringLiteral( "ints" ) );
2334 QCOMPARE( QgsProcessingParameters::parameterAsInts( def.get(), params, context ), QList<int>() << 3 << 2 << 1 );
2335
2336 // as bool
2337 def->setName( QStringLiteral( "double" ) );
2338 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2339 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2340 def->setName( QStringLiteral( "int" ) );
2341 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2342 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2343 def->setName( QStringLiteral( "bool" ) );
2344 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2345 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2346 def->setName( QStringLiteral( "prop" ) );
2347 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2348 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2349 f.setAttribute( 0, false );
2350 context.expressionContext().setFeature( f );
2351 def->setName( QStringLiteral( "prop" ) );
2352 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2353 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2354
2355 // as layer
2356 def->setName( QStringLiteral( "double" ) );
2357 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2358 def->setName( QStringLiteral( "int" ) );
2359 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2360 def->setName( QStringLiteral( "bool" ) );
2361 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2362 def->setName( QStringLiteral( "prop" ) );
2363 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2364
2365 QVERIFY( context.temporaryLayerStore()->mapLayers().isEmpty() );
2366 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
2367 f.setAttribute( 0, QString( testDataDir + "/raster/band1_float32_noct_epsg4326.tif" ) );
2368 context.expressionContext().setFeature( f );
2369 def->setName( QStringLiteral( "prop" ) );
2370 QVERIFY( QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2371 // make sure layer was loaded
2372 QVERIFY( !context.temporaryLayerStore()->mapLayers().isEmpty() );
2373
2374 // parameters as sinks
2375
2376 const QgsWkbTypes::Type wkbType = QgsWkbTypes::PolygonM;
2377 QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem( QStringLiteral( "epsg:3111" ) );
2378 QString destId;
2379 def->setName( QStringLiteral( "string" ) );
2380 params.insert( QStringLiteral( "string" ), QStringLiteral( "memory:mem" ) );
2381 std::unique_ptr< QgsFeatureSink > sink;
2382 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, fields, wkbType, crs, context, destId ) );
2383 QVERIFY( sink.get() );
2384 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destId, context ) );
2385 QVERIFY( layer );
2386 QVERIFY( layer->isValid() );
2387 QCOMPARE( layer->fields().count(), 1 );
2388 QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "a_field" ) );
2389 QCOMPARE( layer->wkbType(), wkbType );
2390 QCOMPARE( layer->crs(), crs );
2391
2392 // property defined sink destination
2393 params.insert( QStringLiteral( "prop" ), QgsProperty::fromExpression( "'memory:mem2'" ) );
2394 def->setName( QStringLiteral( "prop" ) );
2395 crs = QgsCoordinateReferenceSystem( QStringLiteral( "epsg:3113" ) );
2396 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, fields, wkbType, crs, context, destId ) );
2397 QVERIFY( sink.get() );
2398 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destId, context ) );
2399 QVERIFY( layer );
2400 QVERIFY( layer->isValid() );
2401 QCOMPARE( layer->fields().count(), 1 );
2402 QCOMPARE( layer->fields().at( 0 ).name(), QStringLiteral( "a_field" ) );
2403 QCOMPARE( layer->wkbType(), wkbType );
2404 QCOMPARE( layer->crs(), crs );
2405
2406 // QgsProcessingFeatureSinkDefinition as parameter
2407 QgsProject p;
2408 QgsProcessingOutputLayerDefinition fs( QStringLiteral( "test.shp" ) );
2409 fs.destinationProject = &p;
2410 QVERIFY( context.layersToLoadOnCompletion().isEmpty() );
2411 params.insert( QStringLiteral( "fs" ), QVariant::fromValue( fs ) );
2412 def->setName( QStringLiteral( "fs" ) );
2413 crs = QgsCoordinateReferenceSystem( QStringLiteral( "epsg:28356" ) );
2414 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, fields, wkbType, crs, context, destId ) );
2415 QVERIFY( sink.get() );
2416 QgsVectorFileWriter *writer = dynamic_cast< QgsVectorFileWriter *>( dynamic_cast< QgsProcessingFeatureSink * >( sink.get() )->destinationSink() );
2417 QVERIFY( writer );
2418 layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( destId, context ) );
2419 QVERIFY( layer );
2420 QVERIFY( layer->isValid() );
2421 QCOMPARE( layer->wkbType(), QgsWkbTypes::MultiPolygonM ); // shapefile Polygon[XX] get promoted to Multi
2422 QCOMPARE( layer->crs(), crs );
2423
2424 // make sure layer was automatically added to list to load on completion
2425 QCOMPARE( context.layersToLoadOnCompletion().size(), 1 );
2426 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), destId );
2427 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "desc" ) );
2428
2429 // with name overloading
2430 QgsProcessingContext context2;
2431 fs = QgsProcessingOutputLayerDefinition( QStringLiteral( "test.shp" ) );
2432 fs.destinationProject = &p;
2433 fs.destinationName = QStringLiteral( "my_dest" );
2434 params.insert( QStringLiteral( "fs" ), QVariant::fromValue( fs ) );
2435 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, fields, wkbType, crs, context2, destId ) );
2436 QVERIFY( sink.get() );
2437 QCOMPARE( context2.layersToLoadOnCompletion().size(), 1 );
2438 QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), destId );
2439 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) );
2440 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "fs" ) );
2441
2442 // setting layer name to match...
2443 context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( nullptr );
2444 std::unique_ptr< QgsVectorLayer > vl = std::make_unique< QgsVectorLayer >( QStringLiteral( "Point" ), QString(), QStringLiteral( "memory" ) );
2445 QVERIFY( vl->isValid() );
2446 context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( vl.get() );
2447 // temporary layer, must use output name as layer name
2448 QCOMPARE( vl->name(), QStringLiteral( "my_dest" ) );
2449 // otherwise expect to use path
2450 std::unique_ptr< QgsRasterLayer > rl = std::make_unique< QgsRasterLayer >( QStringLiteral( TEST_DATA_DIR ) + "/landsat.tif", QString() );
2451 context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( rl.get() );
2452 QCOMPARE( rl->name(), QStringLiteral( "landsat" ) );
2453 // unless setting prohibits it...
2454 QgsProcessing::settingsPreferFilenameAsLayerName.setValue( false );
2455 context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( rl.get() );
2456 QCOMPARE( rl->name(), QStringLiteral( "my_dest" ) );
2457 // if layer has a layername, we should use that instead of the base file name...
2458 QgsProcessing::settingsPreferFilenameAsLayerName.setValue( true );
2459 vl = std::make_unique< QgsVectorLayer >( QStringLiteral( TEST_DATA_DIR ) + "/points_gpkg.gpkg|layername=points_small", QString() );
2460 context2.layersToLoadOnCompletion().values().at( 0 ).setOutputLayerName( vl.get() );
2461 QCOMPARE( vl->name(), QStringLiteral( "points_small" ) );
2462 // if forced name is true, that should always be used, regardless of the user's local setting
2463 QgsProcessingContext::LayerDetails details( QStringLiteral( "my name" ), context2.project(), QStringLiteral( "my name" ) );
2464 details.forceName = false;
2465 details.setOutputLayerName( vl.get() );
2466 QCOMPARE( vl->name(), QStringLiteral( "points_small" ) );
2467 details.forceName = true;
2468 details.setOutputLayerName( vl.get() );
2469 QCOMPARE( vl->name(), QStringLiteral( "my name" ) );
2470 }
2471
algorithmParameters()2472 void TestQgsProcessing::algorithmParameters()
2473 {
2474 DummyAlgorithm *alg = new DummyAlgorithm( "test" );
2475 DummyProvider p( "test" );
2476 alg->runParameterChecks();
2477
2478 p.addAlgorithm( alg );
2479 alg->runParameterChecks2();
2480 }
2481
algorithmOutputs()2482 void TestQgsProcessing::algorithmOutputs()
2483 {
2484 DummyAlgorithm alg( "test" );
2485 alg.runOutputChecks();
2486 }
2487
parameterGeneral()2488 void TestQgsProcessing::parameterGeneral()
2489 {
2490 // test constructor
2491 QgsProcessingParameterBoolean param( "p1", "desc", true, true );
2492 QCOMPARE( param.name(), QString( "p1" ) );
2493 QCOMPARE( param.description(), QString( "desc" ) );
2494 QCOMPARE( param.defaultValue(), QVariant( true ) );
2495 QVERIFY( param.flags() & QgsProcessingParameterDefinition::FlagOptional );
2496 QVERIFY( param.dependsOnOtherParameters().isEmpty() );
2497 QVERIFY( param.help().isEmpty() );
2498
2499 // test getters and setters
2500 param.setDescription( "p2" );
2501 QCOMPARE( param.description(), QString( "p2" ) );
2502 param.setDefaultValue( false );
2503 QCOMPARE( param.defaultValue(), QVariant( false ) );
2504 param.setFlags( QgsProcessingParameterDefinition::FlagHidden );
2505 QCOMPARE( param.flags(), QgsProcessingParameterDefinition::FlagHidden );
2506 param.setDefaultValue( true );
2507 QCOMPARE( param.defaultValue(), QVariant( true ) );
2508 QCOMPARE( param.defaultValueForGui(), QVariant( true ) );
2509 QVERIFY( !param.guiDefaultValueOverride().isValid() );
2510 param.setGuiDefaultValueOverride( false );
2511 QCOMPARE( param.guiDefaultValueOverride(), QVariant( false ) );
2512 QCOMPARE( param.defaultValueForGui(), QVariant( false ) );
2513
2514 param.setDefaultValue( QVariant() );
2515 QCOMPARE( param.defaultValue(), QVariant() );
2516 param.setHelp( QStringLiteral( "my help" ) );
2517 QCOMPARE( param.help(), QStringLiteral( "my help" ) );
2518
2519 QVariantMap metadata;
2520 metadata.insert( "p1", 5 );
2521 metadata.insert( "p2", 7 );
2522 param.setMetadata( metadata );
2523 QCOMPARE( param.metadata(), metadata );
2524 param.metadata().insert( "p3", 9 );
2525 QCOMPARE( param.metadata().value( "p3" ).toInt(), 9 );
2526
2527 QVERIFY( param.additionalExpressionContextVariables().isEmpty() );
2528 param.setAdditionalExpressionContextVariables( QStringList() << "a" << "b" );
2529 QCOMPARE( param.additionalExpressionContextVariables(), QStringList() << "a" << "b" );
2530 std::unique_ptr< QgsProcessingParameterDefinition > param2( param.clone() );
2531 QCOMPARE( param2->guiDefaultValueOverride(), param.guiDefaultValueOverride() );
2532 QCOMPARE( param2->additionalExpressionContextVariables(), QStringList() << "a" << "b" );
2533
2534 const QVariantMap map = param.toVariantMap();
2535 QgsProcessingParameterBoolean fromMap( "x" );
2536 QVERIFY( fromMap.fromVariantMap( map ) );
2537 QCOMPARE( fromMap.name(), param.name() );
2538 QCOMPARE( fromMap.description(), param.description() );
2539 QCOMPARE( fromMap.flags(), param.flags() );
2540 QCOMPARE( fromMap.defaultValue(), param.defaultValue() );
2541 QCOMPARE( fromMap.guiDefaultValueOverride(), param.guiDefaultValueOverride() );
2542 QCOMPARE( fromMap.metadata(), param.metadata() );
2543 QCOMPARE( fromMap.help(), QStringLiteral( "my help" ) );
2544
2545 // escaping quotes
2546 param = QgsProcessingParameterBoolean( "param_name", "Param's name" );
2547 QString pythonCode = param.asPythonString();
2548 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('param_name', \"Param's name\", defaultValue=None)" ) );
2549
2550 param = QgsProcessingParameterBoolean( "param_name", "Param\"s name" );
2551 pythonCode = param.asPythonString();
2552 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('param_name', 'Param\"s name', defaultValue=None)" ) );
2553
2554 param = QgsProcessingParameterBoolean( "param_name", "Param's \" name" );
2555 pythonCode = param.asPythonString();
2556 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('param_name', 'Param\\'s \" name', defaultValue=None)" ) );
2557 }
2558
parameterBoolean()2559 void TestQgsProcessing::parameterBoolean()
2560 {
2561 QgsProcessingContext context;
2562
2563 // test no def
2564 QVariantMap params;
2565 params.insert( "no_def", false );
2566 QCOMPARE( QgsProcessingParameters::parameterAsBool( nullptr, params, context ), false );
2567 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( nullptr, params, context ), false );
2568 params.insert( "no_def", "false" );
2569 QCOMPARE( QgsProcessingParameters::parameterAsBool( nullptr, params, context ), false );
2570 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( nullptr, params, context ), false );
2571 params.insert( "no_def", QVariant() );
2572 QCOMPARE( QgsProcessingParameters::parameterAsBool( nullptr, params, context ), false );
2573 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( nullptr, params, context ), false );
2574 params.remove( "no_def" );
2575 QCOMPARE( QgsProcessingParameters::parameterAsBool( nullptr, params, context ), false );
2576 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( nullptr, params, context ), false );
2577
2578 // with defs
2579
2580 std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterBoolean( "non_optional_default_false" ) );
2581 QVERIFY( def->checkValueIsAcceptable( false ) );
2582 QVERIFY( def->checkValueIsAcceptable( true ) );
2583 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2584 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2585 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
2586
2587 params.insert( "non_optional_default_false", false );
2588 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2589 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2590 params.insert( "non_optional_default_false", true );
2591 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2592 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2593 params.insert( "non_optional_default_false", "true" );
2594 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2595 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2596 params.insert( "non_optional_default_false", "false" );
2597 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2598 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2599
2600 //non-optional - behavior is undefined, but internally default to false
2601 params.insert( "non_optional_default_false", QVariant() );
2602 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2603 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2604 params.remove( "non_optional_default_false" );
2605 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2606 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2607
2608 QCOMPARE( def->valueAsPythonString( false, context ), QStringLiteral( "False" ) );
2609 QCOMPARE( def->valueAsPythonString( true, context ), QStringLiteral( "True" ) );
2610 QCOMPARE( def->valueAsPythonString( "false", context ), QStringLiteral( "False" ) );
2611 QCOMPARE( def->valueAsPythonString( "true", context ), QStringLiteral( "True" ) );
2612 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
2613 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
2614
2615 QString pythonCode = def->asPythonString();
2616 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('non_optional_default_false', '', defaultValue=None)" ) );
2617
2618 QString code = def->asScriptCode();
2619 QCOMPARE( code, QStringLiteral( "##non_optional_default_false=boolean false" ) );
2620 std::unique_ptr< QgsProcessingParameterBoolean > fromCode( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2621 QVERIFY( fromCode.get() );
2622 QCOMPARE( fromCode->name(), def->name() );
2623 QCOMPARE( fromCode->description(), QStringLiteral( "non optional default false" ) );
2624 QCOMPARE( fromCode->flags(), def->flags() );
2625 QCOMPARE( fromCode->defaultValue().toBool(), false );
2626
2627 const QVariantMap map = def->toVariantMap();
2628 QgsProcessingParameterBoolean fromMap( "x" );
2629 QVERIFY( fromMap.fromVariantMap( map ) );
2630 QCOMPARE( fromMap.name(), def->name() );
2631 QCOMPARE( fromMap.description(), def->description() );
2632 QCOMPARE( fromMap.flags(), def->flags() );
2633 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
2634 def.reset( QgsProcessingParameters::parameterFromVariantMap( map ) );
2635 QVERIFY( dynamic_cast< QgsProcessingParameterBoolean *>( def.get() ) );
2636
2637
2638 def.reset( new QgsProcessingParameterBoolean( "optional_default_true", QString(), true, true ) );
2639
2640 QVERIFY( def->checkValueIsAcceptable( false ) );
2641 QVERIFY( def->checkValueIsAcceptable( true ) );
2642 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2643 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2644 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
2645
2646 params.insert( "optional_default_true", false );
2647 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2648 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2649 params.insert( "optional_default_true", true );
2650 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2651 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2652 params.insert( "optional_default_true", "true" );
2653 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2654 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2655 params.insert( "optional_default_true", "false" );
2656 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2657 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2658 //optional - should be default
2659 params.insert( "optional_default_true", QVariant() );
2660 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2661 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2662 params.remove( "optional_default_true" );
2663 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2664 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2665
2666 pythonCode = def->asPythonString();
2667 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('optional_default_true', '', optional=True, defaultValue=True)" ) );
2668
2669 code = def->asScriptCode();
2670 QCOMPARE( code, QStringLiteral( "##optional_default_true=optional boolean true" ) );
2671 fromCode.reset( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2672 QVERIFY( fromCode.get() );
2673 QCOMPARE( fromCode->name(), def->name() );
2674 QCOMPARE( fromCode->description(), QStringLiteral( "optional default true" ) );
2675 QCOMPARE( fromCode->flags(), def->flags() );
2676 QCOMPARE( fromCode->defaultValue().toBool(), true );
2677
2678 def.reset( new QgsProcessingParameterBoolean( "optional_default_false", QString(), false, true ) );
2679
2680 QVERIFY( def->checkValueIsAcceptable( false ) );
2681 QVERIFY( def->checkValueIsAcceptable( true ) );
2682 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2683 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2684 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
2685 QVERIFY( def->checkValueIsAcceptable( false ) );
2686 QVERIFY( def->checkValueIsAcceptable( true ) );
2687 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2688 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2689 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
2690
2691 params.insert( "optional_default_false", false );
2692 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2693 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2694 params.insert( "optional_default_false", true );
2695 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2696 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2697 params.insert( "optional_default_false", "true" );
2698 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2699 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2700 params.insert( "optional_default_false", "false" );
2701 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2702 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2703 //optional - should be default
2704 params.insert( "optional_default_false", QVariant() );
2705 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2706 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2707 params.remove( "optional_default_false" );
2708 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2709 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2710
2711 pythonCode = def->asPythonString();
2712 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('optional_default_false', '', optional=True, defaultValue=False)" ) );
2713
2714 code = def->asScriptCode();
2715 fromCode.reset( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2716 QVERIFY( fromCode.get() );
2717 QCOMPARE( fromCode->name(), def->name() );
2718 QCOMPARE( fromCode->description(), QStringLiteral( "optional default false" ) );
2719 QCOMPARE( fromCode->flags(), def->flags() );
2720 QCOMPARE( fromCode->defaultValue().toBool(), false );
2721
2722 def.reset( new QgsProcessingParameterBoolean( "non_optional_default_true", QString(), true, false ) );
2723
2724 QVERIFY( def->checkValueIsAcceptable( false ) );
2725 QVERIFY( def->checkValueIsAcceptable( true ) );
2726 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2727 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2728 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, because it falls back to default value
2729
2730 params.insert( "non_optional_default_true", false );
2731 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2732 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2733 params.insert( "non_optional_default_true", true );
2734 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2735 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2736 params.insert( "non_optional_default_true", "true" );
2737 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2738 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2739 params.insert( "non_optional_default_true", "false" );
2740 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), false );
2741 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), false );
2742 //non-optional - behavior is undefined, but internally fallback to default
2743 params.insert( "non_optional_default_true", QVariant() );
2744 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2745 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2746 params.remove( "non_optional_default_true" );
2747 QCOMPARE( QgsProcessingParameters::parameterAsBool( def.get(), params, context ), true );
2748 QCOMPARE( QgsProcessingParameters::parameterAsBoolean( def.get(), params, context ), true );
2749
2750 pythonCode = def->asPythonString();
2751 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBoolean('non_optional_default_true', '', defaultValue=True)" ) );
2752
2753 code = def->asScriptCode();
2754 fromCode.reset( dynamic_cast< QgsProcessingParameterBoolean * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2755 QVERIFY( fromCode.get() );
2756 QCOMPARE( fromCode->name(), def->name() );
2757 QCOMPARE( fromCode->description(), QStringLiteral( "non optional default true" ) );
2758 QCOMPARE( fromCode->flags(), def->flags() );
2759 QCOMPARE( fromCode->defaultValue().toBool(), true );
2760
2761 def.reset( new QgsProcessingParameterBoolean( "non_optional_no_default", QString(), QVariant(), false ) );
2762
2763 QVERIFY( def->checkValueIsAcceptable( false ) );
2764 QVERIFY( def->checkValueIsAcceptable( true ) );
2765 QVERIFY( def->checkValueIsAcceptable( "false" ) );
2766 QVERIFY( def->checkValueIsAcceptable( "true" ) );
2767 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, because it falls back to invalid default value
2768 }
2769
parameterCrs()2770 void TestQgsProcessing::parameterCrs()
2771 {
2772 // setup a context
2773 QgsProject p;
2774 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
2775 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
2776 const QString raster1 = testDataDir + "landsat_4326.tif";
2777 const QString raster2 = testDataDir + "landsat.tif";
2778 const QFileInfo fi1( raster1 );
2779 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
2780 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" );
2781 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 );
2782 QgsProcessingContext context;
2783 context.setProject( &p );
2784
2785 // not optional!
2786 std::unique_ptr< QgsProcessingParameterCrs > def( new QgsProcessingParameterCrs( "non_optional", QString(), QString( "EPSG:3113" ), false ) );
2787 QVERIFY( !def->checkValueIsAcceptable( false ) );
2788 QVERIFY( !def->checkValueIsAcceptable( true ) );
2789 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
2790 QVERIFY( def->checkValueIsAcceptable( "EPSG:12003" ) );
2791 QVERIFY( def->checkValueIsAcceptable( "EPSG:3111" ) );
2792 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
2793 QVERIFY( def->checkValueIsAcceptable( QgsCoordinateReferenceSystem() ) );
2794 QVERIFY( def->checkValueIsAcceptable( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ) );
2795 QVERIFY( !def->checkValueIsAcceptable( "" ) );
2796 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
2797 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( r1->id() ) ) );
2798 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( QVariant::fromValue( r1 ) ) ) ) );
2799 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( r1->id() ) ) );
2800
2801 // using map layer
2802 QVariantMap params;
2803 params.insert( "non_optional", v1->id() );
2804 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3111" ) );
2805 QVERIFY( def->checkValueIsAcceptable( v1->id() ) );
2806 params.insert( "non_optional", QVariant::fromValue( v1 ) );
2807 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3111" ) );
2808
2809 // using QgsCoordinateReferenceSystem
2810 params.insert( "non_optional", QgsCoordinateReferenceSystem( "EPSG:28356" ) );
2811 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:28356" ) );
2812 params.insert( "non_optional", QgsCoordinateReferenceSystem() );
2813 QVERIFY( !QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).isValid() );
2814
2815 // special ProjectCrs string
2816 params.insert( "non_optional", QStringLiteral( "ProjectCrs" ) );
2817 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:28353" ) );
2818 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "ProjectCrs" ) ) );
2819
2820 // string representing a project layer source
2821 params.insert( "non_optional", raster1 );
2822 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:4326" ) );
2823 QVERIFY( def->checkValueIsAcceptable( raster1 ) );
2824
2825 // string representing a non-project layer source
2826 params.insert( "non_optional", raster2 );
2827 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:32633" ) );
2828 QVERIFY( def->checkValueIsAcceptable( raster2 ) );
2829
2830 // string representation of a crs
2831 params.insert( "non_optional", QString( "EPSG:28355" ) );
2832 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:28355" ) );
2833 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "EPSG:28355" ) ) );
2834
2835 // nonsense string
2836 params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
2837 QVERIFY( !QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).isValid() );
2838
2839 // using feature source definition
2840 params.insert( "non_optional", QgsProcessingFeatureSourceDefinition( v1->id() ) );
2841 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3111" ) );
2842 params.insert( "non_optional", QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( QVariant::fromValue( v1 ) ) ) );
2843 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3111" ) );
2844 params.insert( "non_optional", QgsProcessingOutputLayerDefinition( v1->id() ) );
2845 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3111" ) );
2846
2847 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
2848 QCOMPARE( def->valueAsPythonString( QgsCoordinateReferenceSystem( "EPSG:3111" ), context ), QStringLiteral( "QgsCoordinateReferenceSystem('EPSG:3111')" ) );
2849 QCOMPARE( def->valueAsPythonString( QgsCoordinateReferenceSystem(), context ), QStringLiteral( "QgsCoordinateReferenceSystem()" ) );
2850 QCOMPARE( def->valueAsPythonString( "EPSG:12003", context ), QStringLiteral( "'EPSG:12003'" ) );
2851 QCOMPARE( def->valueAsPythonString( "ProjectCrs", context ), QStringLiteral( "'ProjectCrs'" ) );
2852 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
2853 QCOMPARE( def->valueAsPythonString( raster1, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "landsat_4326.tif'" ) ) );
2854 QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "landsat_4326.tif'" ) ) );
2855 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
2856 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
2857
2858 const QVariantMap map = def->toVariantMap();
2859 QgsProcessingParameterCrs fromMap( "x" );
2860 QVERIFY( fromMap.fromVariantMap( map ) );
2861 QCOMPARE( fromMap.name(), def->name() );
2862 QCOMPARE( fromMap.description(), def->description() );
2863 QCOMPARE( fromMap.flags(), def->flags() );
2864 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
2865 def.reset( dynamic_cast< QgsProcessingParameterCrs *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
2866 QVERIFY( dynamic_cast< QgsProcessingParameterCrs *>( def.get() ) );
2867
2868 QString pythonCode = def->asPythonString();
2869 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCrs('non_optional', '', defaultValue='EPSG:3113')" ) );
2870
2871 QString code = def->asScriptCode();
2872 QCOMPARE( code, QStringLiteral( "##non_optional=crs EPSG:3113" ) );
2873 std::unique_ptr< QgsProcessingParameterCrs > fromCode( dynamic_cast< QgsProcessingParameterCrs * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2874 QVERIFY( fromCode.get() );
2875 QCOMPARE( fromCode->name(), def->name() );
2876 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
2877 QCOMPARE( fromCode->flags(), def->flags() );
2878 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
2879
2880 // optional
2881 def.reset( new QgsProcessingParameterCrs( "optional", QString(), QString( "EPSG:3113" ), true ) );
2882 params.insert( "optional", QVariant() );
2883 QCOMPARE( QgsProcessingParameters::parameterAsCrs( def.get(), params, context ).authid(), QString( "EPSG:3113" ) );
2884 QVERIFY( def->checkValueIsAcceptable( false ) );
2885 QVERIFY( def->checkValueIsAcceptable( true ) );
2886 QVERIFY( def->checkValueIsAcceptable( 5 ) );
2887 QVERIFY( def->checkValueIsAcceptable( "EPSG:12003" ) );
2888 QVERIFY( def->checkValueIsAcceptable( "EPSG:3111" ) );
2889 QVERIFY( def->checkValueIsAcceptable( "" ) );
2890 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
2891
2892 pythonCode = def->asPythonString();
2893 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCrs('optional', '', optional=True, defaultValue='EPSG:3113')" ) );
2894
2895 code = def->asScriptCode();
2896 QCOMPARE( code, QStringLiteral( "##optional=optional crs EPSG:3113" ) );
2897 fromCode.reset( dynamic_cast< QgsProcessingParameterCrs * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2898 QVERIFY( fromCode.get() );
2899 QCOMPARE( fromCode->name(), def->name() );
2900 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
2901 QCOMPARE( fromCode->flags(), def->flags() );
2902 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
2903
2904 code = QStringLiteral( "##optional=optional crs None" );
2905 fromCode.reset( dynamic_cast< QgsProcessingParameterCrs * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2906 QVERIFY( fromCode.get() );
2907 QCOMPARE( fromCode->name(), def->name() );
2908 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
2909 QCOMPARE( fromCode->flags(), def->flags() );
2910 QVERIFY( !fromCode->defaultValue().isValid() );
2911 }
2912
parameterMapLayer()2913 void TestQgsProcessing::parameterMapLayer()
2914 {
2915 // setup a context
2916 QgsProject p;
2917 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
2918 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
2919 const QString raster1 = testDataDir + "tenbytenraster.asc";
2920 const QString raster2 = testDataDir + "landsat.tif";
2921 const QFileInfo fi1( raster1 );
2922 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
2923 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" );
2924 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 );
2925 QgsProcessingContext context;
2926 context.setProject( &p );
2927
2928 // not optional!
2929 std::unique_ptr< QgsProcessingParameterMapLayer > def( new QgsProcessingParameterMapLayer( "non_optional", QString(), QString(), false ) );
2930 QVERIFY( !def->checkValueIsAcceptable( false ) );
2931 QVERIFY( !def->checkValueIsAcceptable( true ) );
2932 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
2933 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
2934 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
2935 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
2936 QVERIFY( !def->checkValueIsAcceptable( "" ) );
2937 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
2938
2939 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
2940 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
2941 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
2942 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
2943
2944 // should be OK
2945 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
2946 // ... unless we use context, when the check that the layer actually exists is performed
2947 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
2948
2949 // using existing map layer ID
2950 QVariantMap params;
2951 params.insert( "non_optional", v1->id() );
2952 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context )->id(), v1->id() );
2953 QVERIFY( def->checkValueIsAcceptable( v1->id() ) );
2954 QVERIFY( def->checkValueIsAcceptable( v1->id(), &context ) );
2955
2956 // string representing a project layer source
2957 params.insert( "non_optional", raster1 );
2958 QVERIFY( def->checkValueIsAcceptable( raster1 ) );
2959 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context )->id(), r1->id() );
2960 // string representing a non-project layer source
2961 params.insert( "non_optional", raster2 );
2962 QVERIFY( def->checkValueIsAcceptable( raster2 ) );
2963 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context )->publicSource(), raster2 );
2964
2965 // nonsense string
2966 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
2967 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
2968
2969 // layer
2970 params.insert( "non_optional", QVariant::fromValue( r1 ) );
2971 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context ), r1 );
2972 params.insert( "non_optional", QVariant::fromValue( v1 ) );
2973 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context ), v1 );
2974
2975 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
2976 QCOMPARE( def->valueAsPythonString( raster1, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
2977 QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
2978 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
2979 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
2980 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
2981
2982 QString pythonCode = def->asPythonString();
2983 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='')" ) );
2984
2985 QString code = def->asScriptCode();
2986 QCOMPARE( code, QStringLiteral( "##non_optional=layer" ) );
2987 std::unique_ptr< QgsProcessingParameterMapLayer > fromCode( dynamic_cast< QgsProcessingParameterMapLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
2988 QVERIFY( fromCode.get() );
2989 QCOMPARE( fromCode->name(), def->name() );
2990 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
2991 QCOMPARE( fromCode->flags(), def->flags() );
2992 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
2993
2994 const QVariantMap map = def->toVariantMap();
2995 QgsProcessingParameterMapLayer fromMap( "x" );
2996 QVERIFY( fromMap.fromVariantMap( map ) );
2997 QCOMPARE( fromMap.name(), def->name() );
2998 QCOMPARE( fromMap.description(), def->description() );
2999 QCOMPARE( fromMap.flags(), def->flags() );
3000 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3001 def.reset( dynamic_cast< QgsProcessingParameterMapLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3002 QVERIFY( dynamic_cast< QgsProcessingParameterMapLayer *>( def.get() ) );
3003
3004 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint );
3005 pythonCode = def->asPythonString();
3006 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorPoint])" ) );
3007 code = def->asScriptCode();
3008 QCOMPARE( code, QStringLiteral( "##non_optional=layer point" ) );
3009 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorLine );
3010 pythonCode = def->asPythonString();
3011 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorLine])" ) );
3012 code = def->asScriptCode();
3013 QCOMPARE( code, QStringLiteral( "##non_optional=layer line" ) );
3014 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPolygon );
3015 pythonCode = def->asPythonString();
3016 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorPolygon])" ) );
3017 code = def->asScriptCode();
3018 QCOMPARE( code, QStringLiteral( "##non_optional=layer polygon" ) );
3019 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorAnyGeometry );
3020 pythonCode = def->asPythonString();
3021 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorAnyGeometry])" ) );
3022 code = def->asScriptCode();
3023 QCOMPARE( code, QStringLiteral( "##non_optional=layer hasgeometry" ) );
3024 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorLine );
3025 pythonCode = def->asPythonString();
3026 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorPoint,QgsProcessing.TypeVectorLine])" ) );
3027 code = def->asScriptCode();
3028 QCOMPARE( code, QStringLiteral( "##non_optional=layer point line" ) );
3029 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorPolygon );
3030 pythonCode = def->asPythonString();
3031 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeVectorPoint,QgsProcessing.TypeVectorPolygon])" ) );
3032 code = def->asScriptCode();
3033 QCOMPARE( code, QStringLiteral( "##non_optional=layer point polygon" ) );
3034 def->setDataTypes( QList< int >() << QgsProcessing::TypeRaster << QgsProcessing::TypeVectorPoint );
3035 pythonCode = def->asPythonString();
3036 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeRaster,QgsProcessing.TypeVectorPoint])" ) );
3037 code = def->asScriptCode();
3038 QCOMPARE( code, QStringLiteral( "##non_optional=layer raster point" ) );
3039 def->setDataTypes( QList< int >() << QgsProcessing::TypePlugin );
3040 pythonCode = def->asPythonString();
3041 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypePlugin])" ) );
3042 code = def->asScriptCode();
3043 QCOMPARE( code, QStringLiteral( "##non_optional=layer plugin" ) );
3044 def->setDataTypes( QList< int >() << QgsProcessing::TypePointCloud );
3045 pythonCode = def->asPythonString();
3046 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypePointCloud])" ) );
3047 code = def->asScriptCode();
3048 QCOMPARE( code, QStringLiteral( "##non_optional=layer pointcloud" ) );
3049 def->setDataTypes( QList< int >() << QgsProcessing::TypeAnnotation );
3050 pythonCode = def->asPythonString();
3051 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapLayer('non_optional', '', defaultValue='', types=[QgsProcessing.TypeAnnotation])" ) );
3052 code = def->asScriptCode();
3053 QCOMPARE( code, QStringLiteral( "##non_optional=layer annotation" ) );
3054
3055 // optional
3056 def.reset( new QgsProcessingParameterMapLayer( "optional", QString(), v1->id(), true ) );
3057 params.insert( "optional", QVariant() );
3058 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context )->id(), v1->id() );
3059 QVERIFY( def->checkValueIsAcceptable( false ) );
3060 QVERIFY( def->checkValueIsAcceptable( true ) );
3061 QVERIFY( def->checkValueIsAcceptable( 5 ) );
3062 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
3063 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
3064 QVERIFY( def->checkValueIsAcceptable( "" ) );
3065 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3066 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
3067 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
3068
3069 pythonCode = def->asPythonString();
3070 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterMapLayer('optional', '', optional=True, defaultValue='" ) + v1->id() + "')" ) );
3071
3072 code = def->asScriptCode();
3073 QCOMPARE( code, QString( QStringLiteral( "##optional=optional layer " ) + v1->id() ) );
3074 fromCode.reset( dynamic_cast< QgsProcessingParameterMapLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3075 QVERIFY( fromCode.get() );
3076 QCOMPARE( fromCode->name(), def->name() );
3077 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3078 QCOMPARE( fromCode->flags(), def->flags() );
3079 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3080
3081 // check if can manage QgsProcessingOutputLayerDefinition
3082 // as QVariat value in parameters (e.g. coming from an input of
3083 // another algorithm)
3084
3085 // all ok
3086 def.reset( new QgsProcessingParameterMapLayer( "non_optional", QString(), r1->id(), true ) );
3087 QString sink_name( r1->id() );
3088 const QgsProcessingOutputLayerDefinition val( sink_name );
3089 params.insert( "non_optional", QVariant::fromValue( val ) );
3090 QCOMPARE( QgsProcessingParameters::parameterAsLayer( def.get(), params, context )->id(), r1->id() );
3091
3092 // not ok, e.g. source name is not a layer and it's not possible to generate a layer from it source
3093 def.reset( new QgsProcessingParameterMapLayer( "non_optional", QString(), r1->id(), true ) );
3094 sink_name = QString( "i'm not a layer, and nothing you can do will make me one" );
3095 const QgsProcessingOutputLayerDefinition val2( sink_name );
3096 params.insert( "non_optional", QVariant::fromValue( val2 ) );
3097 QVERIFY( !QgsProcessingParameters::parameterAsLayer( def.get(), params, context ) );
3098 }
3099
parameterExtent()3100 void TestQgsProcessing::parameterExtent()
3101 {
3102 // setup a context
3103 QgsProject p;
3104 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
3105 const QString raster1 = testDataDir + "landsat_4326.tif";
3106 const QString raster2 = testDataDir + "landsat.tif";
3107 const QFileInfo fi1( raster1 );
3108 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
3109 p.addMapLayers( QList<QgsMapLayer *>() << r1 );
3110 QgsProcessingContext context;
3111 context.setProject( &p );
3112
3113 // not optional!
3114 std::unique_ptr< QgsProcessingParameterExtent > def( new QgsProcessingParameterExtent( "non_optional", QString(), QString( "1,2,3,4" ), false ) );
3115 QVERIFY( !def->checkValueIsAcceptable( false ) );
3116 QVERIFY( !def->checkValueIsAcceptable( true ) );
3117 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
3118 QVERIFY( def->checkValueIsAcceptable( "1,2,3,4" ) );
3119 QVERIFY( def->checkValueIsAcceptable( " 1, 2 ,3 , 4 " ) );
3120 QVERIFY( def->checkValueIsAcceptable( " 1, 2 ,3 , 4 ", &context ) );
3121 QVERIFY( def->checkValueIsAcceptable( "-1.1,2,-3,-4" ) );
3122 QVERIFY( def->checkValueIsAcceptable( "-1.1,2,-3,-4", &context ) );
3123 QVERIFY( def->checkValueIsAcceptable( "-1.1,-2.2,-3.3,-4.4" ) );
3124 QVERIFY( def->checkValueIsAcceptable( "-1.1,-2.2,-3.3,-4.4", &context ) );
3125 QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4[EPSG:4326]" ) );
3126 QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4[EPSG:4326]", &context ) );
3127 QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4 [EPSG:4326]" ) );
3128 QVERIFY( def->checkValueIsAcceptable( "1.1,2,3,4.4 [EPSG:4326]", &context ) );
3129 QVERIFY( def->checkValueIsAcceptable( " -1.1, -2, -3, -4.4 [EPSG:4326] " ) );
3130 QVERIFY( def->checkValueIsAcceptable( " -1.1, -2, -3, -4.4 [EPSG:4326] ", &context ) );
3131 QVERIFY( def->checkValueIsAcceptable( "121774.38859446358,948723.6921024882,-264546.200347173,492749.6672022904 [EPSG:3785]" ) );
3132 QVERIFY( def->checkValueIsAcceptable( "121774.38859446358,948723.6921024882,-264546.200347173,492749.6672022904 [EPSG:3785]", &context ) );
3133
3134 QVERIFY( !def->checkValueIsAcceptable( "" ) );
3135 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3136 QVERIFY( def->checkValueIsAcceptable( QgsRectangle( 1, 2, 3, 4 ) ) );
3137 QVERIFY( !def->checkValueIsAcceptable( QgsRectangle() ) );
3138 QVERIFY( def->checkValueIsAcceptable( QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
3139 QVERIFY( !def->checkValueIsAcceptable( QgsReferencedRectangle( QgsRectangle(), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
3140 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromRect( QgsRectangle( 1, 2, 3, 4 ) ) ) );
3141 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromWkt( QStringLiteral( "LineString(10 10, 20 20)" ) ) ) );
3142
3143 // these checks require a context - otherwise we could potentially be referring to a layer source
3144 QVERIFY( def->checkValueIsAcceptable( "1,2,3" ) );
3145 QVERIFY( def->checkValueIsAcceptable( "1,2,3,a" ) );
3146 QVERIFY( !def->checkValueIsAcceptable( "1,2,3", &context ) );
3147 QVERIFY( !def->checkValueIsAcceptable( "1,2,3,a", &context ) );
3148
3149 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( r1->id() ) ) );
3150 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( r1->id() ) ) );
3151
3152 // using map layer
3153 QVariantMap params;
3154 params.insert( "non_optional", r1->id() );
3155 QVERIFY( def->checkValueIsAcceptable( r1->id() ) );
3156 QgsRectangle ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3157 QCOMPARE( ext, r1->extent() );
3158
3159 // string representing a project layer source
3160 params.insert( "non_optional", raster1 );
3161 QVERIFY( def->checkValueIsAcceptable( raster1 ) );
3162 QCOMPARE( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ), r1->extent() );
3163 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3164 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3165 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3166 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3167 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3168 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3169
3170 // layer as parameter
3171 params.insert( "non_optional", QVariant::fromValue( r1 ) );
3172 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
3173 QCOMPARE( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ), r1->extent() );
3174 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3175 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3176 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3177 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3178 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3179 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3180 QgsGeometry gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3181 QCOMPARE( gExt.constGet()->vertexCount(), 5 );
3182 ext = gExt.boundingBox();
3183 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3184 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3185 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3186 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3187
3188 // using feature source definition
3189 params.insert( "non_optional", QgsProcessingFeatureSourceDefinition( r1->id() ) );
3190 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3191 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3192 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3193 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3194 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3195 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3196 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3197 QCOMPARE( gExt.constGet()->vertexCount(), 5 );
3198 ext = gExt.boundingBox();
3199 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3200 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3201 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3202 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3203 params.insert( "non_optional", QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( QVariant::fromValue( r1 ) ) ) );
3204 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3205 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3206 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3207 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3208 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3209 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3210 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3211 QCOMPARE( gExt.constGet()->vertexCount(), 5 );
3212 ext = gExt.boundingBox();
3213 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3214 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3215 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3216 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3217
3218 // using output layer definition, e.g. from a previous model child algorithm
3219 params.insert( "non_optional", QgsProcessingOutputLayerDefinition( r1->id() ) );
3220 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3221 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3222 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3223 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3224 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3225 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3226 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3227 QCOMPARE( gExt.constGet()->vertexCount(), 5 );
3228 ext = gExt.boundingBox();
3229 QGSCOMPARENEAR( ext.xMinimum(), 17.942777, 0.001 );
3230 QGSCOMPARENEAR( ext.xMaximum(), 17.944704, 0.001 );
3231 QGSCOMPARENEAR( ext.yMinimum(), 30.229681, 0.001 );
3232 QGSCOMPARENEAR( ext.yMaximum(), 30.231616, 0.001 );
3233
3234 // string representing a non-project layer source
3235 params.insert( "non_optional", raster2 );
3236 QVERIFY( def->checkValueIsAcceptable( raster2 ) );
3237 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:32633" ) );
3238 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3239 QGSCOMPARENEAR( ext.xMinimum(), 781662.375000, 10 );
3240 QGSCOMPARENEAR( ext.xMaximum(), 793062.375000, 10 );
3241 QGSCOMPARENEAR( ext.yMinimum(), 3339523.125000, 10 );
3242 QGSCOMPARENEAR( ext.yMaximum(), 3350923.125000, 10 );
3243 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3244 QGSCOMPARENEAR( ext.xMinimum(), 17.924273, 0.01 );
3245 QGSCOMPARENEAR( ext.xMaximum(), 18.045658, 0.01 );
3246 QGSCOMPARENEAR( ext.yMinimum(), 30.151856, 0.01 );
3247 QGSCOMPARENEAR( ext.yMaximum(), 30.257289, 0.01 );
3248 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:4326" ) );
3249 QCOMPARE( gExt.constGet()->vertexCount(), 85 );
3250 ext = gExt.boundingBox();
3251 QGSCOMPARENEAR( ext.xMinimum(), 17.924273, 0.01 );
3252 QGSCOMPARENEAR( ext.xMaximum(), 18.045658, 0.01 );
3253 QGSCOMPARENEAR( ext.yMinimum(), 30.151856, 0.01 );
3254 QGSCOMPARENEAR( ext.yMaximum(), 30.257289, 0.01 );
3255
3256 // string representation of an extent
3257 params.insert( "non_optional", QString( "1.1,2.2,3.3,4.4" ) );
3258 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "1.1,2.2,3.3,4.4" ) ) );
3259 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3260 QGSCOMPARENEAR( ext.xMinimum(), 1.1, 0.001 );
3261 QGSCOMPARENEAR( ext.xMaximum(), 2.2, 0.001 );
3262 QGSCOMPARENEAR( ext.yMinimum(), 3.3, 0.001 );
3263 QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
3264
3265 // with target CRS - should make no difference, because source CRS is unknown
3266 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3267 QGSCOMPARENEAR( ext.xMinimum(), 1.1, 0.001 );
3268 QGSCOMPARENEAR( ext.xMaximum(), 2.2, 0.001 );
3269 QGSCOMPARENEAR( ext.yMinimum(), 3.3, 0.001 );
3270 QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
3271
3272 // with crs in string
3273 params.insert( "non_optional", QString( "1.1,3.3,2.2,4.4 [EPSG:4326]" ) );
3274 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3275 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3276 QGSCOMPARENEAR( ext.xMinimum(), 1.1, 0.001 );
3277 QGSCOMPARENEAR( ext.xMaximum(), 3.3, 0.001 );
3278 QGSCOMPARENEAR( ext.yMinimum(), 2.2, 0.001 );
3279 QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
3280 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3281 QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
3282 QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
3283 QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
3284 QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
3285 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3286 QCOMPARE( gExt.constGet()->vertexCount(), 85 );
3287 ext = gExt.boundingBox();
3288 QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
3289 QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
3290 QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
3291 QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
3292
3293 // nonsense string
3294 params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
3295 QVERIFY( QgsProcessingParameters::parameterAsExtent( def.get(), params, context ).isNull() );
3296
3297 // QgsRectangle
3298 params.insert( "non_optional", QgsRectangle( 11.1, 12.2, 13.3, 14.4 ) );
3299 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3300 QGSCOMPARENEAR( ext.xMinimum(), 11.1, 0.001 );
3301 QGSCOMPARENEAR( ext.xMaximum(), 13.3, 0.001 );
3302 QGSCOMPARENEAR( ext.yMinimum(), 12.2, 0.001 );
3303 QGSCOMPARENEAR( ext.yMaximum(), 14.4, 0.001 );
3304
3305 // with target CRS - should make no difference, because source CRS is unknown
3306 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3307 QGSCOMPARENEAR( ext.xMinimum(), 11.1, 0.001 );
3308 QGSCOMPARENEAR( ext.xMaximum(), 13.3, 0.001 );
3309 QGSCOMPARENEAR( ext.yMinimum(), 12.2, 0.001 );
3310 QGSCOMPARENEAR( ext.yMaximum(), 14.4, 0.001 );
3311
3312 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3313 QCOMPARE( gExt.asWkt( 1 ), QStringLiteral( "Polygon ((11.1 12.2, 13.3 12.2, 13.3 14.4, 11.1 14.4, 11.1 12.2))" ) );
3314
3315 p.setCrs( QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3316 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:3785" ) );
3317
3318 // QgsGeometry
3319 params.insert( "non_optional", QgsGeometry::fromRect( QgsRectangle( 13, 14, 15, 16 ) ) );
3320 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3321 QGSCOMPARENEAR( ext.xMinimum(), 13, 0.001 );
3322 QGSCOMPARENEAR( ext.xMaximum(), 15, 0.001 );
3323 QGSCOMPARENEAR( ext.yMinimum(), 14, 0.001 );
3324 QGSCOMPARENEAR( ext.yMaximum(), 16, 0.001 );
3325 // with target CRS - should make no difference, because source CRS is unknown
3326 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3327 QGSCOMPARENEAR( ext.xMinimum(), 13, 0.001 );
3328 QGSCOMPARENEAR( ext.xMaximum(), 15, 0.001 );
3329 QGSCOMPARENEAR( ext.yMinimum(), 14, 0.001 );
3330 QGSCOMPARENEAR( ext.yMaximum(), 16, 0.001 );
3331
3332 // QgsReferencedRectangle
3333 params.insert( "non_optional", QgsReferencedRectangle( QgsRectangle( 1.1, 2.2, 3.3, 4.4 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3334 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3335 QGSCOMPARENEAR( ext.xMinimum(), 1.1, 0.001 );
3336 QGSCOMPARENEAR( ext.xMaximum(), 3.3, 0.001 );
3337 QGSCOMPARENEAR( ext.yMinimum(), 2.2, 0.001 );
3338 QGSCOMPARENEAR( ext.yMaximum(), 4.4, 0.001 );
3339 QCOMPARE( QgsProcessingParameters::parameterAsExtentCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3340
3341 // with target CRS
3342 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3343 QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
3344 QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
3345 QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
3346 QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
3347
3348 // as reprojected geometry
3349 gExt = QgsProcessingParameters::parameterAsExtentGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3350 QCOMPARE( gExt.constGet()->vertexCount(), 85 );
3351 ext = gExt.boundingBox();
3352 QGSCOMPARENEAR( ext.xMinimum(), 122451, 100 );
3353 QGSCOMPARENEAR( ext.xMaximum(), 367354, 100 );
3354 QGSCOMPARENEAR( ext.yMinimum(), 244963, 100 );
3355 QGSCOMPARENEAR( ext.yMaximum(), 490287, 100 );
3356
3357 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
3358 QCOMPARE( def->valueAsPythonString( "1,2,3,4", context ), QStringLiteral( "'1,2,3,4'" ) );
3359 QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "landsat_4326.tif'" ) ) );
3360 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "landsat_4326.tif'" ) ) );
3361 QCOMPARE( def->valueAsPythonString( raster2, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "landsat.tif'" ) ) );
3362 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
3363 QCOMPARE( def->valueAsPythonString( QgsRectangle( 11, 12, 13, 14 ), context ), QStringLiteral( "'11, 13, 12, 14'" ) );
3364 QCOMPARE( def->valueAsPythonString( QgsReferencedRectangle( QgsRectangle( 11, 12, 13, 14 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11, 13, 12, 14 [EPSG:4326]'" ) );
3365 QCOMPARE( def->valueAsPythonString( "1,2,3,4 [EPSG:4326]", context ), QStringLiteral( "'1,2,3,4 [EPSG:4326]'" ) );
3366 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
3367 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
3368 QCOMPARE( def->valueAsPythonString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QStringLiteral( "QgsGeometry.fromWkt('LineString (10 10, 20 20)')" ) );
3369
3370 QString pythonCode = def->asPythonString();
3371 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExtent('non_optional', '', defaultValue='1,2,3,4')" ) );
3372
3373 QString code = def->asScriptCode();
3374 QCOMPARE( code, QStringLiteral( "##non_optional=extent 1,2,3,4" ) );
3375 std::unique_ptr< QgsProcessingParameterExtent > fromCode( dynamic_cast< QgsProcessingParameterExtent * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3376 QVERIFY( fromCode.get() );
3377 QCOMPARE( fromCode->name(), def->name() );
3378 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
3379 QCOMPARE( fromCode->flags(), def->flags() );
3380 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3381
3382 const QVariantMap map = def->toVariantMap();
3383 QgsProcessingParameterExtent fromMap( "x" );
3384 QVERIFY( fromMap.fromVariantMap( map ) );
3385 QCOMPARE( fromMap.name(), def->name() );
3386 QCOMPARE( fromMap.description(), def->description() );
3387 QCOMPARE( fromMap.flags(), def->flags() );
3388 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3389 def.reset( dynamic_cast< QgsProcessingParameterExtent *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3390 QVERIFY( dynamic_cast< QgsProcessingParameterExtent *>( def.get() ) );
3391
3392 // optional
3393 def.reset( new QgsProcessingParameterExtent( "optional", QString(), QString( "5,6,7,8" ), true ) );
3394 QVERIFY( def->checkValueIsAcceptable( false ) );
3395 QVERIFY( def->checkValueIsAcceptable( true ) );
3396 QVERIFY( def->checkValueIsAcceptable( 5 ) );
3397 QVERIFY( def->checkValueIsAcceptable( "1,2,3,4" ) );
3398 QVERIFY( def->checkValueIsAcceptable( "" ) );
3399 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3400
3401 // Extent is unique in that it will let you set invalid, whereas other
3402 // optional parameters become "default" when assigning invalid.
3403 params.insert( "optional", QVariant() );
3404 ext = QgsProcessingParameters::parameterAsExtent( def.get(), params, context );
3405 QVERIFY( ext.isNull() );
3406
3407 pythonCode = def->asPythonString();
3408 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExtent('optional', '', optional=True, defaultValue='5,6,7,8')" ) );
3409
3410 code = def->asScriptCode();
3411 QCOMPARE( code, QStringLiteral( "##optional=optional extent 5,6,7,8" ) );
3412 fromCode.reset( dynamic_cast< QgsProcessingParameterExtent * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3413 QVERIFY( fromCode.get() );
3414 QCOMPARE( fromCode->name(), def->name() );
3415 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3416 QCOMPARE( fromCode->flags(), def->flags() );
3417 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3418 }
3419
parameterPoint()3420 void TestQgsProcessing::parameterPoint()
3421 {
3422 QgsProcessingContext context;
3423
3424 // not optional!
3425 std::unique_ptr< QgsProcessingParameterPoint > def( new QgsProcessingParameterPoint( "non_optional", QString(), QString( "1,2" ), false ) );
3426 QVERIFY( !def->checkValueIsAcceptable( false ) );
3427 QVERIFY( !def->checkValueIsAcceptable( true ) );
3428 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
3429 QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
3430 QVERIFY( def->checkValueIsAcceptable( "(1.1,2)" ) );
3431 QVERIFY( def->checkValueIsAcceptable( " 1.1, 2 " ) );
3432 QVERIFY( def->checkValueIsAcceptable( " ( 1.1, 2 ) " ) );
3433 QVERIFY( def->checkValueIsAcceptable( "-1.1,2" ) );
3434 QVERIFY( def->checkValueIsAcceptable( "1.1,-2" ) );
3435 QVERIFY( def->checkValueIsAcceptable( "-1.1,-2" ) );
3436 QVERIFY( def->checkValueIsAcceptable( "(-1.1,-2)" ) );
3437 QVERIFY( def->checkValueIsAcceptable( "1.1,2[EPSG:4326]" ) );
3438 QVERIFY( def->checkValueIsAcceptable( "1.1,2 [EPSG:4326]" ) );
3439 QVERIFY( def->checkValueIsAcceptable( "(1.1,2 [EPSG:4326] )" ) );
3440 QVERIFY( def->checkValueIsAcceptable( " -1.1, -2 [EPSG:4326] " ) );
3441 QVERIFY( def->checkValueIsAcceptable( " ( -1.1, -2 [EPSG:4326] ) " ) );
3442 QVERIFY( !def->checkValueIsAcceptable( "1.1,a" ) );
3443 QVERIFY( !def->checkValueIsAcceptable( "(1.1,a)" ) );
3444 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
3445 QVERIFY( !def->checkValueIsAcceptable( "(layer12312312)" ) );
3446 QVERIFY( !def->checkValueIsAcceptable( "" ) );
3447 QVERIFY( !def->checkValueIsAcceptable( "()" ) );
3448 QVERIFY( !def->checkValueIsAcceptable( " ( ) " ) );
3449 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3450 QVERIFY( def->checkValueIsAcceptable( QgsPointXY( 1, 2 ) ) );
3451 QVERIFY( def->checkValueIsAcceptable( QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
3452 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromPointXY( QgsPointXY( 1, 2 ) ) ) );
3453 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromWkt( QStringLiteral( "LineString(10 10, 20 20)" ) ) ) );
3454
3455 // string representing a point
3456 QVariantMap params;
3457 params.insert( "non_optional", QString( "1.1,2.2" ) );
3458 QVERIFY( def->checkValueIsAcceptable( "1.1,2.2" ) );
3459 QgsPointXY point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3460 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3461 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3462
3463 // with target CRS - should make no difference, because source CRS is unknown
3464 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3465 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3466 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3467
3468 // with optional brackets
3469 params.insert( "non_optional", QString( "(1.1,2.2)" ) );
3470 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3471 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3472 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3473
3474 params.insert( "non_optional", QString( " ( -1.1 ,-2.2 ) " ) );
3475 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3476 QGSCOMPARENEAR( point.x(), -1.1, 0.001 );
3477 QGSCOMPARENEAR( point.y(), -2.2, 0.001 );
3478
3479 // with CRS as string
3480 params.insert( "non_optional", QString( "1.1,2.2[EPSG:4326]" ) );
3481 QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3482 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3483 QGSCOMPARENEAR( point.x(), 122451, 100 );
3484 QGSCOMPARENEAR( point.y(), 244963, 100 );
3485 params.insert( "non_optional", QString( "1.1,2.2 [EPSG:4326]" ) );
3486 QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3487 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3488 QGSCOMPARENEAR( point.x(), 122451, 100 );
3489 QGSCOMPARENEAR( point.y(), 244963, 100 );
3490
3491 params.insert( "non_optional", QString( " ( 1.1,2.2 [EPSG:4326] ) " ) );
3492 QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3493 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3494 QGSCOMPARENEAR( point.x(), 122451, 100 );
3495 QGSCOMPARENEAR( point.y(), 244963, 100 );
3496
3497 // nonsense string
3498 params.insert( "non_optional", QString( "i'm not a crs, and nothing you can do will make me one" ) );
3499 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3500 QVERIFY( point.isEmpty() );
3501 QGSCOMPARENEAR( point.x(), 0.0, 0.001 );
3502 QGSCOMPARENEAR( point.y(), 0.0, 0.001 );
3503
3504 params.insert( "non_optional", QString( " ( ) " ) );
3505 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3506 QVERIFY( point.isEmpty() );
3507 QGSCOMPARENEAR( point.x(), 0.0, 0.001 );
3508 QGSCOMPARENEAR( point.y(), 0.0, 0.001 );
3509
3510 // QgsPointXY
3511 params.insert( "non_optional", QgsPointXY( 11.1, 12.2 ) );
3512 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3513 QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
3514 QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
3515
3516 // with target CRS - should make no difference, because source CRS is unknown
3517 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3518 QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
3519 QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
3520
3521 // QgsReferencedPointXY
3522 params.insert( "non_optional", QgsReferencedPointXY( QgsPointXY( 1.1, 2.2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3523 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3524 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3525 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3526 QCOMPARE( QgsProcessingParameters::parameterAsPointCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3527
3528 // with target CRS
3529 params.insert( "non_optional", QgsReferencedPointXY( QgsPointXY( 1.1, 2.2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3530 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3531 QGSCOMPARENEAR( point.x(), 122451, 100 );
3532 QGSCOMPARENEAR( point.y(), 244963, 100 );
3533
3534 // QgsGeometry
3535 params.insert( "non_optional", QgsGeometry::fromPointXY( QgsPointXY( 13.1, 14.2 ) ) );
3536 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3537 QGSCOMPARENEAR( point.x(), 13.1, 0.001 );
3538 QGSCOMPARENEAR( point.y(), 14.2, 0.001 );
3539 // non point geometry should use centroid
3540 params.insert( "non_optional", QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 10)" ) ) );
3541 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3542 QGSCOMPARENEAR( point.x(), 15.0, 0.001 );
3543 QGSCOMPARENEAR( point.y(), 10.0, 0.001 );
3544 // with target CRS - should make no difference, because source CRS is unknown
3545 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3546 QGSCOMPARENEAR( point.x(), 15.0, 0.001 );
3547 QGSCOMPARENEAR( point.y(), 10.0, 0.001 );
3548
3549 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
3550 QCOMPARE( def->valueAsPythonString( "1,2", context ), QStringLiteral( "'1,2'" ) );
3551 QCOMPARE( def->valueAsPythonString( "1,2 [EPSG:4326]", context ), QStringLiteral( "'1,2 [EPSG:4326]'" ) );
3552 QCOMPARE( def->valueAsPythonString( QgsPointXY( 11, 12 ), context ), QStringLiteral( "'11,12'" ) );
3553 QCOMPARE( def->valueAsPythonString( QgsReferencedPointXY( QgsPointXY( 11, 12 ), QgsCoordinateReferenceSystem( "epsg:4326" ) ), context ), QStringLiteral( "'11,12 [EPSG:4326]'" ) );
3554 QCOMPARE( def->valueAsPythonString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QStringLiteral( "QgsGeometry.fromWkt('LineString (10 10, 20 20)')" ) );
3555
3556 QString pythonCode = def->asPythonString();
3557 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPoint('non_optional', '', defaultValue='1,2')" ) );
3558
3559 QString code = def->asScriptCode();
3560 QCOMPARE( code, QStringLiteral( "##non_optional=point 1,2" ) );
3561 std::unique_ptr< QgsProcessingParameterPoint > fromCode( dynamic_cast< QgsProcessingParameterPoint * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3562 QVERIFY( fromCode.get() );
3563 QCOMPARE( fromCode->name(), def->name() );
3564 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
3565 QCOMPARE( fromCode->flags(), def->flags() );
3566 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3567
3568 const QVariantMap map = def->toVariantMap();
3569 QgsProcessingParameterPoint fromMap( "x" );
3570 QVERIFY( fromMap.fromVariantMap( map ) );
3571 QCOMPARE( fromMap.name(), def->name() );
3572 QCOMPARE( fromMap.description(), def->description() );
3573 QCOMPARE( fromMap.flags(), def->flags() );
3574 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3575 def.reset( dynamic_cast< QgsProcessingParameterPoint *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3576 QVERIFY( dynamic_cast< QgsProcessingParameterPoint *>( def.get() ) );
3577
3578 // optional
3579 def.reset( new QgsProcessingParameterPoint( "optional", QString(), QString( "5.1,6.2" ), true ) );
3580 QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
3581 QVERIFY( !def->checkValueIsAcceptable( "1.1,a" ) );
3582 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
3583 QVERIFY( def->checkValueIsAcceptable( "" ) );
3584 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3585
3586 params.insert( "optional", QVariant() );
3587 point = QgsProcessingParameters::parameterAsPoint( def.get(), params, context );
3588 QGSCOMPARENEAR( point.x(), 5.1, 0.001 );
3589 QGSCOMPARENEAR( point.y(), 6.2, 0.001 );
3590
3591 pythonCode = def->asPythonString();
3592 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPoint('optional', '', optional=True, defaultValue='5.1,6.2')" ) );
3593
3594 code = def->asScriptCode();
3595 QCOMPARE( code, QStringLiteral( "##optional=optional point 5.1,6.2" ) );
3596 fromCode.reset( dynamic_cast< QgsProcessingParameterPoint * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3597 QVERIFY( fromCode.get() );
3598 QCOMPARE( fromCode->name(), def->name() );
3599 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3600 QCOMPARE( fromCode->flags(), def->flags() );
3601 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3602 }
3603
parameterGeometry()3604 void TestQgsProcessing::parameterGeometry()
3605 {
3606 QgsProcessingContext context;
3607
3608 // not optional!
3609 std::unique_ptr< QgsProcessingParameterGeometry > def( new QgsProcessingParameterGeometry( "non_optional", QString(), QString( "Point(1 2)" ), false ) );
3610 QVERIFY( !def->checkValueIsAcceptable( false ) );
3611 QVERIFY( !def->checkValueIsAcceptable( true ) );
3612 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
3613 QVERIFY( !def->checkValueIsAcceptable( "Nonsense string" ) );
3614 QVERIFY( !def->checkValueIsAcceptable( "" ) );
3615 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3616 QVERIFY( !def->checkValueIsAcceptable( QString( "LineString(10 10, 20 a)" ) ) );
3617 QVERIFY( def->checkValueIsAcceptable( QString( "LineString(10 10, 20 20)" ) ) );
3618 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromPointXY( QgsPointXY( 1, 2 ) ) ) );
3619 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromWkt( QStringLiteral( "LineString(10 10, 20 20)" ) ) ) );
3620 QVERIFY( def->checkValueIsAcceptable( QgsPointXY( 1, 2 ) ) );
3621 QVERIFY( def->checkValueIsAcceptable( QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
3622 QVERIFY( def->checkValueIsAcceptable( QgsRectangle( 10, 10, 20, 20 ) ) );
3623 QVERIFY( def->checkValueIsAcceptable( QgsReferencedRectangle( QgsRectangle( 10, 10, 20, 20 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) ) );
3624 QVERIFY( def->checkValueIsAcceptable( QString( "MultiPoint((10 10), (20 20))" ) ) );
3625
3626 // string representing a geometry
3627 QVariantMap params;
3628 params.insert( "non_optional", QString( "LineString(10 10, 20 20)" ) );
3629 QVERIFY( def->checkValueIsAcceptable( "LineString(10 10, 20 20)" ) );
3630 QgsGeometry geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3631 QCOMPARE( geometry.asWkt(), QStringLiteral( "LineString (10 10, 20 20)" ) );
3632
3633 // with target CRS - should make no difference, because source CRS is unknown
3634 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3635 QCOMPARE( geometry.asWkt(), QStringLiteral( "LineString (10 10, 20 20)" ) );
3636
3637 // with CRS as string
3638 params.insert( "non_optional", QString( "CRS=EPSG:4326;Point ( 1.1 2.2 )" ) );
3639 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3640 QPointF point = geometry.asQPointF();
3641 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3642 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3643 QCOMPARE( QgsProcessingParameters::parameterAsGeometryCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3644 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3645 point = geometry.asQPointF();
3646 QGSCOMPARENEAR( point.x(), 122451, 100 );
3647 QGSCOMPARENEAR( point.y(), 244963, 100 );
3648
3649 // QgsReferencedGeometry
3650 params.insert( "non_optional", QgsReferencedGeometry( QgsGeometry::fromPointXY( QgsPointXY( 1.1, 2.2 ) ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3651 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3652 point = geometry.asQPointF();
3653 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3654 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3655 QCOMPARE( QgsProcessingParameters::parameterAsGeometryCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3656 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3657 point = geometry.asQPointF();
3658 QGSCOMPARENEAR( point.x(), 122451, 100 );
3659 QGSCOMPARENEAR( point.y(), 244963, 100 );
3660
3661 // QgsPointXY
3662 params.insert( "non_optional", QgsPointXY( 11.1, 12.2 ) );
3663 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3664 point = geometry.asQPointF();
3665 QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
3666 QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
3667
3668 // with target CRS - should make no difference, because source CRS is unknown
3669 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3670 point = geometry.asQPointF();
3671 QGSCOMPARENEAR( point.x(), 11.1, 0.001 );
3672 QGSCOMPARENEAR( point.y(), 12.2, 0.001 );
3673
3674 // QgsReferencedPointXY
3675 params.insert( "non_optional", QgsReferencedPointXY( QgsPointXY( 1.1, 2.2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3676 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3677 point = geometry.asQPointF();
3678 QGSCOMPARENEAR( point.x(), 1.1, 0.001 );
3679 QGSCOMPARENEAR( point.y(), 2.2, 0.001 );
3680 QCOMPARE( QgsProcessingParameters::parameterAsGeometryCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3681
3682 // with target CRS
3683 params.insert( "non_optional", QgsReferencedPointXY( QgsPointXY( 1.1, 2.2 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3684 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3685 point = geometry.asQPointF();
3686 QGSCOMPARENEAR( point.x(), 122451, 100 );
3687 QGSCOMPARENEAR( point.y(), 244963, 100 );
3688
3689 // QgsRectangle
3690 params.insert( "non_optional", QgsRectangle( 11.1, 12.2, 13.3, 14.4 ) );
3691 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3692 QCOMPARE( geometry.asWkt( 1 ), QStringLiteral( "Polygon ((11.1 12.2, 13.3 12.2, 13.3 14.4, 11.1 14.4, 11.1 12.2))" ) );
3693
3694 // with target CRS - should make no difference, because source CRS is unknown
3695 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3696 QCOMPARE( geometry.asWkt( 1 ), QStringLiteral( "Polygon ((11.1 12.2, 13.3 12.2, 13.3 14.4, 11.1 14.4, 11.1 12.2))" ) );
3697
3698 // QgsReferenced Rectangle
3699 params.insert( "non_optional", QgsReferencedRectangle( QgsRectangle( 11.1, 12.2, 13.3, 14.4 ), QgsCoordinateReferenceSystem( "EPSG:4326" ) ) );
3700 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3701 QCOMPARE( geometry.asWkt( 1 ), QStringLiteral( "Polygon ((11.1 12.2, 13.3 12.2, 13.3 14.4, 11.1 14.4, 11.1 12.2))" ) );
3702 QCOMPARE( QgsProcessingParameters::parameterAsGeometryCrs( def.get(), params, context ).authid(), QStringLiteral( "EPSG:4326" ) );
3703
3704 // with target CRS
3705 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context, QgsCoordinateReferenceSystem( "EPSG:3785" ) );
3706 QCOMPARE( geometry.constGet()->vertexCount(), 85 );
3707 const QgsRectangle ext = geometry.boundingBox();
3708 QGSCOMPARENEAR( ext.xMinimum(), 1235646, 100 );
3709 QGSCOMPARENEAR( ext.xMaximum(), 1480549, 100 );
3710 QGSCOMPARENEAR( ext.yMinimum(), 1368478, 100 );
3711 QGSCOMPARENEAR( ext.yMaximum(), 1620147, 100 );
3712
3713 // nonsense string
3714 params.insert( "non_optional", QString( "i'm not a geometry, and nothing you can do will make me one" ) );
3715 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3716 QVERIFY( geometry.isNull() );
3717
3718 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
3719 QCOMPARE( def->valueAsPythonString( "LineString( 10 10, 20 20)", context ), QStringLiteral( "'LineString( 10 10, 20 20)'" ) );
3720 QCOMPARE( def->valueAsPythonString( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ), context ), QStringLiteral( "'LineString (10 10, 20 20)'" ) );
3721
3722 // With Srid as string
3723 QCOMPARE( def->valueAsPythonString( QgsReferencedGeometry( QgsGeometry::fromWkt( QStringLiteral( "LineString( 10 10, 20 20)" ) ),
3724 QgsCoordinateReferenceSystem( "EPSG:4326" ) ), context ),
3725 QStringLiteral( "'CRS=EPSG:4326;LineString (10 10, 20 20)'" ) );
3726
3727 QString pythonCode = def->asPythonString();
3728 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('non_optional', '', defaultValue='Point(1 2)')" ) );
3729
3730 QString code = def->asScriptCode();
3731 QCOMPARE( code, QStringLiteral( "##non_optional=geometry Point(1 2)" ) );
3732 std::unique_ptr< QgsProcessingParameterGeometry > fromCode( dynamic_cast< QgsProcessingParameterGeometry * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3733 QVERIFY( fromCode.get() );
3734 QCOMPARE( fromCode->name(), def->name() );
3735 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
3736 QCOMPARE( fromCode->flags(), def->flags() );
3737 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3738
3739 const QVariantMap map = def->toVariantMap();
3740 QgsProcessingParameterGeometry fromMap( "x" );
3741 QVERIFY( fromMap.fromVariantMap( map ) );
3742 QCOMPARE( fromMap.name(), def->name() );
3743 QCOMPARE( fromMap.description(), def->description() );
3744 QCOMPARE( fromMap.flags(), def->flags() );
3745 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3746 def.reset( dynamic_cast< QgsProcessingParameterGeometry *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3747 QVERIFY( dynamic_cast< QgsProcessingParameterGeometry *>( def.get() ) );
3748
3749 // optional
3750 def.reset( new QgsProcessingParameterGeometry( "optional", QString(), QString( "Point(-1 3)" ), true ) );
3751 QVERIFY( def->checkValueIsAcceptable( "LineString(10 10, 20 20)" ) );
3752 QVERIFY( !def->checkValueIsAcceptable( "Point(-1 a)" ) );
3753 QVERIFY( def->checkValueIsAcceptable( "" ) );
3754 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3755
3756 params.insert( "optional", QVariant() );
3757 geometry = QgsProcessingParameters::parameterAsGeometry( def.get(), params, context );
3758 QCOMPARE( geometry.asWkt(), QStringLiteral( "Point (-1 3)" ) );
3759
3760 pythonCode = def->asPythonString();
3761 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('optional', '', optional=True, defaultValue='Point(-1 3)')" ) );
3762
3763 code = def->asScriptCode();
3764 QCOMPARE( code, QStringLiteral( "##optional=optional geometry Point(-1 3)" ) );
3765 fromCode.reset( dynamic_cast< QgsProcessingParameterGeometry * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3766 QVERIFY( fromCode.get() );
3767 QCOMPARE( fromCode->name(), def->name() );
3768 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3769 QCOMPARE( fromCode->flags(), def->flags() );
3770 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3771
3772 // non optional with filter
3773 def.reset( new QgsProcessingParameterGeometry( "filtered", QString(), QString( "Point(-1 3)" ), false,
3774 { QgsWkbTypes::LineGeometry } ) );
3775 QVERIFY( def->geometryTypes().contains( QgsWkbTypes::LineGeometry ) );
3776 QVERIFY( def->checkValueIsAcceptable( "LineString(10 10, 20 20)" ) );
3777 QVERIFY( !def->checkValueIsAcceptable( "Point(1 2)" ) );
3778 QVERIFY( !def->checkValueIsAcceptable( "" ) );
3779 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3780
3781 pythonCode = def->asPythonString();
3782 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('filtered', '', geometryTypes=[ QgsWkbTypes.LineGeometry ], defaultValue='Point(-1 3)')" ) );
3783
3784 const QVariantMap map2 = def->toVariantMap();
3785 QgsProcessingParameterGeometry fromMap2( "x" );
3786 QVERIFY( fromMap2.fromVariantMap( map2 ) );
3787 QCOMPARE( fromMap2.name(), def->name() );
3788 QCOMPARE( fromMap2.description(), def->description() );
3789 QCOMPARE( fromMap2.flags(), def->flags() );
3790 QCOMPARE( fromMap2.defaultValue(), def->defaultValue() );
3791 QCOMPARE( fromMap2.geometryTypes(), def->geometryTypes() );
3792 QCOMPARE( fromMap2.allowMultipart(), def->allowMultipart() );
3793 def.reset( dynamic_cast< QgsProcessingParameterGeometry *>( QgsProcessingParameters::parameterFromVariantMap( map2 ) ) );
3794 QVERIFY( dynamic_cast< QgsProcessingParameterGeometry *>( def.get() ) );
3795
3796 // not multipart
3797 def.reset( new QgsProcessingParameterGeometry( "not_multipart", QString(), QString( "Point(-1 3)" ), false, {}, false ) );
3798 QVERIFY( !def->allowMultipart() );
3799 QVERIFY( !def->checkValueIsAcceptable( QString( "MultiPoint((10 10), (20 20))" ) ) );
3800 QVERIFY( !def->checkValueIsAcceptable( QgsGeometry::fromWkt( QStringLiteral( "MultiPoint((10 10), (20 20))" ) ) ) );
3801 QVERIFY( def->checkValueIsAcceptable( QgsGeometry::fromPointXY( QgsPointXY( 1, 2 ) ) ) );
3802
3803 pythonCode = def->asPythonString();
3804 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterGeometry('not_multipart', '', allowMultipart=False, defaultValue='Point(-1 3)')" ) );
3805
3806 const QVariantMap map3 = def->toVariantMap();
3807 QgsProcessingParameterGeometry fromMap3( "x" );
3808 QVERIFY( fromMap3.fromVariantMap( map3 ) );
3809 QCOMPARE( fromMap3.allowMultipart(), false );
3810
3811 std::unique_ptr< QgsProcessingParameterGeometry > cloned( dynamic_cast< QgsProcessingParameterGeometry *>( def->clone() ) );
3812 QCOMPARE( cloned->name(), def->name() );
3813 QCOMPARE( cloned->description(), def->description() );
3814 QCOMPARE( cloned->flags(), def->flags() );
3815 QCOMPARE( cloned->defaultValue(), def->defaultValue() );
3816 QCOMPARE( cloned->geometryTypes(), def->geometryTypes() );
3817 QCOMPARE( cloned->allowMultipart(), def->allowMultipart() );
3818
3819 }
3820
3821
3822
parameterFile()3823 void TestQgsProcessing::parameterFile()
3824 {
3825 QgsProcessingContext context;
3826
3827 // not optional!
3828 std::unique_ptr< QgsProcessingParameterFile > def( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QString(), QString( "abc.bmp" ), false ) );
3829 QVERIFY( !def->checkValueIsAcceptable( false ) );
3830 QVERIFY( !def->checkValueIsAcceptable( true ) );
3831 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
3832 QVERIFY( def->checkValueIsAcceptable( "bricks.bmp" ) );
3833 QVERIFY( !def->checkValueIsAcceptable( "" ) );
3834 QVERIFY( !def->checkValueIsAcceptable( " " ) );
3835 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3836
3837 // string representing a file
3838 QVariantMap params;
3839 params.insert( "non_optional", QString( "def.bmp" ) );
3840 QCOMPARE( QgsProcessingParameters::parameterAsFile( def.get(), params, context ), QString( "def.bmp" ) );
3841
3842 // no extension
3843 def.reset( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QString(), QVariant(), false ) );
3844 QVERIFY( def->checkValueIsAcceptable( "bricks.bmp" ) );
3845 QVERIFY( def->checkValueIsAcceptable( "bricks.BMP" ) );
3846 QVERIFY( def->checkValueIsAcceptable( "bricks.pcx" ) );
3847 QVERIFY( def->checkValueIsAcceptable( "bricks.PCX" ) );
3848 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
3849 QVERIFY( !def->checkValueIsAcceptable( QString( "" ) ) );
3850 def.reset( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QString(), QVariant(), true ) );
3851 QVERIFY( def->checkValueIsAcceptable( "bricks.bmp" ) );
3852 QVERIFY( def->checkValueIsAcceptable( "bricks.BMP" ) );
3853 QVERIFY( def->checkValueIsAcceptable( "bricks.pcx" ) );
3854 QVERIFY( def->checkValueIsAcceptable( "bricks.PCX" ) );
3855 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3856 QVERIFY( def->checkValueIsAcceptable( QString( "" ) ) );
3857
3858 // with extension
3859 def.reset( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QStringLiteral( ".bmp" ), QString( "abc.bmp" ), false ) );
3860 QVERIFY( def->checkValueIsAcceptable( "bricks.bmp" ) );
3861 QVERIFY( def->checkValueIsAcceptable( "bricks.BMP" ) );
3862 QVERIFY( !def->checkValueIsAcceptable( "bricks.pcx" ) );
3863
3864 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
3865 QCOMPARE( def->valueAsPythonString( "bricks.bmp", context ), QStringLiteral( "'bricks.bmp'" ) );
3866 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
3867 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
3868 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
3869
3870 QString pythonCode = def->asPythonString();
3871 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, extension='.bmp', defaultValue='abc.bmp')" ) );
3872
3873 QString code = def->asScriptCode();
3874 QCOMPARE( code, QStringLiteral( "##non_optional=file abc.bmp" ) );
3875 std::unique_ptr< QgsProcessingParameterFile > fromCode( dynamic_cast< QgsProcessingParameterFile * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3876 QVERIFY( fromCode.get() );
3877 QCOMPARE( fromCode->name(), def->name() );
3878 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
3879 QCOMPARE( fromCode->flags(), def->flags() );
3880 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3881 QCOMPARE( fromCode->behavior(), def->behavior() );
3882
3883 QVariantMap map = def->toVariantMap();
3884 QgsProcessingParameterFile fromMap( "x" );
3885 QVERIFY( fromMap.fromVariantMap( map ) );
3886 QCOMPARE( fromMap.name(), def->name() );
3887 QCOMPARE( fromMap.description(), def->description() );
3888 QCOMPARE( fromMap.flags(), def->flags() );
3889 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3890 QCOMPARE( fromMap.extension(), def->extension() );
3891 QCOMPARE( fromMap.fileFilter(), def->fileFilter() );
3892 QCOMPARE( fromMap.behavior(), def->behavior() );
3893 def.reset( dynamic_cast< QgsProcessingParameterFile *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3894 QVERIFY( dynamic_cast< QgsProcessingParameterFile *>( def.get() ) );
3895
3896 // with file filter
3897 def.reset( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QStringLiteral( ".bmp" ), QString( "abc.bmp" ), false, QStringLiteral( "PNG Files (*.png *.PNG)" ) ) );
3898 QCOMPARE( def->fileFilter(), QStringLiteral( "PNG Files (*.png *.PNG)" ) );
3899 QVERIFY( def->extension().isEmpty() );
3900 QVERIFY( def->checkValueIsAcceptable( "bricks.png" ) );
3901 QVERIFY( def->checkValueIsAcceptable( "bricks.PNG" ) );
3902 QVERIFY( !def->checkValueIsAcceptable( "bricks.pcx" ) );
3903
3904 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
3905 QCOMPARE( def->valueAsPythonString( "bricks.png", context ), QStringLiteral( "'bricks.png'" ) );
3906 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
3907 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
3908 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
3909
3910 pythonCode = def->asPythonString();
3911 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('non_optional', '', behavior=QgsProcessingParameterFile.File, fileFilter='PNG Files (*.png *.PNG)', defaultValue='abc.bmp')" ) );
3912
3913 code = def->asScriptCode();
3914 QCOMPARE( code, QStringLiteral( "##non_optional=file abc.bmp" ) );
3915 fromCode.reset( dynamic_cast< QgsProcessingParameterFile * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3916 QVERIFY( fromCode.get() );
3917 QCOMPARE( fromCode->name(), def->name() );
3918 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
3919 QCOMPARE( fromCode->flags(), def->flags() );
3920 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3921 QCOMPARE( fromCode->behavior(), def->behavior() );
3922
3923 map = def->toVariantMap();
3924 fromMap = QgsProcessingParameterFile( "x" );
3925 QVERIFY( fromMap.fromVariantMap( map ) );
3926 QCOMPARE( fromMap.name(), def->name() );
3927 QCOMPARE( fromMap.description(), def->description() );
3928 QCOMPARE( fromMap.flags(), def->flags() );
3929 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
3930 QCOMPARE( fromMap.extension(), def->extension() );
3931 QCOMPARE( fromMap.fileFilter(), def->fileFilter() );
3932 QCOMPARE( fromMap.behavior(), def->behavior() );
3933 def.reset( dynamic_cast< QgsProcessingParameterFile *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
3934 QVERIFY( dynamic_cast< QgsProcessingParameterFile *>( def.get() ) );
3935
3936 // with file filter with wildcards
3937 def.reset( new QgsProcessingParameterFile( "non_optional", QString(), QgsProcessingParameterFile::File, QStringLiteral( ".bmp" ), QString( "abc.bmp" ), false, QStringLiteral( "PNG Files (*.png);;Other Files (*.*)" ) ) );
3938 QVERIFY( def->checkValueIsAcceptable( "bricks.png" ) );
3939 QVERIFY( def->checkValueIsAcceptable( "bricks.PNG" ) );
3940 QVERIFY( def->checkValueIsAcceptable( "bricks.pcx" ) );
3941 QVERIFY( def->checkValueIsAcceptable( "bricks.PCX" ) );
3942
3943 // optional
3944 def.reset( new QgsProcessingParameterFile( "optional", QString(), QgsProcessingParameterFile::File, QString(), QString( "gef.bmp" ), true ) );
3945 QVERIFY( def->checkValueIsAcceptable( false ) );
3946 QVERIFY( def->checkValueIsAcceptable( true ) );
3947 QVERIFY( def->checkValueIsAcceptable( 5 ) );
3948 QVERIFY( def->checkValueIsAcceptable( "bricks.bmp" ) );
3949 QVERIFY( def->checkValueIsAcceptable( "" ) );
3950 QVERIFY( def->checkValueIsAcceptable( " " ) );
3951 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
3952
3953 params.insert( "optional", QVariant() );
3954 QCOMPARE( QgsProcessingParameters::parameterAsFile( def.get(), params, context ), QString( "gef.bmp" ) );
3955
3956 pythonCode = def->asPythonString();
3957 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('optional', '', optional=True, behavior=QgsProcessingParameterFile.File, fileFilter='All files (*.*)', defaultValue='gef.bmp')" ) );
3958
3959 code = def->asScriptCode();
3960 QCOMPARE( code, QStringLiteral( "##optional=optional file gef.bmp" ) );
3961 fromCode.reset( dynamic_cast< QgsProcessingParameterFile * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3962 QVERIFY( fromCode.get() );
3963 QCOMPARE( fromCode->name(), def->name() );
3964 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3965 QCOMPARE( fromCode->flags(), def->flags() );
3966 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3967 QCOMPARE( fromCode->behavior(), def->behavior() );
3968
3969 // folder
3970 def.reset( new QgsProcessingParameterFile( "optional", QString(), QgsProcessingParameterFile::Folder, QString(), QString( "/home/me" ), true ) );
3971 pythonCode = def->asPythonString();
3972 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFile('optional', '', optional=True, behavior=QgsProcessingParameterFile.Folder, fileFilter='All files (*.*)', defaultValue='/home/me')" ) );
3973 code = def->asScriptCode();
3974 QCOMPARE( code, QStringLiteral( "##optional=optional folder /home/me" ) );
3975 fromCode.reset( dynamic_cast< QgsProcessingParameterFile * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
3976 QVERIFY( fromCode.get() );
3977 QCOMPARE( fromCode->name(), def->name() );
3978 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
3979 QCOMPARE( fromCode->flags(), def->flags() );
3980 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
3981 QCOMPARE( fromCode->behavior(), def->behavior() );
3982
3983 // create file filter
3984 // folder type
3985 QCOMPARE( def->createFileFilter(), QString() );
3986 def.reset( new QgsProcessingParameterFile( "optional", QString(), QgsProcessingParameterFile::File, QString(), QString( "/home/me" ), true ) );
3987 // no filter/extension
3988 QCOMPARE( def->createFileFilter(), QStringLiteral( "All files (*.*)" ) );
3989 def->setExtension( QStringLiteral( "png" ) );
3990 QCOMPARE( def->createFileFilter(), QStringLiteral( "PNG files (*.png);;All files (*.*)" ) );
3991 def->setFileFilter( QStringLiteral( "PNG Files (*.png);;BMP Files (*.bmp)" ) );
3992 QCOMPARE( def->createFileFilter(), QStringLiteral( "PNG Files (*.png);;BMP Files (*.bmp);;All files (*.*)" ) );
3993 def->setFileFilter( QStringLiteral( "All files (*.*)" ) );
3994 QCOMPARE( def->createFileFilter(), QStringLiteral( "All files (*.*)" ) );
3995 }
3996
parameterMatrix()3997 void TestQgsProcessing::parameterMatrix()
3998 {
3999 QgsProcessingContext context;
4000
4001 // not optional!
4002 std::unique_ptr< QgsProcessingParameterMatrix > def( new QgsProcessingParameterMatrix( "non_optional", QString(), 3, false, QStringList(), QVariant(), false ) );
4003 QVERIFY( !def->checkValueIsAcceptable( false ) );
4004 QVERIFY( !def->checkValueIsAcceptable( true ) );
4005 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4006 QVERIFY( def->checkValueIsAcceptable( "1,2,3" ) );
4007 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
4008 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1 << 2 << 3 ) );
4009 QVERIFY( def->checkValueIsAcceptable( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ) ) );
4010 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4011 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
4012
4013 // list
4014 QVariantMap params;
4015 params.insert( "non_optional", QVariantList() << 1 << 2 << 3 );
4016 QCOMPARE( QgsProcessingParameters::parameterAsMatrix( def.get(), params, context ), QVariantList() << 1 << 2 << 3 );
4017
4018 //string
4019 params.insert( "non_optional", QString( "4,5,6" ) );
4020 QCOMPARE( QgsProcessingParameters::parameterAsMatrix( def.get(), params, context ), QVariantList() << 4 << 5 << 6 );
4021
4022 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4023 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "[5]" ) );
4024 QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2.5 << 3, context ), QStringLiteral( "[1,2.5,3]" ) );
4025 QCOMPARE( def->valueAsPythonString( QVariantList() << ( QVariantList() << 1 << 2 << 3 ) << ( QVariantList() << 1 << 2 << 3 ), context ), QStringLiteral( "[1,2,3,1,2,3]" ) );
4026 QCOMPARE( def->valueAsPythonString( QVariantList() << ( QVariantList() << 1 << QStringLiteral( "value" ) << 3 ) << ( QVariantList() << 1 << 2 << QStringLiteral( "it's a value" ) ), context ), QStringLiteral( "[1,'value',3,1,2,\"it's a value\"]" ) );
4027 QCOMPARE( def->valueAsPythonString( QVariantList() << ( QVariantList() << 1 << QVariant() << 3 ) << ( QVariantList() << QVariant() << 2 << 3 ), context ), QStringLiteral( "[1,None,3,None,2,3]" ) );
4028 QCOMPARE( def->valueAsPythonString( QVariantList() << ( QVariantList() << 1 << QString( "" ) << 3 ) << ( QVariantList() << 1 << 2 << QString( "" ) ), context ), QStringLiteral( "[1,'',3,1,2,'']" ) );
4029 QCOMPARE( def->valueAsPythonString( "1,2,3", context ), QStringLiteral( "[1,2,3]" ) );
4030 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4031
4032 QString pythonCode = def->asPythonString();
4033 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('non_optional', '', numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=None)" ) );
4034
4035 QString code = def->asScriptCode();
4036 QCOMPARE( code, QStringLiteral( "##non_optional=matrix" ) );
4037 std::unique_ptr< QgsProcessingParameterMatrix > fromCode( dynamic_cast< QgsProcessingParameterMatrix * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4038 QVERIFY( fromCode.get() );
4039 QCOMPARE( fromCode->name(), def->name() );
4040 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
4041 QCOMPARE( fromCode->flags(), def->flags() );
4042 QVERIFY( !fromCode->defaultValue().isValid() );
4043
4044 const QVariantMap map = def->toVariantMap();
4045 QgsProcessingParameterMatrix fromMap( "x" );
4046 QVERIFY( fromMap.fromVariantMap( map ) );
4047 QCOMPARE( fromMap.name(), def->name() );
4048 QCOMPARE( fromMap.description(), def->description() );
4049 QCOMPARE( fromMap.flags(), def->flags() );
4050 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4051 QCOMPARE( fromMap.headers(), def->headers() );
4052 QCOMPARE( fromMap.numberRows(), def->numberRows() );
4053 QCOMPARE( fromMap.hasFixedNumberRows(), def->hasFixedNumberRows() );
4054 def.reset( dynamic_cast< QgsProcessingParameterMatrix *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4055 QVERIFY( dynamic_cast< QgsProcessingParameterMatrix *>( def.get() ) );
4056
4057 // optional
4058 def.reset( new QgsProcessingParameterMatrix( "optional", QString(), 3, false, QStringList(), QVariantList() << 4 << 5 << 6, true ) );
4059 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4060 QVERIFY( def->checkValueIsAcceptable( "1,2,3" ) );
4061 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1 << 2 << 3 ) );
4062 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
4063 QVERIFY( def->checkValueIsAcceptable( "" ) );
4064 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4065
4066 pythonCode = def->asPythonString();
4067 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('optional', '', optional=True, numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=[4,5,6])" ) );
4068
4069 code = def->asScriptCode();
4070 QCOMPARE( code, QStringLiteral( "##optional=optional matrix" ) );
4071 fromCode.reset( dynamic_cast< QgsProcessingParameterMatrix * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4072 QVERIFY( fromCode.get() );
4073 QCOMPARE( fromCode->name(), def->name() );
4074 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4075 QCOMPARE( fromCode->flags(), def->flags() );
4076 QVERIFY( !fromCode->defaultValue().isValid() );
4077
4078 params.insert( "optional", QVariant() );
4079 QCOMPARE( QgsProcessingParameters::parameterAsMatrix( def.get(), params, context ), QVariantList() << 4 << 5 << 6 );
4080 def.reset( new QgsProcessingParameterMatrix( "optional", QString(), 3, false, QStringList(), QString( "1,2,3" ), true ) );
4081
4082 pythonCode = def->asPythonString();
4083 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMatrix('optional', '', optional=True, numberRows=3, hasFixedNumberRows=False, headers=[], defaultValue=[1,2,3])" ) );
4084
4085 code = def->asScriptCode();
4086 QCOMPARE( code, QStringLiteral( "##optional=optional matrix 1,2,3" ) );
4087 fromCode.reset( dynamic_cast< QgsProcessingParameterMatrix * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4088 QVERIFY( fromCode.get() );
4089 QCOMPARE( fromCode->name(), def->name() );
4090 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4091 QCOMPARE( fromCode->flags(), def->flags() );
4092 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4093
4094 params.insert( "optional", QVariant() );
4095 QCOMPARE( QgsProcessingParameters::parameterAsMatrix( def.get(), params, context ), QVariantList() << 1 << 2 << 3 );
4096 }
4097
parameterLayerList()4098 void TestQgsProcessing::parameterLayerList()
4099 {
4100 // setup a context
4101 QgsProject p;
4102 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
4103 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
4104 const QString raster1 = testDataDir + "tenbytenraster.asc";
4105 const QString raster2 = testDataDir + "landsat.tif";
4106 const QFileInfo fi1( raster1 );
4107 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
4108 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" );
4109 QgsVectorLayer *v2 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V5", "memory" );
4110 p.addMapLayers( QList<QgsMapLayer *>() << v1 << v2 << r1 );
4111 QgsProcessingContext context;
4112 context.setProject( &p );
4113
4114 // not optional!
4115 std::unique_ptr< QgsProcessingParameterMultipleLayers > def( new QgsProcessingParameterMultipleLayers( "non_optional", QString(), QgsProcessing::TypeMapLayer, QString(), false ) );
4116 QVERIFY( !def->checkValueIsAcceptable( false ) );
4117 QVERIFY( !def->checkValueIsAcceptable( true ) );
4118 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4119 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
4120 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4121 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
4122 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
4123 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
4124
4125 // should be OK
4126 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4127 QVERIFY( def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4128 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4129
4130 // ... unless we use context, when the check that the layer actually exists is performed
4131 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4132 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4133 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4134
4135 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
4136 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
4137 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
4138 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
4139
4140 // using existing map layer ID
4141 QVariantMap params;
4142 params.insert( "non_optional", v1->id() );
4143 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 );
4144 // using existing map layer
4145 params.insert( "non_optional", QVariant::fromValue( v1 ) );
4146 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 );
4147
4148 // using two existing map layer ID
4149 params.insert( "non_optional", QVariantList() << v1->id() << r1->id() );
4150 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 );
4151
4152 // using two existing map layers
4153 params.insert( "non_optional", QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( r1 ) );
4154 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 );
4155
4156 // mix of list and single layers (happens from models)
4157 params.insert( "non_optional", QVariantList() << QVariant( QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( v2 ) ) << QVariant::fromValue( r1 ) );
4158 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 << r1 );
4159
4160 // mix of two lists (happens from models)
4161 params.insert( "non_optional", QVariantList() << QVariant( QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( v2 ) ) << QVariant( QVariantList() << QVariant::fromValue( r1 ) ) );
4162 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 << r1 );
4163
4164 // mix of existing layers and non project layer string
4165 params.insert( "non_optional", QVariantList() << v1->id() << raster2 );
4166 const QList< QgsMapLayer *> layers = QgsProcessingParameters::parameterAsLayerList( def.get(), params, context );
4167 QCOMPARE( layers.at( 0 ), v1 );
4168 QCOMPARE( layers.at( 1 )->publicSource(), raster2 );
4169
4170 // mix of existing layer and ID (and check we keep order)
4171 params.insert( "non_optional", QVariantList() << QVariant::fromValue( v1 ) << v2->id() );
4172 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 );
4173
4174 params.insert( "non_optional", QVariantList() << v1->id() << QVariant::fromValue( v2 ) );
4175 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << v2 );
4176
4177 // empty string
4178 params.insert( "non_optional", QString( "" ) );
4179 QVERIFY( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ).isEmpty() );
4180
4181 // nonsense string
4182 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
4183 QVERIFY( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ).isEmpty() );
4184
4185 // with 2 min inputs
4186 def->setMinimumNumberInputs( 2 );
4187 QVERIFY( !def->checkValueIsAcceptable( false ) );
4188 QVERIFY( !def->checkValueIsAcceptable( true ) );
4189 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4190 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4191 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
4192 QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
4193 QVERIFY( def->checkValueIsAcceptable( QStringList() << "layer12312312" << "layerB" ) );
4194 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "layer12312312" << "layerB" ) );
4195 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4196 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
4197 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4198 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4199 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4200 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4201 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4202 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4203
4204 def->setMinimumNumberInputs( 3 );
4205 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "layer12312312" << "layerB" ) );
4206 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "layer12312312" << "layerB" ) );
4207
4208 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4209 QCOMPARE( def->valueAsPythonString( "layer12312312", context ), QStringLiteral( "'layer12312312'" ) );
4210 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc']" ) ) );
4211 QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc']" ) ) );
4212 QCOMPARE( def->valueAsPythonString( QStringList() << r1->id() << raster2, context ), QString( QStringLiteral( "['" ) + testDataDir + QStringLiteral( "tenbytenraster.asc','" ) + testDataDir + QStringLiteral( "landsat.tif']" ) ) );
4213 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4214 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
4215
4216 QString pythonCode = def->asPythonString();
4217 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('non_optional', '', layerType=QgsProcessing.TypeMapLayer, defaultValue='')" ) );
4218
4219 QString code = def->asScriptCode();
4220 QCOMPARE( code, QStringLiteral( "##non_optional=multiple vector" ) );
4221 std::unique_ptr< QgsProcessingParameterMultipleLayers > fromCode( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4222 QVERIFY( fromCode.get() );
4223 QCOMPARE( fromCode->name(), def->name() );
4224 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
4225 QCOMPARE( fromCode->flags(), def->flags() );
4226 QVERIFY( !fromCode->defaultValue().isValid() );
4227 QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry );
4228
4229 const QVariantMap map = def->toVariantMap();
4230 QgsProcessingParameterMultipleLayers fromMap( "x" );
4231 QVERIFY( fromMap.fromVariantMap( map ) );
4232 QCOMPARE( fromMap.name(), def->name() );
4233 QCOMPARE( fromMap.description(), def->description() );
4234 QCOMPARE( fromMap.flags(), def->flags() );
4235 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4236 QCOMPARE( fromMap.layerType(), def->layerType() );
4237 QCOMPARE( fromMap.minimumNumberInputs(), def->minimumNumberInputs() );
4238 def.reset( dynamic_cast< QgsProcessingParameterMultipleLayers *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4239 QVERIFY( dynamic_cast< QgsProcessingParameterMultipleLayers *>( def.get() ) );
4240
4241 // optional with one default layer
4242 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, v1->id(), true ) );
4243 QVERIFY( !def->checkValueIsAcceptable( false ) );
4244 QVERIFY( !def->checkValueIsAcceptable( true ) );
4245 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4246 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
4247 QVERIFY( def->checkValueIsAcceptable( "" ) );
4248 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4249
4250 // should be OK
4251 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4252 QVERIFY( def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4253 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4254
4255 // ... unless we use context, when the check that the layer actually exists is performed
4256 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4257 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4258 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
4259
4260 params.insert( "optional", QVariant() );
4261 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 );
4262
4263 params.insert( "optional", QVariantList() << QVariant::fromValue( r1 ) );
4264 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << r1 );
4265
4266 pythonCode = def->asPythonString();
4267 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterMultipleLayers('optional', '', optional=True, layerType=QgsProcessing.TypeMapLayer, defaultValue='" ) + v1->id() + "')" ) );
4268
4269 code = def->asScriptCode();
4270 QCOMPARE( code, QString( QStringLiteral( "##optional=optional multiple vector " ) + v1->id() ) );
4271 fromCode.reset( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4272 QVERIFY( fromCode.get() );
4273 QCOMPARE( fromCode->name(), def->name() );
4274 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4275 QCOMPARE( fromCode->flags(), def->flags() );
4276 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4277 QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry );
4278
4279 // optional with two default layers
4280 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariantList() << v1->id() << r1->publicSource(), true ) );
4281 params.insert( "optional", QVariant() );
4282 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 );
4283 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
4284 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
4285 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
4286 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
4287
4288 pythonCode = def->asPythonString();
4289 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterMultipleLayers('optional', '', optional=True, layerType=QgsProcessing.TypeMapLayer, defaultValue=['" ) + r1->publicSource() + "'])" ) );
4290
4291 code = def->asScriptCode();
4292 QCOMPARE( code, QString( QStringLiteral( "##optional=optional multiple vector " ) + v1->id() + "," + r1->publicSource() ) );
4293 fromCode.reset( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4294 QVERIFY( fromCode.get() );
4295 QCOMPARE( fromCode->name(), def->name() );
4296 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4297 QCOMPARE( fromCode->flags(), def->flags() );
4298 QCOMPARE( fromCode->defaultValue().toString(), QString( v1->id() + "," + r1->publicSource() ) );
4299 QCOMPARE( fromCode->layerType(), QgsProcessing::TypeVectorAnyGeometry );
4300
4301 // optional with one default direct layer
4302 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariant::fromValue( v1 ), true ) );
4303 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 );
4304
4305 // optional with two default direct layers
4306 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeMapLayer, QVariantList() << QVariant::fromValue( v1 ) << QVariant::fromValue( r1 ), true ) );
4307 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << v1 << r1 );
4308
4309 def.reset( new QgsProcessingParameterMultipleLayers( "type", QString(), QgsProcessing::TypeRaster ) );
4310 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
4311 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
4312 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
4313 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
4314
4315 pythonCode = def->asPythonString();
4316 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('type', '', layerType=QgsProcessing.TypeRaster, defaultValue=None)" ) );
4317 code = def->asScriptCode();
4318 QCOMPARE( code, QStringLiteral( "##type=multiple raster" ) );
4319 fromCode.reset( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4320 QVERIFY( fromCode.get() );
4321 QCOMPARE( fromCode->name(), def->name() );
4322 QCOMPARE( fromCode->description(), QStringLiteral( "type" ) );
4323 QCOMPARE( fromCode->flags(), def->flags() );
4324 QVERIFY( !fromCode->defaultValue().isValid() );
4325 QCOMPARE( fromCode->layerType(), QgsProcessing::TypeRaster );
4326
4327 def.reset( new QgsProcessingParameterMultipleLayers( "type", QString(), QgsProcessing::TypeFile ) );
4328 QCOMPARE( def->createFileFilter(), QStringLiteral( "All files (*.*)" ) );
4329
4330 pythonCode = def->asPythonString();
4331 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMultipleLayers('type', '', layerType=QgsProcessing.TypeFile, defaultValue=None)" ) );
4332 code = def->asScriptCode();
4333 QCOMPARE( code, QStringLiteral( "##type=multiple file" ) );
4334 fromCode.reset( dynamic_cast< QgsProcessingParameterMultipleLayers * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4335 QVERIFY( fromCode.get() );
4336 QCOMPARE( fromCode->name(), def->name() );
4337 QCOMPARE( fromCode->description(), QStringLiteral( "type" ) );
4338 QCOMPARE( fromCode->flags(), def->flags() );
4339 QVERIFY( !fromCode->defaultValue().isValid() );
4340 QCOMPARE( fromCode->layerType(), def->layerType() );
4341
4342 // manage QgsProcessingOutputLayerDefinition as parameter value
4343
4344 // optional with sink to a QgsMapLayer.id()
4345 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeFile ) );
4346 params.insert( QString( "optional" ), QgsProcessingOutputLayerDefinition( r1->publicSource() ) );
4347 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() << r1 );
4348
4349 // optional with sink to an empty string
4350 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeFile ) );
4351 params.insert( QString( "optional" ), QgsProcessingOutputLayerDefinition( QString() ) );
4352 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() );
4353
4354 // optional with sink to an nonsense string
4355 def.reset( new QgsProcessingParameterMultipleLayers( "optional", QString(), QgsProcessing::TypeFile ) );
4356 params.insert( QString( "optional" ), QgsProcessingOutputLayerDefinition( QString( "i'm not a layer, and nothing you can do will make me one" ) ) );
4357 QCOMPARE( QgsProcessingParameters::parameterAsLayerList( def.get(), params, context ), QList< QgsMapLayer *>() );
4358
4359
4360 // TypeFile
4361 def = std::make_unique< QgsProcessingParameterMultipleLayers >( "non_optional", QString(), QgsProcessing::TypeFile, QString(), false );
4362 QVERIFY( !def->checkValueIsAcceptable( false ) );
4363 QVERIFY( !def->checkValueIsAcceptable( true ) );
4364 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4365 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
4366 QVERIFY( def->checkValueIsAcceptable( QStringList() << "layer12312312" << "a" ) );
4367 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4368 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
4369 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
4370 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
4371 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4372 QVERIFY( def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4373 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
4374
4375 params.clear();
4376 params.insert( "non_optional", QString( "a" ) );
4377 QCOMPARE( QgsProcessingParameters::parameterAsFileList( def.get(), params, context ), QStringList() << QStringLiteral( "a" ) );
4378 params.insert( "non_optional", QStringList() << "a" );
4379 QCOMPARE( QgsProcessingParameters::parameterAsFileList( def.get(), params, context ), QStringList() << QStringLiteral( "a" ) );
4380 params.insert( "non_optional", QStringList() << "a" << "b" );
4381 QCOMPARE( QgsProcessingParameters::parameterAsFileList( def.get(), params, context ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) );
4382 params.insert( "non_optional", QVariantList() << QStringLiteral( "c" ) << QStringLiteral( "d" ) );
4383 QCOMPARE( QgsProcessingParameters::parameterAsFileList( def.get(), params, context ), QStringList() << QStringLiteral( "c" ) << QStringLiteral( "d" ) );
4384 // mix of two lists (happens from models)
4385 params.insert( "non_optional", QVariantList() << QVariant( QVariantList() << QStringLiteral( "e" ) << QStringLiteral( "f" ) ) << QVariant( QVariantList() << QStringLiteral( "g" ) ) );
4386 QCOMPARE( QgsProcessingParameters::parameterAsFileList( def.get(), params, context ), QStringList() << QStringLiteral( "e" ) << QStringLiteral( "f" ) << QStringLiteral( "g" ) );
4387
4388 // with 2 min inputs
4389 def->setMinimumNumberInputs( 2 );
4390 QVERIFY( !def->checkValueIsAcceptable( false ) );
4391 QVERIFY( !def->checkValueIsAcceptable( true ) );
4392 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4393 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4394 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
4395 QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
4396 QVERIFY( def->checkValueIsAcceptable( QStringList() << "layer12312312" << "layerB" ) );
4397 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "layer12312312" << "layerB" ) );
4398
4399 def->setMinimumNumberInputs( 3 );
4400 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "layer12312312" << "layerB" ) );
4401 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "layer12312312" << "layerB" ) );
4402
4403 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4404 QCOMPARE( def->valueAsPythonString( "layer12312312", context ), QStringLiteral( "'layer12312312'" ) );
4405 QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "B", context ), QStringLiteral( "['a','B']" ) );
4406 QCOMPARE( def->valueAsPythonString( QVariantList() << "c" << "d", context ), QStringLiteral( "['c','d']" ) );
4407 }
4408
parameterDistance()4409 void TestQgsProcessing::parameterDistance()
4410 {
4411 QgsProcessingContext context;
4412
4413 // not optional!
4414 std::unique_ptr< QgsProcessingParameterDistance > def( new QgsProcessingParameterDistance( "non_optional", QString(), 5, QStringLiteral( "parent" ), false ) );
4415 QCOMPARE( def->parentParameterName(), QStringLiteral( "parent" ) );
4416 def->setParentParameterName( QStringLiteral( "parent2" ) );
4417 QCOMPARE( def->defaultUnit(), QgsUnitTypes::DistanceUnknownUnit );
4418 def->setDefaultUnit( QgsUnitTypes::DistanceFeet );
4419 QCOMPARE( def->defaultUnit(), QgsUnitTypes::DistanceFeet );
4420 std::unique_ptr< QgsProcessingParameterDistance > clone( def->clone() );
4421 QCOMPARE( clone->parentParameterName(), QStringLiteral( "parent2" ) );
4422 QCOMPARE( clone->defaultUnit(), QgsUnitTypes::DistanceFeet );
4423
4424 QCOMPARE( def->parentParameterName(), QStringLiteral( "parent2" ) );
4425 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4426 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4427 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4428 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4429 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4430 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
4431
4432 // string representing a number
4433 QVariantMap params;
4434 params.insert( "non_optional", QString( "1.1" ) );
4435 double number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4436 QGSCOMPARENEAR( number, 1.1, 0.001 );
4437
4438 // double
4439 params.insert( "non_optional", 1.1 );
4440 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4441 QGSCOMPARENEAR( number, 1.1, 0.001 );
4442 // int
4443 params.insert( "non_optional", 1 );
4444 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4445 QGSCOMPARENEAR( number, 1, 0.001 );
4446
4447 // nonsense string
4448 params.insert( "non_optional", QString( "i'm not a number, and nothing you can do will make me one" ) );
4449 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4450 QCOMPARE( number, 5.0 );
4451
4452 // with min value
4453 def->setMinimum( 11 );
4454 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4455 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
4456 QVERIFY( def->checkValueIsAcceptable( 25 ) );
4457 QVERIFY( def->checkValueIsAcceptable( "21.1" ) );
4458 // with max value
4459 def->setMaximum( 21 );
4460 QVERIFY( !def->checkValueIsAcceptable( 35 ) );
4461 QVERIFY( !def->checkValueIsAcceptable( "31.1" ) );
4462 QVERIFY( def->checkValueIsAcceptable( 15 ) );
4463 QVERIFY( def->checkValueIsAcceptable( "11.1" ) );
4464
4465 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4466 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
4467 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) );
4468 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4469
4470 const QVariantMap map = def->toVariantMap();
4471 QgsProcessingParameterDistance fromMap( "x" );
4472 QVERIFY( fromMap.fromVariantMap( map ) );
4473 QCOMPARE( fromMap.name(), def->name() );
4474 QCOMPARE( fromMap.description(), def->description() );
4475 QCOMPARE( fromMap.flags(), def->flags() );
4476 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4477 QCOMPARE( fromMap.minimum(), def->minimum() );
4478 QCOMPARE( fromMap.maximum(), def->maximum() );
4479 QCOMPARE( fromMap.dataType(), def->dataType() );
4480 QCOMPARE( fromMap.parentParameterName(), QStringLiteral( "parent2" ) );
4481 QCOMPARE( fromMap.defaultUnit(), QgsUnitTypes::DistanceFeet );
4482 def.reset( dynamic_cast< QgsProcessingParameterDistance *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4483 QVERIFY( dynamic_cast< QgsProcessingParameterDistance *>( def.get() ) );
4484
4485 // optional
4486 def.reset( new QgsProcessingParameterDistance( "optional", QString(), 5.4, QStringLiteral( "parent" ), true ) );
4487 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4488 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4489 QVERIFY( def->checkValueIsAcceptable( "" ) );
4490 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4491
4492 params.insert( "optional", QVariant() );
4493 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4494 QGSCOMPARENEAR( number, 5.4, 0.001 );
4495 // unconvertible string
4496 params.insert( "optional", QVariant( "aaaa" ) );
4497 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4498 QGSCOMPARENEAR( number, 5.4, 0.001 );
4499
4500 // non-optional, invalid default
4501 def.reset( new QgsProcessingParameterDistance( "non_optional", QString(), QVariant(), QStringLiteral( "parent" ), false ) );
4502 QCOMPARE( def->parentParameterName(), QStringLiteral( "parent" ) );
4503 def->setParentParameterName( QStringLiteral( "parent2" ) );
4504 QCOMPARE( def->parentParameterName(), QStringLiteral( "parent2" ) );
4505 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4506 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4507 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4508 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4509 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4510 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, falls back to invalid default value
4511 }
4512
parameterDuration()4513 void TestQgsProcessing::parameterDuration()
4514 {
4515 QgsProcessingContext context;
4516
4517 // not optional!
4518 std::unique_ptr< QgsProcessingParameterDuration > def( new QgsProcessingParameterDuration( "non_optional", QString(), 5, false ) );
4519 QCOMPARE( def->defaultUnit(), QgsUnitTypes::TemporalMilliseconds );
4520 def->setDefaultUnit( QgsUnitTypes::TemporalDays );
4521 QCOMPARE( def->defaultUnit(), QgsUnitTypes::TemporalDays );
4522 std::unique_ptr< QgsProcessingParameterDuration > clone( def->clone() );
4523 QCOMPARE( clone->defaultUnit(), QgsUnitTypes::TemporalDays );
4524
4525 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4526 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4527 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4528 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4529 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4530 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
4531
4532 // string representing a number
4533 QVariantMap params;
4534 params.insert( "non_optional", QString( "1.1" ) );
4535 double number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4536 QGSCOMPARENEAR( number, 1.1, 0.001 );
4537
4538 // double
4539 params.insert( "non_optional", 1.1 );
4540 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4541 QGSCOMPARENEAR( number, 1.1, 0.001 );
4542 // int
4543 params.insert( "non_optional", 1 );
4544 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4545 QGSCOMPARENEAR( number, 1, 0.001 );
4546
4547 // nonsense string
4548 params.insert( "non_optional", QString( "i'm not a number, and nothing you can do will make me one" ) );
4549 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4550 QCOMPARE( number, 5.0 );
4551
4552 // with min value
4553 def->setMinimum( 11 );
4554 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4555 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
4556 QVERIFY( def->checkValueIsAcceptable( 25 ) );
4557 QVERIFY( def->checkValueIsAcceptable( "21.1" ) );
4558 // with max value
4559 def->setMaximum( 21 );
4560 QVERIFY( !def->checkValueIsAcceptable( 35 ) );
4561 QVERIFY( !def->checkValueIsAcceptable( "31.1" ) );
4562 QVERIFY( def->checkValueIsAcceptable( 15 ) );
4563 QVERIFY( def->checkValueIsAcceptable( "11.1" ) );
4564
4565 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4566 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
4567 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) );
4568 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4569
4570 const QVariantMap map = def->toVariantMap();
4571 QgsProcessingParameterDuration fromMap( "x" );
4572 QVERIFY( fromMap.fromVariantMap( map ) );
4573 QCOMPARE( fromMap.name(), def->name() );
4574 QCOMPARE( fromMap.description(), def->description() );
4575 QCOMPARE( fromMap.flags(), def->flags() );
4576 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4577 QCOMPARE( fromMap.minimum(), def->minimum() );
4578 QCOMPARE( fromMap.maximum(), def->maximum() );
4579 QCOMPARE( fromMap.dataType(), def->dataType() );
4580 QCOMPARE( fromMap.defaultUnit(), QgsUnitTypes::TemporalDays );
4581 def.reset( dynamic_cast< QgsProcessingParameterDuration *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4582 QVERIFY( dynamic_cast< QgsProcessingParameterDuration *>( def.get() ) );
4583
4584 // optional
4585 def.reset( new QgsProcessingParameterDuration( "optional", QString(), 5.4, true ) );
4586 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4587 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4588 QVERIFY( def->checkValueIsAcceptable( "" ) );
4589 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4590
4591 params.insert( "optional", QVariant() );
4592 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4593 QGSCOMPARENEAR( number, 5.4, 0.001 );
4594 // unconvertible string
4595 params.insert( "optional", QVariant( "aaaa" ) );
4596 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4597 QGSCOMPARENEAR( number, 5.4, 0.001 );
4598
4599 // non-optional, invalid default
4600 def.reset( new QgsProcessingParameterDuration( "non_optional", QString(), QVariant(), false ) );
4601 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4602 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4603 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4604 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4605 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4606 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, falls back to invalid default value
4607 }
4608
parameterScale()4609 void TestQgsProcessing::parameterScale()
4610 {
4611 QgsProcessingContext context;
4612
4613 // not optional!
4614 std::unique_ptr< QgsProcessingParameterScale > def( new QgsProcessingParameterScale( "non_optional", QString(), 5, false ) );
4615
4616 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4617 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4618 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4619 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4620 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4621 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
4622
4623 // string representing a number
4624 QVariantMap params;
4625 params.insert( "non_optional", QString( "1.1" ) );
4626 double number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4627 QGSCOMPARENEAR( number, 1.1, 0.001 );
4628
4629 // double
4630 params.insert( "non_optional", 1.1 );
4631 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4632 QGSCOMPARENEAR( number, 1.1, 0.001 );
4633 // int
4634 params.insert( "non_optional", 1 );
4635 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4636 QGSCOMPARENEAR( number, 1, 0.001 );
4637
4638 // nonsense string
4639 params.insert( "non_optional", QString( "i'm not a number, and nothing you can do will make me one" ) );
4640 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4641 QCOMPARE( number, 5.0 );
4642
4643 // with min value
4644 def->setMinimum( 11 );
4645 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4646 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
4647 QVERIFY( def->checkValueIsAcceptable( 25 ) );
4648 QVERIFY( def->checkValueIsAcceptable( "21.1" ) );
4649 // with max value
4650 def->setMaximum( 21 );
4651 QVERIFY( !def->checkValueIsAcceptable( 35 ) );
4652 QVERIFY( !def->checkValueIsAcceptable( "31.1" ) );
4653 QVERIFY( def->checkValueIsAcceptable( 15 ) );
4654 QVERIFY( def->checkValueIsAcceptable( "11.1" ) );
4655
4656 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4657 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
4658 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) );
4659 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4660
4661 const QVariantMap map = def->toVariantMap();
4662 QgsProcessingParameterScale fromMap( "x" );
4663 QVERIFY( fromMap.fromVariantMap( map ) );
4664 QCOMPARE( fromMap.name(), def->name() );
4665 QCOMPARE( fromMap.description(), def->description() );
4666 QCOMPARE( fromMap.flags(), def->flags() );
4667 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4668 def.reset( dynamic_cast< QgsProcessingParameterScale *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4669 QVERIFY( dynamic_cast< QgsProcessingParameterScale *>( def.get() ) );
4670
4671 const QString pythonCode = def->asPythonString();
4672 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterScale('non_optional', '', defaultValue=5)" ) );
4673
4674 const QString code = def->asScriptCode();
4675 QCOMPARE( code, QStringLiteral( "##non_optional=scale 5" ) );
4676 std::unique_ptr< QgsProcessingParameterScale > fromCode( dynamic_cast< QgsProcessingParameterScale * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4677 QVERIFY( fromCode.get() );
4678 QCOMPARE( fromCode->name(), def->name() );
4679 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
4680 QCOMPARE( fromCode->flags(), def->flags() );
4681 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4682
4683 // optional
4684 def.reset( new QgsProcessingParameterScale( "optional", QString(), 5.4, true ) );
4685 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4686 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4687 QVERIFY( def->checkValueIsAcceptable( "" ) );
4688 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4689
4690 params.insert( "optional", QVariant() );
4691 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4692 QGSCOMPARENEAR( number, 5.4, 0.001 );
4693 // unconvertible string
4694 params.insert( "optional", QVariant( "aaaa" ) );
4695 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4696 QGSCOMPARENEAR( number, 5.4, 0.001 );
4697
4698 // non-optional, invalid default
4699 def.reset( new QgsProcessingParameterScale( "non_optional", QString(), QVariant(), false ) );
4700 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4701 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4702 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4703 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4704 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4705 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, falls back to invalid default value
4706 }
4707
parameterNumber()4708 void TestQgsProcessing::parameterNumber()
4709 {
4710 QgsProcessingContext context;
4711
4712 // not optional!
4713 std::unique_ptr< QgsProcessingParameterNumber > def( new QgsProcessingParameterNumber( "non_optional", QString(), QgsProcessingParameterNumber::Double, 5, false ) );
4714 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4715 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4716 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4717 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4718 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4719 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
4720
4721 // string representing a number
4722 QVariantMap params;
4723 params.insert( "non_optional", QString( "1.1" ) );
4724 double number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4725 QGSCOMPARENEAR( number, 1.1, 0.001 );
4726 int iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4727 QCOMPARE( iNumber, 1 );
4728
4729 // double
4730 params.insert( "non_optional", 1.1 );
4731 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4732 QGSCOMPARENEAR( number, 1.1, 0.001 );
4733 iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4734 QCOMPARE( iNumber, 1 );
4735
4736 // int
4737 params.insert( "non_optional", 1 );
4738 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4739 QGSCOMPARENEAR( number, 1, 0.001 );
4740 iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4741 QCOMPARE( iNumber, 1 );
4742
4743 // nonsense string
4744 params.insert( "non_optional", QString( "i'm not a number, and nothing you can do will make me one" ) );
4745 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4746 QCOMPARE( number, 5.0 );
4747 iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4748 QCOMPARE( iNumber, 5 );
4749
4750 // with min value
4751 def->setMinimum( 11 );
4752 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4753 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
4754 QVERIFY( def->checkValueIsAcceptable( 25 ) );
4755 QVERIFY( def->checkValueIsAcceptable( "21.1" ) );
4756 // with max value
4757 def->setMaximum( 21 );
4758 QVERIFY( !def->checkValueIsAcceptable( 35 ) );
4759 QVERIFY( !def->checkValueIsAcceptable( "31.1" ) );
4760 QVERIFY( def->checkValueIsAcceptable( 15 ) );
4761 QVERIFY( def->checkValueIsAcceptable( "11.1" ) );
4762
4763 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4764 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
4765 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1.1" ) );
4766 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4767
4768 QString pythonCode = def->asPythonString();
4769 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterNumber('non_optional', '', type=QgsProcessingParameterNumber.Double, minValue=11, maxValue=21, defaultValue=5)" ) );
4770
4771 QString code = def->asScriptCode();
4772 QCOMPARE( code, QStringLiteral( "##non_optional=number 5" ) );
4773 std::unique_ptr< QgsProcessingParameterNumber > fromCode( dynamic_cast< QgsProcessingParameterNumber * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4774 QVERIFY( fromCode.get() );
4775 QCOMPARE( fromCode->name(), def->name() );
4776 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
4777 QCOMPARE( fromCode->flags(), def->flags() );
4778 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4779
4780
4781 const QVariantMap map = def->toVariantMap();
4782 QgsProcessingParameterNumber fromMap( "x" );
4783 QVERIFY( fromMap.fromVariantMap( map ) );
4784 QCOMPARE( fromMap.name(), def->name() );
4785 QCOMPARE( fromMap.description(), def->description() );
4786 QCOMPARE( fromMap.flags(), def->flags() );
4787 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4788 QCOMPARE( fromMap.minimum(), def->minimum() );
4789 QCOMPARE( fromMap.maximum(), def->maximum() );
4790 QCOMPARE( fromMap.dataType(), def->dataType() );
4791 def.reset( dynamic_cast< QgsProcessingParameterNumber *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4792 QVERIFY( dynamic_cast< QgsProcessingParameterNumber *>( def.get() ) );
4793
4794 // optional
4795 def.reset( new QgsProcessingParameterNumber( "optional", QString(), QgsProcessingParameterNumber::Double, 5.4, true ) );
4796 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4797 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4798 QVERIFY( def->checkValueIsAcceptable( "" ) );
4799 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4800
4801 params.insert( "optional", QVariant() );
4802 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4803 QGSCOMPARENEAR( number, 5.4, 0.001 );
4804 iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4805 QCOMPARE( iNumber, 5 );
4806 // unconvertible string
4807 params.insert( "optional", QVariant( "aaaa" ) );
4808 number = QgsProcessingParameters::parameterAsDouble( def.get(), params, context );
4809 QGSCOMPARENEAR( number, 5.4, 0.001 );
4810 iNumber = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
4811 QCOMPARE( iNumber, 5 );
4812
4813 pythonCode = def->asPythonString();
4814 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterNumber('optional', '', optional=True, type=QgsProcessingParameterNumber.Double, defaultValue=5.4)" ) );
4815
4816 code = def->asScriptCode();
4817 QCOMPARE( code.left( 30 ), QStringLiteral( "##optional=optional number 5.4" ) ); // truncate code to 30, to avoid Qt 5.6 rounding issues
4818 fromCode.reset( dynamic_cast< QgsProcessingParameterNumber * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4819 QVERIFY( fromCode.get() );
4820 QCOMPARE( fromCode->name(), def->name() );
4821 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4822 QCOMPARE( fromCode->flags(), def->flags() );
4823 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4824
4825 fromCode.reset( dynamic_cast< QgsProcessingParameterNumber * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional number None" ) ) ) );
4826 QVERIFY( fromCode.get() );
4827 QCOMPARE( fromCode->name(), def->name() );
4828 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4829 QCOMPARE( fromCode->flags(), def->flags() );
4830 QVERIFY( !fromCode->defaultValue().isValid() );
4831
4832 // non-optional, invalid default
4833 def.reset( new QgsProcessingParameterNumber( "non_optional", QString(), QgsProcessingParameterNumber::Double, QVariant(), false ) );
4834 QVERIFY( def->checkValueIsAcceptable( 5 ) );
4835 QVERIFY( def->checkValueIsAcceptable( "1.1" ) );
4836 QVERIFY( !def->checkValueIsAcceptable( "1.1,2" ) );
4837 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4838 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4839 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, falls back to invalid default value
4840 }
4841
parameterRange()4842 void TestQgsProcessing::parameterRange()
4843 {
4844 QgsProcessingContext context;
4845
4846 // not optional!
4847 std::unique_ptr< QgsProcessingParameterRange > def( new QgsProcessingParameterRange( "non_optional", QString(), QgsProcessingParameterNumber::Double, QString( "5,6" ), false ) );
4848 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
4849 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
4850 QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
4851 QVERIFY( !def->checkValueIsAcceptable( "1.1,2,3" ) );
4852 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
4853 QVERIFY( !def->checkValueIsAcceptable( "1,a" ) );
4854 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1.1 << 2 ) );
4855 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1.1 << 2 << 3 ) );
4856 QVERIFY( !def->checkValueIsAcceptable( "" ) );
4857 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
4858
4859 // string representing a range of numbers
4860 QVariantMap params;
4861 params.insert( "non_optional", QString( "1.1,1.2" ) );
4862 QList< double > range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4863 QGSCOMPARENEAR( range.at( 0 ), 1.1, 0.001 );
4864 QGSCOMPARENEAR( range.at( 1 ), 1.2, 0.001 );
4865
4866 // list
4867 params.insert( "non_optional", QVariantList() << 1.1 << 1.2 );
4868 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4869 QGSCOMPARENEAR( range.at( 0 ), 1.1, 0.001 );
4870 QGSCOMPARENEAR( range.at( 1 ), 1.2, 0.001 );
4871
4872 // too many elements:
4873 params.insert( "non_optional", QString( "1.1,1.2,1.3" ) );
4874 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4875 QGSCOMPARENEAR( range.at( 0 ), 1.1, 0.001 );
4876 QGSCOMPARENEAR( range.at( 1 ), 1.2, 0.001 );
4877 params.insert( "non_optional", QVariantList() << 1.1 << 1.2 << 1.3 );
4878 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4879 QGSCOMPARENEAR( range.at( 0 ), 1.1, 0.001 );
4880 QGSCOMPARENEAR( range.at( 1 ), 1.2, 0.001 );
4881
4882 // not enough elements - don't care about the result, just don't crash!
4883 params.insert( "non_optional", QString( "1.1" ) );
4884 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4885 params.insert( "non_optional", QVariantList() << 1.1 );
4886 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4887
4888 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
4889 QCOMPARE( def->valueAsPythonString( "1.1,2", context ), QStringLiteral( "[1.1,2]" ) );
4890 QCOMPARE( def->valueAsPythonString( QVariantList() << 1.1 << 2, context ), QStringLiteral( "[1.1,2]" ) );
4891 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
4892
4893 QString pythonCode = def->asPythonString();
4894 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('non_optional', '', type=QgsProcessingParameterNumber.Double, defaultValue=[5,6])" ) );
4895
4896 QString code = def->asScriptCode();
4897 QCOMPARE( code, QStringLiteral( "##non_optional=range 5,6" ) );
4898 std::unique_ptr< QgsProcessingParameterRange > fromCode( dynamic_cast< QgsProcessingParameterRange * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4899 QVERIFY( fromCode.get() );
4900 QCOMPARE( fromCode->name(), def->name() );
4901 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
4902 QCOMPARE( fromCode->flags(), def->flags() );
4903 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4904
4905 const QVariantMap map = def->toVariantMap();
4906 QgsProcessingParameterRange fromMap( "x" );
4907 QVERIFY( fromMap.fromVariantMap( map ) );
4908 QCOMPARE( fromMap.name(), def->name() );
4909 QCOMPARE( fromMap.description(), def->description() );
4910 QCOMPARE( fromMap.flags(), def->flags() );
4911 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
4912 QCOMPARE( fromMap.dataType(), def->dataType() );
4913 def.reset( dynamic_cast< QgsProcessingParameterRange *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
4914 QVERIFY( dynamic_cast< QgsProcessingParameterRange *>( def.get() ) );
4915
4916 // optional
4917 def.reset( new QgsProcessingParameterRange( "optional", QString(), QgsProcessingParameterNumber::Double, QString( "5.4,7.4" ), true ) );
4918 QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
4919 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1.1 << 2 ) );
4920 QVERIFY( def->checkValueIsAcceptable( "" ) );
4921 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4922
4923 params.insert( "optional", QVariant() );
4924 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4925 QGSCOMPARENEAR( range.at( 0 ), 5.4, 0.001 );
4926 QGSCOMPARENEAR( range.at( 1 ), 7.4, 0.001 );
4927
4928 pythonCode = def->asPythonString();
4929 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('optional', '', optional=True, type=QgsProcessingParameterNumber.Double, defaultValue=[5.4,7.4])" ) );
4930
4931 code = def->asScriptCode();
4932 QCOMPARE( code, QStringLiteral( "##optional=optional range 5.4,7.4" ) );
4933 fromCode.reset( dynamic_cast< QgsProcessingParameterRange * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
4934 QVERIFY( fromCode.get() );
4935 QCOMPARE( fromCode->name(), def->name() );
4936 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4937 QCOMPARE( fromCode->flags(), def->flags() );
4938 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
4939
4940 // optional, no default value
4941 def.reset( new QgsProcessingParameterRange( "optional", QString(), QgsProcessingParameterNumber::Double, QVariant(), true ) );
4942 QVERIFY( def->checkValueIsAcceptable( "1.1,2" ) );
4943 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1.1 << 2 ) );
4944 QVERIFY( def->checkValueIsAcceptable( "" ) );
4945 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
4946
4947 params.insert( "optional", QVariant() );
4948 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4949 QVERIFY( std::isnan( range.at( 0 ) ) );
4950 QVERIFY( std::isnan( range.at( 1 ) ) );
4951
4952 params.insert( "optional", QStringLiteral( "None,2" ) );
4953 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4954 QVERIFY( std::isnan( range.at( 0 ) ) );
4955 QGSCOMPARENEAR( range.at( 1 ), 2, 0.001 );
4956
4957 params.insert( "optional", QStringLiteral( "1.2,None" ) );
4958 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4959 QGSCOMPARENEAR( range.at( 0 ), 1.2, 0.001 );
4960 QVERIFY( std::isnan( range.at( 1 ) ) );
4961
4962 params.insert( "optional", QStringLiteral( "None,None" ) );
4963 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4964 QVERIFY( std::isnan( range.at( 0 ) ) );
4965 QVERIFY( std::isnan( range.at( 1 ) ) );
4966
4967 params.insert( "optional", QStringLiteral( "None" ) );
4968 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4969 QVERIFY( std::isnan( range.at( 0 ) ) );
4970 QVERIFY( std::isnan( range.at( 1 ) ) );
4971
4972 params.insert( "optional", QVariant() );
4973 range = QgsProcessingParameters::parameterAsRange( def.get(), params, context );
4974 QVERIFY( std::isnan( range.at( 0 ) ) );
4975 QVERIFY( std::isnan( range.at( 1 ) ) );
4976
4977 pythonCode = def->asPythonString();
4978 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRange('optional', '', optional=True, type=QgsProcessingParameterNumber.Double, defaultValue=None)" ) );
4979
4980 fromCode.reset( dynamic_cast< QgsProcessingParameterRange * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional range None" ) ) ) );
4981 QVERIFY( fromCode.get() );
4982 QCOMPARE( fromCode->name(), def->name() );
4983 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
4984 QCOMPARE( fromCode->flags(), def->flags() );
4985 QVERIFY( !fromCode->defaultValue().isValid() );
4986 }
4987
parameterRasterLayer()4988 void TestQgsProcessing::parameterRasterLayer()
4989 {
4990 // setup a context
4991 QgsProject p;
4992 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
4993 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
4994 const QString raster1 = testDataDir + "tenbytenraster.asc";
4995 const QString raster2 = testDataDir + "landsat.tif";
4996 const QFileInfo fi1( raster1 );
4997 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
4998 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" );
4999 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 );
5000 QgsProcessingContext context;
5001 context.setProject( &p );
5002
5003 // not optional!
5004 std::unique_ptr< QgsProcessingParameterRasterLayer > def( new QgsProcessingParameterRasterLayer( "non_optional", QString(), QVariant(), false ) );
5005 QVERIFY( !def->checkValueIsAcceptable( false ) );
5006 QVERIFY( !def->checkValueIsAcceptable( true ) );
5007 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
5008 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
5009 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5010 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5011 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
5012 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
5013
5014 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
5015 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
5016 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
5017 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
5018
5019 // should be OK
5020 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) );
5021 // ... unless we use context, when the check that the layer actually exists is performed
5022 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif", &context ) );
5023
5024 // using existing map layer ID
5025 QVariantMap params;
5026 params.insert( "non_optional", r1->id() );
5027 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5028
5029 // using existing map layer
5030 params.insert( "non_optional", QVariant::fromValue( r1 ) );
5031 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5032
5033 // not raster layer
5034 params.insert( "non_optional", v1->id() );
5035 QVERIFY( !QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context ) );
5036
5037 // using existing vector layer
5038 params.insert( "non_optional", QVariant::fromValue( v1 ) );
5039 QVERIFY( !QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context ) );
5040
5041 // string representing a project layer source
5042 params.insert( "non_optional", raster1 );
5043 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5044 // string representing a non-project layer source
5045 params.insert( "non_optional", raster2 );
5046 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->publicSource(), raster2 );
5047
5048 // nonsense string
5049 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
5050 QVERIFY( !QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context ) );
5051
5052 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5053 QCOMPARE( def->valueAsPythonString( raster1, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
5054 QCOMPARE( def->valueAsPythonString( r1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
5055 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( r1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "tenbytenraster.asc'" ) ) );
5056 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
5057 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
5058
5059 QString pythonCode = def->asPythonString();
5060 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRasterLayer('non_optional', '', defaultValue=None)" ) );
5061
5062 QString code = def->asScriptCode();
5063 QCOMPARE( code, QStringLiteral( "##non_optional=raster" ) );
5064 std::unique_ptr< QgsProcessingParameterRasterLayer > fromCode( dynamic_cast< QgsProcessingParameterRasterLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5065 QVERIFY( fromCode.get() );
5066 QCOMPARE( fromCode->name(), def->name() );
5067 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5068 QCOMPARE( fromCode->flags(), def->flags() );
5069 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5070
5071 // optional
5072 def.reset( new QgsProcessingParameterRasterLayer( "optional", QString(), r1->id(), true ) );
5073 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5074 QVERIFY( def->checkValueIsAcceptable( false ) );
5075 QVERIFY( def->checkValueIsAcceptable( true ) );
5076 QVERIFY( def->checkValueIsAcceptable( 5 ) );
5077 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
5078 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) );
5079 QVERIFY( def->checkValueIsAcceptable( "" ) );
5080 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
5081
5082 params.insert( "optional", QVariant() );
5083 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5084
5085 pythonCode = def->asPythonString();
5086 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterRasterLayer('optional', '', optional=True, defaultValue='" ) + r1->id() + "')" ) );
5087
5088 code = def->asScriptCode();
5089 QCOMPARE( code, QString( QStringLiteral( "##optional=optional raster " ) + r1->id() ) );
5090 fromCode.reset( dynamic_cast< QgsProcessingParameterRasterLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5091 QVERIFY( fromCode.get() );
5092 QCOMPARE( fromCode->name(), def->name() );
5093 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5094 QCOMPARE( fromCode->flags(), def->flags() );
5095 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5096
5097 const QVariantMap map = def->toVariantMap();
5098 QgsProcessingParameterRasterLayer fromMap( "x" );
5099 QVERIFY( fromMap.fromVariantMap( map ) );
5100 QCOMPARE( fromMap.name(), def->name() );
5101 QCOMPARE( fromMap.description(), def->description() );
5102 QCOMPARE( fromMap.flags(), def->flags() );
5103 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5104 def.reset( dynamic_cast< QgsProcessingParameterRasterLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5105 QVERIFY( dynamic_cast< QgsProcessingParameterRasterLayer *>( def.get() ) );
5106
5107 // optional with direct layer
5108 def.reset( new QgsProcessingParameterRasterLayer( "optional", QString(), QVariant::fromValue( r1 ), true ) );
5109 QCOMPARE( QgsProcessingParameters::parameterAsRasterLayer( def.get(), params, context )->id(), r1->id() );
5110
5111 // invalidRasterError
5112 params.clear();
5113 QCOMPARE( QgsProcessingAlgorithm::invalidRasterError( params, QStringLiteral( "MISSING" ) ), QStringLiteral( "Could not load source layer for MISSING: no value specified for parameter" ) );
5114 params.insert( QStringLiteral( "INPUT" ), QStringLiteral( "my layer" ) );
5115 QCOMPARE( QgsProcessingAlgorithm::invalidRasterError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my layer not found" ) );
5116 params.insert( QStringLiteral( "INPUT" ), QgsProperty::fromValue( "my prop layer" ) );
5117 QCOMPARE( QgsProcessingAlgorithm::invalidRasterError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my prop layer not found" ) );
5118 params.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( v1 ) );
5119 QCOMPARE( QgsProcessingAlgorithm::invalidRasterError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: invalid value" ) );
5120 }
5121
parameterEnum()5122 void TestQgsProcessing::parameterEnum()
5123 {
5124 QgsProcessingContext context;
5125
5126 // not optional!
5127 std::unique_ptr< QgsProcessingParameterEnum > def( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", false, 2, false ) );
5128 QVERIFY( !def->checkValueIsAcceptable( false ) );
5129 QVERIFY( !def->checkValueIsAcceptable( true ) );
5130 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5131 QVERIFY( def->checkValueIsAcceptable( "1" ) );
5132 QVERIFY( !def->checkValueIsAcceptable( "1,2" ) );
5133 QVERIFY( def->checkValueIsAcceptable( 0 ) );
5134 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5135 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1 ) );
5136 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5137 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5138 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5139 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5140 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5141 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, because falls back to default value
5142 QVERIFY( !def->checkValueIsAcceptable( "B" ) ); // should not be acceptable, because static strings flag is not set
5143 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because static strings flag is not set
5144
5145 // string representing a number
5146 QVariantMap params;
5147 params.insert( "non_optional", QString( "1" ) );
5148 int iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5149 QCOMPARE( iNumber, 1 );
5150
5151 // double
5152 params.insert( "non_optional", 2.2 );
5153 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5154 QCOMPARE( iNumber, 2 );
5155
5156 // int
5157 params.insert( "non_optional", 1 );
5158 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5159 QCOMPARE( iNumber, 1 );
5160
5161 // nonsense string
5162 params.insert( "non_optional", QString( "i'm not a number, and nothing you can do will make me one" ) );
5163 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5164 QCOMPARE( iNumber, 2 );
5165
5166 // out of range
5167 params.insert( "non_optional", 4 );
5168 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5169 QCOMPARE( iNumber, 2 );
5170
5171 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5172 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
5173 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1.1" ), context ), QStringLiteral( "1" ) );
5174 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
5175
5176 QString pythonCode = def->asPythonString();
5177 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=False, usesStaticStrings=False, defaultValue=2)" ) );
5178
5179 QString code = def->asScriptCode();
5180 QCOMPARE( code, QStringLiteral( "##non_optional=enum A;B;C 2" ) );
5181 std::unique_ptr< QgsProcessingParameterEnum > fromCode( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5182 QVERIFY( fromCode.get() );
5183 QCOMPARE( fromCode->name(), def->name() );
5184 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5185 QCOMPARE( fromCode->flags(), def->flags() );
5186 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5187 QCOMPARE( fromCode->options(), def->options() );
5188 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5189 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5190
5191 const QVariantMap map = def->toVariantMap();
5192 QgsProcessingParameterEnum fromMap( "x" );
5193 QVERIFY( fromMap.fromVariantMap( map ) );
5194 QCOMPARE( fromMap.name(), def->name() );
5195 QCOMPARE( fromMap.description(), def->description() );
5196 QCOMPARE( fromMap.flags(), def->flags() );
5197 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5198 QCOMPARE( fromMap.options(), def->options() );
5199 QCOMPARE( fromMap.allowMultiple(), def->allowMultiple() );
5200 QCOMPARE( fromMap.usesStaticStrings(), def->usesStaticStrings() );
5201 def.reset( dynamic_cast< QgsProcessingParameterEnum *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5202 QVERIFY( dynamic_cast< QgsProcessingParameterEnum *>( def.get() ) );
5203
5204 // multiple
5205 def.reset( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", true, 5, false ) );
5206 QVERIFY( !def->checkValueIsAcceptable( false ) );
5207 QVERIFY( !def->checkValueIsAcceptable( true ) );
5208 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5209 QVERIFY( def->checkValueIsAcceptable( "1" ) );
5210 QVERIFY( def->checkValueIsAcceptable( "1,2" ) );
5211 QVERIFY( def->checkValueIsAcceptable( 0 ) );
5212 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) ); // since non-optional, empty list not allowed
5213 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1 ) );
5214 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5215 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5216 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5217 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5218 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5219 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5220 QVERIFY( !def->checkValueIsAcceptable( "B" ) ); // should not be acceptable, because static strings flag is not set
5221 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because static strings flag is not set
5222
5223 params.insert( "non_optional", QString( "1,2" ) );
5224 QList< int > iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5225 QCOMPARE( iNumbers, QList<int>() << 1 << 2 );
5226 params.insert( "non_optional", QVariantList() << 0 << 2 );
5227 iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5228 QCOMPARE( iNumbers, QList<int>() << 0 << 2 );
5229
5230 // empty list
5231 params.insert( "non_optional", QVariantList() );
5232 iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5233 QCOMPARE( iNumbers, QList<int>() );
5234
5235 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5236 QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2, context ), QStringLiteral( "[1,2]" ) );
5237 QCOMPARE( def->valueAsPythonString( QStringLiteral( "1,2" ), context ), QStringLiteral( "[1,2]" ) );
5238
5239 QCOMPARE( def->valueAsPythonComment( QVariant(), context ), QString() );
5240 QCOMPARE( def->valueAsPythonComment( 2, context ), QStringLiteral( "C" ) );
5241 QCOMPARE( def->valueAsPythonComment( QVariantList() << 1 << 2, context ), QStringLiteral( "B,C" ) );
5242 QCOMPARE( def->valueAsPythonComment( QStringLiteral( "1,2" ), context ), QStringLiteral( "B,C" ) );
5243
5244 pythonCode = def->asPythonString();
5245 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=True, usesStaticStrings=False, defaultValue=5)" ) );
5246
5247 code = def->asScriptCode();
5248 QCOMPARE( code, QStringLiteral( "##non_optional=enum multiple A;B;C 5" ) );
5249 fromCode.reset( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5250 QVERIFY( fromCode.get() );
5251 QCOMPARE( fromCode->name(), def->name() );
5252 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5253 QCOMPARE( fromCode->flags(), def->flags() );
5254 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5255 QCOMPARE( fromCode->options(), def->options() );
5256 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5257 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5258
5259 // optional
5260 def.reset( new QgsProcessingParameterEnum( "optional", QString(), QStringList() << "a" << "b", false, 5, true ) );
5261 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5262 QVERIFY( def->checkValueIsAcceptable( "1" ) );
5263 QVERIFY( !def->checkValueIsAcceptable( "1,2" ) );
5264 QVERIFY( def->checkValueIsAcceptable( 0 ) );
5265 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5266 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1 ) );
5267 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5268 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5269 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5270 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5271 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5272 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5273
5274 pythonCode = def->asPythonString();
5275 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('optional', '', optional=True, options=['a','b'], allowMultiple=False, usesStaticStrings=False, defaultValue=5)" ) );
5276
5277 code = def->asScriptCode();
5278 QCOMPARE( code, QStringLiteral( "##optional=optional enum a;b 5" ) );
5279 fromCode.reset( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5280 QVERIFY( fromCode.get() );
5281 QCOMPARE( fromCode->name(), def->name() );
5282 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5283 QCOMPARE( fromCode->flags(), def->flags() );
5284 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5285 QCOMPARE( fromCode->options(), def->options() );
5286 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5287 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5288
5289 params.insert( "optional", QVariant() );
5290 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5291 QCOMPARE( iNumber, 5 );
5292 // unconvertible string
5293 params.insert( "optional", QVariant( "aaaa" ) );
5294 iNumber = QgsProcessingParameters::parameterAsEnum( def.get(), params, context );
5295 QCOMPARE( iNumber, 5 );
5296 //optional with multiples
5297 def.reset( new QgsProcessingParameterEnum( "optional", QString(), QStringList() << "A" << "B" << "C", true, QVariantList() << 1 << 2, true ) );
5298 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5299 QVERIFY( def->checkValueIsAcceptable( "1" ) );
5300 QVERIFY( def->checkValueIsAcceptable( "1,2" ) );
5301 QVERIFY( def->checkValueIsAcceptable( 0 ) );
5302 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
5303 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1 ) );
5304 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5305 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5306 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5307 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5308 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5309 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
5310 QVERIFY( !def->checkValueIsAcceptable( "B" ) ); // should not be acceptable, because static strings flag is not set
5311 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because static strings flag is not set
5312
5313 params.insert( "optional", QVariant() );
5314 iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5315 QCOMPARE( iNumbers, QList<int>() << 1 << 2 );
5316 def.reset( new QgsProcessingParameterEnum( "optional", QString(), QStringList() << "A" << "B" << "C", true, "1,2", true ) );
5317 params.insert( "optional", QVariant() );
5318 iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5319 QCOMPARE( iNumbers, QList<int>() << 1 << 2 );
5320 // empty list
5321 params.insert( "optional", QVariantList() );
5322 iNumbers = QgsProcessingParameters::parameterAsEnums( def.get(), params, context );
5323 QCOMPARE( iNumbers, QList<int>() );
5324
5325 pythonCode = def->asPythonString();
5326 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('optional', '', optional=True, options=['A','B','C'], allowMultiple=True, usesStaticStrings=False, defaultValue=[1,2])" ) );
5327
5328 code = def->asScriptCode();
5329 QCOMPARE( code, QStringLiteral( "##optional=optional enum multiple A;B;C 1,2" ) );
5330 fromCode.reset( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5331 QVERIFY( fromCode.get() );
5332 QCOMPARE( fromCode->name(), def->name() );
5333 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5334 QCOMPARE( fromCode->flags(), def->flags() );
5335 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5336 QCOMPARE( fromCode->options(), def->options() );
5337 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5338 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5339
5340 // non optional, no default
5341 def.reset( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", false, QVariant(), false ) );
5342 QVERIFY( !def->checkValueIsAcceptable( false ) );
5343 QVERIFY( !def->checkValueIsAcceptable( true ) );
5344 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5345 QVERIFY( def->checkValueIsAcceptable( "1" ) );
5346 QVERIFY( !def->checkValueIsAcceptable( "1,2" ) );
5347 QVERIFY( def->checkValueIsAcceptable( 0 ) );
5348 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5349 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1 ) );
5350 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5351 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5352 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5353 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5354 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5355 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, because falls back to invalid default value
5356 QVERIFY( !def->checkValueIsAcceptable( "B" ) ); // should not be acceptable, because static strings flag is not set
5357 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because static strings flag is not set
5358
5359 // not optional with static strings
5360 def.reset( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", false, "B", false, true ) );
5361 QVERIFY( !def->checkValueIsAcceptable( false ) );
5362 QVERIFY( !def->checkValueIsAcceptable( true ) );
5363 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
5364 QVERIFY( !def->checkValueIsAcceptable( "1" ) );
5365 QVERIFY( !def->checkValueIsAcceptable( "1,2" ) );
5366 QVERIFY( !def->checkValueIsAcceptable( 0 ) );
5367 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5368 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1 ) );
5369 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5370 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5371 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5372 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5373 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5374 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, because falls back to default value
5375 QVERIFY( def->checkValueIsAcceptable( "B" ) ); // should be acceptable, because this is a valid enum value
5376 QVERIFY( !def->checkValueIsAcceptable( "b" ) ); // should not be acceptable, because values are case sensitive
5377 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because value is not in the list of enum values
5378
5379 // valid enum value
5380 params.insert( "non_optional", QString( "A" ) );
5381 QString iString = QgsProcessingParameters::parameterAsEnumString( def.get(), params, context );
5382 QCOMPARE( iString, QStringLiteral( "A" ) );
5383
5384 // invalid enum value
5385 params.insert( "non_optional", QString( "Z" ) );
5386 iString = QgsProcessingParameters::parameterAsEnumString( def.get(), params, context );
5387 QCOMPARE( iString, QStringLiteral( "B" ) );
5388
5389 // lowercase
5390 params.insert( "non_optional", QString( "a" ) );
5391 iString = QgsProcessingParameters::parameterAsEnumString( def.get(), params, context );
5392 QCOMPARE( iString, QStringLiteral( "B" ) );
5393
5394 // empty string
5395 params.insert( "non_optional", QString() );
5396 iString = QgsProcessingParameters::parameterAsEnumString( def.get(), params, context );
5397 QCOMPARE( iString, QStringLiteral( "B" ) );
5398
5399 // number
5400 params.insert( "non_optional", 1 );
5401 iString = QgsProcessingParameters::parameterAsEnumString( def.get(), params, context );
5402 QCOMPARE( iString, QStringLiteral( "B" ) );
5403
5404 pythonCode = def->asPythonString();
5405 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=False, usesStaticStrings=True, defaultValue='B')" ) );
5406
5407 code = def->asScriptCode();
5408 QCOMPARE( code, QStringLiteral( "##non_optional=enum static A;B;C B" ) );
5409 fromCode.reset( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5410 QVERIFY( fromCode.get() );
5411 QCOMPARE( fromCode->name(), def->name() );
5412 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5413 QCOMPARE( fromCode->flags(), def->flags() );
5414 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5415 QCOMPARE( fromCode->options(), def->options() );
5416 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5417 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5418
5419 // multiple with static strings
5420 def.reset( new QgsProcessingParameterEnum( "non_optional", QString(), QStringList() << "A" << "B" << "C", true, "B", false, true ) );
5421 QVERIFY( !def->checkValueIsAcceptable( false ) );
5422 QVERIFY( !def->checkValueIsAcceptable( true ) );
5423 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
5424 QVERIFY( !def->checkValueIsAcceptable( "1" ) );
5425 QVERIFY( !def->checkValueIsAcceptable( "1,2" ) );
5426 QVERIFY( !def->checkValueIsAcceptable( 0 ) );
5427 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5428 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << 1 ) );
5429 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" ) );
5430 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "A" ) );
5431 QVERIFY( !def->checkValueIsAcceptable( 15 ) );
5432 QVERIFY( !def->checkValueIsAcceptable( -1 ) );
5433 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
5434 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5435 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, because falls back to default value
5436 QVERIFY( def->checkValueIsAcceptable( "B" ) ); // should be acceptable, because this is a valid enum value
5437 QVERIFY( !def->checkValueIsAcceptable( "b" ) ); // should not be acceptable, because values are case sensitive
5438 QVERIFY( !def->checkValueIsAcceptable( "Z" ) ); // should not be acceptable, because value is not in the list of enum values
5439
5440 // comma-separated string
5441 params.insert( "non_optional", QString( "A,B" ) );
5442 QStringList iStrings = QgsProcessingParameters::parameterAsEnumStrings( def.get(), params, context );
5443 QCOMPARE( iStrings, QStringList() << "A" << "B" );
5444
5445 // list
5446 params.insert( "non_optional", QVariantList() << "A" << "C" );
5447 iStrings = QgsProcessingParameters::parameterAsEnumStrings( def.get(), params, context );
5448 QCOMPARE( iStrings, QStringList() << "A" << "C" );
5449
5450 // empty list
5451 params.insert( "non_optional", QVariantList() );
5452 iStrings = QgsProcessingParameters::parameterAsEnumStrings( def.get(), params, context );
5453 QCOMPARE( iStrings, QStringList() << "B" );
5454
5455 pythonCode = def->asPythonString();
5456 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterEnum('non_optional', '', options=['A','B','C'], allowMultiple=True, usesStaticStrings=True, defaultValue='B')" ) );
5457
5458 code = def->asScriptCode();
5459 QCOMPARE( code, QStringLiteral( "##non_optional=enum multiple static A;B;C B" ) );
5460 fromCode.reset( dynamic_cast< QgsProcessingParameterEnum * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5461 QVERIFY( fromCode.get() );
5462 QCOMPARE( fromCode->name(), def->name() );
5463 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5464 QCOMPARE( fromCode->flags(), def->flags() );
5465 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5466 QCOMPARE( fromCode->options(), def->options() );
5467 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5468 QCOMPARE( fromCode->usesStaticStrings(), def->usesStaticStrings() );
5469 }
5470
parameterString()5471 void TestQgsProcessing::parameterString()
5472 {
5473 QgsProcessingContext context;
5474
5475 // not optional!
5476 std::unique_ptr< QgsProcessingParameterString > def( new QgsProcessingParameterString( "non_optional", QString(), QString(), false, false ) );
5477 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5478 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5479 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5480 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5481
5482 // string
5483 QVariantMap params;
5484 params.insert( "non_optional", QString( "abcdef" ) );
5485 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
5486
5487 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5488 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
5489 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
5490 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
5491 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
5492 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
5493 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
5494
5495 QString pythonCode = def->asPythonString();
5496 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('non_optional', '', multiLine=False, defaultValue=None)" ) );
5497
5498 QString code = def->asScriptCode();
5499 QCOMPARE( code, QStringLiteral( "##non_optional=string" ) );
5500 std::unique_ptr< QgsProcessingParameterString > fromCode( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5501 QVERIFY( fromCode.get() );
5502 QCOMPARE( fromCode->name(), def->name() );
5503 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5504 QCOMPARE( fromCode->flags(), def->flags() );
5505 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5506 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5507
5508 const QVariantMap map = def->toVariantMap();
5509 QgsProcessingParameterString fromMap( "x" );
5510 QVERIFY( fromMap.fromVariantMap( map ) );
5511 QCOMPARE( fromMap.name(), def->name() );
5512 QCOMPARE( fromMap.description(), def->description() );
5513 QCOMPARE( fromMap.flags(), def->flags() );
5514 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5515 QCOMPARE( fromMap.multiLine(), def->multiLine() );
5516 def.reset( dynamic_cast< QgsProcessingParameterString *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5517 QVERIFY( dynamic_cast< QgsProcessingParameterString *>( def.get() ) );
5518
5519 def->setMultiLine( true );
5520
5521 pythonCode = def->asPythonString();
5522 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('non_optional', '', multiLine=True, defaultValue=None)" ) );
5523
5524 code = def->asScriptCode();
5525 QCOMPARE( code, QStringLiteral( "##non_optional=string long" ) );
5526 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5527 QVERIFY( fromCode.get() );
5528 QCOMPARE( fromCode->name(), def->name() );
5529 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5530 QCOMPARE( fromCode->flags(), def->flags() );
5531 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5532 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5533 def->setMultiLine( false );
5534
5535 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=string None" ) ) ) );
5536 QVERIFY( fromCode.get() );
5537 QCOMPARE( fromCode->name(), def->name() );
5538 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5539 QCOMPARE( fromCode->flags(), def->flags() );
5540 QVERIFY( !fromCode->defaultValue().isValid() );
5541 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5542
5543 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=string it's mario" ) ) ) );
5544 QVERIFY( fromCode.get() );
5545 QCOMPARE( fromCode->name(), def->name() );
5546 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5547 QCOMPARE( fromCode->flags(), def->flags() );
5548 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
5549 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5550
5551 def->setDefaultValue( QStringLiteral( "it's mario" ) );
5552 pythonCode = def->asPythonString();
5553 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('non_optional', '', multiLine=False, defaultValue=\"it's mario\")" ) );
5554 code = def->asScriptCode();
5555 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5556 QVERIFY( fromCode.get() );
5557 QCOMPARE( fromCode->name(), def->name() );
5558 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5559 QCOMPARE( fromCode->flags(), def->flags() );
5560 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5561 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5562
5563 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=string 'my val'" ) ) ) );
5564 QVERIFY( fromCode.get() );
5565 QCOMPARE( fromCode->name(), def->name() );
5566 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5567 QCOMPARE( fromCode->flags(), def->flags() );
5568 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
5569 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5570
5571 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=string \"my val\"" ) ) ) );
5572 QVERIFY( fromCode.get() );
5573 QCOMPARE( fromCode->name(), def->name() );
5574 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5575 QCOMPARE( fromCode->flags(), def->flags() );
5576 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
5577 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5578
5579 // optional
5580 def.reset( new QgsProcessingParameterString( "optional", QString(), QString( "default" ), false, true ) );
5581 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5582 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5583 QVERIFY( def->checkValueIsAcceptable( "" ) );
5584 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
5585
5586 params.insert( "optional", QVariant() );
5587 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
5588 params.insert( "optional", QString() ); //empty string should not result in default value
5589 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
5590
5591 pythonCode = def->asPythonString();
5592 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('optional', '', optional=True, multiLine=False, defaultValue='default')" ) );
5593
5594 code = def->asScriptCode();
5595 QCOMPARE( code, QStringLiteral( "##optional=optional string default" ) );
5596 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5597 QVERIFY( fromCode.get() );
5598 QCOMPARE( fromCode->name(), def->name() );
5599 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5600 QCOMPARE( fromCode->flags(), def->flags() );
5601 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5602 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5603
5604 def->setMultiLine( true );
5605 pythonCode = def->asPythonString();
5606 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterString('optional', '', optional=True, multiLine=True, defaultValue='default')" ) );
5607 code = def->asScriptCode();
5608 QCOMPARE( code, QStringLiteral( "##optional=optional string long default" ) );
5609 fromCode.reset( dynamic_cast< QgsProcessingParameterString * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5610 QVERIFY( fromCode.get() );
5611 QCOMPARE( fromCode->name(), def->name() );
5612 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5613 QCOMPARE( fromCode->flags(), def->flags() );
5614 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5615 QCOMPARE( fromCode->multiLine(), def->multiLine() );
5616
5617 // not optional, valid default!
5618 def.reset( new QgsProcessingParameterString( "non_optional", QString(), QString( "def" ), false, false ) );
5619 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5620 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5621 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5622 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
5623 }
5624
5625
parameterAuthConfig()5626 void TestQgsProcessing::parameterAuthConfig()
5627 {
5628 QgsProcessingContext context;
5629
5630 // not optional!
5631 std::unique_ptr< QgsProcessingParameterAuthConfig > def( new QgsProcessingParameterAuthConfig( "non_optional", QString(), QString(), false ) );
5632 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5633 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5634 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5635 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5636
5637 // string
5638 QVariantMap params;
5639 params.insert( "non_optional", QString( "abcdef" ) );
5640 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
5641
5642 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5643 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
5644 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
5645 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
5646 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
5647 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
5648
5649 QString pythonCode = def->asPythonString();
5650 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAuthConfig('non_optional', '', defaultValue='')" ) );
5651
5652 QString code = def->asScriptCode();
5653 QCOMPARE( code, QStringLiteral( "##non_optional=authcfg" ) );
5654 std::unique_ptr< QgsProcessingParameterAuthConfig > fromCode( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5655 QVERIFY( fromCode.get() );
5656 QCOMPARE( fromCode->name(), def->name() );
5657 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5658 QCOMPARE( fromCode->flags(), def->flags() );
5659 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5660
5661 const QVariantMap map = def->toVariantMap();
5662 QgsProcessingParameterAuthConfig fromMap( "x" );
5663 QVERIFY( fromMap.fromVariantMap( map ) );
5664 QCOMPARE( fromMap.name(), def->name() );
5665 QCOMPARE( fromMap.description(), def->description() );
5666 QCOMPARE( fromMap.flags(), def->flags() );
5667 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5668 def.reset( dynamic_cast< QgsProcessingParameterAuthConfig *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5669 QVERIFY( dynamic_cast< QgsProcessingParameterAuthConfig *>( def.get() ) );
5670
5671 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg None" ) ) ) );
5672 QVERIFY( fromCode.get() );
5673 QCOMPARE( fromCode->name(), def->name() );
5674 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5675 QCOMPARE( fromCode->flags(), def->flags() );
5676 QVERIFY( !fromCode->defaultValue().isValid() );
5677
5678 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg it's mario" ) ) ) );
5679 QVERIFY( fromCode.get() );
5680 QCOMPARE( fromCode->name(), def->name() );
5681 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5682 QCOMPARE( fromCode->flags(), def->flags() );
5683 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
5684
5685 def->setDefaultValue( QStringLiteral( "it's mario" ) );
5686 pythonCode = def->asPythonString();
5687 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAuthConfig('non_optional', '', defaultValue=\"it's mario\")" ) );
5688
5689 code = def->asScriptCode();
5690 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5691 QVERIFY( fromCode.get() );
5692 QCOMPARE( fromCode->name(), def->name() );
5693 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5694 QCOMPARE( fromCode->flags(), def->flags() );
5695 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5696
5697 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg 'my val'" ) ) ) );
5698 QVERIFY( fromCode.get() );
5699 QCOMPARE( fromCode->name(), def->name() );
5700 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5701 QCOMPARE( fromCode->flags(), def->flags() );
5702 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
5703
5704 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=authcfg \"my val\"" ) ) ) );
5705 QVERIFY( fromCode.get() );
5706 QCOMPARE( fromCode->name(), def->name() );
5707 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5708 QCOMPARE( fromCode->flags(), def->flags() );
5709 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
5710
5711 // optional
5712 def.reset( new QgsProcessingParameterAuthConfig( "optional", QString(), QString( "default" ), true ) );
5713 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5714 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5715 QVERIFY( def->checkValueIsAcceptable( "" ) );
5716 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
5717
5718 params.insert( "optional", QVariant() );
5719 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
5720 params.insert( "optional", QString() ); //empty string should not result in default value
5721 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
5722
5723 pythonCode = def->asPythonString();
5724 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAuthConfig('optional', '', optional=True, defaultValue='default')" ) );
5725 code = def->asScriptCode();
5726 QCOMPARE( code, QStringLiteral( "##optional=optional authcfg default" ) );
5727 fromCode.reset( dynamic_cast< QgsProcessingParameterAuthConfig * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5728 QVERIFY( fromCode.get() );
5729 QCOMPARE( fromCode->name(), def->name() );
5730 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5731 QCOMPARE( fromCode->flags(), def->flags() );
5732 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5733
5734 // not optional, valid default!
5735 def.reset( new QgsProcessingParameterAuthConfig( "non_optional", QString(), QString( "def" ), false ) );
5736 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5737 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5738 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5739 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
5740 }
5741
parameterExpression()5742 void TestQgsProcessing::parameterExpression()
5743 {
5744 QgsProcessingContext context;
5745
5746 // not optional!
5747 std::unique_ptr< QgsProcessingParameterExpression > def( new QgsProcessingParameterExpression( "non_optional", QString(), QString( "1+1" ), QString(), false ) );
5748 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5749 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5750 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5751 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, because it will fallback to default value
5752
5753 // string
5754 QVariantMap params;
5755 params.insert( "non_optional", QString( "abcdef" ) );
5756 QCOMPARE( QgsProcessingParameters::parameterAsExpression( def.get(), params, context ), QString( "abcdef" ) );
5757
5758 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5759 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
5760 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
5761 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
5762 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
5763
5764 QString pythonCode = def->asPythonString();
5765 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExpression('non_optional', '', parentLayerParameterName='', defaultValue='1+1')" ) );
5766
5767 QString code = def->asScriptCode();
5768 QCOMPARE( code, QStringLiteral( "##non_optional=expression 1+1" ) );
5769 std::unique_ptr< QgsProcessingParameterExpression > fromCode( dynamic_cast< QgsProcessingParameterExpression * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5770 QVERIFY( fromCode.get() );
5771 QCOMPARE( fromCode->name(), def->name() );
5772 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5773 QCOMPARE( fromCode->flags(), def->flags() );
5774 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5775
5776 const QVariantMap map = def->toVariantMap();
5777 QgsProcessingParameterExpression fromMap( "x" );
5778 QVERIFY( fromMap.fromVariantMap( map ) );
5779 QCOMPARE( fromMap.name(), def->name() );
5780 QCOMPARE( fromMap.description(), def->description() );
5781 QCOMPARE( fromMap.flags(), def->flags() );
5782 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5783 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
5784 def.reset( dynamic_cast< QgsProcessingParameterExpression *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5785 QVERIFY( dynamic_cast< QgsProcessingParameterExpression *>( def.get() ) );
5786
5787 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
5788 def->setParentLayerParameterName( QStringLiteral( "test_layer" ) );
5789 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "test_layer" ) );
5790
5791 // optional
5792 def.reset( new QgsProcessingParameterExpression( "optional", QString(), QString( "default" ), QString(), true ) );
5793 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5794 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5795 QVERIFY( def->checkValueIsAcceptable( "" ) );
5796 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
5797
5798 params.insert( "optional", QVariant() );
5799 QCOMPARE( QgsProcessingParameters::parameterAsExpression( def.get(), params, context ), QString( "default" ) );
5800 // valid expression, should not fallback
5801 params.insert( "optional", QVariant( "1+2" ) );
5802 QCOMPARE( QgsProcessingParameters::parameterAsExpression( def.get(), params, context ), QString( "1+2" ) );
5803 // invalid expression, should fallback
5804 params.insert( "optional", QVariant( "1+" ) );
5805 QCOMPARE( QgsProcessingParameters::parameterAsExpression( def.get(), params, context ), QString( "default" ) );
5806
5807 pythonCode = def->asPythonString();
5808 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterExpression('optional', '', optional=True, parentLayerParameterName='', defaultValue='default')" ) );
5809
5810 code = def->asScriptCode();
5811 QCOMPARE( code, QStringLiteral( "##optional=optional expression default" ) );
5812 fromCode.reset( dynamic_cast< QgsProcessingParameterExpression * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5813 QVERIFY( fromCode.get() );
5814 QCOMPARE( fromCode->name(), def->name() );
5815 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
5816 QCOMPARE( fromCode->flags(), def->flags() );
5817 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5818
5819 // non optional, no default
5820 def.reset( new QgsProcessingParameterExpression( "non_optional", QString(), QString(), QString(), false ) );
5821 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5822 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5823 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5824 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, because it will fallback to invalid default value
5825
5826 }
5827
parameterField()5828 void TestQgsProcessing::parameterField()
5829 {
5830 QgsProcessingContext context;
5831
5832 // not optional!
5833 std::unique_ptr< QgsProcessingParameterField > def( new QgsProcessingParameterField( "non_optional", QString(), QVariant(), QString(), QgsProcessingParameterField::Any, false, false ) );
5834 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5835 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5836 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
5837 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
5838 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5839 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5840
5841 // string
5842 QVariantMap params;
5843 params.insert( "non_optional", QString( "a" ) );
5844 QStringList fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
5845 QCOMPARE( fields, QStringList() << "a" );
5846
5847 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5848 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
5849 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
5850 QCOMPARE( def->valueAsPythonString( "probably\'invalid\"field", context ), QStringLiteral( "'probably\\'invalid\"field'" ) );
5851
5852 QString pythonCode = def->asPythonString();
5853 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) );
5854
5855 QString code = def->asScriptCode();
5856 QCOMPARE( code, QStringLiteral( "##non_optional=field" ) );
5857 std::unique_ptr< QgsProcessingParameterField > fromCode( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5858 QVERIFY( fromCode.get() );
5859 QCOMPARE( fromCode->name(), def->name() );
5860 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5861 QCOMPARE( fromCode->flags(), def->flags() );
5862 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5863 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5864 QCOMPARE( fromCode->dataType(), def->dataType() );
5865 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5866 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5867
5868 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
5869 def->setParentLayerParameterName( "my_parent" );
5870 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) );
5871
5872 pythonCode = def->asPythonString();
5873 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='my_parent', allowMultiple=False, defaultValue=None)" ) );
5874
5875 code = def->asScriptCode();
5876 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5877 QVERIFY( fromCode.get() );
5878 QCOMPARE( fromCode->name(), def->name() );
5879 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5880 QCOMPARE( fromCode->flags(), def->flags() );
5881 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5882 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5883 QCOMPARE( fromCode->dataType(), def->dataType() );
5884 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5885 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5886
5887 def->setDataType( QgsProcessingParameterField::Numeric );
5888 pythonCode = def->asPythonString();
5889 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Numeric, parentLayerParameterName='my_parent', allowMultiple=False, defaultValue=None)" ) );
5890 code = def->asScriptCode();
5891 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5892 QVERIFY( fromCode.get() );
5893 QCOMPARE( fromCode->name(), def->name() );
5894 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5895 QCOMPARE( fromCode->flags(), def->flags() );
5896 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5897 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5898 QCOMPARE( fromCode->dataType(), def->dataType() );
5899 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5900 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5901
5902 def->setDataType( QgsProcessingParameterField::String );
5903 pythonCode = def->asPythonString();
5904 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.String, parentLayerParameterName='my_parent', allowMultiple=False, defaultValue=None)" ) );
5905 code = def->asScriptCode();
5906 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5907 QVERIFY( fromCode.get() );
5908 QCOMPARE( fromCode->name(), def->name() );
5909 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5910 QCOMPARE( fromCode->flags(), def->flags() );
5911 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5912 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5913 QCOMPARE( fromCode->dataType(), def->dataType() );
5914 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5915 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5916
5917 def->setDataType( QgsProcessingParameterField::DateTime );
5918 pythonCode = def->asPythonString();
5919 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.DateTime, parentLayerParameterName='my_parent', allowMultiple=False, defaultValue=None)" ) );
5920 code = def->asScriptCode();
5921 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5922 QVERIFY( fromCode.get() );
5923 QCOMPARE( fromCode->name(), def->name() );
5924 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5925 QCOMPARE( fromCode->flags(), def->flags() );
5926 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5927 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5928 QCOMPARE( fromCode->dataType(), def->dataType() );
5929 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5930 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5931
5932 // multiple
5933 def.reset( new QgsProcessingParameterField( "non_optional", QString(), QVariant(), QString(), QgsProcessingParameterField::Any, true, false ) );
5934 QVERIFY( def->checkValueIsAcceptable( 1 ) );
5935 QVERIFY( def->checkValueIsAcceptable( "test" ) );
5936 QVERIFY( def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
5937 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
5938 QVERIFY( !def->checkValueIsAcceptable( "" ) );
5939 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
5940 QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
5941 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
5942
5943 params.insert( "non_optional", QString( "a;b" ) );
5944 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
5945 QCOMPARE( fields, QStringList() << "a" << "b" );
5946 params.insert( "non_optional", QVariantList() << "a" << "b" );
5947 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
5948 QCOMPARE( fields, QStringList() << "a" << "b" );
5949
5950 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
5951 QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) );
5952 QCOMPARE( def->valueAsPythonString( QStringList() << "a" << "b", context ), QStringLiteral( "['a','b']" ) );
5953
5954 QVariantMap map = def->toVariantMap();
5955 QgsProcessingParameterField fromMap( "x" );
5956 QVERIFY( fromMap.fromVariantMap( map ) );
5957 QCOMPARE( fromMap.name(), def->name() );
5958 QCOMPARE( fromMap.description(), def->description() );
5959 QCOMPARE( fromMap.flags(), def->flags() );
5960 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5961 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
5962 QCOMPARE( fromMap.dataType(), def->dataType() );
5963 QCOMPARE( fromMap.allowMultiple(), def->allowMultiple() );
5964 QCOMPARE( fromMap.defaultToAllFields(), def->defaultToAllFields() );
5965 def.reset( dynamic_cast< QgsProcessingParameterField *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5966 QVERIFY( dynamic_cast< QgsProcessingParameterField *>( def.get() ) );
5967
5968 pythonCode = def->asPythonString();
5969 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=True, defaultValue=None)" ) );
5970 code = def->asScriptCode();
5971 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
5972 QVERIFY( fromCode.get() );
5973 QCOMPARE( fromCode->name(), def->name() );
5974 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
5975 QCOMPARE( fromCode->flags(), def->flags() );
5976 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
5977 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
5978 QCOMPARE( fromCode->dataType(), def->dataType() );
5979 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
5980 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
5981
5982 // default to all fields
5983 def.reset( new QgsProcessingParameterField( "non_optional", QString(), QVariant(), QString(), QgsProcessingParameterField::Any, true, false, true ) );
5984 map = def->toVariantMap();
5985 QVERIFY( fromMap.fromVariantMap( map ) );
5986 QCOMPARE( fromMap.name(), def->name() );
5987 QCOMPARE( fromMap.description(), def->description() );
5988 QCOMPARE( fromMap.flags(), def->flags() );
5989 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
5990 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
5991 QCOMPARE( fromMap.dataType(), def->dataType() );
5992 QCOMPARE( fromMap.allowMultiple(), def->allowMultiple() );
5993 QCOMPARE( fromMap.defaultToAllFields(), def->defaultToAllFields() );
5994 def.reset( dynamic_cast< QgsProcessingParameterField *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
5995 QVERIFY( dynamic_cast< QgsProcessingParameterField *>( def.get() ) );
5996
5997 pythonCode = def->asPythonString();
5998 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('non_optional', '', type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=True, defaultValue=None, defaultToAllFields=True)" ) );
5999 code = def->asScriptCode();
6000 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6001 QVERIFY( fromCode.get() );
6002 QCOMPARE( fromCode->name(), def->name() );
6003 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6004 QCOMPARE( fromCode->flags(), def->flags() );
6005 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6006 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
6007 QCOMPARE( fromCode->dataType(), def->dataType() );
6008 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
6009 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
6010
6011 // optional
6012 def.reset( new QgsProcessingParameterField( "optional", QString(), QString( "def" ), QString(), QgsProcessingParameterField::Any, false, true ) );
6013 QVERIFY( def->checkValueIsAcceptable( 1 ) );
6014 QVERIFY( def->checkValueIsAcceptable( "test" ) );
6015 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
6016 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
6017 QVERIFY( def->checkValueIsAcceptable( "" ) );
6018 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6019
6020 params.insert( "optional", QVariant() );
6021 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6022 QCOMPARE( fields, QStringList() << "def" );
6023
6024 // optional with string list default
6025 def.reset( new QgsProcessingParameterField( "optional", QString(), QStringList() << QStringLiteral( "def" ) << QStringLiteral( "abc" ), QString(), QgsProcessingParameterField::Any, true, true ) );
6026 QVERIFY( def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
6027 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6028 QCOMPARE( fields, QStringList() << "def" << "abc" );
6029 params.insert( "optional", QVariantList() << "f" << "h" );
6030 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6031 QCOMPARE( fields, QStringList() << "f" << "h" );
6032 params.insert( "optional", QStringList() << "g" << "h" );
6033 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6034 QCOMPARE( fields, QStringList() << "g" << "h" );
6035
6036 // optional, no default
6037 def.reset( new QgsProcessingParameterField( "optional", QString(), QVariant(), QString(), QgsProcessingParameterField::Any, false, true ) );
6038 params.insert( "optional", QVariant() );
6039 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6040 QVERIFY( fields.isEmpty() );
6041
6042 pythonCode = def->asPythonString();
6043 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterField('optional', '', optional=True, type=QgsProcessingParameterField.Any, parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) );
6044 code = def->asScriptCode();
6045 QCOMPARE( code, QStringLiteral( "##optional=optional field" ) );
6046 fromCode.reset( dynamic_cast< QgsProcessingParameterField * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6047 QVERIFY( fromCode.get() );
6048 QCOMPARE( fromCode->name(), def->name() );
6049 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6050 QCOMPARE( fromCode->flags(), def->flags() );
6051 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6052 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
6053 QCOMPARE( fromCode->dataType(), def->dataType() );
6054 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
6055 QCOMPARE( fromCode->defaultToAllFields(), def->defaultToAllFields() );
6056
6057 //optional with multiples
6058 def.reset( new QgsProcessingParameterField( "optional", QString(), QString( "abc;def" ), QString(), QgsProcessingParameterField::Any, true, true ) );
6059 QVERIFY( def->checkValueIsAcceptable( 1 ) );
6060 QVERIFY( def->checkValueIsAcceptable( "test" ) );
6061 QVERIFY( def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
6062 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
6063 QVERIFY( def->checkValueIsAcceptable( "" ) );
6064 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6065
6066 params.insert( "optional", QVariant() );
6067 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6068 QCOMPARE( fields, QStringList() << "abc" << "def" );
6069 def.reset( new QgsProcessingParameterField( "optional", QString(), QVariantList() << "abc" << "def", QString(), QgsProcessingParameterField::Any, true, true ) );
6070 params.insert( "optional", QVariant() );
6071 fields = QgsProcessingParameters::parameterAsFields( def.get(), params, context );
6072 QCOMPARE( fields, QStringList() << "abc" << "def" );
6073 }
6074
parameterVectorLayer()6075 void TestQgsProcessing::parameterVectorLayer()
6076 {
6077 // setup a context
6078 QgsProject p;
6079 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6080 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
6081 const QString vector1 = testDataDir + "multipoint.shp";
6082 const QString raster = testDataDir + "landsat.tif";
6083 const QFileInfo fi1( raster );
6084 const QFileInfo fi2( vector1 );
6085 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
6086 QgsVectorLayer *v1 = new QgsVectorLayer( fi2.filePath(), "V4", "ogr" );
6087 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 );
6088 QgsProcessingContext context;
6089 context.setProject( &p );
6090
6091 // not optional!
6092 std::unique_ptr< QgsProcessingParameterVectorLayer > def( new QgsProcessingParameterVectorLayer( "non_optional", QString(), QList< int >(), QString( "somelayer" ), false ) );
6093 QVERIFY( !def->checkValueIsAcceptable( false ) );
6094 QVERIFY( !def->checkValueIsAcceptable( true ) );
6095 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6096 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6097 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6098 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6099 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6100 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
6101 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
6102 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
6103 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6104
6105 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6106 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6107 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6108 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6109
6110 // should be OK
6111 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6112 // ... unless we use context, when the check that the layer actually exists is performed
6113 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
6114
6115 // using existing map layer ID
6116 QVariantMap params;
6117 params.insert( "non_optional", v1->id() );
6118 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6119
6120 // using existing layer
6121 params.insert( "non_optional", QVariant::fromValue( v1 ) );
6122 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6123
6124 // not vector layer
6125 params.insert( "non_optional", r1->id() );
6126 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6127
6128 // using existing non-vector layer
6129 params.insert( "non_optional", QVariant::fromValue( r1 ) );
6130 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6131
6132 // string representing a layer source
6133 params.insert( "non_optional", vector1 );
6134 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->publicSource(), vector1 );
6135
6136 // nonsense string
6137 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
6138 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6139
6140 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6141 QCOMPARE( def->valueAsPythonString( vector1, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "multipoint.shp'" ) ) );
6142 QCOMPARE( def->valueAsPythonString( v1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "multipoint.shp'" ) ) );
6143 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( v1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "multipoint.shp'" ) ) );
6144 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6145 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
6146
6147 QString pythonCode = def->asPythonString();
6148 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorLayer('non_optional', '', defaultValue='somelayer')" ) );
6149
6150 QString code = def->asScriptCode();
6151 QCOMPARE( code, QStringLiteral( "##non_optional=vector somelayer" ) );
6152 std::unique_ptr< QgsProcessingParameterVectorLayer > fromCode( dynamic_cast< QgsProcessingParameterVectorLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6153 QVERIFY( fromCode.get() );
6154 QCOMPARE( fromCode->name(), def->name() );
6155 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6156 QCOMPARE( fromCode->flags(), def->flags() );
6157 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6158
6159 const QVariantMap map = def->toVariantMap();
6160 QgsProcessingParameterVectorLayer fromMap( "x" );
6161 QVERIFY( fromMap.fromVariantMap( map ) );
6162 QCOMPARE( fromMap.name(), def->name() );
6163 QCOMPARE( fromMap.description(), def->description() );
6164 QCOMPARE( fromMap.flags(), def->flags() );
6165 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6166 def.reset( dynamic_cast< QgsProcessingParameterVectorLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6167 QVERIFY( dynamic_cast< QgsProcessingParameterVectorLayer *>( def.get() ) );
6168
6169 // optional
6170 def.reset( new QgsProcessingParameterVectorLayer( "optional", QString(), QList< int >(), v1->id(), true ) );
6171 params.insert( "optional", QVariant() );
6172 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6173 QVERIFY( def->checkValueIsAcceptable( false ) );
6174 QVERIFY( def->checkValueIsAcceptable( true ) );
6175 QVERIFY( def->checkValueIsAcceptable( 5 ) );
6176 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6177 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6178 QVERIFY( def->checkValueIsAcceptable( "" ) );
6179 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6180 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6181
6182 pythonCode = def->asPythonString();
6183 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterVectorLayer('optional', '', optional=True, defaultValue='" ) + v1->id() + "')" ) );
6184
6185 code = def->asScriptCode();
6186 QCOMPARE( code, QString( QStringLiteral( "##optional=optional vector " ) + v1->id() ) );
6187 fromCode.reset( dynamic_cast< QgsProcessingParameterVectorLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6188 QVERIFY( fromCode.get() );
6189 QCOMPARE( fromCode->name(), def->name() );
6190 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6191 QCOMPARE( fromCode->flags(), def->flags() );
6192 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6193
6194 //optional with direct layer default
6195 def.reset( new QgsProcessingParameterVectorLayer( "optional", QString(), QList< int >(), QVariant::fromValue( v1 ), true ) );
6196 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6197 }
6198
parameterMeshLayer()6199 void TestQgsProcessing::parameterMeshLayer()
6200 {
6201 // setup a context
6202 QgsProject p;
6203 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6204 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
6205 const QString vector1 = testDataDir + "multipoint.shp";
6206 const QString raster = testDataDir + "landsat.tif";
6207 const QString mesh = testDataDir + "mesh/quad_and_triangle.2dm";
6208 const QFileInfo fi1( raster );
6209 const QFileInfo fi2( vector1 );
6210 const QFileInfo fi3( mesh );
6211 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
6212 QgsVectorLayer *v1 = new QgsVectorLayer( fi2.filePath(), "V4", "ogr" );
6213 QgsMeshLayer *m1 = new QgsMeshLayer( fi3.filePath(), "M1", "mdal" );
6214 Q_ASSERT( m1 );
6215 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 << m1 );
6216 QgsProcessingContext context;
6217 context.setProject( &p );
6218
6219 // not optional!
6220 std::unique_ptr< QgsProcessingParameterMeshLayer > def( new QgsProcessingParameterMeshLayer( "non_optional", QString(), QString( "somelayer" ), false ) );
6221 QVERIFY( !def->checkValueIsAcceptable( false ) );
6222 QVERIFY( !def->checkValueIsAcceptable( true ) );
6223 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6224 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6225 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6226 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6227 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6228 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( m1 ) ) );
6229 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
6230 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
6231 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
6232 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6233
6234 // should be OK
6235 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.2dm" ) );
6236 // ... unless we use context, when the check that the layer actually exists is performed
6237 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.2dm", &context ) );
6238
6239 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6240 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6241 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6242 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6243
6244 // using existing map layer ID
6245 QVariantMap params;
6246 params.insert( "non_optional", m1->id() );
6247 QCOMPARE( QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context )->id(), m1->id() );
6248
6249 // using existing layer
6250 params.insert( "non_optional", QVariant::fromValue( m1 ) );
6251 QCOMPARE( QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context )->id(), m1->id() );
6252
6253 // not mesh layer
6254 params.insert( "non_optional", r1->id() );
6255 QVERIFY( !QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context ) );
6256
6257 // using existing non-mesh layer
6258 params.insert( "non_optional", QVariant::fromValue( r1 ) );
6259 QVERIFY( !QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context ) );
6260
6261 // string representing a layer source
6262 params.insert( "non_optional", mesh );
6263 QCOMPARE( QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context )->publicSource(), mesh );
6264
6265 // nonsense string
6266 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
6267 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6268
6269 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6270 QCOMPARE( def->valueAsPythonString( mesh, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm'" ) ) );
6271 QCOMPARE( def->valueAsPythonString( m1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm'" ) ) );
6272 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( m1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "mesh/quad_and_triangle.2dm'" ) ) );
6273 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6274 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.2dm" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.2dm'" ) );
6275
6276 QString pythonCode = def->asPythonString();
6277 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshLayer('non_optional', '', defaultValue='somelayer')" ) );
6278
6279 QString code = def->asScriptCode();
6280 QCOMPARE( code, QStringLiteral( "##non_optional=mesh somelayer" ) );
6281 std::unique_ptr< QgsProcessingParameterMeshLayer > fromCode( dynamic_cast< QgsProcessingParameterMeshLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6282 QVERIFY( fromCode.get() );
6283 QCOMPARE( fromCode->name(), def->name() );
6284 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6285 QCOMPARE( fromCode->flags(), def->flags() );
6286 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6287
6288 const QVariantMap map = def->toVariantMap();
6289 QgsProcessingParameterMeshLayer fromMap( "x" );
6290 QVERIFY( fromMap.fromVariantMap( map ) );
6291 QCOMPARE( fromMap.name(), def->name() );
6292 QCOMPARE( fromMap.description(), def->description() );
6293 QCOMPARE( fromMap.flags(), def->flags() );
6294 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6295 def.reset( dynamic_cast< QgsProcessingParameterMeshLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6296 QVERIFY( dynamic_cast< QgsProcessingParameterMeshLayer *>( def.get() ) );
6297
6298 // optional
6299 def.reset( new QgsProcessingParameterMeshLayer( "optional", QString(), m1->id(), true ) );
6300 params.insert( "optional", QVariant() );
6301 QCOMPARE( QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context )->id(), m1->id() );
6302 QVERIFY( def->checkValueIsAcceptable( false ) );
6303 QVERIFY( def->checkValueIsAcceptable( true ) );
6304 QVERIFY( def->checkValueIsAcceptable( 5 ) );
6305 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6306 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.2dm" ) );
6307 QVERIFY( def->checkValueIsAcceptable( "" ) );
6308 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6309 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6310
6311 pythonCode = def->asPythonString();
6312 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterMeshLayer('optional', '', optional=True, defaultValue='" ) + m1->id() + "')" ) );
6313
6314 code = def->asScriptCode();
6315 QCOMPARE( code, QString( QStringLiteral( "##optional=optional mesh " ) + m1->id() ) );
6316 fromCode.reset( dynamic_cast< QgsProcessingParameterMeshLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6317 QVERIFY( fromCode.get() );
6318 QCOMPARE( fromCode->name(), def->name() );
6319 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6320 QCOMPARE( fromCode->flags(), def->flags() );
6321 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6322
6323 //optional with direct layer default
6324 def.reset( new QgsProcessingParameterMeshLayer( "optional", QString(), QVariant::fromValue( m1 ), true ) );
6325 QCOMPARE( QgsProcessingParameters::parameterAsMeshLayer( def.get(), params, context )->id(), m1->id() );
6326 }
6327
parameterFeatureSource()6328 void TestQgsProcessing::parameterFeatureSource()
6329 {
6330 // setup a context
6331 QgsProject p;
6332 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6333 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
6334 const QString vector1 = testDataDir + "multipoint.shp";
6335 const QString vector2 = testDataDir + "lines.shp";
6336 const QString raster = testDataDir + "landsat.tif";
6337 const QFileInfo fi1( raster );
6338 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
6339 QgsVectorLayer *v1 = new QgsVectorLayer( "Polygon?crs=EPSG:3111", "V4", "memory" );
6340 QgsVectorLayer *v2 = new QgsVectorLayer( vector2, "V5", "ogr" );
6341 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 << v2 );
6342 QgsProcessingContext context;
6343 context.setProject( &p );
6344
6345 // not optional!
6346 std::unique_ptr< QgsProcessingParameterFeatureSource > def( new QgsProcessingParameterFeatureSource( "non_optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
6347 QVERIFY( !def->checkValueIsAcceptable( false ) );
6348 QVERIFY( !def->checkValueIsAcceptable( true ) );
6349 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6350 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6351 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6352 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6353 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6354 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "" ) ) );
6355 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
6356 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
6357 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
6358 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6359
6360 // should be OK
6361 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6362 // ... unless we use context, when the check that the layer actually exists is performed
6363 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
6364
6365 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6366 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6367 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6368 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6369
6370 // using existing map layer ID
6371 QVariantMap params;
6372 params.insert( "non_optional", v1->id() );
6373 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6374
6375 // using existing layer
6376 params.insert( "non_optional", QVariant::fromValue( v1 ) );
6377 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6378
6379 // not vector layer
6380 params.insert( "non_optional", r1->id() );
6381 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6382
6383 // using existing non-vector layer
6384 params.insert( "non_optional", QVariant::fromValue( r1 ) );
6385 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6386
6387 // string representing a layer source
6388 params.insert( "non_optional", vector1 );
6389 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->publicSource(), vector1 );
6390
6391 // nonsense string
6392 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
6393 QVERIFY( !QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context ) );
6394
6395 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6396 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
6397 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc" ) ), context ), QStringLiteral( "'abc'" ) );
6398 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( "abc'def" ) ), context ), QStringLiteral( "\"abc'def\"" ) );
6399 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( v2->id() ) ), context ), QStringLiteral( "'%1'" ).arg( vector2 ) );
6400 QCOMPARE( def->valueAsPythonString( v2->id(), context ), QStringLiteral( "'%1'" ).arg( vector2 ) );
6401 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), true ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', selectedFeaturesOnly=True, featureLimit=-1, geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid)" ) );
6402 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "dbname='mydb' host=localhost port=5432 sslmode=disable key='id'" ), true ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(\"dbname='mydb' host=localhost port=5432 sslmode=disable key='id'\", selectedFeaturesOnly=True, featureLimit=-1, geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid)" ) );
6403 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
6404 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || 'def'" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \\'def\\'')" ) );
6405 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), true ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), selectedFeaturesOnly=True, featureLimit=-1, geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid)" ) );
6406 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, 11 ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', selectedFeaturesOnly=False, featureLimit=11, geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid)" ) );
6407 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), false, 11 ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), selectedFeaturesOnly=False, featureLimit=11, geometryCheck=QgsFeatureRequest.GeometryAbortOnInvalid)" ) );
6408 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flags(), QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "'abc'" ) );
6409 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), false, -1, QgsProcessingFeatureSourceDefinition::Flags(), QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
6410 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6411 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6412 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6413 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6414 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromValue( "abc" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition('abc', selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6415 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ), false, -1, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition::Flag::FlagCreateIndividualOutputPerInputFeature, QgsFeatureRequest::GeometrySkipInvalid ) ), context ), QStringLiteral( "QgsProcessingFeatureSourceDefinition(QgsProperty.fromExpression('\"abc\" || \"def\"'), selectedFeaturesOnly=False, featureLimit=-1, flags=QgsProcessingFeatureSourceDefinition.FlagOverrideDefaultGeometryCheck | QgsProcessingFeatureSourceDefinition.FlagCreateIndividualOutputPerInputFeature, geometryCheck=QgsFeatureRequest.GeometrySkipInvalid)" ) );
6416 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6417 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"='my val'" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=\\'my val\\'')" ) );
6418 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( v2 ), context ), QStringLiteral( "'%1'" ).arg( vector2 ) );
6419 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
6420 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
6421 QCOMPARE( def->valueAsPythonString( QStringLiteral( "postgres://uri='complex' username=\"complex\"" ), context ), QStringLiteral( "'postgres://uri=\\'complex\\' username=\"complex\"'" ) );
6422
6423 const QVariantMap map = def->toVariantMap();
6424 QgsProcessingParameterFeatureSource fromMap( "x" );
6425 QVERIFY( fromMap.fromVariantMap( map ) );
6426 QCOMPARE( fromMap.name(), def->name() );
6427 QCOMPARE( fromMap.description(), def->description() );
6428 QCOMPARE( fromMap.flags(), def->flags() );
6429 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6430 QCOMPARE( fromMap.dataTypes(), def->dataTypes() );
6431 def.reset( dynamic_cast< QgsProcessingParameterFeatureSource *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6432 QVERIFY( dynamic_cast< QgsProcessingParameterFeatureSource *>( def.get() ) );
6433
6434 QString pythonCode = def->asPythonString();
6435 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorAnyGeometry], defaultValue='')" ) );
6436
6437 QString code = def->asScriptCode();
6438 QCOMPARE( code, QStringLiteral( "##non_optional=source" ) );
6439 std::unique_ptr< QgsProcessingParameterFeatureSource > fromCode( dynamic_cast< QgsProcessingParameterFeatureSource * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6440 QVERIFY( fromCode.get() );
6441 QCOMPARE( fromCode->name(), def->name() );
6442 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6443 QCOMPARE( fromCode->flags(), def->flags() );
6444 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6445
6446 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint );
6447 pythonCode = def->asPythonString();
6448 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorPoint], defaultValue='')" ) );
6449 code = def->asScriptCode();
6450 QCOMPARE( code, QStringLiteral( "##non_optional=source point" ) );
6451 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorLine );
6452 pythonCode = def->asPythonString();
6453 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorLine], defaultValue='')" ) );
6454 code = def->asScriptCode();
6455 QCOMPARE( code, QStringLiteral( "##non_optional=source line" ) );
6456 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPolygon );
6457 pythonCode = def->asPythonString();
6458 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorPolygon], defaultValue='')" ) );
6459 code = def->asScriptCode();
6460 QCOMPARE( code, QStringLiteral( "##non_optional=source polygon" ) );
6461 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorLine );
6462 pythonCode = def->asPythonString();
6463 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorPoint,QgsProcessing.TypeVectorLine], defaultValue='')" ) );
6464 code = def->asScriptCode();
6465 QCOMPARE( code, QStringLiteral( "##non_optional=source point line" ) );
6466 def->setDataTypes( QList< int >() << QgsProcessing::TypeVectorPoint << QgsProcessing::TypeVectorPolygon );
6467 pythonCode = def->asPythonString();
6468 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSource('non_optional', '', types=[QgsProcessing.TypeVectorPoint,QgsProcessing.TypeVectorPolygon], defaultValue='')" ) );
6469 code = def->asScriptCode();
6470 QCOMPARE( code, QStringLiteral( "##non_optional=source point polygon" ) );
6471
6472
6473 // optional
6474 def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, v1->id(), true ) );
6475 params.insert( "optional", QVariant() );
6476 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6477 QVERIFY( def->checkValueIsAcceptable( false ) );
6478 QVERIFY( def->checkValueIsAcceptable( true ) );
6479 QVERIFY( def->checkValueIsAcceptable( 5 ) );
6480 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6481 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6482 QVERIFY( def->checkValueIsAcceptable( "" ) );
6483 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6484 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
6485
6486 pythonCode = def->asPythonString();
6487 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterFeatureSource('optional', '', optional=True, types=[QgsProcessing.TypeVectorAnyGeometry], defaultValue='" ) + v1->id() + "')" ) );
6488 code = def->asScriptCode();
6489 QCOMPARE( code, QString( QStringLiteral( "##optional=optional source " ) + v1->id() ) );
6490 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSource * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6491 QVERIFY( fromCode.get() );
6492 QCOMPARE( fromCode->name(), def->name() );
6493 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6494 QCOMPARE( fromCode->flags(), def->flags() );
6495 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6496
6497
6498 //optional with direct layer default
6499 def.reset( new QgsProcessingParameterFeatureSource( "optional", QString(), QList< int >() << QgsProcessing::TypeVectorAnyGeometry, QVariant::fromValue( v1 ), true ) );
6500 QCOMPARE( QgsProcessingParameters::parameterAsVectorLayer( def.get(), params, context )->id(), v1->id() );
6501
6502 // invalidSourceError
6503 params.clear();
6504 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "MISSING" ) ), QStringLiteral( "Could not load source layer for MISSING: no value specified for parameter" ) );
6505 params.insert( QStringLiteral( "INPUT" ), QStringLiteral( "my layer" ) );
6506 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my layer not found" ) );
6507 params.insert( QStringLiteral( "INPUT" ), QgsProperty::fromValue( "my prop layer" ) );
6508 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my prop layer not found" ) );
6509 params.insert( QStringLiteral( "INPUT" ), QgsProcessingFeatureSourceDefinition( QStringLiteral( "my prop layer" ) ) );
6510 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my prop layer not found" ) );
6511 params.insert( QStringLiteral( "INPUT" ), QVariant::fromValue( v1 ) );
6512 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: invalid value" ) );
6513 params.insert( QStringLiteral( "INPUT" ), QgsProcessingOutputLayerDefinition( QStringLiteral( "my prop layer" ) ) );
6514 QCOMPARE( QgsProcessingAlgorithm::invalidSourceError( params, QStringLiteral( "INPUT" ) ), QStringLiteral( "Could not load source layer for INPUT: my prop layer not found" ) );
6515 }
6516
parameterFeatureSink()6517 void TestQgsProcessing::parameterFeatureSink()
6518 {
6519 // setup a context
6520 QgsProject p;
6521 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6522 QgsProcessingContext context;
6523 context.setProject( &p );
6524
6525 // not optional!
6526 std::unique_ptr< QgsProcessingParameterFeatureSink > def( new QgsProcessingParameterFeatureSink( "non_optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
6527 QVERIFY( !def->checkValueIsAcceptable( false ) );
6528 QVERIFY( !def->checkValueIsAcceptable( true ) );
6529 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6530 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6531 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6532 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6533 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6534 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "" ) ) );
6535 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
6536 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6537
6538 // should be OK with or without context - it's an output layer!
6539 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6540 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
6541
6542 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6543 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
6544 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "'abc'" ) );
6545 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
6546 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
6547 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6548 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
6549 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
6550
6551 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) );
6552 QCOMPARE( def->generateTemporaryDestination(), QStringLiteral( "memory:" ) );
6553 def->setSupportsNonFileBasedOutput( false );
6554 QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".gpkg" ) ) );
6555 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
6556
6557 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6558 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6559 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6560 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6561
6562 const QVariantMap map = def->toVariantMap();
6563 QgsProcessingParameterFeatureSink fromMap( "x" );
6564 QVERIFY( fromMap.fromVariantMap( map ) );
6565 QCOMPARE( fromMap.name(), def->name() );
6566 QCOMPARE( fromMap.description(), def->description() );
6567 QCOMPARE( fromMap.flags(), def->flags() );
6568 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6569 QCOMPARE( fromMap.dataType(), def->dataType() );
6570 QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
6571 def.reset( dynamic_cast< QgsProcessingParameterFeatureSink *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6572 QVERIFY( dynamic_cast< QgsProcessingParameterFeatureSink *>( def.get() ) );
6573
6574 QString pythonCode = def->asPythonString();
6575 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('non_optional', '', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue='')" ) );
6576
6577 QString code = def->asScriptCode();
6578 QCOMPARE( code, QStringLiteral( "##non_optional=sink" ) );
6579 std::unique_ptr< QgsProcessingParameterFeatureSink > fromCode( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6580 QVERIFY( fromCode.get() );
6581 QCOMPARE( fromCode->name(), def->name() );
6582 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6583 QCOMPARE( fromCode->flags(), def->flags() );
6584 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6585 QCOMPARE( fromCode->dataType(), def->dataType() );
6586
6587 def->setDataType( QgsProcessing::TypeVectorPoint );
6588 pythonCode = def->asPythonString();
6589 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('non_optional', '', type=QgsProcessing.TypeVectorPoint, createByDefault=True, defaultValue='')" ) );
6590 code = def->asScriptCode();
6591 QCOMPARE( code, QStringLiteral( "##non_optional=sink point" ) );
6592 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6593 QCOMPARE( fromCode->dataType(), def->dataType() );
6594 def->setDataType( QgsProcessing::TypeVectorLine );
6595 pythonCode = def->asPythonString();
6596 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('non_optional', '', type=QgsProcessing.TypeVectorLine, createByDefault=True, defaultValue='')" ) );
6597 code = def->asScriptCode();
6598 QCOMPARE( code, QStringLiteral( "##non_optional=sink line" ) );
6599 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6600 QCOMPARE( fromCode->dataType(), def->dataType() );
6601 def->setDataType( QgsProcessing::TypeVectorPolygon );
6602 pythonCode = def->asPythonString();
6603 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('non_optional', '', type=QgsProcessing.TypeVectorPolygon, createByDefault=True, defaultValue='')" ) );
6604 code = def->asScriptCode();
6605 QCOMPARE( code, QStringLiteral( "##non_optional=sink polygon" ) );
6606 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6607 QCOMPARE( fromCode->dataType(), def->dataType() );
6608 def->setDataType( QgsProcessing::TypeVector );
6609 pythonCode = def->asPythonString();
6610 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('non_optional', '', type=QgsProcessing.TypeVector, createByDefault=True, defaultValue='')" ) );
6611 code = def->asScriptCode();
6612 QCOMPARE( code, QStringLiteral( "##non_optional=sink table" ) );
6613 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6614 QCOMPARE( fromCode->dataType(), def->dataType() );
6615
6616 // optional
6617 def.reset( new QgsProcessingParameterFeatureSink( "optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) );
6618 QVERIFY( !def->checkValueIsAcceptable( false ) );
6619 QVERIFY( !def->checkValueIsAcceptable( true ) );
6620 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6621 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6622 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6623 QVERIFY( def->checkValueIsAcceptable( "" ) );
6624 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6625 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6626 def->setCreateByDefault( false );
6627 QVERIFY( !def->createByDefault() );
6628
6629 pythonCode = def->asPythonString();
6630 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('optional', '', optional=True, type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=False, defaultValue='')" ) );
6631 code = def->asScriptCode();
6632 QCOMPARE( code, QStringLiteral( "##optional=optional sink" ) );
6633 fromCode.reset( dynamic_cast< QgsProcessingParameterFeatureSink * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6634 QCOMPARE( fromCode->name(), def->name() );
6635 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6636 QCOMPARE( fromCode->flags(), def->flags() );
6637 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6638 QCOMPARE( fromCode->dataType(), def->dataType() );
6639 QVERIFY( !def->createByDefault() );
6640
6641 // test hasGeometry
6642 QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeMapLayer ).hasGeometry() );
6643 QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorAnyGeometry ).hasGeometry() );
6644 QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorPoint ).hasGeometry() );
6645 QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorLine ).hasGeometry() );
6646 QVERIFY( QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVectorPolygon ).hasGeometry() );
6647 QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeRaster ).hasGeometry() );
6648 QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeFile ).hasGeometry() );
6649 QVERIFY( !QgsProcessingParameterFeatureSink( "test", QString(), QgsProcessing::TypeVector ).hasGeometry() );
6650
6651 // invalidSinkError
6652 QVariantMap params;
6653 QCOMPARE( QgsProcessingAlgorithm::invalidSinkError( params, QStringLiteral( "MISSING" ) ), QStringLiteral( "Could not create destination layer for MISSING: no value specified for parameter" ) );
6654 params.insert( QStringLiteral( "OUTPUT" ), QStringLiteral( "d:/test.shp" ) );
6655 QCOMPARE( QgsProcessingAlgorithm::invalidSinkError( params, QStringLiteral( "OUTPUT" ) ), QStringLiteral( "Could not create destination layer for OUTPUT: d:/test.shp" ) );
6656 params.insert( QStringLiteral( "OUTPUT" ), QgsProperty::fromValue( QStringLiteral( "d:/test2.shp" ) ) );
6657 QCOMPARE( QgsProcessingAlgorithm::invalidSinkError( params, QStringLiteral( "OUTPUT" ) ), QStringLiteral( "Could not create destination layer for OUTPUT: d:/test2.shp" ) );
6658 params.insert( QStringLiteral( "OUTPUT" ), QgsProcessingOutputLayerDefinition( QStringLiteral( "d:/test3.shp" ) ) );
6659 QCOMPARE( QgsProcessingAlgorithm::invalidSinkError( params, QStringLiteral( "OUTPUT" ) ), QStringLiteral( "Could not create destination layer for OUTPUT: d:/test3.shp" ) );
6660 params.insert( QStringLiteral( "OUTPUT" ), QgsProcessingFeatureSourceDefinition( QStringLiteral( "source" ) ) );
6661 QCOMPARE( QgsProcessingAlgorithm::invalidSinkError( params, QStringLiteral( "OUTPUT" ) ), QStringLiteral( "Could not create destination layer for OUTPUT: invalid value" ) );
6662
6663 // test supported output vector layer extensions
6664
6665 def.reset( new QgsProcessingParameterFeatureSink( "with_geom", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) );
6666 DummyProvider3 provider;
6667 provider.loadAlgorithms();
6668 def->mOriginalProvider = &provider;
6669 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 2 );
6670 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "mif" ) );
6671 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 1 ), QStringLiteral( "tab" ) );
6672 def->mOriginalProvider = nullptr;
6673 def->mAlgorithm = const_cast< QgsProcessingAlgorithm * >( provider.algorithms().at( 0 ) );
6674 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 2 );
6675 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "mif" ) );
6676 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 1 ), QStringLiteral( "tab" ) );
6677
6678 def.reset( new QgsProcessingParameterFeatureSink( "no_geom", QString(), QgsProcessing::TypeVector, QString(), true ) );
6679 def->mOriginalProvider = &provider;
6680 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 1 );
6681 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "dbf" ) );
6682 def->mOriginalProvider = nullptr;
6683 def->mAlgorithm = const_cast< QgsProcessingAlgorithm * >( provider.algorithms().at( 0 ) );
6684 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 1 );
6685 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "dbf" ) );
6686 }
6687
parameterVectorOut()6688 void TestQgsProcessing::parameterVectorOut()
6689 {
6690 // setup a context
6691 QgsProject p;
6692 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6693 QgsProcessingContext context;
6694 context.setProject( &p );
6695
6696 // not optional!
6697 std::unique_ptr< QgsProcessingParameterVectorDestination > def( new QgsProcessingParameterVectorDestination( "non_optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
6698 QVERIFY( !def->checkValueIsAcceptable( false ) );
6699 QVERIFY( !def->checkValueIsAcceptable( true ) );
6700 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6701 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6702 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6703 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6704 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6705 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "" ) ) );
6706 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer1231123" ) ) ) );
6707 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6708
6709 // should be OK with or without context - it's an output layer!
6710 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6711 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
6712
6713 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6714 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
6715 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "'abc'" ) );
6716 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
6717 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
6718 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6719 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
6720 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
6721
6722 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "gpkg" ) );
6723 QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".gpkg" ) ) );
6724 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
6725
6726 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6727 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6728 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6729 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6730
6731 const QVariantMap map = def->toVariantMap();
6732 QgsProcessingParameterVectorDestination fromMap( "x" );
6733 QVERIFY( fromMap.fromVariantMap( map ) );
6734 QCOMPARE( fromMap.name(), def->name() );
6735 QCOMPARE( fromMap.description(), def->description() );
6736 QCOMPARE( fromMap.flags(), def->flags() );
6737 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6738 QCOMPARE( fromMap.dataType(), def->dataType() );
6739 def.reset( dynamic_cast< QgsProcessingParameterVectorDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6740 QVERIFY( dynamic_cast< QgsProcessingParameterVectorDestination *>( def.get() ) );
6741
6742 QString pythonCode = def->asPythonString();
6743 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorDestination('non_optional', '', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue='')" ) );
6744 QString code = def->asScriptCode();
6745 QCOMPARE( code, QStringLiteral( "##non_optional=vectorDestination" ) );
6746 std::unique_ptr< QgsProcessingParameterVectorDestination > fromCode( dynamic_cast< QgsProcessingParameterVectorDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6747 QVERIFY( fromCode.get() );
6748 QCOMPARE( fromCode->name(), def->name() );
6749 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6750 QCOMPARE( fromCode->flags(), def->flags() );
6751 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6752 QCOMPARE( fromCode->dataType(), def->dataType() );
6753
6754 def->setDataType( QgsProcessing::TypeVectorPoint );
6755 pythonCode = def->asPythonString();
6756 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorDestination('non_optional', '', type=QgsProcessing.TypeVectorPoint, createByDefault=True, defaultValue='')" ) );
6757 code = def->asScriptCode();
6758 QCOMPARE( code, QStringLiteral( "##non_optional=vectorDestination point" ) );
6759 fromCode.reset( dynamic_cast< QgsProcessingParameterVectorDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6760 QCOMPARE( fromCode->dataType(), def->dataType() );
6761 def->setDataType( QgsProcessing::TypeVectorLine );
6762 pythonCode = def->asPythonString();
6763 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorDestination('non_optional', '', type=QgsProcessing.TypeVectorLine, createByDefault=True, defaultValue='')" ) );
6764 code = def->asScriptCode();
6765 QCOMPARE( code, QStringLiteral( "##non_optional=vectorDestination line" ) );
6766 fromCode.reset( dynamic_cast< QgsProcessingParameterVectorDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6767 QCOMPARE( fromCode->dataType(), def->dataType() );
6768 def->setDataType( QgsProcessing::TypeVectorPolygon );
6769 pythonCode = def->asPythonString();
6770 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorDestination('non_optional', '', type=QgsProcessing.TypeVectorPolygon, createByDefault=True, defaultValue='')" ) );
6771 code = def->asScriptCode();
6772 QCOMPARE( code, QStringLiteral( "##non_optional=vectorDestination polygon" ) );
6773 fromCode.reset( dynamic_cast< QgsProcessingParameterVectorDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6774 QCOMPARE( fromCode->dataType(), def->dataType() );
6775
6776 // optional
6777 def.reset( new QgsProcessingParameterVectorDestination( "optional", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) );
6778 QVERIFY( !def->checkValueIsAcceptable( false ) );
6779 QVERIFY( !def->checkValueIsAcceptable( true ) );
6780 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6781 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6782 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
6783 QVERIFY( def->checkValueIsAcceptable( "" ) );
6784 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6785 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6786
6787 pythonCode = def->asPythonString();
6788 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterVectorDestination('optional', '', optional=True, type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue='')" ) );
6789 code = def->asScriptCode();
6790 QCOMPARE( code, QStringLiteral( "##optional=optional vectorDestination" ) );
6791 fromCode.reset( dynamic_cast< QgsProcessingParameterVectorDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6792 QCOMPARE( fromCode->name(), def->name() );
6793 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6794 QCOMPARE( fromCode->flags(), def->flags() );
6795 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6796 QCOMPARE( fromCode->dataType(), def->dataType() );
6797
6798 // test hasGeometry
6799 QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeMapLayer ).hasGeometry() );
6800 QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorAnyGeometry ).hasGeometry() );
6801 QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorPoint ).hasGeometry() );
6802 QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorLine ).hasGeometry() );
6803 QVERIFY( QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVectorPolygon ).hasGeometry() );
6804 QVERIFY( !QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeRaster ).hasGeometry() );
6805 QVERIFY( !QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeFile ).hasGeometry() );
6806 QVERIFY( !QgsProcessingParameterVectorDestination( "test", QString(), QgsProcessing::TypeVector ).hasGeometry() );
6807
6808 // test layers to load on completion
6809 def.reset( new QgsProcessingParameterVectorDestination( "x", QStringLiteral( "desc" ), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) );
6810 QgsProcessingOutputLayerDefinition fs = QgsProcessingOutputLayerDefinition( QStringLiteral( "test.shp" ) );
6811 fs.destinationProject = &p;
6812 QVariantMap params;
6813 params.insert( QStringLiteral( "x" ), QVariant::fromValue( fs ) );
6814 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context ), QStringLiteral( "test.shp" ) );
6815
6816 // make sure layer was automatically added to list to load on completion
6817 QCOMPARE( context.layersToLoadOnCompletion().size(), 1 );
6818 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), QStringLiteral( "test.shp" ) );
6819 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "desc" ) );
6820 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).layerTypeHint, QgsProcessingUtils::LayerHint::Vector );
6821
6822 // with name overloading
6823 QgsProcessingContext context2;
6824 fs = QgsProcessingOutputLayerDefinition( QStringLiteral( "test.shp" ) );
6825 fs.destinationProject = &p;
6826 fs.destinationName = QStringLiteral( "my_dest" );
6827 params.insert( QStringLiteral( "x" ), QVariant::fromValue( fs ) );
6828 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context2 ), QStringLiteral( "test.shp" ) );
6829 QCOMPARE( context2.layersToLoadOnCompletion().size(), 1 );
6830 QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), QStringLiteral( "test.shp" ) );
6831 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) );
6832 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "x" ) );
6833 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).layerTypeHint, QgsProcessingUtils::LayerHint::Vector );
6834
6835 QgsProcessingContext context3;
6836 params.insert( QStringLiteral( "x" ), QgsProcessing::TEMPORARY_OUTPUT );
6837 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context3 ).right( 6 ), QStringLiteral( "x.gpkg" ) );
6838
6839 QgsProcessingContext context4;
6840 fs.sink = QgsProperty::fromValue( QgsProcessing::TEMPORARY_OUTPUT );
6841 params.insert( QStringLiteral( "x" ), QVariant::fromValue( fs ) );
6842 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context4 ).right( 6 ), QStringLiteral( "x.gpkg" ) );
6843
6844 // test supported output vector layer extensions
6845
6846 def.reset( new QgsProcessingParameterVectorDestination( "with_geom", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), true ) );
6847 DummyProvider3 provider;
6848 QString error;
6849 QVERIFY( provider.isSupportedOutputValue( QVariant(), def.get(), context, error ) ); // optional
6850 QVERIFY( provider.isSupportedOutputValue( QString(), def.get(), context, error ) ); // optional
6851 QVERIFY( !provider.isSupportedOutputValue( "d:/test.shp", def.get(), context, error ) );
6852 QVERIFY( !provider.isSupportedOutputValue( "d:/test.SHP", def.get(), context, error ) );
6853 QVERIFY( !provider.isSupportedOutputValue( "ogr:d:/test.shp", def.get(), context, error ) );
6854 QVERIFY( !provider.isSupportedOutputValue( QgsProcessingOutputLayerDefinition( "d:/test.SHP" ), def.get(), context, error ) );
6855 QVERIFY( provider.isSupportedOutputValue( "d:/test.mif", def.get(), context, error ) );
6856 QVERIFY( provider.isSupportedOutputValue( "d:/test.MIF", def.get(), context, error ) );
6857 QVERIFY( provider.isSupportedOutputValue( "ogr:d:/test.MIF", def.get(), context, error ) );
6858 QVERIFY( provider.isSupportedOutputValue( QgsProcessingOutputLayerDefinition( "d:/test.MIF" ), def.get(), context, error ) );
6859 def.reset( new QgsProcessingParameterVectorDestination( "with_geom", QString(), QgsProcessing::TypeVectorAnyGeometry, QString(), false ) );
6860 QVERIFY( !provider.isSupportedOutputValue( QVariant(), def.get(), context, error ) ); // non-optional
6861 QVERIFY( !provider.isSupportedOutputValue( QString(), def.get(), context, error ) ); // non-optional
6862
6863 provider.loadAlgorithms();
6864 def->mOriginalProvider = &provider;
6865 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 2 );
6866 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "mif" ) );
6867 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 1 ), QStringLiteral( "tab" ) );
6868 def->mOriginalProvider = nullptr;
6869 def->mAlgorithm = const_cast< QgsProcessingAlgorithm * >( provider.algorithms().at( 0 ) );
6870 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 2 );
6871 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "mif" ) );
6872 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 1 ), QStringLiteral( "tab" ) );
6873
6874 def.reset( new QgsProcessingParameterVectorDestination( "no_geom", QString(), QgsProcessing::TypeVector, QString(), true ) );
6875 def->mOriginalProvider = &provider;
6876 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 1 );
6877 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "dbf" ) );
6878 def->mOriginalProvider = nullptr;
6879 def->mAlgorithm = const_cast< QgsProcessingAlgorithm * >( provider.algorithms().at( 0 ) );
6880 QCOMPARE( def->supportedOutputVectorLayerExtensions().count(), 1 );
6881 QCOMPARE( def->supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "dbf" ) );
6882 }
6883
parameterRasterOut()6884 void TestQgsProcessing::parameterRasterOut()
6885 {
6886 // setup a context
6887 QgsProject p;
6888 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
6889 QgsProcessingContext context;
6890 context.setProject( &p );
6891
6892 // not optional!
6893 std::unique_ptr< QgsProcessingParameterRasterDestination > def( new QgsProcessingParameterRasterDestination( "non_optional", QString(), QVariant(), false ) );
6894 QVERIFY( !def->checkValueIsAcceptable( false ) );
6895 QVERIFY( !def->checkValueIsAcceptable( true ) );
6896 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6897 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6898 QVERIFY( !def->checkValueIsAcceptable( "" ) );
6899 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
6900 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6901 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "" ) ) );
6902 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
6903 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
6904
6905 // should be OK with or without context - it's an output layer!
6906 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) );
6907 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif", &context ) );
6908
6909 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
6910 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "tif" ) );
6911 QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".tif" ) ) );
6912 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
6913
6914 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
6915 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
6916 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.2dm" ) ) );
6917 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
6918
6919 QVariantMap params;
6920 params.insert( "non_optional", "test.tif" );
6921 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context ), QStringLiteral( "test.tif" ) );
6922 params.insert( "non_optional", QgsProcessingOutputLayerDefinition( "test.tif" ) );
6923 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context ), QStringLiteral( "test.tif" ) );
6924
6925 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
6926 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "'abc'" ) );
6927 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
6928 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
6929 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
6930 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
6931 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
6932
6933 const QVariantMap map = def->toVariantMap();
6934 QgsProcessingParameterRasterDestination fromMap( "x" );
6935 QVERIFY( fromMap.fromVariantMap( map ) );
6936 QCOMPARE( fromMap.name(), def->name() );
6937 QCOMPARE( fromMap.description(), def->description() );
6938 QCOMPARE( fromMap.flags(), def->flags() );
6939 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
6940 QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
6941 def.reset( dynamic_cast< QgsProcessingParameterRasterDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
6942 QVERIFY( dynamic_cast< QgsProcessingParameterRasterDestination *>( def.get() ) );
6943
6944 QString pythonCode = def->asPythonString();
6945 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRasterDestination('non_optional', '', createByDefault=True, defaultValue=None)" ) );
6946 QString code = def->asScriptCode();
6947 QCOMPARE( code, QStringLiteral( "##non_optional=rasterDestination" ) );
6948 std::unique_ptr< QgsProcessingParameterRasterDestination > fromCode( dynamic_cast< QgsProcessingParameterRasterDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6949 QVERIFY( fromCode.get() );
6950 QCOMPARE( fromCode->name(), def->name() );
6951 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
6952 QCOMPARE( fromCode->flags(), def->flags() );
6953 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6954
6955 // optional
6956 def.reset( new QgsProcessingParameterRasterDestination( "optional", QString(), QString( "default.tif" ), true ) );
6957 QVERIFY( !def->checkValueIsAcceptable( false ) );
6958 QVERIFY( !def->checkValueIsAcceptable( true ) );
6959 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
6960 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
6961 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.tif" ) );
6962 QVERIFY( def->checkValueIsAcceptable( "" ) );
6963 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
6964 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
6965
6966 params.insert( "optional", QVariant() );
6967 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context ), QStringLiteral( "default.tif" ) );
6968
6969 pythonCode = def->asPythonString();
6970 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterRasterDestination('optional', '', optional=True, createByDefault=True, defaultValue='default.tif')" ) );
6971 code = def->asScriptCode();
6972 QCOMPARE( code, QStringLiteral( "##optional=optional rasterDestination default.tif" ) );
6973 fromCode.reset( dynamic_cast< QgsProcessingParameterRasterDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
6974 QVERIFY( fromCode.get() );
6975 QCOMPARE( fromCode->name(), def->name() );
6976 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
6977 QCOMPARE( fromCode->flags(), def->flags() );
6978 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
6979
6980 const DummyProvider3 provider;
6981 QString error;
6982 QVERIFY( !provider.isSupportedOutputValue( QVariant(), def.get(), context, error ) );
6983 QVERIFY( !provider.isSupportedOutputValue( QString(), def.get(), context, error ) );
6984 QVERIFY( !provider.isSupportedOutputValue( "d:/test.tif", def.get(), context, error ) );
6985 QVERIFY( !provider.isSupportedOutputValue( "d:/test.TIF", def.get(), context, error ) );
6986 QVERIFY( !provider.isSupportedOutputValue( QgsProcessingOutputLayerDefinition( "d:/test.tif" ), def.get(), context, error ) );
6987 QVERIFY( provider.isSupportedOutputValue( "d:/test.mig", def.get(), context, error ) );
6988 QVERIFY( provider.isSupportedOutputValue( "d:/test.MIG", def.get(), context, error ) );
6989 QVERIFY( provider.isSupportedOutputValue( QgsProcessingOutputLayerDefinition( "d:/test.MIG" ), def.get(), context, error ) );
6990
6991 // test layers to load on completion
6992 def.reset( new QgsProcessingParameterRasterDestination( "x", QStringLiteral( "desc" ), QStringLiteral( "default.tif" ), true ) );
6993 QgsProcessingOutputLayerDefinition fs = QgsProcessingOutputLayerDefinition( QStringLiteral( "test.tif" ) );
6994 fs.destinationProject = &p;
6995 params.insert( QStringLiteral( "x" ), QVariant::fromValue( fs ) );
6996 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context ), QStringLiteral( "test.tif" ) );
6997
6998 // make sure layer was automatically added to list to load on completion
6999 QCOMPARE( context.layersToLoadOnCompletion().size(), 1 );
7000 QCOMPARE( context.layersToLoadOnCompletion().keys().at( 0 ), QStringLiteral( "test.tif" ) );
7001 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "desc" ) );
7002 QCOMPARE( context.layersToLoadOnCompletion().values().at( 0 ).layerTypeHint, QgsProcessingUtils::LayerHint::Raster );
7003
7004 // with name overloading
7005 QgsProcessingContext context2;
7006 fs = QgsProcessingOutputLayerDefinition( QStringLiteral( "test.tif" ) );
7007 fs.destinationProject = &p;
7008 fs.destinationName = QStringLiteral( "my_dest" );
7009 params.insert( QStringLiteral( "x" ), QVariant::fromValue( fs ) );
7010 QCOMPARE( QgsProcessingParameters::parameterAsOutputLayer( def.get(), params, context2 ), QStringLiteral( "test.tif" ) );
7011 QCOMPARE( context2.layersToLoadOnCompletion().size(), 1 );
7012 QCOMPARE( context2.layersToLoadOnCompletion().keys().at( 0 ), QStringLiteral( "test.tif" ) );
7013 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).name, QStringLiteral( "my_dest" ) );
7014 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).outputName, QStringLiteral( "x" ) );
7015 QCOMPARE( context2.layersToLoadOnCompletion().values().at( 0 ).layerTypeHint, QgsProcessingUtils::LayerHint::Raster );
7016 }
7017
parameterFileOut()7018 void TestQgsProcessing::parameterFileOut()
7019 {
7020 // setup a context
7021 QgsProject p;
7022 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
7023 QgsProcessingContext context;
7024 context.setProject( &p );
7025
7026 // not optional!
7027 std::unique_ptr< QgsProcessingParameterFileDestination > def( new QgsProcessingParameterFileDestination( "non_optional", QString(), QStringLiteral( "BMP files (*.bmp)" ), QVariant(), false ) );
7028 QCOMPARE( def->fileFilter(), QStringLiteral( "BMP files (*.bmp)" ) );
7029 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "bmp" ) );
7030 QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".bmp" ) ) );
7031 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
7032 def->setFileFilter( QStringLiteral( "PCX files (*.pcx)" ) );
7033 QCOMPARE( def->fileFilter(), QStringLiteral( "PCX files (*.pcx)" ) );
7034 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "pcx" ) );
7035 def->setFileFilter( QStringLiteral( "PCX files (*.pcx *.picx)" ) );
7036 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "pcx" ) );
7037 def->setFileFilter( QStringLiteral( "PCX files (*.pcx *.picx);;BMP files (*.bmp)" ) );
7038 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "pcx" ) );
7039 QCOMPARE( def->createFileFilter(), QStringLiteral( "PCX files (*.pcx *.picx);;BMP files (*.bmp);;All files (*.*)" ) );
7040
7041 def->setFileFilter( QString() );
7042 QCOMPARE( def->defaultFileExtension(), QStringLiteral( "file" ) );
7043 QVERIFY( def->generateTemporaryDestination().endsWith( QLatin1String( ".file" ) ) );
7044 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
7045
7046 QVERIFY( !def->checkValueIsAcceptable( false ) );
7047 QVERIFY( !def->checkValueIsAcceptable( true ) );
7048 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
7049 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
7050 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7051 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7052 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
7053 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "" ) ) );
7054 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
7055 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
7056
7057 // should be OK with or without context - it's an output file!
7058 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.txt" ) );
7059 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.txt", &context ) );
7060
7061 QCOMPARE( def->createFileFilter(), QStringLiteral( "All files (*.*)" ) );
7062
7063 QVariantMap params;
7064 params.insert( "non_optional", "test.txt" );
7065 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "test.txt" ) );
7066 params.insert( "non_optional", QgsProcessingOutputLayerDefinition( "test.txt" ) );
7067 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "test.txt" ) );
7068
7069 params.insert( QStringLiteral( "non_optional" ), QgsProcessing::TEMPORARY_OUTPUT );
7070 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ).right( 18 ), QStringLiteral( "/non_optional.file" ) );
7071
7072 QgsProcessingOutputLayerDefinition fs;
7073 fs.sink = QgsProperty::fromValue( QgsProcessing::TEMPORARY_OUTPUT );
7074 params.insert( QStringLiteral( "non_optional" ), QVariant::fromValue( fs ) );
7075 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ).right( 18 ), QStringLiteral( "/non_optional.file" ) );
7076
7077 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7078 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7079 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( "abc" ) ), context ), QStringLiteral( "'abc'" ) );
7080 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( "abc" ) ) ), context ), QStringLiteral( "'abc'" ) );
7081 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( "\"abc\" || \"def\"" ) ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"abc\" || \"def\"')" ) );
7082 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
7083 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
7084 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
7085
7086 const QVariantMap map = def->toVariantMap();
7087 QgsProcessingParameterFileDestination fromMap( "x" );
7088 QVERIFY( fromMap.fromVariantMap( map ) );
7089 QCOMPARE( fromMap.name(), def->name() );
7090 QCOMPARE( fromMap.description(), def->description() );
7091 QCOMPARE( fromMap.flags(), def->flags() );
7092 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7093 QCOMPARE( fromMap.fileFilter(), def->fileFilter() );
7094 QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
7095 def.reset( dynamic_cast< QgsProcessingParameterFileDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7096 QVERIFY( dynamic_cast< QgsProcessingParameterFileDestination *>( def.get() ) );
7097
7098 QString pythonCode = def->asPythonString();
7099 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFileDestination('non_optional', '', fileFilter='', createByDefault=True, defaultValue=None)" ) );
7100 QString code = def->asScriptCode();
7101 QCOMPARE( code, QStringLiteral( "##non_optional=fileDestination" ) );
7102 std::unique_ptr< QgsProcessingParameterFileDestination > fromCode( dynamic_cast< QgsProcessingParameterFileDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7103 QVERIFY( fromCode.get() );
7104 QCOMPARE( fromCode->name(), def->name() );
7105 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7106 QCOMPARE( fromCode->flags(), def->flags() );
7107 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7108
7109 // optional
7110 def.reset( new QgsProcessingParameterFileDestination( "optional", QString(), QString(), QString( "default.txt" ), true ) );
7111 QVERIFY( !def->checkValueIsAcceptable( false ) );
7112 QVERIFY( !def->checkValueIsAcceptable( true ) );
7113 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
7114 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
7115 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.txt" ) );
7116 QVERIFY( def->checkValueIsAcceptable( "" ) );
7117 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7118 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "layer1231123" ) ) );
7119
7120 params.insert( "optional", QVariant() );
7121 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "default.txt" ) );
7122
7123 pythonCode = def->asPythonString();
7124 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFileDestination('optional', '', optional=True, fileFilter='All files (*.*)', createByDefault=True, defaultValue='default.txt')" ) );
7125 code = def->asScriptCode();
7126 QCOMPARE( code, QStringLiteral( "##optional=optional fileDestination default.txt" ) );
7127 fromCode.reset( dynamic_cast< QgsProcessingParameterFileDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7128 QVERIFY( fromCode.get() );
7129 QCOMPARE( fromCode->name(), def->name() );
7130 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7131 QCOMPARE( fromCode->flags(), def->flags() );
7132 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7133
7134 // outputs definition test
7135 def.reset( new QgsProcessingParameterFileDestination( "html", QString(), QString( "HTML files" ), QString(), false ) );
7136 std::unique_ptr< QgsProcessingOutputDefinition > outputDef( def->toOutputDefinition() );
7137 QVERIFY( dynamic_cast< QgsProcessingOutputHtml *>( outputDef.get() ) );
7138 def.reset( new QgsProcessingParameterFileDestination( "html", QString(), QString( "Text files (*.htm)" ), QString(), false ) );
7139 outputDef.reset( def->toOutputDefinition() );
7140 QVERIFY( dynamic_cast< QgsProcessingOutputHtml *>( outputDef.get() ) );
7141 def.reset( new QgsProcessingParameterFileDestination( "file", QString(), QString( "Text files (*.txt)" ), QString(), false ) );
7142 outputDef.reset( def->toOutputDefinition() );
7143 QVERIFY( dynamic_cast< QgsProcessingOutputFile *>( outputDef.get() ) );
7144 def.reset( new QgsProcessingParameterFileDestination( "file", QString(), QString(), QString(), false ) );
7145 outputDef.reset( def->toOutputDefinition() );
7146 QVERIFY( dynamic_cast< QgsProcessingOutputFile *>( outputDef.get() ) );
7147 }
7148
parameterFolderOut()7149 void TestQgsProcessing::parameterFolderOut()
7150 {
7151 // setup a context
7152 QgsProject p;
7153 QgsProcessingContext context;
7154 context.setProject( &p );
7155
7156 // not optional!
7157 std::unique_ptr< QgsProcessingParameterFolderDestination > def( new QgsProcessingParameterFolderDestination( "non_optional", QString(), QVariant(), false ) );
7158
7159 QVERIFY( !def->checkValueIsAcceptable( false ) );
7160 QVERIFY( !def->checkValueIsAcceptable( true ) );
7161 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
7162 QVERIFY( def->checkValueIsAcceptable( "asdasd" ) );
7163 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7164 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7165 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "asdasdas" ) ) ) );
7166 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
7167
7168 // should be OK with or without context - it's an output folder!
7169 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/" ) );
7170 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/", &context ) );
7171
7172 // check that temporary destination does not have dot at the end when there is no extension
7173 QVERIFY( !def->generateTemporaryDestination().endsWith( QLatin1Char( '.' ) ) );
7174 QVERIFY( def->generateTemporaryDestination().startsWith( QgsProcessingUtils::tempFolder() ) );
7175
7176 QVariantMap params;
7177 params.insert( "non_optional", "c:/mine" );
7178 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "c:/mine" ) );
7179
7180 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7181 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7182 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
7183 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
7184 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\'" ) );
7185
7186 const QVariantMap map = def->toVariantMap();
7187 QgsProcessingParameterFolderDestination fromMap( "x" );
7188 QVERIFY( fromMap.fromVariantMap( map ) );
7189 QCOMPARE( fromMap.name(), def->name() );
7190 QCOMPARE( fromMap.description(), def->description() );
7191 QCOMPARE( fromMap.flags(), def->flags() );
7192 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7193 QCOMPARE( fromMap.supportsNonFileBasedOutput(), def->supportsNonFileBasedOutput() );
7194 def.reset( dynamic_cast< QgsProcessingParameterFolderDestination *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7195 QVERIFY( dynamic_cast< QgsProcessingParameterFolderDestination *>( def.get() ) );
7196
7197 QString pythonCode = def->asPythonString();
7198 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFolderDestination('non_optional', '', createByDefault=True, defaultValue=None)" ) );
7199 QString code = def->asScriptCode();
7200 QCOMPARE( code, QStringLiteral( "##non_optional=folderDestination" ) );
7201 std::unique_ptr< QgsProcessingParameterFolderDestination > fromCode( dynamic_cast< QgsProcessingParameterFolderDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7202 QVERIFY( fromCode.get() );
7203 QCOMPARE( fromCode->name(), def->name() );
7204 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7205 QCOMPARE( fromCode->flags(), def->flags() );
7206 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7207
7208 // optional
7209 def.reset( new QgsProcessingParameterFolderDestination( "optional", QString(), QString( "c:/junk" ), true ) );
7210 QVERIFY( !def->checkValueIsAcceptable( false ) );
7211 QVERIFY( !def->checkValueIsAcceptable( true ) );
7212 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
7213 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
7214 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/" ) );
7215 QVERIFY( def->checkValueIsAcceptable( "" ) );
7216 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7217
7218 params.insert( "optional", QVariant() );
7219 QCOMPARE( QgsProcessingParameters::parameterAsFileOutput( def.get(), params, context ), QStringLiteral( "c:/junk" ) );
7220
7221 pythonCode = def->asPythonString();
7222 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFolderDestination('optional', '', optional=True, createByDefault=True, defaultValue='c:/junk')" ) );
7223 code = def->asScriptCode();
7224 QCOMPARE( code, QStringLiteral( "##optional=optional folderDestination c:/junk" ) );
7225 fromCode.reset( dynamic_cast< QgsProcessingParameterFolderDestination * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7226 QVERIFY( fromCode.get() );
7227 QCOMPARE( fromCode->name(), def->name() );
7228 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7229 QCOMPARE( fromCode->flags(), def->flags() );
7230 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7231
7232 // temporary directory
7233 def.reset( new QgsProcessingParameterFolderDestination( "junkdir", QString(), QgsProcessing::TEMPORARY_OUTPUT ) );
7234 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ).right( 8 ), QStringLiteral( "/junkdir" ) );
7235 }
7236
parameterBand()7237 void TestQgsProcessing::parameterBand()
7238 {
7239 QgsProcessingContext context;
7240
7241 // not optional!
7242 std::unique_ptr< QgsProcessingParameterBand > def( new QgsProcessingParameterBand( "non_optional", QString(), QVariant(), QString(), false ) );
7243 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7244 QVERIFY( def->checkValueIsAcceptable( "1" ) );
7245 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7246 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7247
7248 // string representing a band
7249 QVariantMap params;
7250 params.insert( "non_optional", "1" );
7251 int band = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
7252 QCOMPARE( band, 1 );
7253
7254 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7255 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
7256
7257 QString pythonCode = def->asPythonString();
7258 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('non_optional', '', parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) );
7259 QString code = def->asScriptCode();
7260 QCOMPARE( code, QStringLiteral( "##non_optional=band" ) );
7261 std::unique_ptr< QgsProcessingParameterBand > fromCode( dynamic_cast< QgsProcessingParameterBand * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7262 QVERIFY( fromCode.get() );
7263 QCOMPARE( fromCode->name(), def->name() );
7264 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7265 QCOMPARE( fromCode->flags(), def->flags() );
7266 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7267 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
7268
7269 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
7270 def->setParentLayerParameterName( "my_parent" );
7271 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) );
7272
7273 pythonCode = def->asPythonString();
7274 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('non_optional', '', parentLayerParameterName='my_parent', allowMultiple=False, defaultValue=None)" ) );
7275 code = def->asScriptCode();
7276 fromCode.reset( dynamic_cast< QgsProcessingParameterBand * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7277 QVERIFY( fromCode.get() );
7278 QCOMPARE( fromCode->name(), def->name() );
7279 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7280 QCOMPARE( fromCode->flags(), def->flags() );
7281 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7282 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
7283
7284 // multiple
7285 def.reset( new QgsProcessingParameterBand( "non_optional", QString(), QVariant(), QString(), false, true ) );
7286 QVERIFY( def->checkValueIsAcceptable( QStringList() << "1" << "2" ) );
7287 QVERIFY( def->checkValueIsAcceptable( QVariantList() << 1 << 2 ) );
7288 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7289 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7290 QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
7291 QVERIFY( !def->checkValueIsAcceptable( QVariantList() ) );
7292
7293 params.insert( "non_optional", QString( "1;2" ) );
7294 QList<int> bands = QgsProcessingParameters::parameterAsInts( def.get(), params, context );
7295 QCOMPARE( bands, QList<int>() << 1 << 2 );
7296 params.insert( "non_optional", QVariantList() << 1 << 2 );
7297 bands = QgsProcessingParameters::parameterAsInts( def.get(), params, context );
7298 QCOMPARE( bands, QList<int>() << 1 << 2 );
7299
7300 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7301 QCOMPARE( def->valueAsPythonString( QStringList() << "1" << "2", context ), QStringLiteral( "[1,2]" ) );
7302 QCOMPARE( def->valueAsPythonString( QVariantList() << 1 << 2, context ), QStringLiteral( "[1,2]" ) );
7303
7304 const QVariantMap map = def->toVariantMap();
7305 QgsProcessingParameterBand fromMap( "x" );
7306 QVERIFY( fromMap.fromVariantMap( map ) );
7307 QCOMPARE( fromMap.name(), def->name() );
7308 QCOMPARE( fromMap.description(), def->description() );
7309 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7310 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
7311 QCOMPARE( fromMap.allowMultiple(), def->allowMultiple() );
7312 def.reset( dynamic_cast< QgsProcessingParameterBand *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7313 QVERIFY( dynamic_cast< QgsProcessingParameterBand *>( def.get() ) );
7314
7315 pythonCode = def->asPythonString();
7316 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('non_optional', '', parentLayerParameterName='', allowMultiple=True, defaultValue=None)" ) );
7317 code = def->asScriptCode();
7318 fromCode.reset( dynamic_cast< QgsProcessingParameterBand * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7319 QVERIFY( fromCode.get() );
7320 QCOMPARE( fromCode->name(), def->name() );
7321 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7322 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7323 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
7324 QCOMPARE( fromCode->allowMultiple(), def->allowMultiple() );
7325
7326 // optional
7327 def.reset( new QgsProcessingParameterBand( "optional", QString(), 1, QString(), true ) );
7328 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7329 QVERIFY( def->checkValueIsAcceptable( "1" ) );
7330 QVERIFY( def->checkValueIsAcceptable( "" ) );
7331 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7332
7333 params.insert( "optional", QVariant() );
7334 band = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
7335 QCOMPARE( band, 1 );
7336
7337 // optional, no default
7338 def.reset( new QgsProcessingParameterBand( "optional", QString(), QVariant(), QString(), true ) );
7339 params.insert( "optional", QVariant() );
7340 band = QgsProcessingParameters::parameterAsInt( def.get(), params, context );
7341 QCOMPARE( band, 0 );
7342
7343 pythonCode = def->asPythonString();
7344 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterBand('optional', '', optional=True, parentLayerParameterName='', allowMultiple=False, defaultValue=None)" ) );
7345 code = def->asScriptCode();
7346 QCOMPARE( code, QStringLiteral( "##optional=optional band" ) );
7347 fromCode.reset( dynamic_cast< QgsProcessingParameterBand * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7348 QVERIFY( fromCode.get() );
7349 QCOMPARE( fromCode->name(), def->name() );
7350 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7351 QCOMPARE( fromCode->flags(), def->flags() );
7352 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7353 QCOMPARE( fromCode->parentLayerParameterName(), def->parentLayerParameterName() );
7354 }
7355
parameterLayout()7356 void TestQgsProcessing::parameterLayout()
7357 {
7358 QgsProcessingContext context;
7359
7360 QgsProject p;
7361 QgsPrintLayout *l = new QgsPrintLayout( &p );
7362 l->setName( "l1" );
7363 QgsPrintLayout *l2 = new QgsPrintLayout( &p );
7364 l2->setName( "l2" );
7365 p.layoutManager()->addLayout( l );
7366 p.layoutManager()->addLayout( l2 );
7367
7368 // not optional!
7369 std::unique_ptr< QgsProcessingParameterLayout > def( new QgsProcessingParameterLayout( "non_optional", QString(), QString(), false ) );
7370 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7371 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7372 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7373 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7374
7375 // string
7376 QVariantMap params;
7377 params.insert( "non_optional", QString( "abcdef" ) );
7378 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
7379 QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) );
7380 params.insert( "non_optional", QString( "l1" ) );
7381 QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) );
7382 context.setProject( &p );
7383 params.insert( "non_optional", QString( "abcdef" ) );
7384 QVERIFY( !QgsProcessingParameters::parameterAsLayout( def.get(), params, context ) );
7385 params.insert( "non_optional", QString( "l1" ) );
7386 QCOMPARE( QgsProcessingParameters::parameterAsLayout( def.get(), params, context ), l );
7387 params.insert( "non_optional", QString( "l2" ) );
7388 QCOMPARE( QgsProcessingParameters::parameterAsLayout( def.get(), params, context ), l2 );
7389
7390 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7391 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
7392 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7393 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
7394 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
7395 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
7396
7397 QString pythonCode = def->asPythonString();
7398 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayout('non_optional', '', defaultValue=None)" ) );
7399
7400 QString code = def->asScriptCode();
7401 QCOMPARE( code, QStringLiteral( "##non_optional=layout" ) );
7402 std::unique_ptr< QgsProcessingParameterLayout > fromCode( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7403 QVERIFY( fromCode.get() );
7404 QCOMPARE( fromCode->name(), def->name() );
7405 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7406 QCOMPARE( fromCode->flags(), def->flags() );
7407 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7408
7409 const QVariantMap map = def->toVariantMap();
7410 QgsProcessingParameterLayout fromMap( "x" );
7411 QVERIFY( fromMap.fromVariantMap( map ) );
7412 QCOMPARE( fromMap.name(), def->name() );
7413 QCOMPARE( fromMap.description(), def->description() );
7414 QCOMPARE( fromMap.flags(), def->flags() );
7415 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7416 def.reset( dynamic_cast< QgsProcessingParameterLayout *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7417 QVERIFY( dynamic_cast< QgsProcessingParameterLayout *>( def.get() ) );
7418
7419 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=layout None" ) ) ) );
7420 QVERIFY( fromCode.get() );
7421 QCOMPARE( fromCode->name(), def->name() );
7422 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7423 QCOMPARE( fromCode->flags(), def->flags() );
7424 QVERIFY( !fromCode->defaultValue().isValid() );
7425
7426 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=layout it's mario" ) ) ) );
7427 QVERIFY( fromCode.get() );
7428 QCOMPARE( fromCode->name(), def->name() );
7429 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7430 QCOMPARE( fromCode->flags(), def->flags() );
7431 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
7432
7433 def->setDefaultValue( QStringLiteral( "it's mario" ) );
7434 pythonCode = def->asPythonString();
7435 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayout('non_optional', '', defaultValue=\"it's mario\")" ) );
7436
7437 code = def->asScriptCode();
7438 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7439 QVERIFY( fromCode.get() );
7440 QCOMPARE( fromCode->name(), def->name() );
7441 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7442 QCOMPARE( fromCode->flags(), def->flags() );
7443 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7444
7445 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=layout 'my val'" ) ) ) );
7446 QVERIFY( fromCode.get() );
7447 QCOMPARE( fromCode->name(), def->name() );
7448 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7449 QCOMPARE( fromCode->flags(), def->flags() );
7450 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7451
7452 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=layout \"my val\"" ) ) ) );
7453 QVERIFY( fromCode.get() );
7454 QCOMPARE( fromCode->name(), def->name() );
7455 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7456 QCOMPARE( fromCode->flags(), def->flags() );
7457 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7458
7459 // optional
7460 def.reset( new QgsProcessingParameterLayout( "optional", QString(), QString( "default" ), true ) );
7461 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7462 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7463 QVERIFY( def->checkValueIsAcceptable( "" ) );
7464 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7465
7466 params.insert( "optional", QVariant() );
7467 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
7468 params.insert( "optional", QString() ); //empty string should not result in default value
7469 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
7470
7471 pythonCode = def->asPythonString();
7472 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayout('optional', '', optional=True, defaultValue='default')" ) );
7473 code = def->asScriptCode();
7474 QCOMPARE( code, QStringLiteral( "##optional=optional layout default" ) );
7475 fromCode.reset( dynamic_cast< QgsProcessingParameterLayout * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7476 QVERIFY( fromCode.get() );
7477 QCOMPARE( fromCode->name(), def->name() );
7478 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7479 QCOMPARE( fromCode->flags(), def->flags() );
7480 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7481
7482 // not optional, valid default!
7483 def.reset( new QgsProcessingParameterLayout( "non_optional", QString(), QString( "def" ), false ) );
7484 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7485 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7486 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7487 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
7488 }
7489
parameterLayoutItem()7490 void TestQgsProcessing::parameterLayoutItem()
7491 {
7492 QgsProcessingContext context;
7493
7494 QgsProject p;
7495 QgsPrintLayout *l = new QgsPrintLayout( &p );
7496 l->setName( "l1" );
7497 QgsLayoutItemLabel *label1 = new QgsLayoutItemLabel( l );
7498 label1->setId( "a" );
7499 l->addLayoutItem( label1 );
7500 QgsLayoutItemLabel *label2 = new QgsLayoutItemLabel( l );
7501 label2->setId( "b" );
7502 l->addLayoutItem( label2 );
7503
7504 QgsPrintLayout *l2 = new QgsPrintLayout( &p );
7505
7506 // not optional!
7507 std::unique_ptr< QgsProcessingParameterLayoutItem > def( new QgsProcessingParameterLayoutItem( "non_optional", QString(), QVariant(), QString(), -1, false ) );
7508 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7509 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7510 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7511 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7512
7513 // string
7514 QVariantMap params;
7515 params.insert( "non_optional", QString( "aaaa" ) );
7516 QString f = QgsProcessingParameters::parameterAsString( def.get(), params, context );
7517 QCOMPARE( f, QStringLiteral( "aaaa" ) );
7518
7519 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) );
7520 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ) );
7521 params.insert( "non_optional", label1->uuid() );
7522 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) );
7523 params.insert( "non_optional", QString( "abcdef" ) );
7524 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) );
7525 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ) );
7526 params.insert( "non_optional", label1->uuid() );
7527 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, nullptr ) );
7528 QVERIFY( !QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l2 ) );
7529 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 );
7530 params.insert( "non_optional", label1->id() );
7531 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 );
7532 params.insert( "non_optional", label2->uuid() );
7533 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 );
7534 params.insert( "non_optional", label2->id() );
7535 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 );
7536 // UUID matching must take precedence
7537 label1->setId( label2->uuid() );
7538 params.insert( "non_optional", label2->uuid() );
7539 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label2 );
7540 label2->setId( label1->uuid() );
7541 params.insert( "non_optional", label1->uuid() );
7542 QCOMPARE( QgsProcessingParameters::parameterAsLayoutItem( def.get(), params, context, l ), label1 );
7543
7544 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7545 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7546 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
7547 QCOMPARE( def->valueAsPythonString( "probably\'invalid\"item", context ), QStringLiteral( "'probably\\'invalid\"item'" ) );
7548
7549 QString pythonCode = def->asPythonString();
7550 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', parentLayoutParameterName='', defaultValue=None)" ) );
7551
7552 QString code = def->asScriptCode();
7553 QCOMPARE( code, QStringLiteral( "##non_optional=layoutitem" ) );
7554 std::unique_ptr< QgsProcessingParameterLayoutItem > fromCode( dynamic_cast< QgsProcessingParameterLayoutItem * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7555 QVERIFY( fromCode.get() );
7556 QCOMPARE( fromCode->name(), def->name() );
7557 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7558 QCOMPARE( fromCode->flags(), def->flags() );
7559 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7560 QCOMPARE( fromCode->parentLayoutParameterName(), def->parentLayoutParameterName() );
7561 QCOMPARE( fromCode->itemType(), def->itemType() );
7562
7563 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
7564 def->setParentLayoutParameterName( "my_parent" );
7565 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) );
7566
7567 pythonCode = def->asPythonString();
7568 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', parentLayoutParameterName='my_parent', defaultValue=None)" ) );
7569
7570 code = def->asScriptCode();
7571 fromCode.reset( dynamic_cast< QgsProcessingParameterLayoutItem * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7572 QVERIFY( fromCode.get() );
7573 QCOMPARE( fromCode->name(), def->name() );
7574 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7575 QCOMPARE( fromCode->flags(), def->flags() );
7576 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7577 QCOMPARE( fromCode->parentLayoutParameterName(), def->parentLayoutParameterName() );
7578 QCOMPARE( fromCode->itemType(), def->itemType() );
7579
7580 def->setItemType( 100 );
7581 pythonCode = def->asPythonString();
7582 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', itemType=100, parentLayoutParameterName='my_parent', defaultValue=None)" ) );
7583 code = def->asScriptCode();
7584 fromCode.reset( dynamic_cast< QgsProcessingParameterLayoutItem * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7585 QVERIFY( fromCode.get() );
7586 QCOMPARE( fromCode->name(), def->name() );
7587 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7588 QCOMPARE( fromCode->flags(), def->flags() );
7589 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7590 QCOMPARE( fromCode->parentLayoutParameterName(), def->parentLayoutParameterName() );
7591 QCOMPARE( fromCode->itemType(), def->itemType() );
7592
7593 def->setItemType( 101 );
7594 pythonCode = def->asPythonString();
7595 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('non_optional', '', itemType=101, parentLayoutParameterName='my_parent', defaultValue=None)" ) );
7596 code = def->asScriptCode();
7597 fromCode.reset( dynamic_cast< QgsProcessingParameterLayoutItem * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7598 QVERIFY( fromCode.get() );
7599 QCOMPARE( fromCode->name(), def->name() );
7600 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7601 QCOMPARE( fromCode->flags(), def->flags() );
7602 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7603 QCOMPARE( fromCode->parentLayoutParameterName(), def->parentLayoutParameterName() );
7604 QCOMPARE( fromCode->itemType(), def->itemType() );
7605
7606 // optional
7607 def.reset( new QgsProcessingParameterLayoutItem( "optional", QString(), QString( "def" ), QString(), -1, true ) );
7608 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7609 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7610 QVERIFY( def->checkValueIsAcceptable( "" ) );
7611 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7612
7613 params.insert( "optional", QVariant() );
7614 f = QgsProcessingParameters::parameterAsString( def.get(), params, context );
7615 QCOMPARE( f, QStringLiteral( "def" ) );
7616
7617 // optional, no default
7618 def.reset( new QgsProcessingParameterLayoutItem( "optional", QString(), QVariant(), QString(), -1, true ) );
7619 params.insert( "optional", QVariant() );
7620 f = QgsProcessingParameters::parameterAsString( def.get(), params, context );
7621 QVERIFY( f.isEmpty() );
7622
7623 pythonCode = def->asPythonString();
7624 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterLayoutItem('optional', '', optional=True, parentLayoutParameterName='', defaultValue=None)" ) );
7625 code = def->asScriptCode();
7626 QCOMPARE( code, QStringLiteral( "##optional=optional layoutitem" ) );
7627 fromCode.reset( dynamic_cast< QgsProcessingParameterLayoutItem * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7628 QVERIFY( fromCode.get() );
7629 QCOMPARE( fromCode->name(), def->name() );
7630 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7631 QCOMPARE( fromCode->flags(), def->flags() );
7632 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7633 QCOMPARE( fromCode->parentLayoutParameterName(), def->parentLayoutParameterName() );
7634 QCOMPARE( fromCode->itemType(), def->itemType() );
7635
7636 }
7637
parameterColor()7638 void TestQgsProcessing::parameterColor()
7639 {
7640 QgsProcessingContext context;
7641
7642 // not optional!
7643 std::unique_ptr< QgsProcessingParameterColor > def( new QgsProcessingParameterColor( "non_optional", QString(), QString(), true, false ) );
7644 QVERIFY( def->opacityEnabled() );
7645 def->setOpacityEnabled( false );
7646 QVERIFY( !def->opacityEnabled() );
7647 def->setOpacityEnabled( true );
7648
7649 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
7650 QVERIFY( def->checkValueIsAcceptable( "#ff0000" ) );
7651 QVERIFY( !def->checkValueIsAcceptable( "bbbbbbbbbbbbbbbbbbbb" ) );
7652 QVERIFY( def->checkValueIsAcceptable( QColor( 255, 0, 0 ) ) );
7653 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7654 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7655
7656 // string
7657 QVariantMap params;
7658 params.insert( "non_optional", QString( "xxx" ) );
7659 QVERIFY( !QgsProcessingParameters::parameterAsColor( def.get(), params, context ).isValid() );
7660 params.insert( "non_optional", QString( "#ff0000" ) );
7661 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ff0000" ) );
7662 params.insert( "non_optional", QString( "rgba(255,0,0,0.1" ) );
7663 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ff0000" ) );
7664 params.insert( "non_optional", QColor( 255, 255, 0 ) );
7665 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ffff00" ) );
7666 params.insert( "non_optional", QColor( 255, 255, 0, 100 ) );
7667 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ), QColor( 255, 255, 0, 100 ) );
7668
7669 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7670 QCOMPARE( def->valueAsPythonString( QStringLiteral( "#ff0000" ), context ), QStringLiteral( "QColor(255, 0, 0)" ) );
7671 QCOMPARE( def->valueAsPythonString( QColor(), context ), QStringLiteral( "QColor()" ) );
7672 QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0 ), context ), QStringLiteral( "QColor(255, 0, 0)" ) );
7673 QCOMPARE( def->valueAsPythonString( QColor( 255, 0, 0, 100 ), context ), QStringLiteral( "QColor(255, 0, 0, 100)" ) );
7674
7675 QString pythonCode = def->asPythonString();
7676 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=None)" ) );
7677
7678 QString code = def->asScriptCode();
7679 QCOMPARE( code, QStringLiteral( "##non_optional=color withopacity" ) );
7680 std::unique_ptr< QgsProcessingParameterColor > fromCode( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7681 QVERIFY( fromCode.get() );
7682 QCOMPARE( fromCode->name(), def->name() );
7683 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7684 QCOMPARE( fromCode->flags(), def->flags() );
7685 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7686 QVERIFY( fromCode->opacityEnabled() );
7687
7688 const QVariantMap map = def->toVariantMap();
7689 QgsProcessingParameterColor fromMap( "x" );
7690 QVERIFY( fromMap.fromVariantMap( map ) );
7691 QCOMPARE( fromMap.name(), def->name() );
7692 QCOMPARE( fromMap.description(), def->description() );
7693 QCOMPARE( fromMap.flags(), def->flags() );
7694 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7695 QVERIFY( fromMap.opacityEnabled() );
7696 def.reset( dynamic_cast< QgsProcessingParameterColor *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7697 QVERIFY( dynamic_cast< QgsProcessingParameterColor *>( def.get() ) );
7698
7699 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color withopacity None" ) ) ) );
7700 QVERIFY( fromCode.get() );
7701 QCOMPARE( fromCode->name(), def->name() );
7702 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7703 QCOMPARE( fromCode->flags(), def->flags() );
7704 QVERIFY( !fromCode->defaultValue().isValid() );
7705 QVERIFY( fromCode->opacityEnabled() );
7706
7707 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color #aabbcc" ) ) ) );
7708 QVERIFY( fromCode.get() );
7709 QCOMPARE( fromCode->name(), def->name() );
7710 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7711 QCOMPARE( fromCode->flags(), def->flags() );
7712 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "#aabbcc" ) );
7713 QVERIFY( !fromCode->opacityEnabled() );
7714
7715 def->setDefaultValue( QColor( 10, 20, 30 ) );
7716 pythonCode = def->asPythonString();
7717 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=True, defaultValue=QColor(10, 20, 30))" ) );
7718
7719 def->setOpacityEnabled( false );
7720 pythonCode = def->asPythonString();
7721 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('non_optional', '', opacityEnabled=False, defaultValue=QColor(10, 20, 30))" ) );
7722 def->setOpacityEnabled( true );
7723
7724 code = def->asScriptCode();
7725 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7726 QVERIFY( fromCode.get() );
7727 QCOMPARE( fromCode->name(), def->name() );
7728 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7729 QCOMPARE( fromCode->flags(), def->flags() );
7730 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7731 QVERIFY( fromCode->opacityEnabled() );
7732
7733 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color 'my val'" ) ) ) );
7734 QVERIFY( fromCode.get() );
7735 QCOMPARE( fromCode->name(), def->name() );
7736 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7737 QCOMPARE( fromCode->flags(), def->flags() );
7738 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7739 QVERIFY( !fromCode->opacityEnabled() );
7740
7741 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=color withopacity \"my val\"" ) ) ) );
7742 QVERIFY( fromCode.get() );
7743 QCOMPARE( fromCode->name(), def->name() );
7744 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7745 QCOMPARE( fromCode->flags(), def->flags() );
7746 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7747 QVERIFY( fromCode->opacityEnabled() );
7748
7749 // optional
7750 def.reset( new QgsProcessingParameterColor( "optional", QString(), QString( "#ff00ff" ), false, true ) );
7751 QVERIFY( def->checkValueIsAcceptable( "#ff0000" ) );
7752 QVERIFY( def->checkValueIsAcceptable( QColor( 255, 0, 0 ) ) );
7753 QVERIFY( def->checkValueIsAcceptable( "" ) );
7754 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7755 QVERIFY( !def->opacityEnabled() );
7756
7757 params.insert( "optional", QVariant() );
7758 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ).name(), QString( "#ff00ff" ) );
7759 params.insert( "optional", QColor() ); //invalid color should not result in default value
7760 QVERIFY( !QgsProcessingParameters::parameterAsColor( def.get(), params, context ).isValid() );
7761 params.insert( "optional", QString() ); //empty string should not result in default value
7762 QVERIFY( !QgsProcessingParameters::parameterAsColor( def.get(), params, context ).isValid() );
7763
7764 // not opacity enabled, should be stripped off
7765 params.insert( "optional", QColor( 255, 255, 0, 100 ) );
7766 QCOMPARE( QgsProcessingParameters::parameterAsColor( def.get(), params, context ), QColor( 255, 255, 0 ) );
7767
7768 pythonCode = def->asPythonString();
7769 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterColor('optional', '', optional=True, opacityEnabled=False, defaultValue=QColor(255, 0, 255))" ) );
7770 code = def->asScriptCode();
7771 QCOMPARE( code, QStringLiteral( "##optional=optional color #ff00ff" ) );
7772 fromCode.reset( dynamic_cast< QgsProcessingParameterColor * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7773 QVERIFY( fromCode.get() );
7774 QCOMPARE( fromCode->name(), def->name() );
7775 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7776 QCOMPARE( fromCode->flags(), def->flags() );
7777 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7778 QVERIFY( !fromCode->opacityEnabled() );
7779
7780 // not optional, valid default!
7781 def.reset( new QgsProcessingParameterColor( "non_optional", QString(), QString( "#ff00ff" ), true, false ) );
7782 QVERIFY( def->checkValueIsAcceptable( "#dddddd" ) );
7783 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7784 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
7785 }
7786
parameterCoordinateOperation()7787 void TestQgsProcessing::parameterCoordinateOperation()
7788 {
7789 QgsProcessingContext context;
7790
7791 // not optional!
7792 std::unique_ptr< QgsProcessingParameterCoordinateOperation > def( new QgsProcessingParameterCoordinateOperation( "non_optional", QString(), QString(), QString(), QString(), QVariant(), QVariant(), false ) );
7793 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7794 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7795 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7796 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7797 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
7798 def->setSourceCrsParameterName( QStringLiteral( "src" ) );
7799 QCOMPARE( def->sourceCrsParameterName(), QStringLiteral( "src" ) );
7800 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "src" ) );
7801 def->setDestinationCrsParameterName( QStringLiteral( "dest" ) );
7802 QCOMPARE( def->destinationCrsParameterName(), QStringLiteral( "dest" ) );
7803 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "src" ) << QStringLiteral( "dest" ) );
7804 def->setSourceCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:7855" ) ) );
7805 QCOMPARE( def->sourceCrs().value< QgsCoordinateReferenceSystem >().authid(), QStringLiteral( "EPSG:7855" ) );
7806 def->setDestinationCrs( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28355" ) ) );
7807 QCOMPARE( def->destinationCrs().value< QgsCoordinateReferenceSystem >().authid(), QStringLiteral( "EPSG:28355" ) );
7808
7809 // string value
7810 QVariantMap params;
7811 params.insert( "non_optional", QStringLiteral( "abcdef" ) );
7812 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
7813
7814 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7815 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
7816 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7817 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
7818 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
7819 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
7820 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
7821
7822 QString pythonCode = def->asPythonString();
7823 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCoordinateOperation('non_optional', '', sourceCrsParameterName='src', destinationCrsParameterName='dest', staticSourceCrs=QgsCoordinateReferenceSystem('EPSG:7855'), staticDestinationCrs=QgsCoordinateReferenceSystem('EPSG:28355'), defaultValue=None)" ) );
7824
7825 QString code = def->asScriptCode();
7826 QCOMPARE( code, QStringLiteral( "##non_optional=coordinateoperation" ) );
7827 std::unique_ptr< QgsProcessingParameterCoordinateOperation > fromCode( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7828 QVERIFY( fromCode.get() );
7829 QCOMPARE( fromCode->name(), def->name() );
7830 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7831 QCOMPARE( fromCode->flags(), def->flags() );
7832 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7833
7834 const QVariantMap map = def->toVariantMap();
7835 QgsProcessingParameterCoordinateOperation fromMap( "x" );
7836 QVERIFY( fromMap.fromVariantMap( map ) );
7837 QCOMPARE( fromMap.name(), def->name() );
7838 QCOMPARE( fromMap.description(), def->description() );
7839 QCOMPARE( fromMap.flags(), def->flags() );
7840 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7841 QCOMPARE( fromMap.sourceCrsParameterName(), def->sourceCrsParameterName() );
7842 QCOMPARE( fromMap.destinationCrsParameterName(), def->destinationCrsParameterName() );
7843 QCOMPARE( fromMap.sourceCrs().value< QgsCoordinateReferenceSystem >().authid(), def->sourceCrs().value< QgsCoordinateReferenceSystem >().authid() );
7844 QCOMPARE( fromMap.destinationCrs().value< QgsCoordinateReferenceSystem >().authid(), def->destinationCrs().value< QgsCoordinateReferenceSystem >().authid() );
7845 def.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7846 QVERIFY( dynamic_cast< QgsProcessingParameterCoordinateOperation *>( def.get() ) );
7847
7848 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=coordinateoperation None" ) ) ) );
7849 QVERIFY( fromCode.get() );
7850 QCOMPARE( fromCode->name(), def->name() );
7851 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7852 QCOMPARE( fromCode->flags(), def->flags() );
7853 QVERIFY( !fromCode->defaultValue().isValid() );
7854
7855 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=coordinateoperation it's mario" ) ) ) );
7856 QVERIFY( fromCode.get() );
7857 QCOMPARE( fromCode->name(), def->name() );
7858 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7859 QCOMPARE( fromCode->flags(), def->flags() );
7860 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
7861
7862 def->setDefaultValue( QStringLiteral( "it's mario" ) );
7863 pythonCode = def->asPythonString();
7864 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCoordinateOperation('non_optional', '', sourceCrsParameterName='src', destinationCrsParameterName='dest', staticSourceCrs=QgsCoordinateReferenceSystem('EPSG:7855'), staticDestinationCrs=QgsCoordinateReferenceSystem('EPSG:28355'), defaultValue=\"it's mario\")" ) );
7865 code = def->asScriptCode();
7866 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7867 QVERIFY( fromCode.get() );
7868 QCOMPARE( fromCode->name(), def->name() );
7869 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7870 QCOMPARE( fromCode->flags(), def->flags() );
7871 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7872
7873 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=coordinateoperation 'my val'" ) ) ) );
7874 QVERIFY( fromCode.get() );
7875 QCOMPARE( fromCode->name(), def->name() );
7876 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7877 QCOMPARE( fromCode->flags(), def->flags() );
7878 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7879
7880 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=coordinateoperation \"my val\"" ) ) ) );
7881 QVERIFY( fromCode.get() );
7882 QCOMPARE( fromCode->name(), def->name() );
7883 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7884 QCOMPARE( fromCode->flags(), def->flags() );
7885 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7886
7887 // optional
7888 def.reset( new QgsProcessingParameterCoordinateOperation( "optional", QString(), QString( "default" ), QString(), QString(), QVariant(), QVariant(), true ) );
7889 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7890 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7891 QVERIFY( def->checkValueIsAcceptable( "" ) );
7892 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
7893
7894 params.insert( "optional", QVariant() );
7895 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
7896 params.insert( "optional", QString() ); //empty string should not result in default value
7897 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
7898
7899 pythonCode = def->asPythonString();
7900 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterCoordinateOperation('optional', '', optional=True, defaultValue='default')" ) );
7901
7902 code = def->asScriptCode();
7903 QCOMPARE( code, QStringLiteral( "##optional=optional coordinateoperation default" ) );
7904 fromCode.reset( dynamic_cast< QgsProcessingParameterCoordinateOperation * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7905 QVERIFY( fromCode.get() );
7906 QCOMPARE( fromCode->name(), def->name() );
7907 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
7908 QCOMPARE( fromCode->flags(), def->flags() );
7909 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7910
7911 // not optional, valid default!
7912 def.reset( new QgsProcessingParameterCoordinateOperation( "non_optional", QString(), QString( "def" ), QString(), QString(), QVariant(), QVariant(), false ) );
7913 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7914 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7915 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7916 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
7917 }
7918
parameterMapTheme()7919 void TestQgsProcessing::parameterMapTheme()
7920 {
7921 QgsProcessingContext context;
7922
7923 // not optional!
7924 std::unique_ptr< QgsProcessingParameterMapTheme > def( new QgsProcessingParameterMapTheme( "non_optional", QString(), QVariant(), false ) );
7925 QVERIFY( def->checkValueIsAcceptable( 1 ) );
7926 QVERIFY( def->checkValueIsAcceptable( "test" ) );
7927 QVERIFY( !def->checkValueIsAcceptable( "" ) );
7928 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
7929
7930 // string
7931 QVariantMap params;
7932 params.insert( "non_optional", QString( "abcdef" ) );
7933 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "abcdef" ) );
7934
7935 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
7936 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
7937 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
7938 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
7939 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
7940 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
7941 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
7942
7943 QString pythonCode = def->asPythonString();
7944 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapTheme('non_optional', '', defaultValue=None)" ) );
7945
7946 QString code = def->asScriptCode();
7947 QCOMPARE( code, QStringLiteral( "##non_optional=maptheme" ) );
7948 std::unique_ptr< QgsProcessingParameterMapTheme > fromCode( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7949 QVERIFY( fromCode.get() );
7950 QCOMPARE( fromCode->name(), def->name() );
7951 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7952 QCOMPARE( fromCode->flags(), def->flags() );
7953 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7954
7955 const QVariantMap map = def->toVariantMap();
7956 QgsProcessingParameterMapTheme fromMap( "x" );
7957 QVERIFY( fromMap.fromVariantMap( map ) );
7958 QCOMPARE( fromMap.name(), def->name() );
7959 QCOMPARE( fromMap.description(), def->description() );
7960 QCOMPARE( fromMap.flags(), def->flags() );
7961 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
7962 def.reset( dynamic_cast< QgsProcessingParameterMapTheme *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
7963 QVERIFY( dynamic_cast< QgsProcessingParameterMapTheme *>( def.get() ) );
7964
7965 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=maptheme None" ) ) ) );
7966 QVERIFY( fromCode.get() );
7967 QCOMPARE( fromCode->name(), def->name() );
7968 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7969 QCOMPARE( fromCode->flags(), def->flags() );
7970 QVERIFY( !fromCode->defaultValue().isValid() );
7971
7972 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=maptheme it's mario" ) ) ) );
7973 QVERIFY( fromCode.get() );
7974 QCOMPARE( fromCode->name(), def->name() );
7975 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7976 QCOMPARE( fromCode->flags(), def->flags() );
7977 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
7978
7979 def->setDefaultValue( QStringLiteral( "it's mario" ) );
7980 pythonCode = def->asPythonString();
7981 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapTheme('non_optional', '', defaultValue=\"it's mario\")" ) );
7982 code = def->asScriptCode();
7983 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
7984 QVERIFY( fromCode.get() );
7985 QCOMPARE( fromCode->name(), def->name() );
7986 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7987 QCOMPARE( fromCode->flags(), def->flags() );
7988 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
7989
7990 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=maptheme 'my val'" ) ) ) );
7991 QVERIFY( fromCode.get() );
7992 QCOMPARE( fromCode->name(), def->name() );
7993 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
7994 QCOMPARE( fromCode->flags(), def->flags() );
7995 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
7996
7997 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=maptheme \"my val\"" ) ) ) );
7998 QVERIFY( fromCode.get() );
7999 QCOMPARE( fromCode->name(), def->name() );
8000 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8001 QCOMPARE( fromCode->flags(), def->flags() );
8002 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
8003
8004 // optional
8005 def.reset( new QgsProcessingParameterMapTheme( "optional", QString(), QString( "default" ), true ) );
8006 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8007 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8008 QVERIFY( def->checkValueIsAcceptable( "" ) );
8009 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8010
8011 params.insert( "optional", QVariant() );
8012 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString( "default" ) );
8013 params.insert( "optional", QString() ); //empty string should not result in default value
8014 QCOMPARE( QgsProcessingParameters::parameterAsString( def.get(), params, context ), QString() );
8015
8016 pythonCode = def->asPythonString();
8017 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMapTheme('optional', '', optional=True, defaultValue='default')" ) );
8018
8019 code = def->asScriptCode();
8020 QCOMPARE( code, QStringLiteral( "##optional=optional maptheme default" ) );
8021 fromCode.reset( dynamic_cast< QgsProcessingParameterMapTheme * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8022 QVERIFY( fromCode.get() );
8023 QCOMPARE( fromCode->name(), def->name() );
8024 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8025 QCOMPARE( fromCode->flags(), def->flags() );
8026 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8027
8028 // not optional, valid default!
8029 def.reset( new QgsProcessingParameterMapTheme( "non_optional", QString(), QString( "def" ), false ) );
8030 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8031 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8032 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8033 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
8034 }
8035
parameterProviderConnection()8036 void TestQgsProcessing::parameterProviderConnection()
8037 {
8038 QgsProcessingContext context;
8039
8040 // not optional!
8041 std::unique_ptr< QgsProcessingParameterProviderConnection > def( new QgsProcessingParameterProviderConnection( "non_optional", QString(), QStringLiteral( "ogr" ), QVariant(), false ) );
8042 QCOMPARE( def->providerId(), QStringLiteral( "ogr" ) );
8043 def->setProviderId( QStringLiteral( "postgres" ) );
8044 QCOMPARE( def->providerId(), QStringLiteral( "postgres" ) );
8045 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8046 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8047 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8048 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
8049
8050 // string value
8051 QVariantMap params;
8052 params.insert( "non_optional", QString( "abcdef" ) );
8053 QCOMPARE( QgsProcessingParameters::parameterAsConnectionName( def.get(), params, context ), QString( "abcdef" ) );
8054
8055 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8056 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "'5'" ) );
8057 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
8058 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
8059 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8060 QCOMPARE( def->valueAsPythonString( "uri='complex' username=\"complex\"", context ), QStringLiteral( "'uri=\\'complex\\' username=\"complex\"'" ) );
8061 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.dat" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.dat'" ) );
8062
8063 QString pythonCode = def->asPythonString();
8064 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterProviderConnection('non_optional', '', 'postgres', defaultValue=None)" ) );
8065
8066 QString code = def->asScriptCode();
8067 QCOMPARE( code, QStringLiteral( "##non_optional=providerconnection postgres" ) );
8068 std::unique_ptr< QgsProcessingParameterProviderConnection > fromCode( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8069 QVERIFY( fromCode.get() );
8070 QCOMPARE( fromCode->name(), def->name() );
8071 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8072 QCOMPARE( fromCode->flags(), def->flags() );
8073 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8074 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8075
8076 const QVariantMap map = def->toVariantMap();
8077 QgsProcessingParameterProviderConnection fromMap( "x", QString(), QString() );
8078 QVERIFY( fromMap.fromVariantMap( map ) );
8079 QCOMPARE( fromMap.name(), def->name() );
8080 QCOMPARE( fromMap.description(), def->description() );
8081 QCOMPARE( fromMap.flags(), def->flags() );
8082 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8083 QCOMPARE( fromMap.providerId(), QStringLiteral( "postgres" ) );
8084 def.reset( dynamic_cast< QgsProcessingParameterProviderConnection *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
8085 QVERIFY( dynamic_cast< QgsProcessingParameterProviderConnection *>( def.get() ) );
8086
8087 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=providerconnection postgres None" ) ) ) );
8088 QVERIFY( fromCode.get() );
8089 QCOMPARE( fromCode->name(), def->name() );
8090 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8091 QCOMPARE( fromCode->flags(), def->flags() );
8092 QVERIFY( !fromCode->defaultValue().isValid() );
8093 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8094
8095 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=providerconnection postgres it's mario" ) ) ) );
8096 QVERIFY( fromCode.get() );
8097 QCOMPARE( fromCode->name(), def->name() );
8098 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8099 QCOMPARE( fromCode->flags(), def->flags() );
8100 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "it's mario" ) );
8101 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8102
8103 def->setDefaultValue( QStringLiteral( "it's mario" ) );
8104 pythonCode = def->asPythonString();
8105 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterProviderConnection('non_optional', '', 'postgres', defaultValue=\"it's mario\")" ) );
8106 code = def->asScriptCode();
8107 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8108 QVERIFY( fromCode.get() );
8109 QCOMPARE( fromCode->name(), def->name() );
8110 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8111 QCOMPARE( fromCode->flags(), def->flags() );
8112 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8113 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8114
8115 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=providerconnection postgres 'my val'" ) ) ) );
8116 QVERIFY( fromCode.get() );
8117 QCOMPARE( fromCode->name(), def->name() );
8118 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8119 QCOMPARE( fromCode->flags(), def->flags() );
8120 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
8121 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8122
8123 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##non_optional=providerconnection postgres \"my val\"" ) ) ) );
8124 QVERIFY( fromCode.get() );
8125 QCOMPARE( fromCode->name(), def->name() );
8126 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8127 QCOMPARE( fromCode->flags(), def->flags() );
8128 QCOMPARE( fromCode->defaultValue().toString(), QStringLiteral( "my val" ) );
8129 QCOMPARE( fromCode->providerId(), QStringLiteral( "postgres" ) );
8130
8131 // optional
8132 def.reset( new QgsProcessingParameterProviderConnection( "optional", QString(), QStringLiteral( "ogr" ), QString( "default" ), true ) );
8133 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8134 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8135 QVERIFY( def->checkValueIsAcceptable( "" ) );
8136 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8137
8138 params.insert( "optional", QVariant() );
8139 QCOMPARE( QgsProcessingParameters::parameterAsConnectionName( def.get(), params, context ), QString( "default" ) );
8140 params.insert( "optional", QString() ); //empty string should not result in default value
8141 QCOMPARE( QgsProcessingParameters::parameterAsConnectionName( def.get(), params, context ), QString() );
8142
8143 pythonCode = def->asPythonString();
8144 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterProviderConnection('optional', '', 'ogr', optional=True, defaultValue='default')" ) );
8145
8146 code = def->asScriptCode();
8147 QCOMPARE( code, QStringLiteral( "##optional=optional providerconnection ogr default" ) );
8148 fromCode.reset( dynamic_cast< QgsProcessingParameterProviderConnection * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8149 QVERIFY( fromCode.get() );
8150 QCOMPARE( fromCode->name(), def->name() );
8151 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8152 QCOMPARE( fromCode->flags(), def->flags() );
8153 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8154 QCOMPARE( fromCode->providerId(), QStringLiteral( "ogr" ) );
8155
8156 // not optional, valid default!
8157 def.reset( new QgsProcessingParameterProviderConnection( "non_optional", QString(), QStringLiteral( "ogr" ), QString( "def" ), false ) );
8158 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8159 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8160 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8161 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be valid, falls back to valid default
8162 }
8163
parameterDatabaseSchema()8164 void TestQgsProcessing::parameterDatabaseSchema()
8165 {
8166 QgsProcessingContext context;
8167
8168 // not optional!
8169 std::unique_ptr< QgsProcessingParameterDatabaseSchema > def( new QgsProcessingParameterDatabaseSchema( "non_optional", QString(), QString(), QVariant(), false ) );
8170 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8171 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8172 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8173 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
8174
8175 // string
8176 QVariantMap params;
8177 params.insert( "non_optional", QString( "a" ) );
8178 QCOMPARE( QgsProcessingParameters::parameterAsSchema( def.get(), params, context ), QStringLiteral( "a" ) );
8179
8180 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8181 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
8182 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8183 QCOMPARE( def->valueAsPythonString( "probably\'invalid\"schema", context ), QStringLiteral( "'probably\\'invalid\"schema'" ) );
8184
8185 QString pythonCode = def->asPythonString();
8186 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseSchema('non_optional', '', connectionParameterName='', defaultValue=None)" ) );
8187
8188 QString code = def->asScriptCode();
8189 QCOMPARE( code, QStringLiteral( "##non_optional=databaseschema" ) );
8190 std::unique_ptr< QgsProcessingParameterDatabaseSchema > fromCode( dynamic_cast< QgsProcessingParameterDatabaseSchema * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8191 QVERIFY( fromCode.get() );
8192 QCOMPARE( fromCode->name(), def->name() );
8193 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8194 QCOMPARE( fromCode->flags(), def->flags() );
8195 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8196 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8197
8198 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8199 def->setParentConnectionParameterName( "my_parent" );
8200 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) );
8201
8202 pythonCode = def->asPythonString();
8203 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseSchema('non_optional', '', connectionParameterName='my_parent', defaultValue=None)" ) );
8204
8205 code = def->asScriptCode();
8206 fromCode.reset( dynamic_cast< QgsProcessingParameterDatabaseSchema * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8207 QVERIFY( fromCode.get() );
8208 QCOMPARE( fromCode->name(), def->name() );
8209 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8210 QCOMPARE( fromCode->flags(), def->flags() );
8211 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8212 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8213
8214 // optional
8215 def.reset( new QgsProcessingParameterDatabaseSchema( "optional", QString(), QString(), QStringLiteral( "def" ), true ) );
8216 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8217 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8218 QVERIFY( def->checkValueIsAcceptable( "" ) );
8219 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8220
8221 params.insert( "optional", QVariant() );
8222 QCOMPARE( QgsProcessingParameters::parameterAsSchema( def.get(), params, context ), QStringLiteral( "def" ) );
8223
8224 // optional, no default
8225 def.reset( new QgsProcessingParameterDatabaseSchema( "optional", QString(), QString(), QVariant(), true ) );
8226 params.insert( "optional", QVariant() );
8227 QVERIFY( QgsProcessingParameters::parameterAsSchema( def.get(), params, context ).isEmpty() );
8228
8229 pythonCode = def->asPythonString();
8230 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseSchema('optional', '', optional=True, connectionParameterName='', defaultValue=None)" ) );
8231 code = def->asScriptCode();
8232 QCOMPARE( code, QStringLiteral( "##optional=optional databaseschema" ) );
8233 fromCode.reset( dynamic_cast< QgsProcessingParameterDatabaseSchema * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8234 QVERIFY( fromCode.get() );
8235 QCOMPARE( fromCode->name(), def->name() );
8236 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8237 QCOMPARE( fromCode->flags(), def->flags() );
8238 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8239 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8240 }
8241
parameterDatabaseTable()8242 void TestQgsProcessing::parameterDatabaseTable()
8243 {
8244 QgsProcessingContext context;
8245
8246 // not optional!
8247 std::unique_ptr< QgsProcessingParameterDatabaseTable > def( new QgsProcessingParameterDatabaseTable( "non_optional", QString(), QString(), QString(), QVariant(), false ) );
8248 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8249 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8250 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8251 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
8252
8253 // string
8254 QVariantMap params;
8255 params.insert( "non_optional", QString( "a" ) );
8256 QCOMPARE( QgsProcessingParameters::parameterAsDatabaseTableName( def.get(), params, context ), QStringLiteral( "a" ) );
8257
8258 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8259 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
8260 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8261 QCOMPARE( def->valueAsPythonString( "probably\'invalid\"schema", context ), QStringLiteral( "'probably\\'invalid\"schema'" ) );
8262
8263 QString pythonCode = def->asPythonString();
8264 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('non_optional', '', connectionParameterName='', schemaParameterName='', defaultValue=None)" ) );
8265
8266 QString code = def->asScriptCode();
8267 QCOMPARE( code, QStringLiteral( "##non_optional=databasetable none none" ) );
8268 std::unique_ptr< QgsProcessingParameterDatabaseTable > fromCode( dynamic_cast< QgsProcessingParameterDatabaseTable * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8269 QVERIFY( fromCode.get() );
8270 QCOMPARE( fromCode->name(), def->name() );
8271 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8272 QCOMPARE( fromCode->flags(), def->flags() );
8273 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8274 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8275 QCOMPARE( fromCode->parentSchemaParameterName(), def->parentSchemaParameterName() );
8276
8277 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8278 def->setParentConnectionParameterName( "my_parent" );
8279 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) );
8280 def->setParentSchemaParameterName( "my_schema" );
8281 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "my_parent" ) << QStringLiteral( "my_schema" ) );
8282
8283 pythonCode = def->asPythonString();
8284 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('non_optional', '', connectionParameterName='my_parent', schemaParameterName='my_schema', defaultValue=None)" ) );
8285
8286 code = def->asScriptCode();
8287 fromCode.reset( dynamic_cast< QgsProcessingParameterDatabaseTable * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8288 QVERIFY( fromCode.get() );
8289 QCOMPARE( fromCode->name(), def->name() );
8290 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8291 QCOMPARE( fromCode->flags(), def->flags() );
8292 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8293 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8294 QCOMPARE( fromCode->parentSchemaParameterName(), def->parentSchemaParameterName() );
8295
8296 // optional
8297 def.reset( new QgsProcessingParameterDatabaseTable( "optional", QString(), QString(), QString(), QStringLiteral( "def" ), true ) );
8298 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8299 QVERIFY( def->checkValueIsAcceptable( "test" ) );
8300 QVERIFY( def->checkValueIsAcceptable( "" ) );
8301 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8302
8303 params.insert( "optional", QVariant() );
8304 QCOMPARE( QgsProcessingParameters::parameterAsDatabaseTableName( def.get(), params, context ), QStringLiteral( "def" ) );
8305
8306 // optional, no default
8307 def.reset( new QgsProcessingParameterDatabaseTable( "optional", QString(), QString(), QString(), QVariant(), true ) );
8308 params.insert( "optional", QVariant() );
8309 QVERIFY( QgsProcessingParameters::parameterAsDatabaseTableName( def.get(), params, context ).isEmpty() );
8310
8311 pythonCode = def->asPythonString();
8312 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('optional', '', optional=True, connectionParameterName='', schemaParameterName='', defaultValue=None)" ) );
8313 code = def->asScriptCode();
8314 QCOMPARE( code, QStringLiteral( "##optional=optional databasetable none none" ) );
8315 fromCode.reset( dynamic_cast< QgsProcessingParameterDatabaseTable * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8316 QVERIFY( fromCode.get() );
8317 QCOMPARE( fromCode->name(), def->name() );
8318 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8319 QCOMPARE( fromCode->flags(), def->flags() );
8320 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8321 QCOMPARE( fromCode->parentConnectionParameterName(), def->parentConnectionParameterName() );
8322 QCOMPARE( fromCode->parentSchemaParameterName(), def->parentSchemaParameterName() );
8323
8324 // allow new table names
8325 def.reset( new QgsProcessingParameterDatabaseTable( "new", QString(), QStringLiteral( "con" ), QStringLiteral( "schema" ), QVariant(), false, true ) );
8326
8327 pythonCode = def->asPythonString();
8328 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDatabaseTable('new', '', allowNewTableNames=True, connectionParameterName='con', schemaParameterName='schema', defaultValue=None)" ) );
8329 const QVariantMap var = def->toVariantMap();
8330 def.reset( dynamic_cast<QgsProcessingParameterDatabaseTable *>( QgsProcessingParameters::parameterFromVariantMap( var ) ) );
8331 QCOMPARE( def->parentConnectionParameterName(), QStringLiteral( "con" ) );
8332 QCOMPARE( def->parentSchemaParameterName(), QStringLiteral( "schema" ) );
8333 QVERIFY( def->allowNewTableNames() );
8334 }
8335
parameterFieldMapping()8336 void TestQgsProcessing::parameterFieldMapping()
8337 {
8338 QgsProcessingContext context;
8339
8340 // not optional!
8341 std::unique_ptr< QgsProcessingParameterFieldMapping > def( new QgsProcessingParameterFieldMapping( "non_optional", QString(), QStringLiteral( "parent" ), false ) );
8342 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8343 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8344 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8345 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
8346 QVariantMap map;
8347 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8348 map.insert( QStringLiteral( "name" ), QStringLiteral( "n" ) );
8349 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8350 map.insert( QStringLiteral( "type" ), QStringLiteral( "t" ) );
8351 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8352 map.insert( QStringLiteral( "expression" ), QStringLiteral( "e" ) );
8353 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map ) );
8354 QVariantMap map2;
8355 map2.insert( QStringLiteral( "name" ), QStringLiteral( "n2" ) );
8356 map2.insert( QStringLiteral( "type" ), QStringLiteral( "t2" ) );
8357 map2.insert( QStringLiteral( "expression" ), QStringLiteral( "e2" ) );
8358 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map << map2 ) );
8359
8360 QVariantMap params;
8361 params.insert( "non_optional", QVariantList() << map << map2 );
8362
8363 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8364 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
8365 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
8366 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
8367 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8368 QCOMPARE( def->valueAsPythonString( QVariant( QVariantList() << map << map2 ), context ), QStringLiteral( "[{'expression': 'e','name': 'n','type': 't'},{'expression': 'e2','name': 'n2','type': 't2'}]" ) );
8369
8370 QString pythonCode = def->asPythonString();
8371 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFieldMapping('non_optional', '', parentLayerParameterName='parent')" ) );
8372
8373 const QVariantMap mapDef = def->toVariantMap();
8374 QgsProcessingParameterFieldMapping fromMap( "x" );
8375 QVERIFY( fromMap.fromVariantMap( mapDef ) );
8376 QCOMPARE( fromMap.name(), def->name() );
8377 QCOMPARE( fromMap.description(), def->description() );
8378 QCOMPARE( fromMap.flags(), def->flags() );
8379 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8380 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
8381 def.reset( dynamic_cast< QgsProcessingParameterFieldMapping *>( QgsProcessingParameters::parameterFromVariantMap( mapDef ) ) );
8382 QVERIFY( dynamic_cast< QgsProcessingParameterFieldMapping *>( def.get() ) );
8383
8384 def->setParentLayerParameterName( QString() );
8385 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8386 def->setParentLayerParameterName( QStringLiteral( "test_layer" ) );
8387 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "test_layer" ) );
8388
8389
8390 // optional
8391 def.reset( new QgsProcessingParameterFieldMapping( "non_optional", QString(), QStringLiteral( "parent" ), true ) );
8392 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8393 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8394 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8395 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
8396 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map << map2 ) );
8397 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8398
8399 pythonCode = def->asPythonString();
8400 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFieldMapping('non_optional', '', parentLayerParameterName='parent', optional=True)" ) );
8401 }
8402
parameterAggregate()8403 void TestQgsProcessing::parameterAggregate()
8404 {
8405 QgsProcessingContext context;
8406
8407 // not optional!
8408 std::unique_ptr< QgsProcessingParameterAggregate > def( new QgsProcessingParameterAggregate( "non_optional", QString(), QStringLiteral( "parent" ), false ) );
8409 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8410 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8411 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8412 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
8413 QVariantMap map;
8414 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8415 map.insert( QStringLiteral( "name" ), QStringLiteral( "n" ) );
8416 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8417 map.insert( QStringLiteral( "type" ), QStringLiteral( "t" ) );
8418 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8419 map.insert( QStringLiteral( "aggregate" ), QStringLiteral( "e" ) );
8420 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << map ) );
8421 map.insert( QStringLiteral( "input" ), QStringLiteral( "i" ) );
8422 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map ) );
8423 QVariantMap map2;
8424 map2.insert( QStringLiteral( "name" ), QStringLiteral( "n2" ) );
8425 map2.insert( QStringLiteral( "type" ), QStringLiteral( "t2" ) );
8426 map2.insert( QStringLiteral( "aggregate" ), QStringLiteral( "e2" ) );
8427 map2.insert( QStringLiteral( "input" ), QStringLiteral( "i2" ) );
8428 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map << map2 ) );
8429
8430 QVariantMap params;
8431 params.insert( "non_optional", QVariantList() << map << map2 );
8432
8433 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8434 QCOMPARE( def->valueAsPythonString( 5, context ), QStringLiteral( "5" ) );
8435 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc" ), context ), QStringLiteral( "'abc'" ) );
8436 QCOMPARE( def->valueAsPythonString( QStringLiteral( "abc\ndef" ), context ), QStringLiteral( "'abc\\ndef'" ) );
8437 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8438 QCOMPARE( def->valueAsPythonString( QVariant( QVariantList() << map << map2 ), context ), QStringLiteral( "[{'aggregate': 'e','input': 'i','name': 'n','type': 't'},{'aggregate': 'e2','input': 'i2','name': 'n2','type': 't2'}]" ) );
8439
8440 QString pythonCode = def->asPythonString();
8441 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAggregate('non_optional', '', parentLayerParameterName='parent')" ) );
8442
8443 const QVariantMap mapDef = def->toVariantMap();
8444 QgsProcessingParameterAggregate fromMap( "x" );
8445 QVERIFY( fromMap.fromVariantMap( mapDef ) );
8446 QCOMPARE( fromMap.name(), def->name() );
8447 QCOMPARE( fromMap.description(), def->description() );
8448 QCOMPARE( fromMap.flags(), def->flags() );
8449 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8450 QCOMPARE( fromMap.parentLayerParameterName(), def->parentLayerParameterName() );
8451 def.reset( dynamic_cast< QgsProcessingParameterAggregate *>( QgsProcessingParameters::parameterFromVariantMap( mapDef ) ) );
8452 QVERIFY( dynamic_cast< QgsProcessingParameterAggregate *>( def.get() ) );
8453
8454 def->setParentLayerParameterName( QString() );
8455 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8456 def->setParentLayerParameterName( QStringLiteral( "test_layer" ) );
8457 QCOMPARE( def->dependsOnOtherParameters(), QStringList() << QStringLiteral( "test_layer" ) );
8458
8459
8460 // optional
8461 def.reset( new QgsProcessingParameterAggregate( "non_optional", QString(), QStringLiteral( "parent" ), true ) );
8462 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8463 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8464 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8465 QVERIFY( def->checkValueIsAcceptable( QVariantList() ) );
8466 QVERIFY( def->checkValueIsAcceptable( QVariantList() << map << map2 ) );
8467 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8468
8469 pythonCode = def->asPythonString();
8470 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAggregate('non_optional', '', parentLayerParameterName='parent', optional=True)" ) );
8471 }
8472
parameterTinInputLayers()8473 void TestQgsProcessing::parameterTinInputLayers()
8474 {
8475 QgsProcessingContext context;
8476 QgsProject project;
8477 context.setProject( &project );
8478 QgsVectorLayer *vectorLayer = new QgsVectorLayer( QStringLiteral( "Point" ),
8479 QStringLiteral( "PointLayerForTin" ),
8480 QStringLiteral( "memory" ) );
8481 project.addMapLayer( vectorLayer );
8482
8483 std::unique_ptr< QgsProcessingParameterTinInputLayers > def( new QgsProcessingParameterTinInputLayers( "tin input layer" ) );
8484 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8485 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8486 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8487 QVariantList layerList;
8488 QVERIFY( !def->checkValueIsAcceptable( layerList ) );
8489 QVariantMap layerMap;
8490 layerList.append( layerMap );
8491 QVERIFY( !def->checkValueIsAcceptable( layerList ) );
8492 layerMap["source"] = "layerName";
8493 layerMap["type"] = 0;
8494 layerMap["attributeIndex"] = -1;
8495 layerList[0] = layerMap;
8496 QVERIFY( def->checkValueIsAcceptable( layerList ) );
8497 QVERIFY( !def->checkValueIsAcceptable( layerList, &context ) ); //no corresponding layer in the context's project
8498 layerMap["source"] = "PointLayerForTin";
8499 layerMap["attributeIndex"] = 1; //change for invalid attribute index
8500 layerList[0] = layerMap;
8501 QVERIFY( !def->checkValueIsAcceptable( layerList, &context ) );
8502
8503 layerMap["attributeIndex"] = -1; //valid attribute index (-1 is for Z coordinate of features)
8504 layerList[0] = layerMap;
8505 QVERIFY( def->checkValueIsAcceptable( layerList, &context ) );
8506
8507 const QString valueAsPythonString = def->valueAsPythonString( layerList, context );
8508 QCOMPARE( valueAsPythonString, QStringLiteral( "[{'source': 'PointLayerForTin','type': 0,'attributeIndex': -1}]" ) );
8509
8510 const QString pythonCode = def->asPythonString();
8511 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterTinInputLayers('tin input layer', '')" ) );
8512 }
8513
parameterMeshDatasetGroups()8514 void TestQgsProcessing::parameterMeshDatasetGroups()
8515 {
8516 QgsProcessingContext context;
8517 QgsProject project;
8518 context.setProject( &project );
8519
8520 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariant() ), QList<int>( {0} ) );
8521 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariantList() ), QList<int>( {0} ) );
8522 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( 3 ), QList<int>( {3} ) );
8523 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariant( "3" ) ), QList<int>( {3} ) );
8524 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariantList( { "3", "4", "5"} ) ), QList<int>( {3, 4, 5 } ) );
8525 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariantList( { 3, 4, 5} ) ), QList<int>( {3, 4, 5 } ) );
8526 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( QVariantList( { 3.0, 4.0, 5.0} ) ), QList<int>( {3, 4, 5 } ) );
8527
8528 QSet<int> supportedData;
8529 supportedData << QgsMeshDatasetGroupMetadata::DataOnVertices;
8530 std::unique_ptr< QgsProcessingParameterMeshDatasetGroups> def(
8531 new QgsProcessingParameterMeshDatasetGroups( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ), QString(), supportedData ) );
8532
8533 QVERIFY( def->type() == QLatin1String( "meshdatasetgroups" ) );
8534 QVERIFY( def->isDataTypeSupported( QgsMeshDatasetGroupMetadata::DataOnVertices ) );
8535 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8536 QVERIFY( def->checkValueIsAcceptable( 1.0 ) );
8537 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8538 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
8539 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
8540 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8541 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); //not optional
8542
8543 QVariantList groupsList;
8544 QVERIFY( !def->checkValueIsAcceptable( groupsList ) ); //not optional
8545
8546 groupsList.append( 0 );
8547 QVERIFY( def->checkValueIsAcceptable( groupsList ) );
8548 groupsList.append( 5 );
8549 QVERIFY( def->checkValueIsAcceptable( groupsList ) );
8550
8551 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8552
8553 QString valueAsPythonString = def->valueAsPythonString( groupsList, context );
8554 QCOMPARE( valueAsPythonString, QStringLiteral( "[0,5]" ) );
8555 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList<int>() << 0 << 5 );
8556
8557 QString pythonCode = def->asPythonString();
8558 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', dataType=[QgsMeshDatasetGroupMetadata.DataOnVertices])" ) );
8559
8560 // optional, layer parameter and data on faces
8561 supportedData << QgsMeshDatasetGroupMetadata::DataOnFaces;
8562 def.reset( new QgsProcessingParameterMeshDatasetGroups(
8563 QStringLiteral( "dataset groups" ),
8564 QStringLiteral( "groups" ),
8565 QStringLiteral( "layer parameter" ),
8566 supportedData, true ) );
8567 QVERIFY( def->isDataTypeSupported( QgsMeshDatasetGroupMetadata::DataOnFaces ) );
8568 QVERIFY( def->checkValueIsAcceptable( 1 ) );
8569 QVERIFY( def->checkValueIsAcceptable( 1.0 ) );
8570 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8571 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8572 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
8573 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
8574 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8575 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8576 groupsList = QVariantList();
8577 QVERIFY( def->checkValueIsAcceptable( groupsList ) );
8578 groupsList.append( 2 );
8579 QVERIFY( def->checkValueIsAcceptable( groupsList ) );
8580 groupsList.append( 6 );
8581 QVERIFY( def->checkValueIsAcceptable( groupsList ) );
8582
8583 valueAsPythonString = def->valueAsPythonString( groupsList, context );
8584 QCOMPARE( valueAsPythonString, QStringLiteral( "[2,6]" ) );
8585 QCOMPARE( QgsProcessingParameterMeshDatasetGroups::valueAsDatasetGroup( groupsList ), QList<int>() << 2 << 6 );
8586
8587 QVERIFY( !def->dependsOnOtherParameters().isEmpty() );
8588 QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) );
8589
8590 pythonCode = def->asPythonString();
8591 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetGroups('dataset groups', 'groups', meshLayerParameterName='layer parameter', dataType=[QgsMeshDatasetGroupMetadata.DataOnFaces,QgsMeshDatasetGroupMetadata.DataOnVertices], optional=True)" ) );
8592 }
8593
parameterMeshDatasetTime()8594 void TestQgsProcessing::parameterMeshDatasetTime()
8595 {
8596 QgsProcessingContext context;
8597 QgsProject project;
8598 context.setProject( &project );
8599
8600 std::unique_ptr< QgsProcessingParameterMeshDatasetTime> def( new QgsProcessingParameterMeshDatasetTime( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ) ) );
8601 QVERIFY( def->type() == QLatin1String( "meshdatasettime" ) );
8602 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ) ) );
8603 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ) ).toString() ) );
8604 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
8605 QVERIFY( !def->checkValueIsAcceptable( 1.0 ) );
8606 QVERIFY( !def->checkValueIsAcceptable( "test" ) );
8607 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "a" << "b" ) );
8608 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "a" << "b" ) );
8609 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8610 QVERIFY( !def->checkValueIsAcceptable( QStringList() ) );
8611 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
8612
8613 QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ) ),
8614 QStringLiteral( "defined-date-time" ) );
8615 QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ) ),
8616 QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ), Qt::UTC ) );
8617 QCOMPARE( def->valueAsPythonString( QDateTime( QDate( 2020, 01, 01 ), QTime( 10, 0, 0 ) ), context ),
8618 QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2020, 1, 1), QTime(10, 0, 0))}" ) );
8619
8620 QVariantMap value;
8621 QVERIFY( !def->checkValueIsAcceptable( value ) );
8622 value[QStringLiteral( "test" )] = QStringLiteral( "test" );
8623 QVERIFY( !def->checkValueIsAcceptable( value ) );
8624
8625 value.clear();
8626 value[QStringLiteral( "type" )] = QStringLiteral( "test" );
8627 QVERIFY( !def->checkValueIsAcceptable( value ) );
8628
8629 value[QStringLiteral( "type" )] = QStringLiteral( "static" );
8630 QVERIFY( def->checkValueIsAcceptable( value ) );
8631 QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'static'}" ) );
8632 QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "static" ) );
8633
8634 value[QStringLiteral( "type" )] = QStringLiteral( "current-context-time" );
8635 QVERIFY( def->checkValueIsAcceptable( value ) );
8636 QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'current-context-time'}" ) );
8637 QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "current-context-time" ) );
8638
8639 value[QStringLiteral( "type" )] = QStringLiteral( "defined-date-time" );
8640 QVERIFY( !def->checkValueIsAcceptable( value ) );
8641 value[QStringLiteral( "value" )] = QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) );
8642 QVERIFY( def->checkValueIsAcceptable( value ) );
8643 QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'defined-date-time','value': QDateTime(QDate(2123, 1, 2), QTime(1, 2, 3))}" ) );
8644 QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "defined-date-time" ) );
8645 QCOMPARE( QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ), QDateTime( QDate( 2123, 1, 2 ), QTime( 1, 2, 3 ) ) );
8646 QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ).isValid() );
8647
8648 value.clear();
8649 value[QStringLiteral( "type" )] = QStringLiteral( "dataset-time-step" );
8650 QVERIFY( !def->checkValueIsAcceptable( value ) );
8651 value[QStringLiteral( "value" )] = QVariantList() << 1 << 5;
8652 QVERIFY( def->checkValueIsAcceptable( value ) );
8653 QCOMPARE( def->valueAsPythonString( value, context ), QStringLiteral( "{'type': 'dataset-time-step','value': [1,5]}" ) );
8654 QCOMPARE( QgsProcessingParameterMeshDatasetTime::valueAsTimeType( value ), QStringLiteral( "dataset-time-step" ) );
8655 QVERIFY( !QgsProcessingParameterMeshDatasetTime::timeValueAsDefinedDateTime( value ).isValid() );
8656 QVERIFY( QgsProcessingParameterMeshDatasetTime::timeValueAsDatasetIndex( value ) == QgsMeshDatasetIndex( 1, 5 ) );
8657
8658 QString pythonCode = def->asPythonString();
8659 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetTime('dataset groups', 'groups')" ) );
8660
8661 QVERIFY( def->dependsOnOtherParameters().isEmpty() );
8662
8663 def.reset( new QgsProcessingParameterMeshDatasetTime( QStringLiteral( "dataset groups" ), QStringLiteral( "groups" ), QStringLiteral( "layer parameter" ), QStringLiteral( "dataset group parameter" ) ) );
8664 pythonCode = def->asPythonString();
8665 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterMeshDatasetTime('dataset groups', 'groups', meshLayerParameterName='layer parameter', datasetGroupParameterName='dataset group parameter')" ) );
8666
8667 QVERIFY( !def->dependsOnOtherParameters().isEmpty() );
8668 QCOMPARE( def->meshLayerParameterName(), QStringLiteral( "layer parameter" ) );
8669 QCOMPARE( def->datasetGroupParameterName(), QStringLiteral( "dataset group parameter" ) );
8670 }
8671
parameterDateTime()8672 void TestQgsProcessing::parameterDateTime()
8673 {
8674 QgsProcessingContext context;
8675
8676 // not optional!
8677 std::unique_ptr< QgsProcessingParameterDateTime > def( new QgsProcessingParameterDateTime( "non_optional", QString(), QgsProcessingParameterDateTime::DateTime, QDateTime( QDate( 2010, 4, 3 ), QTime( 12, 11, 10 ) ), false ) );
8678 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
8679 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
8680 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 2, 2 ), QTime( 0, 0, 0 ) ) ) );
8681 QVERIFY( def->checkValueIsAcceptable( QDate( 2020, 2, 2 ) ) );
8682 QVERIFY( !def->checkValueIsAcceptable( QTime( 13, 14, 15 ) ) );
8683 QVERIFY( def->checkValueIsAcceptable( "2020-02-03" ) );
8684 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) ) );
8685 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
8686 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8687 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
8688
8689 // QDateTime value
8690 QVariantMap params;
8691 params.insert( "non_optional", QDateTime( QDate( 2010, 3, 4 ), QTime( 12, 11, 10 ) ) );
8692 QDateTime d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8693 QCOMPARE( d, QDateTime( QDate( 2010, 3, 4 ), QTime( 12, 11, 10 ) ) );
8694 QTime t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8695 QCOMPARE( t, QTime( 12, 11, 10 ) );
8696
8697 params.insert( "non_optional", QDateTime( QDate( 2010, 3, 4 ), QTime( 0, 0, 0 ) ) );
8698 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8699 QCOMPARE( d, QDateTime( QDate( 2010, 3, 4 ), QTime() ) );
8700
8701 // string representing a datetime
8702 params.insert( "non_optional", QString( "2010-03-04" ) );
8703 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8704 QCOMPARE( d, QDateTime( QDate( 2010, 3, 4 ), QTime() ) );
8705
8706 // expression
8707 params.insert( "non_optional", QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) );
8708 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8709 QCOMPARE( d, QDateTime( QDate( 2010, 2, 5 ), QTime() ) );
8710
8711 // nonsense string
8712 params.insert( "non_optional", QString( "i'm not a datetime, and nothing you can do will make me one" ) );
8713 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8714 QCOMPARE( d, QDateTime( QDate( 2010, 4, 3 ), QTime( 12, 11, 10 ) ) );
8715
8716 // with min value
8717 def->setMinimum( QDateTime( QDate( 2015, 1, 1 ), QTime( 0, 0, 0 ) ) );
8718 QVERIFY( !def->checkValueIsAcceptable( QDateTime( QDate( 2014, 12, 31 ), QTime( 0, 0, 0 ) ) ) );
8719 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2014-12-31" ) ) );
8720 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 12, 31 ), QTime( 0, 0, 0 ) ) ) );
8721 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "2020-12-31" ) ) );
8722 // with max value
8723 def->setMaximum( QDateTime( QDate( 2015, 12, 31 ), QTime( 0, 0, 0 ) ) );
8724 QVERIFY( !def->checkValueIsAcceptable( QDateTime( QDate( 2014, 12, 31 ), QTime( 0, 0, 0 ) ) ) );
8725 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2014-12-31" ) ) );
8726 QVERIFY( !def->checkValueIsAcceptable( QDateTime( QDate( 2016, 1, 1 ), QTime( 0, 0, 0 ) ) ) );
8727 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2016-01-01" ) ) );
8728 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2015, 12, 31 ), QTime( 0, 0, 0 ) ) ) );
8729 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "2015-12-31" ) ) );
8730
8731 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
8732 QCOMPARE( def->valueAsPythonString( QDateTime( QDate( 2014, 12, 31 ), QTime( 0, 0, 0 ) ), context ), QStringLiteral( "QDateTime(QDate(2014, 12, 31), QTime(0, 0, 0))" ) );
8733 QCOMPARE( def->valueAsPythonString( QDateTime( QDate( 2014, 12, 31 ), QTime( 12, 11, 10 ) ), context ), QStringLiteral( "QDateTime(QDate(2014, 12, 31), QTime(12, 11, 10))" ) );
8734 QCOMPARE( def->valueAsPythonString( QStringLiteral( "2015-12-31" ), context ), QStringLiteral( "2015-12-31" ) );
8735 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
8736
8737 QString pythonCode = def->asPythonString();
8738 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.DateTime, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDateTime(QDate(2010, 4, 3), QTime(12, 11, 10)))" ) );
8739
8740 QString code = def->asScriptCode();
8741 QCOMPARE( code.left( 43 ), QStringLiteral( "##non_optional=datetime 2010-04-03T12:11:10" ) );
8742 std::unique_ptr< QgsProcessingParameterDateTime > fromCode( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8743 QVERIFY( fromCode.get() );
8744 QCOMPARE( fromCode->name(), def->name() );
8745 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
8746 QCOMPARE( fromCode->flags(), def->flags() );
8747 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8748
8749 QVariantMap map = def->toVariantMap();
8750 QgsProcessingParameterDateTime fromMap( "x" );
8751 QVERIFY( fromMap.fromVariantMap( map ) );
8752 QCOMPARE( fromMap.name(), def->name() );
8753 QCOMPARE( fromMap.description(), def->description() );
8754 QCOMPARE( fromMap.flags(), def->flags() );
8755 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8756 QCOMPARE( fromMap.minimum(), def->minimum() );
8757 QCOMPARE( fromMap.maximum(), def->maximum() );
8758 QCOMPARE( fromMap.dataType(), def->dataType() );
8759 def.reset( dynamic_cast< QgsProcessingParameterDateTime *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
8760 QVERIFY( dynamic_cast< QgsProcessingParameterDateTime *>( def.get() ) );
8761
8762 // optional
8763 def.reset( new QgsProcessingParameterDateTime( "optional", QString(), QgsProcessingParameterDateTime::DateTime, QDateTime( QDate( 2018, 5, 6 ), QTime( 4, 5, 6 ) ), true ) );
8764 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
8765 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
8766 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 2, 2 ), QTime( 0, 0, 0 ) ) ) );
8767 QVERIFY( def->checkValueIsAcceptable( QDate( 2020, 2, 2 ) ) );
8768 QVERIFY( def->checkValueIsAcceptable( "2020-02-03" ) );
8769 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) ) );
8770 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
8771 QVERIFY( def->checkValueIsAcceptable( "" ) );
8772 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
8773
8774 params.insert( "optional", QVariant() );
8775 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8776 QCOMPARE( d, QDateTime( QDate( 2018, 5, 6 ), QTime( 4, 5, 6 ) ) );
8777 params.insert( "optional", QString() );
8778 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8779 QCOMPARE( d, QDateTime( QDate( 2018, 5, 6 ), QTime( 4, 5, 6 ) ) );
8780 params.insert( "optional", QVariant( "aaaa" ) );
8781 d = QgsProcessingParameters::parameterAsDateTime( def.get(), params, context );
8782 QCOMPARE( d, QDateTime( QDate( 2018, 5, 6 ), QTime( 4, 5, 6 ) ) );
8783
8784 pythonCode = def->asPythonString();
8785 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('optional', '', optional=True, type=QgsProcessingParameterDateTime.DateTime, defaultValue=QDateTime(QDate(2018, 5, 6), QTime(4, 5, 6)))" ) );
8786
8787 code = def->asScriptCode();
8788 QCOMPARE( code.left( 48 ), QStringLiteral( "##optional=optional datetime 2018-05-06T04:05:06" ) );
8789 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8790 QVERIFY( fromCode.get() );
8791 QCOMPARE( fromCode->name(), def->name() );
8792 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8793 QCOMPARE( fromCode->flags(), def->flags() );
8794 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8795
8796 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional datetime None" ) ) ) );
8797 QVERIFY( fromCode.get() );
8798 QCOMPARE( fromCode->name(), def->name() );
8799 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8800 QCOMPARE( fromCode->flags(), def->flags() );
8801 QVERIFY( !fromCode->defaultValue().isValid() );
8802
8803 // non-optional, invalid default
8804 def.reset( new QgsProcessingParameterDateTime( "non_optional", QString(), QgsProcessingParameterDateTime::DateTime, QVariant(), false ) );
8805 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
8806 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
8807 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 2, 2 ), QTime( 0, 0, 0 ) ) ) );
8808 QVERIFY( def->checkValueIsAcceptable( QDate( 2020, 2, 2 ) ) );
8809 QVERIFY( def->checkValueIsAcceptable( "2020-02-03" ) );
8810 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) ) );
8811 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
8812 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8813 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) ); // should NOT be acceptable, falls back to invalid default value
8814
8815
8816 // date only mode
8817
8818 // not optional!
8819 def.reset( new QgsProcessingParameterDateTime( "non_optional", QString(), QgsProcessingParameterDateTime::Date, QDate( 2010, 4, 3 ), false ) );
8820 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
8821 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
8822 QVERIFY( def->checkValueIsAcceptable( QDateTime( QDate( 2020, 2, 2 ), QTime( 0, 0, 0 ) ) ) );
8823 QVERIFY( def->checkValueIsAcceptable( QDate( 2020, 2, 2 ) ) );
8824 QVERIFY( !def->checkValueIsAcceptable( QTime( 13, 14, 15 ) ) );
8825 QVERIFY( def->checkValueIsAcceptable( "2020-02-03" ) );
8826 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) ) );
8827 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
8828 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8829 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
8830
8831 // QDateTime value
8832 params.insert( "non_optional", QDateTime( QDate( 2010, 3, 4 ), QTime( 12, 11, 10 ) ) );
8833 QDate dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8834 QCOMPARE( dt, QDate( 2010, 3, 4 ) );
8835
8836 params.insert( "non_optional", QDate( 2010, 3, 4 ) );
8837 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8838 QCOMPARE( dt, QDate( 2010, 3, 4 ) );
8839
8840 // string representing a date
8841 params.insert( "non_optional", QString( "2010-03-04" ) );
8842 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8843 QCOMPARE( dt, QDate( 2010, 3, 4 ) );
8844
8845 // expression
8846 params.insert( "non_optional", QgsProperty::fromExpression( "to_date( '2010-02-03') + to_interval( '2 days')" ) );
8847 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8848 QCOMPARE( dt, QDate( 2010, 2, 5 ) );
8849
8850 // nonsense string
8851 params.insert( "non_optional", QString( "i'm not a datetime, and nothing you can do will make me one" ) );
8852 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8853 QCOMPARE( dt, QDate( QDate( 2010, 4, 3 ) ) );
8854
8855 // with min value
8856 def->setMinimum( QDateTime( QDate( 2015, 1, 1 ), QTime( 0, 0, 0 ) ) );
8857 QVERIFY( !def->checkValueIsAcceptable( QDate( 2014, 12, 31 ) ) );
8858 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2014-12-31" ) ) );
8859 QVERIFY( def->checkValueIsAcceptable( QDate( 2020, 12, 31 ) ) );
8860 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "2020-12-31" ) ) );
8861 // with max value
8862 def->setMaximum( QDateTime( QDate( 2015, 12, 31 ), QTime( 0, 0, 0 ) ) );
8863 QVERIFY( !def->checkValueIsAcceptable( QDate( 2014, 12, 31 ) ) );
8864 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2014-12-31" ) ) );
8865 QVERIFY( !def->checkValueIsAcceptable( QDate( 2016, 1, 1 ) ) );
8866 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "2016-01-01" ) ) );
8867 QVERIFY( def->checkValueIsAcceptable( QDate( 2015, 12, 31 ) ) );
8868 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "2015-12-31" ) ) );
8869
8870 QCOMPARE( def->valueAsPythonString( QDate( 2014, 12, 31 ), context ), QStringLiteral( "QDate(2014, 12, 31)" ) );
8871
8872 pythonCode = def->asPythonString();
8873 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Date, minValue=QDateTime(QDate(2015, 1, 1), QTime(0, 0, 0)), maxValue=QDateTime(QDate(2015, 12, 31), QTime(0, 0, 0)), defaultValue=QDate(2010, 4, 3))" ) );
8874
8875 map = def->toVariantMap();
8876 fromMap = QgsProcessingParameterDateTime( "x" );
8877 QVERIFY( fromMap.fromVariantMap( map ) );
8878 QCOMPARE( fromMap.name(), def->name() );
8879 QCOMPARE( fromMap.description(), def->description() );
8880 QCOMPARE( fromMap.flags(), def->flags() );
8881 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8882 QCOMPARE( fromMap.minimum(), def->minimum() );
8883 QCOMPARE( fromMap.maximum(), def->maximum() );
8884 QCOMPARE( fromMap.dataType(), def->dataType() );
8885 def.reset( dynamic_cast< QgsProcessingParameterDateTime *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
8886 QVERIFY( dynamic_cast< QgsProcessingParameterDateTime *>( def.get() ) );
8887
8888 // optional
8889 def.reset( new QgsProcessingParameterDateTime( "optional", QString(), QgsProcessingParameterDateTime::Date, QDate( 2018, 5, 6 ), true ) );
8890
8891 params.insert( "optional", QVariant() );
8892 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8893 QCOMPARE( dt, QDate( 2018, 5, 6 ) );
8894 params.insert( "optional", QString() );
8895 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8896 QCOMPARE( dt, QDate( 2018, 5, 6 ) );
8897 params.insert( "optional", QVariant( "aaaa" ) );
8898 dt = QgsProcessingParameters::parameterAsDate( def.get(), params, context );
8899 QCOMPARE( dt, QDate( 2018, 5, 6 ) );
8900
8901 pythonCode = def->asPythonString();
8902 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('optional', '', optional=True, type=QgsProcessingParameterDateTime.Date, defaultValue=QDate(2018, 5, 6))" ) );
8903
8904 code = def->asScriptCode();
8905 QCOMPARE( code, QStringLiteral( "##optional=optional datetime 2018-05-06" ) );
8906 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
8907 QVERIFY( fromCode.get() );
8908 QCOMPARE( fromCode->name(), def->name() );
8909 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8910 QCOMPARE( fromCode->flags(), def->flags() );
8911 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
8912
8913 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional datetime None" ) ) ) );
8914 QVERIFY( fromCode.get() );
8915 QCOMPARE( fromCode->name(), def->name() );
8916 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
8917 QCOMPARE( fromCode->flags(), def->flags() );
8918 QVERIFY( !fromCode->defaultValue().isValid() );
8919
8920 // time only mode
8921
8922 // not optional!
8923 def.reset( new QgsProcessingParameterDateTime( "non_optional", QString(), QgsProcessingParameterDateTime::Time, QTime( 12, 11, 13 ), false ) );
8924 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
8925 QVERIFY( !def->checkValueIsAcceptable( "1.1" ) );
8926 QVERIFY( !def->checkValueIsAcceptable( QDateTime( QDate( 2020, 2, 2 ), QTime( 0, 0, 0 ) ) ) );
8927 QVERIFY( !def->checkValueIsAcceptable( QDate( 2020, 2, 2 ) ) );
8928 QVERIFY( def->checkValueIsAcceptable( QTime( 13, 14, 15 ) ) );
8929 QVERIFY( def->checkValueIsAcceptable( "13:14:15" ) );
8930 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromExpression( "to_time('12:30:01') + to_interval( '2 minutes')" ) ) );
8931 QVERIFY( !def->checkValueIsAcceptable( "layer12312312" ) );
8932 QVERIFY( !def->checkValueIsAcceptable( "" ) );
8933 QVERIFY( def->checkValueIsAcceptable( QVariant() ) ); // should be acceptable, falls back to default value
8934
8935 // QTime value
8936 params.insert( "non_optional", QTime( 12, 11, 10 ) );
8937 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8938 QCOMPARE( t, QTime( 12, 11, 10 ) );
8939
8940 params.insert( "non_optional", QDate( 2010, 3, 4 ) );
8941 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8942 QCOMPARE( t, QTime( 12, 11, 13 ) );
8943
8944 // string representing a time
8945 params.insert( "non_optional", QString( "13:14:15" ) );
8946 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8947 QCOMPARE( t, QTime( 13, 14, 15 ) );
8948
8949 // expression
8950 params.insert( "non_optional", QgsProperty::fromExpression( "to_time('12:30:01') + to_interval( '2 minutes')" ) );
8951 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8952 QCOMPARE( t, QTime( 12, 32, 1 ) );
8953
8954 // nonsense string
8955 params.insert( "non_optional", QString( "i'm not a datetime, and nothing you can do will make me one" ) );
8956 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8957 QCOMPARE( t, QTime( 12, 11, 13 ) );
8958
8959 // with min value
8960 def->setMinimum( QDateTime( QDate( 1, 1, 1 ), QTime( 10, 0, 0 ) ) );
8961 QVERIFY( !def->checkValueIsAcceptable( QTime( 9, 0, 0 ) ) );
8962 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "9:00:00" ) ) );
8963 QVERIFY( def->checkValueIsAcceptable( QTime( 12, 0, 0 ) ) );
8964 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "12:00:00" ) ) );
8965 // with max value
8966 def->setMaximum( QDateTime( QDate( 1, 1, 1 ), QTime( 11, 0, 0 ) ) );
8967 QVERIFY( !def->checkValueIsAcceptable( QTime( 9, 0, 0 ) ) );
8968 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "9:00:00" ) ) );
8969 QVERIFY( !def->checkValueIsAcceptable( QTime( 11, 0, 1 ) ) );
8970 QVERIFY( !def->checkValueIsAcceptable( QStringLiteral( "11:00:01" ) ) );
8971 QVERIFY( def->checkValueIsAcceptable( QTime( 10, 40, 1 ) ) );
8972 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "10:40:01" ) ) );
8973
8974 QCOMPARE( def->valueAsPythonString( QTime( 13, 14, 15 ), context ), QStringLiteral( "QTime(13, 14, 15)" ) );
8975
8976 pythonCode = def->asPythonString();
8977 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('non_optional', '', type=QgsProcessingParameterDateTime.Time, minValue=QDateTime(QDate(1, 1, 1), QTime(10, 0, 0)), maxValue=QDateTime(QDate(1, 1, 1), QTime(11, 0, 0)), defaultValue=QTime(12, 11, 13))" ) );
8978
8979 map = def->toVariantMap();
8980 fromMap = QgsProcessingParameterDateTime( "x" );
8981 QVERIFY( fromMap.fromVariantMap( map ) );
8982 QCOMPARE( fromMap.name(), def->name() );
8983 QCOMPARE( fromMap.description(), def->description() );
8984 QCOMPARE( fromMap.flags(), def->flags() );
8985 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
8986 QCOMPARE( fromMap.minimum(), def->minimum() );
8987 QCOMPARE( fromMap.maximum(), def->maximum() );
8988 QCOMPARE( fromMap.dataType(), def->dataType() );
8989 def.reset( dynamic_cast< QgsProcessingParameterDateTime *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
8990 QVERIFY( dynamic_cast< QgsProcessingParameterDateTime *>( def.get() ) );
8991
8992 // optional
8993 def.reset( new QgsProcessingParameterDateTime( "optional", QString(), QgsProcessingParameterDateTime::Time, QTime( 14, 15, 16 ), true ) );
8994
8995 params.insert( "optional", QVariant() );
8996 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
8997 QCOMPARE( t, QTime( 14, 15, 16 ) );
8998 params.insert( "optional", QString() );
8999 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
9000 QCOMPARE( t, QTime( 14, 15, 16 ) );
9001 params.insert( "optional", QVariant( "aaaa" ) );
9002 t = QgsProcessingParameters::parameterAsTime( def.get(), params, context );
9003 QCOMPARE( t, QTime( 14, 15, 16 ) );
9004
9005 pythonCode = def->asPythonString();
9006 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDateTime('optional', '', optional=True, type=QgsProcessingParameterDateTime.Time, defaultValue=QTime(14, 15, 16))" ) );
9007
9008 code = def->asScriptCode();
9009 QCOMPARE( code.left( 37 ), QStringLiteral( "##optional=optional datetime 14:15:16" ) );
9010 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
9011 QVERIFY( fromCode.get() );
9012 QCOMPARE( fromCode->name(), def->name() );
9013 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
9014 QCOMPARE( fromCode->flags(), def->flags() );
9015 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
9016
9017 fromCode.reset( dynamic_cast< QgsProcessingParameterDateTime * >( QgsProcessingParameters::parameterFromScriptCode( QStringLiteral( "##optional=optional datetime None" ) ) ) );
9018 QVERIFY( fromCode.get() );
9019 QCOMPARE( fromCode->name(), def->name() );
9020 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
9021 QCOMPARE( fromCode->flags(), def->flags() );
9022 QVERIFY( !fromCode->defaultValue().isValid() );
9023 }
9024
parameterDxfLayers()9025 void TestQgsProcessing::parameterDxfLayers()
9026 {
9027 QgsProcessingContext context;
9028 QgsProject project;
9029 context.setProject( &project );
9030 QgsVectorLayer *vectorLayer = new QgsVectorLayer( QStringLiteral( "Point" ),
9031 QStringLiteral( "PointLayer" ),
9032 QStringLiteral( "memory" ) );
9033 project.addMapLayer( vectorLayer );
9034
9035 std::unique_ptr< QgsProcessingParameterDxfLayers > def( new QgsProcessingParameterDxfLayers( "dxf input layer" ) );
9036 QVERIFY( !def->checkValueIsAcceptable( 1 ) );
9037 QVERIFY( def->checkValueIsAcceptable( "test" ) );
9038 QVERIFY( !def->checkValueIsAcceptable( "" ) );
9039 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
9040 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( vectorLayer ) ) );
9041
9042 // should also be OK
9043 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
9044 QVERIFY( def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
9045 QVERIFY( def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp" ) );
9046 // ... unless we use context, when the check that the layer actually exists is performed
9047 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
9048 QVERIFY( !def->checkValueIsAcceptable( QStringList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
9049 QVERIFY( !def->checkValueIsAcceptable( QVariantList() << "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.shp", &context ) );
9050
9051 QVariantList layerList;
9052 QVERIFY( !def->checkValueIsAcceptable( layerList ) );
9053 QVariantMap layerMap;
9054 layerList.append( layerMap );
9055 QVERIFY( !def->checkValueIsAcceptable( layerList ) );
9056 layerMap["layer"] = "layerName";
9057 layerMap["attributeIndex"] = -1;
9058 layerList[0] = layerMap;
9059 QVERIFY( def->checkValueIsAcceptable( layerList ) );
9060 QVERIFY( !def->checkValueIsAcceptable( layerList, &context ) ); //no corresponding layer in the context's project
9061 layerMap["layer"] = "PointLayer";
9062 layerMap["attributeIndex"] = 1; //change for invalid attribute index
9063 layerList[0] = layerMap;
9064 QVERIFY( !def->checkValueIsAcceptable( layerList, &context ) );
9065
9066 layerMap["attributeIndex"] = -1;
9067 layerList[0] = layerMap;
9068 QVERIFY( def->checkValueIsAcceptable( layerList, &context ) );
9069
9070 const QString valueAsPythonString = def->valueAsPythonString( layerList, context );
9071 QCOMPARE( valueAsPythonString, QStringLiteral( "[{'layer': '%1','attributeIndex': -1}]" ).arg( vectorLayer->source() ) );
9072
9073 const QString pythonCode = def->asPythonString();
9074 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterDxfLayers('dxf input layer', '')" ) );
9075
9076 const QgsDxfExport::DxfLayer dxfLayer( vectorLayer );
9077 QList<QgsDxfExport::DxfLayer> dxfList = def->parameterAsLayers( QVariant( vectorLayer->source() ), context );
9078 QCOMPARE( dxfList.at( 0 ).layer()->source(), dxfLayer.layer()->source() );
9079 QCOMPARE( dxfList.at( 0 ).layerOutputAttributeIndex(), dxfLayer.layerOutputAttributeIndex() );
9080 dxfList = def->parameterAsLayers( QVariant( QStringList() << vectorLayer->source() ), context );
9081 QCOMPARE( dxfList.at( 0 ).layer()->source(), dxfLayer.layer()->source() );
9082 QCOMPARE( dxfList.at( 0 ).layerOutputAttributeIndex(), dxfLayer.layerOutputAttributeIndex() );
9083 dxfList = def->parameterAsLayers( layerList, context );
9084 QCOMPARE( dxfList.at( 0 ).layer()->source(), dxfLayer.layer()->source() );
9085 QCOMPARE( dxfList.at( 0 ).layerOutputAttributeIndex(), dxfLayer.layerOutputAttributeIndex() );
9086 }
9087
parameterAnnotationLayer()9088 void TestQgsProcessing::parameterAnnotationLayer()
9089 {
9090 // setup a context
9091 QgsProject p;
9092 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
9093 QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
9094 QString vector1 = testDataDir + "multipoint.shp";
9095 QString raster = testDataDir + "landsat.tif";
9096 QFileInfo fi1( raster );
9097 QFileInfo fi2( vector1 );
9098 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
9099 QgsVectorLayer *v1 = new QgsVectorLayer( fi2.filePath(), "V4", "ogr" );
9100
9101 QgsAnnotationLayer *al = new QgsAnnotationLayer( QStringLiteral( "secondary annotation layer" ), QgsAnnotationLayer::LayerOptions( p.transformContext() ) );
9102 Q_ASSERT( al );
9103 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 << al );
9104 QgsProcessingContext context;
9105 context.setProject( &p );
9106
9107 // not optional!
9108 std::unique_ptr< QgsProcessingParameterAnnotationLayer > def( new QgsProcessingParameterAnnotationLayer( "non_optional", QString(), QString( "somelayer" ), false ) );
9109 QVERIFY( !def->checkValueIsAcceptable( false ) );
9110 QVERIFY( !def->checkValueIsAcceptable( true ) );
9111 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
9112 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
9113 QVERIFY( !def->checkValueIsAcceptable( "" ) );
9114 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
9115 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
9116 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( al ) ) );
9117 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( p.mainAnnotationLayer() ) ) );
9118 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
9119 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
9120 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
9121 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
9122
9123 // should be OK
9124 QVERIFY( def->checkValueIsAcceptable( "tertiary annotation layer" ) );
9125 // ... unless we use context, when the check that the layer actually exists is performed
9126 QVERIFY( !def->checkValueIsAcceptable( "tertiary annotation layer", &context ) );
9127
9128 // using existing map layer ID
9129 QVariantMap params;
9130 params.insert( "non_optional", al->id() );
9131 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context )->id(), al->id() );
9132
9133 // using existing layer
9134 params.insert( "non_optional", QVariant::fromValue( al ) );
9135 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context )->id(), al->id() );
9136
9137 // not annotation layer
9138 params.insert( "non_optional", r1->id() );
9139 QVERIFY( !QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context ) );
9140
9141 // using existing non-annotation layer
9142 params.insert( "non_optional", QVariant::fromValue( r1 ) );
9143 QVERIFY( !QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context ) );
9144
9145 // string representing a layer source
9146 params.insert( "non_optional", QStringLiteral( "secondary annotation layer" ) );
9147 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context ), al );
9148
9149 // main annotation layer
9150 params.insert( "non_optional", QStringLiteral( "main" ) );
9151 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context ), p.mainAnnotationLayer() );
9152
9153 // nonsense string
9154 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
9155 QVERIFY( !QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context ) );
9156
9157 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
9158 QCOMPARE( def->valueAsPythonString( QStringLiteral( "main" ), context ), QStringLiteral( "'main'" ) );
9159 QCOMPARE( def->valueAsPythonString( al->id(), context ), QStringLiteral( "'%1'" ).arg( al->id() ) );
9160 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( al ), context ), QStringLiteral( "'%1'" ).arg( al->id() ) );
9161 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
9162
9163 QString pythonCode = def->asPythonString();
9164 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterAnnotationLayer('non_optional', '', defaultValue='somelayer')" ) );
9165
9166 QString code = def->asScriptCode();
9167 QCOMPARE( code, QStringLiteral( "##non_optional=annotation somelayer" ) );
9168 std::unique_ptr< QgsProcessingParameterAnnotationLayer > fromCode( dynamic_cast< QgsProcessingParameterAnnotationLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
9169 QVERIFY( fromCode.get() );
9170 QCOMPARE( fromCode->name(), def->name() );
9171 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
9172 QCOMPARE( fromCode->flags(), def->flags() );
9173 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
9174
9175 QVariantMap map = def->toVariantMap();
9176 QgsProcessingParameterAnnotationLayer fromMap( "x" );
9177 QVERIFY( fromMap.fromVariantMap( map ) );
9178 QCOMPARE( fromMap.name(), def->name() );
9179 QCOMPARE( fromMap.description(), def->description() );
9180 QCOMPARE( fromMap.flags(), def->flags() );
9181 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
9182 def.reset( dynamic_cast< QgsProcessingParameterAnnotationLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
9183 QVERIFY( dynamic_cast< QgsProcessingParameterAnnotationLayer *>( def.get() ) );
9184
9185 // optional
9186 def.reset( new QgsProcessingParameterAnnotationLayer( "optional", QString(), al->id(), true ) );
9187 params.insert( "optional", QVariant() );
9188 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context )->id(), al->id() );
9189 QVERIFY( def->checkValueIsAcceptable( false ) );
9190 QVERIFY( def->checkValueIsAcceptable( true ) );
9191 QVERIFY( def->checkValueIsAcceptable( 5 ) );
9192 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
9193 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.las" ) );
9194 QVERIFY( def->checkValueIsAcceptable( "" ) );
9195 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
9196 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
9197
9198 pythonCode = def->asPythonString();
9199 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterAnnotationLayer('optional', '', optional=True, defaultValue='" ) + al->id() + "')" ) );
9200
9201 code = def->asScriptCode();
9202 QCOMPARE( code, QString( QStringLiteral( "##optional=optional annotation " ) + al->id() ) );
9203 fromCode.reset( dynamic_cast< QgsProcessingParameterAnnotationLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
9204 QVERIFY( fromCode.get() );
9205 QCOMPARE( fromCode->name(), def->name() );
9206 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
9207 QCOMPARE( fromCode->flags(), def->flags() );
9208 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
9209
9210 //optional with direct layer default
9211 def.reset( new QgsProcessingParameterAnnotationLayer( "optional", QString(), QVariant::fromValue( al ), true ) );
9212 QCOMPARE( QgsProcessingParameters::parameterAsAnnotationLayer( def.get(), params, context )->id(), al->id() );
9213 }
9214
9215 #ifdef HAVE_EPT
parameterPointCloudLayer()9216 void TestQgsProcessing::parameterPointCloudLayer()
9217 {
9218 // setup a context
9219 QgsProject p;
9220 p.setCrs( QgsCoordinateReferenceSystem::fromEpsgId( 28353 ) );
9221 QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
9222 QString vector1 = testDataDir + "multipoint.shp";
9223 QString raster = testDataDir + "landsat.tif";
9224 QString pointCloud = testDataDir + "point_clouds/ept/sunshine-coast/ept.json";
9225 QFileInfo fi1( raster );
9226 QFileInfo fi2( vector1 );
9227 QFileInfo fi3( pointCloud );
9228 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
9229 QgsVectorLayer *v1 = new QgsVectorLayer( fi2.filePath(), "V4", "ogr" );
9230 QgsPointCloudLayer *pc1 = new QgsPointCloudLayer( fi3.filePath(), "PC1", "ept" );
9231 Q_ASSERT( pc1 );
9232 p.addMapLayers( QList<QgsMapLayer *>() << v1 << r1 << pc1 );
9233 QgsProcessingContext context;
9234 context.setProject( &p );
9235
9236 // not optional!
9237 std::unique_ptr< QgsProcessingParameterPointCloudLayer > def( new QgsProcessingParameterPointCloudLayer( "non_optional", QString(), QString( "somelayer" ), false ) );
9238 QVERIFY( !def->checkValueIsAcceptable( false ) );
9239 QVERIFY( !def->checkValueIsAcceptable( true ) );
9240 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
9241 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
9242 QVERIFY( !def->checkValueIsAcceptable( "" ) );
9243 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
9244 QVERIFY( !def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
9245 QVERIFY( def->checkValueIsAcceptable( QVariant::fromValue( pc1 ) ) );
9246 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( v1 ) ) );
9247 QVERIFY( !def->checkValueIsAcceptable( QVariant::fromValue( r1 ) ) );
9248 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( QStringLiteral( "layer12312312" ) ) ) );
9249 QVERIFY( !def->checkValueIsAcceptable( QgsProperty::fromValue( QString() ) ) );
9250
9251 // should be OK
9252 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.las" ) );
9253 // ... unless we use context, when the check that the layer actually exists is performed
9254 QVERIFY( !def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.las", &context ) );
9255
9256 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.shp" ) ) );
9257 QVERIFY( !def->createFileFilter().contains( QStringLiteral( "*.tif" ) ) );
9258 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.las" ) ) );
9259 QVERIFY( def->createFileFilter().contains( QStringLiteral( "*.*" ) ) );
9260
9261 // using existing map layer ID
9262 QVariantMap params;
9263 params.insert( "non_optional", pc1->id() );
9264 QCOMPARE( QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context )->id(), pc1->id() );
9265
9266 // using existing layer
9267 params.insert( "non_optional", QVariant::fromValue( pc1 ) );
9268 QCOMPARE( QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context )->id(), pc1->id() );
9269
9270 // not mesh layer
9271 params.insert( "non_optional", r1->id() );
9272 QVERIFY( !QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context ) );
9273
9274 // using existing non-mesh layer
9275 params.insert( "non_optional", QVariant::fromValue( r1 ) );
9276 QVERIFY( !QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context ) );
9277
9278 // string representing a layer source
9279 params.insert( "non_optional", pointCloud );
9280 QCOMPARE( QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context )->publicSource(), pointCloud );
9281
9282 // nonsense string
9283 params.insert( "non_optional", QString( "i'm not a layer, and nothing you can do will make me one" ) );
9284 QVERIFY( !QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context ) );
9285
9286 QCOMPARE( def->valueAsPythonString( QVariant(), context ), QStringLiteral( "None" ) );
9287 QCOMPARE( def->valueAsPythonString( pointCloud, context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json'" ) ) );
9288 QCOMPARE( def->valueAsPythonString( pc1->id(), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json'" ) ) );
9289 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( pc1 ), context ), QString( QString( "'" ) + testDataDir + QStringLiteral( "point_clouds/ept/sunshine-coast/ept.json'" ) ) );
9290 QCOMPARE( def->valueAsPythonString( QVariant::fromValue( QgsProperty::fromExpression( "\"a\"=1" ) ), context ), QStringLiteral( "QgsProperty.fromExpression('\"a\"=1')" ) );
9291 QCOMPARE( def->valueAsPythonString( QStringLiteral( "c:\\test\\new data\\test.las" ), context ), QStringLiteral( "'c:\\\\test\\\\new data\\\\test.las'" ) );
9292
9293 QString pythonCode = def->asPythonString();
9294 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterPointCloudLayer('non_optional', '', defaultValue='somelayer')" ) );
9295
9296 QString code = def->asScriptCode();
9297 QCOMPARE( code, QStringLiteral( "##non_optional=pointcloud somelayer" ) );
9298 std::unique_ptr< QgsProcessingParameterPointCloudLayer > fromCode( dynamic_cast< QgsProcessingParameterPointCloudLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
9299 QVERIFY( fromCode.get() );
9300 QCOMPARE( fromCode->name(), def->name() );
9301 QCOMPARE( fromCode->description(), QStringLiteral( "non optional" ) );
9302 QCOMPARE( fromCode->flags(), def->flags() );
9303 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
9304
9305 QVariantMap map = def->toVariantMap();
9306 QgsProcessingParameterPointCloudLayer fromMap( "x" );
9307 QVERIFY( fromMap.fromVariantMap( map ) );
9308 QCOMPARE( fromMap.name(), def->name() );
9309 QCOMPARE( fromMap.description(), def->description() );
9310 QCOMPARE( fromMap.flags(), def->flags() );
9311 QCOMPARE( fromMap.defaultValue(), def->defaultValue() );
9312 def.reset( dynamic_cast< QgsProcessingParameterPointCloudLayer *>( QgsProcessingParameters::parameterFromVariantMap( map ) ) );
9313 QVERIFY( dynamic_cast< QgsProcessingParameterPointCloudLayer *>( def.get() ) );
9314
9315 // optional
9316 def.reset( new QgsProcessingParameterPointCloudLayer( "optional", QString(), pc1->id(), true ) );
9317 params.insert( "optional", QVariant() );
9318 QCOMPARE( QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context )->id(), pc1->id() );
9319 QVERIFY( def->checkValueIsAcceptable( false ) );
9320 QVERIFY( def->checkValueIsAcceptable( true ) );
9321 QVERIFY( def->checkValueIsAcceptable( 5 ) );
9322 QVERIFY( def->checkValueIsAcceptable( "layer12312312" ) );
9323 QVERIFY( def->checkValueIsAcceptable( "c:/Users/admin/Desktop/roads_clipped_transformed_v1_reprojected_final_clipped_aAAA.las" ) );
9324 QVERIFY( def->checkValueIsAcceptable( "" ) );
9325 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
9326 QVERIFY( def->checkValueIsAcceptable( QgsProcessingFeatureSourceDefinition( "layer1231123" ) ) );
9327
9328 pythonCode = def->asPythonString();
9329 QCOMPARE( pythonCode, QString( QStringLiteral( "QgsProcessingParameterPointCloudLayer('optional', '', optional=True, defaultValue='" ) + pc1->id() + "')" ) );
9330
9331 code = def->asScriptCode();
9332 QCOMPARE( code, QString( QStringLiteral( "##optional=optional pointcloud " ) + pc1->id() ) );
9333 fromCode.reset( dynamic_cast< QgsProcessingParameterPointCloudLayer * >( QgsProcessingParameters::parameterFromScriptCode( code ) ) );
9334 QVERIFY( fromCode.get() );
9335 QCOMPARE( fromCode->name(), def->name() );
9336 QCOMPARE( fromCode->description(), QStringLiteral( "optional" ) );
9337 QCOMPARE( fromCode->flags(), def->flags() );
9338 QCOMPARE( fromCode->defaultValue(), def->defaultValue() );
9339
9340 //optional with direct layer default
9341 def.reset( new QgsProcessingParameterPointCloudLayer( "optional", QString(), QVariant::fromValue( pc1 ), true ) );
9342 QCOMPARE( QgsProcessingParameters::parameterAsPointCloudLayer( def.get(), params, context )->id(), pc1->id() );
9343 }
9344 #endif
9345
checkParamValues()9346 void TestQgsProcessing::checkParamValues()
9347 {
9348 DummyAlgorithm a( "asd" );
9349 a.checkParameterVals();
9350 }
9351
combineLayerExtent()9352 void TestQgsProcessing::combineLayerExtent()
9353 {
9354 QgsProcessingContext context;
9355 QgsRectangle ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>(), QgsCoordinateReferenceSystem(), context );
9356 QVERIFY( ext.isNull() );
9357
9358 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
9359
9360 const QString raster1 = testDataDir + "tenbytenraster.asc";
9361 const QString raster2 = testDataDir + "landsat.tif";
9362 const QFileInfo fi1( raster1 );
9363 const std::unique_ptr< QgsRasterLayer > r1( new QgsRasterLayer( fi1.filePath(), "R1" ) );
9364 const QFileInfo fi2( raster2 );
9365 const std::unique_ptr< QgsRasterLayer > r2( new QgsRasterLayer( fi2.filePath(), "R2" ) );
9366
9367 ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1.get(), QgsCoordinateReferenceSystem(), context );
9368 QGSCOMPARENEAR( ext.xMinimum(), 1535375.000000, 10 );
9369 QGSCOMPARENEAR( ext.xMaximum(), 1535475, 10 );
9370 QGSCOMPARENEAR( ext.yMinimum(), 5083255, 10 );
9371 QGSCOMPARENEAR( ext.yMaximum(), 5083355, 10 );
9372
9373 ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1.get() << r2.get(), QgsCoordinateReferenceSystem(), context );
9374 QGSCOMPARENEAR( ext.xMinimum(), 781662, 10 );
9375 QGSCOMPARENEAR( ext.xMaximum(), 1535475, 10 );
9376 QGSCOMPARENEAR( ext.yMinimum(), 3339523, 10 );
9377 QGSCOMPARENEAR( ext.yMaximum(), 5083355, 10 );
9378
9379 // with reprojection
9380 ext = QgsProcessingUtils::combineLayerExtents( QList< QgsMapLayer *>() << r1.get() << r2.get(), QgsCoordinateReferenceSystem::fromEpsgId( 3785 ), context );
9381 QGSCOMPARENEAR( ext.xMinimum(), 1535375.0, 10 );
9382 QGSCOMPARENEAR( ext.xMaximum(), 2008833, 10 );
9383 QGSCOMPARENEAR( ext.yMinimum(), 3523084, 10 );
9384 QGSCOMPARENEAR( ext.yMaximum(), 5083355, 10 );
9385 }
9386
processingFeatureSource()9387 void TestQgsProcessing::processingFeatureSource()
9388 {
9389 const QString sourceString = QStringLiteral( "test.shp" );
9390 const QgsProcessingFeatureSourceDefinition fs( sourceString, true, 21, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck, QgsFeatureRequest::GeometrySkipInvalid );
9391 QCOMPARE( fs.source.staticValue().toString(), sourceString );
9392 QVERIFY( fs.selectedFeaturesOnly );
9393 QCOMPARE( fs.featureLimit, 21LL );
9394 QCOMPARE( fs.flags, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck );
9395 QCOMPARE( fs.geometryCheck, QgsFeatureRequest::GeometrySkipInvalid );
9396
9397 // test storing QgsProcessingFeatureSource in variant and retrieving
9398 const QVariant fsInVariant = QVariant::fromValue( fs );
9399 QVERIFY( fsInVariant.isValid() );
9400
9401 // test converting to variant map and back
9402 const QVariant res = fs.toVariant();
9403 QgsProcessingFeatureSourceDefinition dd;
9404 QVERIFY( dd.loadVariant( res.toMap() ) );
9405 QCOMPARE( dd.source.staticValue().toString(), sourceString );
9406 QVERIFY( dd.selectedFeaturesOnly );
9407 QCOMPARE( dd.featureLimit, 21LL );
9408 QCOMPARE( dd.flags, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck );
9409 QCOMPARE( dd.geometryCheck, QgsFeatureRequest::GeometrySkipInvalid );
9410
9411 const QgsProcessingFeatureSourceDefinition fromVar = qvariant_cast<QgsProcessingFeatureSourceDefinition>( fsInVariant );
9412 QCOMPARE( fromVar.source.staticValue().toString(), sourceString );
9413 QVERIFY( fromVar.selectedFeaturesOnly );
9414 QCOMPARE( fromVar.featureLimit, 21LL );
9415 QCOMPARE( fromVar.flags, QgsProcessingFeatureSourceDefinition::Flag::FlagOverrideDefaultGeometryCheck );
9416 QCOMPARE( fromVar.geometryCheck, QgsFeatureRequest::GeometrySkipInvalid );
9417
9418 // test evaluating parameter as source
9419 QgsVectorLayer *layer = new QgsVectorLayer( "Point", "v1", "memory" );
9420 QgsFeature f( 10001 );
9421 f.setGeometry( QgsGeometry( new QgsPoint( 1, 2 ) ) );
9422 layer->dataProvider()->addFeatures( QgsFeatureList() << f );
9423
9424 QgsProject p;
9425 p.addMapLayer( layer );
9426 QgsProcessingContext context;
9427 context.setProject( &p );
9428
9429 // first using static string definition
9430 const std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterString( QStringLiteral( "layer" ) ) );
9431 QVariantMap params;
9432 params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSourceDefinition( layer->id(), false ) );
9433 std::unique_ptr< QgsFeatureSource > source( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
9434 // can't directly match it to layer, so instead just get the feature and test that it matches what we expect
9435 QgsFeature f2;
9436 QVERIFY( source.get() );
9437 QVERIFY( source->getFeatures().nextFeature( f2 ) );
9438 QCOMPARE( f2.geometry().asWkt(), f.geometry().asWkt() );
9439
9440 // direct map layer
9441 params.insert( QStringLiteral( "layer" ), QVariant::fromValue( layer ) );
9442 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
9443 // can't directly match it to layer, so instead just get the feature and test that it matches what we expect
9444 QVERIFY( source.get() );
9445 QVERIFY( source->getFeatures().nextFeature( f2 ) );
9446 QCOMPARE( f2.geometry().asWkt(), f.geometry().asWkt() );
9447
9448 // next using property based definition
9449 params.insert( QStringLiteral( "layer" ), QgsProcessingFeatureSourceDefinition( QgsProperty::fromExpression( QStringLiteral( "trim('%1' + ' ')" ).arg( layer->id() ) ), false ) );
9450 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
9451 // can't directly match it to layer, so instead just get the feature and test that it matches what we expect
9452 QVERIFY( source.get() );
9453 QVERIFY( source->getFeatures().nextFeature( f2 ) );
9454 QCOMPARE( f2.geometry().asWkt(), f.geometry().asWkt() );
9455
9456 // we also must accept QgsProcessingOutputLayerDefinition - e.g. to allow outputs from earlier child algorithms in models
9457 params.insert( QStringLiteral( "layer" ), QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( layer->id() ) ) );
9458 source.reset( QgsProcessingParameters::parameterAsSource( def.get(), params, context ) );
9459 // can't directly match it to layer, so instead just get the feature and test that it matches what we expect
9460 QVERIFY( source.get() );
9461 QVERIFY( source->getFeatures().nextFeature( f2 ) );
9462 QCOMPARE( f2.geometry().asWkt(), f.geometry().asWkt() );
9463 }
9464
processingFeatureSink()9465 void TestQgsProcessing::processingFeatureSink()
9466 {
9467 const QString sinkString( QStringLiteral( "test.shp" ) );
9468 QgsProject p;
9469 QgsProcessingOutputLayerDefinition fs( sinkString, &p );
9470 QgsRemappingSinkDefinition remap;
9471 QVERIFY( !fs.useRemapping() );
9472 remap.setDestinationWkbType( QgsWkbTypes::Point );
9473 fs.setRemappingDefinition( remap );
9474 QVERIFY( fs.useRemapping() );
9475
9476 QCOMPARE( fs.sink.staticValue().toString(), sinkString );
9477 QCOMPARE( fs.destinationProject, &p );
9478 QCOMPARE( fs.remappingDefinition().destinationWkbType(), QgsWkbTypes::Point );
9479
9480 // test storing QgsProcessingFeatureSink in variant and retrieving
9481 const QVariant fsInVariant = QVariant::fromValue( fs );
9482 QVERIFY( fsInVariant.isValid() );
9483
9484 const QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( fsInVariant );
9485 QCOMPARE( fromVar.sink.staticValue().toString(), sinkString );
9486 QCOMPARE( fromVar.destinationProject, &p );
9487 QCOMPARE( fromVar.remappingDefinition().destinationWkbType(), QgsWkbTypes::Point );
9488
9489 // test evaluating parameter as sink
9490 QgsProcessingContext context;
9491 context.setProject( &p );
9492
9493 // first using static string definition
9494 std::unique_ptr< QgsProcessingParameterFeatureSink > def( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ) ) );
9495 QVariantMap params;
9496 params.insert( QStringLiteral( "layer" ), QgsProcessingOutputLayerDefinition( "memory:test", nullptr ) );
9497 QString dest;
9498 std::unique_ptr< QgsFeatureSink > sink( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3111" ), context, dest ) );
9499 QVERIFY( sink.get() );
9500 QgsVectorLayer *layer = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context, false ) );
9501 QVERIFY( layer );
9502 QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:3111" ) );
9503
9504 // next using property based definition
9505 params.insert( QStringLiteral( "layer" ), QgsProcessingOutputLayerDefinition( QgsProperty::fromExpression( QStringLiteral( "trim('memory' + ':test2')" ) ), nullptr ) );
9506 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
9507 QVERIFY( sink.get() );
9508 QgsVectorLayer *layer2 = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context, false ) );
9509 QVERIFY( layer2 );
9510 QCOMPARE( layer2->crs().authid(), QStringLiteral( "EPSG:3113" ) );
9511
9512 // temporary sink
9513 params.insert( QStringLiteral( "layer" ), QgsProcessing::TEMPORARY_OUTPUT );
9514 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:28356" ), context, dest ) );
9515 QVERIFY( sink.get() );
9516 QgsVectorLayer *layer3 = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context, false ) );
9517 QVERIFY( layer3 );
9518 QCOMPARE( layer3->crs().authid(), QStringLiteral( "EPSG:28356" ) );
9519 QCOMPARE( layer3->dataProvider()->name(), QStringLiteral( "memory" ) );
9520
9521 params.insert( QStringLiteral( "layer" ), QgsProcessingOutputLayerDefinition( QgsProperty::fromValue( QgsProcessing::TEMPORARY_OUTPUT ), nullptr ) );
9522 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:28354" ), context, dest ) );
9523 QVERIFY( sink.get() );
9524 QgsVectorLayer *layer4 = qobject_cast< QgsVectorLayer *>( QgsProcessingUtils::mapLayerFromString( dest, context, false ) );
9525 QVERIFY( layer4 );
9526 QCOMPARE( layer4->crs().authid(), QStringLiteral( "EPSG:28354" ) );
9527 QCOMPARE( layer4->dataProvider()->name(), QStringLiteral( "memory" ) );
9528
9529 // non optional sink
9530 def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QVariant(), false ) );
9531 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "memory:test" ) ) );
9532 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "memory:test" ) ) );
9533 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( "memory:test" ) ) );
9534 QVERIFY( !def->checkValueIsAcceptable( QString() ) );
9535 QVERIFY( !def->checkValueIsAcceptable( QVariant() ) );
9536 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
9537 params.insert( QStringLiteral( "layer" ), QStringLiteral( "memory:test" ) );
9538 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
9539 QVERIFY( sink.get() );
9540
9541 // optional sink
9542 def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QVariant(), true ) );
9543 QVERIFY( def->checkValueIsAcceptable( QStringLiteral( "memory:test" ) ) );
9544 QVERIFY( def->checkValueIsAcceptable( QgsProcessingOutputLayerDefinition( "memory:test" ) ) );
9545 QVERIFY( def->checkValueIsAcceptable( QgsProperty::fromValue( "memory:test" ) ) );
9546 QVERIFY( def->checkValueIsAcceptable( QString() ) );
9547 QVERIFY( def->checkValueIsAcceptable( QVariant() ) );
9548 QVERIFY( !def->checkValueIsAcceptable( 5 ) );
9549 params.insert( QStringLiteral( "layer" ), QStringLiteral( "memory:test" ) );
9550 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
9551 QVERIFY( sink.get() );
9552 // optional sink, not set - should be no sink
9553 params.insert( QStringLiteral( "layer" ), QVariant() );
9554 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
9555 QVERIFY( !sink.get() );
9556
9557 //.... unless there's a default set
9558 def.reset( new QgsProcessingParameterFeatureSink( QStringLiteral( "layer" ), QString(), QgsProcessing::TypeMapLayer, QStringLiteral( "memory:defaultlayer" ), true ) );
9559 params.insert( QStringLiteral( "layer" ), QVariant() );
9560 sink.reset( QgsProcessingParameters::parameterAsSink( def.get(), params, QgsFields(), QgsWkbTypes::Point, QgsCoordinateReferenceSystem( "EPSG:3113" ), context, dest ) );
9561 QVERIFY( sink.get() );
9562
9563 // appendable
9564 def->setSupportsAppend( true );
9565 QVERIFY( def->supportsAppend() );
9566 QString pythonCode = def->asPythonString();
9567 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('layer', '', optional=True, type=QgsProcessing.TypeMapLayer, createByDefault=True, supportsAppend=True, defaultValue='memory:defaultlayer')" ) );
9568
9569 const QVariantMap val = def->toVariantMap();
9570 QgsProcessingParameterFeatureSink fromMap( "x" );
9571 QVERIFY( fromMap.fromVariantMap( val ) );
9572 QVERIFY( fromMap.supportsAppend() );
9573
9574 def->setSupportsAppend( false );
9575 QVERIFY( !def->supportsAppend() );
9576 pythonCode = def->asPythonString();
9577 QCOMPARE( pythonCode, QStringLiteral( "QgsProcessingParameterFeatureSink('layer', '', optional=True, type=QgsProcessing.TypeMapLayer, createByDefault=True, defaultValue='memory:defaultlayer')" ) );
9578 }
9579
algorithmScope()9580 void TestQgsProcessing::algorithmScope()
9581 {
9582 QgsProcessingContext pc;
9583
9584 // no alg
9585 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::processingAlgorithmScope( nullptr, QVariantMap(), pc ) );
9586 QVERIFY( scope.get() );
9587
9588 // with alg
9589 std::unique_ptr< QgsProcessingAlgorithm > alg( new DummyAlgorithm( "alg1" ) );
9590 QVariantMap params;
9591 params.insert( QStringLiteral( "a_param" ), 5 );
9592 scope.reset( QgsExpressionContextUtils::processingAlgorithmScope( alg.get(), params, pc ) );
9593 QVERIFY( scope.get() );
9594 QCOMPARE( scope->variable( QStringLiteral( "algorithm_id" ) ).toString(), alg->id() );
9595
9596 QgsExpressionContext context;
9597 context.appendScope( scope.release() );
9598 QgsExpression exp( "parameter('bad')" );
9599 QVERIFY( !exp.evaluate( &context ).isValid() );
9600 QgsExpression exp2( "parameter('a_param')" );
9601 QCOMPARE( exp2.evaluate( &context ).toInt(), 5 );
9602 }
9603
modelScope()9604 void TestQgsProcessing::modelScope()
9605 {
9606 QgsProcessingContext pc;
9607
9608 QgsProcessingModelAlgorithm alg( "test", "testGroup" );
9609
9610 QVariantMap variables;
9611 variables.insert( QStringLiteral( "v1" ), 5 );
9612 variables.insert( QStringLiteral( "v2" ), QStringLiteral( "aabbccd" ) );
9613 alg.setVariables( variables );
9614
9615 QVariantMap params;
9616 params.insert( QStringLiteral( "a_param" ), 5 );
9617 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::processingModelAlgorithmScope( &alg, params, pc ) );
9618 QVERIFY( scope.get() );
9619 QCOMPARE( scope->variable( QStringLiteral( "model_name" ) ).toString(), QStringLiteral( "test" ) );
9620 QCOMPARE( scope->variable( QStringLiteral( "model_group" ) ).toString(), QStringLiteral( "testGroup" ) );
9621 QVERIFY( scope->hasVariable( QStringLiteral( "model_path" ) ) );
9622 QVERIFY( scope->hasVariable( QStringLiteral( "model_folder" ) ) );
9623 QCOMPARE( scope->variable( QStringLiteral( "model_path" ) ).toString(), QString() );
9624 QCOMPARE( scope->variable( QStringLiteral( "model_folder" ) ).toString(), QString() );
9625 QCOMPARE( scope->variable( QStringLiteral( "v1" ) ).toInt(), 5 );
9626 QCOMPARE( scope->variable( QStringLiteral( "v2" ) ).toString(), QStringLiteral( "aabbccd" ) );
9627
9628 QgsProject p;
9629 pc.setProject( &p );
9630 p.setFileName( TEST_DATA_DIR + QStringLiteral( "/test_file.qgs" ) );
9631 scope.reset( QgsExpressionContextUtils::processingModelAlgorithmScope( &alg, params, pc ) );
9632 QCOMPARE( scope->variable( QStringLiteral( "model_path" ) ).toString(), QString( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/test_file.qgs" ) ) );
9633 QCOMPARE( scope->variable( QStringLiteral( "model_folder" ) ).toString(), QStringLiteral( TEST_DATA_DIR ) );
9634
9635 alg.setSourceFilePath( TEST_DATA_DIR + QStringLiteral( "/processing/my_model.model3" ) );
9636 scope.reset( QgsExpressionContextUtils::processingModelAlgorithmScope( &alg, params, pc ) );
9637 QCOMPARE( scope->variable( QStringLiteral( "model_path" ) ).toString(), QString( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/processing/my_model.model3" ) ) );
9638 QCOMPARE( scope->variable( QStringLiteral( "model_folder" ) ).toString(), QString( QStringLiteral( TEST_DATA_DIR ) + QStringLiteral( "/processing" ) ) );
9639
9640 const QgsExpressionContext ctx = alg.createExpressionContext( QVariantMap(), pc );
9641 QVERIFY( scope->hasVariable( QStringLiteral( "model_path" ) ) );
9642 QVERIFY( scope->hasVariable( QStringLiteral( "model_folder" ) ) );
9643 }
9644
validateInputCrs()9645 void TestQgsProcessing::validateInputCrs()
9646 {
9647 DummyAlgorithm alg( "test" );
9648 alg.runValidateInputCrsChecks();
9649 }
9650
generateIteratingDestination()9651 void TestQgsProcessing::generateIteratingDestination()
9652 {
9653 QgsProcessingContext context;
9654 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "memory:x", 1, context ).toString(), QStringLiteral( "memory:x_1" ) );
9655 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "memory:x", 2, context ).toString(), QStringLiteral( "memory:x_2" ) );
9656 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "ape.shp", 1, context ).toString(), QStringLiteral( "ape_1.shp" ) );
9657 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "ape.shp", 2, context ).toString(), QStringLiteral( "ape_2.shp" ) );
9658 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( "/home/bif.o/ape.shp", 2, context ).toString(), QStringLiteral( "/home/bif.o/ape_2.shp" ) );
9659 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( QgsProcessing::TEMPORARY_OUTPUT, 2, context ).toString(), QgsProcessing::TEMPORARY_OUTPUT );
9660 QCOMPARE( QgsProcessingUtils::generateIteratingDestination( QgsProperty::fromValue( QgsProcessing::TEMPORARY_OUTPUT ), 2, context ).toString(), QgsProcessing::TEMPORARY_OUTPUT );
9661
9662 QgsProject p;
9663 QgsProcessingOutputLayerDefinition def;
9664 def.sink = QgsProperty::fromValue( "ape.shp" );
9665 def.destinationProject = &p;
9666 QVariant res = QgsProcessingUtils::generateIteratingDestination( def, 2, context );
9667 QVERIFY( res.canConvert<QgsProcessingOutputLayerDefinition>() );
9668 QgsProcessingOutputLayerDefinition fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( res );
9669 QCOMPARE( fromVar.sink.staticValue().toString(), QStringLiteral( "ape_2.shp" ) );
9670 QCOMPARE( fromVar.destinationProject, &p );
9671
9672 def.sink = QgsProperty::fromExpression( "'ape' || '.shp'" );
9673 res = QgsProcessingUtils::generateIteratingDestination( def, 2, context );
9674 QVERIFY( res.canConvert<QgsProcessingOutputLayerDefinition>() );
9675 fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( res );
9676 QCOMPARE( fromVar.sink.staticValue().toString(), QStringLiteral( "ape_2.shp" ) );
9677 QCOMPARE( fromVar.destinationProject, &p );
9678
9679 QgsProcessingOutputLayerDefinition def2;
9680 def2.sink = QgsProperty::fromValue( QgsProcessing::TEMPORARY_OUTPUT );
9681 def2.destinationProject = &p;
9682 res = QgsProcessingUtils::generateIteratingDestination( def2, 2, context );
9683 QVERIFY( res.canConvert<QgsProcessingOutputLayerDefinition>() );
9684 fromVar = qvariant_cast<QgsProcessingOutputLayerDefinition>( res );
9685 QCOMPARE( fromVar.sink.staticValue().toString(), QgsProcessing::TEMPORARY_OUTPUT );
9686 QCOMPARE( fromVar.destinationProject, &p );
9687 }
9688
asPythonCommand()9689 void TestQgsProcessing::asPythonCommand()
9690 {
9691 DummyAlgorithm alg( "test" );
9692 alg.runAsPythonCommandChecks();
9693 }
9694
modelerAlgorithm()9695 void TestQgsProcessing::modelerAlgorithm()
9696 {
9697 //static value source
9698 QgsProcessingModelChildParameterSource svSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
9699 QCOMPARE( svSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9700 QCOMPARE( svSource.staticValue().toInt(), 5 );
9701 svSource.setStaticValue( 7 );
9702 QCOMPARE( svSource.staticValue().toInt(), 7 );
9703 QMap< QString, QString > friendlyNames;
9704 QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "7" ) );
9705 svSource = QgsProcessingModelChildParameterSource::fromModelParameter( "a" );
9706 // check that calling setStaticValue flips source to StaticValue
9707 QCOMPARE( svSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
9708 QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['a']" ) );
9709 svSource.setStaticValue( 7 );
9710 QCOMPARE( svSource.staticValue().toInt(), 7 );
9711 QCOMPARE( svSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9712 QCOMPARE( svSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "7" ) );
9713
9714 // model parameter source
9715 QgsProcessingModelChildParameterSource mpSource = QgsProcessingModelChildParameterSource::fromModelParameter( "a" );
9716 QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
9717 QCOMPARE( mpSource.parameterName(), QStringLiteral( "a" ) );
9718 QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['a']" ) );
9719 mpSource.setParameterName( "b" );
9720 QCOMPARE( mpSource.parameterName(), QStringLiteral( "b" ) );
9721 QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['b']" ) );
9722 mpSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
9723 // check that calling setParameterName flips source to ModelParameter
9724 QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9725 QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
9726 mpSource.setParameterName( "c" );
9727 QCOMPARE( mpSource.parameterName(), QStringLiteral( "c" ) );
9728 QCOMPARE( mpSource.source(), QgsProcessingModelChildParameterSource::ModelParameter );
9729 QCOMPARE( mpSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "parameters['c']" ) );
9730
9731 // child alg output source
9732 QgsProcessingModelChildParameterSource oSource = QgsProcessingModelChildParameterSource::fromChildOutput( "a", "b" );
9733 QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
9734 QCOMPARE( oSource.outputChildId(), QStringLiteral( "a" ) );
9735 QCOMPARE( oSource.outputName(), QStringLiteral( "b" ) );
9736 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['a']['b']" ) );
9737 // with friendly name
9738 friendlyNames.insert( QStringLiteral( "a" ), QStringLiteral( "alga" ) );
9739 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['alga']['b']" ) );
9740 oSource.setOutputChildId( "c" );
9741 QCOMPARE( oSource.outputChildId(), QStringLiteral( "c" ) );
9742 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['c']['b']" ) );
9743 oSource.setOutputName( "d" );
9744 QCOMPARE( oSource.outputName(), QStringLiteral( "d" ) );
9745 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "outputs['c']['d']" ) );
9746 oSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
9747 // check that calling setOutputChildId flips source to ChildOutput
9748 QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9749 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
9750 oSource.setOutputChildId( "c" );
9751 QCOMPARE( oSource.outputChildId(), QStringLiteral( "c" ) );
9752 QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
9753 oSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
9754 // check that calling setOutputName flips source to ChildOutput
9755 QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9756 QCOMPARE( oSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
9757 oSource.setOutputName( "d" );
9758 QCOMPARE( oSource.outputName(), QStringLiteral( "d" ) );
9759 QCOMPARE( oSource.source(), QgsProcessingModelChildParameterSource::ChildOutput );
9760
9761 // expression source
9762 QgsProcessingModelChildParameterSource expSource = QgsProcessingModelChildParameterSource::fromExpression( "1+2" );
9763 QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::Expression );
9764 QCOMPARE( expSource.expression(), QStringLiteral( "1+2" ) );
9765 QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+2').evaluate()" ) );
9766 expSource.setExpression( "1+3" );
9767 QCOMPARE( expSource.expression(), QStringLiteral( "1+3" ) );
9768 QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+3').evaluate()" ) );
9769 expSource.setExpression( "'a' || 'b\\'c'" );
9770 QCOMPARE( expSource.expression(), QStringLiteral( "'a' || 'b\\'c'" ) );
9771 QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression(\"'a' || 'b\\\\'c'\").evaluate()" ) );
9772 expSource = QgsProcessingModelChildParameterSource::fromStaticValue( 5 );
9773 // check that calling setExpression flips source to Expression
9774 QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::StaticValue );
9775 QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "5" ) );
9776 expSource.setExpression( "1+4" );
9777 QCOMPARE( expSource.expression(), QStringLiteral( "1+4" ) );
9778 QCOMPARE( expSource.source(), QgsProcessingModelChildParameterSource::Expression );
9779 QCOMPARE( expSource.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, nullptr, friendlyNames ), QStringLiteral( "QgsExpression('1+4').evaluate()" ) );
9780
9781 // source equality operator
9782 QVERIFY( QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) ==
9783 QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) );
9784 QVERIFY( QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) !=
9785 QgsProcessingModelChildParameterSource::fromStaticValue( 7 ) );
9786 QVERIFY( QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) !=
9787 QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "a" ) ) );
9788 QVERIFY( QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "a" ) ) ==
9789 QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "a" ) ) );
9790 QVERIFY( QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "a" ) ) !=
9791 QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "b" ) ) );
9792 QVERIFY( QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "a" ) ) !=
9793 QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) );
9794 QVERIFY( QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) ==
9795 QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) );
9796 QVERIFY( QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) !=
9797 QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg2" ), QStringLiteral( "out" ) ) );
9798 QVERIFY( QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out" ) ) !=
9799 QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "alg" ), QStringLiteral( "out2" ) ) );
9800 QVERIFY( QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "a" ) ) ==
9801 QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "a" ) ) );
9802 QVERIFY( QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "a" ) ) !=
9803 QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "b" ) ) );
9804 QVERIFY( QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "a" ) ) !=
9805 QgsProcessingModelChildParameterSource::fromStaticValue( QStringLiteral( "b" ) ) );
9806
9807 // a comment
9808 QgsProcessingModelComment comment;
9809 comment.setSize( QSizeF( 9, 8 ) );
9810 QCOMPARE( comment.size(), QSizeF( 9, 8 ) );
9811 comment.setPosition( QPointF( 11, 14 ) );
9812 QCOMPARE( comment.position(), QPointF( 11, 14 ) );
9813 comment.setDescription( QStringLiteral( "a comment" ) );
9814 QCOMPARE( comment.description(), QStringLiteral( "a comment" ) );
9815 comment.setColor( QColor( 123, 45, 67 ) );
9816 QCOMPARE( comment.color(), QColor( 123, 45, 67 ) );
9817 std::unique_ptr< QgsProcessingModelComment > commentClone( comment.clone() );
9818 QCOMPARE( commentClone->toVariant(), comment.toVariant() );
9819 QCOMPARE( commentClone->size(), QSizeF( 9, 8 ) );
9820 QCOMPARE( commentClone->position(), QPointF( 11, 14 ) );
9821 QCOMPARE( commentClone->description(), QStringLiteral( "a comment" ) );
9822 QCOMPARE( commentClone->color(), QColor( 123, 45, 67 ) );
9823 QgsProcessingModelComment comment2;
9824 comment2.loadVariant( comment.toVariant().toMap() );
9825 QCOMPARE( comment2.size(), QSizeF( 9, 8 ) );
9826 QCOMPARE( comment2.position(), QPointF( 11, 14 ) );
9827 QCOMPARE( comment2.description(), QStringLiteral( "a comment" ) );
9828 QCOMPARE( comment2.color(), QColor( 123, 45, 67 ) );
9829
9830 // group boxes
9831 QgsProcessingModelGroupBox groupBox;
9832 groupBox.setSize( QSizeF( 9, 8 ) );
9833 QCOMPARE( groupBox.size(), QSizeF( 9, 8 ) );
9834 groupBox.setPosition( QPointF( 11, 14 ) );
9835 QCOMPARE( groupBox.position(), QPointF( 11, 14 ) );
9836 groupBox.setDescription( QStringLiteral( "a comment" ) );
9837 QCOMPARE( groupBox.description(), QStringLiteral( "a comment" ) );
9838 groupBox.setColor( QColor( 123, 45, 67 ) );
9839 QCOMPARE( groupBox.color(), QColor( 123, 45, 67 ) );
9840 std::unique_ptr< QgsProcessingModelGroupBox > groupClone( groupBox.clone() );
9841 QCOMPARE( groupClone->toVariant(), groupBox.toVariant() );
9842 QCOMPARE( groupClone->size(), QSizeF( 9, 8 ) );
9843 QCOMPARE( groupClone->position(), QPointF( 11, 14 ) );
9844 QCOMPARE( groupClone->description(), QStringLiteral( "a comment" ) );
9845 QCOMPARE( groupClone->color(), QColor( 123, 45, 67 ) );
9846 QCOMPARE( groupClone->uuid(), groupBox.uuid() );
9847 QgsProcessingModelGroupBox groupBox2;
9848 groupBox2.loadVariant( groupBox.toVariant().toMap() );
9849 QCOMPARE( groupBox2.size(), QSizeF( 9, 8 ) );
9850 QCOMPARE( groupBox2.position(), QPointF( 11, 14 ) );
9851 QCOMPARE( groupBox2.description(), QStringLiteral( "a comment" ) );
9852 QCOMPARE( groupBox2.color(), QColor( 123, 45, 67 ) );
9853 QCOMPARE( groupBox2.uuid(), groupBox.uuid() );
9854
9855 const QMap< QString, QString > friendlyOutputNames;
9856 QgsProcessingModelChildAlgorithm child( QStringLiteral( "some_id" ) );
9857 QCOMPARE( child.algorithmId(), QStringLiteral( "some_id" ) );
9858 QVERIFY( !child.algorithm() );
9859 QVERIFY( !child.setAlgorithmId( QStringLiteral( "blah" ) ) );
9860 QVERIFY( !child.reattach() );
9861 QVERIFY( child.setAlgorithmId( QStringLiteral( "native:centroids" ) ) );
9862 QVERIFY( child.algorithm() );
9863 QCOMPARE( child.algorithm()->id(), QStringLiteral( "native:centroids" ) );
9864 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, QgsStringMap(), 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " alg_params = {\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
9865 QgsStringMap extraParams;
9866 extraParams[QStringLiteral( "SOMETHING" )] = QStringLiteral( "SOMETHING_ELSE" );
9867 extraParams[QStringLiteral( "SOMETHING2" )] = QStringLiteral( "SOMETHING_ELSE2" );
9868 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " alg_params = {\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs[''] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
9869 // bit of a hack -- but try to simulate an algorithm not originally available!
9870 child.mAlgorithm.reset();
9871 QVERIFY( !child.algorithm() );
9872 QVERIFY( child.reattach() );
9873 QVERIFY( child.algorithm() );
9874 QCOMPARE( child.algorithm()->id(), QStringLiteral( "native:centroids" ) );
9875
9876 QVariantMap myConfig;
9877 myConfig.insert( QStringLiteral( "some_key" ), 11 );
9878 child.setConfiguration( myConfig );
9879 QCOMPARE( child.configuration(), myConfig );
9880
9881 child.setDescription( QStringLiteral( "desc" ) );
9882 QCOMPARE( child.description(), QStringLiteral( "desc" ) );
9883 QVERIFY( child.isActive() );
9884 child.setActive( false );
9885 QVERIFY( !child.isActive() );
9886 child.setPosition( QPointF( 1, 2 ) );
9887 QCOMPARE( child.position(), QPointF( 1, 2 ) );
9888 child.setSize( QSizeF( 3, 4 ) );
9889 QCOMPARE( child.size(), QSizeF( 3, 4 ) );
9890 QVERIFY( child.linksCollapsed( Qt::TopEdge ) );
9891 child.setLinksCollapsed( Qt::TopEdge, false );
9892 QVERIFY( !child.linksCollapsed( Qt::TopEdge ) );
9893 QVERIFY( child.linksCollapsed( Qt::BottomEdge ) );
9894 child.setLinksCollapsed( Qt::BottomEdge, false );
9895 QVERIFY( !child.linksCollapsed( Qt::BottomEdge ) );
9896 child.comment()->setDescription( QStringLiteral( "com" ) );
9897 QCOMPARE( child.comment()->description(), QStringLiteral( "com" ) );
9898 child.comment()->setSize( QSizeF( 56, 78 ) );
9899 child.comment()->setPosition( QPointF( 111, 122 ) );
9900
9901 QgsProcessingModelChildAlgorithm other;
9902 other.setChildId( QStringLiteral( "diff" ) );
9903 other.setDescription( QStringLiteral( "d2" ) );
9904 other.setAlgorithmId( QStringLiteral( "alg33" ) );
9905 other.setLinksCollapsed( Qt::BottomEdge, true );
9906 other.setLinksCollapsed( Qt::TopEdge, true );
9907 other.comment()->setDescription( QStringLiteral( "other comment" ) );
9908 other.copyNonDefinitionProperties( child );
9909 // only subset of properties should have been copied!
9910 QCOMPARE( other.description(), QStringLiteral( "d2" ) );
9911 QCOMPARE( other.position(), QPointF( 1, 2 ) );
9912 QCOMPARE( other.size(), QSizeF( 3, 4 ) );
9913 QVERIFY( !other.linksCollapsed( Qt::TopEdge ) );
9914 QVERIFY( !other.linksCollapsed( Qt::BottomEdge ) );
9915 QCOMPARE( other.comment()->description(), QStringLiteral( "other comment" ) );
9916 QCOMPARE( other.comment()->position(), QPointF( 111, 122 ) );
9917 QCOMPARE( other.comment()->size(), QSizeF( 56, 78 ) );
9918
9919 child.comment()->setDescription( QString() );
9920
9921 child.setChildId( QStringLiteral( "my_id" ) );
9922 QCOMPARE( child.childId(), QStringLiteral( "my_id" ) );
9923
9924 child.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "a" ) << QgsProcessingModelChildDependency( "b" ) );
9925 QCOMPARE( child.dependencies(), QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "a" ) << QgsProcessingModelChildDependency( "b" ) );
9926
9927 QMap< QString, QgsProcessingModelChildParameterSources > sources;
9928 sources.insert( QStringLiteral( "a" ), QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) );
9929 child.setParameterSources( sources );
9930 QCOMPARE( child.parameterSources().value( QStringLiteral( "a" ) ).at( 0 ).staticValue().toInt(), 5 );
9931 child.addParameterSources( QStringLiteral( "b" ), QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 7 ) << QgsProcessingModelChildParameterSource::fromStaticValue( 9 ) );
9932 QCOMPARE( child.parameterSources().value( QStringLiteral( "a" ) ).at( 0 ).staticValue().toInt(), 5 );
9933 QCOMPARE( child.parameterSources().value( QStringLiteral( "b" ) ).count(), 2 );
9934 QCOMPARE( child.parameterSources().value( QStringLiteral( "b" ) ).at( 0 ).staticValue().toInt(), 7 );
9935 QCOMPARE( child.parameterSources().value( QStringLiteral( "b" ) ).at( 1 ).staticValue().toInt(), 9 );
9936
9937 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " # desc\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
9938 child.comment()->setDescription( QStringLiteral( "do something useful" ) );
9939 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " # desc\n # do something useful\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)" ) );
9940
9941 std::unique_ptr< QgsProcessingModelChildAlgorithm > childClone( child.clone() );
9942 QCOMPARE( childClone->toVariant(), child.toVariant() );
9943 QCOMPARE( childClone->comment()->description(), QStringLiteral( "do something useful" ) );
9944
9945 QgsProcessingModelOutput testModelOut;
9946 testModelOut.setChildId( QStringLiteral( "my_id" ) );
9947 QCOMPARE( testModelOut.childId(), QStringLiteral( "my_id" ) );
9948 testModelOut.setChildOutputName( QStringLiteral( "my_output" ) );
9949 QCOMPARE( testModelOut.childOutputName(), QStringLiteral( "my_output" ) );
9950 testModelOut.setDefaultValue( QStringLiteral( "my_value" ) );
9951 QCOMPARE( testModelOut.defaultValue().toString(), QStringLiteral( "my_value" ) );
9952 testModelOut.setMandatory( true );
9953 QVERIFY( testModelOut.isMandatory() );
9954 testModelOut.comment()->setDescription( QStringLiteral( "my comm" ) );
9955 QCOMPARE( testModelOut.comment()->description(), QStringLiteral( "my comm" ) );
9956 std::unique_ptr< QgsProcessingModelOutput > outputClone( testModelOut.clone() );
9957 QCOMPARE( outputClone->toVariant(), testModelOut.toVariant() );
9958 QCOMPARE( outputClone->comment()->description(), QStringLiteral( "my comm" ) );
9959 QgsProcessingModelOutput testModelOutV;
9960 testModelOutV.loadVariant( testModelOut.toVariant().toMap() );
9961 QCOMPARE( testModelOutV.comment()->description(), QStringLiteral( "my comm" ) );
9962
9963 QgsProcessingOutputLayerDefinition layerDef( QStringLiteral( "my_path" ) );
9964 layerDef.createOptions["fileEncoding"] = QStringLiteral( "my_encoding" );
9965 testModelOut.setDefaultValue( layerDef );
9966 QCOMPARE( testModelOut.defaultValue().value<QgsProcessingOutputLayerDefinition>().sink.staticValue().toString(), QStringLiteral( "my_path" ) );
9967 QVariantMap map = testModelOut.toVariant().toMap();
9968 QCOMPARE( map["default_value"].toMap()["sink"].toMap()["val"].toString(), QStringLiteral( "my_path" ) );
9969 QCOMPARE( map["default_value"].toMap()["create_options"].toMap()["fileEncoding"].toString(), QStringLiteral( "my_encoding" ) );
9970 QgsProcessingModelOutput out;
9971 out.loadVariant( map );
9972 QVERIFY( out.defaultValue().canConvert<QgsProcessingOutputLayerDefinition>() );
9973 layerDef = out.defaultValue().value<QgsProcessingOutputLayerDefinition>();
9974 QCOMPARE( layerDef.sink.staticValue().toString(), QStringLiteral( "my_path" ) );
9975 QCOMPARE( layerDef.createOptions["fileEncoding"].toString(), QStringLiteral( "my_encoding" ) );
9976
9977 QMap<QString, QgsProcessingModelOutput> outputs;
9978 QgsProcessingModelOutput out1;
9979 out1.setDescription( QStringLiteral( "my output" ) );
9980 outputs.insert( QStringLiteral( "a" ), out1 );
9981 child.setModelOutputs( outputs );
9982 QCOMPARE( child.modelOutputs().count(), 1 );
9983 QCOMPARE( child.modelOutputs().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "my output" ) );
9984 QCOMPARE( child.modelOutput( "a" ).description(), QStringLiteral( "my output" ) );
9985 child.modelOutput( "a" ).setDescription( QStringLiteral( "my output 2" ) );
9986 QCOMPARE( child.modelOutput( "a" ).description(), QStringLiteral( "my output 2" ) );
9987 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " # desc\n # do something useful\n alg_params = {\n 'a': 5,\n 'b': [7,9],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
9988
9989 // ensure friendly name is used if present
9990 child.addParameterSources( QStringLiteral( "b" ), QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "a", "out" ) );
9991 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " # desc\n # do something useful\n alg_params = {\n 'a': 5,\n 'b': outputs['alga']['out'],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
9992 friendlyNames.remove( "a" );
9993 QCOMPARE( child.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, extraParams, 4, 2, friendlyNames, friendlyOutputNames ).join( '\n' ), QStringLiteral( " # desc\n # do something useful\n alg_params = {\n 'a': 5,\n 'b': outputs['a']['out'],\n 'SOMETHING': SOMETHING_ELSE,\n 'SOMETHING2': SOMETHING_ELSE2\n }\n outputs['my_id'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n results['my_id:a'] = outputs['my_id']['']" ) );
9994
9995 // no existent
9996 child.modelOutput( "b" ).setDescription( QStringLiteral( "my output 3" ) );
9997 QCOMPARE( child.modelOutput( "b" ).description(), QStringLiteral( "my output 3" ) );
9998 QCOMPARE( child.modelOutputs().count(), 2 );
9999 child.removeModelOutput( QStringLiteral( "a" ) );
10000 QCOMPARE( child.modelOutputs().count(), 1 );
10001
10002 // model algorithm tests
10003
10004 QgsProcessingModelAlgorithm alg( "test", "testGroup" );
10005 QCOMPARE( alg.name(), QStringLiteral( "test" ) );
10006 QCOMPARE( alg.displayName(), QStringLiteral( "test" ) );
10007 QCOMPARE( alg.group(), QStringLiteral( "testGroup" ) );
10008 alg.setName( QStringLiteral( "test2" ) );
10009 QCOMPARE( alg.name(), QStringLiteral( "test2" ) );
10010 QCOMPARE( alg.displayName(), QStringLiteral( "test2" ) );
10011 alg.setGroup( QStringLiteral( "group2" ) );
10012 QCOMPARE( alg.group(), QStringLiteral( "group2" ) );
10013
10014 QVariantMap help;
10015 alg.setHelpContent( help );
10016 QVERIFY( alg.helpContent().isEmpty() );
10017 QVERIFY( alg.helpUrl().isEmpty() );
10018 QVERIFY( alg.shortDescription().isEmpty() );
10019 help.insert( QStringLiteral( "SHORT_DESCRIPTION" ), QStringLiteral( "short" ) );
10020 help.insert( QStringLiteral( "HELP_URL" ), QStringLiteral( "url" ) );
10021 alg.setHelpContent( help );
10022 QCOMPARE( alg.helpContent(), help );
10023 QCOMPARE( alg.shortDescription(), QStringLiteral( "short" ) );
10024 QCOMPARE( alg.helpUrl(), QStringLiteral( "url" ) );
10025
10026 QVERIFY( alg.groupBoxes().isEmpty() );
10027 alg.addGroupBox( groupBox );
10028 QCOMPARE( alg.groupBoxes().size(), 1 );
10029 QCOMPARE( alg.groupBoxes().at( 0 ).uuid(), groupBox.uuid() );
10030 QCOMPARE( alg.groupBoxes().at( 0 ).uuid(), groupBox.uuid() );
10031 alg.removeGroupBox( QStringLiteral( "a" ) );
10032 QCOMPARE( alg.groupBoxes().size(), 1 );
10033 alg.removeGroupBox( groupBox.uuid() );
10034 QVERIFY( alg.groupBoxes().isEmpty() );
10035
10036
10037 QVariantMap lastParams;
10038 lastParams.insert( QStringLiteral( "a" ), 2 );
10039 lastParams.insert( QStringLiteral( "b" ), 4 );
10040 alg.setDesignerParameterValues( lastParams );
10041 QCOMPARE( alg.designerParameterValues(), lastParams );
10042
10043 // child algorithms
10044 QMap<QString, QgsProcessingModelChildAlgorithm> algs;
10045 QgsProcessingModelChildAlgorithm a1;
10046 a1.setDescription( QStringLiteral( "alg1" ) );
10047 QgsProcessingModelChildAlgorithm a2;
10048 a2.setDescription( QStringLiteral( "alg2" ) );
10049 a2.setPosition( QPointF( 112, 131 ) );
10050 a2.setSize( QSizeF( 44, 55 ) );
10051 a2.comment()->setSize( QSizeF( 111, 222 ) );
10052 a2.comment()->setPosition( QPointF( 113, 114 ) );
10053 a2.comment()->setDescription( QStringLiteral( "c" ) );
10054 a2.comment()->setColor( QColor( 255, 254, 253 ) );
10055 QgsProcessingModelOutput oo;
10056 oo.setPosition( QPointF( 312, 331 ) );
10057 oo.setSize( QSizeF( 344, 355 ) );
10058 oo.comment()->setSize( QSizeF( 311, 322 ) );
10059 oo.comment()->setPosition( QPointF( 313, 314 ) );
10060 oo.comment()->setDescription( QStringLiteral( "c3" ) );
10061 oo.comment()->setColor( QColor( 155, 14, 353 ) );
10062 QMap< QString, QgsProcessingModelOutput > a2Outs;
10063 a2Outs.insert( QStringLiteral( "out1" ), oo );
10064 a2.setModelOutputs( a2Outs );
10065
10066 algs.insert( QStringLiteral( "a" ), a1 );
10067 algs.insert( QStringLiteral( "b" ), a2 );
10068 alg.setChildAlgorithms( algs );
10069 QCOMPARE( alg.childAlgorithms().count(), 2 );
10070 QCOMPARE( alg.childAlgorithms().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "alg1" ) );
10071 QCOMPARE( alg.childAlgorithms().value( QStringLiteral( "b" ) ).description(), QStringLiteral( "alg2" ) );
10072
10073 QgsProcessingModelChildAlgorithm a2other;
10074 a2other.setChildId( QStringLiteral( "b" ) );
10075 a2other.setDescription( QStringLiteral( "alg2 other" ) );
10076 const QgsProcessingModelOutput oo2;
10077 QMap< QString, QgsProcessingModelOutput > a2Outs2;
10078 a2Outs2.insert( QStringLiteral( "out1" ), oo2 );
10079 a2other.setModelOutputs( a2Outs2 );
10080
10081 a2other.copyNonDefinitionPropertiesFromModel( &alg );
10082 QCOMPARE( a2other.description(), QStringLiteral( "alg2 other" ) );
10083 QCOMPARE( a2other.position(), QPointF( 112, 131 ) );
10084 QCOMPARE( a2other.size(), QSizeF( 44, 55 ) );
10085 QCOMPARE( a2other.comment()->size(), QSizeF( 111, 222 ) );
10086 QCOMPARE( a2other.comment()->position(), QPointF( 113, 114 ) );
10087 // should not be copied
10088 QCOMPARE( a2other.comment()->description(), QString() );
10089 QVERIFY( !a2other.comment()->color().isValid() );
10090
10091 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).position(), QPointF( 312, 331 ) );
10092 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).size(), QSizeF( 344, 355 ) );
10093 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->size(), QSizeF( 311, 322 ) );
10094 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->position(), QPointF( 313, 314 ) );
10095 // should be copied for outputs
10096 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->description(), QStringLiteral( "c3" ) );
10097 QCOMPARE( a2other.modelOutput( QStringLiteral( "out1" ) ).comment()->color(), QColor( 155, 14, 353 ) );
10098
10099 QgsProcessingModelChildAlgorithm a3;
10100 a3.setChildId( QStringLiteral( "c" ) );
10101 a3.setDescription( QStringLiteral( "alg3" ) );
10102 QCOMPARE( alg.addChildAlgorithm( a3 ), QStringLiteral( "c" ) );
10103 QCOMPARE( alg.childAlgorithms().count(), 3 );
10104 QCOMPARE( alg.childAlgorithms().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "alg1" ) );
10105 QCOMPARE( alg.childAlgorithms().value( QStringLiteral( "b" ) ).description(), QStringLiteral( "alg2" ) );
10106 QCOMPARE( alg.childAlgorithms().value( QStringLiteral( "c" ) ).description(), QStringLiteral( "alg3" ) );
10107 QCOMPARE( alg.childAlgorithm( "a" ).description(), QStringLiteral( "alg1" ) );
10108 QCOMPARE( alg.childAlgorithm( "b" ).description(), QStringLiteral( "alg2" ) );
10109 QCOMPARE( alg.childAlgorithm( "c" ).description(), QStringLiteral( "alg3" ) );
10110 // initially non-existent
10111 QVERIFY( alg.childAlgorithm( "d" ).description().isEmpty() );
10112 alg.childAlgorithm( "d" ).setDescription( QStringLiteral( "alg4" ) );
10113 QCOMPARE( alg.childAlgorithm( "d" ).description(), QStringLiteral( "alg4" ) );
10114 // overwrite existing
10115 QgsProcessingModelChildAlgorithm a4a;
10116 a4a.setChildId( "d" );
10117 a4a.setDescription( "new" );
10118 alg.setChildAlgorithm( a4a );
10119 QCOMPARE( alg.childAlgorithm( "d" ).description(), QStringLiteral( "new" ) );
10120
10121
10122 // generating child ids
10123 QgsProcessingModelChildAlgorithm c1;
10124 c1.setAlgorithmId( QStringLiteral( "buffer" ) );
10125 c1.generateChildId( alg );
10126 QCOMPARE( c1.childId(), QStringLiteral( "buffer_1" ) );
10127 QCOMPARE( alg.addChildAlgorithm( c1 ), QStringLiteral( "buffer_1" ) );
10128 QgsProcessingModelChildAlgorithm c2;
10129 c2.setAlgorithmId( QStringLiteral( "buffer" ) );
10130 c2.generateChildId( alg );
10131 QCOMPARE( c2.childId(), QStringLiteral( "buffer_2" ) );
10132 QCOMPARE( alg.addChildAlgorithm( c2 ), QStringLiteral( "buffer_2" ) );
10133 QgsProcessingModelChildAlgorithm c3;
10134 c3.setAlgorithmId( QStringLiteral( "centroid" ) );
10135 c3.generateChildId( alg );
10136 QCOMPARE( c3.childId(), QStringLiteral( "centroid_1" ) );
10137 QCOMPARE( alg.addChildAlgorithm( c3 ), QStringLiteral( "centroid_1" ) );
10138 QgsProcessingModelChildAlgorithm c4;
10139 c4.setAlgorithmId( QStringLiteral( "centroid" ) );
10140 c4.setChildId( QStringLiteral( "centroid_1" ) );// dupe id
10141 QCOMPARE( alg.addChildAlgorithm( c4 ), QStringLiteral( "centroid_2" ) );
10142 QCOMPARE( alg.childAlgorithm( QStringLiteral( "centroid_2" ) ).childId(), QStringLiteral( "centroid_2" ) );
10143
10144 // parameter components
10145 QMap<QString, QgsProcessingModelParameter> pComponents;
10146 QgsProcessingModelParameter pc1;
10147 pc1.setParameterName( QStringLiteral( "my_param" ) );
10148 QCOMPARE( pc1.parameterName(), QStringLiteral( "my_param" ) );
10149 pc1.comment()->setDescription( QStringLiteral( "my comment" ) );
10150 QCOMPARE( pc1.comment()->description(), QStringLiteral( "my comment" ) );
10151 std::unique_ptr< QgsProcessingModelParameter > paramClone( pc1.clone() );
10152 QCOMPARE( paramClone->toVariant(), pc1.toVariant() );
10153 QCOMPARE( paramClone->comment()->description(), QStringLiteral( "my comment" ) );
10154 QgsProcessingModelParameter pcc1;
10155 pcc1.loadVariant( pc1.toVariant().toMap() );
10156 QCOMPARE( pcc1.comment()->description(), QStringLiteral( "my comment" ) );
10157 pComponents.insert( QStringLiteral( "my_param" ), pc1 );
10158 alg.setParameterComponents( pComponents );
10159 QCOMPARE( alg.parameterComponents().count(), 1 );
10160 QCOMPARE( alg.parameterComponents().value( QStringLiteral( "my_param" ) ).parameterName(), QStringLiteral( "my_param" ) );
10161 QCOMPARE( alg.parameterComponent( "my_param" ).parameterName(), QStringLiteral( "my_param" ) );
10162 alg.parameterComponent( "my_param" ).setDescription( QStringLiteral( "my param 2" ) );
10163 QCOMPARE( alg.parameterComponent( "my_param" ).description(), QStringLiteral( "my param 2" ) );
10164 // no existent
10165 alg.parameterComponent( "b" ).setDescription( QStringLiteral( "my param 3" ) );
10166 QCOMPARE( alg.parameterComponent( "b" ).description(), QStringLiteral( "my param 3" ) );
10167 QCOMPARE( alg.parameterComponent( "b" ).parameterName(), QStringLiteral( "b" ) );
10168 QCOMPARE( alg.parameterComponents().count(), 2 );
10169
10170 // parameter definitions
10171 QgsProcessingModelAlgorithm alg1a( "test", "testGroup" );
10172 QgsProcessingModelParameter bool1;
10173 bool1.setPosition( QPointF( 1, 2 ) );
10174 bool1.setSize( QSizeF( 11, 12 ) );
10175 alg1a.addModelParameter( new QgsProcessingParameterBoolean( "p1", "desc" ), bool1 );
10176 QCOMPARE( alg1a.parameterDefinitions().count(), 1 );
10177 QCOMPARE( alg1a.parameterDefinition( "p1" )->type(), QStringLiteral( "boolean" ) );
10178 QCOMPARE( alg1a.parameterComponent( "p1" ).position().x(), 1.0 );
10179 QCOMPARE( alg1a.parameterComponent( "p1" ).position().y(), 2.0 );
10180 QCOMPARE( alg1a.parameterComponent( "p1" ).size().width(), 11.0 );
10181 QCOMPARE( alg1a.parameterComponent( "p1" ).size().height(), 12.0 );
10182 alg1a.updateModelParameter( new QgsProcessingParameterBoolean( "p1", "descx" ) );
10183 QCOMPARE( alg1a.parameterDefinition( "p1" )->description(), QStringLiteral( "descx" ) );
10184 alg1a.removeModelParameter( "bad" );
10185 QCOMPARE( alg1a.parameterDefinitions().count(), 1 );
10186 alg1a.removeModelParameter( "p1" );
10187 QVERIFY( alg1a.parameterDefinitions().isEmpty() );
10188 QVERIFY( alg1a.parameterComponents().isEmpty() );
10189
10190
10191 // test canExecute
10192 QgsProcessingModelAlgorithm alg2( "test", "testGroup" );
10193 QVERIFY( alg2.canExecute() );
10194 QgsProcessingModelChildAlgorithm c5;
10195 c5.setAlgorithmId( "native:centroids" );
10196 alg2.addChildAlgorithm( c5 );
10197 QVERIFY( alg2.canExecute() );
10198 // non-existing alg
10199 QgsProcessingModelChildAlgorithm c6;
10200 c6.setAlgorithmId( "i'm not an alg" );
10201 alg2.addChildAlgorithm( c6 );
10202 QVERIFY( !alg2.canExecute() );
10203
10204 // test that children are re-attached before testing for canExecute
10205 QgsProcessingModelAlgorithm alg2a( "test", "testGroup" );
10206 QgsProcessingModelChildAlgorithm c5a;
10207 c5a.setAlgorithmId( "native:centroids" );
10208 alg2a.addChildAlgorithm( c5a );
10209 // simulate initially missing provider or algorithm (e.g. another model as a child algorithm)
10210 alg2a.mChildAlgorithms.begin().value().mAlgorithm.reset();
10211 QVERIFY( alg2a.canExecute() );
10212
10213 // dependencies
10214 QgsProcessingModelAlgorithm alg3( "test", "testGroup" );
10215 QVERIFY( alg3.dependentChildAlgorithms( "notvalid" ).isEmpty() );
10216 QVERIFY( alg3.dependsOnChildAlgorithms( "notvalid" ).isEmpty() );
10217 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "notvalid" ) ).isEmpty() );
10218
10219 // add a child
10220 QgsProcessingModelChildAlgorithm c7;
10221 c7.setChildId( "c7" );
10222 alg3.addChildAlgorithm( c7 );
10223 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).isEmpty() );
10224 QVERIFY( alg3.dependsOnChildAlgorithms( "c7" ).isEmpty() );
10225 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c7" ) ).isEmpty() );
10226
10227 // direct dependency
10228 QgsProcessingModelChildAlgorithm c8;
10229 c8.setChildId( "c8" );
10230 c8.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "c7" ) );
10231 alg3.addChildAlgorithm( c8 );
10232 QVERIFY( alg3.dependentChildAlgorithms( "c8" ).isEmpty() );
10233 QVERIFY( alg3.dependsOnChildAlgorithms( "c7" ).isEmpty() );
10234 QCOMPARE( alg3.dependentChildAlgorithms( "c7" ).count(), 1 );
10235 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c8" ) );
10236 QCOMPARE( alg3.dependsOnChildAlgorithms( "c8" ).count(), 1 );
10237 QVERIFY( alg3.dependsOnChildAlgorithms( "c8" ).contains( "c7" ) );
10238 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c7" ) ).isEmpty() );
10239 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).size(), 1 );
10240 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).at( 0 ).childId, QStringLiteral( "c7" ) );
10241
10242 // dependency via parameter source
10243 QgsProcessingModelChildAlgorithm c9;
10244 c9.setChildId( "c9" );
10245 c9.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "c8", "x" ) );
10246 alg3.addChildAlgorithm( c9 );
10247 QVERIFY( alg3.dependentChildAlgorithms( "c9" ).isEmpty() );
10248 QCOMPARE( alg3.dependentChildAlgorithms( "c8" ).count(), 1 );
10249 QVERIFY( alg3.dependentChildAlgorithms( "c8" ).contains( "c9" ) );
10250 QCOMPARE( alg3.dependentChildAlgorithms( "c7" ).count(), 2 );
10251 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c8" ) );
10252 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c9" ) );
10253
10254 QVERIFY( alg3.dependsOnChildAlgorithms( "c7" ).isEmpty() );
10255 QCOMPARE( alg3.dependsOnChildAlgorithms( "c8" ).count(), 1 );
10256 QVERIFY( alg3.dependsOnChildAlgorithms( "c8" ).contains( "c7" ) );
10257 QCOMPARE( alg3.dependsOnChildAlgorithms( "c9" ).count(), 2 );
10258 QVERIFY( alg3.dependsOnChildAlgorithms( "c9" ).contains( "c7" ) );
10259 QVERIFY( alg3.dependsOnChildAlgorithms( "c9" ).contains( "c8" ) );
10260
10261 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c7" ) ).isEmpty() );
10262 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).size(), 1 );
10263 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).at( 0 ).childId, QStringLiteral( "c7" ) );
10264 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).size(), 2 );
10265 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c7" ) ) ) );
10266 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c8" ) ) ) );
10267
10268 QgsProcessingModelChildAlgorithm c9b;
10269 c9b.setChildId( "c9b" );
10270 c9b.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "c9", "x" ) );
10271 alg3.addChildAlgorithm( c9b );
10272
10273 QCOMPARE( alg3.dependentChildAlgorithms( "c9" ).count(), 1 );
10274 QCOMPARE( alg3.dependentChildAlgorithms( "c8" ).count(), 2 );
10275 QVERIFY( alg3.dependentChildAlgorithms( "c8" ).contains( "c9" ) );
10276 QVERIFY( alg3.dependentChildAlgorithms( "c8" ).contains( "c9b" ) );
10277 QCOMPARE( alg3.dependentChildAlgorithms( "c7" ).count(), 3 );
10278 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c8" ) );
10279 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c9" ) );
10280 QVERIFY( alg3.dependentChildAlgorithms( "c7" ).contains( "c9b" ) );
10281
10282 QVERIFY( alg3.dependsOnChildAlgorithms( "c7" ).isEmpty() );
10283 QCOMPARE( alg3.dependsOnChildAlgorithms( "c8" ).count(), 1 );
10284 QVERIFY( alg3.dependsOnChildAlgorithms( "c8" ).contains( "c7" ) );
10285 QCOMPARE( alg3.dependsOnChildAlgorithms( "c9" ).count(), 2 );
10286 QVERIFY( alg3.dependsOnChildAlgorithms( "c9" ).contains( "c7" ) );
10287 QVERIFY( alg3.dependsOnChildAlgorithms( "c9" ).contains( "c8" ) );
10288 QCOMPARE( alg3.dependsOnChildAlgorithms( "c9b" ).count(), 3 );
10289 QVERIFY( alg3.dependsOnChildAlgorithms( "c9b" ).contains( "c7" ) );
10290 QVERIFY( alg3.dependsOnChildAlgorithms( "c9b" ).contains( "c8" ) );
10291 QVERIFY( alg3.dependsOnChildAlgorithms( "c9b" ).contains( "c9" ) );
10292
10293 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c7" ) ).isEmpty() );
10294 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).size(), 1 );
10295 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c8" ) ).at( 0 ).childId, QStringLiteral( "c7" ) );
10296 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).size(), 2 );
10297 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c7" ) ) ) );
10298 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c8" ) ) ) );
10299 QCOMPARE( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9b" ) ).size(), 3 );
10300 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9b" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c7" ) ) ) );
10301 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9b" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c8" ) ) ) );
10302 QVERIFY( alg3.availableDependenciesForChildAlgorithm( QStringLiteral( "c9b" ) ).contains( QgsProcessingModelChildDependency( QStringLiteral( "c9" ) ) ) );
10303
10304 alg3.removeChildAlgorithm( "c9b" );
10305
10306
10307 // (de)activate child algorithm
10308 alg3.deactivateChildAlgorithm( "c9" );
10309 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10310 QVERIFY( alg3.activateChildAlgorithm( "c9" ) );
10311 QVERIFY( alg3.childAlgorithm( "c9" ).isActive() );
10312 alg3.deactivateChildAlgorithm( "c8" );
10313 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10314 QVERIFY( !alg3.childAlgorithm( "c8" ).isActive() );
10315 QVERIFY( !alg3.activateChildAlgorithm( "c9" ) );
10316 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10317 QVERIFY( !alg3.childAlgorithm( "c8" ).isActive() );
10318 QVERIFY( alg3.activateChildAlgorithm( "c8" ) );
10319 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10320 QVERIFY( alg3.childAlgorithm( "c8" ).isActive() );
10321 QVERIFY( alg3.activateChildAlgorithm( "c9" ) );
10322 QVERIFY( alg3.childAlgorithm( "c9" ).isActive() );
10323 QVERIFY( alg3.childAlgorithm( "c8" ).isActive() );
10324 alg3.deactivateChildAlgorithm( "c7" );
10325 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10326 QVERIFY( !alg3.childAlgorithm( "c8" ).isActive() );
10327 QVERIFY( !alg3.childAlgorithm( "c7" ).isActive() );
10328 QVERIFY( !alg3.activateChildAlgorithm( "c9" ) );
10329 QVERIFY( !alg3.activateChildAlgorithm( "c8" ) );
10330 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10331 QVERIFY( !alg3.childAlgorithm( "c8" ).isActive() );
10332 QVERIFY( !alg3.childAlgorithm( "c7" ).isActive() );
10333 QVERIFY( !alg3.activateChildAlgorithm( "c8" ) );
10334 QVERIFY( alg3.activateChildAlgorithm( "c7" ) );
10335 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10336 QVERIFY( !alg3.childAlgorithm( "c8" ).isActive() );
10337 QVERIFY( alg3.childAlgorithm( "c7" ).isActive() );
10338 QVERIFY( !alg3.activateChildAlgorithm( "c9" ) );
10339 QVERIFY( alg3.activateChildAlgorithm( "c8" ) );
10340 QVERIFY( !alg3.childAlgorithm( "c9" ).isActive() );
10341 QVERIFY( alg3.childAlgorithm( "c8" ).isActive() );
10342 QVERIFY( alg3.childAlgorithm( "c7" ).isActive() );
10343 QVERIFY( alg3.activateChildAlgorithm( "c9" ) );
10344 QVERIFY( alg3.childAlgorithm( "c9" ).isActive() );
10345 QVERIFY( alg3.childAlgorithm( "c8" ).isActive() );
10346 QVERIFY( alg3.childAlgorithm( "c7" ).isActive() );
10347
10348
10349
10350 //remove child algorithm
10351 QVERIFY( !alg3.removeChildAlgorithm( "c7" ) );
10352 QVERIFY( !alg3.removeChildAlgorithm( "c8" ) );
10353 QVERIFY( alg3.removeChildAlgorithm( "c9" ) );
10354 QCOMPARE( alg3.childAlgorithms().count(), 2 );
10355 QVERIFY( alg3.childAlgorithms().contains( "c7" ) );
10356 QVERIFY( alg3.childAlgorithms().contains( "c8" ) );
10357 QVERIFY( !alg3.removeChildAlgorithm( "c7" ) );
10358 QVERIFY( alg3.removeChildAlgorithm( "c8" ) );
10359 QCOMPARE( alg3.childAlgorithms().count(), 1 );
10360 QVERIFY( alg3.childAlgorithms().contains( "c7" ) );
10361 QVERIFY( alg3.removeChildAlgorithm( "c7" ) );
10362 QVERIFY( alg3.childAlgorithms().isEmpty() );
10363
10364 // parameter dependencies
10365 QgsProcessingModelAlgorithm alg4( "test", "testGroup" );
10366 QVERIFY( !alg4.childAlgorithmsDependOnParameter( "not a param" ) );
10367 QgsProcessingModelChildAlgorithm c10;
10368 c10.setChildId( "c10" );
10369 alg4.addChildAlgorithm( c10 );
10370 QVERIFY( !alg4.childAlgorithmsDependOnParameter( "not a param" ) );
10371 const QgsProcessingModelParameter bool2;
10372 alg4.addModelParameter( new QgsProcessingParameterBoolean( "p1", "desc" ), bool2 );
10373 QVERIFY( !alg4.childAlgorithmsDependOnParameter( "p1" ) );
10374 c10.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "p2" ) );
10375 alg4.setChildAlgorithm( c10 );
10376 QVERIFY( !alg4.childAlgorithmsDependOnParameter( "p1" ) );
10377 c10.addParameterSources( "y", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "p1" ) );
10378 alg4.setChildAlgorithm( c10 );
10379 QVERIFY( alg4.childAlgorithmsDependOnParameter( "p1" ) );
10380
10381 const QgsProcessingModelParameter vlP;
10382 alg4.addModelParameter( new QgsProcessingParameterVectorLayer( "layer" ), vlP );
10383 const QgsProcessingModelParameter field;
10384 alg4.addModelParameter( new QgsProcessingParameterField( "field", QString(), QVariant(), QStringLiteral( "layer" ) ), field );
10385 QVERIFY( !alg4.otherParametersDependOnParameter( "p1" ) );
10386 QVERIFY( !alg4.otherParametersDependOnParameter( "field" ) );
10387 QVERIFY( alg4.otherParametersDependOnParameter( "layer" ) );
10388
10389
10390
10391
10392
10393 // to/from XML
10394 QgsProcessingModelAlgorithm alg5( "test", "testGroup" );
10395 alg5.helpContent().insert( "author", "me" );
10396 alg5.helpContent().insert( "usage", "run" );
10397 alg5.addGroupBox( groupBox );
10398 QVariantMap variables;
10399 variables.insert( QStringLiteral( "v1" ), 5 );
10400 variables.insert( QStringLiteral( "v2" ), QStringLiteral( "aabbccd" ) );
10401 alg5.setVariables( variables );
10402 QCOMPARE( alg5.variables(), variables );
10403 QgsProcessingModelChildAlgorithm alg5c1;
10404 alg5c1.setChildId( "cx1" );
10405 alg5c1.setAlgorithmId( "buffer" );
10406 alg5c1.setConfiguration( myConfig );
10407 alg5c1.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "p1" ) );
10408 alg5c1.addParameterSources( "y", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "cx2", "out3" ) );
10409 alg5c1.addParameterSources( "z", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 5 ) );
10410 alg5c1.addParameterSources( "a", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromExpression( "2*2" ) );
10411 alg5c1.addParameterSources( "zm", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 6 )
10412 << QgsProcessingModelChildParameterSource::fromModelParameter( "p2" )
10413 << QgsProcessingModelChildParameterSource::fromChildOutput( "cx2", "out4" )
10414 << QgsProcessingModelChildParameterSource::fromExpression( "1+2" )
10415 << QgsProcessingModelChildParameterSource::fromStaticValue( QgsProperty::fromExpression( "1+8" ) ) );
10416 alg5c1.setActive( true );
10417 alg5c1.setLinksCollapsed( Qt::BottomEdge, true );
10418 alg5c1.setLinksCollapsed( Qt::TopEdge, true );
10419 alg5c1.setDescription( "child 1" );
10420 alg5c1.setPosition( QPointF( 1, 2 ) );
10421 alg5c1.setSize( QSizeF( 11, 21 ) );
10422 QMap<QString, QgsProcessingModelOutput> alg5c1outputs;
10423 QgsProcessingModelOutput alg5c1out1;
10424 alg5c1out1.setDescription( QStringLiteral( "my output" ) );
10425 alg5c1out1.setPosition( QPointF( 3, 4 ) );
10426 alg5c1out1.setSize( QSizeF( 31, 41 ) );
10427 alg5c1outputs.insert( QStringLiteral( "a" ), alg5c1out1 );
10428 alg5c1.setModelOutputs( alg5c1outputs );
10429 alg5.addChildAlgorithm( alg5c1 );
10430
10431 QgsProcessingModelChildAlgorithm alg5c2;
10432 alg5c2.setChildId( "cx2" );
10433 alg5c2.setAlgorithmId( QStringLiteral( "native:centroids" ) );
10434 alg5c2.setActive( false );
10435 alg5c2.setLinksCollapsed( Qt::BottomEdge, false );
10436 alg5c2.setLinksCollapsed( Qt::TopEdge, false );
10437 alg5c2.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "a" ) << QgsProcessingModelChildDependency( "b" ) );
10438 alg5.addChildAlgorithm( alg5c2 );
10439
10440 QgsProcessingModelParameter alg5pc1;
10441 alg5pc1.setParameterName( QStringLiteral( "my_param" ) );
10442 alg5pc1.setPosition( QPointF( 11, 12 ) );
10443 alg5pc1.setSize( QSizeF( 21, 22 ) );
10444 alg5.addModelParameter( new QgsProcessingParameterBoolean( QStringLiteral( "my_param" ) ), alg5pc1 );
10445 alg5.setDesignerParameterValues( lastParams );
10446
10447 QDomDocument doc = QDomDocument( "model" );
10448 alg5.initAlgorithm();
10449 const QVariant v = alg5.toVariant();
10450 // make sure private parameters weren't included in the definition
10451 QVERIFY( !v.toMap().value( QStringLiteral( "parameterDefinitions" ) ).toMap().contains( QStringLiteral( "VERBOSE_LOG" ) ) );
10452
10453 const QDomElement elem = QgsXmlUtils::writeVariant( v, doc );
10454 doc.appendChild( elem );
10455
10456 QgsProcessingModelAlgorithm alg6;
10457 QVERIFY( alg6.loadVariant( QgsXmlUtils::readVariant( doc.firstChildElement() ) ) );
10458 QCOMPARE( alg6.name(), QStringLiteral( "test" ) );
10459 QCOMPARE( alg6.group(), QStringLiteral( "testGroup" ) );
10460 QCOMPARE( alg6.helpContent(), alg5.helpContent() );
10461 QCOMPARE( alg6.variables(), variables );
10462 QCOMPARE( alg6.designerParameterValues(), lastParams );
10463
10464 QCOMPARE( alg6.groupBoxes().size(), 1 );
10465 QCOMPARE( alg6.groupBoxes().at( 0 ).size(), QSizeF( 9, 8 ) );
10466 QCOMPARE( alg6.groupBoxes().at( 0 ).position(), QPointF( 11, 14 ) );
10467 QCOMPARE( alg6.groupBoxes().at( 0 ).description(), QStringLiteral( "a comment" ) );
10468 QCOMPARE( alg6.groupBoxes().at( 0 ).color(), QColor( 123, 45, 67 ) );
10469
10470 QgsProcessingModelChildAlgorithm alg6c1 = alg6.childAlgorithm( "cx1" );
10471 QCOMPARE( alg6c1.childId(), QStringLiteral( "cx1" ) );
10472 QCOMPARE( alg6c1.algorithmId(), QStringLiteral( "buffer" ) );
10473 QCOMPARE( alg6c1.configuration(), myConfig );
10474 QVERIFY( alg6c1.isActive() );
10475 QVERIFY( alg6c1.linksCollapsed( Qt::BottomEdge ) );
10476 QVERIFY( alg6c1.linksCollapsed( Qt::TopEdge ) );
10477 QCOMPARE( alg6c1.description(), QStringLiteral( "child 1" ) );
10478 QCOMPARE( alg6c1.position().x(), 1.0 );
10479 QCOMPARE( alg6c1.position().y(), 2.0 );
10480 QCOMPARE( alg6c1.size().width(), 11.0 );
10481 QCOMPARE( alg6c1.size().height(), 21.0 );
10482 QCOMPARE( alg6c1.parameterSources().count(), 5 );
10483 QCOMPARE( alg6c1.parameterSources().value( "x" ).at( 0 ).source(), QgsProcessingModelChildParameterSource::ModelParameter );
10484 QCOMPARE( alg6c1.parameterSources().value( "x" ).at( 0 ).parameterName(), QStringLiteral( "p1" ) );
10485 QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).source(), QgsProcessingModelChildParameterSource::ChildOutput );
10486 QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).outputChildId(), QStringLiteral( "cx2" ) );
10487 QCOMPARE( alg6c1.parameterSources().value( "y" ).at( 0 ).outputName(), QStringLiteral( "out3" ) );
10488 QCOMPARE( alg6c1.parameterSources().value( "z" ).at( 0 ).source(), QgsProcessingModelChildParameterSource::StaticValue );
10489 QCOMPARE( alg6c1.parameterSources().value( "z" ).at( 0 ).staticValue().toInt(), 5 );
10490 QCOMPARE( alg6c1.parameterSources().value( "a" ).at( 0 ).source(), QgsProcessingModelChildParameterSource::Expression );
10491 QCOMPARE( alg6c1.parameterSources().value( "a" ).at( 0 ).expression(), QStringLiteral( "2*2" ) );
10492 QCOMPARE( alg6c1.parameterSources().value( "zm" ).count(), 5 );
10493 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 0 ).source(), QgsProcessingModelChildParameterSource::StaticValue );
10494 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 0 ).staticValue().toInt(), 6 );
10495 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 1 ).source(), QgsProcessingModelChildParameterSource::ModelParameter );
10496 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 1 ).parameterName(), QStringLiteral( "p2" ) );
10497 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).source(), QgsProcessingModelChildParameterSource::ChildOutput );
10498 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).outputChildId(), QStringLiteral( "cx2" ) );
10499 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 2 ).outputName(), QStringLiteral( "out4" ) );
10500 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 3 ).source(), QgsProcessingModelChildParameterSource::Expression );
10501 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 3 ).expression(), QStringLiteral( "1+2" ) );
10502 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 4 ).source(), QgsProcessingModelChildParameterSource::StaticValue );
10503 QVERIFY( alg6c1.parameterSources().value( "zm" ).at( 4 ).staticValue().canConvert< QgsProperty >() );
10504 QCOMPARE( alg6c1.parameterSources().value( "zm" ).at( 4 ).staticValue().value< QgsProperty >().expressionString(), QStringLiteral( "1+8" ) );
10505
10506 QCOMPARE( alg6c1.modelOutputs().count(), 1 );
10507 QCOMPARE( alg6c1.modelOutputs().value( QStringLiteral( "a" ) ).description(), QStringLiteral( "my output" ) );
10508 QCOMPARE( alg6c1.modelOutput( "a" ).description(), QStringLiteral( "my output" ) );
10509 QCOMPARE( alg6c1.modelOutput( "a" ).position().x(), 3.0 );
10510 QCOMPARE( alg6c1.modelOutput( "a" ).position().y(), 4.0 );
10511 QCOMPARE( alg6c1.modelOutput( "a" ).size().width(), 31.0 );
10512 QCOMPARE( alg6c1.modelOutput( "a" ).size().height(), 41.0 );
10513
10514 const QgsProcessingModelChildAlgorithm alg6c2 = alg6.childAlgorithm( "cx2" );
10515 QCOMPARE( alg6c2.childId(), QStringLiteral( "cx2" ) );
10516 QVERIFY( !alg6c2.isActive() );
10517 QVERIFY( !alg6c2.linksCollapsed( Qt::BottomEdge ) );
10518 QVERIFY( !alg6c2.linksCollapsed( Qt::TopEdge ) );
10519 QCOMPARE( alg6c2.dependencies(), QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "a" ) << QgsProcessingModelChildDependency( "b" ) );
10520
10521 QCOMPARE( alg6.parameterComponents().count(), 1 );
10522 QCOMPARE( alg6.parameterComponents().value( QStringLiteral( "my_param" ) ).parameterName(), QStringLiteral( "my_param" ) );
10523 QCOMPARE( alg6.parameterComponent( "my_param" ).parameterName(), QStringLiteral( "my_param" ) );
10524 QCOMPARE( alg6.parameterComponent( "my_param" ).position().x(), 11.0 );
10525 QCOMPARE( alg6.parameterComponent( "my_param" ).position().y(), 12.0 );
10526 QCOMPARE( alg6.parameterComponent( "my_param" ).size().width(), 21.0 );
10527 QCOMPARE( alg6.parameterComponent( "my_param" ).size().height(), 22.0 );
10528 QCOMPARE( alg6.parameterDefinitions().count(), 1 );
10529 QCOMPARE( alg6.parameterDefinitions().at( 0 )->type(), QStringLiteral( "boolean" ) );
10530
10531 // destination parameters
10532 QgsProcessingModelAlgorithm alg7( "test", "testGroup" );
10533 QgsProcessingModelChildAlgorithm alg7c1;
10534 alg7c1.setChildId( "cx1" );
10535 alg7c1.setAlgorithmId( "native:centroids" );
10536 QMap<QString, QgsProcessingModelOutput> alg7c1outputs;
10537 QgsProcessingModelOutput alg7c1out1( QStringLiteral( "my_output" ) );
10538 alg7c1out1.setChildId( "cx1" );
10539 alg7c1out1.setChildOutputName( "OUTPUT" );
10540 alg7c1out1.setDescription( QStringLiteral( "my output" ) );
10541 alg7c1outputs.insert( QStringLiteral( "my_output" ), alg7c1out1 );
10542 alg7c1.setModelOutputs( alg7c1outputs );
10543 alg7.addChildAlgorithm( alg7c1 );
10544 // verify that model has destination parameter created
10545 QCOMPARE( alg7.destinationParameterDefinitions().count(), 1 );
10546 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->name(), QStringLiteral( "cx1:my_output" ) );
10547 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->description(), QStringLiteral( "my output" ) );
10548 QCOMPARE( static_cast< const QgsProcessingDestinationParameter * >( alg7.destinationParameterDefinitions().at( 0 ) )->originalProvider()->id(), QStringLiteral( "native" ) );
10549 QCOMPARE( alg7.outputDefinitions().count(), 1 );
10550 QCOMPARE( alg7.outputDefinitions().at( 0 )->name(), QStringLiteral( "cx1:my_output" ) );
10551 QCOMPARE( alg7.outputDefinitions().at( 0 )->type(), QStringLiteral( "outputVector" ) );
10552 QCOMPARE( alg7.outputDefinitions().at( 0 )->description(), QStringLiteral( "my output" ) );
10553
10554 QgsProcessingModelChildAlgorithm alg7c2;
10555 alg7c2.setChildId( "cx2" );
10556 alg7c2.setAlgorithmId( "native:centroids" );
10557 QMap<QString, QgsProcessingModelOutput> alg7c2outputs;
10558 QgsProcessingModelOutput alg7c2out1( QStringLiteral( "my_output2" ) );
10559 alg7c2out1.setChildId( "cx2" );
10560 alg7c2out1.setChildOutputName( "OUTPUT" );
10561 alg7c2out1.setDescription( QStringLiteral( "my output2" ) );
10562 alg7c2out1.setDefaultValue( QStringLiteral( "my value" ) );
10563 alg7c2out1.setMandatory( true );
10564 alg7c2outputs.insert( QStringLiteral( "my_output2" ), alg7c2out1 );
10565 alg7c2.setModelOutputs( alg7c2outputs );
10566 alg7.addChildAlgorithm( alg7c2 );
10567
10568 QCOMPARE( alg7.destinationParameterDefinitions().count(), 2 );
10569 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->name(), QStringLiteral( "cx1:my_output" ) );
10570 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->description(), QStringLiteral( "my output" ) );
10571 QVERIFY( alg7.destinationParameterDefinitions().at( 0 )->defaultValue().isNull() );
10572 QVERIFY( !( alg7.destinationParameterDefinitions().at( 0 )->flags() & QgsProcessingParameterDefinition::FlagOptional ) );
10573 QCOMPARE( alg7.destinationParameterDefinitions().at( 1 )->name(), QStringLiteral( "cx2:my_output2" ) );
10574 QCOMPARE( alg7.destinationParameterDefinitions().at( 1 )->description(), QStringLiteral( "my output2" ) );
10575 QCOMPARE( alg7.destinationParameterDefinitions().at( 1 )->defaultValue().toString(), QStringLiteral( "my value" ) );
10576 QVERIFY( !( alg7.destinationParameterDefinitions().at( 1 )->flags() & QgsProcessingParameterDefinition::FlagOptional ) );
10577 QCOMPARE( alg7.outputDefinitions().count(), 2 );
10578 QCOMPARE( alg7.outputDefinitions().at( 0 )->name(), QStringLiteral( "cx1:my_output" ) );
10579 QCOMPARE( alg7.outputDefinitions().at( 0 )->type(), QStringLiteral( "outputVector" ) );
10580 QCOMPARE( alg7.outputDefinitions().at( 0 )->description(), QStringLiteral( "my output" ) );
10581 QCOMPARE( alg7.outputDefinitions().at( 1 )->name(), QStringLiteral( "cx2:my_output2" ) );
10582 QCOMPARE( alg7.outputDefinitions().at( 1 )->type(), QStringLiteral( "outputVector" ) );
10583 QCOMPARE( alg7.outputDefinitions().at( 1 )->description(), QStringLiteral( "my output2" ) );
10584
10585 alg7.removeChildAlgorithm( "cx1" );
10586 QCOMPARE( alg7.destinationParameterDefinitions().count(), 1 );
10587 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->name(), QStringLiteral( "cx2:my_output2" ) );
10588 QCOMPARE( alg7.destinationParameterDefinitions().at( 0 )->description(), QStringLiteral( "my output2" ) );
10589 QCOMPARE( alg7.outputDefinitions().count(), 1 );
10590 QCOMPARE( alg7.outputDefinitions().at( 0 )->name(), QStringLiteral( "cx2:my_output2" ) );
10591 QCOMPARE( alg7.outputDefinitions().at( 0 )->type(), QStringLiteral( "outputVector" ) );
10592 QCOMPARE( alg7.outputDefinitions().at( 0 )->description(), QStringLiteral( "my output2" ) );
10593
10594 // mandatory model output with optional child algorithm parameter
10595 QgsProcessingModelChildAlgorithm alg7c3;
10596 alg7c3.setChildId( "cx3" );
10597 alg7c3.setAlgorithmId( "native:extractbyexpression" );
10598 QMap<QString, QgsProcessingModelOutput> alg7c3outputs;
10599 QgsProcessingModelOutput alg7c3out1;
10600 alg7c3out1.setChildId( "cx3" );
10601 alg7c3out1.setChildOutputName( "FAIL_OUTPUT" );
10602 alg7c3out1.setDescription( QStringLiteral( "my_output3" ) );
10603 alg7c3outputs.insert( QStringLiteral( "my_output3" ), alg7c3out1 );
10604 alg7c3.setModelOutputs( alg7c3outputs );
10605 alg7.addChildAlgorithm( alg7c3 );
10606 QVERIFY( alg7.destinationParameterDefinitions().at( 1 )->flags() & QgsProcessingParameterDefinition::FlagOptional );
10607 alg7.childAlgorithm( alg7c3.childId() ).modelOutput( QStringLiteral( "my_output3" ) ).setMandatory( true );
10608 alg7.updateDestinationParameters();
10609 QVERIFY( !( alg7.destinationParameterDefinitions().at( 1 )->flags() & QgsProcessingParameterDefinition::FlagOptional ) );
10610 }
10611
modelExecution()10612 void TestQgsProcessing::modelExecution()
10613 {
10614 // test childOutputIsRequired
10615 QgsProcessingModelAlgorithm model1;
10616 QgsProcessingModelChildAlgorithm algc1;
10617 algc1.setChildId( "cx1" );
10618 algc1.setAlgorithmId( "native:centroids" );
10619 model1.addChildAlgorithm( algc1 );
10620 QgsProcessingModelChildAlgorithm algc2;
10621 algc2.setChildId( "cx2" );
10622 algc2.setAlgorithmId( "native:centroids" );
10623 algc2.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "cx1", "p1" ) );
10624 model1.addChildAlgorithm( algc2 );
10625 QgsProcessingModelChildAlgorithm algc3;
10626 algc3.setChildId( "cx3" );
10627 algc3.setAlgorithmId( "native:centroids" );
10628 algc3.addParameterSources( "x", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "cx1", "p2" ) );
10629 algc3.setActive( false );
10630 model1.addChildAlgorithm( algc3 );
10631
10632 QVERIFY( model1.childOutputIsRequired( "cx1", "p1" ) ); // cx2 depends on p1
10633 QVERIFY( !model1.childOutputIsRequired( "cx1", "p2" ) ); // cx3 depends on p2, but cx3 is not active
10634 QVERIFY( !model1.childOutputIsRequired( "cx1", "p3" ) ); // nothing requires p3
10635 QVERIFY( !model1.childOutputIsRequired( "cx2", "p1" ) );
10636 QVERIFY( !model1.childOutputIsRequired( "cx3", "p1" ) );
10637
10638 // test parametersForChildAlgorithm
10639 QgsProcessingModelAlgorithm model2;
10640 QgsProcessingModelParameter sourceParam( "SOURCE_LAYER" );
10641 sourceParam.comment()->setDescription( QStringLiteral( "an input" ) );
10642 model2.addModelParameter( new QgsProcessingParameterFeatureSource( "SOURCE_LAYER" ), sourceParam );
10643 model2.addModelParameter( new QgsProcessingParameterNumber( "DIST", QString(), QgsProcessingParameterNumber::Double ), QgsProcessingModelParameter( "DIST" ) );
10644 QgsProcessingParameterCrs *p = new QgsProcessingParameterCrs( "CRS", QString(), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28355" ) ) );
10645 p->setFlags( p->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
10646 model2.addModelParameter( p, QgsProcessingModelParameter( "CRS" ) );
10647 QgsProcessingModelChildAlgorithm alg2c1;
10648 QgsExpressionContext expContext;
10649 QgsExpressionContextScope *scope = new QgsExpressionContextScope();
10650 scope->setVariable( "myvar", 8 );
10651 expContext.appendScope( scope );
10652 alg2c1.setChildId( "cx1" );
10653 alg2c1.setAlgorithmId( "native:buffer" );
10654 alg2c1.addParameterSources( "INPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "SOURCE_LAYER" ) );
10655 alg2c1.addParameterSources( "DISTANCE", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "DIST" ) );
10656 alg2c1.addParameterSources( "SEGMENTS", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromExpression( QStringLiteral( "@myvar*2" ) ) );
10657 alg2c1.addParameterSources( "END_CAP_STYLE", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 1 ) );
10658 alg2c1.addParameterSources( "JOIN_STYLE", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( 2 ) );
10659 alg2c1.addParameterSources( "DISSOLVE", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( false ) );
10660 QMap<QString, QgsProcessingModelOutput> outputs1;
10661 QgsProcessingModelOutput out1( "MODEL_OUT_LAYER" );
10662 out1.setChildOutputName( "OUTPUT" );
10663 outputs1.insert( QStringLiteral( "MODEL_OUT_LAYER" ), out1 );
10664 alg2c1.setModelOutputs( outputs1 );
10665 model2.addChildAlgorithm( alg2c1 );
10666
10667 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
10668 const QString vector = testDataDir + "points.shp";
10669
10670 QVariantMap modelInputs;
10671 modelInputs.insert( "SOURCE_LAYER", vector );
10672 modelInputs.insert( "DIST", 271 );
10673 modelInputs.insert( "cx1:MODEL_OUT_LAYER", "dest.shp" );
10674 QgsProcessingOutputLayerDefinition layerDef( "memory:" );
10675 layerDef.destinationName = "my_dest";
10676 modelInputs.insert( "cx3:MY_OUT", QVariant::fromValue( layerDef ) );
10677 QVariantMap childResults;
10678 QVariantMap params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx1" ), modelInputs, childResults, expContext );
10679 QCOMPARE( params.value( "DISSOLVE" ).toBool(), false );
10680 QCOMPARE( params.value( "DISTANCE" ).toInt(), 271 );
10681 QCOMPARE( params.value( "SEGMENTS" ).toInt(), 16 );
10682 QCOMPARE( params.value( "END_CAP_STYLE" ).toInt(), 1 );
10683 QCOMPARE( params.value( "JOIN_STYLE" ).toInt(), 2 );
10684 QCOMPARE( params.value( "INPUT" ).toString(), vector );
10685 QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "dest.shp" ) );
10686 QCOMPARE( params.count(), 7 );
10687
10688 QgsProcessingContext context;
10689
10690 // Check variables for child algorithm
10691 // without values
10692 QMap<QString, QgsProcessingModelAlgorithm::VariableDefinition> variables = model2.variablesForChildAlgorithm( "cx1", context );
10693 QCOMPARE( variables.count(), 7 );
10694 QCOMPARE( variables.value( "DIST" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10695 QCOMPARE( variables.value( "CRS" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10696 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10697 QCOMPARE( variables.value( "SOURCE_LAYER_minx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10698 QCOMPARE( variables.value( "SOURCE_LAYER_miny" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10699 QCOMPARE( variables.value( "SOURCE_LAYER_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10700 QCOMPARE( variables.value( "SOURCE_LAYER_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10701
10702 // with values
10703 variables = model2.variablesForChildAlgorithm( "cx1", context, modelInputs, childResults );
10704 QCOMPARE( variables.count(), 7 );
10705 QCOMPARE( variables.value( "DIST" ).value.toInt(), 271 );
10706 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.parameterName(), QString( "SOURCE_LAYER" ) );
10707 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_minx" ).value.toDouble(), -118.8888, 0.001 );
10708 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_miny" ).value.toDouble(), 22.8002, 0.001 );
10709 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxx" ).value.toDouble(), -83.3333, 0.001 );
10710 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxy" ).value.toDouble(), 46.8719, 0.001 );
10711
10712 std::unique_ptr< QgsExpressionContextScope > childScope( model2.createExpressionContextScopeForChildAlgorithm( "cx1", context, modelInputs, childResults ) );
10713 QCOMPARE( childScope->name(), QStringLiteral( "algorithm_inputs" ) );
10714 QCOMPARE( childScope->variableCount(), 7 );
10715 QCOMPARE( childScope->variable( "DIST" ).toInt(), 271 );
10716 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.parameterName(), QString( "SOURCE_LAYER" ) );
10717 QGSCOMPARENEAR( childScope->variable( "SOURCE_LAYER_minx" ).toDouble(), -118.8888, 0.001 );
10718 QGSCOMPARENEAR( childScope->variable( "SOURCE_LAYER_miny" ).toDouble(), 22.8002, 0.001 );
10719 QGSCOMPARENEAR( childScope->variable( "SOURCE_LAYER_maxx" ).toDouble(), -83.3333, 0.001 );
10720 QGSCOMPARENEAR( childScope->variable( "SOURCE_LAYER_maxy" ).toDouble(), 46.8719, 0.001 );
10721
10722
10723 QVariantMap results;
10724 results.insert( "OUTPUT", QStringLiteral( "dest.shp" ) );
10725 childResults.insert( "cx1", results );
10726
10727 // a child who uses an output from another alg as a parameter value
10728 QgsProcessingModelChildAlgorithm alg2c2;
10729 alg2c2.setChildId( "cx2" );
10730 alg2c2.setAlgorithmId( "native:centroids" );
10731 alg2c2.addParameterSources( "INPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "cx1", "OUTPUT" ) );
10732 model2.addChildAlgorithm( alg2c2 );
10733 params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx2" ), modelInputs, childResults, expContext );
10734 QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "dest.shp" ) );
10735 QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "memory:Centroids" ) );
10736 QCOMPARE( params.count(), 2 );
10737
10738 variables = model2.variablesForChildAlgorithm( "cx2", context );
10739 QCOMPARE( variables.count(), 12 );
10740 QCOMPARE( variables.value( "DIST" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10741 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10742 QCOMPARE( variables.value( "SOURCE_LAYER_minx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10743 QCOMPARE( variables.value( "SOURCE_LAYER_miny" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10744 QCOMPARE( variables.value( "SOURCE_LAYER_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10745 QCOMPARE( variables.value( "SOURCE_LAYER_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10746 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10747 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10748 QCOMPARE( variables.value( "cx1_OUTPUT_minx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10749 QCOMPARE( variables.value( "cx1_OUTPUT_minx" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10750 QCOMPARE( variables.value( "cx1_OUTPUT_miny" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10751 QCOMPARE( variables.value( "cx1_OUTPUT_miny" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10752 QCOMPARE( variables.value( "cx1_OUTPUT_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10753 QCOMPARE( variables.value( "cx1_OUTPUT_maxx" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10754 QCOMPARE( variables.value( "cx1_OUTPUT_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10755 QCOMPARE( variables.value( "cx1_OUTPUT_maxy" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10756
10757 // with values
10758 variables = model2.variablesForChildAlgorithm( "cx2", context, modelInputs, childResults );
10759 QCOMPARE( variables.count(), 12 );
10760 QCOMPARE( variables.value( "DIST" ).value.toInt(), 271 );
10761 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.parameterName(), QString( "SOURCE_LAYER" ) );
10762 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.outputChildId(), QString( "cx1" ) );
10763 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.parameterName(), QString( "" ) );
10764 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_minx" ).value.toDouble(), -118.8888, 0.001 );
10765 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_miny" ).value.toDouble(), 22.8002, 0.001 );
10766 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxx" ).value.toDouble(), -83.3333, 0.001 );
10767 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxy" ).value.toDouble(), 46.8719, 0.001 );
10768
10769 // a child with an optional output
10770 QgsProcessingModelChildAlgorithm alg2c3;
10771 alg2c3.setChildId( "cx3" );
10772 alg2c3.setAlgorithmId( "native:extractbyexpression" );
10773 alg2c3.addParameterSources( "INPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromChildOutput( "cx1", "OUTPUT" ) );
10774 alg2c3.addParameterSources( "EXPRESSION", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( "true" ) );
10775 alg2c3.addParameterSources( "OUTPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromModelParameter( "MY_OUT" ) );
10776 alg2c3.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "cx2" ) );
10777 QMap<QString, QgsProcessingModelOutput> outputs3;
10778 QgsProcessingModelOutput out2( "MY_OUT" );
10779 out2.setChildOutputName( "OUTPUT" );
10780 outputs3.insert( QStringLiteral( "MY_OUT" ), out2 );
10781 alg2c3.setModelOutputs( outputs3 );
10782
10783 model2.addChildAlgorithm( alg2c3 );
10784 params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx3" ), modelInputs, childResults, expContext );
10785 QCOMPARE( params.value( "INPUT" ).toString(), QStringLiteral( "dest.shp" ) );
10786 QCOMPARE( params.value( "EXPRESSION" ).toString(), QStringLiteral( "true" ) );
10787 QVERIFY( params.value( "OUTPUT" ).canConvert<QgsProcessingOutputLayerDefinition>() );
10788 const QgsProcessingOutputLayerDefinition outDef = qvariant_cast<QgsProcessingOutputLayerDefinition>( params.value( "OUTPUT" ) );
10789 QCOMPARE( outDef.destinationName, QStringLiteral( "MY_OUT" ) );
10790 QCOMPARE( outDef.sink.staticValue().toString(), QStringLiteral( "memory:" ) );
10791 QCOMPARE( params.count(), 3 ); // don't want FAIL_OUTPUT set!
10792
10793 // a child with an static output value
10794 QgsProcessingModelChildAlgorithm alg2c4;
10795 alg2c4.setChildId( "cx4" );
10796 alg2c4.setAlgorithmId( "native:extractbyexpression" );
10797 alg2c4.addParameterSources( "OUTPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromStaticValue( "STATIC" ) );
10798 model2.addChildAlgorithm( alg2c4 );
10799 params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx4" ), modelInputs, childResults, expContext );
10800 QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "STATIC" ) );
10801 model2.removeChildAlgorithm( "cx4" );
10802 // expression based output value
10803 alg2c4.addParameterSources( "OUTPUT", QgsProcessingModelChildParameterSources() << QgsProcessingModelChildParameterSource::fromExpression( "'A' || 'B'" ) );
10804 model2.addChildAlgorithm( alg2c4 );
10805 params = model2.parametersForChildAlgorithm( model2.childAlgorithm( "cx4" ), modelInputs, childResults, expContext );
10806 QCOMPARE( params.value( "OUTPUT" ).toString(), QStringLiteral( "AB" ) );
10807 model2.removeChildAlgorithm( "cx4" );
10808
10809
10810 variables = model2.variablesForChildAlgorithm( "cx3", context );
10811 QCOMPARE( variables.count(), 17 );
10812 QCOMPARE( variables.value( "DIST" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10813 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10814 QCOMPARE( variables.value( "SOURCE_LAYER_minx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10815 QCOMPARE( variables.value( "SOURCE_LAYER_miny" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10816 QCOMPARE( variables.value( "SOURCE_LAYER_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10817 QCOMPARE( variables.value( "SOURCE_LAYER_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ModelParameter );
10818 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10819 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10820 QCOMPARE( variables.value( "cx1_OUTPUT_minx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10821 QCOMPARE( variables.value( "cx1_OUTPUT_minx" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10822 QCOMPARE( variables.value( "cx1_OUTPUT_miny" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10823 QCOMPARE( variables.value( "cx1_OUTPUT_miny" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10824 QCOMPARE( variables.value( "cx1_OUTPUT_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10825 QCOMPARE( variables.value( "cx1_OUTPUT_maxx" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10826 QCOMPARE( variables.value( "cx1_OUTPUT_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10827 QCOMPARE( variables.value( "cx1_OUTPUT_maxy" ).source.outputChildId(), QStringLiteral( "cx1" ) );
10828 QCOMPARE( variables.value( "cx2_OUTPUT" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10829 QCOMPARE( variables.value( "cx2_OUTPUT" ).source.outputChildId(), QStringLiteral( "cx2" ) );
10830 QCOMPARE( variables.value( "cx2_OUTPUT_minx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10831 QCOMPARE( variables.value( "cx2_OUTPUT_minx" ).source.outputChildId(), QStringLiteral( "cx2" ) );
10832 QCOMPARE( variables.value( "cx2_OUTPUT_miny" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10833 QCOMPARE( variables.value( "cx2_OUTPUT_miny" ).source.outputChildId(), QStringLiteral( "cx2" ) );
10834 QCOMPARE( variables.value( "cx2_OUTPUT_maxx" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10835 QCOMPARE( variables.value( "cx2_OUTPUT_maxx" ).source.outputChildId(), QStringLiteral( "cx2" ) );
10836 QCOMPARE( variables.value( "cx2_OUTPUT_maxy" ).source.source(), QgsProcessingModelChildParameterSource::ChildOutput );
10837 QCOMPARE( variables.value( "cx2_OUTPUT_maxy" ).source.outputChildId(), QStringLiteral( "cx2" ) );
10838 // with values
10839 variables = model2.variablesForChildAlgorithm( "cx3", context, modelInputs, childResults );
10840 QCOMPARE( variables.count(), 17 );
10841 QCOMPARE( variables.value( "DIST" ).value.toInt(), 271 );
10842 QCOMPARE( variables.value( "SOURCE_LAYER" ).source.parameterName(), QString( "SOURCE_LAYER" ) );
10843 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.outputChildId(), QString( "cx1" ) );
10844 QCOMPARE( variables.value( "cx1_OUTPUT" ).source.parameterName(), QString( "" ) );
10845 QCOMPARE( variables.value( "cx2_OUTPUT" ).source.outputChildId(), QString( "cx2" ) );
10846 QCOMPARE( variables.value( "cx2_OUTPUT" ).source.parameterName(), QString( "" ) );
10847 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_minx" ).value.toDouble(), -118.8888, 0.001 );
10848 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_miny" ).value.toDouble(), 22.8002, 0.001 );
10849 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxx" ).value.toDouble(), -83.3333, 0.001 );
10850 QGSCOMPARENEAR( variables.value( "SOURCE_LAYER_maxy" ).value.toDouble(), 46.8719, 0.001 );
10851
10852 // test safe name of the child alg parameter as source to another algorithm
10853 // parameter name should have [\s ' ( ) : .] chars changed to "_" (regexp [\\s'\"\\(\\):\.])
10854 // this case is esecially important in case of grass algs where name algorithm contains "."
10855 // name of the variable is get from childDescription or childId. Refs https://github.com/qgis/QGIS/issues/36377
10856 QgsProcessingModelChildAlgorithm &cx1 = model2.childAlgorithm( "cx1" );
10857 const QString oldDescription = cx1.description();
10858 cx1.setDescription( "cx '():.1" );
10859 variables = model2.variablesForChildAlgorithm( "cx3", context );
10860 QVERIFY( !variables.contains( "cx1_OUTPUT" ) );
10861 QVERIFY( !variables.contains( "cx '():.1_OUTPUT" ) );
10862 QVERIFY( variables.contains( "cx______1_OUTPUT" ) );
10863 cx1.setDescription( oldDescription ); // set descrin back to avoid fail of following tests
10864
10865 // test model to python conversion
10866 model2.setName( QStringLiteral( "2my model" ) );
10867 model2.childAlgorithm( "cx1" ).modelOutput( QStringLiteral( "MODEL_OUT_LAYER" ) ).setDescription( "my model output" );
10868 model2.updateDestinationParameters();
10869 model2.childAlgorithm( "cx1" ).setDescription( "first step in my model" );
10870 const QStringList actualParts = model2.asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, 2 );
10871 QgsDebugMsg( actualParts.join( '\n' ) );
10872 const QStringList expectedParts = QStringLiteral( "\"\"\"\n"
10873 "Model exported as python.\n"
10874 "Name : 2my model\n"
10875 "Group : \n"
10876 "With QGIS : %1\n"
10877 "\"\"\"\n\n"
10878 "from qgis.core import QgsProcessing\n"
10879 "from qgis.core import QgsProcessingAlgorithm\n"
10880 "from qgis.core import QgsProcessingMultiStepFeedback\n"
10881 "from qgis.core import QgsProcessingParameterFeatureSource\n"
10882 "from qgis.core import QgsProcessingParameterNumber\n"
10883 "from qgis.core import QgsProcessingParameterCrs\n"
10884 "from qgis.core import QgsProcessingParameterFeatureSink\n"
10885 "from qgis.core import QgsProcessingParameterDefinition\n"
10886 "from qgis.core import QgsCoordinateReferenceSystem\n"
10887 "from qgis.core import QgsExpression\n"
10888 "import processing\n"
10889 "\n"
10890 "\n"
10891 "class MyModel(QgsProcessingAlgorithm):\n"
10892 "\n"
10893 " def initAlgorithm(self, config=None):\n"
10894 " # an input\n"
10895 " self.addParameter(QgsProcessingParameterFeatureSource('SOURCE_LAYER', '', defaultValue=None))\n"
10896 " self.addParameter(QgsProcessingParameterNumber('DIST', '', type=QgsProcessingParameterNumber.Double, defaultValue=None))\n"
10897 " param = QgsProcessingParameterCrs('CRS', '', defaultValue=QgsCoordinateReferenceSystem('EPSG:28355'))\n"
10898 " param.setFlags(param.flags() | QgsProcessingParameterDefinition.FlagAdvanced)\n"
10899 " self.addParameter(param)\n"
10900 " self.addParameter(QgsProcessingParameterFeatureSink('MyModelOutput', 'my model output', type=QgsProcessing.TypeVectorPolygon, createByDefault=True, supportsAppend=True, defaultValue=None))\n"
10901 " self.addParameter(QgsProcessingParameterFeatureSink('cx3:MY_OUT', '', type=QgsProcessing.TypeVectorAnyGeometry, createByDefault=True, defaultValue=None))\n"
10902 "\n"
10903 " def processAlgorithm(self, parameters, context, model_feedback):\n"
10904 " # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the\n"
10905 " # overall progress through the model\n"
10906 " feedback = QgsProcessingMultiStepFeedback(3, model_feedback)\n"
10907 " results = {}\n"
10908 " outputs = {}\n"
10909 "\n"
10910 " # first step in my model\n"
10911 " alg_params = {\n"
10912 " 'DISSOLVE': False,\n"
10913 " 'DISTANCE': parameters['DIST'],\n"
10914 " 'END_CAP_STYLE': 1, # Flat\n"
10915 " 'INPUT': parameters['SOURCE_LAYER'],\n"
10916 " 'JOIN_STYLE': 2, # Bevel\n"
10917 " 'SEGMENTS': QgsExpression('@myvar*2').evaluate(),\n"
10918 " 'OUTPUT': parameters['MyModelOutput']\n"
10919 " }\n"
10920 " outputs['FirstStepInMyModel'] = processing.run('native:buffer', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
10921 " results['MyModelOutput'] = outputs['FirstStepInMyModel']['OUTPUT']\n"
10922 "\n"
10923 " feedback.setCurrentStep(1)\n"
10924 " if feedback.isCanceled():\n"
10925 " return {}\n"
10926 "\n"
10927 " alg_params = {\n"
10928 " 'INPUT': outputs['FirstStepInMyModel']['OUTPUT'],\n"
10929 " 'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT\n"
10930 " }\n"
10931 " outputs['cx2'] = processing.run('native:centroids', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
10932 "\n"
10933 " feedback.setCurrentStep(2)\n"
10934 " if feedback.isCanceled():\n"
10935 " return {}\n"
10936 "\n"
10937 " alg_params = {\n"
10938 " 'EXPRESSION': 'true',\n"
10939 " 'INPUT': outputs['FirstStepInMyModel']['OUTPUT'],\n"
10940 " 'OUTPUT': parameters['MY_OUT'],\n"
10941 " 'OUTPUT': parameters['cx3:MY_OUT']\n"
10942 " }\n"
10943 " outputs['cx3'] = processing.run('native:extractbyexpression', alg_params, context=context, feedback=feedback, is_child_algorithm=True)\n"
10944 " results['cx3:MY_OUT'] = outputs['cx3']['OUTPUT']\n"
10945 " return results\n"
10946 "\n"
10947 " def name(self):\n"
10948 " return '2my model'\n"
10949 "\n"
10950 " def displayName(self):\n"
10951 " return '2my model'\n"
10952 "\n"
10953 " def group(self):\n"
10954 " return ''\n"
10955 "\n"
10956 " def groupId(self):\n"
10957 " return ''\n"
10958 "\n"
10959 " def createInstance(self):\n"
10960 " return MyModel()\n" ).arg( Qgis::versionInt() ).split( '\n' );
10961 QCOMPARE( actualParts, expectedParts );
10962 }
10963
modelBranchPruning()10964 void TestQgsProcessing::modelBranchPruning()
10965 {
10966 QgsVectorLayer *layer3111 = new QgsVectorLayer( "Point?crs=epsg:3111", "v1", "memory" );
10967 QgsProject p;
10968 p.addMapLayer( layer3111 );
10969
10970 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
10971 const QString raster1 = testDataDir + "landsat_4326.tif";
10972 const QFileInfo fi1( raster1 );
10973 QgsRasterLayer *r1 = new QgsRasterLayer( fi1.filePath(), "R1" );
10974 QVERIFY( r1->isValid() );
10975 p.addMapLayer( r1 );
10976
10977 QgsProcessingContext context;
10978 context.setProject( &p );
10979
10980 // test that model branches are trimmed for algorithms which return the FlagPruneModelBranchesBasedOnAlgorithmResults flag
10981 QgsProcessingModelAlgorithm model1;
10982
10983 // first add the filter by layer type alg
10984 QgsProcessingModelChildAlgorithm algc1;
10985 algc1.setChildId( "filter" );
10986 algc1.setAlgorithmId( "native:filterlayersbytype" );
10987 QgsProcessingModelParameter param;
10988 param.setParameterName( QStringLiteral( "LAYER" ) );
10989 model1.addModelParameter( new QgsProcessingParameterMapLayer( QStringLiteral( "LAYER" ) ), param );
10990 algc1.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromModelParameter( QStringLiteral( "LAYER" ) ) );
10991 model1.addChildAlgorithm( algc1 );
10992
10993 //then create some branches which come off this, depending on the layer type
10994 QgsProcessingModelChildAlgorithm algc2;
10995 algc2.setChildId( "buffer" );
10996 algc2.setAlgorithmId( "native:buffer" );
10997 algc2.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "filter" ), QStringLiteral( "VECTOR" ) ) );
10998 QMap<QString, QgsProcessingModelOutput> outputsc2;
10999 QgsProcessingModelOutput outc2( "BUFFER_OUTPUT" );
11000 outc2.setChildOutputName( "OUTPUT" );
11001 outputsc2.insert( QStringLiteral( "BUFFER_OUTPUT" ), outc2 );
11002 algc2.setModelOutputs( outputsc2 );
11003 model1.addChildAlgorithm( algc2 );
11004 // ...we want a complex branch, so add some more bits to the branch
11005 QgsProcessingModelChildAlgorithm algc3;
11006 algc3.setChildId( "buffer2" );
11007 algc3.setAlgorithmId( "native:buffer" );
11008 algc3.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "buffer" ), QStringLiteral( "OUTPUT" ) ) );
11009 QMap<QString, QgsProcessingModelOutput> outputsc3;
11010 QgsProcessingModelOutput outc3( "BUFFER2_OUTPUT" );
11011 outc3.setChildOutputName( "OUTPUT" );
11012 outputsc3.insert( QStringLiteral( "BUFFER2_OUTPUT" ), outc3 );
11013 algc3.setModelOutputs( outputsc3 );
11014 model1.addChildAlgorithm( algc3 );
11015 QgsProcessingModelChildAlgorithm algc4;
11016 algc4.setChildId( "buffer3" );
11017 algc4.setAlgorithmId( "native:buffer" );
11018 algc4.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "buffer" ), QStringLiteral( "OUTPUT" ) ) );
11019 QMap<QString, QgsProcessingModelOutput> outputsc4;
11020 QgsProcessingModelOutput outc4( "BUFFER3_OUTPUT" );
11021 outc4.setChildOutputName( "OUTPUT" );
11022 outputsc4.insert( QStringLiteral( "BUFFER3_OUTPUT" ), outc4 );
11023 algc4.setModelOutputs( outputsc4 );
11024 model1.addChildAlgorithm( algc4 );
11025
11026 // now add some bits to the raster branch
11027 QgsProcessingModelChildAlgorithm algr2;
11028 algr2.setChildId( "fill2" );
11029 algr2.setAlgorithmId( "native:fillnodata" );
11030 algr2.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "filter" ), QStringLiteral( "RASTER" ) ) );
11031 QMap<QString, QgsProcessingModelOutput> outputsr2;
11032 QgsProcessingModelOutput outr2( "RASTER_OUTPUT" );
11033 outr2.setChildOutputName( "OUTPUT" );
11034 outputsr2.insert( QStringLiteral( "RASTER_OUTPUT" ), outr2 );
11035 algr2.setModelOutputs( outputsr2 );
11036 model1.addChildAlgorithm( algr2 );
11037
11038 // some more bits on the raster branch
11039 QgsProcessingModelChildAlgorithm algr3;
11040 algr3.setChildId( "fill3" );
11041 algr3.setAlgorithmId( "native:fillnodata" );
11042 algr3.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "fill2" ), QStringLiteral( "OUTPUT" ) ) );
11043 QMap<QString, QgsProcessingModelOutput> outputsr3;
11044 QgsProcessingModelOutput outr3( "RASTER_OUTPUT2" );
11045 outr3.setChildOutputName( "OUTPUT" );
11046 outputsr3.insert( QStringLiteral( "RASTER_OUTPUT2" ), outr3 );
11047 algr3.setModelOutputs( outputsr3 );
11048 model1.addChildAlgorithm( algr3 );
11049
11050 QgsProcessingModelChildAlgorithm algr4;
11051 algr4.setChildId( "fill4" );
11052 algr4.setAlgorithmId( "native:fillnodata" );
11053 algr4.addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << QgsProcessingModelChildParameterSource::fromChildOutput( QStringLiteral( "fill2" ), QStringLiteral( "OUTPUT" ) ) );
11054 QMap<QString, QgsProcessingModelOutput> outputsr4;
11055 QgsProcessingModelOutput outr4( "RASTER_OUTPUT3" );
11056 outr4.setChildOutputName( "OUTPUT" );
11057 outputsr4.insert( QStringLiteral( "RASTER_OUTPUT3" ), outr4 );
11058 algr4.setModelOutputs( outputsr4 );
11059 model1.addChildAlgorithm( algr4 );
11060
11061 QgsProcessingFeedback feedback;
11062 QVariantMap params;
11063 // vector input
11064 params.insert( QStringLiteral( "LAYER" ), QStringLiteral( "v1" ) );
11065 params.insert( QStringLiteral( "buffer:BUFFER_OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
11066 params.insert( QStringLiteral( "buffer2:BUFFER2_OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
11067 params.insert( QStringLiteral( "buffer3:BUFFER3_OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
11068 params.insert( QStringLiteral( "fill2:RASTER_OUTPUT" ), QgsProcessing::TEMPORARY_OUTPUT );
11069 params.insert( QStringLiteral( "fill3:RASTER_OUTPUT2" ), QgsProcessing::TEMPORARY_OUTPUT );
11070 params.insert( QStringLiteral( "fill4:RASTER_OUTPUT3" ), QgsProcessing::TEMPORARY_OUTPUT );
11071 QVariantMap results = model1.run( params, context, &feedback );
11072 // we should get the vector branch outputs only
11073 QVERIFY( !results.value( QStringLiteral( "buffer:BUFFER_OUTPUT" ) ).toString().isEmpty() );
11074 QVERIFY( !results.value( QStringLiteral( "buffer2:BUFFER2_OUTPUT" ) ).toString().isEmpty() );
11075 QVERIFY( !results.value( QStringLiteral( "buffer3:BUFFER3_OUTPUT" ) ).toString().isEmpty() );
11076 QVERIFY( !results.contains( QStringLiteral( "fill2:RASTER_OUTPUT" ) ) );
11077 QVERIFY( !results.contains( QStringLiteral( "fill3:RASTER_OUTPUT2" ) ) );
11078 QVERIFY( !results.contains( QStringLiteral( "fill4:RASTER_OUTPUT3" ) ) );
11079
11080 // raster input
11081 params.insert( QStringLiteral( "LAYER" ), QStringLiteral( "R1" ) );
11082 results = model1.run( params, context, &feedback );
11083 // we should get the raster branch outputs only
11084 QVERIFY( !results.value( QStringLiteral( "fill2:RASTER_OUTPUT" ) ).toString().isEmpty() );
11085 QVERIFY( !results.value( QStringLiteral( "fill3:RASTER_OUTPUT2" ) ).toString().isEmpty() );
11086 QVERIFY( !results.value( QStringLiteral( "fill4:RASTER_OUTPUT3" ) ).toString().isEmpty() );
11087 QVERIFY( !results.contains( QStringLiteral( "buffer:BUFFER_OUTPUT" ) ) );
11088 QVERIFY( !results.contains( QStringLiteral( "buffer2:BUFFER2_OUTPUT" ) ) );
11089 QVERIFY( !results.contains( QStringLiteral( "buffer3:BUFFER3_OUTPUT" ) ) );
11090 }
11091
modelBranchPruningConditional()11092 void TestQgsProcessing::modelBranchPruningConditional()
11093 {
11094 QgsProcessingContext context;
11095
11096 context.expressionContext().appendScope( new QgsExpressionContextScope() );
11097 context.expressionContext().scope( 0 )->setVariable( QStringLiteral( "var1" ), 1 );
11098 context.expressionContext().scope( 0 )->setVariable( QStringLiteral( "var2" ), 0 );
11099
11100 // test that model branches are trimmed for algorithms which depend on conditional branches
11101 QgsProcessingModelAlgorithm model1;
11102
11103 // first add the filter by layer type alg
11104 QgsProcessingModelChildAlgorithm algc1;
11105 algc1.setChildId( "branch" );
11106 algc1.setAlgorithmId( "native:condition" );
11107 QVariantMap config;
11108 QVariantList conditions;
11109 QVariantMap cond1;
11110 cond1.insert( QStringLiteral( "name" ), QStringLiteral( "name1" ) );
11111 cond1.insert( QStringLiteral( "expression" ), QStringLiteral( "@var1" ) );
11112 conditions << cond1;
11113 QVariantMap cond2;
11114 cond2.insert( QStringLiteral( "name" ), QStringLiteral( "name2" ) );
11115 cond2.insert( QStringLiteral( "expression" ), QStringLiteral( "@var2" ) );
11116 conditions << cond2;
11117 config.insert( QStringLiteral( "conditions" ), conditions );
11118 algc1.setConfiguration( config );
11119 model1.addChildAlgorithm( algc1 );
11120
11121 //then create some branches which come off this
11122 QgsProcessingModelChildAlgorithm algc2;
11123 algc2.setChildId( "exception" );
11124 algc2.setAlgorithmId( "native:raiseexception" );
11125 algc2.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( QStringLiteral( "branch" ), QStringLiteral( "name1" ) ) );
11126 model1.addChildAlgorithm( algc2 );
11127
11128 QgsProcessingModelChildAlgorithm algc3;
11129 algc2.setChildId( "exception" );
11130 algc3.setAlgorithmId( "native:raisewarning" );
11131 algc3.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( QStringLiteral( "branch" ), QStringLiteral( "name2" ) ) );
11132 model1.addChildAlgorithm( algc3 );
11133
11134 QgsProcessingFeedback feedback;
11135 const QVariantMap params;
11136 bool ok = false;
11137 QVariantMap results = model1.run( params, context, &feedback, &ok );
11138 QVERIFY( !ok ); // the branch with the exception should be hit
11139
11140 // flip the condition results
11141 context.expressionContext().scope( 0 )->setVariable( QStringLiteral( "var1" ), 0 );
11142 context.expressionContext().scope( 0 )->setVariable( QStringLiteral( "var2" ), 1 );
11143
11144 results = model1.run( params, context, &feedback, &ok );
11145 QVERIFY( ok ); // the branch with the exception should NOT be hit
11146 }
11147
modelWithProviderWithLimitedTypes()11148 void TestQgsProcessing::modelWithProviderWithLimitedTypes()
11149 {
11150 QgsApplication::processingRegistry()->addProvider( new DummyProvider4() );
11151
11152 QgsProcessingModelAlgorithm alg( "test", "testGroup" );
11153 QgsProcessingModelChildAlgorithm algc1;
11154 algc1.setChildId( "cx1" );
11155 algc1.setAlgorithmId( "dummy4:alg1" );
11156 QMap<QString, QgsProcessingModelOutput> algc1outputs;
11157 QgsProcessingModelOutput algc1out1( QStringLiteral( "my_vector_output" ) );
11158 algc1out1.setChildId( "cx1" );
11159 algc1out1.setChildOutputName( "vector_dest" );
11160 algc1out1.setDescription( QStringLiteral( "my output" ) );
11161 algc1outputs.insert( QStringLiteral( "my_vector_output" ), algc1out1 );
11162 QgsProcessingModelOutput algc1out2( QStringLiteral( "my_raster_output" ) );
11163 algc1out2.setChildId( "cx1" );
11164 algc1out2.setChildOutputName( "raster_dest" );
11165 algc1out2.setDescription( QStringLiteral( "my output" ) );
11166 algc1outputs.insert( QStringLiteral( "my_raster_output" ), algc1out2 );
11167 QgsProcessingModelOutput algc1out3( QStringLiteral( "my_sink_output" ) );
11168 algc1out3.setChildId( "cx1" );
11169 algc1out3.setChildOutputName( "sink" );
11170 algc1out3.setDescription( QStringLiteral( "my output" ) );
11171 algc1outputs.insert( QStringLiteral( "my_sink_output" ), algc1out3 );
11172 algc1.setModelOutputs( algc1outputs );
11173 alg.addChildAlgorithm( algc1 );
11174 // verify that model has destination parameter created
11175 QCOMPARE( alg.destinationParameterDefinitions().count(), 3 );
11176 QCOMPARE( alg.destinationParameterDefinitions().at( 2 )->name(), QStringLiteral( "cx1:my_vector_output" ) );
11177 QCOMPARE( alg.destinationParameterDefinitions().at( 2 )->description(), QStringLiteral( "my output" ) );
11178 QCOMPARE( static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 2 ) )->originalProvider()->id(), QStringLiteral( "dummy4" ) );
11179 QCOMPARE( static_cast< const QgsProcessingParameterVectorDestination * >( alg.destinationParameterDefinitions().at( 2 ) )->supportedOutputVectorLayerExtensions(), QStringList() << QStringLiteral( "mif" ) );
11180 QCOMPARE( static_cast< const QgsProcessingParameterVectorDestination * >( alg.destinationParameterDefinitions().at( 2 ) )->defaultFileExtension(), QStringLiteral( "mif" ) );
11181 QVERIFY( static_cast< const QgsProcessingParameterVectorDestination * >( alg.destinationParameterDefinitions().at( 2 ) )->generateTemporaryDestination().endsWith( QLatin1String( ".mif" ) ) );
11182 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 2 ) )->supportsNonFileBasedOutput() );
11183
11184 QCOMPARE( alg.destinationParameterDefinitions().at( 0 )->name(), QStringLiteral( "cx1:my_raster_output" ) );
11185 QCOMPARE( alg.destinationParameterDefinitions().at( 0 )->description(), QStringLiteral( "my output" ) );
11186 QCOMPARE( static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 0 ) )->originalProvider()->id(), QStringLiteral( "dummy4" ) );
11187 QCOMPARE( static_cast< const QgsProcessingParameterRasterDestination * >( alg.destinationParameterDefinitions().at( 0 ) )->supportedOutputRasterLayerExtensions(), QStringList() << QStringLiteral( "mig" ) );
11188 QCOMPARE( static_cast< const QgsProcessingParameterRasterDestination * >( alg.destinationParameterDefinitions().at( 0 ) )->defaultFileExtension(), QStringLiteral( "mig" ) );
11189 QVERIFY( static_cast< const QgsProcessingParameterRasterDestination * >( alg.destinationParameterDefinitions().at( 0 ) )->generateTemporaryDestination().endsWith( QLatin1String( ".mig" ) ) );
11190 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 0 ) )->supportsNonFileBasedOutput() );
11191
11192 QCOMPARE( alg.destinationParameterDefinitions().at( 1 )->name(), QStringLiteral( "cx1:my_sink_output" ) );
11193 QCOMPARE( alg.destinationParameterDefinitions().at( 1 )->description(), QStringLiteral( "my output" ) );
11194 QCOMPARE( static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 1 ) )->originalProvider()->id(), QStringLiteral( "dummy4" ) );
11195 QCOMPARE( static_cast< const QgsProcessingParameterFeatureSink * >( alg.destinationParameterDefinitions().at( 1 ) )->supportedOutputVectorLayerExtensions(), QStringList() << QStringLiteral( "mif" ) );
11196 QCOMPARE( static_cast< const QgsProcessingParameterFeatureSink * >( alg.destinationParameterDefinitions().at( 1 ) )->defaultFileExtension(), QStringLiteral( "mif" ) );
11197 QVERIFY( static_cast< const QgsProcessingParameterFeatureSink * >( alg.destinationParameterDefinitions().at( 1 ) )->generateTemporaryDestination().endsWith( QLatin1String( ".mif" ) ) );
11198 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 1 ) )->supportsNonFileBasedOutput() );
11199 }
11200
modelVectorOutputIsCompatibleType()11201 void TestQgsProcessing::modelVectorOutputIsCompatibleType()
11202 {
11203 // IMPORTANT: This method is intended to be "permissive" rather than "restrictive".
11204 // I.e. we only reject outputs which we know can NEVER be acceptable, but
11205 // if there's doubt then we default to returning true.
11206
11207 // empty acceptable type list = all are compatible
11208 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeVector ) );
11209 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeVectorAnyGeometry ) );
11210 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeVectorPoint ) );
11211 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeVectorLine ) );
11212 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeVectorPolygon ) );
11213 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( QList<int>(), QgsProcessing::TypeMapLayer ) );
11214
11215 // accept any vector
11216 QList< int > dataTypes;
11217 dataTypes << QgsProcessing::TypeVector;
11218 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11219 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11220 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11221 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11222 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11223 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11224
11225 // accept any vector with geometry
11226 dataTypes.clear();
11227 dataTypes << QgsProcessing::TypeVectorAnyGeometry;
11228 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11229 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11230 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11231 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11232 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11233 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11234
11235 // accept any point vector
11236 dataTypes.clear();
11237 dataTypes << QgsProcessing::TypeVectorPoint;
11238 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11239 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11240 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11241 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11242 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11243 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11244
11245 // accept any line vector
11246 dataTypes.clear();
11247 dataTypes << QgsProcessing::TypeVectorLine;
11248 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11249 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11250 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11251 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11252 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11253 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11254
11255 // accept any polygon vector
11256 dataTypes.clear();
11257 dataTypes << QgsProcessing::TypeVectorPolygon;
11258 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11259 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11260 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11261 QVERIFY( !QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11262 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11263 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11264
11265 // accept any map layer
11266 dataTypes.clear();
11267 dataTypes << QgsProcessing::TypeMapLayer;
11268 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVector ) );
11269 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorAnyGeometry ) );
11270 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPoint ) );
11271 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorLine ) );
11272 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeVectorPolygon ) );
11273 QVERIFY( QgsProcessingModelAlgorithm::vectorOutputIsCompatibleType( dataTypes, QgsProcessing::TypeMapLayer ) );
11274 }
11275
modelAcceptableValues()11276 void TestQgsProcessing::modelAcceptableValues()
11277 {
11278 QgsProcessingModelAlgorithm m;
11279 const QgsProcessingModelParameter stringParam1( "string" );
11280 m.addModelParameter( new QgsProcessingParameterString( "string" ), stringParam1 );
11281
11282 const QgsProcessingModelParameter stringParam2( "string2" );
11283 m.addModelParameter( new QgsProcessingParameterString( "string2" ), stringParam2 );
11284
11285 const QgsProcessingModelParameter numParam( "number" );
11286 m.addModelParameter( new QgsProcessingParameterNumber( "number" ), numParam );
11287
11288 const QgsProcessingModelParameter tableFieldParam( "field" );
11289 m.addModelParameter( new QgsProcessingParameterField( "field" ), tableFieldParam );
11290
11291 const QgsProcessingModelParameter fileParam( "file" );
11292 m.addModelParameter( new QgsProcessingParameterFile( "file" ), fileParam );
11293
11294 // test single types
11295 QgsProcessingModelChildParameterSources sources = m.availableSourcesForChild( QString(), QStringList() << "number" );
11296 QCOMPARE( sources.count(), 1 );
11297 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "number" ) );
11298 sources = m.availableSourcesForChild( QString(), QStringList() << "field" );
11299 QCOMPARE( sources.count(), 1 );
11300 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "field" ) );
11301 sources = m.availableSourcesForChild( QString(), QStringList() << "file" );
11302 QCOMPARE( sources.count(), 1 );
11303 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "file" ) );
11304
11305 // test multiple types
11306 sources = m.availableSourcesForChild( QString(), QStringList() << "string" << "number" << "file" );
11307 QCOMPARE( sources.count(), 4 );
11308 QSet< QString > res;
11309 res << sources.at( 0 ).parameterName();
11310 res << sources.at( 1 ).parameterName();
11311 res << sources.at( 2 ).parameterName();
11312 res << sources.at( 3 ).parameterName();
11313
11314 QCOMPARE( res, QSet< QString >() << QStringLiteral( "string" )
11315 << QStringLiteral( "string2" )
11316 << QStringLiteral( "number" )
11317 << QStringLiteral( "file" ) );
11318
11319 // check outputs
11320 QgsProcessingModelChildAlgorithm alg2c1;
11321 alg2c1.setChildId( "cx1" );
11322 alg2c1.setAlgorithmId( "native:centroids" );
11323 m.addChildAlgorithm( alg2c1 );
11324
11325 sources = m.availableSourcesForChild( QString(), QStringList(), QStringList() << "string" << "outputVector" );
11326 QCOMPARE( sources.count(), 1 );
11327 res.clear();
11328 res << sources.at( 0 ).outputChildId() + ':' + sources.at( 0 ).outputName();
11329 QCOMPARE( res, QSet< QString >() << "cx1:OUTPUT" );
11330
11331 // with dependencies between child algs
11332 QgsProcessingModelChildAlgorithm alg2c2;
11333 alg2c2.setChildId( "cx2" );
11334 alg2c2.setAlgorithmId( "native:centroids" );
11335 alg2c2.setDependencies( QList< QgsProcessingModelChildDependency >() << QgsProcessingModelChildDependency( "cx1" ) );
11336 m.addChildAlgorithm( alg2c2 );
11337 sources = m.availableSourcesForChild( QString(), QStringList(), QStringList() << "string" << "outputVector" );
11338 QCOMPARE( sources.count(), 2 );
11339 res.clear();
11340 res << sources.at( 0 ).outputChildId() + ':' + sources.at( 0 ).outputName();
11341 res << sources.at( 1 ).outputChildId() + ':' + sources.at( 1 ).outputName();
11342 QCOMPARE( res, QSet< QString >() << "cx1:OUTPUT" << "cx2:OUTPUT" );
11343
11344 sources = m.availableSourcesForChild( QStringLiteral( "cx1" ), QStringList(), QStringList() << "string" << "outputVector" );
11345 QCOMPARE( sources.count(), 0 );
11346
11347 sources = m.availableSourcesForChild( QString( "cx2" ), QStringList(), QStringList() << "string" << "outputVector" );
11348 QCOMPARE( sources.count(), 1 );
11349 res.clear();
11350 res << sources.at( 0 ).outputChildId() + ':' + sources.at( 0 ).outputName();
11351 QCOMPARE( res, QSet< QString >() << "cx1:OUTPUT" );
11352
11353 // test limiting by data types
11354 QgsProcessingModelAlgorithm m2;
11355 const QgsProcessingModelParameter vlInput( "vl" );
11356 // with no limit on data types
11357 m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl" ), vlInput );
11358 const QgsProcessingModelParameter fsInput( "fs" );
11359 m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs" ), fsInput );
11360
11361 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" );
11362 QCOMPARE( sources.count(), 2 );
11363 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11364 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11365 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorPoint );
11366 QCOMPARE( sources.count(), 2 );
11367 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11368 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11369 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVector );
11370 QCOMPARE( sources.count(), 2 );
11371 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11372 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11373 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry );
11374 QCOMPARE( sources.count(), 2 );
11375 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11376 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11377 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeMapLayer );
11378 QCOMPARE( sources.count(), 2 );
11379 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11380 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11381
11382 // inputs are limited to vector layers
11383 m2.removeModelParameter( vlInput.parameterName() );
11384 m2.removeModelParameter( fsInput.parameterName() );
11385 m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList<int>() << QgsProcessing::TypeVector ), vlInput );
11386 m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList<int>() << QgsProcessing::TypeVector ), fsInput );
11387 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" );
11388 QCOMPARE( sources.count(), 2 );
11389 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11390 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11391 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorPoint );
11392 QCOMPARE( sources.count(), 2 );
11393 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11394 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11395 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVector );
11396 QCOMPARE( sources.count(), 2 );
11397 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11398 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11399 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry );
11400 QCOMPARE( sources.count(), 2 );
11401 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11402 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11403 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeMapLayer );
11404 QCOMPARE( sources.count(), 2 );
11405 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11406 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11407
11408 // inputs are limited to vector layers with geometries
11409 m2.removeModelParameter( vlInput.parameterName() );
11410 m2.removeModelParameter( fsInput.parameterName() );
11411 m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry ), vlInput );
11412 m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry ), fsInput );
11413 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" );
11414 QCOMPARE( sources.count(), 2 );
11415 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11416 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11417 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorPoint );
11418 QCOMPARE( sources.count(), 2 );
11419 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11420 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11421 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVector );
11422 QCOMPARE( sources.count(), 2 );
11423 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11424 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11425 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry );
11426 QCOMPARE( sources.count(), 2 );
11427 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11428 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11429 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeMapLayer );
11430 QCOMPARE( sources.count(), 2 );
11431 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11432 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11433
11434 // inputs are limited to vector layers with lines
11435 m2.removeModelParameter( vlInput.parameterName() );
11436 m2.removeModelParameter( fsInput.parameterName() );
11437 m2.addModelParameter( new QgsProcessingParameterVectorLayer( "vl", QString(), QList<int>() << QgsProcessing::TypeVectorLine ), vlInput );
11438 m2.addModelParameter( new QgsProcessingParameterFeatureSource( "fs", QString(), QList<int>() << QgsProcessing::TypeVectorLine ), fsInput );
11439 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source" );
11440 QCOMPARE( sources.count(), 2 );
11441 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11442 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11443 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorPoint );
11444 QCOMPARE( sources.count(), 0 );
11445 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorPolygon );
11446 QCOMPARE( sources.count(), 0 );
11447 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorLine );
11448 QCOMPARE( sources.count(), 2 );
11449 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11450 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11451 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVector );
11452 QCOMPARE( sources.count(), 2 );
11453 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11454 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11455 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeVectorAnyGeometry );
11456 QCOMPARE( sources.count(), 2 );
11457 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11458 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11459 sources = m2.availableSourcesForChild( QString(), QStringList() << "vector" << "source", QStringList(), QList<int>() << QgsProcessing::TypeMapLayer );
11460 QCOMPARE( sources.count(), 2 );
11461 QCOMPARE( sources.at( 0 ).parameterName(), QStringLiteral( "fs" ) );
11462 QCOMPARE( sources.at( 1 ).parameterName(), QStringLiteral( "vl" ) );
11463 }
11464
modelValidate()11465 void TestQgsProcessing::modelValidate()
11466 {
11467 QgsProcessingModelAlgorithm m;
11468 QStringList errors;
11469 QVERIFY( !m.validate( errors ) );
11470 QCOMPARE( errors.size(), 1 );
11471 QCOMPARE( errors.at( 0 ), QStringLiteral( "Model does not contain any algorithms" ) );
11472
11473 const QgsProcessingModelParameter stringParam1( "string" );
11474 m.addModelParameter( new QgsProcessingParameterString( "string" ), stringParam1 );
11475 QgsProcessingModelChildAlgorithm alg2c1;
11476 alg2c1.setChildId( "cx1" );
11477 alg2c1.setAlgorithmId( "native:centroids" );
11478 alg2c1.setDescription( QStringLiteral( "centroids" ) );
11479 m.addChildAlgorithm( alg2c1 );
11480
11481 QVERIFY( !m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11482 QCOMPARE( errors.size(), 2 );
11483 QCOMPARE( errors.at( 0 ), QStringLiteral( "Parameter <i>INPUT</i> is mandatory" ) );
11484 QCOMPARE( errors.at( 1 ), QStringLiteral( "Parameter <i>ALL_PARTS</i> is mandatory" ) );
11485
11486 QVERIFY( !m.validate( errors ) );
11487 QCOMPARE( errors.size(), 2 );
11488 QCOMPARE( errors.at( 0 ), QStringLiteral( "<b>centroids</b>: Parameter <i>INPUT</i> is mandatory" ) );
11489 QCOMPARE( errors.at( 1 ), QStringLiteral( "<b>centroids</b>: Parameter <i>ALL_PARTS</i> is mandatory" ) );
11490
11491 QgsProcessingModelChildParameterSource badSource;
11492 badSource.setSource( QgsProcessingModelChildParameterSource::StaticValue );
11493 badSource.setStaticValue( 56 );
11494 m.childAlgorithm( QStringLiteral( "cx1" ) ).addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << badSource );
11495
11496 QVERIFY( !m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11497 QCOMPARE( errors.size(), 2 );
11498 QCOMPARE( errors.at( 0 ), QStringLiteral( "Value for <i>INPUT</i> is not acceptable for this parameter" ) );
11499 QCOMPARE( errors.at( 1 ), QStringLiteral( "Parameter <i>ALL_PARTS</i> is mandatory" ) );
11500
11501 QgsProcessingModelChildParameterSource goodSource;
11502 goodSource.setSource( QgsProcessingModelChildParameterSource::Expression );
11503 m.childAlgorithm( QStringLiteral( "cx1" ) ).addParameterSources( QStringLiteral( "ALL_PARTS" ), QList< QgsProcessingModelChildParameterSource >() << goodSource );
11504
11505 QVERIFY( !m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11506 QCOMPARE( errors.size(), 1 );
11507 QCOMPARE( errors.at( 0 ), QStringLiteral( "Value for <i>INPUT</i> is not acceptable for this parameter" ) );
11508
11509 badSource.setSource( QgsProcessingModelChildParameterSource::ChildOutput );
11510 badSource.setOutputChildId( QStringLiteral( "cc" ) );
11511 m.childAlgorithm( QStringLiteral( "cx1" ) ).addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << badSource );
11512
11513 QVERIFY( !m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11514 QCOMPARE( errors.size(), 1 );
11515 QCOMPARE( errors.at( 0 ), QStringLiteral( "Child algorithm <i>cc</i> used for parameter <i>INPUT</i> does not exist" ) );
11516
11517 badSource.setSource( QgsProcessingModelChildParameterSource::ModelParameter );
11518 badSource.setParameterName( QStringLiteral( "cc" ) );
11519 m.childAlgorithm( QStringLiteral( "cx1" ) ).addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << badSource );
11520
11521 QVERIFY( !m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11522 QCOMPARE( errors.size(), 1 );
11523 QCOMPARE( errors.at( 0 ), QStringLiteral( "Model input <i>cc</i> used for parameter <i>INPUT</i> does not exist" ) );
11524
11525 goodSource.setSource( QgsProcessingModelChildParameterSource::StaticValue );
11526 goodSource.setStaticValue( QString( QStringLiteral( TEST_DATA_DIR ) + "/polys.shp" ) );
11527 m.childAlgorithm( QStringLiteral( "cx1" ) ).addParameterSources( QStringLiteral( "INPUT" ), QList< QgsProcessingModelChildParameterSource >() << goodSource );
11528
11529 QVERIFY( m.validateChildAlgorithm( QStringLiteral( "cx1" ), errors ) );
11530 QCOMPARE( errors.size(), 0 );
11531
11532 QVERIFY( m.validate( errors ) );
11533 QCOMPARE( errors.size(), 0 );
11534 }
11535
modelInputs()11536 void TestQgsProcessing::modelInputs()
11537 {
11538 QgsProcessingModelAlgorithm m;
11539
11540 // add a bunch of inputs
11541 const QgsProcessingModelParameter stringParam1( "string" );
11542 m.addModelParameter( new QgsProcessingParameterString( "string" ), stringParam1 );
11543
11544 const QgsProcessingModelParameter stringParam2( "a string" );
11545 m.addModelParameter( new QgsProcessingParameterString( "a string" ), stringParam2 );
11546
11547 const QgsProcessingModelParameter stringParam3( "cc string" );
11548 m.addModelParameter( new QgsProcessingParameterString( "cc string" ), stringParam3 );
11549
11550 // set specific input order for parameters
11551 m.setParameterOrder( QStringList() << "cc string" << "a string" );
11552
11553 QgsProcessingModelAlgorithm m2;
11554 m2.loadVariant( m.toVariant() );
11555 QCOMPARE( m2.orderedParameters().count(), 3 );
11556 QCOMPARE( m2.orderedParameters().at( 0 ).parameterName(), QStringLiteral( "cc string" ) );
11557 QCOMPARE( m2.orderedParameters().at( 1 ).parameterName(), QStringLiteral( "a string" ) );
11558 QCOMPARE( m2.orderedParameters().at( 2 ).parameterName(), QStringLiteral( "string" ) );
11559
11560 QCOMPARE( m2.parameterDefinitions().at( 0 )->name(), QStringLiteral( "cc string" ) );
11561 QCOMPARE( m2.parameterDefinitions().at( 1 )->name(), QStringLiteral( "a string" ) );
11562 QCOMPARE( m2.parameterDefinitions().at( 2 )->name(), QStringLiteral( "string" ) );
11563 }
11564
modelDependencies()11565 void TestQgsProcessing::modelDependencies()
11566 {
11567 const QgsProcessingModelChildDependency dep( QStringLiteral( "childId" ), QStringLiteral( "branch" ) );
11568
11569 QCOMPARE( dep.childId, QStringLiteral( "childId" ) );
11570 QCOMPARE( dep.conditionalBranch, QStringLiteral( "branch" ) );
11571
11572 const QVariant v = dep.toVariant();
11573 QgsProcessingModelChildDependency dep2;
11574 QVERIFY( dep2.loadVariant( v.toMap() ) );
11575
11576 QCOMPARE( dep2.childId, QStringLiteral( "childId" ) );
11577 QCOMPARE( dep2.conditionalBranch, QStringLiteral( "branch" ) );
11578
11579 QVERIFY( dep == dep2 );
11580 QVERIFY( !( dep != dep2 ) );
11581 dep2.conditionalBranch = QStringLiteral( "b" );
11582
11583 QVERIFY( !( dep == dep2 ) );
11584 QVERIFY( dep != dep2 );
11585 dep2.conditionalBranch = QStringLiteral( "branch" );
11586 dep2.childId = QStringLiteral( "c" );
11587 QVERIFY( !( dep == dep2 ) );
11588 QVERIFY( dep != dep2 );
11589 dep2.childId = QStringLiteral( "childId" );
11590 QVERIFY( dep == dep2 );
11591 QVERIFY( !( dep != dep2 ) );
11592 }
11593
tempUtils()11594 void TestQgsProcessing::tempUtils()
11595 {
11596 QString tempFolder = QgsProcessingUtils::tempFolder();
11597 // tempFolder should remain constant for session
11598 QCOMPARE( QgsProcessingUtils::tempFolder(), tempFolder );
11599
11600 const QString tempFile1 = QgsProcessingUtils::generateTempFilename( "test.txt" );
11601 QVERIFY( tempFile1.endsWith( "test.txt" ) );
11602 QVERIFY( tempFile1.startsWith( tempFolder ) );
11603
11604 // expect a different file
11605 const QString tempFile2 = QgsProcessingUtils::generateTempFilename( "test.txt" );
11606 QVERIFY( tempFile1 != tempFile2 );
11607 QVERIFY( tempFile2.endsWith( "test.txt" ) );
11608 QVERIFY( tempFile2.startsWith( tempFolder ) );
11609
11610 // invalid characters
11611 const QString tempFile3 = QgsProcessingUtils::generateTempFilename( "mybad:file.txt" );
11612 QVERIFY( tempFile3.endsWith( "mybad_file.txt" ) );
11613 QVERIFY( tempFile3.startsWith( tempFolder ) );
11614
11615 // change temp folder in the settings
11616 std::unique_ptr< QTemporaryDir > dir = std::make_unique< QTemporaryDir >();
11617 const QString tempDirPath = dir->path();
11618 dir.reset();
11619
11620 const QgsSettings settings;
11621 const QString alternative_tempFolder1 = tempDirPath + QStringLiteral( "/alternative_temp_test_one" );
11622 QgsProcessing::settingsTempPath.setValue( alternative_tempFolder1 );
11623 // check folder and if it's constant with alternative temp folder 1
11624 tempFolder = QgsProcessingUtils::tempFolder();
11625 QCOMPARE( tempFolder.left( alternative_tempFolder1.length() ), alternative_tempFolder1 );
11626 QCOMPARE( QgsProcessingUtils::tempFolder(), tempFolder );
11627 // create file
11628 const QString alternativeTempFile1 = QgsProcessingUtils::generateTempFilename( "alternative_temptest.txt" );
11629 QVERIFY( alternativeTempFile1.endsWith( "alternative_temptest.txt" ) );
11630 QVERIFY( alternativeTempFile1.startsWith( tempFolder ) );
11631 QVERIFY( alternativeTempFile1.startsWith( alternative_tempFolder1 ) );
11632 // change temp folder in the settings again
11633 const QString alternative_tempFolder2 = tempDirPath + QStringLiteral( "/alternative_temp_test_two" );
11634 QgsProcessing::settingsTempPath.setValue( alternative_tempFolder2 );
11635 // check folder and if it's constant constant with alternative temp folder 2
11636 tempFolder = QgsProcessingUtils::tempFolder();
11637 QCOMPARE( tempFolder.left( alternative_tempFolder2.length() ), alternative_tempFolder2 );
11638 QCOMPARE( QgsProcessingUtils::tempFolder(), tempFolder );
11639 // create file
11640 const QString alternativeTempFile2 = QgsProcessingUtils::generateTempFilename( "alternative_temptest.txt" );
11641 QVERIFY( alternativeTempFile2.endsWith( "alternative_temptest.txt" ) );
11642 QVERIFY( alternativeTempFile2.startsWith( tempFolder ) );
11643 QVERIFY( alternativeTempFile2.startsWith( alternative_tempFolder2 ) );
11644 QgsProcessing::settingsTempPath.setValue( QString() );
11645
11646 }
11647
convertCompatible()11648 void TestQgsProcessing::convertCompatible()
11649 {
11650 // start with a compatible shapefile
11651 const QString testDataDir = QStringLiteral( TEST_DATA_DIR ) + '/'; //defined in CmakeLists.txt
11652 const QString vector = testDataDir + "points.shp";
11653 QgsVectorLayer *layer = new QgsVectorLayer( vector, "vl" );
11654 QgsProject p;
11655 p.addMapLayer( layer );
11656
11657 QgsProcessingContext context;
11658 context.setProject( &p );
11659 QgsProcessingFeedback feedback;
11660 QString out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11661 // layer should be returned unchanged - underlying source is compatible
11662 QCOMPARE( out, layer->source() );
11663
11664 QString layerName;
11665 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11666 // layer should be returned unchanged - underlying source is compatible
11667 QCOMPARE( out, layer->source() );
11668 QCOMPARE( layerName, QString() );
11669
11670 // path with layer suffix
11671 const QString vectorWithLayer = testDataDir + "points.shp|layername=points";
11672 QgsVectorLayer *layer2 = new QgsVectorLayer( vectorWithLayer, "vl" );
11673 p.addMapLayer( layer2 );
11674 out = QgsProcessingUtils::convertToCompatibleFormat( layer2, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11675 // layer should be returned unchanged - underlying source is compatible
11676 QCOMPARE( out, vector );
11677 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer2, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11678 QCOMPARE( out, vector );
11679 QCOMPARE( layerName, QStringLiteral( "points" ) );
11680
11681 // don't include shp as compatible type
11682 out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
11683 QVERIFY( out != layer->source() );
11684 QVERIFY( out.endsWith( ".tab" ) );
11685 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11686
11687 // make sure all features are copied
11688 std::unique_ptr< QgsVectorLayer > t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11689 QCOMPARE( layer->featureCount(), t->featureCount() );
11690 QCOMPARE( layer->crs().authid(), QStringLiteral( "EPSG:4326" ) );
11691
11692 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test2" ), QStringList() << "tab", QString( "tab" ), context, &feedback, layerName );
11693 QVERIFY( out != layer->source() );
11694 QVERIFY( out.endsWith( ".tab" ) );
11695 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11696 QCOMPARE( layerName, QString() );
11697
11698 // use a selection - this will require translation
11699 QgsFeatureIds ids;
11700 QgsFeature f;
11701 QgsFeatureIterator it = layer->getFeatures();
11702 it.nextFeature( f );
11703 ids.insert( f.id() );
11704 it.nextFeature( f );
11705 ids.insert( f.id() );
11706
11707 layer->selectByIds( ids );
11708 out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback );
11709 QVERIFY( out != layer->source() );
11710 QVERIFY( out.endsWith( ".tab" ) );
11711 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11712 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11713 QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
11714
11715 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, true, QStringLiteral( "test" ), QStringList() << "tab", QString( "tab" ), context, &feedback, layerName );
11716 QVERIFY( out != layer->source() );
11717 QVERIFY( out.endsWith( ".tab" ) );
11718 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11719 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11720 QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
11721 QCOMPARE( layerName, QString() );
11722
11723 // using a selection but existing format - will still require translation
11724 out = QgsProcessingUtils::convertToCompatibleFormat( layer, true, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11725 QVERIFY( out != layer->source() );
11726 QVERIFY( out.endsWith( ".shp" ) );
11727 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11728 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11729 QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
11730
11731 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, true, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11732 QVERIFY( out != layer->source() );
11733 QVERIFY( out.endsWith( ".shp" ) );
11734 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11735 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11736 QCOMPARE( t->featureCount(), static_cast< long >( ids.count() ) );
11737 QCOMPARE( layerName, QString() );
11738
11739 // using a feature filter -- this will require translation
11740 layer->setSubsetString( "1 or 2" );
11741 out = QgsProcessingUtils::convertToCompatibleFormat( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11742 QVERIFY( out != layer->source() );
11743 QVERIFY( out.endsWith( ".shp" ) );
11744 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11745 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11746 QCOMPARE( t->featureCount(), layer->featureCount() );
11747
11748 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( layer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11749 QVERIFY( out != layer->source() );
11750 QVERIFY( out.endsWith( ".shp" ) );
11751 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11752 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11753 QCOMPARE( t->featureCount(), layer->featureCount() );
11754 QCOMPARE( layerName, QString() );
11755 layer->setSubsetString( QString() );
11756
11757 // using GDAL's virtual I/O (/vsizip/, etc.)
11758 const QString vsiPath = "/vsizip/" + testDataDir + "zip/points2.zip/points.shp";
11759 QgsVectorLayer *vsiLayer = new QgsVectorLayer( vsiPath, "vl" );
11760 p.addMapLayer( vsiLayer );
11761 out = QgsProcessingUtils::convertToCompatibleFormat( vsiLayer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11762 QVERIFY( out != layer->source() );
11763 QVERIFY( out.endsWith( ".shp" ) );
11764 QVERIFY( !out.contains( "/vsizip" ) );
11765 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11766 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11767 QCOMPARE( t->featureCount(), layer->featureCount() );
11768
11769 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( vsiLayer, false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11770 QVERIFY( out != layer->source() );
11771 QVERIFY( out.endsWith( ".shp" ) );
11772 QVERIFY( !out.contains( "/vsizip" ) );
11773 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11774 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11775 QCOMPARE( t->featureCount(), layer->featureCount() );
11776 QCOMPARE( layerName, QString() );
11777
11778 // non-OGR source -- must be translated, regardless of extension. (e.g. delimited text provider handles CSV very different to OGR!)
11779 std::unique_ptr< QgsVectorLayer > memLayer = std::make_unique< QgsVectorLayer> ( "Point", "v1", "memory" );
11780 for ( int i = 1; i < 6; ++i )
11781 {
11782 QgsFeature f( i );
11783 f.setGeometry( QgsGeometry( new QgsPoint( 1, 2 ) ) );
11784 memLayer->dataProvider()->addFeatures( QgsFeatureList() << f );
11785 }
11786 out = QgsProcessingUtils::convertToCompatibleFormat( memLayer.get(), false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback );
11787 QVERIFY( out != memLayer->source() );
11788 QVERIFY( out.endsWith( ".shp" ) );
11789 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11790 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11791 QCOMPARE( t->featureCount(), memLayer->featureCount() );
11792
11793 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( memLayer.get(), false, QStringLiteral( "test" ), QStringList() << "shp", QString( "shp" ), context, &feedback, layerName );
11794 QVERIFY( out != memLayer->source() );
11795 QVERIFY( out.endsWith( ".shp" ) );
11796 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11797 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11798 QCOMPARE( t->featureCount(), memLayer->featureCount() );
11799 QCOMPARE( layerName, QString() );
11800
11801 //delimited text -- must be translated, regardless of extension. (delimited text provider handles CSV very different to OGR!)
11802 const QString csvPath = "file://" + testDataDir + "delimitedtext/testpt.csv?type=csv&useHeader=No&detectTypes=yes&xyDms=yes&geomType=none&subsetIndex=no&watchFile=no";
11803 std::unique_ptr< QgsVectorLayer > csvLayer = std::make_unique< QgsVectorLayer >( csvPath, "vl", "delimitedtext" );
11804 QVERIFY( csvLayer->isValid() );
11805 out = QgsProcessingUtils::convertToCompatibleFormat( csvLayer.get(), false, QStringLiteral( "test" ), QStringList() << "csv", QString( "csv" ), context, &feedback );
11806 QVERIFY( out != csvLayer->source() );
11807 QVERIFY( out.endsWith( ".csv" ) );
11808 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11809 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11810 QCOMPARE( t->featureCount(), csvLayer->featureCount() );
11811
11812 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( csvLayer.get(), false, QStringLiteral( "test" ), QStringList() << "csv", QString( "csv" ), context, &feedback, layerName );
11813 QVERIFY( out != csvLayer->source() );
11814 QVERIFY( out.endsWith( ".csv" ) );
11815 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11816 t = std::make_unique< QgsVectorLayer >( out, "vl2" );
11817 QCOMPARE( t->featureCount(), csvLayer->featureCount() );
11818 QCOMPARE( layerName, QString() );
11819
11820 // geopackage with layer
11821 QString gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
11822 std::unique_ptr< QgsVectorLayer > gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11823 QVERIFY( gpkgLayer->isValid() );
11824 out = QgsProcessingUtils::convertToCompatibleFormat( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback );
11825 // layer must be translated -- we do not know if external tool can handle picking the correct layer automatically
11826 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11827 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
11828 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11829 QVERIFY( gpkgLayer->isValid() );
11830 out = QgsProcessingUtils::convertToCompatibleFormat( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback );
11831 QVERIFY( out.endsWith( ".shp" ) );
11832 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11833
11834 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
11835 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11836 QVERIFY( gpkgLayer->isValid() );
11837 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback, layerName );
11838 // layer SHOULD NOT be translated -- in this case we know that the external tool can handle specifying the correct layer
11839 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11840 QCOMPARE( layerName, QStringLiteral( "points_gpkg" ) );
11841 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
11842 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11843 QVERIFY( gpkgLayer->isValid() );
11844 out = QgsProcessingUtils::convertToCompatibleFormatAndLayerName( gpkgLayer.get(), false, QStringLiteral( "test" ), QStringList() << "gpkg" << "shp", QString( "shp" ), context, &feedback, layerName );
11845 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11846 QCOMPARE( layerName, QStringLiteral( "points_small" ) );
11847
11848 // also test evaluating parameter to compatible format
11849 std::unique_ptr< QgsProcessingParameterDefinition > def( new QgsProcessingParameterFeatureSource( QStringLiteral( "source" ) ) );
11850 QVariantMap params;
11851 params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), false ) );
11852 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11853 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11854 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11855 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11856 QCOMPARE( layerName, QString() );
11857
11858 // incompatible format, will be converted
11859 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback );
11860 QVERIFY( out != layer->source() );
11861 QVERIFY( out.endsWith( ".tab" ) );
11862 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11863 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback, &layerName );
11864 QVERIFY( out != layer->source() );
11865 QVERIFY( out.endsWith( ".tab" ) );
11866 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11867 QCOMPARE( layerName, QString() );
11868
11869 // layer as input
11870 params.insert( QStringLiteral( "source" ), QVariant::fromValue( layer ) );
11871 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11872 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11873 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11874 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11875 QCOMPARE( layerName, QString() );
11876
11877 // incompatible format, will be converted
11878 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback );
11879 QVERIFY( out != layer->source() );
11880 QVERIFY( out.endsWith( ".tab" ) );
11881 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11882 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "tab", QString( "tab" ), &feedback, &layerName );
11883 QVERIFY( out != layer->source() );
11884 QVERIFY( out.endsWith( ".tab" ) );
11885 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11886 QCOMPARE( layerName, QString() );
11887
11888 // selected only, will force export
11889 params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), true ) );
11890 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11891 QVERIFY( out != layer->source() );
11892 QVERIFY( out.endsWith( ".shp" ) );
11893 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11894 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11895 QVERIFY( out != layer->source() );
11896 QVERIFY( out.endsWith( ".shp" ) );
11897 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11898 QCOMPARE( layerName, QString() );
11899
11900 // feature limit, will force export
11901 params.insert( QStringLiteral( "source" ), QgsProcessingFeatureSourceDefinition( layer->id(), false, 2 ) );
11902 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11903 QVERIFY( out != layer->source() );
11904 QVERIFY( out.endsWith( ".shp" ) );
11905 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11906 QgsVectorLayer *subset = new QgsVectorLayer( out );
11907 QVERIFY( subset->isValid() );
11908 QCOMPARE( subset->featureCount(), 2L );
11909 delete subset;
11910
11911 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11912 QVERIFY( out != layer->source() );
11913 QVERIFY( out.endsWith( ".shp" ) );
11914 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11915 QCOMPARE( layerName, QString() );
11916 subset = new QgsVectorLayer( out );
11917 QVERIFY( subset->isValid() );
11918 QCOMPARE( subset->featureCount(), 2L );
11919 delete subset;
11920
11921 // vector layer as default
11922 def.reset( new QgsProcessingParameterFeatureSource( QStringLiteral( "source" ), QString(), QList<int>(), QVariant::fromValue( layer ) ) );
11923 params.remove( QStringLiteral( "source" ) );
11924 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11925 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11926 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11927 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11928 QCOMPARE( layerName, QString() );
11929
11930 // geopackage with layer
11931 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
11932 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11933 QVERIFY( gpkgLayer->isValid() );
11934 params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
11935 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback );
11936 // layer must be translated -- we do not know if external tool can handle picking the correct layer automatically
11937 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11938 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
11939 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11940 QVERIFY( gpkgLayer->isValid() );
11941 params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
11942 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback );
11943 QVERIFY( out.endsWith( ".shp" ) );
11944 QVERIFY( out.startsWith( QgsProcessingUtils::tempFolder() ) );
11945
11946 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_gpkg";
11947 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11948 QVERIFY( gpkgLayer->isValid() );
11949 params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
11950 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback, &layerName );
11951 // layer SHOULD NOT be translated -- in this case we know that the external tool can handle specifying the correct layer
11952 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11953 QCOMPARE( layerName, QStringLiteral( "points_gpkg" ) );
11954 gpkgPath = testDataDir + "points_gpkg.gpkg|layername=points_small";
11955 gpkgLayer = std::make_unique< QgsVectorLayer >( gpkgPath, "vl" );
11956 QVERIFY( gpkgLayer->isValid() );
11957 params.insert( QStringLiteral( "source" ), QVariant::fromValue( gpkgLayer.get() ) );
11958 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "gpkg" << "shp", QString( "shp" ), &feedback, &layerName );
11959 QCOMPARE( out, QString( testDataDir + QStringLiteral( "points_gpkg.gpkg" ) ) );
11960 QCOMPARE( layerName, QStringLiteral( "points_small" ) );
11961
11962 // output layer as input - e.g. from a previous model child
11963 params.insert( QStringLiteral( "source" ), QgsProcessingOutputLayerDefinition( layer->id() ) );
11964 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPath( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback );
11965 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11966 out = QgsProcessingParameters::parameterAsCompatibleSourceLayerPathAndLayerName( def.get(), params, context, QStringList() << "shp", QString( "shp" ), &feedback, &layerName );
11967 QCOMPARE( out, QString( testDataDir + "points.shp" ) );
11968 QCOMPARE( layerName, QString() );
11969 }
11970
create()11971 void TestQgsProcessing::create()
11972 {
11973 DummyAlgorithm alg( QStringLiteral( "test" ) );
11974 DummyProvider p( QStringLiteral( "test_provider" ) );
11975 alg.setProvider( &p );
11976
11977 std::unique_ptr< QgsProcessingAlgorithm > newInstance( alg.create() );
11978 QVERIFY( newInstance.get() );
11979 QCOMPARE( newInstance->provider(), &p );
11980 }
11981
combineFields()11982 void TestQgsProcessing::combineFields()
11983 {
11984 QgsFields a;
11985 QgsFields b;
11986 // combine empty fields
11987 QgsFields res = QgsProcessingUtils::combineFields( a, b );
11988 QVERIFY( res.isEmpty() );
11989
11990 // fields in a
11991 a.append( QgsField( "name" ) );
11992 res = QgsProcessingUtils::combineFields( a, b );
11993 QCOMPARE( res.count(), 1 );
11994 QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
11995 b.append( QgsField( "name" ) );
11996 res = QgsProcessingUtils::combineFields( a, b );
11997 QCOMPARE( res.count(), 2 );
11998 QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
11999 QCOMPARE( res.at( 1 ).name(), QStringLiteral( "name_2" ) );
12000
12001 a.append( QgsField( "NEW" ) );
12002 res = QgsProcessingUtils::combineFields( a, b );
12003 QCOMPARE( res.count(), 3 );
12004 QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
12005 QCOMPARE( res.at( 1 ).name(), QStringLiteral( "NEW" ) );
12006 QCOMPARE( res.at( 2 ).name(), QStringLiteral( "name_2" ) );
12007
12008 b.append( QgsField( "new" ) );
12009 res = QgsProcessingUtils::combineFields( a, b );
12010 QCOMPARE( res.count(), 4 );
12011 QCOMPARE( res.at( 0 ).name(), QStringLiteral( "name" ) );
12012 QCOMPARE( res.at( 1 ).name(), QStringLiteral( "NEW" ) );
12013 QCOMPARE( res.at( 2 ).name(), QStringLiteral( "name_2" ) );
12014 QCOMPARE( res.at( 3 ).name(), QStringLiteral( "new_2" ) );
12015 }
12016
fieldNamesToIndices()12017 void TestQgsProcessing::fieldNamesToIndices()
12018 {
12019 QgsFields fields;
12020 fields.append( QgsField( "name" ) );
12021 fields.append( QgsField( "address" ) );
12022 fields.append( QgsField( "age" ) );
12023
12024 const QList<int> indices1 = QgsProcessingUtils::fieldNamesToIndices( QStringList(), fields );
12025 QCOMPARE( indices1, QList<int>() << 0 << 1 << 2 );
12026
12027 const QList<int> indices2 = QgsProcessingUtils::fieldNamesToIndices( QStringList() << "address" << "age", fields );
12028 QCOMPARE( indices2, QList<int>() << 1 << 2 );
12029
12030 const QList<int> indices3 = QgsProcessingUtils::fieldNamesToIndices( QStringList() << "address" << "agegege", fields );
12031 QCOMPARE( indices3, QList<int>() << 1 );
12032 }
12033
indicesToFields()12034 void TestQgsProcessing::indicesToFields()
12035 {
12036 QgsFields fields;
12037 fields.append( QgsField( "name" ) );
12038 fields.append( QgsField( "address" ) );
12039 fields.append( QgsField( "age" ) );
12040
12041 const QList<int> indices1 = QList<int>() << 0 << 1 << 2;
12042 const QgsFields fields1 = QgsProcessingUtils::indicesToFields( indices1, fields );
12043 QCOMPARE( fields1, fields );
12044
12045 const QList<int> indices2 = QList<int>() << 1;
12046 QgsFields fields2expected;
12047 fields2expected.append( QgsField( "address" ) );
12048 const QgsFields fields2 = QgsProcessingUtils::indicesToFields( indices2, fields );
12049 QCOMPARE( fields2, fields2expected );
12050
12051 const QList<int> indices3;
12052 const QgsFields fields3 = QgsProcessingUtils::indicesToFields( indices3, fields );
12053 QCOMPARE( fields3, QgsFields() );
12054 }
12055
variantToPythonLiteral()12056 void TestQgsProcessing::variantToPythonLiteral()
12057 {
12058 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant() ), QStringLiteral( "None" ) );
12059 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsProperty::fromExpression( QStringLiteral( "1+2" ) ) ) ), QStringLiteral( "QgsProperty.fromExpression('1+2')" ) );
12060 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsCoordinateReferenceSystem() ) ), QStringLiteral( "QgsCoordinateReferenceSystem()" ) );
12061 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:3111" ) ) ) ), QStringLiteral( "QgsCoordinateReferenceSystem('EPSG:3111')" ) );
12062 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsRectangle( 1, 2, 3, 4 ) ) ), QStringLiteral( "'1, 3, 2, 4'" ) );
12063 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsReferencedRectangle( QgsRectangle( 1, 2, 3, 4 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ) ) ), QStringLiteral( "'1, 3, 2, 4 [EPSG:28356]'" ) );
12064 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsPointXY( 1, 2 ) ) ), QStringLiteral( "'1,2'" ) );
12065 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariant::fromValue( QgsReferencedPointXY( QgsPointXY( 1, 2 ), QgsCoordinateReferenceSystem( QStringLiteral( "EPSG:28356" ) ) ) ) ), QStringLiteral( "'1,2 [EPSG:28356]'" ) );
12066 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( true ), QStringLiteral( "True" ) );
12067 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( false ), QStringLiteral( "False" ) );
12068 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( 5 ), QStringLiteral( "5" ) );
12069 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( 5.5 ), QStringLiteral( "5.5" ) );
12070 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( 5LL ), QStringLiteral( "5" ) );
12071 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QVariantList() << true << QVariant() << QStringLiteral( "a" ) ), QStringLiteral( "[True,None,'a']" ) );
12072 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a" ) ), QStringLiteral( "'a'" ) );
12073 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QString() ), QStringLiteral( "''" ) );
12074 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a 'string'" ) ), QStringLiteral( "\"a 'string'\"" ) );
12075 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a \"string\" and a ' quote" ) ), QStringLiteral( "'a \"string\" and a \\' quote'" ) );
12076 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a \"string\"" ) ), QStringLiteral( "'a \"string\"'" ) );
12077 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QStringLiteral( "a \n str\tin\\g" ) ), QStringLiteral( "'a \\n str\\tin\\\\g'" ) );
12078 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( QDateTime( QDate( 2345, 1, 2 ), QTime( 6, 57, 58 ) ) ), QStringLiteral( "QDateTime(QDate(2345, 1, 2), QTime(6, 57, 58))" ) );
12079 QVariantMap map;
12080 map.insert( QStringLiteral( "list" ), QVariantList() << 1 << 2 << "a" );
12081 map.insert( QStringLiteral( "another" ), 4 );
12082 map.insert( QStringLiteral( "another2" ), QStringLiteral( "test" ) );
12083 QCOMPARE( QgsProcessingUtils::variantToPythonLiteral( map ), QStringLiteral( "{'another': 4,'another2': 'test','list': [1,2,'a']}" ) );
12084 }
12085
stringToPythonLiteral()12086 void TestQgsProcessing::stringToPythonLiteral()
12087 {
12088 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a" ) ), QStringLiteral( "'a'" ) );
12089 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QString() ), QStringLiteral( "''" ) );
12090 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a \n str\tin\\g" ) ), QStringLiteral( "'a \\n str\\tin\\\\g'" ) );
12091
12092
12093 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a 'string'" ) ), QStringLiteral( "\"a 'string'\"" ) );
12094 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a \"string\" and a ' quote" ) ), QStringLiteral( "'a \"string\" and a \\' quote'" ) );
12095 QCOMPARE( QgsProcessingUtils::stringToPythonLiteral( QStringLiteral( "a \"string\"" ) ), QStringLiteral( "'a \"string\"'" ) );
12096 }
12097
12098
12099
defaultExtensionsForProvider()12100 void TestQgsProcessing::defaultExtensionsForProvider()
12101 {
12102 const DummyProvider3 provider;
12103 // default implementation should return first supported format for provider
12104 QCOMPARE( provider.defaultVectorFileExtension( true ), QStringLiteral( "mif" ) );
12105 QCOMPARE( provider.defaultRasterFileExtension(), QStringLiteral( "mig" ) );
12106
12107 // a default context should use reasonable defaults
12108 const QgsProcessingContext context;
12109 QCOMPARE( context.preferredVectorFormat(), QStringLiteral( "gpkg" ) );
12110 QCOMPARE( context.preferredRasterFormat(), QStringLiteral( "tif" ) );
12111
12112 // unless the user has set a default format, which IS supported by that provider
12113 QgsProcessing::settingsDefaultOutputVectorLayerExt.setValue( QgsVectorFileWriter::supportedFormatExtensions().indexOf( QLatin1String( "tab" ) ) );
12114 QgsProcessing::settingsDefaultOutputRasterLayerExt.setValue( QgsRasterFileWriter::supportedFormatExtensions().indexOf( QLatin1String( "sdat" ) ) );
12115
12116 QCOMPARE( provider.defaultVectorFileExtension( true ), QStringLiteral( "tab" ) );
12117 QCOMPARE( provider.defaultRasterFileExtension(), QStringLiteral( "sdat" ) );
12118
12119 // context should respect these as preferred formats
12120 const QgsProcessingContext context2;
12121 QCOMPARE( context2.preferredVectorFormat(), QStringLiteral( "tab" ) );
12122 QCOMPARE( context2.preferredRasterFormat(), QStringLiteral( "sdat" ) );
12123
12124 // but if default is not supported by provider, we use a supported format
12125 QgsProcessing::settingsDefaultOutputVectorLayerExt.setValue( QgsVectorFileWriter::supportedFormatExtensions().indexOf( QLatin1String( "gpkg" ) ) );
12126 QgsProcessing::settingsDefaultOutputRasterLayerExt.setValue( QgsRasterFileWriter::supportedFormatExtensions().indexOf( QLatin1String( "ecw" ) ) );
12127 QCOMPARE( provider.defaultVectorFileExtension( true ), QStringLiteral( "mif" ) );
12128 QCOMPARE( provider.defaultRasterFileExtension(), QStringLiteral( "mig" ) );
12129 }
12130
supportedExtensions()12131 void TestQgsProcessing::supportedExtensions()
12132 {
12133 const DummyProvider4 provider;
12134 QCOMPARE( provider.supportedOutputVectorLayerExtensions().count(), 1 );
12135 QCOMPARE( provider.supportedOutputVectorLayerExtensions().at( 0 ), QStringLiteral( "mif" ) );
12136
12137 // if supportedOutputTableExtensions is not implemented, supportedOutputVectorLayerExtensions should be used instead
12138 QCOMPARE( provider.supportedOutputTableExtensions().count(), 1 );
12139 QCOMPARE( provider.supportedOutputTableExtensions().at( 0 ), QStringLiteral( "mif" ) );
12140 }
12141
supportsNonFileBasedOutput()12142 void TestQgsProcessing::supportsNonFileBasedOutput()
12143 {
12144 DummyAlgorithm alg( QStringLiteral( "test" ) );
12145 DummyProvider p( QStringLiteral( "test_provider" ) );
12146 alg.addDestParams();
12147 // provider has no support for file based outputs, so both output parameters should deny support
12148 alg.setProvider( &p );
12149 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 0 ) )->supportsNonFileBasedOutput() );
12150 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg.destinationParameterDefinitions().at( 1 ) )->supportsNonFileBasedOutput() );
12151
12152 DummyAlgorithm alg2( QStringLiteral( "test" ) );
12153 DummyProvider p2( QStringLiteral( "test_provider" ) );
12154 p2.supportsNonFileOutputs = true;
12155 alg2.addDestParams();
12156 // provider has support for file based outputs, but only first output parameter should indicate support (since the second has support explicitly denied)
12157 alg2.setProvider( &p2 );
12158 QVERIFY( static_cast< const QgsProcessingDestinationParameter * >( alg2.destinationParameterDefinitions().at( 0 ) )->supportsNonFileBasedOutput() );
12159 QVERIFY( !static_cast< const QgsProcessingDestinationParameter * >( alg2.destinationParameterDefinitions().at( 1 ) )->supportsNonFileBasedOutput() );
12160 }
12161
addParameterType()12162 void TestQgsProcessing::addParameterType()
12163 {
12164 QgsProcessingRegistry reg;
12165 const QSignalSpy spy( ®, &QgsProcessingRegistry::parameterTypeAdded );
12166 DummyParameterType *dpt = new DummyParameterType();
12167 QVERIFY( reg.addParameterType( dpt ) );
12168 QCOMPARE( spy.count(), 1 );
12169 QVERIFY( !reg.addParameterType( dpt ) );
12170 QCOMPARE( spy.count(), 1 );
12171 QVERIFY( !reg.addParameterType( new DummyParameterType() ) );
12172 QCOMPARE( spy.count(), 1 );
12173 }
12174
removeParameterType()12175 void TestQgsProcessing::removeParameterType()
12176 {
12177 QgsProcessingRegistry reg;
12178
12179 auto paramType = new DummyParameterType();
12180
12181 reg.addParameterType( paramType );
12182 const QSignalSpy spy( ®, &QgsProcessingRegistry::parameterTypeRemoved );
12183 reg.removeParameterType( paramType );
12184 QCOMPARE( spy.count(), 1 );
12185 }
12186
parameterTypes()12187 void TestQgsProcessing::parameterTypes()
12188 {
12189 QgsProcessingRegistry reg;
12190 const int coreParamCount = reg.parameterTypes().count();
12191 QVERIFY( coreParamCount > 5 );
12192
12193 auto paramType = new DummyParameterType();
12194
12195 reg.addParameterType( paramType );
12196 QCOMPARE( reg.parameterTypes().count(), coreParamCount + 1 );
12197 QVERIFY( reg.parameterTypes().contains( paramType ) );
12198 }
12199
parameterType()12200 void TestQgsProcessing::parameterType()
12201 {
12202 QgsProcessingRegistry reg;
12203
12204 QVERIFY( reg.parameterType( QStringLiteral( "string" ) ) );
12205 QVERIFY( !reg.parameterType( QStringLiteral( "borken" ) ) ); //#spellok
12206
12207 auto paramType = new DummyParameterType();
12208
12209 reg.addParameterType( paramType );
12210 QCOMPARE( reg.parameterType( QStringLiteral( "paramType" ) ), paramType );
12211 }
12212
sourceTypeToString_data()12213 void TestQgsProcessing::sourceTypeToString_data()
12214 {
12215 QTest::addColumn<int>( "type" );
12216 QTest::addColumn<QString>( "expected" );
12217
12218 // IMPORTANT -- these must match the original enum values!
12219 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeMapLayer ) << QStringLiteral( "TypeMapLayer" );
12220 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeVectorAnyGeometry ) << QStringLiteral( "TypeVectorAnyGeometry" );
12221 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeVectorPoint ) << QStringLiteral( "TypeVectorPoint" );
12222 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeVectorLine ) << QStringLiteral( "TypeVectorLine" );
12223 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeVectorPolygon ) << QStringLiteral( "TypeVectorPolygon" );
12224 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeRaster ) << QStringLiteral( "TypeRaster" );
12225 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeFile ) << QStringLiteral( "TypeFile" );
12226 QTest::newRow( "map layer" ) << static_cast< int >( QgsProcessing::TypeMesh ) << QStringLiteral( "TypeMesh" );
12227 }
12228
sourceTypeToString()12229 void TestQgsProcessing::sourceTypeToString()
12230 {
12231 QFETCH( int, type );
12232 QFETCH( QString, expected );
12233
12234 const QgsProcessing::SourceType sourceType = static_cast< QgsProcessing::SourceType >( type );
12235 QCOMPARE( QgsProcessing::sourceTypeToString( sourceType ), expected );
12236 }
12237
modelSource()12238 void TestQgsProcessing::modelSource()
12239 {
12240 QgsProcessingModelChildParameterSource source;
12241 source.setExpression( QStringLiteral( "expression" ) );
12242 source.setExpressionText( QStringLiteral( "expression string" ) );
12243 source.setOutputName( QStringLiteral( "output name " ) );
12244 source.setStaticValue( QString( "value" ) );
12245 source.setOutputChildId( QStringLiteral( "output child id" ) );
12246 source.setParameterName( QStringLiteral( "parameter name" ) );
12247 source.setSource( QgsProcessingModelChildParameterSource::ChildOutput );
12248
12249 QByteArray ba;
12250 QDataStream ds( &ba, QIODevice::ReadWrite );
12251 ds << source;
12252
12253 ds.device()->seek( 0 );
12254
12255 QgsProcessingModelChildParameterSource res;
12256 ds >> res;
12257
12258 QCOMPARE( res.expression(), QStringLiteral( "expression" ) );
12259 QCOMPARE( res.expressionText(), QStringLiteral( "expression string" ) );
12260 QCOMPARE( res.outputName(), QStringLiteral( "output name " ) );
12261 QCOMPARE( res.staticValue().toString(), QString( "value" ) );
12262 QCOMPARE( res.outputChildId(), QStringLiteral( "output child id" ) );
12263 QCOMPARE( res.parameterName(), QStringLiteral( "parameter name" ) );
12264 QCOMPARE( res.source(), QgsProcessingModelChildParameterSource::ChildOutput );
12265 }
12266
12267 QGSTEST_MAIN( TestQgsProcessing )
12268 #include "testqgsprocessing.moc"
12269