Line data Source code
1 : ///////////////////////////////////////////////////////////////////////////////
2 : //
3 : // Project: C++ Test Suite for GDAL/OGR
4 : // Purpose: Test GDALAlgorithm
5 : // Author: Even Rouault <even.rouault at spatialys.com>
6 : //
7 : ///////////////////////////////////////////////////////////////////////////////
8 : // Copyright (c) 2024, Even Rouault <even.rouault at spatialys.com>
9 : /*
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdal_unit_test.h"
14 :
15 : #include "cpl_error.h"
16 : #include "cpl_string.h"
17 : #include "cpl_multiproc.h"
18 : #include "gdalalgorithm.h"
19 : #include "gdal_priv.h"
20 : #include "ogr_spatialref.h"
21 :
22 : #include "test_data.h"
23 :
24 : #include "gtest_include.h"
25 :
26 : #include <algorithm>
27 : #include <limits>
28 :
29 : #if defined(__clang__)
30 : #pragma clang diagnostic push
31 : #pragma clang diagnostic ignored "-Wweak-vtables"
32 : #endif
33 :
34 : namespace test_gdal_algorithm
35 : {
36 :
37 : struct test_gdal_algorithm : public ::testing::Test
38 : {
39 : };
40 :
41 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgTypeName)
42 : {
43 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_BOOLEAN), "boolean");
44 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_STRING), "string");
45 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_INTEGER), "integer");
46 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_REAL), "real");
47 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_DATASET), "dataset");
48 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_STRING_LIST), "string_list");
49 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_INTEGER_LIST), "integer_list");
50 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_REAL_LIST), "real_list");
51 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_DATASET_LIST), "dataset_list");
52 1 : }
53 :
54 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgDatasetTypeName)
55 : {
56 2 : EXPECT_STREQ(GDALAlgorithmArgDatasetTypeName(GDAL_OF_RASTER).c_str(),
57 : "raster");
58 2 : EXPECT_STREQ(GDALAlgorithmArgDatasetTypeName(GDAL_OF_VECTOR).c_str(),
59 : "vector");
60 2 : EXPECT_STREQ(
61 : GDALAlgorithmArgDatasetTypeName(GDAL_OF_MULTIDIM_RASTER).c_str(),
62 : "multidimensional raster");
63 2 : EXPECT_STREQ(
64 : GDALAlgorithmArgDatasetTypeName(GDAL_OF_RASTER | GDAL_OF_VECTOR)
65 : .c_str(),
66 : "raster or vector");
67 2 : EXPECT_STREQ(GDALAlgorithmArgDatasetTypeName(GDAL_OF_RASTER |
68 : GDAL_OF_MULTIDIM_RASTER)
69 : .c_str(),
70 : "raster or multidimensional raster");
71 2 : EXPECT_STREQ(GDALAlgorithmArgDatasetTypeName(
72 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER)
73 : .c_str(),
74 : "raster, vector or multidimensional raster");
75 2 : EXPECT_STREQ(GDALAlgorithmArgDatasetTypeName(GDAL_OF_VECTOR |
76 : GDAL_OF_MULTIDIM_RASTER)
77 : .c_str(),
78 : "vector or multidimensional raster");
79 1 : }
80 :
81 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgDecl_SetMinCount)
82 : {
83 : {
84 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
85 1 : CPLErrorReset();
86 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN)
87 : .SetMinCount(2)
88 : .GetMinCount(),
89 : 0);
90 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
91 : }
92 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST)
93 : .SetMinCount(2)
94 : .GetMinCount(),
95 : 2);
96 1 : }
97 :
98 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgDecl_SetMaxCount)
99 : {
100 : {
101 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
102 1 : CPLErrorReset();
103 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN)
104 : .SetMaxCount(2)
105 : .GetMaxCount(),
106 : 1);
107 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
108 : }
109 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST)
110 : .SetMaxCount(2)
111 : .GetMaxCount(),
112 : 2);
113 1 : }
114 :
115 : class MyAlgorithmWithDummyRun : public GDALAlgorithm
116 : {
117 : public:
118 250 : MyAlgorithmWithDummyRun(const std::string &name = "test",
119 : const std::string &description = "",
120 : const std::string &url = "https://example.com")
121 250 : : GDALAlgorithm(name, description, url)
122 : {
123 250 : }
124 :
125 1 : bool RunImpl(GDALProgressFunc, void *) override
126 : {
127 1 : return true;
128 : }
129 : };
130 :
131 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArg_SetDefault)
132 : {
133 :
134 : class MyAlgorithm : public MyAlgorithmWithDummyRun
135 : {
136 : public:
137 1 : MyAlgorithm()
138 1 : {
139 : {
140 : bool v;
141 1 : auto &arg = AddArg("", 0, "", &v);
142 1 : arg.SetDefault(true);
143 1 : EXPECT_TRUE(arg.GetDefault<bool>());
144 :
145 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
146 1 : CPLErrorReset();
147 1 : arg.SetDefault("invalid");
148 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
149 : }
150 :
151 : {
152 : int v;
153 1 : auto &arg = AddArg("", 0, "", &v);
154 1 : arg.SetDefault(5);
155 1 : EXPECT_EQ(arg.GetDefault<int>(), 5);
156 :
157 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
158 1 : CPLErrorReset();
159 1 : arg.SetDefault("invalid");
160 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
161 : }
162 :
163 : {
164 : double v;
165 1 : auto &arg = AddArg("", 0, "", &v);
166 1 : arg.SetDefault(4.5);
167 1 : EXPECT_EQ(arg.GetDefault<double>(), 4.5);
168 :
169 1 : arg.SetDefault(5);
170 1 : EXPECT_EQ(arg.GetDefault<double>(), 5);
171 :
172 1 : arg.SetDefault(2.5f);
173 1 : EXPECT_EQ(arg.GetDefault<double>(), 2.5);
174 :
175 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
176 1 : CPLErrorReset();
177 1 : arg.SetDefault("invalid");
178 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
179 : }
180 :
181 : {
182 2 : std::string v;
183 1 : auto &arg = AddArg("", 0, "", &v);
184 :
185 1 : arg.SetDefault("ab");
186 1 : EXPECT_STREQ(arg.GetDefault<std::string>().c_str(), "ab");
187 :
188 1 : arg.SetDefault(std::string("cd"));
189 1 : EXPECT_STREQ(arg.GetDefault<std::string>().c_str(), "cd");
190 :
191 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
192 1 : CPLErrorReset();
193 1 : arg.SetDefault(0);
194 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
195 : }
196 :
197 : {
198 2 : std::vector<int> v;
199 1 : auto &arg = AddArg("", 0, "", &v);
200 1 : arg.SetDefault(5);
201 2 : std::vector<int> expected{5};
202 1 : EXPECT_EQ(arg.GetDefault<std::vector<int>>(), expected);
203 :
204 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
205 1 : CPLErrorReset();
206 1 : arg.SetDefault("invalid");
207 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
208 : }
209 :
210 : {
211 2 : std::vector<double> v;
212 1 : auto &arg = AddArg("", 0, "", &v);
213 1 : arg.SetDefault(4.5);
214 : {
215 2 : std::vector<double> expected{4.5};
216 1 : EXPECT_EQ(arg.GetDefault<std::vector<double>>(), expected);
217 : }
218 :
219 1 : arg.SetDefault(5);
220 : {
221 2 : std::vector<double> expected{5};
222 1 : EXPECT_EQ(arg.GetDefault<std::vector<double>>(), expected);
223 : }
224 :
225 1 : arg.SetDefault(2.5f);
226 : {
227 2 : std::vector<double> expected{2.5};
228 1 : EXPECT_EQ(arg.GetDefault<std::vector<double>>(), expected);
229 : }
230 :
231 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
232 1 : CPLErrorReset();
233 1 : arg.SetDefault("invalid");
234 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
235 : }
236 :
237 : {
238 2 : std::vector<std::string> v;
239 1 : auto &arg = AddArg("", 0, "", &v);
240 :
241 1 : arg.SetDefault("ab");
242 : {
243 5 : std::vector<std::string> expected{"ab"};
244 1 : EXPECT_EQ(arg.GetDefault<std::vector<std::string>>(),
245 : expected);
246 : }
247 :
248 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
249 1 : CPLErrorReset();
250 1 : arg.SetDefault(0);
251 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
252 : }
253 :
254 : {
255 2 : GDALArgDatasetValue v;
256 1 : auto &arg = AddArg("", 0, "", &v);
257 :
258 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
259 1 : CPLErrorReset();
260 1 : arg.SetDefault(0);
261 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
262 : }
263 :
264 : {
265 2 : std::vector<GDALArgDatasetValue> v;
266 1 : auto &arg = AddArg("", 0, "", &v);
267 :
268 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
269 1 : CPLErrorReset();
270 1 : arg.SetDefault(0);
271 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
272 : }
273 1 : }
274 : };
275 :
276 1 : MyAlgorithm alg;
277 1 : }
278 :
279 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArg_Set)
280 : {
281 : {
282 1 : bool val = false;
283 : auto arg = GDALAlgorithmArg(
284 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN), &val);
285 1 : arg.Set(true);
286 1 : EXPECT_EQ(arg.Get<bool>(), true);
287 1 : EXPECT_EQ(val, true);
288 :
289 : {
290 1 : bool val2 = false;
291 : auto arg2 = GDALAlgorithmArg(
292 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN), &val2);
293 1 : arg2.SetFrom(arg);
294 1 : EXPECT_EQ(arg2.Get<bool>(), true);
295 : }
296 :
297 1 : arg.Set(false);
298 1 : EXPECT_EQ(val, false);
299 :
300 1 : arg.Set(1);
301 1 : EXPECT_EQ(val, true);
302 :
303 1 : arg.Set(0);
304 1 : EXPECT_EQ(val, false);
305 :
306 1 : arg.Set("1");
307 1 : EXPECT_EQ(val, true);
308 :
309 1 : arg.Set("0");
310 1 : EXPECT_EQ(val, false);
311 :
312 1 : arg.Set("yes");
313 1 : EXPECT_EQ(val, true);
314 :
315 1 : arg.Set("no");
316 1 : EXPECT_EQ(val, false);
317 :
318 1 : arg.Set("true");
319 1 : EXPECT_EQ(val, true);
320 :
321 1 : arg.Set("false");
322 1 : EXPECT_EQ(val, false);
323 :
324 1 : arg.Set("on");
325 1 : EXPECT_EQ(val, true);
326 :
327 1 : arg.Set("off");
328 1 : EXPECT_EQ(val, false);
329 :
330 1 : arg = true;
331 1 : EXPECT_EQ(val, true);
332 :
333 1 : arg.Set(false);
334 : {
335 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
336 1 : CPLErrorReset();
337 1 : arg.Set(2);
338 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
339 1 : EXPECT_EQ(val, false);
340 :
341 1 : CPLErrorReset();
342 1 : arg.Set(1.5);
343 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
344 1 : EXPECT_EQ(val, false);
345 :
346 1 : CPLErrorReset();
347 1 : arg.Set("foo");
348 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
349 1 : EXPECT_EQ(val, false);
350 :
351 1 : CPLErrorReset();
352 1 : arg.Set(std::vector<std::string>());
353 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
354 1 : EXPECT_EQ(val, false);
355 :
356 1 : CPLErrorReset();
357 1 : arg.Set(std::vector<int>());
358 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
359 1 : EXPECT_EQ(val, false);
360 :
361 1 : CPLErrorReset();
362 1 : arg.Set(std::vector<double>());
363 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
364 1 : EXPECT_EQ(val, false);
365 :
366 1 : CPLErrorReset();
367 1 : arg.Set(std::vector<GDALArgDatasetValue>());
368 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
369 1 : EXPECT_EQ(val, false);
370 :
371 : auto poDS = std::unique_ptr<GDALDataset>(
372 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
373 2 : "", 1, 1, 1, GDT_Byte, nullptr));
374 1 : CPLErrorReset();
375 1 : arg.Set(std::move(poDS));
376 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
377 1 : EXPECT_EQ(val, false);
378 : }
379 :
380 : {
381 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
382 1 : CPLErrorReset();
383 1 : int val2 = 1;
384 : auto arg2 = GDALAlgorithmArg(
385 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val2);
386 1 : arg2.SetFrom(arg);
387 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
388 1 : EXPECT_EQ(val2, 1);
389 : }
390 : }
391 : {
392 1 : int val = 0;
393 : auto arg = GDALAlgorithmArg(
394 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
395 1 : arg.Set(1);
396 1 : EXPECT_EQ(arg.Get<int>(), 1);
397 1 : EXPECT_EQ(val, 1);
398 :
399 1 : int val2 = 0;
400 : auto arg2 = GDALAlgorithmArg(
401 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val2);
402 1 : arg2.SetFrom(arg);
403 1 : EXPECT_EQ(arg2.Get<int>(), 1);
404 :
405 1 : arg.Set("2");
406 1 : EXPECT_EQ(val, 2);
407 :
408 1 : arg.Set(3.0);
409 1 : EXPECT_EQ(val, 3);
410 :
411 1 : arg.Set(std::vector<int>{1});
412 1 : EXPECT_EQ(val, 1);
413 :
414 1 : arg.Set(std::vector<double>{2.0});
415 1 : EXPECT_EQ(val, 2);
416 :
417 2 : arg.Set(std::vector<std::string>{"3"});
418 1 : EXPECT_EQ(val, 3);
419 :
420 1 : arg = 4;
421 1 : EXPECT_EQ(val, 4);
422 :
423 1 : arg.Set(0);
424 : {
425 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
426 1 : CPLErrorReset();
427 :
428 1 : arg.Set(true);
429 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
430 1 : EXPECT_EQ(val, 0);
431 :
432 1 : arg.Set(1.5);
433 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
434 1 : EXPECT_EQ(val, 0);
435 :
436 1 : arg.Set("12345679812346798123456");
437 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
438 1 : EXPECT_EQ(val, 0);
439 :
440 1 : arg.Set("foo");
441 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
442 1 : EXPECT_EQ(val, 0);
443 :
444 2 : arg.Set(std::vector<std::string>{"12345679812346798123456"});
445 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
446 1 : EXPECT_EQ(val, 0);
447 :
448 1 : arg.Set(std::vector<int>{1, 2});
449 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
450 1 : EXPECT_EQ(val, 0);
451 : }
452 : }
453 : {
454 1 : double val = 0;
455 : auto arg = GDALAlgorithmArg(
456 2 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL).SetDefault(-1), &val);
457 1 : arg.Set(1.5);
458 1 : EXPECT_EQ(arg.Get<double>(), 1.5);
459 1 : EXPECT_EQ(val, 1.5);
460 1 : arg.Set(1);
461 1 : EXPECT_EQ(arg.Get<double>(), 1);
462 :
463 1 : double val2 = 0;
464 : auto arg2 = GDALAlgorithmArg(
465 2 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL).SetDefault(-1.5), &val2);
466 1 : arg2.SetFrom(arg);
467 1 : EXPECT_EQ(val2, 1);
468 :
469 1 : arg.Set("2.5");
470 1 : EXPECT_EQ(val, 2.5);
471 :
472 1 : arg.Set(std::vector<int>{1});
473 1 : EXPECT_EQ(val, 1);
474 :
475 1 : arg.Set(std::vector<double>{2.5});
476 1 : EXPECT_EQ(val, 2.5);
477 :
478 2 : arg.Set(std::vector<std::string>{"3.5"});
479 1 : EXPECT_EQ(val, 3.5);
480 :
481 1 : arg = 4.5;
482 1 : EXPECT_EQ(val, 4.5);
483 :
484 1 : arg.Set(0);
485 : {
486 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
487 1 : CPLErrorReset();
488 :
489 1 : arg.Set(true);
490 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
491 1 : EXPECT_EQ(val, 0);
492 :
493 1 : arg.Set("foo");
494 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
495 1 : EXPECT_EQ(val, 0);
496 :
497 1 : arg.Set(std::vector<int>{1, 2});
498 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
499 1 : EXPECT_EQ(val, 0);
500 : }
501 : }
502 : {
503 2 : std::string val;
504 : auto arg = GDALAlgorithmArg(
505 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING), &val);
506 1 : arg.Set("foo");
507 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
508 1 : EXPECT_STREQ(val.c_str(), "foo");
509 :
510 2 : std::string val2;
511 : auto arg2 = GDALAlgorithmArg(
512 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING), &val2);
513 1 : arg2.SetFrom(arg);
514 1 : EXPECT_STREQ(arg2.Get<std::string>().c_str(), "foo");
515 :
516 1 : arg.Set(1);
517 1 : EXPECT_STREQ(val.c_str(), "1");
518 :
519 1 : arg.Set(1.5);
520 1 : EXPECT_EQ(CPLAtof(val.c_str()), 1.5);
521 :
522 1 : arg.Set(std::vector<int>{1});
523 1 : EXPECT_STREQ(val.c_str(), "1");
524 :
525 1 : arg.Set(std::vector<double>{1.5});
526 1 : EXPECT_EQ(CPLAtof(val.c_str()), 1.5);
527 :
528 2 : arg.Set(std::vector<std::string>{"bar"});
529 1 : EXPECT_STREQ(val.c_str(), "bar");
530 :
531 1 : arg = "x";
532 1 : EXPECT_STREQ(val.c_str(), "x");
533 :
534 1 : arg = std::string("y");
535 1 : EXPECT_STREQ(val.c_str(), "y");
536 :
537 1 : arg = GDT_Byte;
538 1 : EXPECT_STREQ(val.c_str(), "Byte");
539 :
540 2 : OGRSpatialReference srs;
541 1 : srs.SetFromUserInput("WGS84");
542 1 : arg = srs;
543 1 : EXPECT_EQ(val.find("GEOGCRS["), 0U);
544 :
545 1 : arg.Set("foo");
546 : {
547 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
548 1 : CPLErrorReset();
549 1 : arg.Set(true);
550 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
551 1 : EXPECT_STREQ(val.c_str(), "foo");
552 : }
553 : {
554 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
555 1 : CPLErrorReset();
556 1 : arg.Set(static_cast<GDALDataset *>(nullptr));
557 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
558 1 : EXPECT_STREQ(val.c_str(), "foo");
559 : }
560 : {
561 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
562 1 : CPLErrorReset();
563 3 : arg.Set(std::vector<std::string>{"bar", "foo"});
564 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
565 1 : EXPECT_STREQ(val.c_str(), "foo");
566 : }
567 : {
568 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
569 1 : CPLErrorReset();
570 1 : arg.SetDatasetName("bar");
571 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
572 1 : EXPECT_STREQ(val.c_str(), "foo");
573 : }
574 : {
575 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
576 1 : CPLErrorReset();
577 2 : GDALArgDatasetValue dsValue;
578 1 : arg.SetFrom(dsValue);
579 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
580 1 : EXPECT_STREQ(val.c_str(), "foo");
581 : }
582 : }
583 : {
584 2 : std::string val;
585 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
586 1 : .SetReadFromFileAtSyntaxAllowed()
587 1 : .SetRemoveSQLCommentsEnabled(),
588 2 : &val);
589 1 : EXPECT_TRUE(arg.Set("foo"));
590 1 : EXPECT_STREQ(val.c_str(), "foo");
591 : }
592 : {
593 2 : std::string osTmpFilename = VSIMemGenerateHiddenFilename("temp.sql");
594 1 : VSILFILE *fpTmp = VSIFOpenL(osTmpFilename.c_str(), "wb");
595 1 : VSIFPrintfL(fpTmp, "\xEF\xBB\xBF"); // UTF-8 BOM
596 1 : VSIFPrintfL(fpTmp, "-- this is a comment\n");
597 1 : VSIFPrintfL(fpTmp, "value");
598 1 : VSIFCloseL(fpTmp);
599 :
600 2 : std::string val;
601 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
602 1 : .SetReadFromFileAtSyntaxAllowed()
603 1 : .SetRemoveSQLCommentsEnabled(),
604 2 : &val);
605 1 : EXPECT_TRUE(arg.Set(("@" + osTmpFilename).c_str()));
606 1 : EXPECT_STREQ(val.c_str(), "value");
607 1 : VSIUnlink(osTmpFilename.c_str());
608 : }
609 : {
610 2 : std::string val;
611 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
612 1 : .SetReadFromFileAtSyntaxAllowed(),
613 2 : &val);
614 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
615 1 : EXPECT_FALSE(arg.Set("@i_do_not_exist"));
616 : }
617 : {
618 : auto poMEMDS = std::unique_ptr<GDALDataset>(
619 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
620 2 : "", 1, 1, 1, GDT_Byte, nullptr));
621 2 : GDALArgDatasetValue val;
622 : auto arg = GDALAlgorithmArg(
623 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET), &val);
624 1 : auto poMEMDSRaw = poMEMDS.get();
625 :
626 1 : arg.Set(poMEMDS.release());
627 1 : EXPECT_EQ(val.GetDatasetRef(), poMEMDSRaw);
628 :
629 1 : poMEMDS.reset(val.BorrowDataset());
630 1 : EXPECT_EQ(poMEMDS.get(), poMEMDSRaw);
631 1 : EXPECT_EQ(val.GetDatasetRef(), nullptr);
632 :
633 1 : EXPECT_TRUE(arg.Set(std::move(poMEMDS)));
634 1 : EXPECT_EQ(val.GetDatasetRef(), poMEMDSRaw);
635 :
636 1 : poMEMDSRaw->ReleaseRef();
637 :
638 1 : arg.SetDatasetName("foo");
639 1 : EXPECT_STREQ(val.GetName().c_str(), "foo");
640 :
641 2 : GDALArgDatasetValue val2;
642 1 : val2.Set("bar");
643 1 : arg.SetFrom(val2);
644 1 : EXPECT_STREQ(val.GetName().c_str(), "bar");
645 :
646 : auto arg2 = GDALAlgorithmArg(
647 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET), &val2);
648 1 : val2.Set("baz");
649 1 : arg.SetFrom(arg2);
650 1 : EXPECT_STREQ(val.GetName().c_str(), "baz");
651 : }
652 :
653 : {
654 2 : GDALArgDatasetValue val;
655 3 : auto decl = GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET);
656 1 : decl.SetIsOutput();
657 1 : decl.SetDatasetInputFlags(GADV_NAME);
658 1 : decl.SetDatasetOutputFlags(GADV_OBJECT);
659 2 : auto arg = GDALAlgorithmArg(decl, &val);
660 :
661 : {
662 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
663 1 : CPLErrorReset();
664 1 : arg.Set(static_cast<GDALDataset *>(nullptr));
665 1 : EXPECT_TRUE(strstr(
666 : CPLGetLastErrorMsg(),
667 : "is created by algorithm and cannot be set as an input"));
668 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
669 : }
670 :
671 : {
672 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
673 1 : CPLErrorReset();
674 1 : arg.Set(std::unique_ptr<GDALDataset>(nullptr));
675 1 : EXPECT_TRUE(strstr(
676 : CPLGetLastErrorMsg(),
677 : "is created by algorithm and cannot be set as an input"));
678 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
679 : }
680 :
681 : {
682 2 : GDALArgDatasetValue val2;
683 1 : val2.Set(std::unique_ptr<GDALDataset>(
684 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
685 : "", 1, 1, 1, GDT_Byte, nullptr)));
686 :
687 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
688 1 : CPLErrorReset();
689 1 : arg.SetFrom(val2);
690 1 : EXPECT_TRUE(strstr(
691 : CPLGetLastErrorMsg(),
692 : "is created by algorithm and cannot be set as an input"));
693 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
694 : }
695 : }
696 :
697 : {
698 2 : GDALArgDatasetValue val;
699 3 : auto decl = GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET);
700 1 : decl.SetDatasetInputFlags(0);
701 1 : decl.SetDatasetOutputFlags(0);
702 2 : auto arg = GDALAlgorithmArg(decl, &val);
703 :
704 : {
705 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
706 1 : CPLErrorReset();
707 1 : arg.Set(static_cast<GDALDataset *>(nullptr));
708 1 : EXPECT_TRUE(
709 : strstr(CPLGetLastErrorMsg(),
710 : "Dataset '' must be provided by name, not as object"));
711 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
712 : }
713 : }
714 :
715 : {
716 : class MyAlgorithm : public MyAlgorithmWithDummyRun
717 : {
718 : public:
719 1 : MyAlgorithm()
720 1 : {
721 2 : GDALArgDatasetValue val;
722 1 : AddArg("", 0, "", &val).SetDatasetInputFlags(0);
723 :
724 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
725 1 : CPLErrorReset();
726 :
727 1 : val.Set(std::unique_ptr<GDALDataset>(
728 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
729 : "", 1, 1, 1, GDT_Byte, nullptr)));
730 :
731 1 : Run();
732 :
733 1 : EXPECT_TRUE(strstr(
734 : CPLGetLastErrorMsg(),
735 : "Dataset '' must be provided by name, not as object"));
736 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
737 1 : }
738 : };
739 :
740 1 : MyAlgorithm alg;
741 : }
742 :
743 : {
744 1 : std::vector<std::string> val;
745 : auto arg = GDALAlgorithmArg(
746 2 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val);
747 : {
748 6 : const std::vector<std::string> expected{"foo", "bar"};
749 1 : arg.Set(expected);
750 1 : EXPECT_EQ(arg.Get<std::vector<std::string>>(), expected);
751 1 : EXPECT_EQ(val, expected);
752 :
753 2 : std::vector<std::string> val2;
754 : auto arg2 = GDALAlgorithmArg(
755 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val2);
756 1 : arg2.SetFrom(arg);
757 1 : EXPECT_EQ(arg2.Get<std::vector<std::string>>(), expected);
758 :
759 : {
760 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
761 1 : CPLErrorReset();
762 1 : arg.Set(true);
763 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
764 1 : EXPECT_EQ(val, expected);
765 : }
766 : }
767 :
768 : {
769 1 : arg.Set(1);
770 5 : const std::vector<std::string> expected{"1"};
771 1 : EXPECT_EQ(val, expected);
772 : }
773 :
774 : {
775 1 : arg.Set("1");
776 5 : const std::vector<std::string> expected{"1"};
777 1 : EXPECT_EQ(val, expected);
778 : }
779 :
780 : {
781 1 : arg.Set(std::vector<int>{1, 2});
782 6 : const std::vector<std::string> expected{"1", "2"};
783 1 : EXPECT_EQ(val, expected);
784 : }
785 :
786 : {
787 1 : arg.Set(3.5);
788 1 : ASSERT_EQ(val.size(), 1);
789 1 : EXPECT_EQ(CPLAtof(val[0].c_str()), 3.5);
790 : }
791 :
792 : {
793 1 : arg.Set(std::vector<double>{1.5, 2.5});
794 1 : ASSERT_EQ(val.size(), 2);
795 1 : EXPECT_EQ(CPLAtof(val[0].c_str()), 1.5);
796 1 : EXPECT_EQ(CPLAtof(val[1].c_str()), 2.5);
797 : }
798 :
799 : {
800 3 : arg = std::vector<std::string>{"foo", "bar"};
801 6 : const std::vector<std::string> expected{"foo", "bar"};
802 1 : EXPECT_EQ(val, expected);
803 : }
804 : }
805 : {
806 2 : std::vector<int> val;
807 : auto arg = GDALAlgorithmArg(
808 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val);
809 : {
810 2 : const std::vector<int> expected{1, 2};
811 1 : arg.Set(expected);
812 1 : EXPECT_EQ(arg.Get<std::vector<int>>(), expected);
813 1 : EXPECT_EQ(val, expected);
814 :
815 2 : std::vector<int> val2;
816 : auto arg2 = GDALAlgorithmArg(
817 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val2);
818 1 : arg2.SetFrom(arg);
819 1 : EXPECT_EQ(arg2.Get<std::vector<int>>(), expected);
820 :
821 : {
822 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
823 1 : CPLErrorReset();
824 1 : arg.Set(true);
825 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
826 1 : EXPECT_EQ(val, expected);
827 : }
828 : }
829 :
830 : {
831 1 : arg.Set(3);
832 2 : const std::vector<int> expected{3};
833 1 : EXPECT_EQ(val, expected);
834 : }
835 :
836 : {
837 1 : arg.Set(4.0);
838 2 : const std::vector<int> expected{4};
839 1 : EXPECT_EQ(val, expected);
840 : }
841 :
842 : {
843 1 : arg.Set("5");
844 2 : const std::vector<int> expected{5};
845 1 : EXPECT_EQ(val, expected);
846 : }
847 :
848 : {
849 1 : arg.Set(std::vector<double>{6.0});
850 2 : const std::vector<int> expected{6};
851 1 : EXPECT_EQ(val, expected);
852 : }
853 :
854 : {
855 2 : arg.Set(std::vector<std::string>{"7"});
856 2 : const std::vector<int> expected{7};
857 1 : EXPECT_EQ(val, expected);
858 : }
859 :
860 : {
861 1 : arg = std::vector<int>{4, 5};
862 2 : const std::vector<int> expected{4, 5};
863 1 : EXPECT_EQ(val, expected);
864 : }
865 :
866 : {
867 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
868 1 : CPLErrorReset();
869 :
870 1 : arg.Set(6.5);
871 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
872 : }
873 :
874 : {
875 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
876 1 : CPLErrorReset();
877 :
878 1 : arg.Set("foo");
879 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
880 : }
881 :
882 : {
883 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
884 1 : CPLErrorReset();
885 :
886 1 : arg.Set("12345679812346798123456");
887 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
888 : }
889 :
890 : {
891 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
892 1 : CPLErrorReset();
893 :
894 1 : arg.Set(std::vector<double>{6.5});
895 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
896 : }
897 :
898 : {
899 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
900 1 : CPLErrorReset();
901 :
902 2 : arg.Set(std::vector<std::string>{"foo"});
903 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
904 : }
905 :
906 : {
907 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
908 1 : CPLErrorReset();
909 :
910 2 : arg.Set(std::vector<std::string>{"12345679812346798123456"});
911 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
912 : }
913 : }
914 : {
915 2 : std::vector<double> val;
916 : auto arg = GDALAlgorithmArg(
917 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val);
918 : {
919 2 : const std::vector<double> expected{1.5, 2.5};
920 1 : arg.Set(expected);
921 1 : EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
922 1 : EXPECT_EQ(val, expected);
923 :
924 2 : std::vector<double> val2;
925 : auto arg2 = GDALAlgorithmArg(
926 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val2);
927 1 : arg2.SetFrom(arg);
928 1 : EXPECT_EQ(arg2.Get<std::vector<double>>(), expected);
929 :
930 : {
931 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
932 1 : CPLErrorReset();
933 1 : arg.Set(true);
934 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
935 1 : EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
936 : }
937 : }
938 :
939 : {
940 1 : arg.Set(3);
941 2 : const std::vector<double> expected{3.0};
942 1 : EXPECT_EQ(val, expected);
943 : }
944 :
945 : {
946 1 : arg.Set("4.5");
947 2 : const std::vector<double> expected{4.5};
948 1 : EXPECT_EQ(val, expected);
949 : }
950 :
951 : {
952 1 : arg.Set(std::vector<int>{5});
953 2 : const std::vector<double> expected{5.0};
954 1 : EXPECT_EQ(val, expected);
955 : }
956 :
957 : {
958 1 : arg.Set(std::vector<double>{6.5});
959 2 : const std::vector<double> expected{6.5};
960 1 : EXPECT_EQ(val, expected);
961 : }
962 :
963 : {
964 2 : arg.Set(std::vector<std::string>{"7.5"});
965 2 : const std::vector<double> expected{7.5};
966 1 : EXPECT_EQ(val, expected);
967 : }
968 :
969 : {
970 1 : arg = std::vector<double>{4, 5};
971 2 : const std::vector<double> expected{4, 5};
972 1 : EXPECT_EQ(val, expected);
973 : }
974 :
975 : {
976 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
977 1 : CPLErrorReset();
978 :
979 1 : arg.Set("foo");
980 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
981 : }
982 :
983 : {
984 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
985 1 : CPLErrorReset();
986 :
987 2 : arg.Set(std::vector<std::string>{"foo"});
988 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
989 : }
990 : }
991 : {
992 2 : std::vector<GDALArgDatasetValue> val;
993 : auto arg = GDALAlgorithmArg(
994 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val);
995 : {
996 2 : std::vector<GDALArgDatasetValue> val2;
997 1 : val2.emplace_back(GDALArgDatasetValue("foo"));
998 1 : val2.emplace_back(GDALArgDatasetValue("bar"));
999 1 : arg.Set(std::move(val2));
1000 1 : EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
1001 1 : EXPECT_EQ(val.size(), 2U);
1002 : }
1003 :
1004 2 : std::vector<GDALArgDatasetValue> val2;
1005 : auto arg2 = GDALAlgorithmArg(
1006 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val2);
1007 1 : arg2.SetFrom(arg);
1008 1 : EXPECT_EQ(arg2.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
1009 :
1010 : {
1011 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1012 1 : CPLErrorReset();
1013 1 : arg.Set(true);
1014 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1015 1 : EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
1016 : }
1017 : }
1018 : }
1019 :
1020 4 : TEST_F(test_gdal_algorithm, RunValidationActions)
1021 : {
1022 1 : int val = 0;
1023 : auto arg = GDALInConstructionAlgorithmArg(
1024 3 : nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
1025 3 : arg.AddValidationAction([&arg]() { return arg.Get<int>() == 1; });
1026 1 : EXPECT_TRUE(arg.Set(1));
1027 1 : EXPECT_FALSE(arg.Set(2));
1028 1 : }
1029 :
1030 4 : TEST_F(test_gdal_algorithm, SetIsCRSArg_wrong_type)
1031 : {
1032 1 : int val = 0;
1033 : auto arg = GDALInConstructionAlgorithmArg(
1034 3 : nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
1035 : {
1036 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1037 1 : CPLErrorReset();
1038 1 : arg.SetIsCRSArg();
1039 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1040 : }
1041 1 : }
1042 :
1043 4 : TEST_F(test_gdal_algorithm, wrong_long_name_dash)
1044 : {
1045 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1046 : {
1047 : public:
1048 : bool m_flag = false;
1049 :
1050 1 : MyAlgorithm()
1051 1 : {
1052 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1053 1 : CPLErrorReset();
1054 1 : AddArg("-", 0, "", &m_flag);
1055 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1056 1 : }
1057 : };
1058 :
1059 1 : MyAlgorithm alg;
1060 1 : CPL_IGNORE_RET_VAL(alg.Run());
1061 1 : }
1062 :
1063 4 : TEST_F(test_gdal_algorithm, wrong_long_name_contains_equal)
1064 : {
1065 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1066 : {
1067 : public:
1068 : bool m_flag = false;
1069 :
1070 1 : MyAlgorithm()
1071 1 : {
1072 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1073 1 : CPLErrorReset();
1074 1 : AddArg("foo=bar", 0, "", &m_flag);
1075 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1076 1 : }
1077 : };
1078 :
1079 1 : MyAlgorithm alg;
1080 1 : }
1081 :
1082 4 : TEST_F(test_gdal_algorithm, long_name_duplicated)
1083 : {
1084 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1085 : {
1086 : public:
1087 : bool m_flag = false;
1088 :
1089 1 : MyAlgorithm()
1090 1 : {
1091 1 : AddArg("foo", 0, "", &m_flag);
1092 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1093 1 : CPLErrorReset();
1094 1 : AddArg("foo", 0, "", &m_flag);
1095 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1096 1 : }
1097 : };
1098 :
1099 1 : MyAlgorithm alg;
1100 1 : }
1101 :
1102 4 : TEST_F(test_gdal_algorithm, wrong_short_name)
1103 : {
1104 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1105 : {
1106 : public:
1107 : bool m_flag = false;
1108 :
1109 1 : MyAlgorithm()
1110 1 : {
1111 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1112 1 : CPLErrorReset();
1113 1 : AddArg("", '-', "", &m_flag);
1114 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1115 1 : }
1116 : };
1117 :
1118 1 : MyAlgorithm alg;
1119 1 : }
1120 :
1121 4 : TEST_F(test_gdal_algorithm, short_name_duplicated)
1122 : {
1123 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1124 : {
1125 : public:
1126 : bool m_flag = false;
1127 :
1128 1 : MyAlgorithm()
1129 1 : {
1130 1 : AddArg("", 'x', "", &m_flag);
1131 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1132 1 : CPLErrorReset();
1133 1 : AddArg("", 'x', "", &m_flag);
1134 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1135 1 : }
1136 : };
1137 :
1138 1 : MyAlgorithm alg;
1139 1 : }
1140 :
1141 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias)
1142 : {
1143 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1144 : {
1145 : public:
1146 : bool m_flag = false;
1147 :
1148 1 : MyAlgorithm()
1149 1 : {
1150 1 : AddArg("flag", 'f', "boolean flag", &m_flag).AddAlias("alias");
1151 1 : }
1152 : };
1153 :
1154 2 : MyAlgorithm alg;
1155 1 : alg.GetUsageForCLI(false);
1156 1 : EXPECT_NE(alg.GetArg("flag"), nullptr);
1157 1 : EXPECT_NE(alg.GetArg("--flag"), nullptr);
1158 1 : EXPECT_NE(alg.GetArg("-f"), nullptr);
1159 1 : EXPECT_NE(alg.GetArg("f"), nullptr);
1160 1 : EXPECT_NE(alg.GetArg("alias"), nullptr);
1161 1 : EXPECT_EQ(alg.GetArg("invalid"), nullptr);
1162 1 : EXPECT_EQ(alg.GetArg("-"), nullptr);
1163 :
1164 2 : EXPECT_STREQ(alg["flag"].GetName().c_str(), "flag");
1165 :
1166 1 : alg["flag"] = true;
1167 1 : EXPECT_EQ(alg.m_flag, true);
1168 :
1169 2 : EXPECT_STREQ(const_cast<const MyAlgorithm &>(alg)["flag"].GetName().c_str(),
1170 : "flag");
1171 :
1172 : {
1173 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1174 1 : CPLErrorReset();
1175 2 : EXPECT_STREQ(alg["invalid"].GetName().c_str(), "dummy");
1176 1 : EXPECT_NE(CPLGetLastErrorType(), CE_None);
1177 : }
1178 : {
1179 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1180 1 : CPLErrorReset();
1181 2 : EXPECT_STREQ(
1182 : const_cast<const MyAlgorithm &>(alg)["invalid"].GetName().c_str(),
1183 : "dummy");
1184 1 : EXPECT_NE(CPLGetLastErrorType(), CE_None);
1185 : }
1186 :
1187 : {
1188 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1189 1 : CPLErrorReset();
1190 1 : EXPECT_EQ(alg.GetArg("flig", /* suggestionAllowed = */ true), nullptr);
1191 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
1192 : "Argument 'flig' is unknown. Do you mean 'flag'?");
1193 : }
1194 :
1195 : {
1196 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1197 1 : CPLErrorReset();
1198 1 : EXPECT_EQ(alg.GetArg("flga", /* suggestionAllowed = */ true), nullptr);
1199 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
1200 : "Argument 'flga' is unknown. Do you mean 'flag'?");
1201 : }
1202 1 : }
1203 :
1204 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias_redundant)
1205 : {
1206 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1207 : {
1208 : public:
1209 : bool m_flag = false;
1210 : bool m_flag2 = false;
1211 :
1212 1 : MyAlgorithm()
1213 1 : {
1214 1 : AddArg("flag", 'F', "boolean flag", &m_flag).AddAlias("alias");
1215 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1216 1 : CPLErrorReset();
1217 1 : AddArg("flag2", '9', "boolean flag2", &m_flag2).AddAlias("alias");
1218 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1219 1 : }
1220 : };
1221 :
1222 2 : MyAlgorithm alg;
1223 1 : EXPECT_NE(alg.GetArg("alias"), nullptr);
1224 1 : }
1225 :
1226 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddHiddenAlias)
1227 : {
1228 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1229 : {
1230 : public:
1231 : bool m_flag = false;
1232 :
1233 1 : MyAlgorithm()
1234 1 : {
1235 2 : AddArg("flag", 'f', "boolean flag", &m_flag)
1236 1 : .AddHiddenAlias("hidden_alias");
1237 1 : }
1238 : };
1239 :
1240 2 : MyAlgorithm alg;
1241 1 : EXPECT_NE(alg.GetArg("hidden_alias"), nullptr);
1242 1 : }
1243 :
1244 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_SetPositional)
1245 : {
1246 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1247 : {
1248 : public:
1249 : int m_val = 0;
1250 :
1251 1 : MyAlgorithm()
1252 1 : {
1253 1 : AddArg("option", 0, "option", &m_val).SetPositional();
1254 1 : }
1255 : };
1256 :
1257 2 : MyAlgorithm alg;
1258 1 : EXPECT_TRUE(alg.GetArg("option")->IsPositional());
1259 1 : }
1260 :
1261 4 : TEST_F(test_gdal_algorithm, GDALArgDatasetValue)
1262 : {
1263 : {
1264 : auto poDS = std::unique_ptr<GDALDataset>(
1265 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
1266 2 : "", 1, 1, 1, GDT_Byte, nullptr));
1267 1 : auto poDSRaw = poDS.get();
1268 2 : GDALArgDatasetValue value(poDS.release());
1269 1 : EXPECT_EQ(value.GetDatasetRef(), poDSRaw);
1270 1 : EXPECT_STREQ(value.GetName().c_str(), poDSRaw->GetDescription());
1271 :
1272 2 : GDALArgDatasetValue value2;
1273 1 : value2 = std::move(value);
1274 1 : EXPECT_STREQ(value2.GetName().c_str(), poDSRaw->GetDescription());
1275 :
1276 1 : poDSRaw->ReleaseRef();
1277 : }
1278 : {
1279 3 : GDALArgDatasetValue value("foo");
1280 1 : EXPECT_STREQ(value.GetName().c_str(), "foo");
1281 :
1282 2 : GDALArgDatasetValue value2;
1283 1 : value2 = std::move(value);
1284 1 : EXPECT_STREQ(value2.GetName().c_str(), "foo");
1285 : }
1286 1 : }
1287 :
1288 4 : TEST_F(test_gdal_algorithm, bool_flag)
1289 : {
1290 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1291 : {
1292 : public:
1293 : bool m_flag = false;
1294 : std::string m_dummy{};
1295 :
1296 14 : MyAlgorithm()
1297 14 : {
1298 14 : AddArg("flag", 'f', "boolean flag", &m_flag);
1299 14 : AddArg("of", 0, "", &m_dummy);
1300 14 : }
1301 : };
1302 :
1303 : {
1304 2 : MyAlgorithm alg;
1305 1 : alg.GetUsageForCLI(true);
1306 1 : alg.GetUsageForCLI(false);
1307 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
1308 1 : EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "test");
1309 : }
1310 :
1311 : {
1312 2 : MyAlgorithm alg;
1313 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
1314 1 : EXPECT_TRUE(alg.m_flag);
1315 : }
1316 :
1317 : {
1318 2 : MyAlgorithm alg;
1319 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=true"}));
1320 1 : EXPECT_TRUE(alg.m_flag);
1321 : }
1322 :
1323 : {
1324 2 : MyAlgorithm alg;
1325 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=false"}));
1326 1 : EXPECT_FALSE(alg.m_flag);
1327 : }
1328 :
1329 : {
1330 2 : MyAlgorithm alg;
1331 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1332 1 : CPLErrorReset();
1333 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag=invalid"}));
1334 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1335 : }
1336 :
1337 : {
1338 2 : MyAlgorithm alg;
1339 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1340 1 : CPLErrorReset();
1341 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag", "--flag"}));
1342 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1343 : }
1344 :
1345 : {
1346 2 : MyAlgorithm alg;
1347 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1348 1 : CPLErrorReset();
1349 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flig=invalid"}));
1350 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1351 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "test: Option '--flig' is "
1352 : "unknown. Do you mean '--flag'?");
1353 : }
1354 :
1355 : {
1356 2 : MyAlgorithm alg;
1357 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1358 1 : CPLErrorReset();
1359 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-x", "foo"}));
1360 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1361 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "test: Short name option 'x' is "
1362 : "unknown.");
1363 : }
1364 :
1365 : {
1366 2 : MyAlgorithm alg;
1367 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1368 1 : CPLErrorReset();
1369 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-of", "foo"}));
1370 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1371 1 : EXPECT_STREQ(
1372 : CPLGetLastErrorMsg(),
1373 : "test: Short name option 'o' is "
1374 : "unknown. Do you mean '--of' (with leading double dash) ?");
1375 : }
1376 :
1377 : {
1378 2 : MyAlgorithm alg;
1379 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1380 1 : CPLErrorReset();
1381 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-ofx", "foo"}));
1382 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1383 1 : EXPECT_STREQ(
1384 : CPLGetLastErrorMsg(),
1385 : "test: Short name option 'o' is "
1386 : "unknown. Do you mean '--of' (with leading double dash) ?");
1387 : }
1388 :
1389 : {
1390 2 : MyAlgorithm alg;
1391 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1392 1 : CPLErrorReset();
1393 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--invalid"}));
1394 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1395 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
1396 : "test: Option '--invalid' is unknown.");
1397 : }
1398 :
1399 : {
1400 2 : MyAlgorithm alg;
1401 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1402 1 : CPLErrorReset();
1403 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-"}));
1404 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1405 : }
1406 :
1407 : {
1408 2 : MyAlgorithm alg;
1409 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1410 1 : CPLErrorReset();
1411 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-x"}));
1412 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1413 : }
1414 :
1415 : {
1416 2 : MyAlgorithm alg;
1417 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1418 1 : CPLErrorReset();
1419 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-xy"}));
1420 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1421 : }
1422 1 : }
1423 :
1424 4 : TEST_F(test_gdal_algorithm, int)
1425 : {
1426 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1427 : {
1428 : public:
1429 : int m_val = 0;
1430 :
1431 5 : MyAlgorithm()
1432 5 : {
1433 5 : AddArg("val", 0, "", &m_val);
1434 5 : }
1435 : };
1436 :
1437 : {
1438 2 : MyAlgorithm alg;
1439 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
1440 1 : EXPECT_EQ(alg.m_val, 5);
1441 : }
1442 :
1443 : {
1444 2 : MyAlgorithm alg;
1445 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1446 1 : CPLErrorReset();
1447 : // Missing value
1448 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val"}));
1449 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1450 1 : EXPECT_EQ(alg.m_val, 0);
1451 : }
1452 :
1453 : {
1454 2 : MyAlgorithm alg;
1455 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1456 1 : CPLErrorReset();
1457 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
1458 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1459 1 : EXPECT_EQ(alg.m_val, 0);
1460 : }
1461 :
1462 : {
1463 2 : MyAlgorithm alg;
1464 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1465 1 : CPLErrorReset();
1466 3 : EXPECT_FALSE(
1467 : alg.ParseCommandLineArguments({"--val=12345679812346798123456"}));
1468 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1469 1 : EXPECT_EQ(alg.m_val, 0);
1470 : }
1471 :
1472 : {
1473 2 : MyAlgorithm alg;
1474 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1475 1 : CPLErrorReset();
1476 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1.5"}));
1477 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1478 1 : EXPECT_EQ(alg.m_val, 0);
1479 : }
1480 1 : }
1481 :
1482 4 : TEST_F(test_gdal_algorithm, int_min_val_included)
1483 : {
1484 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1485 : {
1486 : public:
1487 : int m_val = 0;
1488 :
1489 2 : MyAlgorithm()
1490 2 : {
1491 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMinValueIncluded(0);
1492 2 : const auto [minVal, minValIncluded] = arg.GetMinValue();
1493 2 : EXPECT_EQ(minVal, 0);
1494 2 : EXPECT_TRUE(minValIncluded);
1495 2 : }
1496 : };
1497 :
1498 : {
1499 2 : MyAlgorithm alg;
1500 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0"}));
1501 1 : EXPECT_EQ(alg.m_val, 0);
1502 : }
1503 :
1504 : {
1505 2 : MyAlgorithm alg;
1506 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1507 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=-1"}));
1508 : }
1509 1 : }
1510 :
1511 4 : TEST_F(test_gdal_algorithm, int_min_val_excluded)
1512 : {
1513 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1514 : {
1515 : public:
1516 : int m_val = 0;
1517 :
1518 2 : MyAlgorithm()
1519 2 : {
1520 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMinValueExcluded(0);
1521 2 : const auto [minVal, minValIncluded] = arg.GetMinValue();
1522 2 : EXPECT_EQ(minVal, 0);
1523 2 : EXPECT_FALSE(minValIncluded);
1524 2 : }
1525 : };
1526 :
1527 : {
1528 2 : MyAlgorithm alg;
1529 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1"}));
1530 1 : EXPECT_EQ(alg.m_val, 1);
1531 : }
1532 :
1533 : {
1534 2 : MyAlgorithm alg;
1535 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1536 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=0"}));
1537 : }
1538 1 : }
1539 :
1540 4 : TEST_F(test_gdal_algorithm, int_max_val_included)
1541 : {
1542 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1543 : {
1544 : public:
1545 : int m_val = 0;
1546 :
1547 2 : MyAlgorithm()
1548 2 : {
1549 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueIncluded(5);
1550 2 : const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
1551 2 : EXPECT_EQ(maxVal, 5);
1552 2 : EXPECT_TRUE(maxValIncluded);
1553 2 : }
1554 : };
1555 :
1556 : {
1557 2 : MyAlgorithm alg;
1558 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
1559 1 : EXPECT_EQ(alg.m_val, 5);
1560 : }
1561 :
1562 : {
1563 2 : MyAlgorithm alg;
1564 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1565 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=6"}));
1566 : }
1567 1 : }
1568 :
1569 4 : TEST_F(test_gdal_algorithm, int_max_val_excluded)
1570 : {
1571 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1572 : {
1573 : public:
1574 : int m_val = 0;
1575 :
1576 2 : MyAlgorithm()
1577 2 : {
1578 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueExcluded(5);
1579 2 : const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
1580 2 : EXPECT_EQ(maxVal, 5);
1581 2 : EXPECT_FALSE(maxValIncluded);
1582 2 : }
1583 : };
1584 :
1585 : {
1586 2 : MyAlgorithm alg;
1587 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=4"}));
1588 1 : EXPECT_EQ(alg.m_val, 4);
1589 : }
1590 :
1591 : {
1592 2 : MyAlgorithm alg;
1593 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1594 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5"}));
1595 : }
1596 1 : }
1597 :
1598 4 : TEST_F(test_gdal_algorithm, double_min_val_included)
1599 : {
1600 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1601 : {
1602 : public:
1603 : double m_val = 0;
1604 :
1605 2 : MyAlgorithm()
1606 2 : {
1607 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMinValueIncluded(0);
1608 2 : const auto [minVal, minValIncluded] = arg.GetMinValue();
1609 2 : EXPECT_EQ(minVal, 0);
1610 2 : EXPECT_TRUE(minValIncluded);
1611 2 : }
1612 : };
1613 :
1614 : {
1615 2 : MyAlgorithm alg;
1616 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0"}));
1617 1 : EXPECT_EQ(alg.m_val, 0);
1618 : }
1619 :
1620 : {
1621 2 : MyAlgorithm alg;
1622 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1623 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=-0.1"}));
1624 : }
1625 1 : }
1626 :
1627 4 : TEST_F(test_gdal_algorithm, double_min_val_excluded)
1628 : {
1629 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1630 : {
1631 : public:
1632 : double m_val = 0;
1633 :
1634 2 : MyAlgorithm()
1635 2 : {
1636 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMinValueExcluded(0);
1637 2 : const auto [minVal, minValIncluded] = arg.GetMinValue();
1638 2 : EXPECT_EQ(minVal, 0);
1639 2 : EXPECT_FALSE(minValIncluded);
1640 2 : }
1641 : };
1642 :
1643 : {
1644 2 : MyAlgorithm alg;
1645 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0.1"}));
1646 1 : EXPECT_EQ(alg.m_val, 0.1);
1647 : }
1648 :
1649 : {
1650 2 : MyAlgorithm alg;
1651 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1652 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=0"}));
1653 : }
1654 1 : }
1655 :
1656 4 : TEST_F(test_gdal_algorithm, double_max_val_included)
1657 : {
1658 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1659 : {
1660 : public:
1661 : double m_val = 0;
1662 :
1663 2 : MyAlgorithm()
1664 2 : {
1665 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueIncluded(5);
1666 2 : const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
1667 2 : EXPECT_EQ(maxVal, 5);
1668 2 : EXPECT_TRUE(maxValIncluded);
1669 2 : }
1670 : };
1671 :
1672 : {
1673 2 : MyAlgorithm alg;
1674 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
1675 1 : EXPECT_EQ(alg.m_val, 5);
1676 : }
1677 :
1678 : {
1679 2 : MyAlgorithm alg;
1680 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1681 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5.1"}));
1682 : }
1683 1 : }
1684 :
1685 4 : TEST_F(test_gdal_algorithm, double_max_val_excluded)
1686 : {
1687 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1688 : {
1689 : public:
1690 : double m_val = 0;
1691 :
1692 2 : MyAlgorithm()
1693 2 : {
1694 2 : auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueExcluded(5);
1695 2 : const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
1696 2 : EXPECT_EQ(maxVal, 5);
1697 2 : EXPECT_FALSE(maxValIncluded);
1698 2 : }
1699 : };
1700 :
1701 : {
1702 2 : MyAlgorithm alg;
1703 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=4.9"}));
1704 1 : EXPECT_EQ(alg.m_val, 4.9);
1705 : }
1706 :
1707 : {
1708 2 : MyAlgorithm alg;
1709 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1710 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5"}));
1711 : }
1712 1 : }
1713 :
1714 4 : TEST_F(test_gdal_algorithm, string_min_char_count)
1715 : {
1716 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1717 : {
1718 : public:
1719 : std::string m_val{};
1720 :
1721 2 : MyAlgorithm()
1722 2 : {
1723 2 : AddArg("val", 0, "", &m_val).SetMinCharCount(2);
1724 2 : }
1725 : };
1726 :
1727 : {
1728 2 : MyAlgorithm alg;
1729 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=ab"}));
1730 1 : EXPECT_STREQ(alg.m_val.c_str(), "ab");
1731 : }
1732 :
1733 : {
1734 2 : MyAlgorithm alg;
1735 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1736 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=a"}));
1737 : }
1738 1 : }
1739 :
1740 4 : TEST_F(test_gdal_algorithm, string_vector_min_char_count)
1741 : {
1742 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1743 : {
1744 : public:
1745 : std::vector<std::string> m_val{};
1746 :
1747 2 : MyAlgorithm()
1748 2 : {
1749 2 : AddArg("val", 0, "", &m_val).SetMinCharCount(2);
1750 2 : }
1751 : };
1752 :
1753 : {
1754 2 : MyAlgorithm alg;
1755 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=ab"}));
1756 1 : EXPECT_STREQ(alg.m_val[0].c_str(), "ab");
1757 : }
1758 :
1759 : {
1760 2 : MyAlgorithm alg;
1761 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1762 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=a"}));
1763 : }
1764 1 : }
1765 :
1766 4 : TEST_F(test_gdal_algorithm, SetDisplayInJSONUsage)
1767 : {
1768 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1769 : {
1770 : public:
1771 1 : MyAlgorithm()
1772 1 : {
1773 1 : SetDisplayInJSONUsage(false);
1774 1 : }
1775 : };
1776 :
1777 : {
1778 1 : MyAlgorithm alg;
1779 1 : alg.GetUsageForCLI(false);
1780 1 : alg.GetUsageAsJSON();
1781 : }
1782 1 : }
1783 :
1784 4 : TEST_F(test_gdal_algorithm, int_with_default)
1785 : {
1786 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1787 : {
1788 : public:
1789 : int m_val = 0;
1790 :
1791 1 : MyAlgorithm()
1792 1 : {
1793 1 : AddArg("val", 0, "", &m_val).SetDefault(3);
1794 1 : }
1795 : };
1796 :
1797 : {
1798 2 : MyAlgorithm alg;
1799 1 : alg.GetUsageForCLI(false);
1800 1 : alg.GetUsageAsJSON();
1801 1 : EXPECT_TRUE(alg.ValidateArguments());
1802 1 : EXPECT_EQ(alg.m_val, 3);
1803 : }
1804 1 : }
1805 :
1806 4 : TEST_F(test_gdal_algorithm, double)
1807 : {
1808 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1809 : {
1810 : public:
1811 : double m_val = 0;
1812 :
1813 2 : MyAlgorithm()
1814 2 : {
1815 2 : AddArg("val", 0, "", &m_val);
1816 2 : }
1817 : };
1818 :
1819 : {
1820 2 : MyAlgorithm alg;
1821 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5"}));
1822 1 : EXPECT_EQ(alg.m_val, 1.5);
1823 : }
1824 :
1825 : {
1826 2 : MyAlgorithm alg;
1827 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1828 1 : CPLErrorReset();
1829 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
1830 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1831 1 : EXPECT_EQ(alg.m_val, 0);
1832 : }
1833 1 : }
1834 :
1835 4 : TEST_F(test_gdal_algorithm, double_with_default)
1836 : {
1837 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1838 : {
1839 : public:
1840 : double m_val = 0;
1841 :
1842 1 : MyAlgorithm()
1843 1 : {
1844 1 : AddArg("val", 0, "", &m_val).SetDefault(3.5);
1845 1 : }
1846 : };
1847 :
1848 : {
1849 2 : MyAlgorithm alg;
1850 1 : alg.GetUsageForCLI(false);
1851 1 : alg.GetUsageAsJSON();
1852 1 : EXPECT_TRUE(alg.ValidateArguments());
1853 1 : EXPECT_EQ(alg.m_val, 3.5);
1854 : }
1855 1 : }
1856 :
1857 4 : TEST_F(test_gdal_algorithm, string_with_default)
1858 : {
1859 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1860 : {
1861 : public:
1862 : std::string m_val{};
1863 :
1864 1 : MyAlgorithm()
1865 1 : {
1866 1 : AddArg("val", 0, "", &m_val).SetDefault("foo");
1867 1 : }
1868 : };
1869 :
1870 : {
1871 2 : MyAlgorithm alg;
1872 1 : alg.GetUsageForCLI(false);
1873 1 : alg.GetUsageAsJSON();
1874 1 : EXPECT_TRUE(alg.ValidateArguments());
1875 1 : EXPECT_STREQ(alg.m_val.c_str(), "foo");
1876 : }
1877 1 : }
1878 :
1879 4 : TEST_F(test_gdal_algorithm, dataset)
1880 : {
1881 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1882 : {
1883 : public:
1884 : GDALArgDatasetValue m_val{};
1885 :
1886 5 : MyAlgorithm()
1887 5 : {
1888 5 : AddArg("val", 0, "", &m_val).SetRequired();
1889 5 : }
1890 : };
1891 :
1892 : {
1893 2 : MyAlgorithm alg;
1894 3 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1895 : {"--val=" GCORE_DATA_DIR "byte.tif"}));
1896 1 : EXPECT_NE(alg.m_val.GetDatasetRef(), nullptr);
1897 : }
1898 :
1899 : {
1900 2 : MyAlgorithm alg;
1901 : auto poDS = std::unique_ptr<GDALDataset>(
1902 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
1903 2 : "", 1, 1, 1, GDT_Byte, nullptr));
1904 1 : auto poDSRaw = poDS.get();
1905 1 : alg.GetArg("val")->Set(poDS.release());
1906 1 : EXPECT_EQ(alg.m_val.GetDatasetRef(), poDSRaw);
1907 1 : poDSRaw->ReleaseRef();
1908 : }
1909 :
1910 : {
1911 2 : MyAlgorithm alg;
1912 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1913 1 : CPLErrorReset();
1914 3 : EXPECT_FALSE(
1915 : alg.ParseCommandLineArguments({"--val=i_do_not_exist.tif"}));
1916 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1917 : }
1918 :
1919 : {
1920 2 : MyAlgorithm alg;
1921 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1922 1 : CPLErrorReset();
1923 1 : EXPECT_FALSE(alg.Run());
1924 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1925 : }
1926 :
1927 : {
1928 2 : MyAlgorithm alg;
1929 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1930 1 : CPLErrorReset();
1931 2 : GDALArgDatasetValue value;
1932 1 : alg.GetArg("val")->SetFrom(value);
1933 1 : EXPECT_FALSE(alg.Run());
1934 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1935 : }
1936 1 : }
1937 :
1938 4 : TEST_F(test_gdal_algorithm, input_update)
1939 : {
1940 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1941 : {
1942 : public:
1943 : GDALArgDatasetValue m_input{};
1944 : GDALArgDatasetValue m_output{};
1945 : bool m_update = false;
1946 :
1947 1 : MyAlgorithm()
1948 1 : {
1949 1 : AddInputDatasetArg(&m_input);
1950 1 : AddUpdateArg(&m_update);
1951 1 : }
1952 : };
1953 :
1954 : {
1955 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
1956 1 : if (!poDriver)
1957 : {
1958 0 : GTEST_SKIP() << "GPKG support missing";
1959 : }
1960 : else
1961 : {
1962 : std::string osTmpFilename =
1963 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
1964 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
1965 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
1966 1 : poDS->CreateLayer("foo");
1967 1 : poDS.reset();
1968 :
1969 1 : MyAlgorithm alg;
1970 1 : EXPECT_TRUE(!alg.GetUsageAsJSON().empty());
1971 4 : EXPECT_TRUE(
1972 : alg.ParseCommandLineArguments({"--update", osTmpFilename}));
1973 1 : ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
1974 1 : EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
1975 :
1976 1 : alg.Finalize();
1977 :
1978 1 : VSIUnlink(osTmpFilename.c_str());
1979 : }
1980 : }
1981 : }
1982 :
1983 4 : TEST_F(test_gdal_algorithm, same_input_output_dataset_sqlite)
1984 : {
1985 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1986 : {
1987 : public:
1988 : GDALArgDatasetValue m_input{};
1989 : GDALArgDatasetValue m_output{};
1990 : bool m_update = false;
1991 :
1992 1 : MyAlgorithm()
1993 1 : {
1994 1 : AddInputDatasetArg(&m_input);
1995 1 : AddOutputDatasetArg(&m_output).SetDatasetInputFlags(GADV_NAME |
1996 1 : GADV_OBJECT);
1997 1 : AddUpdateArg(&m_update);
1998 1 : }
1999 : };
2000 :
2001 : {
2002 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
2003 1 : if (!poDriver)
2004 : {
2005 0 : GTEST_SKIP() << "GPKG support missing";
2006 : }
2007 : else
2008 : {
2009 : std::string osTmpFilename =
2010 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
2011 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
2012 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
2013 1 : poDS->CreateLayer("foo");
2014 1 : poDS.reset();
2015 :
2016 1 : MyAlgorithm alg;
2017 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2018 : {"--update", osTmpFilename, osTmpFilename}));
2019 1 : ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
2020 1 : EXPECT_NE(alg.m_output.GetDatasetRef(), nullptr);
2021 1 : EXPECT_EQ(alg.m_input.GetDatasetRef(),
2022 : alg.m_output.GetDatasetRef());
2023 1 : EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
2024 :
2025 1 : alg.Finalize();
2026 :
2027 1 : VSIUnlink(osTmpFilename.c_str());
2028 : }
2029 : }
2030 : }
2031 :
2032 4 : TEST_F(test_gdal_algorithm, output_dataset_created_by_alg)
2033 : {
2034 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2035 : {
2036 : public:
2037 : GDALArgDatasetValue m_output{};
2038 :
2039 2 : MyAlgorithm()
2040 2 : {
2041 2 : AddOutputDatasetArg(&m_output)
2042 2 : .SetDatasetInputFlags(GADV_NAME)
2043 2 : .SetDatasetOutputFlags(GADV_OBJECT);
2044 2 : }
2045 : };
2046 :
2047 : {
2048 1 : MyAlgorithm alg;
2049 1 : alg.GetUsageForCLI(false);
2050 : }
2051 :
2052 : {
2053 2 : MyAlgorithm alg;
2054 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--output=-"}));
2055 1 : EXPECT_STREQ(alg.m_output.GetName().c_str(), "/vsistdout/");
2056 : }
2057 1 : }
2058 :
2059 4 : TEST_F(test_gdal_algorithm, string_choices)
2060 : {
2061 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2062 : {
2063 : public:
2064 : std::string m_val{};
2065 :
2066 4 : MyAlgorithm()
2067 4 : {
2068 8 : AddArg("val", 0, "", &m_val)
2069 4 : .SetChoices("foo", "bar")
2070 4 : .SetHiddenChoices("baz");
2071 4 : }
2072 : };
2073 :
2074 : {
2075 2 : MyAlgorithm alg;
2076 1 : alg.GetUsageForCLI(false);
2077 :
2078 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo"}));
2079 1 : EXPECT_STREQ(alg.m_val.c_str(), "foo");
2080 : }
2081 :
2082 : {
2083 2 : MyAlgorithm alg;
2084 1 : alg.GetUsageForCLI(false);
2085 :
2086 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=FOO"}));
2087 1 : EXPECT_STREQ(alg.m_val.c_str(), "foo");
2088 : }
2089 :
2090 : {
2091 2 : MyAlgorithm alg;
2092 1 : alg.GetUsageForCLI(false);
2093 :
2094 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=baz"}));
2095 1 : EXPECT_STREQ(alg.m_val.c_str(), "baz");
2096 : }
2097 :
2098 : {
2099 2 : MyAlgorithm alg;
2100 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2101 1 : CPLErrorReset();
2102 1 : EXPECT_FALSE(alg.GetArg("val")->Set("invalid"));
2103 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2104 : }
2105 1 : }
2106 :
2107 4 : TEST_F(test_gdal_algorithm, vector_int)
2108 : {
2109 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2110 : {
2111 : public:
2112 : std::vector<int> m_val{};
2113 :
2114 6 : MyAlgorithm()
2115 6 : {
2116 6 : AddArg("val", 0, "", &m_val);
2117 6 : }
2118 : };
2119 :
2120 : {
2121 2 : MyAlgorithm alg;
2122 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5,6"}));
2123 2 : auto expected = std::vector<int>{5, 6};
2124 1 : EXPECT_EQ(alg.m_val, expected);
2125 : }
2126 :
2127 : {
2128 2 : MyAlgorithm alg;
2129 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2130 1 : CPLErrorReset();
2131 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
2132 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2133 1 : EXPECT_TRUE(alg.m_val.empty());
2134 : }
2135 :
2136 : {
2137 2 : MyAlgorithm alg;
2138 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2139 1 : CPLErrorReset();
2140 3 : EXPECT_FALSE(
2141 : alg.ParseCommandLineArguments({"--val=1,12345679812346798123456"}));
2142 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2143 1 : EXPECT_TRUE(alg.m_val.empty());
2144 : }
2145 :
2146 : {
2147 2 : MyAlgorithm alg;
2148 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2149 1 : CPLErrorReset();
2150 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
2151 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2152 1 : EXPECT_TRUE(alg.m_val.empty());
2153 : }
2154 :
2155 : {
2156 2 : MyAlgorithm alg;
2157 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2158 1 : CPLErrorReset();
2159 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3, ,4"}));
2160 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2161 1 : EXPECT_TRUE(alg.m_val.empty());
2162 : }
2163 :
2164 : {
2165 2 : MyAlgorithm alg;
2166 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2167 1 : CPLErrorReset();
2168 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3,,4"}));
2169 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2170 1 : EXPECT_TRUE(alg.m_val.empty());
2171 : }
2172 1 : }
2173 :
2174 4 : TEST_F(test_gdal_algorithm, vector_int_validation_fails)
2175 : {
2176 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2177 : {
2178 : public:
2179 : std::vector<int> m_val{};
2180 :
2181 1 : MyAlgorithm()
2182 1 : {
2183 2 : AddArg("val", 0, "", &m_val)
2184 : .AddValidationAction(
2185 1 : []()
2186 : {
2187 1 : CPLError(CE_Failure, CPLE_AppDefined,
2188 : "validation failed");
2189 1 : return false;
2190 1 : });
2191 1 : }
2192 : };
2193 :
2194 : {
2195 2 : MyAlgorithm alg;
2196 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2197 1 : CPLErrorReset();
2198 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
2199 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
2200 : }
2201 1 : }
2202 :
2203 4 : TEST_F(test_gdal_algorithm, vector_double)
2204 : {
2205 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2206 : {
2207 : public:
2208 : std::vector<double> m_val{};
2209 :
2210 5 : MyAlgorithm()
2211 5 : {
2212 5 : AddArg("val", 0, "", &m_val);
2213 5 : }
2214 : };
2215 :
2216 : {
2217 2 : MyAlgorithm alg;
2218 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5,2.5"}));
2219 2 : auto expected = std::vector<double>{1.5, 2.5};
2220 1 : EXPECT_EQ(alg.m_val, expected);
2221 : }
2222 :
2223 : {
2224 2 : MyAlgorithm alg;
2225 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2226 1 : CPLErrorReset();
2227 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
2228 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2229 1 : EXPECT_TRUE(alg.m_val.empty());
2230 : }
2231 :
2232 : {
2233 2 : MyAlgorithm alg;
2234 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2235 1 : CPLErrorReset();
2236 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3, ,4"}));
2237 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2238 1 : EXPECT_TRUE(alg.m_val.empty());
2239 : }
2240 :
2241 : {
2242 2 : MyAlgorithm alg;
2243 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2244 1 : CPLErrorReset();
2245 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3,,4"}));
2246 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2247 1 : EXPECT_TRUE(alg.m_val.empty());
2248 : }
2249 :
2250 : {
2251 2 : MyAlgorithm alg;
2252 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2253 1 : CPLErrorReset();
2254 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
2255 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2256 1 : EXPECT_TRUE(alg.m_val.empty());
2257 : }
2258 1 : }
2259 :
2260 4 : TEST_F(test_gdal_algorithm, vector_double_validation_fails)
2261 : {
2262 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2263 : {
2264 : public:
2265 : std::vector<double> m_val{};
2266 :
2267 1 : MyAlgorithm()
2268 1 : {
2269 2 : AddArg("val", 0, "", &m_val)
2270 : .AddValidationAction(
2271 1 : []()
2272 : {
2273 1 : CPLError(CE_Failure, CPLE_AppDefined,
2274 : "validation failed");
2275 1 : return false;
2276 1 : });
2277 1 : }
2278 : };
2279 :
2280 : {
2281 2 : MyAlgorithm alg;
2282 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2283 1 : CPLErrorReset();
2284 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
2285 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
2286 : }
2287 1 : }
2288 :
2289 4 : TEST_F(test_gdal_algorithm, vector_string)
2290 : {
2291 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2292 : {
2293 : public:
2294 : std::vector<std::string> m_val{};
2295 :
2296 1 : MyAlgorithm()
2297 1 : {
2298 1 : AddArg("val", 0, "", &m_val);
2299 1 : }
2300 : };
2301 :
2302 : {
2303 2 : MyAlgorithm alg;
2304 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
2305 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2306 1 : EXPECT_EQ(alg.m_val, expected);
2307 : }
2308 1 : }
2309 :
2310 4 : TEST_F(test_gdal_algorithm, vector_string_validation_fails)
2311 : {
2312 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2313 : {
2314 : public:
2315 : std::vector<std::string> m_val{};
2316 :
2317 1 : MyAlgorithm()
2318 1 : {
2319 2 : AddArg("val", 0, "", &m_val)
2320 : .AddValidationAction(
2321 1 : []()
2322 : {
2323 1 : CPLError(CE_Failure, CPLE_AppDefined,
2324 : "validation failed");
2325 1 : return false;
2326 1 : });
2327 1 : }
2328 : };
2329 :
2330 : {
2331 2 : MyAlgorithm alg;
2332 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2333 1 : CPLErrorReset();
2334 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
2335 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
2336 : }
2337 1 : }
2338 :
2339 4 : TEST_F(test_gdal_algorithm, vector_string_choices)
2340 : {
2341 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2342 : {
2343 : public:
2344 : std::vector<std::string> m_val{};
2345 :
2346 4 : MyAlgorithm()
2347 4 : {
2348 4 : AddArg("val", 0, "", &m_val).SetChoices("foo", "bar");
2349 4 : }
2350 : };
2351 :
2352 : {
2353 2 : MyAlgorithm alg;
2354 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
2355 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2356 1 : EXPECT_EQ(alg.m_val, expected);
2357 : }
2358 :
2359 : {
2360 2 : MyAlgorithm alg;
2361 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=FOO,BAR"}));
2362 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2363 1 : EXPECT_EQ(alg.m_val, expected);
2364 : }
2365 :
2366 : {
2367 2 : MyAlgorithm alg;
2368 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2369 1 : CPLErrorReset();
2370 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo,invalid"}));
2371 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2372 : }
2373 :
2374 : {
2375 2 : MyAlgorithm alg;
2376 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2377 1 : CPLErrorReset();
2378 4 : EXPECT_FALSE(
2379 : alg.ParseCommandLineArguments({"--val=foo", "--val=invalid"}));
2380 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2381 : }
2382 1 : }
2383 :
2384 4 : TEST_F(test_gdal_algorithm, vector_dataset)
2385 : {
2386 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2387 : {
2388 : public:
2389 : std::vector<GDALArgDatasetValue> m_val{};
2390 :
2391 3 : MyAlgorithm()
2392 3 : {
2393 3 : AddArg("val", 0, "", &m_val);
2394 3 : }
2395 : };
2396 :
2397 : {
2398 1 : MyAlgorithm alg;
2399 3 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2400 : {"--val=" GCORE_DATA_DIR "byte.tif"}));
2401 1 : ASSERT_EQ(alg.m_val.size(), 1U);
2402 1 : EXPECT_NE(alg.m_val[0].GetDatasetRef(), nullptr);
2403 : }
2404 :
2405 : {
2406 1 : MyAlgorithm alg;
2407 1 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2408 1 : CPLErrorReset();
2409 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=non_existing.tif"}));
2410 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2411 1 : ASSERT_EQ(alg.m_val.size(), 1U);
2412 1 : EXPECT_EQ(alg.m_val[0].GetDatasetRef(), nullptr);
2413 : }
2414 :
2415 : {
2416 2 : MyAlgorithm alg;
2417 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2418 1 : CPLErrorReset();
2419 1 : alg.GetArg("val")->Set(std::vector<GDALArgDatasetValue>(1));
2420 1 : EXPECT_FALSE(alg.Run());
2421 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2422 : }
2423 : }
2424 :
2425 4 : TEST_F(test_gdal_algorithm, vector_dataset_validation_fails)
2426 : {
2427 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2428 : {
2429 : public:
2430 : std::vector<GDALArgDatasetValue> m_val{};
2431 :
2432 1 : MyAlgorithm()
2433 1 : {
2434 2 : AddArg("val", 0, "", &m_val)
2435 : .AddValidationAction(
2436 1 : []()
2437 : {
2438 1 : CPLError(CE_Failure, CPLE_AppDefined,
2439 : "validation failed");
2440 1 : return false;
2441 1 : });
2442 1 : }
2443 : };
2444 :
2445 : {
2446 2 : MyAlgorithm alg;
2447 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2448 1 : CPLErrorReset();
2449 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
2450 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
2451 : }
2452 1 : }
2453 :
2454 4 : TEST_F(test_gdal_algorithm, vector_input)
2455 : {
2456 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2457 : {
2458 : public:
2459 : std::vector<GDALArgDatasetValue> m_input{};
2460 : std::vector<std::string> m_oo{};
2461 : std::vector<std::string> m_if{};
2462 : bool m_update = false;
2463 :
2464 1 : MyAlgorithm()
2465 1 : {
2466 1 : AddInputDatasetArg(&m_input);
2467 1 : AddOpenOptionsArg(&m_oo);
2468 1 : AddInputFormatsArg(&m_if);
2469 1 : AddUpdateArg(&m_update);
2470 1 : }
2471 : };
2472 :
2473 : {
2474 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
2475 1 : if (!poDriver)
2476 : {
2477 0 : GTEST_SKIP() << "GPKG support missing";
2478 : }
2479 : else
2480 : {
2481 : std::string osTmpFilename =
2482 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
2483 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
2484 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
2485 1 : poDS->CreateLayer("foo");
2486 1 : poDS.reset();
2487 :
2488 1 : MyAlgorithm alg;
2489 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2490 : {"--update", "--oo=LIST_ALL_TABLES=YES", "--if=GPKG",
2491 : osTmpFilename}));
2492 1 : ASSERT_EQ(alg.m_input.size(), 1U);
2493 1 : ASSERT_NE(alg.m_input[0].GetDatasetRef(), nullptr);
2494 1 : EXPECT_EQ(alg.m_input[0].GetDatasetRef()->GetAccess(), GA_Update);
2495 :
2496 1 : alg.Finalize();
2497 :
2498 1 : VSIUnlink(osTmpFilename.c_str());
2499 : }
2500 : }
2501 : }
2502 :
2503 4 : TEST_F(test_gdal_algorithm, several_values)
2504 : {
2505 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2506 : {
2507 : public:
2508 : std::vector<std::string> m_co{};
2509 :
2510 5 : MyAlgorithm()
2511 5 : {
2512 5 : AddArg("co", 0, "creation options", &m_co);
2513 5 : }
2514 : };
2515 :
2516 : {
2517 2 : MyAlgorithm alg;
2518 1 : alg.GetUsageForCLI(false);
2519 :
2520 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2521 : }
2522 :
2523 : {
2524 2 : MyAlgorithm alg;
2525 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "FOO=BAR"}));
2526 4 : EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
2527 : }
2528 :
2529 : {
2530 2 : MyAlgorithm alg;
2531 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR"}));
2532 4 : EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
2533 : }
2534 :
2535 : {
2536 2 : MyAlgorithm alg;
2537 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR,BAR=BAZ"}));
2538 6 : auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
2539 1 : EXPECT_EQ(alg.m_co, expected);
2540 : }
2541 :
2542 : {
2543 2 : MyAlgorithm alg;
2544 5 : EXPECT_TRUE(
2545 : alg.ParseCommandLineArguments({"--co=FOO=BAR", "--co", "BAR=BAZ"}));
2546 6 : auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
2547 1 : EXPECT_EQ(alg.m_co, expected);
2548 : }
2549 1 : }
2550 :
2551 4 : TEST_F(test_gdal_algorithm, required_arg)
2552 : {
2553 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2554 : {
2555 : public:
2556 : std::string m_arg{};
2557 :
2558 2 : MyAlgorithm()
2559 2 : {
2560 2 : AddArg("arg", 0, "required arg", &m_arg).SetRequired();
2561 2 : }
2562 : };
2563 :
2564 : {
2565 2 : MyAlgorithm alg;
2566 1 : alg.GetUsageForCLI(false);
2567 :
2568 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2569 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2570 : }
2571 :
2572 : {
2573 2 : MyAlgorithm alg;
2574 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg", "foo"}));
2575 1 : EXPECT_STREQ(alg.m_arg.c_str(), "foo");
2576 : }
2577 1 : }
2578 :
2579 4 : TEST_F(test_gdal_algorithm, single_positional_arg)
2580 : {
2581 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2582 : {
2583 : public:
2584 : std::string m_value{};
2585 :
2586 4 : MyAlgorithm()
2587 4 : {
2588 4 : AddArg("input", 0, "input value", &m_value).SetPositional();
2589 4 : }
2590 : };
2591 :
2592 : {
2593 2 : MyAlgorithm alg;
2594 1 : alg.GetUsageForCLI(false);
2595 :
2596 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2597 : }
2598 :
2599 : {
2600 2 : MyAlgorithm alg;
2601 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
2602 1 : EXPECT_TRUE(alg.GetArg("input")->IsExplicitlySet());
2603 2 : EXPECT_STREQ(alg.GetArg("input")->Get<std::string>().c_str(),
2604 : "my_input");
2605 : }
2606 :
2607 : {
2608 2 : MyAlgorithm alg;
2609 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input", "my_input"}));
2610 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
2611 : }
2612 :
2613 : {
2614 2 : MyAlgorithm alg;
2615 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
2616 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
2617 : }
2618 1 : }
2619 :
2620 4 : TEST_F(test_gdal_algorithm, single_positional_arg_required)
2621 : {
2622 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2623 : {
2624 : public:
2625 : std::string m_value{};
2626 :
2627 2 : MyAlgorithm()
2628 2 : {
2629 4 : AddArg("input", 0, "input value", &m_value)
2630 2 : .SetPositional()
2631 2 : .SetRequired();
2632 2 : }
2633 : };
2634 :
2635 : {
2636 2 : MyAlgorithm alg;
2637 1 : alg.GetUsageForCLI(false);
2638 :
2639 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2640 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2641 : }
2642 :
2643 : {
2644 2 : MyAlgorithm alg;
2645 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
2646 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
2647 : }
2648 1 : }
2649 :
2650 4 : TEST_F(test_gdal_algorithm, two_positional_arg)
2651 : {
2652 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2653 : {
2654 : public:
2655 : std::string m_input_value{};
2656 : std::string m_output_value{};
2657 :
2658 12 : MyAlgorithm()
2659 12 : {
2660 12 : AddArg("input", 'i', "input value", &m_input_value).SetPositional();
2661 24 : AddArg("output", 'o', "output value", &m_output_value)
2662 12 : .SetPositional();
2663 12 : }
2664 : };
2665 :
2666 : {
2667 2 : MyAlgorithm alg;
2668 1 : alg.GetUsageForCLI(false);
2669 :
2670 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2671 : }
2672 :
2673 : {
2674 2 : MyAlgorithm alg;
2675 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
2676 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2677 : }
2678 :
2679 : {
2680 2 : MyAlgorithm alg;
2681 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-i", "my_input"}));
2682 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2683 : }
2684 :
2685 : {
2686 2 : MyAlgorithm alg;
2687 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input", "my_output"}));
2688 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2689 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2690 : }
2691 :
2692 : {
2693 2 : MyAlgorithm alg;
2694 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2695 : {"--input", "my_input", "-o", "my_output"}));
2696 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2697 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2698 : }
2699 :
2700 : {
2701 2 : MyAlgorithm alg;
2702 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2703 : {"-o", "my_output", "--input", "my_input"}));
2704 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2705 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2706 : }
2707 :
2708 : {
2709 2 : MyAlgorithm alg;
2710 5 : EXPECT_TRUE(
2711 : alg.ParseCommandLineArguments({"-o", "my_output", "my_input"}));
2712 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2713 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2714 : }
2715 :
2716 : {
2717 2 : MyAlgorithm alg;
2718 5 : EXPECT_TRUE(
2719 : alg.ParseCommandLineArguments({"my_input", "-o", "my_output"}));
2720 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2721 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2722 : }
2723 :
2724 : {
2725 2 : MyAlgorithm alg;
2726 1 : alg.GetArg("input")->Set("my_input");
2727 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_output"}));
2728 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2729 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2730 : }
2731 :
2732 : {
2733 2 : MyAlgorithm alg;
2734 1 : alg.GetArg("input")->Set("my_input");
2735 1 : alg.GetArg("output")->Set("my_output");
2736 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2737 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2738 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2739 : }
2740 :
2741 : {
2742 2 : MyAlgorithm alg;
2743 1 : alg.GetArg("input")->Set("my_input");
2744 1 : alg.GetArg("output")->Set("my_output");
2745 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2746 1 : CPLErrorReset();
2747 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"unexpected"}));
2748 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2749 : }
2750 :
2751 : {
2752 2 : MyAlgorithm alg;
2753 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2754 1 : CPLErrorReset();
2755 5 : EXPECT_FALSE(alg.ParseCommandLineArguments({"foo", "bar", "baz"}));
2756 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2757 : }
2758 1 : }
2759 :
2760 4 : TEST_F(test_gdal_algorithm, two_positional_arg_first_two_values)
2761 : {
2762 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2763 : {
2764 : public:
2765 : std::vector<int> m_input_value{};
2766 : std::string m_output_value{};
2767 :
2768 3 : MyAlgorithm()
2769 3 : {
2770 6 : AddArg("input", 'i', "input value", &m_input_value)
2771 3 : .SetPositional()
2772 3 : .SetMinCount(2)
2773 3 : .SetMaxCount(2)
2774 3 : .SetDisplayHintAboutRepetition(false);
2775 6 : AddArg("output", 'o', "output value", &m_output_value)
2776 3 : .SetPositional();
2777 3 : }
2778 : };
2779 :
2780 : {
2781 2 : MyAlgorithm alg;
2782 1 : alg.GetUsageForCLI(false);
2783 :
2784 5 : EXPECT_TRUE(alg.ParseCommandLineArguments({"1", "2", "baz"}));
2785 2 : auto expected = std::vector<int>{1, 2};
2786 1 : EXPECT_EQ(alg.m_input_value, expected);
2787 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "baz");
2788 : }
2789 :
2790 : {
2791 2 : MyAlgorithm alg;
2792 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2793 1 : CPLErrorReset();
2794 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"1"}));
2795 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2796 : }
2797 :
2798 : {
2799 2 : MyAlgorithm alg;
2800 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2801 1 : CPLErrorReset();
2802 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"1", "foo"}));
2803 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2804 : }
2805 1 : }
2806 :
2807 4 : TEST_F(test_gdal_algorithm, unlimited_input_single_output)
2808 : {
2809 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2810 : {
2811 : public:
2812 : std::vector<std::string> m_input_values{};
2813 : std::string m_output_value{};
2814 :
2815 2 : MyAlgorithm()
2816 2 : {
2817 4 : AddArg("input", 'i', "input value", &m_input_values)
2818 2 : .SetPositional();
2819 4 : AddArg("output", 'o', "output value", &m_output_value)
2820 2 : .SetPositional()
2821 2 : .SetRequired();
2822 2 : }
2823 : };
2824 :
2825 : {
2826 2 : MyAlgorithm alg;
2827 1 : alg.GetUsageForCLI(false);
2828 :
2829 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2830 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2831 : }
2832 :
2833 : {
2834 2 : MyAlgorithm alg;
2835 5 : EXPECT_TRUE(
2836 : alg.ParseCommandLineArguments({"input1", "input2", "my_output"}));
2837 6 : auto expected = std::vector<std::string>{"input1", "input2"};
2838 1 : EXPECT_EQ(alg.m_input_values, expected);
2839 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
2840 : }
2841 1 : }
2842 :
2843 4 : TEST_F(test_gdal_algorithm, single_input_unlimited_outputs)
2844 : {
2845 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2846 : {
2847 : public:
2848 : std::string m_input_value{};
2849 : std::vector<std::string> m_output_values{};
2850 :
2851 2 : MyAlgorithm()
2852 2 : {
2853 4 : AddArg("input", 'i', "input value", &m_input_value)
2854 2 : .SetPositional()
2855 2 : .SetRequired();
2856 4 : AddArg("output", 'o', "output value", &m_output_values)
2857 2 : .SetPositional();
2858 2 : }
2859 : };
2860 :
2861 : {
2862 2 : MyAlgorithm alg;
2863 1 : alg.GetUsageForCLI(false);
2864 :
2865 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2866 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2867 : }
2868 :
2869 : {
2870 2 : MyAlgorithm alg;
2871 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2872 : {"my_input", "my_output1", "my_output2"}));
2873 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
2874 6 : auto expected = std::vector<std::string>{"my_output1", "my_output2"};
2875 1 : EXPECT_EQ(alg.m_output_values, expected);
2876 : }
2877 1 : }
2878 :
2879 4 : TEST_F(test_gdal_algorithm, min_max_count)
2880 : {
2881 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2882 : {
2883 : public:
2884 : std::vector<std::string> m_arg{};
2885 :
2886 5 : MyAlgorithm()
2887 5 : {
2888 10 : AddArg("arg", 0, "arg", &m_arg)
2889 5 : .SetRequired()
2890 5 : .SetMinCount(2)
2891 5 : .SetMaxCount(3);
2892 5 : }
2893 : };
2894 :
2895 : {
2896 2 : MyAlgorithm alg;
2897 1 : alg.GetUsageForCLI(false);
2898 :
2899 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2900 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2901 : }
2902 :
2903 : {
2904 2 : MyAlgorithm alg;
2905 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2906 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo"}));
2907 : }
2908 :
2909 : {
2910 2 : MyAlgorithm alg;
2911 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2912 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=1,2,3,4"}));
2913 : }
2914 :
2915 : {
2916 2 : MyAlgorithm alg;
2917 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
2918 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2919 1 : EXPECT_EQ(alg.m_arg, expected);
2920 : }
2921 :
2922 : {
2923 2 : MyAlgorithm alg;
2924 8 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2925 : {"--arg", "foo", "--arg", "bar", "--arg", "baz"}));
2926 7 : auto expected = std::vector<std::string>{"foo", "bar", "baz"};
2927 1 : EXPECT_EQ(alg.m_arg, expected);
2928 : }
2929 1 : }
2930 :
2931 4 : TEST_F(test_gdal_algorithm, min_max_count_equal)
2932 : {
2933 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2934 : {
2935 : public:
2936 : std::vector<std::string> m_arg{};
2937 :
2938 2 : MyAlgorithm()
2939 2 : {
2940 4 : AddArg("arg", 0, "arg", &m_arg)
2941 2 : .SetRequired()
2942 2 : .SetMinCount(2)
2943 2 : .SetMaxCount(2);
2944 2 : }
2945 : };
2946 :
2947 : {
2948 2 : MyAlgorithm alg;
2949 1 : alg.GetUsageForCLI(false);
2950 :
2951 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2952 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2953 : }
2954 :
2955 : {
2956 2 : MyAlgorithm alg;
2957 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2958 3 : EXPECT_FALSE(alg.GetArg("arg")->Set(std::vector<std::string>{"foo"}));
2959 1 : EXPECT_FALSE(alg.ValidateArguments());
2960 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
2961 : "test: 1 value has been specified for argument 'arg', "
2962 : "whereas exactly 2 were expected.");
2963 : }
2964 1 : }
2965 :
2966 4 : TEST_F(test_gdal_algorithm, repeated_arg_allowed_false)
2967 : {
2968 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2969 : {
2970 : public:
2971 : std::vector<std::string> m_arg{};
2972 :
2973 3 : MyAlgorithm()
2974 3 : {
2975 6 : AddArg("arg", 0, "arg", &m_arg)
2976 3 : .SetRepeatedArgAllowed(false)
2977 3 : .SetMinCount(2)
2978 3 : .SetMaxCount(3);
2979 3 : }
2980 : };
2981 :
2982 : {
2983 2 : MyAlgorithm alg;
2984 1 : alg.GetUsageForCLI(false);
2985 :
2986 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2987 : }
2988 :
2989 : {
2990 2 : MyAlgorithm alg;
2991 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
2992 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2993 1 : EXPECT_EQ(alg.m_arg, expected);
2994 : }
2995 :
2996 : {
2997 2 : MyAlgorithm alg;
2998 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2999 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
3000 : }
3001 1 : }
3002 :
3003 4 : TEST_F(test_gdal_algorithm, ambiguous_positional_unlimited_and_then_varying)
3004 : {
3005 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3006 : {
3007 : public:
3008 : std::vector<std::string> m_input_values{};
3009 : std::vector<std::string> m_output_values{};
3010 :
3011 1 : MyAlgorithm()
3012 1 : {
3013 2 : AddArg("input", 'i', "input value", &m_input_values)
3014 1 : .SetPositional();
3015 2 : AddArg("output", 'o', "output value", &m_output_values)
3016 1 : .SetPositional()
3017 1 : .SetMinCount(2)
3018 1 : .SetMaxCount(3);
3019 1 : }
3020 : };
3021 :
3022 : {
3023 2 : MyAlgorithm alg;
3024 1 : alg.GetUsageForCLI(false);
3025 :
3026 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3027 1 : CPLErrorReset();
3028 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3029 : {"my_input", "my_output1", "my_output2"}));
3030 1 : EXPECT_STREQ(
3031 : CPLGetLastErrorMsg(),
3032 : "test: Ambiguity in definition of positional argument 'output' "
3033 : "given it has a varying number of values, but follows argument "
3034 : "'input' which also has a varying number of values");
3035 : }
3036 1 : }
3037 :
3038 4 : TEST_F(test_gdal_algorithm,
3039 : ambiguous_positional_unlimited_and_then_non_required)
3040 : {
3041 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3042 : {
3043 : public:
3044 : std::vector<std::string> m_input_values{};
3045 : std::string m_output_value{};
3046 :
3047 1 : MyAlgorithm()
3048 1 : {
3049 2 : AddArg("input", 'i', "input value", &m_input_values)
3050 1 : .SetPositional();
3051 2 : AddArg("output", 'o', "output value", &m_output_value)
3052 1 : .SetPositional();
3053 1 : }
3054 : };
3055 :
3056 : {
3057 2 : MyAlgorithm alg;
3058 1 : alg.GetUsageForCLI(false);
3059 :
3060 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3061 1 : CPLErrorReset();
3062 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3063 : {"my_input1", "my_input2", "my_output"}));
3064 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3065 : "test: Ambiguity in definition of positional argument "
3066 : "'output', given it is not required but follows argument "
3067 : "'input' which has a varying number of values");
3068 : }
3069 1 : }
3070 :
3071 4 : TEST_F(test_gdal_algorithm,
3072 : ambiguous_positional_fixed_then_unlimited_then_fixed)
3073 : {
3074 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3075 : {
3076 : public:
3077 : std::string m_input_value{};
3078 : std::vector<std::string> m_something{};
3079 : std::string m_output_value{};
3080 :
3081 1 : MyAlgorithm()
3082 1 : {
3083 1 : AddArg("input", 'i', "input value", &m_input_value).SetPositional();
3084 1 : AddArg("something", 0, "something", &m_something).SetPositional();
3085 2 : AddArg("output", 'o', "output value", &m_output_value)
3086 1 : .SetPositional();
3087 1 : }
3088 : };
3089 :
3090 : {
3091 2 : MyAlgorithm alg;
3092 1 : alg.GetUsageForCLI(false);
3093 :
3094 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3095 1 : CPLErrorReset();
3096 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3097 : {"my_input", "something", "my_output"}));
3098 : // Actually this is not ambiguous here, but our parser does not support
3099 : // that for now
3100 1 : EXPECT_STREQ(
3101 : CPLGetLastErrorMsg(),
3102 : "test: Ambiguity in definition of positional arguments: arguments "
3103 : "with varying number of values must be first or last one.");
3104 : }
3105 1 : }
3106 :
3107 4 : TEST_F(test_gdal_algorithm, positional_unlimited_and_then_2)
3108 : {
3109 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3110 : {
3111 : public:
3112 : std::vector<std::string> m_input_values{};
3113 : std::vector<std::string> m_output_values{};
3114 :
3115 2 : MyAlgorithm()
3116 2 : {
3117 4 : AddArg("input", 'i', "input value", &m_input_values)
3118 2 : .SetPositional();
3119 4 : AddArg("output", 'o', "output value", &m_output_values)
3120 2 : .SetPositional()
3121 2 : .SetMinCount(2)
3122 2 : .SetMaxCount(2);
3123 2 : }
3124 : };
3125 :
3126 : {
3127 2 : MyAlgorithm alg;
3128 1 : alg.GetUsageForCLI(false);
3129 :
3130 7 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
3131 : "my_input3", "my_output1",
3132 : "my_output2"}));
3133 1 : EXPECT_EQ(alg.m_input_values.size(), 3U);
3134 1 : EXPECT_EQ(alg.m_output_values.size(), 2U);
3135 : }
3136 :
3137 : {
3138 2 : MyAlgorithm alg;
3139 :
3140 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3141 1 : CPLErrorReset();
3142 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"my_output1"}));
3143 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3144 : "test: Not enough positional values.");
3145 : }
3146 1 : }
3147 :
3148 4 : TEST_F(test_gdal_algorithm, positional_unlimited_validation_error_and_then_2)
3149 : {
3150 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3151 : {
3152 : public:
3153 : std::vector<std::string> m_input_values{};
3154 : std::vector<std::string> m_output_values{};
3155 :
3156 1 : MyAlgorithm()
3157 1 : {
3158 2 : AddArg("input", 'i', "input value", &m_input_values)
3159 1 : .SetPositional()
3160 : .AddValidationAction(
3161 1 : []()
3162 : {
3163 1 : CPLError(CE_Failure, CPLE_AppDefined,
3164 : "validation failed");
3165 1 : return false;
3166 1 : });
3167 2 : AddArg("output", 'o', "output value", &m_output_values)
3168 1 : .SetPositional()
3169 1 : .SetMinCount(2)
3170 1 : .SetMaxCount(2);
3171 1 : }
3172 : };
3173 :
3174 : {
3175 2 : MyAlgorithm alg;
3176 :
3177 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3178 1 : CPLErrorReset();
3179 7 : EXPECT_FALSE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
3180 : "my_input3", "my_output1",
3181 : "my_output2"}));
3182 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
3183 : }
3184 1 : }
3185 :
3186 4 : TEST_F(test_gdal_algorithm,
3187 : positional_unlimited_validation_error_and_then_required)
3188 : {
3189 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3190 : {
3191 : public:
3192 : std::vector<std::string> m_input_values{};
3193 : std::string m_output_value{};
3194 :
3195 1 : MyAlgorithm()
3196 1 : {
3197 2 : AddArg("input", 'i', "input value", &m_input_values)
3198 1 : .SetPositional()
3199 1 : .SetChoices("foo");
3200 2 : AddArg("output", 'o', "output value", &m_output_value)
3201 1 : .SetPositional()
3202 1 : .SetRequired();
3203 1 : }
3204 : };
3205 :
3206 : {
3207 2 : MyAlgorithm alg;
3208 :
3209 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3210 1 : CPLErrorReset();
3211 5 : EXPECT_FALSE(
3212 : alg.ParseCommandLineArguments({"foo", "bar", "my_output"}));
3213 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3214 : "Invalid value 'bar' for string argument 'input'. "
3215 : "Should be one among 'foo'.");
3216 : }
3217 1 : }
3218 :
3219 4 : TEST_F(test_gdal_algorithm,
3220 : positional_required_and_then_unlimited_validation_error)
3221 : {
3222 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3223 : {
3224 : public:
3225 : std::string m_input_value{};
3226 : std::vector<std::string> m_output_values{};
3227 :
3228 1 : MyAlgorithm()
3229 1 : {
3230 2 : AddArg("input", 'i', "input value", &m_input_value)
3231 1 : .SetPositional()
3232 1 : .SetRequired();
3233 2 : AddArg("output", 'o', "output values", &m_output_values)
3234 1 : .SetPositional()
3235 1 : .SetChoices("foo");
3236 1 : }
3237 : };
3238 :
3239 : {
3240 2 : MyAlgorithm alg;
3241 :
3242 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3243 1 : CPLErrorReset();
3244 5 : EXPECT_FALSE(
3245 : alg.ParseCommandLineArguments({"something", "foo", "bar"}));
3246 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3247 : "Invalid value 'bar' for string argument 'output'. "
3248 : "Should be one among 'foo'.");
3249 : }
3250 1 : }
3251 :
3252 4 : TEST_F(test_gdal_algorithm,
3253 : positional_required_then_unlimited_required_then_positional_required)
3254 : {
3255 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3256 : {
3257 : public:
3258 : std::string m_input_value{};
3259 : std::vector<std::string> m_something{};
3260 : std::string m_output_value{};
3261 :
3262 7 : MyAlgorithm()
3263 7 : {
3264 14 : AddArg("input", 'i', "input value", &m_input_value)
3265 7 : .SetMinCharCount(2)
3266 7 : .SetPositional()
3267 7 : .SetRequired();
3268 14 : AddArg("something", 0, "something", &m_something)
3269 7 : .SetMinCharCount(2)
3270 7 : .SetPositional()
3271 7 : .SetMinCount(1);
3272 14 : AddArg("output", 'o', "output value", &m_output_value)
3273 7 : .SetMinCharCount(2)
3274 7 : .SetMaxCharCount(20)
3275 7 : .SetPositional()
3276 7 : .SetRequired();
3277 7 : }
3278 : };
3279 :
3280 : {
3281 2 : MyAlgorithm alg;
3282 :
3283 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
3284 : {"my_input", "something", "my_output"}));
3285 : }
3286 :
3287 : {
3288 2 : MyAlgorithm alg;
3289 :
3290 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
3291 : {"my_input", "something", "else", "my_output"}));
3292 : }
3293 :
3294 : {
3295 2 : MyAlgorithm alg;
3296 :
3297 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3298 1 : CPLErrorReset();
3299 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"input", "output"}));
3300 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3301 : "test: Not enough positional values.");
3302 : }
3303 :
3304 : {
3305 2 : MyAlgorithm alg;
3306 :
3307 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3308 1 : CPLErrorReset();
3309 5 : EXPECT_FALSE(
3310 : alg.ParseCommandLineArguments({"x", "something", "output"}));
3311 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3312 : "Value of argument 'input' is 'x', but should have at "
3313 : "least 2 characters");
3314 : }
3315 :
3316 : {
3317 2 : MyAlgorithm alg;
3318 :
3319 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3320 1 : CPLErrorReset();
3321 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3322 : {"input", "something", "output_waaaaaaaay_too_long"}));
3323 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3324 : "Value of argument 'output' is "
3325 : "'output_waaaaaaaay_too_long', but should have no "
3326 : "more than 20 characters");
3327 : }
3328 :
3329 : {
3330 2 : MyAlgorithm alg;
3331 :
3332 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3333 1 : CPLErrorReset();
3334 5 : EXPECT_FALSE(alg.ParseCommandLineArguments({"input", "x", "output"}));
3335 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3336 : "Value of argument 'something' is 'x', but should have at "
3337 : "least 2 characters");
3338 : }
3339 :
3340 : {
3341 2 : MyAlgorithm alg;
3342 :
3343 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3344 1 : CPLErrorReset();
3345 5 : EXPECT_FALSE(
3346 : alg.ParseCommandLineArguments({"input", "something", "x"}));
3347 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3348 : "Value of argument 'output' is 'x', but should have at "
3349 : "least 2 characters");
3350 : }
3351 1 : }
3352 :
3353 4 : TEST_F(test_gdal_algorithm, packed_values_allowed_false)
3354 : {
3355 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3356 : {
3357 : public:
3358 : std::vector<std::string> m_arg{};
3359 :
3360 3 : MyAlgorithm()
3361 3 : {
3362 6 : AddArg("arg", 0, "arg", &m_arg)
3363 3 : .SetPackedValuesAllowed(false)
3364 3 : .SetMinCount(2)
3365 3 : .SetMaxCount(3);
3366 3 : }
3367 : };
3368 :
3369 : {
3370 2 : MyAlgorithm alg;
3371 1 : alg.GetUsageForCLI(false);
3372 :
3373 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
3374 : }
3375 :
3376 : {
3377 2 : MyAlgorithm alg;
3378 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
3379 6 : auto expected = std::vector<std::string>{"foo", "bar"};
3380 1 : EXPECT_EQ(alg.m_arg, expected);
3381 :
3382 2 : std::string serialized;
3383 1 : EXPECT_TRUE(alg.GetArg("arg")->Serialize(serialized));
3384 1 : EXPECT_STREQ(serialized.c_str(), "--arg foo --arg bar");
3385 : }
3386 :
3387 : {
3388 2 : MyAlgorithm alg;
3389 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3390 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
3391 : }
3392 1 : }
3393 :
3394 4 : TEST_F(test_gdal_algorithm, actions)
3395 : {
3396 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3397 : {
3398 : public:
3399 : bool m_flag = false;
3400 : bool m_flagSpecified = false;
3401 :
3402 1 : MyAlgorithm()
3403 1 : {
3404 2 : AddArg("flag", 'f', "boolean flag", &m_flag)
3405 1 : .AddAction([this]() { m_flagSpecified = true; });
3406 1 : }
3407 : };
3408 :
3409 : {
3410 2 : MyAlgorithm alg;
3411 1 : alg.GetUsageForCLI(false);
3412 :
3413 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
3414 1 : EXPECT_TRUE(alg.m_flag);
3415 1 : EXPECT_TRUE(alg.m_flagSpecified);
3416 : }
3417 1 : }
3418 :
3419 4 : TEST_F(test_gdal_algorithm, various)
3420 : {
3421 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3422 : {
3423 : public:
3424 : bool m_flag = false;
3425 :
3426 6 : MyAlgorithm()
3427 6 : {
3428 6 : AddProgressArg();
3429 6 : }
3430 : };
3431 :
3432 : {
3433 2 : MyAlgorithm alg;
3434 1 : alg.GetUsageForCLI(false);
3435 :
3436 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
3437 : // Parse again
3438 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3439 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
3440 : }
3441 :
3442 : {
3443 2 : MyAlgorithm alg;
3444 1 : alg.SetCalledFromCommandLine();
3445 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-h"}));
3446 1 : EXPECT_TRUE(alg.IsHelpRequested());
3447 : }
3448 :
3449 : {
3450 2 : MyAlgorithm alg;
3451 1 : alg.SetCalledFromCommandLine();
3452 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--help"}));
3453 1 : EXPECT_TRUE(alg.IsHelpRequested());
3454 : }
3455 :
3456 : {
3457 2 : MyAlgorithm alg;
3458 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"help"}));
3459 1 : EXPECT_TRUE(alg.IsHelpRequested());
3460 : }
3461 :
3462 : {
3463 2 : MyAlgorithm alg;
3464 1 : alg.SetCalledFromCommandLine();
3465 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--json-usage"}));
3466 1 : EXPECT_TRUE(alg.IsJSONUsageRequested());
3467 : }
3468 :
3469 : {
3470 2 : MyAlgorithm alg;
3471 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--progress"}));
3472 1 : EXPECT_TRUE(alg.IsProgressBarRequested());
3473 : }
3474 1 : }
3475 :
3476 4 : TEST_F(test_gdal_algorithm, mutually_exclusive)
3477 : {
3478 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3479 : {
3480 : public:
3481 : bool m_flag1 = false;
3482 : bool m_flag2 = false;
3483 : bool m_flag3 = false;
3484 :
3485 4 : MyAlgorithm()
3486 4 : {
3487 8 : AddArg("flag1", 0, "", &m_flag1)
3488 4 : .SetMutualExclusionGroup("my_group");
3489 8 : AddArg("flag2", 0, "", &m_flag2)
3490 4 : .SetMutualExclusionGroup("my_group");
3491 8 : AddArg("flag3", 0, "", &m_flag3)
3492 4 : .SetMutualExclusionGroup("my_group");
3493 4 : }
3494 : };
3495 :
3496 : {
3497 2 : MyAlgorithm alg;
3498 1 : alg.GetUsageForCLI(false);
3499 :
3500 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
3501 : }
3502 :
3503 : {
3504 2 : MyAlgorithm alg;
3505 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag1"}));
3506 : }
3507 :
3508 : {
3509 2 : MyAlgorithm alg;
3510 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag2"}));
3511 : }
3512 :
3513 : {
3514 2 : MyAlgorithm alg;
3515 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3516 1 : CPLErrorReset();
3517 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1", "--flag2"}));
3518 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3519 : }
3520 1 : }
3521 :
3522 4 : TEST_F(test_gdal_algorithm, mutually_dependent)
3523 : {
3524 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3525 : {
3526 : public:
3527 : bool m_flag1 = false;
3528 : bool m_flag2 = false;
3529 : bool m_flag3 = false;
3530 :
3531 8 : MyAlgorithm()
3532 8 : {
3533 16 : AddArg("flag1", 0, "", &m_flag1)
3534 8 : .SetMutualDependencyGroup("my_group");
3535 16 : AddArg("flag2", 0, "", &m_flag2)
3536 8 : .SetMutualDependencyGroup("my_group");
3537 16 : AddArg("flag3", 0, "", &m_flag3)
3538 8 : .SetMutualDependencyGroup("my_group");
3539 8 : }
3540 : };
3541 :
3542 : {
3543 2 : MyAlgorithm alg;
3544 1 : alg.GetUsageForCLI(false);
3545 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
3546 : }
3547 :
3548 : {
3549 2 : MyAlgorithm alg;
3550 5 : EXPECT_TRUE(
3551 : alg.ParseCommandLineArguments({"--flag1", "--flag2", "--flag3"}));
3552 : }
3553 :
3554 : {
3555 2 : MyAlgorithm alg;
3556 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3557 1 : CPLErrorReset();
3558 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1"}));
3559 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3560 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3561 : "test: Argument(s) 'flag1' require(s) that the following "
3562 : "argument(s) are also specified: flag2, flag3.");
3563 : }
3564 :
3565 : {
3566 2 : MyAlgorithm alg;
3567 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3568 1 : CPLErrorReset();
3569 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag2"}));
3570 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3571 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3572 : "test: Argument(s) 'flag2' require(s) that the following "
3573 : "argument(s) are also specified: flag1, flag3.");
3574 : }
3575 :
3576 : {
3577 2 : MyAlgorithm alg;
3578 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3579 1 : CPLErrorReset();
3580 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag3"}));
3581 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3582 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3583 : "test: Argument(s) 'flag3' require(s) that the following "
3584 : "argument(s) are also specified: flag1, flag2.");
3585 : }
3586 :
3587 : {
3588 2 : MyAlgorithm alg;
3589 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3590 1 : CPLErrorReset();
3591 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1", "--flag2"}));
3592 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3593 : }
3594 :
3595 : {
3596 2 : MyAlgorithm alg;
3597 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3598 1 : CPLErrorReset();
3599 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag2", "--flag3"}));
3600 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3601 : }
3602 :
3603 : {
3604 2 : MyAlgorithm alg;
3605 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3606 1 : CPLErrorReset();
3607 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1", "--flag3"}));
3608 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3609 : }
3610 1 : }
3611 :
3612 : // Test dependencies
3613 4 : TEST_F(test_gdal_algorithm, direct_dependencies)
3614 : {
3615 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3616 : {
3617 : public:
3618 : bool m_flag1 = false;
3619 : bool m_flag2 = false;
3620 :
3621 5 : MyAlgorithm()
3622 5 : {
3623 10 : AddArg("flag1", 0, "", &m_flag1)
3624 5 : .AddDirectDependency(AddArg("flag2", 0, "", &m_flag2));
3625 5 : }
3626 : };
3627 :
3628 : {
3629 2 : MyAlgorithm alg;
3630 4 : EXPECT_EQ(alg.GetArgDependencies("flag1"),
3631 : std::vector<std::string>{"flag2"});
3632 2 : EXPECT_EQ(alg.GetArgDependencies("flag2"), std::vector<std::string>{});
3633 : }
3634 :
3635 : {
3636 2 : MyAlgorithm alg;
3637 1 : alg.GetUsageForCLI(false);
3638 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
3639 : }
3640 :
3641 : {
3642 2 : MyAlgorithm alg;
3643 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag2"}));
3644 : }
3645 :
3646 : {
3647 2 : MyAlgorithm alg;
3648 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3649 1 : CPLErrorReset();
3650 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1"}));
3651 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3652 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3653 : "test: Argument 'flag1' depends on argument 'flag2' that "
3654 : "has not been specified.");
3655 : }
3656 :
3657 : {
3658 2 : MyAlgorithm alg;
3659 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag1", "--flag2"}));
3660 : }
3661 1 : }
3662 :
3663 4 : TEST_F(test_gdal_algorithm, invalid_input_format)
3664 : {
3665 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3666 : {
3667 : public:
3668 : std::vector<std::string> m_if{};
3669 :
3670 2 : MyAlgorithm()
3671 2 : {
3672 2 : AddInputFormatsArg(&m_if).AddMetadataItem(
3673 4 : GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
3674 2 : }
3675 : };
3676 :
3677 : {
3678 2 : MyAlgorithm alg;
3679 1 : alg.GetUsageForCLI(false);
3680 :
3681 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3682 1 : CPLErrorReset();
3683 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=I_DO_NOT_EXIST"}));
3684 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3685 : }
3686 :
3687 : {
3688 2 : MyAlgorithm alg;
3689 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
3690 1 : CPLErrorReset();
3691 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=GTIFF"}));
3692 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3693 : }
3694 1 : }
3695 :
3696 4 : TEST_F(test_gdal_algorithm, arg_layer_name_single)
3697 : {
3698 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3699 : {
3700 : public:
3701 : std::string m_layerName{};
3702 :
3703 1 : MyAlgorithm()
3704 1 : {
3705 1 : AddLayerNameArg(&m_layerName);
3706 1 : }
3707 : };
3708 :
3709 : {
3710 2 : MyAlgorithm alg;
3711 1 : alg.GetUsageForCLI(false);
3712 :
3713 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo"}));
3714 1 : EXPECT_STREQ(alg.m_layerName.c_str(), "foo");
3715 : }
3716 1 : }
3717 :
3718 4 : TEST_F(test_gdal_algorithm, arg_layer_name_multiple)
3719 : {
3720 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3721 : {
3722 : public:
3723 : std::vector<std::string> m_layerNames{};
3724 :
3725 1 : MyAlgorithm()
3726 1 : {
3727 1 : AddLayerNameArg(&m_layerNames);
3728 1 : }
3729 : };
3730 :
3731 : {
3732 2 : MyAlgorithm alg;
3733 6 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo", "-l", "bar"}));
3734 1 : EXPECT_EQ(alg.m_layerNames.size(), 2U);
3735 : }
3736 1 : }
3737 :
3738 4 : TEST_F(test_gdal_algorithm, arg_co)
3739 : {
3740 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3741 : {
3742 : public:
3743 : std::vector<std::string> m_co{};
3744 :
3745 7 : MyAlgorithm()
3746 7 : {
3747 7 : AddCreationOptionsArg(&m_co);
3748 7 : }
3749 : };
3750 :
3751 : {
3752 2 : MyAlgorithm alg;
3753 1 : alg.GetUsageForCLI(false);
3754 :
3755 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
3756 : {"--co", "foo=bar", "--co", "bar=baz"}));
3757 6 : const std::vector<std::string> expected{"foo=bar", "bar=baz"};
3758 1 : EXPECT_EQ(alg.m_co, expected);
3759 : }
3760 :
3761 : {
3762 2 : MyAlgorithm alg;
3763 1 : alg.GetUsageForCLI(false);
3764 :
3765 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,bar=baz"}));
3766 6 : const std::vector<std::string> expected{"foo=bar", "bar=baz"};
3767 1 : EXPECT_EQ(alg.m_co, expected);
3768 : }
3769 :
3770 : {
3771 2 : MyAlgorithm alg;
3772 1 : alg.GetUsageForCLI(false);
3773 :
3774 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,baz"}));
3775 5 : const std::vector<std::string> expected{"foo=bar,baz"};
3776 1 : EXPECT_EQ(alg.m_co, expected);
3777 : }
3778 :
3779 : {
3780 2 : MyAlgorithm alg;
3781 1 : alg.GetUsageForCLI(false);
3782 :
3783 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar=,a"}));
3784 5 : const std::vector<std::string> expected{"foo=bar=,a"};
3785 1 : EXPECT_EQ(alg.m_co, expected);
3786 : }
3787 :
3788 : {
3789 2 : MyAlgorithm alg;
3790 1 : alg.GetUsageForCLI(false);
3791 :
3792 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,,"}));
3793 5 : const std::vector<std::string> expected{"foo=bar,,"};
3794 1 : EXPECT_EQ(alg.m_co, expected);
3795 : }
3796 :
3797 : {
3798 2 : MyAlgorithm alg;
3799 1 : alg.GetUsageForCLI(false);
3800 :
3801 4 : EXPECT_TRUE(
3802 : alg.ParseCommandLineArguments({"--co", "foo=bar,\"foo=baz\""}));
3803 5 : const std::vector<std::string> expected{"foo=bar,\"foo=baz\""};
3804 1 : EXPECT_EQ(alg.m_co, expected);
3805 : }
3806 :
3807 : {
3808 2 : MyAlgorithm alg;
3809 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3810 1 : CPLErrorReset();
3811 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--co", "foo"}));
3812 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3813 : }
3814 1 : }
3815 :
3816 4 : TEST_F(test_gdal_algorithm, arg_lco)
3817 : {
3818 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3819 : {
3820 : public:
3821 : std::vector<std::string> m_lco{};
3822 :
3823 2 : MyAlgorithm()
3824 2 : {
3825 2 : AddLayerCreationOptionsArg(&m_lco);
3826 2 : }
3827 : };
3828 :
3829 : {
3830 2 : MyAlgorithm alg;
3831 1 : alg.GetUsageForCLI(false);
3832 :
3833 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
3834 : {"--lco", "foo=bar", "--lco", "bar=baz"}));
3835 1 : EXPECT_EQ(alg.m_lco.size(), 2U);
3836 : }
3837 :
3838 : {
3839 2 : MyAlgorithm alg;
3840 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3841 1 : CPLErrorReset();
3842 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--lco", "foo"}));
3843 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3844 : }
3845 1 : }
3846 :
3847 4 : TEST_F(test_gdal_algorithm, arg_band)
3848 : {
3849 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3850 : {
3851 : public:
3852 : int m_band{};
3853 :
3854 2 : MyAlgorithm()
3855 2 : {
3856 2 : AddBandArg(&m_band);
3857 2 : }
3858 : };
3859 :
3860 : {
3861 2 : MyAlgorithm alg;
3862 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--band=1"}));
3863 1 : EXPECT_EQ(alg.m_band, 1);
3864 : }
3865 :
3866 : {
3867 2 : MyAlgorithm alg;
3868 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3869 1 : CPLErrorReset();
3870 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--band=0"}));
3871 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3872 : }
3873 1 : }
3874 :
3875 4 : TEST_F(test_gdal_algorithm, arg_band_with_input_dataset)
3876 : {
3877 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3878 : {
3879 : public:
3880 : GDALArgDatasetValue m_input{};
3881 : int m_band{};
3882 :
3883 3 : MyAlgorithm()
3884 3 : {
3885 3 : AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false);
3886 3 : AddBandArg(&m_band);
3887 3 : }
3888 : };
3889 :
3890 : {
3891 2 : MyAlgorithm alg;
3892 4 : EXPECT_TRUE(alg.ParseCommandLineArguments(
3893 : {std::string("--input=")
3894 : .append(tut::common::data_basedir)
3895 : .append(SEP)
3896 : .append("byte.tif"),
3897 : "--band=1"}));
3898 1 : EXPECT_EQ(alg.m_band, 1);
3899 : }
3900 :
3901 : {
3902 2 : MyAlgorithm alg;
3903 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3904 4 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3905 : {std::string("--input=")
3906 : .append(tut::common::data_basedir)
3907 : .append(SEP)
3908 : .append("byte.tif"),
3909 : "--band=2"}));
3910 : }
3911 :
3912 : {
3913 2 : MyAlgorithm alg;
3914 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3915 4 : EXPECT_FALSE(alg.ParseCommandLineArguments(
3916 : {"--input=i_do_not_exist", "--band=1"}));
3917 : }
3918 1 : }
3919 :
3920 4 : TEST_F(test_gdal_algorithm, AddInputDatasetArg_single)
3921 : {
3922 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3923 : {
3924 : public:
3925 : GDALArgDatasetValue m_input{};
3926 :
3927 1 : MyAlgorithm()
3928 1 : {
3929 1 : AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false)
3930 1 : .SetAutoOpenDataset(false);
3931 1 : }
3932 : };
3933 :
3934 : {
3935 2 : MyAlgorithm alg;
3936 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=-"}));
3937 1 : EXPECT_STREQ(alg.m_input.GetName().c_str(), "/vsistdin/");
3938 : }
3939 1 : }
3940 :
3941 4 : TEST_F(test_gdal_algorithm, AddInputDatasetArg_several)
3942 : {
3943 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3944 : {
3945 : public:
3946 : std::vector<GDALArgDatasetValue> m_input{};
3947 :
3948 1 : MyAlgorithm()
3949 1 : {
3950 1 : AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false)
3951 1 : .SetAutoOpenDataset(false);
3952 1 : }
3953 : };
3954 :
3955 : {
3956 1 : MyAlgorithm alg;
3957 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=-"}));
3958 1 : ASSERT_EQ(alg.m_input.size(), 1);
3959 1 : EXPECT_STREQ(alg.m_input[0].GetName().c_str(), "/vsistdin/");
3960 : }
3961 : }
3962 :
3963 4 : TEST_F(test_gdal_algorithm, arg_band_vector)
3964 : {
3965 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3966 : {
3967 : public:
3968 : std::vector<int> m_band{};
3969 :
3970 2 : MyAlgorithm()
3971 2 : {
3972 2 : AddBandArg(&m_band);
3973 2 : }
3974 : };
3975 :
3976 : {
3977 2 : MyAlgorithm alg;
3978 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--band=1,2"}));
3979 2 : const std::vector<int> expected{1, 2};
3980 1 : EXPECT_EQ(alg.m_band, expected);
3981 : }
3982 :
3983 : {
3984 2 : MyAlgorithm alg;
3985 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3986 1 : CPLErrorReset();
3987 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--band=1,0"}));
3988 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3989 : }
3990 1 : }
3991 :
3992 4 : TEST_F(test_gdal_algorithm, arg_band_vector_with_input_dataset)
3993 : {
3994 : class MyAlgorithm : public MyAlgorithmWithDummyRun
3995 : {
3996 : public:
3997 : GDALArgDatasetValue m_input{};
3998 : std::vector<int> m_band{};
3999 :
4000 3 : MyAlgorithm()
4001 3 : {
4002 3 : AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false);
4003 3 : AddBandArg(&m_band);
4004 3 : }
4005 : };
4006 :
4007 : {
4008 2 : MyAlgorithm alg;
4009 4 : EXPECT_TRUE(alg.ParseCommandLineArguments(
4010 : {std::string("--input=")
4011 : .append(tut::common::data_basedir)
4012 : .append(SEP)
4013 : .append("byte.tif"),
4014 : "--band=1"}));
4015 2 : const std::vector<int> expected{1};
4016 1 : EXPECT_EQ(alg.m_band, expected);
4017 : }
4018 :
4019 : {
4020 2 : MyAlgorithm alg;
4021 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4022 4 : EXPECT_FALSE(alg.ParseCommandLineArguments(
4023 : {std::string("--input=")
4024 : .append(tut::common::data_basedir)
4025 : .append(SEP)
4026 : .append("byte.tif"),
4027 : "--band=2"}));
4028 : }
4029 :
4030 : {
4031 2 : MyAlgorithm alg;
4032 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4033 4 : EXPECT_FALSE(alg.ParseCommandLineArguments(
4034 : {"--input=i_do_not_exist", "--band=1"}));
4035 : }
4036 1 : }
4037 :
4038 4 : TEST_F(test_gdal_algorithm, SetHidden)
4039 : {
4040 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4041 : {
4042 : public:
4043 : bool m_b = false;
4044 :
4045 1 : MyAlgorithm()
4046 1 : {
4047 1 : AddArg("flag", 0, "", &m_b).SetHidden().SetCategory(GAAC_ESOTERIC);
4048 1 : }
4049 : };
4050 :
4051 1 : MyAlgorithm alg;
4052 1 : EXPECT_TRUE(alg.GetArg("flag")->IsHiddenForCLI());
4053 1 : EXPECT_TRUE(alg.GetArg("flag")->IsHiddenForAPI());
4054 1 : EXPECT_TRUE(alg.GetArg("flag")->IsHidden());
4055 1 : alg.GetUsageForCLI(false);
4056 1 : }
4057 :
4058 4 : TEST_F(test_gdal_algorithm, SetHiddenForCLI)
4059 : {
4060 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4061 : {
4062 : public:
4063 : bool m_b = false;
4064 :
4065 1 : MyAlgorithm()
4066 1 : {
4067 2 : AddArg("flag", 0, "", &m_b)
4068 1 : .SetHiddenForCLI()
4069 1 : .SetCategory(GAAC_ESOTERIC);
4070 1 : }
4071 : };
4072 :
4073 1 : MyAlgorithm alg;
4074 1 : EXPECT_TRUE(alg.GetArg("flag")->IsHiddenForCLI());
4075 1 : EXPECT_FALSE(alg.GetArg("flag")->IsHiddenForAPI());
4076 1 : EXPECT_FALSE(alg.GetArg("flag")->IsHidden());
4077 1 : alg.GetUsageForCLI(false);
4078 1 : }
4079 :
4080 4 : TEST_F(test_gdal_algorithm, SetHiddenForAPI)
4081 : {
4082 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4083 : {
4084 : public:
4085 : bool m_b = false;
4086 :
4087 1 : MyAlgorithm()
4088 1 : {
4089 2 : AddArg("flag", 0, "", &m_b)
4090 1 : .SetHiddenForAPI()
4091 1 : .SetCategory("my category");
4092 1 : m_longDescription = "long description";
4093 1 : }
4094 : };
4095 :
4096 1 : MyAlgorithm alg;
4097 1 : EXPECT_TRUE(alg.GetArg("flag")->IsHiddenForAPI());
4098 1 : EXPECT_FALSE(alg.GetArg("flag")->IsHiddenForCLI());
4099 1 : EXPECT_FALSE(alg.GetArg("flag")->IsHidden());
4100 1 : alg.GetUsageForCLI(false);
4101 1 : }
4102 :
4103 4 : TEST_F(test_gdal_algorithm, SetSkipIfAlreadySet)
4104 : {
4105 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4106 : {
4107 : public:
4108 : int m_val = 0;
4109 :
4110 2 : MyAlgorithm()
4111 2 : {
4112 2 : AddArg("option", 0, "option", &m_val).SetPositional();
4113 2 : }
4114 : };
4115 :
4116 : {
4117 2 : MyAlgorithm alg;
4118 1 : alg.GetArg("option")->Set(1);
4119 1 : alg.GetArg("option")->SetSkipIfAlreadySet();
4120 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--option=1"}));
4121 : }
4122 :
4123 : {
4124 2 : MyAlgorithm alg;
4125 1 : alg.GetArg("option")->Set(1);
4126 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4127 1 : CPLErrorReset();
4128 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--option=1"}));
4129 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4130 : }
4131 1 : }
4132 :
4133 4 : TEST_F(test_gdal_algorithm, alg_with_aliases)
4134 : {
4135 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4136 : {
4137 : public:
4138 : int m_val = 0;
4139 :
4140 1 : MyAlgorithm()
4141 1 : {
4142 1 : m_aliases.push_back("one_alias");
4143 1 : m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
4144 1 : m_aliases.push_back("hidden_alias");
4145 1 : }
4146 : };
4147 :
4148 2 : MyAlgorithm alg;
4149 1 : alg.GetUsageForCLI(false);
4150 1 : EXPECT_EQ(alg.GetAliases().size(), 3U);
4151 1 : }
4152 :
4153 4 : TEST_F(test_gdal_algorithm, subalgorithms)
4154 : {
4155 1 : bool hasRun = false;
4156 :
4157 : class SubAlgorithm : public GDALAlgorithm
4158 : {
4159 : public:
4160 : bool &m_bHasRun;
4161 : bool m_flag = false;
4162 :
4163 4 : SubAlgorithm(bool &lHasRun)
4164 4 : : GDALAlgorithm("subalg", "", "https://example.com"),
4165 4 : m_bHasRun(lHasRun)
4166 : {
4167 4 : AddProgressArg();
4168 4 : m_aliases.push_back("one_alias");
4169 4 : m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
4170 4 : m_aliases.push_back("hidden_alias");
4171 4 : }
4172 :
4173 1 : bool RunImpl(GDALProgressFunc, void *) override
4174 : {
4175 1 : m_bHasRun = true;
4176 1 : return true;
4177 : }
4178 : };
4179 :
4180 : class MyAlgorithm : public MyAlgorithmWithDummyRun
4181 : {
4182 : public:
4183 5 : MyAlgorithm(bool &lHasRun)
4184 5 : {
4185 10 : GDALAlgorithmRegistry::AlgInfo info;
4186 5 : info.m_name = "subalg";
4187 4 : info.m_creationFunc = [&lHasRun]()
4188 9 : { return std::make_unique<SubAlgorithm>(lHasRun); };
4189 5 : RegisterSubAlgorithm(info);
4190 : // RegisterSubAlgorithm(SubAlgorithm);
4191 5 : }
4192 : };
4193 :
4194 : {
4195 2 : MyAlgorithm alg(hasRun);
4196 1 : alg.GetUsageForCLI(false);
4197 :
4198 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4199 1 : CPLErrorReset();
4200 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
4201 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4202 : }
4203 :
4204 : {
4205 2 : MyAlgorithm alg(hasRun);
4206 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4207 1 : CPLErrorReset();
4208 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"invalid_subcommand"}));
4209 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4210 : }
4211 :
4212 : {
4213 1 : MyAlgorithm alg(hasRun);
4214 2 : alg.SetCallPath(std::vector<std::string>{"main"});
4215 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg"}));
4216 1 : EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "subalg");
4217 1 : EXPECT_TRUE(alg.ValidateArguments());
4218 1 : EXPECT_TRUE(alg.Run());
4219 1 : EXPECT_TRUE(hasRun);
4220 : {
4221 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4222 1 : EXPECT_FALSE(alg.Run());
4223 1 : EXPECT_STREQ(
4224 : CPLGetLastErrorMsg(),
4225 : "subalg: Run() can be called only once per algorithm instance");
4226 : }
4227 1 : EXPECT_TRUE(alg.Finalize());
4228 1 : alg.GetUsageForCLI(false);
4229 : }
4230 :
4231 : {
4232 1 : MyAlgorithm alg(hasRun);
4233 1 : alg.SetCalledFromCommandLine();
4234 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "-h"}));
4235 1 : EXPECT_TRUE(alg.IsHelpRequested());
4236 1 : EXPECT_TRUE(alg.ValidateArguments());
4237 1 : alg.GetUsageForCLI(false);
4238 : }
4239 :
4240 : {
4241 1 : MyAlgorithm alg(hasRun);
4242 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "--progress"}));
4243 1 : EXPECT_TRUE(alg.IsProgressBarRequested());
4244 1 : EXPECT_TRUE(alg.ValidateArguments());
4245 1 : alg.GetUsageForCLI(false);
4246 : }
4247 1 : }
4248 :
4249 : class MyRedundantRasterAlgorithm : public MyAlgorithmWithDummyRun
4250 : {
4251 : public:
4252 : static constexpr const char *NAME = "raster";
4253 : static constexpr const char *DESCRIPTION =
4254 : "redundant with existing raster!!!";
4255 : static constexpr const char *HELP_URL = "";
4256 : };
4257 :
4258 : class MyAlgorithmWithAlias : public MyAlgorithmWithDummyRun
4259 : {
4260 : public:
4261 : static constexpr const char *NAME = "MyAlgorithmWithAlias";
4262 : static constexpr const char *DESCRIPTION = "";
4263 : static constexpr const char *HELP_URL = "";
4264 :
4265 1 : static std::vector<std::string> GetAliasesStatic()
4266 : {
4267 : return {"alias", GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR,
4268 4 : "hidden_alias"};
4269 : }
4270 : };
4271 :
4272 : class MyAlgorithmWithRedundantAlias : public MyAlgorithmWithDummyRun
4273 : {
4274 : public:
4275 : static constexpr const char *NAME = "MyAlgorithmWithRedundantAlias";
4276 : static constexpr const char *DESCRIPTION = "";
4277 : static constexpr const char *HELP_URL = "";
4278 :
4279 1 : static std::vector<std::string> GetAliasesStatic()
4280 : {
4281 2 : return {"alias"};
4282 : }
4283 : };
4284 :
4285 : class MyAlgorithmWithRedundantHiddenAlias : public MyAlgorithmWithDummyRun
4286 : {
4287 : public:
4288 : static constexpr const char *NAME = "MyAlgorithmWithRedundantHiddenAlias";
4289 : static constexpr const char *DESCRIPTION = "";
4290 : static constexpr const char *HELP_URL = "";
4291 :
4292 1 : static std::vector<std::string> GetAliasesStatic()
4293 : {
4294 3 : return {GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR, "hidden_alias"};
4295 : }
4296 : };
4297 :
4298 4 : TEST_F(test_gdal_algorithm, GDALGlobalAlgorithmRegistry)
4299 : {
4300 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4301 1 : EXPECT_NE(singleton.GetInfo("raster"), nullptr);
4302 1 : EXPECT_EQ(singleton.GetInfo("not_existing"), nullptr);
4303 2 : auto alg = singleton.Instantiate("raster");
4304 1 : ASSERT_NE(alg, nullptr);
4305 1 : EXPECT_TRUE(!alg->GetUsageAsJSON().empty());
4306 :
4307 : {
4308 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4309 1 : EXPECT_FALSE(singleton.Register<MyRedundantRasterAlgorithm>());
4310 : }
4311 :
4312 1 : EXPECT_TRUE(singleton.Register<MyAlgorithmWithAlias>());
4313 : {
4314 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4315 1 : EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantAlias>());
4316 : }
4317 : {
4318 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4319 1 : EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantHiddenAlias>());
4320 : }
4321 : }
4322 :
4323 4 : TEST_F(test_gdal_algorithm, registry)
4324 : {
4325 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4326 2 : EXPECT_EQ(singleton.Instantiate(std::vector<std::string>()), nullptr);
4327 2 : EXPECT_EQ(singleton.Instantiate("vector", "not_existing"), nullptr);
4328 1 : }
4329 :
4330 4 : TEST_F(test_gdal_algorithm, vector_pipeline_GetUsageForCLI)
4331 : {
4332 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4333 2 : auto pipeline = singleton.Instantiate("vector", "pipeline");
4334 1 : ASSERT_NE(pipeline, nullptr);
4335 1 : pipeline->GetUsageForCLI(false);
4336 1 : pipeline->GetUsageForCLI(true);
4337 : }
4338 :
4339 4 : TEST_F(test_gdal_algorithm, raster_pipeline_GetUsageForCLI)
4340 : {
4341 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4342 2 : auto raster = singleton.Instantiate("raster");
4343 1 : ASSERT_NE(raster, nullptr);
4344 2 : auto pipeline = raster->InstantiateSubAlgorithm("pipeline");
4345 1 : ASSERT_NE(pipeline, nullptr);
4346 1 : pipeline->GetUsageForCLI(false);
4347 1 : pipeline->GetUsageForCLI(true);
4348 :
4349 : {
4350 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4351 1 : CPLErrorReset();
4352 2 : EXPECT_EQ(raster->InstantiateSubAlgorithm("pipline"), nullptr);
4353 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
4354 : "Algorithm 'pipline' is unknown. Do you mean 'pipeline'?");
4355 : }
4356 :
4357 : {
4358 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4359 1 : CPLErrorReset();
4360 2 : EXPECT_EQ(raster->InstantiateSubAlgorithm("pipleine"), nullptr);
4361 1 : EXPECT_STREQ(
4362 : CPLGetLastErrorMsg(),
4363 : "Algorithm 'pipleine' is unknown. Do you mean 'pipeline'?");
4364 : }
4365 : }
4366 :
4367 4 : TEST_F(test_gdal_algorithm, registry_c_api)
4368 : {
4369 1 : auto reg = GDALGetGlobalAlgorithmRegistry();
4370 1 : ASSERT_NE(reg, nullptr);
4371 1 : char **names = GDALAlgorithmRegistryGetAlgNames(reg);
4372 1 : EXPECT_GE(CSLCount(names), 2);
4373 1 : CSLDestroy(names);
4374 : {
4375 1 : auto alg = GDALAlgorithmRegistryInstantiateAlg(reg, "raster");
4376 1 : ASSERT_NE(alg, nullptr);
4377 1 : EXPECT_EQ(GDALAlgorithmRegistryInstantiateAlg(reg, "not_existing"),
4378 : nullptr);
4379 1 : GDALAlgorithmRelease(alg);
4380 : }
4381 : {
4382 1 : const char *const apszPath[] = {"raster", "reproject", nullptr};
4383 1 : auto alg = GDALAlgorithmRegistryInstantiateAlgFromPath(reg, apszPath);
4384 1 : ASSERT_NE(alg, nullptr);
4385 1 : GDALAlgorithmRelease(alg);
4386 : }
4387 1 : GDALAlgorithmRegistryRelease(reg);
4388 : }
4389 :
4390 4 : TEST_F(test_gdal_algorithm, algorithm_c_api)
4391 : {
4392 : class MyAlgorithm : public GDALAlgorithm
4393 : {
4394 : public:
4395 : bool m_flag = false;
4396 : std::string m_str{};
4397 : int m_int = 0;
4398 : double m_double = 0;
4399 : std::vector<std::string> m_strlist{};
4400 : std::vector<int> m_intlist{};
4401 : std::vector<double> m_doublelist{};
4402 : GDALArgDatasetValue m_dsValue{};
4403 :
4404 : bool m_hasParsedCommandLinearguments = false;
4405 : bool m_hasRun = false;
4406 : bool m_hasFinalized = false;
4407 :
4408 1 : MyAlgorithm()
4409 1 : : GDALAlgorithm("test", "description", "http://example.com")
4410 : {
4411 1 : m_longDescription = "long description";
4412 1 : AddArg("flag", 'f', "boolean flag", &m_flag).SetDefault(true);
4413 1 : AddArg("str", 0, "str", &m_str).SetDefault("default");
4414 1 : AddArg("int", 0, "int", &m_int).SetDefault(1);
4415 1 : AddArg("double", 0, "double", &m_double).SetDefault(1.5);
4416 2 : AddArg("strlist", 0, "strlist", &m_strlist)
4417 3 : .SetDefault(std::vector<std::string>{"one", "two"});
4418 2 : AddArg("doublelist", 0, "doublelist", &m_doublelist)
4419 1 : .SetDefault(std::vector<double>{1.5, 2.5});
4420 2 : AddArg("intlist", 0, "intlist", &m_intlist)
4421 1 : .SetDefault(std::vector<int>{1, 2});
4422 2 : AddArg("dataset", 0, "dataset", &m_dsValue)
4423 1 : .SetAvailableInPipelineStep(false);
4424 1 : }
4425 :
4426 : bool
4427 1 : ParseCommandLineArguments(const std::vector<std::string> &args) override
4428 : {
4429 1 : m_hasParsedCommandLinearguments = true;
4430 1 : return GDALAlgorithm::ParseCommandLineArguments(args);
4431 : }
4432 :
4433 1 : bool RunImpl(GDALProgressFunc, void *) override
4434 : {
4435 1 : m_hasRun = true;
4436 1 : return true;
4437 : }
4438 :
4439 1 : bool Finalize() override
4440 : {
4441 1 : m_hasFinalized = true;
4442 1 : return GDALAlgorithm::Finalize();
4443 : }
4444 : };
4445 :
4446 : auto hAlg =
4447 1 : std::make_unique<GDALAlgorithmHS>(std::make_unique<MyAlgorithm>());
4448 1 : MyAlgorithm *pAlg = cpl::down_cast<MyAlgorithm *>(hAlg->ptr);
4449 1 : EXPECT_STREQ(GDALAlgorithmGetName(hAlg.get()), "test");
4450 1 : EXPECT_STREQ(GDALAlgorithmGetDescription(hAlg.get()), "description");
4451 1 : EXPECT_STREQ(GDALAlgorithmGetLongDescription(hAlg.get()),
4452 : "long description");
4453 1 : EXPECT_STREQ(GDALAlgorithmGetHelpFullURL(hAlg.get()), "http://example.com");
4454 1 : EXPECT_FALSE(GDALAlgorithmHasSubAlgorithms(hAlg.get()));
4455 1 : EXPECT_EQ(GDALAlgorithmGetSubAlgorithmNames(hAlg.get()), nullptr);
4456 1 : EXPECT_EQ(GDALAlgorithmInstantiateSubAlgorithm(hAlg.get(), "not_existing"),
4457 : nullptr);
4458 3 : EXPECT_TRUE(GDALAlgorithmParseCommandLineArguments(
4459 : hAlg.get(), CPLStringList(std::vector<std::string>({"-f"})).List()));
4460 1 : EXPECT_TRUE(pAlg->m_hasParsedCommandLinearguments);
4461 1 : EXPECT_TRUE(GDALAlgorithmRun(hAlg.get(), nullptr, nullptr));
4462 1 : EXPECT_TRUE(pAlg->m_hasRun);
4463 1 : EXPECT_TRUE(GDALAlgorithmFinalize(hAlg.get()));
4464 1 : EXPECT_TRUE(pAlg->m_hasFinalized);
4465 1 : char *jsonUsage = GDALAlgorithmGetUsageAsJSON(hAlg.get());
4466 1 : EXPECT_NE(jsonUsage, nullptr);
4467 1 : CPLFree(jsonUsage);
4468 :
4469 1 : char **argNames = GDALAlgorithmGetArgNames(hAlg.get());
4470 1 : ASSERT_NE(argNames, nullptr);
4471 1 : EXPECT_EQ(CSLCount(argNames), 12);
4472 1 : CSLDestroy(argNames);
4473 :
4474 1 : EXPECT_EQ(GDALAlgorithmGetArg(hAlg.get(), "non_existing"), nullptr);
4475 : {
4476 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "flag");
4477 1 : ASSERT_NE(hArg, nullptr);
4478 1 : EXPECT_TRUE(GDALAlgorithmArgHasDefaultValue(hArg));
4479 : {
4480 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4481 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsString(hArg), nullptr);
4482 : }
4483 1 : EXPECT_TRUE(GDALAlgorithmArgGetDefaultAsBoolean(hArg));
4484 1 : GDALAlgorithmArgSetAsBoolean(hArg, true);
4485 1 : EXPECT_TRUE(GDALAlgorithmArgGetAsBoolean(hArg));
4486 1 : GDALAlgorithmArgRelease(hArg);
4487 : }
4488 : {
4489 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "str");
4490 1 : ASSERT_NE(hArg, nullptr);
4491 : {
4492 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4493 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsInteger(hArg), 0);
4494 : }
4495 1 : EXPECT_STREQ(GDALAlgorithmArgGetDefaultAsString(hArg), "default");
4496 1 : GDALAlgorithmArgSetAsString(hArg, "foo");
4497 1 : EXPECT_STREQ(GDALAlgorithmArgGetAsString(hArg), "foo");
4498 1 : GDALAlgorithmArgRelease(hArg);
4499 : }
4500 : {
4501 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "int");
4502 1 : ASSERT_NE(hArg, nullptr);
4503 : {
4504 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4505 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsDouble(hArg), 0);
4506 : }
4507 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsInteger(hArg), 1);
4508 1 : GDALAlgorithmArgSetAsInteger(hArg, 2);
4509 1 : EXPECT_EQ(GDALAlgorithmArgGetAsInteger(hArg), 2);
4510 1 : GDALAlgorithmArgRelease(hArg);
4511 : }
4512 : {
4513 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "double");
4514 1 : ASSERT_NE(hArg, nullptr);
4515 : {
4516 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4517 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsStringList(hArg), nullptr);
4518 : }
4519 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsDouble(hArg), 1.5);
4520 1 : GDALAlgorithmArgSetAsDouble(hArg, 2.5);
4521 1 : EXPECT_EQ(GDALAlgorithmArgGetAsDouble(hArg), 2.5);
4522 1 : GDALAlgorithmArgRelease(hArg);
4523 : }
4524 : {
4525 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "strlist");
4526 1 : ASSERT_NE(hArg, nullptr);
4527 : {
4528 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4529 : size_t nCount;
4530 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsIntegerList(hArg, &nCount),
4531 : nullptr);
4532 : }
4533 : {
4534 1 : char **ret = GDALAlgorithmArgGetDefaultAsStringList(hArg);
4535 1 : EXPECT_EQ(CSLCount(ret), 2);
4536 1 : if (CSLCount(ret) == 2)
4537 : {
4538 1 : EXPECT_STREQ(ret[0], "one");
4539 1 : EXPECT_STREQ(ret[1], "two");
4540 : }
4541 1 : CSLDestroy(ret);
4542 : }
4543 6 : const CPLStringList list(std::vector<std::string>({"foo", "bar"}));
4544 1 : GDALAlgorithmArgSetAsStringList(hArg, list.List());
4545 1 : char **ret = GDALAlgorithmArgGetAsStringList(hArg);
4546 1 : EXPECT_EQ(CSLCount(ret), 2);
4547 1 : if (CSLCount(ret) == 2)
4548 : {
4549 1 : EXPECT_STREQ(ret[0], "foo");
4550 1 : EXPECT_STREQ(ret[1], "bar");
4551 : }
4552 1 : CSLDestroy(ret);
4553 1 : GDALAlgorithmArgRelease(hArg);
4554 : }
4555 : {
4556 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "intlist");
4557 1 : ASSERT_NE(hArg, nullptr);
4558 : {
4559 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4560 : size_t nCount;
4561 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsDoubleList(hArg, &nCount),
4562 : nullptr);
4563 : }
4564 : {
4565 1 : size_t nCount = 0;
4566 : const int *ret =
4567 1 : GDALAlgorithmArgGetDefaultAsIntegerList(hArg, &nCount);
4568 1 : ASSERT_EQ(nCount, 2);
4569 1 : ASSERT_NE(ret, nullptr);
4570 1 : EXPECT_EQ(ret[0], 1);
4571 1 : EXPECT_EQ(ret[1], 2);
4572 : }
4573 1 : std::vector<int> vals{2, 3};
4574 1 : GDALAlgorithmArgSetAsIntegerList(hArg, vals.size(), vals.data());
4575 1 : size_t nCount = 0;
4576 1 : const int *ret = GDALAlgorithmArgGetAsIntegerList(hArg, &nCount);
4577 1 : ASSERT_EQ(nCount, 2);
4578 1 : ASSERT_NE(ret, nullptr);
4579 1 : EXPECT_EQ(ret[0], 2);
4580 1 : EXPECT_EQ(ret[1], 3);
4581 1 : GDALAlgorithmArgRelease(hArg);
4582 : }
4583 : {
4584 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "doublelist");
4585 1 : ASSERT_NE(hArg, nullptr);
4586 : {
4587 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4588 1 : EXPECT_EQ(GDALAlgorithmArgGetDefaultAsBoolean(hArg), false);
4589 : }
4590 1 : EXPECT_TRUE(GDALAlgorithmArgIsAvailableInPipelineStep(hArg));
4591 : {
4592 1 : size_t nCount = 0;
4593 : const double *ret =
4594 1 : GDALAlgorithmArgGetDefaultAsDoubleList(hArg, &nCount);
4595 1 : ASSERT_EQ(nCount, 2);
4596 1 : ASSERT_NE(ret, nullptr);
4597 1 : EXPECT_EQ(ret[0], 1.5);
4598 1 : EXPECT_EQ(ret[1], 2.5);
4599 : }
4600 1 : std::vector<double> vals{2.5, 3.5};
4601 1 : GDALAlgorithmArgSetAsDoubleList(hArg, vals.size(), vals.data());
4602 1 : size_t nCount = 0;
4603 1 : const double *ret = GDALAlgorithmArgGetAsDoubleList(hArg, &nCount);
4604 1 : ASSERT_EQ(nCount, 2);
4605 1 : ASSERT_NE(ret, nullptr);
4606 1 : EXPECT_EQ(ret[0], 2.5);
4607 1 : EXPECT_EQ(ret[1], 3.5);
4608 1 : GDALAlgorithmArgRelease(hArg);
4609 : }
4610 : {
4611 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "dataset");
4612 1 : ASSERT_NE(hArg, nullptr);
4613 1 : EXPECT_EQ(GDALAlgorithmArgGetDatasetType(hArg),
4614 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
4615 1 : EXPECT_EQ(GDALAlgorithmArgGetDatasetInputFlags(hArg),
4616 : GADV_NAME | GADV_OBJECT);
4617 1 : EXPECT_EQ(GDALAlgorithmArgGetDatasetOutputFlags(hArg), GADV_OBJECT);
4618 1 : EXPECT_FALSE(GDALAlgorithmArgIsAvailableInPipelineStep(hArg));
4619 1 : GDALArgDatasetValueH hVal = GDALArgDatasetValueCreate();
4620 1 : GDALArgDatasetValueSetName(hVal, "foo");
4621 :
4622 : {
4623 : auto poDS = std::unique_ptr<GDALDataset>(
4624 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
4625 2 : "", 1, 1, 1, GDT_Byte, nullptr));
4626 1 : GDALArgDatasetValueSetDataset(hVal, poDS.release());
4627 : }
4628 :
4629 1 : GDALAlgorithmArgSetAsDatasetValue(hArg, hVal);
4630 1 : GDALArgDatasetValueRelease(hVal);
4631 :
4632 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
4633 1 : ASSERT_NE(hVal, nullptr);
4634 1 : auto hDS = GDALArgDatasetValueGetDatasetRef(hVal);
4635 1 : EXPECT_NE(hDS, nullptr);
4636 : {
4637 1 : auto hDS2 = GDALArgDatasetValueGetDatasetIncreaseRefCount(hVal);
4638 1 : EXPECT_EQ(hDS2, hDS);
4639 1 : GDALReleaseDataset(hDS2);
4640 : }
4641 1 : GDALArgDatasetValueRelease(hVal);
4642 :
4643 1 : GDALAlgorithmArgSetDataset(hArg, nullptr);
4644 :
4645 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
4646 1 : ASSERT_NE(hVal, nullptr);
4647 1 : EXPECT_EQ(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
4648 1 : GDALArgDatasetValueRelease(hVal);
4649 :
4650 : {
4651 : auto poDS = std::unique_ptr<GDALDataset>(
4652 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
4653 2 : "", 1, 1, 1, GDT_Byte, nullptr));
4654 1 : GDALAlgorithmArgSetDataset(hArg, poDS.release());
4655 : }
4656 :
4657 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
4658 1 : ASSERT_NE(hVal, nullptr);
4659 1 : EXPECT_NE(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
4660 1 : GDALArgDatasetValueRelease(hVal);
4661 :
4662 1 : GDALAlgorithmArgRelease(hArg);
4663 : }
4664 : }
4665 :
4666 4 : TEST_F(test_gdal_algorithm, DispatcherGetUsageForCLI)
4667 : {
4668 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4669 : {
4670 2 : auto info = singleton.Instantiate("info");
4671 1 : info->GetUsageForCLI(false);
4672 : }
4673 : {
4674 2 : auto info = singleton.Instantiate("info");
4675 3 : EXPECT_TRUE(info->ParseCommandLineArguments(
4676 : std::vector<std::string>{GCORE_DATA_DIR "byte.tif"}));
4677 1 : info->GetUsageForCLI(false);
4678 : }
4679 : {
4680 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
4681 1 : if (!poDriver)
4682 : {
4683 0 : GTEST_SKIP() << "GPKG support missing";
4684 : }
4685 : else
4686 : {
4687 : std::string osTmpFilename =
4688 2 : VSIMemGenerateHiddenFilename("temp.gpkg");
4689 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
4690 2 : osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, nullptr));
4691 1 : double adfGT[] = {1, 1, 0, 1, 0, -1};
4692 1 : poDS->SetGeoTransform(adfGT);
4693 1 : poDS->CreateLayer("foo");
4694 1 : poDS.reset();
4695 :
4696 3 : auto info = singleton.Instantiate("info");
4697 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4698 3 : EXPECT_FALSE(info->ParseCommandLineArguments(
4699 : std::vector<std::string>{osTmpFilename.c_str()}));
4700 1 : info->GetUsageForCLI(false);
4701 :
4702 1 : VSIUnlink(osTmpFilename.c_str());
4703 : }
4704 : }
4705 : }
4706 :
4707 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_dataset_0_0)
4708 : {
4709 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4710 2 : auto raster = singleton.Instantiate("raster");
4711 1 : ASSERT_NE(raster, nullptr);
4712 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4713 1 : ASSERT_NE(edit, nullptr);
4714 :
4715 : class MyDataset : public GDALDataset
4716 : {
4717 : public:
4718 1 : MyDataset()
4719 1 : {
4720 1 : nRasterXSize = 0;
4721 1 : nRasterYSize = 0;
4722 1 : eAccess = GA_Update;
4723 1 : }
4724 : };
4725 :
4726 1 : auto datasetArg = edit->GetArg("dataset");
4727 1 : ASSERT_NE(datasetArg, nullptr);
4728 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4729 :
4730 1 : auto extentArg = edit->GetArg("bbox");
4731 1 : ASSERT_NE(extentArg, nullptr);
4732 1 : extentArg->Set(std::vector<double>{2, 49, 3, 50});
4733 :
4734 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4735 1 : CPLErrorReset();
4736 1 : EXPECT_FALSE(edit->Run());
4737 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4738 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Cannot set extent because one of "
4739 : "dataset height or width is null");
4740 : }
4741 :
4742 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_none)
4743 : {
4744 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4745 2 : auto raster = singleton.Instantiate("raster");
4746 1 : ASSERT_NE(raster, nullptr);
4747 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4748 1 : ASSERT_NE(edit, nullptr);
4749 :
4750 : class MyDataset : public GDALDataset
4751 : {
4752 : public:
4753 1 : MyDataset()
4754 1 : {
4755 1 : eAccess = GA_Update;
4756 1 : }
4757 :
4758 1 : CPLErr SetSpatialRef(const OGRSpatialReference *) override
4759 : {
4760 1 : return CE_Failure;
4761 : }
4762 : };
4763 :
4764 1 : auto datasetArg = edit->GetArg("dataset");
4765 1 : ASSERT_NE(datasetArg, nullptr);
4766 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4767 :
4768 1 : auto crsArg = edit->GetArg("crs");
4769 1 : ASSERT_NE(crsArg, nullptr);
4770 1 : crsArg->Set("none");
4771 :
4772 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4773 1 : CPLErrorReset();
4774 1 : EXPECT_FALSE(edit->Run());
4775 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4776 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: SetSpatialRef(none) failed");
4777 : }
4778 :
4779 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_regular)
4780 : {
4781 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4782 2 : auto raster = singleton.Instantiate("raster");
4783 1 : ASSERT_NE(raster, nullptr);
4784 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4785 1 : ASSERT_NE(edit, nullptr);
4786 :
4787 : class MyDataset : public GDALDataset
4788 : {
4789 : public:
4790 1 : MyDataset()
4791 1 : {
4792 1 : eAccess = GA_Update;
4793 1 : }
4794 :
4795 1 : CPLErr SetSpatialRef(const OGRSpatialReference *) override
4796 : {
4797 1 : return CE_Failure;
4798 : }
4799 : };
4800 :
4801 1 : auto datasetArg = edit->GetArg("dataset");
4802 1 : ASSERT_NE(datasetArg, nullptr);
4803 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4804 :
4805 1 : auto crsArg = edit->GetArg("crs");
4806 1 : ASSERT_NE(crsArg, nullptr);
4807 1 : crsArg->Set("EPSG:32632");
4808 :
4809 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4810 1 : CPLErrorReset();
4811 1 : EXPECT_FALSE(edit->Run());
4812 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4813 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
4814 : "edit: SetSpatialRef(EPSG:32632) failed");
4815 : }
4816 :
4817 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_geo_transform)
4818 : {
4819 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4820 2 : auto raster = singleton.Instantiate("raster");
4821 1 : ASSERT_NE(raster, nullptr);
4822 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4823 1 : ASSERT_NE(edit, nullptr);
4824 :
4825 : class MyDataset : public GDALDataset
4826 : {
4827 : public:
4828 1 : MyDataset()
4829 1 : {
4830 1 : eAccess = GA_Update;
4831 1 : }
4832 :
4833 1 : CPLErr SetGeoTransform(const GDALGeoTransform &) override
4834 : {
4835 1 : return CE_Failure;
4836 : }
4837 : };
4838 :
4839 1 : auto datasetArg = edit->GetArg("dataset");
4840 1 : ASSERT_NE(datasetArg, nullptr);
4841 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4842 :
4843 1 : auto extentArg = edit->GetArg("bbox");
4844 1 : ASSERT_NE(extentArg, nullptr);
4845 1 : extentArg->Set(std::vector<double>{2, 49, 3, 50});
4846 :
4847 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4848 1 : CPLErrorReset();
4849 1 : EXPECT_FALSE(edit->Run());
4850 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4851 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Setting extent failed");
4852 : }
4853 :
4854 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_metadata)
4855 : {
4856 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4857 2 : auto raster = singleton.Instantiate("raster");
4858 1 : ASSERT_NE(raster, nullptr);
4859 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4860 1 : ASSERT_NE(edit, nullptr);
4861 :
4862 : class MyDataset : public GDALDataset
4863 : {
4864 : public:
4865 1 : MyDataset()
4866 1 : {
4867 1 : eAccess = GA_Update;
4868 1 : }
4869 :
4870 1 : CPLErr SetMetadataItem(const char *, const char *,
4871 : const char *) override
4872 : {
4873 1 : return CE_Failure;
4874 : }
4875 : };
4876 :
4877 1 : auto datasetArg = edit->GetArg("dataset");
4878 1 : ASSERT_NE(datasetArg, nullptr);
4879 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4880 :
4881 1 : auto extentArg = edit->GetArg("metadata");
4882 1 : ASSERT_NE(extentArg, nullptr);
4883 2 : extentArg->Set(std::vector<std::string>{"foo=bar"});
4884 :
4885 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4886 1 : CPLErrorReset();
4887 1 : EXPECT_FALSE(edit->Run());
4888 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4889 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
4890 : "edit: SetMetadataItem('foo', 'bar') failed");
4891 : }
4892 :
4893 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_unset_metadata)
4894 : {
4895 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4896 2 : auto raster = singleton.Instantiate("raster");
4897 1 : ASSERT_NE(raster, nullptr);
4898 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
4899 1 : ASSERT_NE(edit, nullptr);
4900 :
4901 : class MyDataset : public GDALDataset
4902 : {
4903 : public:
4904 1 : MyDataset()
4905 1 : {
4906 1 : eAccess = GA_Update;
4907 1 : }
4908 :
4909 1 : CPLErr SetMetadataItem(const char *, const char *,
4910 : const char *) override
4911 : {
4912 1 : return CE_Failure;
4913 : }
4914 : };
4915 :
4916 1 : auto datasetArg = edit->GetArg("dataset");
4917 1 : ASSERT_NE(datasetArg, nullptr);
4918 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
4919 :
4920 1 : auto extentArg = edit->GetArg("unset-metadata");
4921 1 : ASSERT_NE(extentArg, nullptr);
4922 2 : extentArg->Set(std::vector<std::string>{"foo"});
4923 :
4924 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
4925 1 : CPLErrorReset();
4926 1 : EXPECT_FALSE(edit->Run());
4927 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
4928 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
4929 : "edit: SetMetadataItem('foo', NULL) failed");
4930 : }
4931 :
4932 4 : TEST_F(test_gdal_algorithm, register_plugin_algorithms)
4933 : {
4934 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
4935 1 : bool flag = false;
4936 3 : singleton.DeclareAlgorithm(
4937 : {"foo", "bar"},
4938 5 : [&flag]() -> std::unique_ptr<GDALAlgorithm>
4939 : {
4940 5 : flag = true;
4941 5 : return std::make_unique<GDALContainerAlgorithm>("dummy");
4942 2 : });
4943 :
4944 : {
4945 2 : EXPECT_NE(singleton.Instantiate("foo"), nullptr);
4946 1 : EXPECT_FALSE(flag);
4947 : }
4948 :
4949 : {
4950 5 : auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal"});
4951 1 : EXPECT_TRUE(std::find(got.begin(), got.end(), "foo") != got.end());
4952 1 : EXPECT_FALSE(flag);
4953 : }
4954 :
4955 : {
4956 5 : auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal", "foo"});
4957 1 : EXPECT_TRUE(std::find(got.begin(), got.end(), "bar") != got.end());
4958 1 : EXPECT_TRUE(flag);
4959 1 : flag = false;
4960 : }
4961 :
4962 : {
4963 : auto got =
4964 7 : singleton.GetDeclaredSubAlgorithmNames({"gdal", "foo", "bar"});
4965 1 : EXPECT_TRUE(got.empty());
4966 1 : EXPECT_FALSE(flag);
4967 : }
4968 :
4969 : {
4970 6 : auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal", "bar"});
4971 1 : EXPECT_TRUE(got.empty());
4972 1 : EXPECT_FALSE(flag);
4973 : }
4974 :
4975 : {
4976 5 : auto alg = singleton.InstantiateDeclaredSubAlgorithm({"gdal", "foo"});
4977 1 : ASSERT_NE(alg, nullptr);
4978 1 : EXPECT_TRUE(alg->HasSubAlgorithms());
4979 1 : EXPECT_EQ(alg->GetSubAlgorithmNames().size(), 1);
4980 1 : EXPECT_TRUE(flag);
4981 1 : flag = false;
4982 : }
4983 :
4984 : {
4985 : auto alg =
4986 6 : singleton.InstantiateDeclaredSubAlgorithm({"gdal", "foo", "bar"});
4987 1 : ASSERT_NE(alg, nullptr);
4988 1 : EXPECT_TRUE(flag);
4989 1 : flag = false;
4990 : }
4991 :
4992 : {
4993 2 : auto alg = singleton.Instantiate("foo")->InstantiateSubAlgorithm("bar");
4994 1 : ASSERT_NE(alg, nullptr);
4995 1 : EXPECT_TRUE(flag);
4996 : }
4997 :
4998 : {
4999 5 : auto alg = singleton.InstantiateDeclaredSubAlgorithm({"gdal", "bar"});
5000 1 : ASSERT_EQ(alg, nullptr);
5001 : }
5002 :
5003 3 : singleton.DeclareAlgorithm({"foo", "bar"},
5004 0 : []() -> std::unique_ptr<GDALAlgorithm>
5005 2 : { return nullptr; });
5006 : }
5007 :
5008 4 : TEST_F(test_gdal_algorithm, AddNumThreadsArg)
5009 : {
5010 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5011 : {
5012 : public:
5013 : int m_numThreads = 0;
5014 : std::string m_numThreadsStr{"ALL_CPUS"};
5015 :
5016 9 : MyAlgorithm()
5017 9 : {
5018 9 : AddNumThreadsArg(&m_numThreads, &m_numThreadsStr);
5019 9 : }
5020 : };
5021 :
5022 : {
5023 2 : MyAlgorithm alg;
5024 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
5025 1 : EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
5026 : }
5027 :
5028 : {
5029 2 : CPLConfigOptionSetter oSetter("GDAL_NUM_THREADS", "1", false);
5030 2 : MyAlgorithm alg;
5031 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
5032 1 : EXPECT_EQ(alg.m_numThreads, 1);
5033 : }
5034 :
5035 : {
5036 2 : CPLConfigOptionSetter oSetter("GDAL_NUM_THREADS", "ALL_CPUS", false);
5037 2 : MyAlgorithm alg;
5038 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
5039 1 : EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
5040 : }
5041 :
5042 : {
5043 2 : MyAlgorithm alg;
5044 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--num-threads=1"}));
5045 1 : EXPECT_EQ(alg.m_numThreads, 1);
5046 : }
5047 :
5048 : {
5049 2 : MyAlgorithm alg;
5050 3 : EXPECT_TRUE(
5051 : alg.ParseCommandLineArguments({"--num-threads=2147483647"}));
5052 1 : EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
5053 : }
5054 :
5055 : {
5056 2 : MyAlgorithm alg;
5057 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--num-threads=ALL_CPUS"}));
5058 1 : EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
5059 : }
5060 :
5061 : {
5062 2 : MyAlgorithm alg;
5063 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
5064 1 : CPLErrorReset();
5065 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=invalid"}));
5066 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
5067 : }
5068 :
5069 : {
5070 2 : MyAlgorithm alg;
5071 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
5072 1 : CPLErrorReset();
5073 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=-1"}));
5074 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
5075 : }
5076 :
5077 : {
5078 2 : MyAlgorithm alg;
5079 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
5080 1 : CPLErrorReset();
5081 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=2147483648"}));
5082 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
5083 : }
5084 1 : }
5085 :
5086 4 : TEST_F(test_gdal_algorithm, AddAppendLayerArg_without_update)
5087 : {
5088 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5089 : {
5090 : public:
5091 : bool m_boolean = false;
5092 :
5093 1 : MyAlgorithm()
5094 1 : {
5095 1 : AddAppendLayerArg(&m_boolean);
5096 1 : }
5097 : };
5098 :
5099 : {
5100 2 : MyAlgorithm alg;
5101 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5102 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
5103 1 : EXPECT_STREQ(
5104 : CPLGetLastErrorMsg(),
5105 : "test: --update argument must exist for --append, even if hidden");
5106 : }
5107 1 : }
5108 :
5109 4 : TEST_F(test_gdal_algorithm, AddOverwriteLayerArg_without_update)
5110 : {
5111 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5112 : {
5113 : public:
5114 : bool m_boolean = false;
5115 :
5116 1 : MyAlgorithm()
5117 1 : {
5118 1 : AddOverwriteLayerArg(&m_boolean);
5119 1 : }
5120 : };
5121 :
5122 : {
5123 2 : MyAlgorithm alg;
5124 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5125 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
5126 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5127 : "test: --update argument must exist for "
5128 : "--overwrite-layer, even if hidden");
5129 : }
5130 1 : }
5131 :
5132 4 : TEST_F(test_gdal_algorithm, duplicate_values)
5133 : {
5134 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5135 : {
5136 : public:
5137 : std::vector<std::string> m_strListDuplicatesAllowed{};
5138 : std::vector<std::string> m_strList{};
5139 : std::vector<int> m_intList{};
5140 : std::vector<double> m_doubleList{};
5141 : std::vector<GDALArgDatasetValue> m_datasetList{};
5142 :
5143 14 : MyAlgorithm()
5144 14 : {
5145 : AddArg("strListDuplicatesAllowed", 0, "strListDuplicatesAllowed",
5146 14 : &m_strListDuplicatesAllowed);
5147 28 : AddArg("strList", 0, "strList", &m_strList)
5148 14 : .SetDuplicateValuesAllowed(false);
5149 28 : AddArg("intList", 0, "intList", &m_intList)
5150 14 : .SetDuplicateValuesAllowed(false);
5151 28 : AddArg("doubleList", 0, "doubleList", &m_doubleList)
5152 14 : .SetDuplicateValuesAllowed(false);
5153 28 : AddArg("datasetListNoAutoOpen", 0, "datasetList", &m_datasetList)
5154 14 : .SetAutoOpenDataset(false)
5155 14 : .SetDuplicateValuesAllowed(false);
5156 28 : AddArg("datasetListAutoOpen", 0, "datasetList", &m_datasetList)
5157 14 : .SetDuplicateValuesAllowed(false);
5158 14 : }
5159 : };
5160 :
5161 : {
5162 2 : MyAlgorithm alg;
5163 3 : EXPECT_TRUE(alg.ParseCommandLineArguments(
5164 : {"--strListDuplicatesAllowed=foo,bar,foo"}));
5165 : }
5166 :
5167 : {
5168 2 : MyAlgorithm alg;
5169 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--strList=foo,bar"}));
5170 : }
5171 :
5172 : {
5173 2 : MyAlgorithm alg;
5174 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5175 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--strList=foo,bar,foo"}));
5176 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5177 : "'strList' must be a list of unique values.");
5178 : }
5179 :
5180 : {
5181 2 : MyAlgorithm alg;
5182 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--intList=2,1"}));
5183 : }
5184 :
5185 : {
5186 2 : MyAlgorithm alg;
5187 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5188 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--intList=1,2,1"}));
5189 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5190 : "'intList' must be a list of unique values.");
5191 : }
5192 :
5193 : {
5194 2 : MyAlgorithm alg;
5195 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--doubleList=2,nan,1"}));
5196 : }
5197 :
5198 : {
5199 2 : MyAlgorithm alg;
5200 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5201 3 : EXPECT_FALSE(
5202 : alg.ParseCommandLineArguments({"--doubleList=nan,2,nan,1"}));
5203 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5204 : "'doubleList' must be a list of unique values.");
5205 : }
5206 :
5207 : {
5208 2 : MyAlgorithm alg;
5209 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5210 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--doubleList=1,2,1"}));
5211 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5212 : "'doubleList' must be a list of unique values.");
5213 : }
5214 :
5215 : {
5216 2 : MyAlgorithm alg;
5217 3 : EXPECT_TRUE(
5218 : alg.ParseCommandLineArguments({"--datasetListNoAutoOpen=foo,bar"}));
5219 : }
5220 :
5221 : {
5222 2 : MyAlgorithm alg;
5223 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5224 3 : EXPECT_FALSE(alg.ParseCommandLineArguments(
5225 : {"--datasetListNoAutoOpen=foo,bar,foo"}));
5226 1 : EXPECT_STREQ(
5227 : CPLGetLastErrorMsg(),
5228 : "'datasetListNoAutoOpen' must be a list of unique values.");
5229 : }
5230 :
5231 : {
5232 : auto poDS1 = std::unique_ptr<GDALDataset>(
5233 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
5234 2 : "", 1, 1, 1, GDT_Byte, nullptr));
5235 : auto poDS2 = std::unique_ptr<GDALDataset>(
5236 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
5237 2 : "", 1, 1, 1, GDT_Byte, nullptr));
5238 2 : std::vector<GDALArgDatasetValue> values;
5239 1 : values.emplace_back(poDS1.get());
5240 1 : values.emplace_back(poDS2.get());
5241 2 : MyAlgorithm alg;
5242 1 : alg.GetArg("datasetListAutoOpen")->Set(std::move(values));
5243 1 : EXPECT_TRUE(alg.ValidateArguments());
5244 : }
5245 :
5246 : {
5247 : auto poDS1 = std::unique_ptr<GDALDataset>(
5248 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
5249 2 : "", 1, 1, 1, GDT_Byte, nullptr));
5250 2 : std::vector<GDALArgDatasetValue> values;
5251 1 : values.emplace_back(poDS1.get());
5252 1 : values.emplace_back(poDS1.get());
5253 2 : MyAlgorithm alg;
5254 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5255 1 : alg.GetArg("datasetListAutoOpen")->Set(std::move(values));
5256 1 : EXPECT_FALSE(alg.ValidateArguments());
5257 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5258 : "'datasetListAutoOpen' must be a list of unique values.");
5259 : }
5260 :
5261 : {
5262 : auto poDS1 = std::unique_ptr<GDALDataset>(
5263 2 : GDALDataset::Open(GCORE_DATA_DIR "byte.tif"));
5264 : auto poDS2 = std::unique_ptr<GDALDataset>(
5265 2 : GDALDataset::Open(GCORE_DATA_DIR "uint16.tif"));
5266 2 : std::vector<GDALArgDatasetValue> values;
5267 1 : values.emplace_back(poDS1.get());
5268 1 : values.emplace_back(poDS2.get());
5269 2 : MyAlgorithm alg;
5270 1 : alg.GetArg("datasetListAutoOpen")->Set(std::move(values));
5271 1 : EXPECT_TRUE(alg.ValidateArguments());
5272 : }
5273 :
5274 : {
5275 : auto poDS1 = std::unique_ptr<GDALDataset>(
5276 2 : GDALDataset::Open(GCORE_DATA_DIR "byte.tif"));
5277 : auto poDS2 = std::unique_ptr<GDALDataset>(
5278 2 : GDALDataset::Open(GCORE_DATA_DIR "byte.tif"));
5279 2 : std::vector<GDALArgDatasetValue> values;
5280 1 : values.emplace_back(poDS1.get());
5281 1 : values.emplace_back(poDS2.get());
5282 2 : MyAlgorithm alg;
5283 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5284 1 : alg.GetArg("datasetListAutoOpen")->Set(std::move(values));
5285 1 : EXPECT_FALSE(alg.ValidateArguments());
5286 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
5287 : "'datasetListAutoOpen' must be a list of unique values.");
5288 : }
5289 1 : }
5290 :
5291 4 : TEST_F(test_gdal_algorithm, list_single_valued_dataset_followed_by_positional)
5292 : {
5293 : // Scenario of https://github.com/OSGeo/gdal/issues/14358
5294 :
5295 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5296 : {
5297 : public:
5298 : std::vector<GDALArgDatasetValue> m_inputDataset{};
5299 : GDALArgDatasetValue m_outputDataset{};
5300 :
5301 6 : MyAlgorithm()
5302 6 : {
5303 6 : AddInputDatasetArg(&m_inputDataset, GDAL_OF_RASTER, true)
5304 6 : .SetMaxCount(1)
5305 6 : .SetAutoOpenDataset(false);
5306 6 : AddOutputDatasetArg(&m_outputDataset, GDAL_OF_RASTER, true)
5307 6 : .SetDatasetInputFlags(GADV_NAME | GADV_OBJECT);
5308 6 : }
5309 : };
5310 :
5311 : {
5312 2 : MyAlgorithm alg;
5313 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
5314 : {"--input", "/vsimem/in.tif", "/vsimem/out.tif"}));
5315 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5316 : }
5317 :
5318 : {
5319 2 : MyAlgorithm alg;
5320 4 : EXPECT_TRUE(alg.ParseCommandLineArguments(
5321 : {"--input=/vsimem/in.tif", "/vsimem/out.tif"}));
5322 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5323 : }
5324 :
5325 : {
5326 2 : MyAlgorithm alg;
5327 4 : EXPECT_TRUE(alg.ParseCommandLineArguments(
5328 : {"/vsimem/in.tif", "/vsimem/out.tif"}));
5329 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5330 : }
5331 :
5332 : {
5333 2 : MyAlgorithm alg;
5334 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
5335 : {"--input", "/vsimem/in.tif", "--output", "/vsimem/out.tif"}));
5336 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5337 : }
5338 :
5339 : {
5340 2 : MyAlgorithm alg;
5341 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5342 7 : EXPECT_FALSE(alg.ParseCommandLineArguments(
5343 : {"--input", "/vsimem/in.tif", "--input", "not_expected.tif",
5344 : "/vsimem/out.tif"}));
5345 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5346 : }
5347 :
5348 : {
5349 2 : MyAlgorithm alg;
5350 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5351 4 : EXPECT_FALSE(
5352 : alg.ParseCommandLineArguments({"--input", "/vsimem/in.tif"}));
5353 1 : EXPECT_EQ(alg.m_inputDataset.size(), 1U);
5354 : }
5355 1 : }
5356 :
5357 4 : TEST_F(test_gdal_algorithm, list_single_valued_str_followed_by_positional)
5358 : {
5359 : // Scenario of https://github.com/OSGeo/gdal/issues/14358
5360 :
5361 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5362 : {
5363 : public:
5364 : std::vector<std::string> m_a{};
5365 : int m_b{};
5366 :
5367 2 : MyAlgorithm()
5368 2 : {
5369 4 : AddArg("a", 0, "a", &m_a)
5370 2 : .SetPositional()
5371 2 : .SetRequired()
5372 2 : .SetMaxCount(1);
5373 2 : AddArg("b", 0, "b", &m_b).SetPositional().SetRequired();
5374 2 : }
5375 : };
5376 :
5377 : {
5378 2 : MyAlgorithm alg;
5379 5 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--a", "str", "1"}));
5380 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5381 : }
5382 :
5383 : {
5384 2 : MyAlgorithm alg;
5385 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"str", "1"}));
5386 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5387 : }
5388 1 : }
5389 :
5390 4 : TEST_F(test_gdal_algorithm, list_single_valued_int_followed_by_positional)
5391 : {
5392 : // Scenario of https://github.com/OSGeo/gdal/issues/14358
5393 :
5394 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5395 : {
5396 : public:
5397 : std::vector<int> m_a{};
5398 : int m_b{};
5399 :
5400 2 : MyAlgorithm()
5401 2 : {
5402 4 : AddArg("a", 0, "a", &m_a)
5403 2 : .SetPositional()
5404 2 : .SetRequired()
5405 2 : .SetMaxCount(1);
5406 2 : AddArg("b", 0, "b", &m_b).SetPositional().SetRequired();
5407 2 : }
5408 : };
5409 :
5410 : {
5411 2 : MyAlgorithm alg;
5412 5 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--a", "2", "1"}));
5413 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5414 : }
5415 :
5416 : {
5417 2 : MyAlgorithm alg;
5418 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"2", "1"}));
5419 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5420 : }
5421 1 : }
5422 :
5423 4 : TEST_F(test_gdal_algorithm, list_single_valued_real_followed_by_positional)
5424 : {
5425 : // Scenario of https://github.com/OSGeo/gdal/issues/14358
5426 :
5427 : class MyAlgorithm : public MyAlgorithmWithDummyRun
5428 : {
5429 : public:
5430 : std::vector<double> m_a{};
5431 : int m_b{};
5432 :
5433 2 : MyAlgorithm()
5434 2 : {
5435 4 : AddArg("a", 0, "a", &m_a)
5436 2 : .SetPositional()
5437 2 : .SetRequired()
5438 2 : .SetMaxCount(1);
5439 2 : AddArg("b", 0, "b", &m_b).SetPositional().SetRequired();
5440 2 : }
5441 : };
5442 :
5443 : {
5444 2 : MyAlgorithm alg;
5445 5 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--a", "2.5", "1"}));
5446 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5447 : }
5448 :
5449 : {
5450 2 : MyAlgorithm alg;
5451 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"2.5", "1"}));
5452 1 : EXPECT_EQ(alg.m_a.size(), 1U);
5453 : }
5454 1 : }
5455 :
5456 : } // namespace test_gdal_algorithm
5457 :
5458 : #if defined(__clang__)
5459 : #pragma clang diagnostic pop
5460 : #endif
|