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 "gdalalgorithm.h"
18 : #include "gdal_priv.h"
19 :
20 : #include "test_data.h"
21 :
22 : #include "gtest_include.h"
23 :
24 : #include <limits>
25 :
26 : namespace test_gdal_algorithm
27 : {
28 :
29 : struct test_gdal_algorithm : public ::testing::Test
30 : {
31 : };
32 :
33 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgTypeName)
34 : {
35 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_BOOLEAN), "boolean");
36 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_STRING), "string");
37 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_INTEGER), "integer");
38 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_REAL), "real");
39 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_DATASET), "dataset");
40 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_STRING_LIST), "string_list");
41 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_INTEGER_LIST), "integer_list");
42 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_REAL_LIST), "real_list");
43 1 : EXPECT_STREQ(GDALAlgorithmArgTypeName(GAAT_DATASET_LIST), "dataset_list");
44 1 : }
45 :
46 4 : TEST_F(test_gdal_algorithm, GDALArgDatasetValueTypeName)
47 : {
48 2 : EXPECT_STREQ(GDALArgDatasetValueTypeName(GDAL_OF_RASTER).c_str(), "raster");
49 2 : EXPECT_STREQ(GDALArgDatasetValueTypeName(GDAL_OF_VECTOR).c_str(), "vector");
50 2 : EXPECT_STREQ(GDALArgDatasetValueTypeName(GDAL_OF_MULTIDIM_RASTER).c_str(),
51 : "multidimensional raster");
52 2 : EXPECT_STREQ(
53 : GDALArgDatasetValueTypeName(GDAL_OF_RASTER | GDAL_OF_VECTOR).c_str(),
54 : "raster or vector");
55 2 : EXPECT_STREQ(
56 : GDALArgDatasetValueTypeName(GDAL_OF_RASTER | GDAL_OF_MULTIDIM_RASTER)
57 : .c_str(),
58 : "raster or multidimensional raster");
59 2 : EXPECT_STREQ(GDALArgDatasetValueTypeName(GDAL_OF_RASTER | GDAL_OF_VECTOR |
60 : GDAL_OF_MULTIDIM_RASTER)
61 : .c_str(),
62 : "raster, vector or multidimensional raster");
63 2 : EXPECT_STREQ(
64 : GDALArgDatasetValueTypeName(GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER)
65 : .c_str(),
66 : "vector or multidimensional raster");
67 1 : }
68 :
69 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgDecl_SetMinCount)
70 : {
71 : {
72 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
73 1 : CPLErrorReset();
74 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN)
75 : .SetMinCount(2)
76 : .GetMinCount(),
77 : 0);
78 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
79 : }
80 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST)
81 : .SetMinCount(2)
82 : .GetMinCount(),
83 : 2);
84 1 : }
85 :
86 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArgDecl_SetMaxCount)
87 : {
88 : {
89 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
90 1 : CPLErrorReset();
91 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN)
92 : .SetMaxCount(2)
93 : .GetMaxCount(),
94 : 1);
95 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
96 : }
97 1 : EXPECT_EQ(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST)
98 : .SetMaxCount(2)
99 : .GetMaxCount(),
100 : 2);
101 1 : }
102 :
103 4 : TEST_F(test_gdal_algorithm, GDALAlgorithmArg_Set)
104 : {
105 : {
106 1 : bool val = false;
107 : auto arg = GDALAlgorithmArg(
108 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN), &val);
109 1 : arg.Set(true);
110 1 : EXPECT_EQ(arg.Get<bool>(), true);
111 1 : EXPECT_EQ(val, true);
112 :
113 : {
114 1 : bool val2 = false;
115 : auto arg2 = GDALAlgorithmArg(
116 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_BOOLEAN), &val2);
117 1 : arg2.SetFrom(arg);
118 1 : EXPECT_EQ(arg2.Get<bool>(), true);
119 : }
120 :
121 1 : arg.Set(false);
122 :
123 : {
124 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
125 1 : CPLErrorReset();
126 1 : arg.Set(1);
127 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
128 1 : EXPECT_EQ(arg.Get<bool>(), false);
129 :
130 1 : CPLErrorReset();
131 1 : arg.Set(1.5);
132 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
133 1 : EXPECT_EQ(arg.Get<bool>(), false);
134 :
135 1 : CPLErrorReset();
136 1 : arg.Set("foo");
137 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
138 1 : EXPECT_EQ(arg.Get<bool>(), false);
139 :
140 1 : CPLErrorReset();
141 1 : arg.Set(std::vector<std::string>());
142 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
143 1 : EXPECT_EQ(arg.Get<bool>(), false);
144 :
145 1 : CPLErrorReset();
146 1 : arg.Set(std::vector<int>());
147 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
148 1 : EXPECT_EQ(arg.Get<bool>(), false);
149 :
150 1 : CPLErrorReset();
151 1 : arg.Set(std::vector<double>());
152 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
153 1 : EXPECT_EQ(arg.Get<bool>(), false);
154 :
155 1 : CPLErrorReset();
156 1 : arg.Set(std::vector<GDALArgDatasetValue>());
157 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
158 1 : EXPECT_EQ(arg.Get<bool>(), false);
159 :
160 : auto poDS = std::unique_ptr<GDALDataset>(
161 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
162 2 : "", 1, 1, 1, GDT_Byte, nullptr));
163 1 : CPLErrorReset();
164 1 : arg.Set(std::move(poDS));
165 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
166 1 : EXPECT_EQ(arg.Get<bool>(), false);
167 : }
168 :
169 : {
170 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
171 1 : CPLErrorReset();
172 1 : int val2 = 1;
173 : auto arg2 = GDALAlgorithmArg(
174 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val2);
175 1 : arg2.SetFrom(arg);
176 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
177 1 : EXPECT_EQ(val2, 1);
178 : }
179 : }
180 : {
181 1 : int val = 0;
182 : auto arg = GDALAlgorithmArg(
183 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
184 1 : arg.Set(1);
185 1 : EXPECT_EQ(arg.Get<int>(), 1);
186 1 : EXPECT_EQ(val, 1);
187 :
188 1 : int val2 = 0;
189 : auto arg2 = GDALAlgorithmArg(
190 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val2);
191 1 : arg2.SetFrom(arg);
192 1 : EXPECT_EQ(arg2.Get<int>(), 1);
193 :
194 1 : arg.Set(0);
195 : {
196 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
197 1 : CPLErrorReset();
198 1 : arg.Set(true);
199 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
200 1 : EXPECT_EQ(arg.Get<int>(), 0);
201 : }
202 : }
203 : {
204 1 : double val = 0;
205 : auto arg = GDALAlgorithmArg(
206 2 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL).SetDefault(-1), &val);
207 1 : arg.Set(1.5);
208 1 : EXPECT_EQ(arg.Get<double>(), 1.5);
209 1 : EXPECT_EQ(val, 1.5);
210 1 : arg.Set(1);
211 1 : EXPECT_EQ(arg.Get<double>(), 1);
212 :
213 1 : double val2 = 0;
214 : auto arg2 = GDALAlgorithmArg(
215 2 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL).SetDefault(-1.5), &val2);
216 1 : arg2.SetFrom(arg);
217 1 : EXPECT_EQ(arg2.Get<double>(), 1);
218 :
219 1 : arg.Set(0);
220 : {
221 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
222 1 : CPLErrorReset();
223 1 : arg.Set(true);
224 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
225 1 : EXPECT_EQ(arg.Get<double>(), 0);
226 : }
227 : }
228 : {
229 2 : std::string val;
230 : auto arg = GDALAlgorithmArg(
231 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING), &val);
232 1 : arg.Set("foo");
233 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
234 1 : EXPECT_STREQ(val.c_str(), "foo");
235 :
236 2 : std::string val2;
237 : auto arg2 = GDALAlgorithmArg(
238 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING), &val2);
239 1 : arg2.SetFrom(arg);
240 1 : EXPECT_STREQ(arg2.Get<std::string>().c_str(), "foo");
241 :
242 : {
243 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
244 1 : CPLErrorReset();
245 1 : arg.Set(true);
246 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
247 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
248 : }
249 : {
250 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
251 1 : CPLErrorReset();
252 1 : arg.Set(static_cast<GDALDataset *>(nullptr));
253 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
254 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
255 : }
256 : {
257 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
258 1 : CPLErrorReset();
259 1 : arg.SetDatasetName("bar");
260 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
261 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
262 : }
263 : {
264 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
265 1 : CPLErrorReset();
266 2 : GDALArgDatasetValue dsValue;
267 1 : arg.SetFrom(dsValue);
268 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
269 1 : EXPECT_STREQ(arg.Get<std::string>().c_str(), "foo");
270 : }
271 : }
272 : {
273 2 : std::string val;
274 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
275 1 : .SetReadFromFileAtSyntaxAllowed()
276 1 : .SetRemoveSQLCommentsEnabled(),
277 2 : &val);
278 1 : EXPECT_TRUE(arg.Set("foo"));
279 1 : EXPECT_STREQ(val.c_str(), "foo");
280 : }
281 : {
282 2 : std::string osTmpFilename = VSIMemGenerateHiddenFilename("temp.sql");
283 1 : VSILFILE *fpTmp = VSIFOpenL(osTmpFilename.c_str(), "wb");
284 1 : VSIFPrintfL(fpTmp, "\xEF\xBB\xBF"); // UTF-8 BOM
285 1 : VSIFPrintfL(fpTmp, "-- this is a comment\n");
286 1 : VSIFPrintfL(fpTmp, "value");
287 1 : VSIFCloseL(fpTmp);
288 :
289 2 : std::string val;
290 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
291 1 : .SetReadFromFileAtSyntaxAllowed()
292 1 : .SetRemoveSQLCommentsEnabled(),
293 2 : &val);
294 1 : EXPECT_TRUE(arg.Set(("@" + osTmpFilename).c_str()));
295 1 : EXPECT_STREQ(val.c_str(), "value");
296 1 : VSIUnlink(osTmpFilename.c_str());
297 : }
298 : {
299 2 : std::string val;
300 2 : auto arg = GDALAlgorithmArg(GDALAlgorithmArgDecl("", 0, "", GAAT_STRING)
301 1 : .SetReadFromFileAtSyntaxAllowed(),
302 2 : &val);
303 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
304 1 : EXPECT_FALSE(arg.Set("@i_do_not_exist"));
305 : }
306 : {
307 : auto poMEMDS = std::unique_ptr<GDALDataset>(
308 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
309 2 : "", 1, 1, 1, GDT_Byte, nullptr));
310 2 : GDALArgDatasetValue val;
311 : auto arg = GDALAlgorithmArg(
312 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET), &val);
313 1 : auto poMEMDSRaw = poMEMDS.get();
314 :
315 1 : arg.Set(poMEMDS.release());
316 1 : EXPECT_EQ(val.GetDatasetRef(), poMEMDSRaw);
317 :
318 1 : poMEMDS.reset(val.BorrowDataset());
319 1 : EXPECT_EQ(poMEMDS.get(), poMEMDSRaw);
320 1 : EXPECT_EQ(val.GetDatasetRef(), nullptr);
321 :
322 1 : EXPECT_TRUE(arg.Set(std::move(poMEMDS)));
323 1 : EXPECT_EQ(val.GetDatasetRef(), poMEMDSRaw);
324 :
325 1 : poMEMDSRaw->ReleaseRef();
326 :
327 1 : arg.SetDatasetName("foo");
328 1 : EXPECT_STREQ(val.GetName().c_str(), "foo");
329 :
330 2 : GDALArgDatasetValue val2;
331 1 : val2.Set("bar");
332 1 : arg.SetFrom(val2);
333 1 : EXPECT_STREQ(val.GetName().c_str(), "bar");
334 :
335 : auto arg2 = GDALAlgorithmArg(
336 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET), &val2);
337 1 : val2.Set("baz");
338 1 : arg.SetFrom(arg2);
339 1 : EXPECT_STREQ(val.GetName().c_str(), "baz");
340 :
341 1 : val.SetInputFlags(GADV_NAME);
342 1 : val.SetOutputFlags(GADV_OBJECT);
343 : {
344 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
345 1 : CPLErrorReset();
346 1 : arg.Set(static_cast<GDALDataset *>(nullptr));
347 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
348 : }
349 : }
350 : {
351 2 : std::vector<std::string> val;
352 : auto arg = GDALAlgorithmArg(
353 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val);
354 6 : const std::vector<std::string> expected{"foo", "bar"};
355 1 : arg.Set(expected);
356 1 : EXPECT_EQ(arg.Get<std::vector<std::string>>(), expected);
357 1 : EXPECT_EQ(val, expected);
358 :
359 2 : std::vector<std::string> val2;
360 : auto arg2 = GDALAlgorithmArg(
361 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val2);
362 1 : arg2.SetFrom(arg);
363 1 : EXPECT_EQ(arg2.Get<std::vector<std::string>>(), expected);
364 :
365 : {
366 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
367 1 : CPLErrorReset();
368 1 : arg.Set(true);
369 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
370 1 : EXPECT_EQ(arg.Get<std::vector<std::string>>(), expected);
371 : }
372 : }
373 : {
374 2 : std::vector<int> val;
375 : auto arg = GDALAlgorithmArg(
376 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val);
377 2 : const std::vector<int> expected{1, 2};
378 1 : arg.Set(expected);
379 1 : EXPECT_EQ(arg.Get<std::vector<int>>(), expected);
380 1 : EXPECT_EQ(val, expected);
381 :
382 2 : std::vector<int> val2;
383 : auto arg2 = GDALAlgorithmArg(
384 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val2);
385 1 : arg2.SetFrom(arg);
386 1 : EXPECT_EQ(arg2.Get<std::vector<int>>(), expected);
387 :
388 : {
389 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
390 1 : CPLErrorReset();
391 1 : arg.Set(true);
392 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
393 1 : EXPECT_EQ(arg.Get<std::vector<int>>(), expected);
394 : }
395 : }
396 : {
397 2 : std::vector<double> val;
398 : auto arg = GDALAlgorithmArg(
399 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val);
400 2 : const std::vector<double> expected{1.5, 2.5};
401 1 : arg.Set(expected);
402 1 : EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
403 1 : EXPECT_EQ(val, expected);
404 :
405 2 : std::vector<double> val2;
406 : auto arg2 = GDALAlgorithmArg(
407 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val2);
408 1 : arg2.SetFrom(arg);
409 1 : EXPECT_EQ(arg2.Get<std::vector<double>>(), expected);
410 :
411 : {
412 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
413 1 : CPLErrorReset();
414 1 : arg.Set(true);
415 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
416 1 : EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
417 : }
418 : }
419 : {
420 2 : std::vector<GDALArgDatasetValue> val;
421 : auto arg = GDALAlgorithmArg(
422 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val);
423 : {
424 2 : std::vector<GDALArgDatasetValue> val2;
425 1 : val2.emplace_back(GDALArgDatasetValue("foo"));
426 1 : val2.emplace_back(GDALArgDatasetValue("bar"));
427 1 : arg.Set(std::move(val2));
428 1 : EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
429 1 : EXPECT_EQ(val.size(), 2U);
430 : }
431 :
432 2 : std::vector<GDALArgDatasetValue> val2;
433 : auto arg2 = GDALAlgorithmArg(
434 3 : GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val2);
435 1 : arg2.SetFrom(arg);
436 1 : EXPECT_EQ(arg2.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
437 :
438 : {
439 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
440 1 : CPLErrorReset();
441 1 : arg.Set(true);
442 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
443 1 : EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
444 : }
445 : }
446 1 : }
447 :
448 4 : TEST_F(test_gdal_algorithm, RunValidationActions)
449 : {
450 1 : int val = 0;
451 : auto arg = GDALInConstructionAlgorithmArg(
452 3 : nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
453 3 : arg.AddValidationAction([&arg]() { return arg.Get<int>() == 1; });
454 1 : EXPECT_TRUE(arg.Set(1));
455 1 : EXPECT_FALSE(arg.Set(2));
456 1 : }
457 :
458 4 : TEST_F(test_gdal_algorithm, SetIsCRSArg_wrong_type)
459 : {
460 1 : int val = 0;
461 : auto arg = GDALInConstructionAlgorithmArg(
462 3 : nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
463 : {
464 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
465 1 : CPLErrorReset();
466 1 : arg.SetIsCRSArg();
467 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
468 : }
469 1 : }
470 :
471 : class MyAlgorithmWithDummyRun : public GDALAlgorithm
472 : {
473 : public:
474 142 : MyAlgorithmWithDummyRun(const std::string &name = "test",
475 : const std::string &description = "",
476 : const std::string &url = "https://example.com")
477 142 : : GDALAlgorithm(name, description, url)
478 : {
479 142 : }
480 :
481 1 : bool RunImpl(GDALProgressFunc, void *) override
482 : {
483 1 : return true;
484 : }
485 : };
486 :
487 4 : TEST_F(test_gdal_algorithm, wrong_long_name_dash)
488 : {
489 : class MyAlgorithm : public MyAlgorithmWithDummyRun
490 : {
491 : public:
492 : bool m_flag = false;
493 :
494 1 : MyAlgorithm()
495 1 : {
496 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
497 1 : CPLErrorReset();
498 1 : AddArg("-", 0, "", &m_flag);
499 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
500 1 : }
501 : };
502 :
503 1 : MyAlgorithm alg;
504 1 : CPL_IGNORE_RET_VAL(alg.Run());
505 1 : }
506 :
507 4 : TEST_F(test_gdal_algorithm, wrong_long_name_contains_equal)
508 : {
509 : class MyAlgorithm : public MyAlgorithmWithDummyRun
510 : {
511 : public:
512 : bool m_flag = false;
513 :
514 1 : MyAlgorithm()
515 1 : {
516 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
517 1 : CPLErrorReset();
518 1 : AddArg("foo=bar", 0, "", &m_flag);
519 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
520 1 : }
521 : };
522 :
523 1 : MyAlgorithm alg;
524 1 : }
525 :
526 4 : TEST_F(test_gdal_algorithm, long_name_duplicated)
527 : {
528 : class MyAlgorithm : public MyAlgorithmWithDummyRun
529 : {
530 : public:
531 : bool m_flag = false;
532 :
533 1 : MyAlgorithm()
534 1 : {
535 1 : AddArg("foo", 0, "", &m_flag);
536 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
537 1 : CPLErrorReset();
538 1 : AddArg("foo", 0, "", &m_flag);
539 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
540 1 : }
541 : };
542 :
543 1 : MyAlgorithm alg;
544 1 : }
545 :
546 4 : TEST_F(test_gdal_algorithm, wrong_short_name)
547 : {
548 : class MyAlgorithm : public MyAlgorithmWithDummyRun
549 : {
550 : public:
551 : bool m_flag = false;
552 :
553 1 : MyAlgorithm()
554 1 : {
555 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
556 1 : CPLErrorReset();
557 1 : AddArg("", '-', "", &m_flag);
558 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
559 1 : }
560 : };
561 :
562 1 : MyAlgorithm alg;
563 1 : }
564 :
565 4 : TEST_F(test_gdal_algorithm, short_name_duplicated)
566 : {
567 : class MyAlgorithm : public MyAlgorithmWithDummyRun
568 : {
569 : public:
570 : bool m_flag = false;
571 :
572 1 : MyAlgorithm()
573 1 : {
574 1 : AddArg("", 'x', "", &m_flag);
575 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
576 1 : CPLErrorReset();
577 1 : AddArg("", 'x', "", &m_flag);
578 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
579 1 : }
580 : };
581 :
582 1 : MyAlgorithm alg;
583 1 : }
584 :
585 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias)
586 : {
587 : class MyAlgorithm : public MyAlgorithmWithDummyRun
588 : {
589 : public:
590 : bool m_flag = false;
591 :
592 1 : MyAlgorithm()
593 1 : {
594 1 : AddArg("flag", 'f', "boolean flag", &m_flag).AddAlias("alias");
595 1 : }
596 : };
597 :
598 2 : MyAlgorithm alg;
599 1 : alg.GetUsageForCLI(false);
600 1 : EXPECT_NE(alg.GetArg("flag"), nullptr);
601 1 : EXPECT_NE(alg.GetArg("--flag"), nullptr);
602 1 : EXPECT_NE(alg.GetArg("-f"), nullptr);
603 1 : EXPECT_NE(alg.GetArg("f"), nullptr);
604 1 : EXPECT_NE(alg.GetArg("alias"), nullptr);
605 1 : EXPECT_EQ(alg.GetArg("invalid"), nullptr);
606 1 : EXPECT_EQ(alg.GetArg("-"), nullptr);
607 1 : }
608 :
609 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias_redundant)
610 : {
611 : class MyAlgorithm : public MyAlgorithmWithDummyRun
612 : {
613 : public:
614 : bool m_flag = false;
615 : bool m_flag2 = false;
616 :
617 1 : MyAlgorithm()
618 1 : {
619 1 : AddArg("flag", 'F', "boolean flag", &m_flag).AddAlias("alias");
620 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
621 1 : CPLErrorReset();
622 1 : AddArg("flag2", '9', "boolean flag2", &m_flag2).AddAlias("alias");
623 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
624 1 : }
625 : };
626 :
627 2 : MyAlgorithm alg;
628 1 : EXPECT_NE(alg.GetArg("alias"), nullptr);
629 1 : }
630 :
631 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddHiddenAlias)
632 : {
633 : class MyAlgorithm : public MyAlgorithmWithDummyRun
634 : {
635 : public:
636 : bool m_flag = false;
637 :
638 1 : MyAlgorithm()
639 1 : {
640 2 : AddArg("flag", 'f', "boolean flag", &m_flag)
641 1 : .AddHiddenAlias("hidden_alias");
642 1 : }
643 : };
644 :
645 2 : MyAlgorithm alg;
646 1 : EXPECT_NE(alg.GetArg("hidden_alias"), nullptr);
647 1 : }
648 :
649 4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_SetPositional)
650 : {
651 : class MyAlgorithm : public MyAlgorithmWithDummyRun
652 : {
653 : public:
654 : int m_val = 0;
655 :
656 1 : MyAlgorithm()
657 1 : {
658 1 : AddArg("option", 0, "option", &m_val).SetPositional();
659 1 : }
660 : };
661 :
662 2 : MyAlgorithm alg;
663 1 : EXPECT_TRUE(alg.GetArg("option")->IsPositional());
664 1 : }
665 :
666 4 : TEST_F(test_gdal_algorithm, GDALArgDatasetValue)
667 : {
668 : {
669 : auto poDS = std::unique_ptr<GDALDataset>(
670 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
671 2 : "", 1, 1, 1, GDT_Byte, nullptr));
672 1 : auto poDSRaw = poDS.get();
673 2 : GDALArgDatasetValue value(poDS.release());
674 1 : EXPECT_EQ(value.GetDatasetRef(), poDSRaw);
675 1 : EXPECT_STREQ(value.GetName().c_str(), poDSRaw->GetDescription());
676 :
677 2 : GDALArgDatasetValue value2;
678 1 : value2 = std::move(value);
679 1 : EXPECT_STREQ(value2.GetName().c_str(), poDSRaw->GetDescription());
680 :
681 1 : poDSRaw->ReleaseRef();
682 : }
683 : {
684 3 : GDALArgDatasetValue value("foo");
685 1 : EXPECT_STREQ(value.GetName().c_str(), "foo");
686 :
687 2 : GDALArgDatasetValue value2;
688 1 : value2 = std::move(value);
689 1 : EXPECT_STREQ(value2.GetName().c_str(), "foo");
690 : }
691 1 : }
692 :
693 4 : TEST_F(test_gdal_algorithm, bool_flag)
694 : {
695 : class MyAlgorithm : public MyAlgorithmWithDummyRun
696 : {
697 : public:
698 : bool m_flag = false;
699 :
700 10 : MyAlgorithm()
701 10 : {
702 10 : AddArg("flag", 'f', "boolean flag", &m_flag);
703 10 : }
704 : };
705 :
706 : {
707 2 : MyAlgorithm alg;
708 1 : alg.GetUsageForCLI(true);
709 1 : alg.GetUsageForCLI(false);
710 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
711 1 : EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "test");
712 : }
713 :
714 : {
715 2 : MyAlgorithm alg;
716 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
717 1 : EXPECT_TRUE(alg.m_flag);
718 : }
719 :
720 : {
721 2 : MyAlgorithm alg;
722 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=true"}));
723 1 : EXPECT_TRUE(alg.m_flag);
724 : }
725 :
726 : {
727 2 : MyAlgorithm alg;
728 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=false"}));
729 1 : EXPECT_FALSE(alg.m_flag);
730 : }
731 :
732 : {
733 2 : MyAlgorithm alg;
734 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
735 1 : CPLErrorReset();
736 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag=invalid"}));
737 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
738 : }
739 :
740 : {
741 2 : MyAlgorithm alg;
742 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
743 1 : CPLErrorReset();
744 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag", "--flag"}));
745 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
746 : }
747 :
748 : {
749 2 : MyAlgorithm alg;
750 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
751 1 : CPLErrorReset();
752 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--invalid"}));
753 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
754 : }
755 :
756 : {
757 2 : MyAlgorithm alg;
758 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
759 1 : CPLErrorReset();
760 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-"}));
761 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
762 : }
763 :
764 : {
765 2 : MyAlgorithm alg;
766 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
767 1 : CPLErrorReset();
768 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-x"}));
769 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
770 : }
771 :
772 : {
773 2 : MyAlgorithm alg;
774 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
775 1 : CPLErrorReset();
776 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"-xy"}));
777 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
778 : }
779 1 : }
780 :
781 4 : TEST_F(test_gdal_algorithm, int)
782 : {
783 : class MyAlgorithm : public MyAlgorithmWithDummyRun
784 : {
785 : public:
786 : int m_val = 0;
787 :
788 5 : MyAlgorithm()
789 5 : {
790 5 : AddArg("val", 0, "", &m_val);
791 5 : }
792 : };
793 :
794 : {
795 2 : MyAlgorithm alg;
796 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
797 1 : EXPECT_EQ(alg.m_val, 5);
798 : }
799 :
800 : {
801 2 : MyAlgorithm alg;
802 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
803 1 : CPLErrorReset();
804 : // Missing value
805 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val"}));
806 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
807 1 : EXPECT_EQ(alg.m_val, 0);
808 : }
809 :
810 : {
811 2 : MyAlgorithm alg;
812 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
813 1 : CPLErrorReset();
814 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
815 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
816 1 : EXPECT_EQ(alg.m_val, 0);
817 : }
818 :
819 : {
820 2 : MyAlgorithm alg;
821 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
822 1 : CPLErrorReset();
823 3 : EXPECT_FALSE(
824 : alg.ParseCommandLineArguments({"--val=12345679812346798123456"}));
825 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
826 1 : EXPECT_EQ(alg.m_val, 0);
827 : }
828 :
829 : {
830 2 : MyAlgorithm alg;
831 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
832 1 : CPLErrorReset();
833 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1.5"}));
834 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
835 1 : EXPECT_EQ(alg.m_val, 0);
836 : }
837 1 : }
838 :
839 4 : TEST_F(test_gdal_algorithm, SetDisplayInJSONUsage)
840 : {
841 : class MyAlgorithm : public MyAlgorithmWithDummyRun
842 : {
843 : public:
844 1 : MyAlgorithm()
845 1 : {
846 1 : SetDisplayInJSONUsage(false);
847 1 : }
848 : };
849 :
850 : {
851 1 : MyAlgorithm alg;
852 1 : alg.GetUsageForCLI(false);
853 1 : alg.GetUsageAsJSON();
854 : }
855 1 : }
856 :
857 4 : TEST_F(test_gdal_algorithm, int_with_default)
858 : {
859 : class MyAlgorithm : public MyAlgorithmWithDummyRun
860 : {
861 : public:
862 : int m_val = 0;
863 :
864 1 : MyAlgorithm()
865 1 : {
866 1 : AddArg("val", 0, "", &m_val).SetDefault(3);
867 1 : }
868 : };
869 :
870 : {
871 2 : MyAlgorithm alg;
872 1 : alg.GetUsageForCLI(false);
873 1 : alg.GetUsageAsJSON();
874 1 : EXPECT_TRUE(alg.ValidateArguments());
875 1 : EXPECT_EQ(alg.m_val, 3);
876 : }
877 1 : }
878 :
879 4 : TEST_F(test_gdal_algorithm, double)
880 : {
881 : class MyAlgorithm : public MyAlgorithmWithDummyRun
882 : {
883 : public:
884 : double m_val = 0;
885 :
886 2 : MyAlgorithm()
887 2 : {
888 2 : AddArg("val", 0, "", &m_val);
889 2 : }
890 : };
891 :
892 : {
893 2 : MyAlgorithm alg;
894 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5"}));
895 1 : EXPECT_EQ(alg.m_val, 1.5);
896 : }
897 :
898 : {
899 2 : MyAlgorithm alg;
900 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
901 1 : CPLErrorReset();
902 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
903 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
904 1 : EXPECT_EQ(alg.m_val, 0);
905 : }
906 1 : }
907 :
908 4 : TEST_F(test_gdal_algorithm, double_with_default)
909 : {
910 : class MyAlgorithm : public MyAlgorithmWithDummyRun
911 : {
912 : public:
913 : double m_val = 0;
914 :
915 1 : MyAlgorithm()
916 1 : {
917 1 : AddArg("val", 0, "", &m_val).SetDefault(3.5);
918 1 : }
919 : };
920 :
921 : {
922 2 : MyAlgorithm alg;
923 1 : alg.GetUsageForCLI(false);
924 1 : alg.GetUsageAsJSON();
925 1 : EXPECT_TRUE(alg.ValidateArguments());
926 1 : EXPECT_EQ(alg.m_val, 3.5);
927 : }
928 1 : }
929 :
930 4 : TEST_F(test_gdal_algorithm, string_with_default)
931 : {
932 : class MyAlgorithm : public MyAlgorithmWithDummyRun
933 : {
934 : public:
935 : std::string m_val{};
936 :
937 1 : MyAlgorithm()
938 1 : {
939 1 : AddArg("val", 0, "", &m_val).SetDefault("foo");
940 1 : }
941 : };
942 :
943 : {
944 2 : MyAlgorithm alg;
945 1 : alg.GetUsageForCLI(false);
946 1 : alg.GetUsageAsJSON();
947 1 : EXPECT_TRUE(alg.ValidateArguments());
948 1 : EXPECT_STREQ(alg.m_val.c_str(), "foo");
949 : }
950 1 : }
951 :
952 4 : TEST_F(test_gdal_algorithm, dataset)
953 : {
954 : class MyAlgorithm : public MyAlgorithmWithDummyRun
955 : {
956 : public:
957 : GDALArgDatasetValue m_val{};
958 :
959 5 : MyAlgorithm()
960 5 : {
961 5 : AddArg("val", 0, "", &m_val).SetRequired();
962 5 : }
963 : };
964 :
965 : {
966 2 : MyAlgorithm alg;
967 3 : EXPECT_TRUE(alg.ParseCommandLineArguments(
968 : {"--val=" GCORE_DATA_DIR "byte.tif"}));
969 1 : EXPECT_NE(alg.m_val.GetDatasetRef(), nullptr);
970 : }
971 :
972 : {
973 2 : MyAlgorithm alg;
974 : auto poDS = std::unique_ptr<GDALDataset>(
975 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
976 2 : "", 1, 1, 1, GDT_Byte, nullptr));
977 1 : auto poDSRaw = poDS.get();
978 1 : alg.GetArg("val")->Set(poDS.release());
979 1 : EXPECT_EQ(alg.m_val.GetDatasetRef(), poDSRaw);
980 1 : poDSRaw->ReleaseRef();
981 : }
982 :
983 : {
984 2 : MyAlgorithm alg;
985 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
986 1 : CPLErrorReset();
987 3 : EXPECT_FALSE(
988 : alg.ParseCommandLineArguments({"--val=i_do_not_exist.tif"}));
989 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
990 : }
991 :
992 : {
993 2 : MyAlgorithm alg;
994 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
995 1 : CPLErrorReset();
996 1 : EXPECT_FALSE(alg.Run());
997 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
998 : }
999 :
1000 : {
1001 2 : MyAlgorithm alg;
1002 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1003 1 : CPLErrorReset();
1004 2 : GDALArgDatasetValue value;
1005 1 : alg.GetArg("val")->SetFrom(value);
1006 1 : EXPECT_FALSE(alg.Run());
1007 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1008 : }
1009 1 : }
1010 :
1011 4 : TEST_F(test_gdal_algorithm, input_update)
1012 : {
1013 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1014 : {
1015 : public:
1016 : GDALArgDatasetValue m_input{};
1017 : GDALArgDatasetValue m_output{};
1018 : bool m_update = false;
1019 :
1020 1 : MyAlgorithm()
1021 1 : {
1022 1 : AddInputDatasetArg(&m_input);
1023 1 : AddUpdateArg(&m_update);
1024 1 : }
1025 : };
1026 :
1027 : {
1028 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
1029 1 : if (!poDriver)
1030 : {
1031 0 : GTEST_SKIP() << "GPKG support missing";
1032 : }
1033 : else
1034 : {
1035 : std::string osTmpFilename =
1036 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
1037 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
1038 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
1039 1 : poDS->CreateLayer("foo");
1040 1 : poDS.reset();
1041 :
1042 1 : MyAlgorithm alg;
1043 1 : EXPECT_TRUE(!alg.GetUsageAsJSON().empty());
1044 4 : EXPECT_TRUE(
1045 : alg.ParseCommandLineArguments({"--update", osTmpFilename}));
1046 1 : ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
1047 1 : EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
1048 :
1049 1 : alg.Finalize();
1050 :
1051 1 : VSIUnlink(osTmpFilename.c_str());
1052 : }
1053 : }
1054 : }
1055 :
1056 4 : TEST_F(test_gdal_algorithm, same_input_output_dataset_sqlite)
1057 : {
1058 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1059 : {
1060 : public:
1061 : GDALArgDatasetValue m_input{};
1062 : GDALArgDatasetValue m_output{};
1063 : bool m_update = false;
1064 :
1065 1 : MyAlgorithm()
1066 1 : {
1067 1 : AddInputDatasetArg(&m_input);
1068 1 : AddOutputDatasetArg(&m_output);
1069 1 : m_output.SetInputFlags(GADV_NAME | GADV_OBJECT);
1070 1 : AddUpdateArg(&m_update);
1071 1 : }
1072 : };
1073 :
1074 : {
1075 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
1076 1 : if (!poDriver)
1077 : {
1078 0 : GTEST_SKIP() << "GPKG support missing";
1079 : }
1080 : else
1081 : {
1082 : std::string osTmpFilename =
1083 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
1084 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
1085 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
1086 1 : poDS->CreateLayer("foo");
1087 1 : poDS.reset();
1088 :
1089 1 : MyAlgorithm alg;
1090 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1091 : {"--update", osTmpFilename, osTmpFilename}));
1092 1 : ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
1093 1 : EXPECT_NE(alg.m_output.GetDatasetRef(), nullptr);
1094 1 : EXPECT_EQ(alg.m_input.GetDatasetRef(),
1095 : alg.m_output.GetDatasetRef());
1096 1 : EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
1097 :
1098 1 : alg.Finalize();
1099 :
1100 1 : VSIUnlink(osTmpFilename.c_str());
1101 : }
1102 : }
1103 : }
1104 :
1105 4 : TEST_F(test_gdal_algorithm, output_dataset_created_by_alg)
1106 : {
1107 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1108 : {
1109 : public:
1110 : GDALArgDatasetValue m_output{};
1111 :
1112 1 : MyAlgorithm()
1113 1 : {
1114 1 : AddOutputDatasetArg(&m_output);
1115 1 : m_output.SetInputFlags(GADV_NAME);
1116 1 : m_output.SetOutputFlags(GADV_OBJECT);
1117 1 : }
1118 : };
1119 :
1120 1 : MyAlgorithm alg;
1121 1 : alg.GetUsageForCLI(false);
1122 1 : }
1123 :
1124 4 : TEST_F(test_gdal_algorithm, string_choices)
1125 : {
1126 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1127 : {
1128 : public:
1129 : std::string m_val{};
1130 :
1131 3 : MyAlgorithm()
1132 3 : {
1133 6 : AddArg("val", 0, "", &m_val)
1134 3 : .SetChoices("foo", "bar")
1135 3 : .SetHiddenChoices("baz");
1136 3 : }
1137 : };
1138 :
1139 : {
1140 2 : MyAlgorithm alg;
1141 1 : alg.GetUsageForCLI(false);
1142 :
1143 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo"}));
1144 1 : EXPECT_STREQ(alg.m_val.c_str(), "foo");
1145 : }
1146 :
1147 : {
1148 2 : MyAlgorithm alg;
1149 1 : alg.GetUsageForCLI(false);
1150 :
1151 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=baz"}));
1152 1 : EXPECT_STREQ(alg.m_val.c_str(), "baz");
1153 : }
1154 :
1155 : {
1156 2 : MyAlgorithm alg;
1157 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1158 1 : CPLErrorReset();
1159 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
1160 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1161 1 : EXPECT_TRUE(alg.m_val.empty());
1162 : }
1163 1 : }
1164 :
1165 4 : TEST_F(test_gdal_algorithm, vector_int)
1166 : {
1167 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1168 : {
1169 : public:
1170 : std::vector<int> m_val{};
1171 :
1172 4 : MyAlgorithm()
1173 4 : {
1174 4 : AddArg("val", 0, "", &m_val);
1175 4 : }
1176 : };
1177 :
1178 : {
1179 2 : MyAlgorithm alg;
1180 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5,6"}));
1181 2 : auto expected = std::vector<int>{5, 6};
1182 1 : EXPECT_EQ(alg.m_val, expected);
1183 : }
1184 :
1185 : {
1186 2 : MyAlgorithm alg;
1187 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1188 1 : CPLErrorReset();
1189 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
1190 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1191 1 : EXPECT_TRUE(alg.m_val.empty());
1192 : }
1193 :
1194 : {
1195 2 : MyAlgorithm alg;
1196 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1197 1 : CPLErrorReset();
1198 3 : EXPECT_FALSE(
1199 : alg.ParseCommandLineArguments({"--val=1,12345679812346798123456"}));
1200 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1201 1 : EXPECT_TRUE(alg.m_val.empty());
1202 : }
1203 :
1204 : {
1205 2 : MyAlgorithm alg;
1206 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1207 1 : CPLErrorReset();
1208 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
1209 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1210 1 : EXPECT_TRUE(alg.m_val.empty());
1211 : }
1212 1 : }
1213 :
1214 4 : TEST_F(test_gdal_algorithm, vector_int_validation_fails)
1215 : {
1216 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1217 : {
1218 : public:
1219 : std::vector<int> m_val{};
1220 :
1221 1 : MyAlgorithm()
1222 1 : {
1223 2 : AddArg("val", 0, "", &m_val)
1224 : .AddValidationAction(
1225 1 : []()
1226 : {
1227 1 : CPLError(CE_Failure, CPLE_AppDefined,
1228 : "validation failed");
1229 1 : return false;
1230 1 : });
1231 1 : }
1232 : };
1233 :
1234 : {
1235 2 : MyAlgorithm alg;
1236 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1237 1 : CPLErrorReset();
1238 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
1239 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
1240 : }
1241 1 : }
1242 :
1243 4 : TEST_F(test_gdal_algorithm, vector_double)
1244 : {
1245 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1246 : {
1247 : public:
1248 : std::vector<double> m_val{};
1249 :
1250 3 : MyAlgorithm()
1251 3 : {
1252 3 : AddArg("val", 0, "", &m_val);
1253 3 : }
1254 : };
1255 :
1256 : {
1257 2 : MyAlgorithm alg;
1258 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5,2.5"}));
1259 2 : auto expected = std::vector<double>{1.5, 2.5};
1260 1 : EXPECT_EQ(alg.m_val, expected);
1261 : }
1262 :
1263 : {
1264 2 : MyAlgorithm alg;
1265 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1266 1 : CPLErrorReset();
1267 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
1268 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1269 1 : EXPECT_TRUE(alg.m_val.empty());
1270 : }
1271 :
1272 : {
1273 2 : MyAlgorithm alg;
1274 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1275 1 : CPLErrorReset();
1276 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
1277 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1278 1 : EXPECT_TRUE(alg.m_val.empty());
1279 : }
1280 1 : }
1281 :
1282 4 : TEST_F(test_gdal_algorithm, vector_double_validation_fails)
1283 : {
1284 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1285 : {
1286 : public:
1287 : std::vector<double> m_val{};
1288 :
1289 1 : MyAlgorithm()
1290 1 : {
1291 2 : AddArg("val", 0, "", &m_val)
1292 : .AddValidationAction(
1293 1 : []()
1294 : {
1295 1 : CPLError(CE_Failure, CPLE_AppDefined,
1296 : "validation failed");
1297 1 : return false;
1298 1 : });
1299 1 : }
1300 : };
1301 :
1302 : {
1303 2 : MyAlgorithm alg;
1304 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1305 1 : CPLErrorReset();
1306 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
1307 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
1308 : }
1309 1 : }
1310 :
1311 4 : TEST_F(test_gdal_algorithm, vector_string)
1312 : {
1313 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1314 : {
1315 : public:
1316 : std::vector<std::string> m_val{};
1317 :
1318 1 : MyAlgorithm()
1319 1 : {
1320 1 : AddArg("val", 0, "", &m_val);
1321 1 : }
1322 : };
1323 :
1324 : {
1325 2 : MyAlgorithm alg;
1326 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
1327 6 : auto expected = std::vector<std::string>{"foo", "bar"};
1328 1 : EXPECT_EQ(alg.m_val, expected);
1329 : }
1330 1 : }
1331 :
1332 4 : TEST_F(test_gdal_algorithm, vector_string_validation_fails)
1333 : {
1334 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1335 : {
1336 : public:
1337 : std::vector<std::string> m_val{};
1338 :
1339 1 : MyAlgorithm()
1340 1 : {
1341 2 : AddArg("val", 0, "", &m_val)
1342 : .AddValidationAction(
1343 1 : []()
1344 : {
1345 1 : CPLError(CE_Failure, CPLE_AppDefined,
1346 : "validation failed");
1347 1 : return false;
1348 1 : });
1349 1 : }
1350 : };
1351 :
1352 : {
1353 2 : MyAlgorithm alg;
1354 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1355 1 : CPLErrorReset();
1356 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
1357 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
1358 : }
1359 1 : }
1360 :
1361 4 : TEST_F(test_gdal_algorithm, vector_string_choices)
1362 : {
1363 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1364 : {
1365 : public:
1366 : std::vector<std::string> m_val{};
1367 :
1368 3 : MyAlgorithm()
1369 3 : {
1370 3 : AddArg("val", 0, "", &m_val).SetChoices("foo", "bar");
1371 3 : }
1372 : };
1373 :
1374 : {
1375 2 : MyAlgorithm alg;
1376 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
1377 6 : auto expected = std::vector<std::string>{"foo", "bar"};
1378 1 : EXPECT_EQ(alg.m_val, expected);
1379 : }
1380 :
1381 : {
1382 2 : MyAlgorithm alg;
1383 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1384 1 : CPLErrorReset();
1385 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo,invalid"}));
1386 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1387 1 : EXPECT_TRUE(alg.m_val.empty());
1388 : }
1389 :
1390 : {
1391 2 : MyAlgorithm alg;
1392 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1393 1 : CPLErrorReset();
1394 4 : EXPECT_FALSE(
1395 : alg.ParseCommandLineArguments({"--val=foo", "--val=invalid"}));
1396 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1397 1 : EXPECT_TRUE(alg.m_val.empty());
1398 : }
1399 1 : }
1400 :
1401 4 : TEST_F(test_gdal_algorithm, vector_dataset)
1402 : {
1403 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1404 : {
1405 : public:
1406 : std::vector<GDALArgDatasetValue> m_val{};
1407 :
1408 3 : MyAlgorithm()
1409 3 : {
1410 3 : AddArg("val", 0, "", &m_val);
1411 3 : }
1412 : };
1413 :
1414 : {
1415 1 : MyAlgorithm alg;
1416 3 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1417 : {"--val=" GCORE_DATA_DIR "byte.tif"}));
1418 1 : ASSERT_EQ(alg.m_val.size(), 1U);
1419 1 : EXPECT_NE(alg.m_val[0].GetDatasetRef(), nullptr);
1420 : }
1421 :
1422 : {
1423 1 : MyAlgorithm alg;
1424 1 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1425 1 : CPLErrorReset();
1426 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=non_existing.tif"}));
1427 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1428 1 : ASSERT_EQ(alg.m_val.size(), 1U);
1429 1 : EXPECT_EQ(alg.m_val[0].GetDatasetRef(), nullptr);
1430 : }
1431 :
1432 : {
1433 2 : MyAlgorithm alg;
1434 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1435 1 : CPLErrorReset();
1436 1 : alg.GetArg("val")->Set(std::vector<GDALArgDatasetValue>(1));
1437 1 : EXPECT_FALSE(alg.Run());
1438 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1439 : }
1440 : }
1441 :
1442 4 : TEST_F(test_gdal_algorithm, vector_dataset_validation_fails)
1443 : {
1444 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1445 : {
1446 : public:
1447 : std::vector<GDALArgDatasetValue> m_val{};
1448 :
1449 1 : MyAlgorithm()
1450 1 : {
1451 2 : AddArg("val", 0, "", &m_val)
1452 : .AddValidationAction(
1453 1 : []()
1454 : {
1455 1 : CPLError(CE_Failure, CPLE_AppDefined,
1456 : "validation failed");
1457 1 : return false;
1458 1 : });
1459 1 : }
1460 : };
1461 :
1462 : {
1463 2 : MyAlgorithm alg;
1464 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1465 1 : CPLErrorReset();
1466 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
1467 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
1468 : }
1469 1 : }
1470 :
1471 4 : TEST_F(test_gdal_algorithm, vector_input)
1472 : {
1473 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1474 : {
1475 : public:
1476 : std::vector<GDALArgDatasetValue> m_input{};
1477 : std::vector<std::string> m_oo{};
1478 : std::vector<std::string> m_if{};
1479 : bool m_update = false;
1480 :
1481 1 : MyAlgorithm()
1482 1 : {
1483 1 : AddInputDatasetArg(&m_input);
1484 1 : AddOpenOptionsArg(&m_oo);
1485 1 : AddInputFormatsArg(&m_if);
1486 1 : AddUpdateArg(&m_update);
1487 1 : }
1488 : };
1489 :
1490 : {
1491 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
1492 1 : if (!poDriver)
1493 : {
1494 0 : GTEST_SKIP() << "GPKG support missing";
1495 : }
1496 : else
1497 : {
1498 : std::string osTmpFilename =
1499 1 : VSIMemGenerateHiddenFilename("temp.gpkg");
1500 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
1501 1 : osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
1502 1 : poDS->CreateLayer("foo");
1503 1 : poDS.reset();
1504 :
1505 1 : MyAlgorithm alg;
1506 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1507 : {"--update", "--oo=LIST_ALL_TABLES=YES", "--if=GPKG",
1508 : osTmpFilename}));
1509 1 : ASSERT_EQ(alg.m_input.size(), 1U);
1510 1 : ASSERT_NE(alg.m_input[0].GetDatasetRef(), nullptr);
1511 1 : EXPECT_EQ(alg.m_input[0].GetDatasetRef()->GetAccess(), GA_Update);
1512 :
1513 1 : alg.Finalize();
1514 :
1515 1 : VSIUnlink(osTmpFilename.c_str());
1516 : }
1517 : }
1518 : }
1519 :
1520 4 : TEST_F(test_gdal_algorithm, several_values)
1521 : {
1522 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1523 : {
1524 : public:
1525 : std::vector<std::string> m_co{};
1526 :
1527 5 : MyAlgorithm()
1528 5 : {
1529 5 : AddArg("co", 0, "creation options", &m_co);
1530 5 : }
1531 : };
1532 :
1533 : {
1534 2 : MyAlgorithm alg;
1535 1 : alg.GetUsageForCLI(false);
1536 :
1537 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
1538 : }
1539 :
1540 : {
1541 2 : MyAlgorithm alg;
1542 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "FOO=BAR"}));
1543 4 : EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
1544 : }
1545 :
1546 : {
1547 2 : MyAlgorithm alg;
1548 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR"}));
1549 4 : EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
1550 : }
1551 :
1552 : {
1553 2 : MyAlgorithm alg;
1554 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR,BAR=BAZ"}));
1555 6 : auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
1556 1 : EXPECT_EQ(alg.m_co, expected);
1557 : }
1558 :
1559 : {
1560 2 : MyAlgorithm alg;
1561 5 : EXPECT_TRUE(
1562 : alg.ParseCommandLineArguments({"--co=FOO=BAR", "--co", "BAR=BAZ"}));
1563 6 : auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
1564 1 : EXPECT_EQ(alg.m_co, expected);
1565 : }
1566 1 : }
1567 :
1568 4 : TEST_F(test_gdal_algorithm, required_arg)
1569 : {
1570 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1571 : {
1572 : public:
1573 : std::string m_arg{};
1574 :
1575 2 : MyAlgorithm()
1576 2 : {
1577 2 : AddArg("arg", 0, "required arg", &m_arg).SetRequired();
1578 2 : }
1579 : };
1580 :
1581 : {
1582 2 : MyAlgorithm alg;
1583 1 : alg.GetUsageForCLI(false);
1584 :
1585 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1586 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1587 : }
1588 :
1589 : {
1590 2 : MyAlgorithm alg;
1591 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg", "foo"}));
1592 1 : EXPECT_STREQ(alg.m_arg.c_str(), "foo");
1593 : }
1594 1 : }
1595 :
1596 4 : TEST_F(test_gdal_algorithm, single_positional_arg)
1597 : {
1598 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1599 : {
1600 : public:
1601 : std::string m_value{};
1602 :
1603 4 : MyAlgorithm()
1604 4 : {
1605 4 : AddArg("input", 0, "input value", &m_value).SetPositional();
1606 4 : }
1607 : };
1608 :
1609 : {
1610 2 : MyAlgorithm alg;
1611 1 : alg.GetUsageForCLI(false);
1612 :
1613 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
1614 : }
1615 :
1616 : {
1617 2 : MyAlgorithm alg;
1618 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
1619 1 : EXPECT_TRUE(alg.GetArg("input")->IsExplicitlySet());
1620 2 : EXPECT_STREQ(alg.GetArg("input")->Get<std::string>().c_str(),
1621 : "my_input");
1622 : }
1623 :
1624 : {
1625 2 : MyAlgorithm alg;
1626 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input", "my_input"}));
1627 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
1628 : }
1629 :
1630 : {
1631 2 : MyAlgorithm alg;
1632 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
1633 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
1634 : }
1635 1 : }
1636 :
1637 4 : TEST_F(test_gdal_algorithm, single_positional_arg_required)
1638 : {
1639 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1640 : {
1641 : public:
1642 : std::string m_value{};
1643 :
1644 2 : MyAlgorithm()
1645 2 : {
1646 4 : AddArg("input", 0, "input value", &m_value)
1647 2 : .SetPositional()
1648 2 : .SetRequired();
1649 2 : }
1650 : };
1651 :
1652 : {
1653 2 : MyAlgorithm alg;
1654 1 : alg.GetUsageForCLI(false);
1655 :
1656 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1657 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1658 : }
1659 :
1660 : {
1661 2 : MyAlgorithm alg;
1662 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
1663 1 : EXPECT_STREQ(alg.m_value.c_str(), "my_input");
1664 : }
1665 1 : }
1666 :
1667 4 : TEST_F(test_gdal_algorithm, two_positional_arg)
1668 : {
1669 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1670 : {
1671 : public:
1672 : std::string m_input_value{};
1673 : std::string m_output_value{};
1674 :
1675 12 : MyAlgorithm()
1676 12 : {
1677 12 : AddArg("input", 'i', "input value", &m_input_value).SetPositional();
1678 24 : AddArg("output", 'o', "output value", &m_output_value)
1679 12 : .SetPositional();
1680 12 : }
1681 : };
1682 :
1683 : {
1684 2 : MyAlgorithm alg;
1685 1 : alg.GetUsageForCLI(false);
1686 :
1687 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
1688 : }
1689 :
1690 : {
1691 2 : MyAlgorithm alg;
1692 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
1693 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1694 : }
1695 :
1696 : {
1697 2 : MyAlgorithm alg;
1698 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-i", "my_input"}));
1699 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1700 : }
1701 :
1702 : {
1703 2 : MyAlgorithm alg;
1704 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input", "my_output"}));
1705 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1706 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1707 : }
1708 :
1709 : {
1710 2 : MyAlgorithm alg;
1711 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1712 : {"--input", "my_input", "-o", "my_output"}));
1713 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1714 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1715 : }
1716 :
1717 : {
1718 2 : MyAlgorithm alg;
1719 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1720 : {"-o", "my_output", "--input", "my_input"}));
1721 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1722 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1723 : }
1724 :
1725 : {
1726 2 : MyAlgorithm alg;
1727 5 : EXPECT_TRUE(
1728 : alg.ParseCommandLineArguments({"-o", "my_output", "my_input"}));
1729 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1730 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1731 : }
1732 :
1733 : {
1734 2 : MyAlgorithm alg;
1735 5 : EXPECT_TRUE(
1736 : alg.ParseCommandLineArguments({"my_input", "-o", "my_output"}));
1737 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1738 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1739 : }
1740 :
1741 : {
1742 2 : MyAlgorithm alg;
1743 1 : alg.GetArg("input")->Set("my_input");
1744 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_output"}));
1745 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1746 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1747 : }
1748 :
1749 : {
1750 2 : MyAlgorithm alg;
1751 1 : alg.GetArg("input")->Set("my_input");
1752 1 : alg.GetArg("output")->Set("my_output");
1753 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
1754 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1755 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1756 : }
1757 :
1758 : {
1759 2 : MyAlgorithm alg;
1760 1 : alg.GetArg("input")->Set("my_input");
1761 1 : alg.GetArg("output")->Set("my_output");
1762 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1763 1 : CPLErrorReset();
1764 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"unexpected"}));
1765 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1766 : }
1767 :
1768 : {
1769 2 : MyAlgorithm alg;
1770 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1771 1 : CPLErrorReset();
1772 5 : EXPECT_FALSE(alg.ParseCommandLineArguments({"foo", "bar", "baz"}));
1773 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1774 : }
1775 1 : }
1776 :
1777 4 : TEST_F(test_gdal_algorithm, two_positional_arg_first_two_values)
1778 : {
1779 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1780 : {
1781 : public:
1782 : std::vector<int> m_input_value{};
1783 : std::string m_output_value{};
1784 :
1785 3 : MyAlgorithm()
1786 3 : {
1787 6 : AddArg("input", 'i', "input value", &m_input_value)
1788 3 : .SetPositional()
1789 3 : .SetMinCount(2)
1790 3 : .SetMaxCount(2)
1791 3 : .SetDisplayHintAboutRepetition(false);
1792 6 : AddArg("output", 'o', "output value", &m_output_value)
1793 3 : .SetPositional();
1794 3 : }
1795 : };
1796 :
1797 : {
1798 2 : MyAlgorithm alg;
1799 1 : alg.GetUsageForCLI(false);
1800 :
1801 5 : EXPECT_TRUE(alg.ParseCommandLineArguments({"1", "2", "baz"}));
1802 2 : auto expected = std::vector<int>{1, 2};
1803 1 : EXPECT_EQ(alg.m_input_value, expected);
1804 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "baz");
1805 : }
1806 :
1807 : {
1808 2 : MyAlgorithm alg;
1809 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1810 1 : CPLErrorReset();
1811 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"1"}));
1812 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1813 : }
1814 :
1815 : {
1816 2 : MyAlgorithm alg;
1817 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1818 1 : CPLErrorReset();
1819 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"1", "foo"}));
1820 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
1821 : }
1822 1 : }
1823 :
1824 4 : TEST_F(test_gdal_algorithm, unlimited_input_single_output)
1825 : {
1826 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1827 : {
1828 : public:
1829 : std::vector<std::string> m_input_values{};
1830 : std::string m_output_value{};
1831 :
1832 2 : MyAlgorithm()
1833 2 : {
1834 4 : AddArg("input", 'i', "input value", &m_input_values)
1835 2 : .SetPositional();
1836 4 : AddArg("output", 'o', "output value", &m_output_value)
1837 2 : .SetPositional()
1838 2 : .SetRequired();
1839 2 : }
1840 : };
1841 :
1842 : {
1843 2 : MyAlgorithm alg;
1844 1 : alg.GetUsageForCLI(false);
1845 :
1846 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1847 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1848 : }
1849 :
1850 : {
1851 2 : MyAlgorithm alg;
1852 5 : EXPECT_TRUE(
1853 : alg.ParseCommandLineArguments({"input1", "input2", "my_output"}));
1854 6 : auto expected = std::vector<std::string>{"input1", "input2"};
1855 1 : EXPECT_EQ(alg.m_input_values, expected);
1856 1 : EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
1857 : }
1858 1 : }
1859 :
1860 4 : TEST_F(test_gdal_algorithm, single_input_unlimited_outputs)
1861 : {
1862 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1863 : {
1864 : public:
1865 : std::string m_input_value{};
1866 : std::vector<std::string> m_output_values{};
1867 :
1868 2 : MyAlgorithm()
1869 2 : {
1870 4 : AddArg("input", 'i', "input value", &m_input_value)
1871 2 : .SetPositional()
1872 2 : .SetRequired();
1873 4 : AddArg("output", 'o', "output value", &m_output_values)
1874 2 : .SetPositional();
1875 2 : }
1876 : };
1877 :
1878 : {
1879 2 : MyAlgorithm alg;
1880 1 : alg.GetUsageForCLI(false);
1881 :
1882 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1883 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1884 : }
1885 :
1886 : {
1887 2 : MyAlgorithm alg;
1888 5 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1889 : {"my_input", "my_output1", "my_output2"}));
1890 1 : EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
1891 6 : auto expected = std::vector<std::string>{"my_output1", "my_output2"};
1892 1 : EXPECT_EQ(alg.m_output_values, expected);
1893 : }
1894 1 : }
1895 :
1896 4 : TEST_F(test_gdal_algorithm, min_max_count)
1897 : {
1898 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1899 : {
1900 : public:
1901 : std::vector<std::string> m_arg{};
1902 :
1903 5 : MyAlgorithm()
1904 5 : {
1905 10 : AddArg("arg", 0, "arg", &m_arg)
1906 5 : .SetRequired()
1907 5 : .SetMinCount(2)
1908 5 : .SetMaxCount(3);
1909 5 : }
1910 : };
1911 :
1912 : {
1913 2 : MyAlgorithm alg;
1914 1 : alg.GetUsageForCLI(false);
1915 :
1916 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1917 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1918 : }
1919 :
1920 : {
1921 2 : MyAlgorithm alg;
1922 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1923 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo"}));
1924 : }
1925 :
1926 : {
1927 2 : MyAlgorithm alg;
1928 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1929 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=1,2,3,4"}));
1930 : }
1931 :
1932 : {
1933 2 : MyAlgorithm alg;
1934 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
1935 6 : auto expected = std::vector<std::string>{"foo", "bar"};
1936 1 : EXPECT_EQ(alg.m_arg, expected);
1937 : }
1938 :
1939 : {
1940 2 : MyAlgorithm alg;
1941 8 : EXPECT_TRUE(alg.ParseCommandLineArguments(
1942 : {"--arg", "foo", "--arg", "bar", "--arg", "baz"}));
1943 7 : auto expected = std::vector<std::string>{"foo", "bar", "baz"};
1944 1 : EXPECT_EQ(alg.m_arg, expected);
1945 : }
1946 1 : }
1947 :
1948 4 : TEST_F(test_gdal_algorithm, min_max_count_equal)
1949 : {
1950 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1951 : {
1952 : public:
1953 : std::vector<std::string> m_arg{};
1954 :
1955 2 : MyAlgorithm()
1956 2 : {
1957 4 : AddArg("arg", 0, "arg", &m_arg)
1958 2 : .SetRequired()
1959 2 : .SetMinCount(2)
1960 2 : .SetMaxCount(2);
1961 2 : }
1962 : };
1963 :
1964 : {
1965 2 : MyAlgorithm alg;
1966 1 : alg.GetUsageForCLI(false);
1967 :
1968 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
1969 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
1970 : }
1971 :
1972 : {
1973 2 : MyAlgorithm alg;
1974 2 : alg.GetArg("arg")->Set(std::vector<std::string>{"foo"});
1975 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
1976 1 : EXPECT_FALSE(alg.ValidateArguments());
1977 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
1978 : "test: 1 value(s) have been specified for argument 'arg', "
1979 : "whereas exactly 2 were expected.");
1980 : }
1981 1 : }
1982 :
1983 4 : TEST_F(test_gdal_algorithm, repeated_arg_allowed_false)
1984 : {
1985 : class MyAlgorithm : public MyAlgorithmWithDummyRun
1986 : {
1987 : public:
1988 : std::vector<std::string> m_arg{};
1989 :
1990 3 : MyAlgorithm()
1991 3 : {
1992 6 : AddArg("arg", 0, "arg", &m_arg)
1993 3 : .SetRepeatedArgAllowed(false)
1994 3 : .SetMinCount(2)
1995 3 : .SetMaxCount(3);
1996 3 : }
1997 : };
1998 :
1999 : {
2000 2 : MyAlgorithm alg;
2001 1 : alg.GetUsageForCLI(false);
2002 :
2003 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2004 : }
2005 :
2006 : {
2007 2 : MyAlgorithm alg;
2008 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
2009 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2010 1 : EXPECT_EQ(alg.m_arg, expected);
2011 : }
2012 :
2013 : {
2014 2 : MyAlgorithm alg;
2015 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2016 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
2017 : }
2018 1 : }
2019 :
2020 4 : TEST_F(test_gdal_algorithm, ambiguous_positional_unlimited_and_then_varying)
2021 : {
2022 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2023 : {
2024 : public:
2025 : std::vector<std::string> m_input_values{};
2026 : std::vector<std::string> m_output_values{};
2027 :
2028 1 : MyAlgorithm()
2029 1 : {
2030 2 : AddArg("input", 'i', "input value", &m_input_values)
2031 1 : .SetPositional();
2032 2 : AddArg("output", 'o', "output value", &m_output_values)
2033 1 : .SetPositional()
2034 1 : .SetMinCount(2)
2035 1 : .SetMaxCount(3);
2036 1 : }
2037 : };
2038 :
2039 : {
2040 2 : MyAlgorithm alg;
2041 1 : alg.GetUsageForCLI(false);
2042 :
2043 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2044 1 : CPLErrorReset();
2045 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
2046 : {"my_input", "my_output1", "my_output2"}));
2047 1 : EXPECT_STREQ(
2048 : CPLGetLastErrorMsg(),
2049 : "test: Ambiguity in definition of positional argument 'output' "
2050 : "given it has a varying number of values, but follows argument "
2051 : "'input' which also has a varying number of values");
2052 : }
2053 1 : }
2054 :
2055 4 : TEST_F(test_gdal_algorithm,
2056 : ambiguous_positional_unlimited_and_then_non_required)
2057 : {
2058 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2059 : {
2060 : public:
2061 : std::vector<std::string> m_input_values{};
2062 : std::string m_output_value{};
2063 :
2064 1 : MyAlgorithm()
2065 1 : {
2066 2 : AddArg("input", 'i', "input value", &m_input_values)
2067 1 : .SetPositional();
2068 2 : AddArg("output", 'o', "output value", &m_output_value)
2069 1 : .SetPositional();
2070 1 : }
2071 : };
2072 :
2073 : {
2074 2 : MyAlgorithm alg;
2075 1 : alg.GetUsageForCLI(false);
2076 :
2077 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2078 1 : CPLErrorReset();
2079 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
2080 : {"my_input1", "my_input2", "my_output"}));
2081 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
2082 : "test: Ambiguity in definition of positional argument "
2083 : "'output', given it is not required but follows argument "
2084 : "'input' which has a varying number of values");
2085 : }
2086 1 : }
2087 :
2088 4 : TEST_F(test_gdal_algorithm,
2089 : ambiguous_positional_fixed_then_unlimited_then_fixed)
2090 : {
2091 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2092 : {
2093 : public:
2094 : std::string m_input_value{};
2095 : std::vector<std::string> m_something{};
2096 : std::string m_output_value{};
2097 :
2098 1 : MyAlgorithm()
2099 1 : {
2100 1 : AddArg("input", 'i', "input value", &m_input_value).SetPositional();
2101 1 : AddArg("something", 0, "something", &m_something).SetPositional();
2102 2 : AddArg("output", 'o', "output value", &m_output_value)
2103 1 : .SetPositional();
2104 1 : }
2105 : };
2106 :
2107 : {
2108 2 : MyAlgorithm alg;
2109 1 : alg.GetUsageForCLI(false);
2110 :
2111 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2112 1 : CPLErrorReset();
2113 5 : EXPECT_FALSE(alg.ParseCommandLineArguments(
2114 : {"my_input", "something", "my_output"}));
2115 : // Actually this is not ambiguous here, but our parser does not support
2116 : // that for now
2117 1 : EXPECT_STREQ(
2118 : CPLGetLastErrorMsg(),
2119 : "test: Ambiguity in definition of positional arguments: arguments "
2120 : "with varying number of values must be first or last one.");
2121 : }
2122 1 : }
2123 :
2124 4 : TEST_F(test_gdal_algorithm, positional_unlimited_and_then_2)
2125 : {
2126 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2127 : {
2128 : public:
2129 : std::vector<std::string> m_input_values{};
2130 : std::vector<std::string> m_output_values{};
2131 :
2132 2 : MyAlgorithm()
2133 2 : {
2134 4 : AddArg("input", 'i', "input value", &m_input_values)
2135 2 : .SetPositional();
2136 4 : AddArg("output", 'o', "output value", &m_output_values)
2137 2 : .SetPositional()
2138 2 : .SetMinCount(2)
2139 2 : .SetMaxCount(2);
2140 2 : }
2141 : };
2142 :
2143 : {
2144 2 : MyAlgorithm alg;
2145 1 : alg.GetUsageForCLI(false);
2146 :
2147 7 : EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
2148 : "my_input3", "my_output1",
2149 : "my_output2"}));
2150 1 : EXPECT_EQ(alg.m_input_values.size(), 3U);
2151 1 : EXPECT_EQ(alg.m_output_values.size(), 2U);
2152 : }
2153 :
2154 : {
2155 2 : MyAlgorithm alg;
2156 :
2157 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2158 1 : CPLErrorReset();
2159 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"my_output1"}));
2160 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
2161 : "test: Not enough positional values.");
2162 : }
2163 1 : }
2164 :
2165 4 : TEST_F(test_gdal_algorithm, positional_unlimited_validation_error_and_then_2)
2166 : {
2167 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2168 : {
2169 : public:
2170 : std::vector<std::string> m_input_values{};
2171 : std::vector<std::string> m_output_values{};
2172 :
2173 1 : MyAlgorithm()
2174 1 : {
2175 2 : AddArg("input", 'i', "input value", &m_input_values)
2176 1 : .SetPositional()
2177 : .AddValidationAction(
2178 1 : []()
2179 : {
2180 1 : CPLError(CE_Failure, CPLE_AppDefined,
2181 : "validation failed");
2182 1 : return false;
2183 1 : });
2184 2 : AddArg("output", 'o', "output value", &m_output_values)
2185 1 : .SetPositional()
2186 1 : .SetMinCount(2)
2187 1 : .SetMaxCount(2);
2188 1 : }
2189 : };
2190 :
2191 : {
2192 2 : MyAlgorithm alg;
2193 :
2194 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2195 1 : CPLErrorReset();
2196 7 : EXPECT_FALSE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
2197 : "my_input3", "my_output1",
2198 : "my_output2"}));
2199 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
2200 : }
2201 1 : }
2202 :
2203 4 : TEST_F(test_gdal_algorithm,
2204 : positional_unlimited_validation_error_and_then_required)
2205 : {
2206 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2207 : {
2208 : public:
2209 : std::vector<std::string> m_input_values{};
2210 : std::string m_output_value{};
2211 :
2212 1 : MyAlgorithm()
2213 1 : {
2214 2 : AddArg("input", 'i', "input value", &m_input_values)
2215 1 : .SetPositional()
2216 1 : .SetChoices("foo");
2217 2 : AddArg("output", 'o', "output value", &m_output_value)
2218 1 : .SetPositional()
2219 1 : .SetRequired();
2220 1 : }
2221 : };
2222 :
2223 : {
2224 2 : MyAlgorithm alg;
2225 :
2226 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2227 1 : CPLErrorReset();
2228 5 : EXPECT_FALSE(
2229 : alg.ParseCommandLineArguments({"foo", "bar", "my_output"}));
2230 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
2231 : "test: Invalid value 'bar' for string argument 'input'. "
2232 : "Should be one among 'foo'.");
2233 : }
2234 1 : }
2235 :
2236 4 : TEST_F(test_gdal_algorithm,
2237 : positional_required_and_then_unlimited_validation_error)
2238 : {
2239 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2240 : {
2241 : public:
2242 : std::string m_input_value{};
2243 : std::vector<std::string> m_output_values{};
2244 :
2245 1 : MyAlgorithm()
2246 1 : {
2247 2 : AddArg("input", 'i', "input value", &m_input_value)
2248 1 : .SetPositional()
2249 1 : .SetRequired();
2250 2 : AddArg("output", 'o', "output values", &m_output_values)
2251 1 : .SetPositional()
2252 1 : .SetChoices("foo");
2253 1 : }
2254 : };
2255 :
2256 : {
2257 2 : MyAlgorithm alg;
2258 :
2259 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2260 1 : CPLErrorReset();
2261 5 : EXPECT_FALSE(
2262 : alg.ParseCommandLineArguments({"something", "foo", "bar"}));
2263 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
2264 : "test: Invalid value 'bar' for string argument 'output'. "
2265 : "Should be one among 'foo'.");
2266 : }
2267 1 : }
2268 :
2269 4 : TEST_F(test_gdal_algorithm, packed_values_allowed_false)
2270 : {
2271 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2272 : {
2273 : public:
2274 : std::vector<std::string> m_arg{};
2275 :
2276 3 : MyAlgorithm()
2277 3 : {
2278 6 : AddArg("arg", 0, "arg", &m_arg)
2279 3 : .SetPackedValuesAllowed(false)
2280 3 : .SetMinCount(2)
2281 3 : .SetMaxCount(3);
2282 3 : }
2283 : };
2284 :
2285 : {
2286 2 : MyAlgorithm alg;
2287 1 : alg.GetUsageForCLI(false);
2288 :
2289 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2290 : }
2291 :
2292 : {
2293 2 : MyAlgorithm alg;
2294 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
2295 6 : auto expected = std::vector<std::string>{"foo", "bar"};
2296 1 : EXPECT_EQ(alg.m_arg, expected);
2297 : }
2298 :
2299 : {
2300 2 : MyAlgorithm alg;
2301 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2302 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
2303 : }
2304 1 : }
2305 :
2306 4 : TEST_F(test_gdal_algorithm, actions)
2307 : {
2308 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2309 : {
2310 : public:
2311 : bool m_flag = false;
2312 : bool m_flagSpecified = false;
2313 :
2314 1 : MyAlgorithm()
2315 1 : {
2316 2 : AddArg("flag", 'f', "boolean flag", &m_flag)
2317 1 : .AddAction([this]() { m_flagSpecified = true; });
2318 1 : }
2319 : };
2320 :
2321 : {
2322 2 : MyAlgorithm alg;
2323 1 : alg.GetUsageForCLI(false);
2324 :
2325 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
2326 1 : EXPECT_TRUE(alg.m_flag);
2327 1 : EXPECT_TRUE(alg.m_flagSpecified);
2328 : }
2329 1 : }
2330 :
2331 4 : TEST_F(test_gdal_algorithm, various)
2332 : {
2333 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2334 : {
2335 : public:
2336 : bool m_flag = false;
2337 :
2338 6 : MyAlgorithm()
2339 6 : {
2340 6 : AddProgressArg();
2341 6 : }
2342 : };
2343 :
2344 : {
2345 2 : MyAlgorithm alg;
2346 1 : alg.GetUsageForCLI(false);
2347 :
2348 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2349 : // Parse again
2350 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2351 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2352 : }
2353 :
2354 : {
2355 2 : MyAlgorithm alg;
2356 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-h"}));
2357 1 : EXPECT_TRUE(alg.IsHelpRequested());
2358 : }
2359 :
2360 : {
2361 2 : MyAlgorithm alg;
2362 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--help"}));
2363 1 : EXPECT_TRUE(alg.IsHelpRequested());
2364 : }
2365 :
2366 : {
2367 2 : MyAlgorithm alg;
2368 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"help"}));
2369 1 : EXPECT_TRUE(alg.IsHelpRequested());
2370 : }
2371 :
2372 : {
2373 2 : MyAlgorithm alg;
2374 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--json-usage"}));
2375 1 : EXPECT_TRUE(alg.IsJSONUsageRequested());
2376 : }
2377 :
2378 : {
2379 2 : MyAlgorithm alg;
2380 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--progress"}));
2381 1 : EXPECT_TRUE(alg.IsProgressBarRequested());
2382 : }
2383 1 : }
2384 :
2385 4 : TEST_F(test_gdal_algorithm, mutually_exclusive)
2386 : {
2387 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2388 : {
2389 : public:
2390 : bool m_flag1 = false;
2391 : bool m_flag2 = false;
2392 : bool m_flag3 = false;
2393 :
2394 4 : MyAlgorithm()
2395 4 : {
2396 8 : AddArg("flag1", 0, "", &m_flag1)
2397 4 : .SetMutualExclusionGroup("my_group");
2398 8 : AddArg("flag2", 0, "", &m_flag2)
2399 4 : .SetMutualExclusionGroup("my_group");
2400 8 : AddArg("flag3", 0, "", &m_flag3)
2401 4 : .SetMutualExclusionGroup("my_group");
2402 4 : }
2403 : };
2404 :
2405 : {
2406 2 : MyAlgorithm alg;
2407 1 : alg.GetUsageForCLI(false);
2408 :
2409 1 : EXPECT_TRUE(alg.ParseCommandLineArguments({}));
2410 : }
2411 :
2412 : {
2413 2 : MyAlgorithm alg;
2414 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag1"}));
2415 : }
2416 :
2417 : {
2418 2 : MyAlgorithm alg;
2419 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag2"}));
2420 : }
2421 :
2422 : {
2423 2 : MyAlgorithm alg;
2424 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2425 1 : CPLErrorReset();
2426 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1", "--flag2"}));
2427 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2428 : }
2429 1 : }
2430 :
2431 4 : TEST_F(test_gdal_algorithm, invalid_input_format)
2432 : {
2433 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2434 : {
2435 : public:
2436 : std::vector<std::string> m_if{};
2437 :
2438 2 : MyAlgorithm()
2439 2 : {
2440 2 : AddInputFormatsArg(&m_if).AddMetadataItem(
2441 4 : GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
2442 2 : }
2443 : };
2444 :
2445 : {
2446 2 : MyAlgorithm alg;
2447 1 : alg.GetUsageForCLI(false);
2448 :
2449 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2450 1 : CPLErrorReset();
2451 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=I_DO_NOT_EXIST"}));
2452 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2453 : }
2454 :
2455 : {
2456 2 : MyAlgorithm alg;
2457 2 : CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
2458 1 : CPLErrorReset();
2459 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=GTIFF"}));
2460 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2461 : }
2462 1 : }
2463 :
2464 4 : TEST_F(test_gdal_algorithm, arg_layer_name_single)
2465 : {
2466 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2467 : {
2468 : public:
2469 : std::string m_layerName{};
2470 :
2471 1 : MyAlgorithm()
2472 1 : {
2473 1 : AddLayerNameArg(&m_layerName);
2474 1 : }
2475 : };
2476 :
2477 : {
2478 2 : MyAlgorithm alg;
2479 1 : alg.GetUsageForCLI(false);
2480 :
2481 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo"}));
2482 1 : EXPECT_STREQ(alg.m_layerName.c_str(), "foo");
2483 : }
2484 1 : }
2485 :
2486 4 : TEST_F(test_gdal_algorithm, arg_layer_name_multiple)
2487 : {
2488 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2489 : {
2490 : public:
2491 : std::vector<std::string> m_layerNames{};
2492 :
2493 1 : MyAlgorithm()
2494 1 : {
2495 1 : AddLayerNameArg(&m_layerNames);
2496 1 : }
2497 : };
2498 :
2499 : {
2500 2 : MyAlgorithm alg;
2501 6 : EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo", "-l", "bar"}));
2502 1 : EXPECT_EQ(alg.m_layerNames.size(), 2U);
2503 : }
2504 1 : }
2505 :
2506 4 : TEST_F(test_gdal_algorithm, arg_co)
2507 : {
2508 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2509 : {
2510 : public:
2511 : std::vector<std::string> m_co{};
2512 :
2513 2 : MyAlgorithm()
2514 2 : {
2515 2 : AddCreationOptionsArg(&m_co);
2516 2 : }
2517 : };
2518 :
2519 : {
2520 2 : MyAlgorithm alg;
2521 1 : alg.GetUsageForCLI(false);
2522 :
2523 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2524 : {"--co", "foo=bar", "--co", "bar=baz"}));
2525 1 : EXPECT_EQ(alg.m_co.size(), 2U);
2526 : }
2527 :
2528 : {
2529 2 : MyAlgorithm alg;
2530 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2531 1 : CPLErrorReset();
2532 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--co", "foo"}));
2533 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2534 : }
2535 1 : }
2536 :
2537 4 : TEST_F(test_gdal_algorithm, arg_lco)
2538 : {
2539 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2540 : {
2541 : public:
2542 : std::vector<std::string> m_lco{};
2543 :
2544 2 : MyAlgorithm()
2545 2 : {
2546 2 : AddLayerCreationOptionsArg(&m_lco);
2547 2 : }
2548 : };
2549 :
2550 : {
2551 2 : MyAlgorithm alg;
2552 1 : alg.GetUsageForCLI(false);
2553 :
2554 6 : EXPECT_TRUE(alg.ParseCommandLineArguments(
2555 : {"--lco", "foo=bar", "--lco", "bar=baz"}));
2556 1 : EXPECT_EQ(alg.m_lco.size(), 2U);
2557 : }
2558 :
2559 : {
2560 2 : MyAlgorithm alg;
2561 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2562 1 : CPLErrorReset();
2563 4 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--lco", "foo"}));
2564 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2565 : }
2566 1 : }
2567 :
2568 4 : TEST_F(test_gdal_algorithm, SetHiddenForCLI)
2569 : {
2570 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2571 : {
2572 : public:
2573 : bool m_b = false;
2574 :
2575 1 : MyAlgorithm()
2576 1 : {
2577 2 : AddArg("flag", 0, "", &m_b)
2578 1 : .SetHiddenForCLI()
2579 1 : .SetCategory(GAAC_ESOTERIC);
2580 1 : }
2581 : };
2582 :
2583 1 : MyAlgorithm alg;
2584 1 : alg.GetUsageForCLI(false);
2585 1 : }
2586 :
2587 4 : TEST_F(test_gdal_algorithm, SetOnlyForCLI)
2588 : {
2589 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2590 : {
2591 : public:
2592 : bool m_b = false;
2593 :
2594 1 : MyAlgorithm()
2595 1 : {
2596 2 : AddArg("flag", 0, "", &m_b)
2597 1 : .SetOnlyForCLI()
2598 1 : .SetCategory("my category");
2599 1 : m_longDescription = "long description";
2600 1 : }
2601 : };
2602 :
2603 1 : MyAlgorithm alg;
2604 1 : alg.GetUsageForCLI(false);
2605 1 : }
2606 :
2607 4 : TEST_F(test_gdal_algorithm, SetSkipIfAlreadySet)
2608 : {
2609 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2610 : {
2611 : public:
2612 : int m_val = 0;
2613 :
2614 2 : MyAlgorithm()
2615 2 : {
2616 2 : AddArg("option", 0, "option", &m_val).SetPositional();
2617 2 : }
2618 : };
2619 :
2620 : {
2621 2 : MyAlgorithm alg;
2622 1 : alg.GetArg("option")->Set(1);
2623 1 : alg.GetArg("option")->SetSkipIfAlreadySet();
2624 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"--option=1"}));
2625 : }
2626 :
2627 : {
2628 2 : MyAlgorithm alg;
2629 1 : alg.GetArg("option")->Set(1);
2630 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2631 1 : CPLErrorReset();
2632 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"--option=1"}));
2633 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2634 : }
2635 1 : }
2636 :
2637 4 : TEST_F(test_gdal_algorithm, alg_with_aliases)
2638 : {
2639 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2640 : {
2641 : public:
2642 : int m_val = 0;
2643 :
2644 1 : MyAlgorithm()
2645 1 : {
2646 1 : m_aliases.push_back("one_alias");
2647 1 : m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
2648 1 : m_aliases.push_back("hidden_alias");
2649 1 : }
2650 : };
2651 :
2652 2 : MyAlgorithm alg;
2653 1 : alg.GetUsageForCLI(false);
2654 1 : EXPECT_EQ(alg.GetAliases().size(), 3U);
2655 1 : }
2656 :
2657 4 : TEST_F(test_gdal_algorithm, subalgorithms)
2658 : {
2659 1 : bool hasRun = false;
2660 :
2661 : class SubAlgorithm : public GDALAlgorithm
2662 : {
2663 : public:
2664 : bool &m_bHasRun;
2665 : bool m_flag = false;
2666 :
2667 4 : SubAlgorithm(bool &lHasRun)
2668 4 : : GDALAlgorithm("subalg", "", "https://example.com"),
2669 4 : m_bHasRun(lHasRun)
2670 : {
2671 4 : AddProgressArg();
2672 4 : m_aliases.push_back("one_alias");
2673 4 : m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
2674 4 : m_aliases.push_back("hidden_alias");
2675 4 : }
2676 :
2677 1 : bool RunImpl(GDALProgressFunc, void *) override
2678 : {
2679 1 : m_bHasRun = true;
2680 1 : return true;
2681 : }
2682 : };
2683 :
2684 : class MyAlgorithm : public MyAlgorithmWithDummyRun
2685 : {
2686 : public:
2687 5 : MyAlgorithm(bool &lHasRun)
2688 5 : {
2689 10 : GDALAlgorithmRegistry::AlgInfo info;
2690 5 : info.m_name = "subalg";
2691 4 : info.m_creationFunc = [&lHasRun]()
2692 9 : { return std::make_unique<SubAlgorithm>(lHasRun); };
2693 5 : RegisterSubAlgorithm(info);
2694 : // RegisterSubAlgorithm(SubAlgorithm);
2695 5 : }
2696 : };
2697 :
2698 : {
2699 2 : MyAlgorithm alg(hasRun);
2700 1 : alg.GetUsageForCLI(false);
2701 :
2702 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2703 1 : CPLErrorReset();
2704 1 : EXPECT_FALSE(alg.ParseCommandLineArguments({}));
2705 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2706 : }
2707 :
2708 : {
2709 2 : MyAlgorithm alg(hasRun);
2710 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2711 1 : CPLErrorReset();
2712 3 : EXPECT_FALSE(alg.ParseCommandLineArguments({"invalid_subcommand"}));
2713 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
2714 : }
2715 :
2716 : {
2717 1 : MyAlgorithm alg(hasRun);
2718 2 : alg.SetCallPath(std::vector<std::string>{"main"});
2719 3 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg"}));
2720 1 : EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "subalg");
2721 1 : EXPECT_TRUE(alg.ValidateArguments());
2722 1 : EXPECT_TRUE(alg.Run());
2723 1 : EXPECT_TRUE(hasRun);
2724 1 : EXPECT_TRUE(alg.Finalize());
2725 1 : alg.GetUsageForCLI(false);
2726 : }
2727 :
2728 : {
2729 1 : MyAlgorithm alg(hasRun);
2730 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "-h"}));
2731 1 : EXPECT_TRUE(alg.IsHelpRequested());
2732 1 : EXPECT_TRUE(alg.ValidateArguments());
2733 1 : alg.GetUsageForCLI(false);
2734 : }
2735 :
2736 : {
2737 1 : MyAlgorithm alg(hasRun);
2738 4 : EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "--progress"}));
2739 1 : EXPECT_TRUE(alg.IsProgressBarRequested());
2740 1 : EXPECT_TRUE(alg.ValidateArguments());
2741 1 : alg.GetUsageForCLI(false);
2742 : }
2743 1 : }
2744 :
2745 : class MyRedundantRasterAlgorithm : public MyAlgorithmWithDummyRun
2746 : {
2747 : public:
2748 : static constexpr const char *NAME = "raster";
2749 : static constexpr const char *DESCRIPTION =
2750 : "redundant with existing raster!!!";
2751 : static constexpr const char *HELP_URL = "";
2752 :
2753 1 : static std::vector<std::string> GetAliases()
2754 : {
2755 1 : return {};
2756 : }
2757 : };
2758 :
2759 : class MyAlgorithmWithAlias : public MyAlgorithmWithDummyRun
2760 : {
2761 : public:
2762 : static constexpr const char *NAME = "MyAlgorithmWithAlias";
2763 : static constexpr const char *DESCRIPTION = "";
2764 : static constexpr const char *HELP_URL = "";
2765 :
2766 1 : static std::vector<std::string> GetAliases()
2767 : {
2768 : return {"alias", GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR,
2769 4 : "hidden_alias"};
2770 : }
2771 : };
2772 :
2773 : class MyAlgorithmWithRedundantAlias : public MyAlgorithmWithDummyRun
2774 : {
2775 : public:
2776 : static constexpr const char *NAME = "MyAlgorithmWithRedundantAlias";
2777 : static constexpr const char *DESCRIPTION = "";
2778 : static constexpr const char *HELP_URL = "";
2779 :
2780 1 : static std::vector<std::string> GetAliases()
2781 : {
2782 2 : return {"alias"};
2783 : }
2784 : };
2785 :
2786 : class MyAlgorithmWithRedundantHiddenAlias : public MyAlgorithmWithDummyRun
2787 : {
2788 : public:
2789 : static constexpr const char *NAME = "MyAlgorithmWithRedundantHiddenAlias";
2790 : static constexpr const char *DESCRIPTION = "";
2791 : static constexpr const char *HELP_URL = "";
2792 :
2793 1 : static std::vector<std::string> GetAliases()
2794 : {
2795 3 : return {GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR, "hidden_alias"};
2796 : }
2797 : };
2798 :
2799 4 : TEST_F(test_gdal_algorithm, GDALGlobalAlgorithmRegistry)
2800 : {
2801 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
2802 1 : EXPECT_NE(singleton.GetInfo("raster"), nullptr);
2803 1 : EXPECT_EQ(singleton.GetInfo("not_existing"), nullptr);
2804 2 : auto alg = singleton.Instantiate("raster");
2805 1 : ASSERT_NE(alg, nullptr);
2806 1 : EXPECT_TRUE(!alg->GetUsageAsJSON().empty());
2807 :
2808 : {
2809 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2810 1 : EXPECT_FALSE(singleton.Register<MyRedundantRasterAlgorithm>());
2811 : }
2812 :
2813 1 : EXPECT_TRUE(singleton.Register<MyAlgorithmWithAlias>());
2814 : {
2815 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2816 1 : EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantAlias>());
2817 : }
2818 : {
2819 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
2820 1 : EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantHiddenAlias>());
2821 : }
2822 : }
2823 :
2824 4 : TEST_F(test_gdal_algorithm, vector_pipeline_GetUsageForCLI)
2825 : {
2826 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
2827 2 : auto vector = singleton.Instantiate("vector");
2828 1 : ASSERT_NE(vector, nullptr);
2829 2 : auto pipeline = vector->InstantiateSubAlgorithm("pipeline");
2830 1 : ASSERT_NE(pipeline, nullptr);
2831 1 : pipeline->GetUsageForCLI(false);
2832 1 : pipeline->GetUsageForCLI(true);
2833 : }
2834 :
2835 4 : TEST_F(test_gdal_algorithm, raster_pipeline_GetUsageForCLI)
2836 : {
2837 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
2838 2 : auto raster = singleton.Instantiate("raster");
2839 1 : ASSERT_NE(raster, nullptr);
2840 2 : auto pipeline = raster->InstantiateSubAlgorithm("pipeline");
2841 1 : ASSERT_NE(pipeline, nullptr);
2842 1 : pipeline->GetUsageForCLI(false);
2843 1 : pipeline->GetUsageForCLI(true);
2844 : }
2845 :
2846 4 : TEST_F(test_gdal_algorithm, registry_c_api)
2847 : {
2848 1 : auto reg = GDALGetGlobalAlgorithmRegistry();
2849 1 : ASSERT_NE(reg, nullptr);
2850 1 : char **names = GDALAlgorithmRegistryGetAlgNames(reg);
2851 1 : EXPECT_GE(CSLCount(names), 2);
2852 1 : CSLDestroy(names);
2853 1 : auto alg = GDALAlgorithmRegistryInstantiateAlg(reg, "raster");
2854 1 : ASSERT_NE(alg, nullptr);
2855 1 : EXPECT_EQ(GDALAlgorithmRegistryInstantiateAlg(reg, "not_existing"),
2856 : nullptr);
2857 1 : GDALAlgorithmRelease(alg);
2858 1 : GDALAlgorithmRegistryRelease(reg);
2859 : }
2860 :
2861 4 : TEST_F(test_gdal_algorithm, algorithm_c_api)
2862 : {
2863 : class MyAlgorithm : public GDALAlgorithm
2864 : {
2865 : public:
2866 : bool m_flag = false;
2867 : std::string m_str{};
2868 : int m_int = 0;
2869 : double m_double = 0;
2870 : std::vector<std::string> m_strlist{};
2871 : std::vector<int> m_intlist{};
2872 : std::vector<double> m_doublelist{};
2873 : GDALArgDatasetValue m_dsValue{};
2874 :
2875 : bool m_hasParsedCommandLinearguments = false;
2876 : bool m_hasRun = false;
2877 : bool m_hasFinalized = false;
2878 :
2879 1 : MyAlgorithm()
2880 1 : : GDALAlgorithm("test", "description", "http://example.com")
2881 : {
2882 1 : m_longDescription = "long description";
2883 1 : AddArg("flag", 'f', "boolean flag", &m_flag);
2884 1 : AddArg("str", 0, "str", &m_str);
2885 1 : AddArg("int", 0, "int", &m_int);
2886 1 : AddArg("double", 0, "double", &m_double);
2887 1 : AddArg("strlist", 0, "strlist", &m_strlist);
2888 1 : AddArg("doublelist", 0, "doublelist", &m_doublelist);
2889 1 : AddArg("intlist", 0, "intlist", &m_intlist);
2890 1 : AddArg("dataset", 0, "dataset", &m_dsValue);
2891 1 : }
2892 :
2893 : bool
2894 1 : ParseCommandLineArguments(const std::vector<std::string> &args) override
2895 : {
2896 1 : m_hasParsedCommandLinearguments = true;
2897 1 : return GDALAlgorithm::ParseCommandLineArguments(args);
2898 : }
2899 :
2900 1 : bool RunImpl(GDALProgressFunc, void *) override
2901 : {
2902 1 : m_hasRun = true;
2903 1 : return true;
2904 : }
2905 :
2906 1 : bool Finalize() override
2907 : {
2908 1 : m_hasFinalized = true;
2909 1 : return GDALAlgorithm::Finalize();
2910 : }
2911 : };
2912 :
2913 : auto hAlg =
2914 1 : std::make_unique<GDALAlgorithmHS>(std::make_unique<MyAlgorithm>());
2915 1 : MyAlgorithm *pAlg = cpl::down_cast<MyAlgorithm *>(hAlg->ptr);
2916 1 : EXPECT_STREQ(GDALAlgorithmGetName(hAlg.get()), "test");
2917 1 : EXPECT_STREQ(GDALAlgorithmGetDescription(hAlg.get()), "description");
2918 1 : EXPECT_STREQ(GDALAlgorithmGetLongDescription(hAlg.get()),
2919 : "long description");
2920 1 : EXPECT_STREQ(GDALAlgorithmGetHelpFullURL(hAlg.get()), "http://example.com");
2921 1 : EXPECT_FALSE(GDALAlgorithmHasSubAlgorithms(hAlg.get()));
2922 1 : EXPECT_EQ(GDALAlgorithmGetSubAlgorithmNames(hAlg.get()), nullptr);
2923 1 : EXPECT_EQ(GDALAlgorithmInstantiateSubAlgorithm(hAlg.get(), "not_existing"),
2924 : nullptr);
2925 3 : EXPECT_TRUE(GDALAlgorithmParseCommandLineArguments(
2926 : hAlg.get(), CPLStringList(std::vector<std::string>({"-f"})).List()));
2927 1 : EXPECT_TRUE(pAlg->m_hasParsedCommandLinearguments);
2928 1 : EXPECT_TRUE(GDALAlgorithmRun(hAlg.get(), nullptr, nullptr));
2929 1 : EXPECT_TRUE(pAlg->m_hasRun);
2930 1 : EXPECT_TRUE(GDALAlgorithmFinalize(hAlg.get()));
2931 1 : EXPECT_TRUE(pAlg->m_hasFinalized);
2932 1 : char *jsonUsage = GDALAlgorithmGetUsageAsJSON(hAlg.get());
2933 1 : EXPECT_NE(jsonUsage, nullptr);
2934 1 : CPLFree(jsonUsage);
2935 :
2936 1 : char **argNames = GDALAlgorithmGetArgNames(hAlg.get());
2937 1 : ASSERT_NE(argNames, nullptr);
2938 1 : EXPECT_EQ(CSLCount(argNames), 13);
2939 1 : CSLDestroy(argNames);
2940 :
2941 1 : EXPECT_EQ(GDALAlgorithmGetArg(hAlg.get(), "non_existing"), nullptr);
2942 : {
2943 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "flag");
2944 1 : ASSERT_NE(hArg, nullptr);
2945 1 : GDALAlgorithmArgSetAsBoolean(hArg, true);
2946 1 : EXPECT_TRUE(GDALAlgorithmArgGetAsBoolean(hArg));
2947 1 : GDALAlgorithmArgRelease(hArg);
2948 : }
2949 : {
2950 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "str");
2951 1 : ASSERT_NE(hArg, nullptr);
2952 1 : GDALAlgorithmArgSetAsString(hArg, "foo");
2953 1 : EXPECT_STREQ(GDALAlgorithmArgGetAsString(hArg), "foo");
2954 1 : GDALAlgorithmArgRelease(hArg);
2955 : }
2956 : {
2957 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "int");
2958 1 : ASSERT_NE(hArg, nullptr);
2959 1 : GDALAlgorithmArgSetAsInteger(hArg, 2);
2960 1 : EXPECT_EQ(GDALAlgorithmArgGetAsInteger(hArg), 2);
2961 1 : GDALAlgorithmArgRelease(hArg);
2962 : }
2963 : {
2964 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "double");
2965 1 : ASSERT_NE(hArg, nullptr);
2966 1 : GDALAlgorithmArgSetAsDouble(hArg, 2.5);
2967 1 : EXPECT_EQ(GDALAlgorithmArgGetAsDouble(hArg), 2.5);
2968 1 : GDALAlgorithmArgRelease(hArg);
2969 : }
2970 : {
2971 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "strlist");
2972 1 : ASSERT_NE(hArg, nullptr);
2973 6 : const CPLStringList list(std::vector<std::string>({"foo", "bar"}));
2974 1 : GDALAlgorithmArgSetAsStringList(hArg, list.List());
2975 1 : char **ret = GDALAlgorithmArgGetAsStringList(hArg);
2976 1 : EXPECT_EQ(CSLCount(ret), 2);
2977 1 : CSLDestroy(ret);
2978 1 : GDALAlgorithmArgRelease(hArg);
2979 : }
2980 : {
2981 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "intlist");
2982 1 : ASSERT_NE(hArg, nullptr);
2983 1 : std::vector<int> vals{2, 3};
2984 1 : GDALAlgorithmArgSetAsIntegerList(hArg, vals.size(), vals.data());
2985 1 : size_t nCount = 0;
2986 1 : const int *ret = GDALAlgorithmArgGetAsIntegerList(hArg, &nCount);
2987 1 : ASSERT_EQ(nCount, 2);
2988 1 : ASSERT_NE(ret, nullptr);
2989 1 : EXPECT_EQ(ret[0], 2);
2990 1 : EXPECT_EQ(ret[1], 3);
2991 1 : GDALAlgorithmArgRelease(hArg);
2992 : }
2993 : {
2994 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "doublelist");
2995 1 : ASSERT_NE(hArg, nullptr);
2996 1 : std::vector<double> vals{2.5, 3.5};
2997 1 : GDALAlgorithmArgSetAsDoubleList(hArg, vals.size(), vals.data());
2998 1 : size_t nCount = 0;
2999 1 : const double *ret = GDALAlgorithmArgGetAsDoubleList(hArg, &nCount);
3000 1 : ASSERT_EQ(nCount, 2);
3001 1 : ASSERT_NE(ret, nullptr);
3002 1 : EXPECT_EQ(ret[0], 2.5);
3003 1 : EXPECT_EQ(ret[1], 3.5);
3004 1 : GDALAlgorithmArgRelease(hArg);
3005 : }
3006 : {
3007 1 : auto hArg = GDALAlgorithmGetArg(hAlg.get(), "dataset");
3008 1 : ASSERT_NE(hArg, nullptr);
3009 1 : GDALArgDatasetValueH hVal = GDALArgDatasetValueCreate();
3010 1 : EXPECT_EQ(GDALArgDatasetValueGetType(hVal),
3011 : GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
3012 1 : EXPECT_EQ(GDALArgDatasetValueGetInputFlags(hVal),
3013 : GADV_NAME | GADV_OBJECT);
3014 1 : EXPECT_EQ(GDALArgDatasetValueGetOutputFlags(hVal), GADV_OBJECT);
3015 1 : GDALArgDatasetValueSetName(hVal, "foo");
3016 :
3017 : {
3018 : auto poDS = std::unique_ptr<GDALDataset>(
3019 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
3020 2 : "", 1, 1, 1, GDT_Byte, nullptr));
3021 1 : GDALArgDatasetValueSetDataset(hVal, poDS.release());
3022 : }
3023 :
3024 1 : GDALAlgorithmArgSetAsDatasetValue(hArg, hVal);
3025 1 : GDALArgDatasetValueRelease(hVal);
3026 :
3027 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
3028 1 : ASSERT_NE(hVal, nullptr);
3029 1 : auto hDS = GDALArgDatasetValueGetDatasetRef(hVal);
3030 1 : EXPECT_NE(hDS, nullptr);
3031 : {
3032 1 : auto hDS2 = GDALArgDatasetValueGetDatasetIncreaseRefCount(hVal);
3033 1 : EXPECT_EQ(hDS2, hDS);
3034 1 : GDALReleaseDataset(hDS2);
3035 : }
3036 1 : GDALArgDatasetValueRelease(hVal);
3037 :
3038 1 : GDALAlgorithmArgSetDataset(hArg, nullptr);
3039 :
3040 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
3041 1 : ASSERT_NE(hVal, nullptr);
3042 1 : EXPECT_EQ(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
3043 1 : GDALArgDatasetValueRelease(hVal);
3044 :
3045 : {
3046 : auto poDS = std::unique_ptr<GDALDataset>(
3047 : GetGDALDriverManager()->GetDriverByName("MEM")->Create(
3048 2 : "", 1, 1, 1, GDT_Byte, nullptr));
3049 1 : GDALAlgorithmArgSetDataset(hArg, poDS.release());
3050 : }
3051 :
3052 1 : hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
3053 1 : ASSERT_NE(hVal, nullptr);
3054 1 : EXPECT_NE(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
3055 1 : GDALArgDatasetValueRelease(hVal);
3056 :
3057 1 : GDALAlgorithmArgRelease(hArg);
3058 : }
3059 : }
3060 :
3061 4 : TEST_F(test_gdal_algorithm, DispatcherGetUsageForCLI)
3062 : {
3063 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3064 : {
3065 2 : auto info = singleton.Instantiate("info");
3066 1 : info->GetUsageForCLI(false);
3067 : }
3068 : {
3069 2 : auto info = singleton.Instantiate("info");
3070 3 : EXPECT_TRUE(info->ParseCommandLineArguments(
3071 : std::vector<std::string>{GCORE_DATA_DIR "byte.tif"}));
3072 1 : info->GetUsageForCLI(false);
3073 : }
3074 : {
3075 1 : auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
3076 1 : if (!poDriver)
3077 : {
3078 0 : GTEST_SKIP() << "GPKG support missing";
3079 : }
3080 : else
3081 : {
3082 : std::string osTmpFilename =
3083 2 : VSIMemGenerateHiddenFilename("temp.gpkg");
3084 : auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
3085 2 : osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, nullptr));
3086 1 : double adfGT[] = {1, 1, 0, 1, 0, -1};
3087 1 : poDS->SetGeoTransform(adfGT);
3088 1 : poDS->CreateLayer("foo");
3089 1 : poDS.reset();
3090 :
3091 3 : auto info = singleton.Instantiate("info");
3092 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3093 3 : EXPECT_FALSE(info->ParseCommandLineArguments(
3094 : std::vector<std::string>{osTmpFilename.c_str()}));
3095 1 : info->GetUsageForCLI(false);
3096 :
3097 1 : VSIUnlink(osTmpFilename.c_str());
3098 : }
3099 : }
3100 : }
3101 :
3102 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_dataset_0_0)
3103 : {
3104 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3105 2 : auto raster = singleton.Instantiate("raster");
3106 1 : ASSERT_NE(raster, nullptr);
3107 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3108 1 : ASSERT_NE(edit, nullptr);
3109 :
3110 : class MyDataset : public GDALDataset
3111 : {
3112 : public:
3113 1 : MyDataset()
3114 1 : {
3115 1 : nRasterXSize = 0;
3116 1 : nRasterYSize = 0;
3117 1 : eAccess = GA_Update;
3118 1 : }
3119 : };
3120 :
3121 1 : auto datasetArg = edit->GetArg("dataset");
3122 1 : ASSERT_NE(datasetArg, nullptr);
3123 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3124 :
3125 1 : auto extentArg = edit->GetArg("bbox");
3126 1 : ASSERT_NE(extentArg, nullptr);
3127 1 : extentArg->Set(std::vector<double>{2, 49, 3, 50});
3128 :
3129 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3130 1 : CPLErrorReset();
3131 1 : EXPECT_FALSE(edit->Run());
3132 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3133 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3134 : "edit: Cannot set extent because dataset has 0x0 dimension");
3135 : }
3136 :
3137 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_none)
3138 : {
3139 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3140 2 : auto raster = singleton.Instantiate("raster");
3141 1 : ASSERT_NE(raster, nullptr);
3142 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3143 1 : ASSERT_NE(edit, nullptr);
3144 :
3145 : class MyDataset : public GDALDataset
3146 : {
3147 : public:
3148 1 : MyDataset()
3149 1 : {
3150 1 : eAccess = GA_Update;
3151 1 : }
3152 :
3153 1 : CPLErr SetSpatialRef(const OGRSpatialReference *) override
3154 : {
3155 1 : return CE_Failure;
3156 : }
3157 : };
3158 :
3159 1 : auto datasetArg = edit->GetArg("dataset");
3160 1 : ASSERT_NE(datasetArg, nullptr);
3161 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3162 :
3163 1 : auto crsArg = edit->GetArg("crs");
3164 1 : ASSERT_NE(crsArg, nullptr);
3165 1 : crsArg->Set("none");
3166 :
3167 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3168 1 : CPLErrorReset();
3169 1 : EXPECT_FALSE(edit->Run());
3170 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3171 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: SetSpatialRef(none) failed");
3172 : }
3173 :
3174 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_regular)
3175 : {
3176 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3177 2 : auto raster = singleton.Instantiate("raster");
3178 1 : ASSERT_NE(raster, nullptr);
3179 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3180 1 : ASSERT_NE(edit, nullptr);
3181 :
3182 : class MyDataset : public GDALDataset
3183 : {
3184 : public:
3185 1 : MyDataset()
3186 1 : {
3187 1 : eAccess = GA_Update;
3188 1 : }
3189 :
3190 1 : CPLErr SetSpatialRef(const OGRSpatialReference *) override
3191 : {
3192 1 : return CE_Failure;
3193 : }
3194 : };
3195 :
3196 1 : auto datasetArg = edit->GetArg("dataset");
3197 1 : ASSERT_NE(datasetArg, nullptr);
3198 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3199 :
3200 1 : auto crsArg = edit->GetArg("crs");
3201 1 : ASSERT_NE(crsArg, nullptr);
3202 1 : crsArg->Set("EPSG:32632");
3203 :
3204 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3205 1 : CPLErrorReset();
3206 1 : EXPECT_FALSE(edit->Run());
3207 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3208 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3209 : "edit: SetSpatialRef(EPSG:32632) failed");
3210 : }
3211 :
3212 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_geo_transform)
3213 : {
3214 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3215 2 : auto raster = singleton.Instantiate("raster");
3216 1 : ASSERT_NE(raster, nullptr);
3217 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3218 1 : ASSERT_NE(edit, nullptr);
3219 :
3220 : class MyDataset : public GDALDataset
3221 : {
3222 : public:
3223 1 : MyDataset()
3224 1 : {
3225 1 : eAccess = GA_Update;
3226 1 : }
3227 :
3228 1 : CPLErr SetGeoTransform(double *) override
3229 : {
3230 1 : return CE_Failure;
3231 : }
3232 : };
3233 :
3234 1 : auto datasetArg = edit->GetArg("dataset");
3235 1 : ASSERT_NE(datasetArg, nullptr);
3236 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3237 :
3238 1 : auto extentArg = edit->GetArg("bbox");
3239 1 : ASSERT_NE(extentArg, nullptr);
3240 1 : extentArg->Set(std::vector<double>{2, 49, 3, 50});
3241 :
3242 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3243 1 : CPLErrorReset();
3244 1 : EXPECT_FALSE(edit->Run());
3245 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3246 1 : EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Setting extent failed");
3247 : }
3248 :
3249 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_metadata)
3250 : {
3251 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3252 2 : auto raster = singleton.Instantiate("raster");
3253 1 : ASSERT_NE(raster, nullptr);
3254 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3255 1 : ASSERT_NE(edit, nullptr);
3256 :
3257 : class MyDataset : public GDALDataset
3258 : {
3259 : public:
3260 1 : MyDataset()
3261 1 : {
3262 1 : eAccess = GA_Update;
3263 1 : }
3264 :
3265 1 : CPLErr SetMetadataItem(const char *, const char *,
3266 : const char *) override
3267 : {
3268 1 : return CE_Failure;
3269 : }
3270 : };
3271 :
3272 1 : auto datasetArg = edit->GetArg("dataset");
3273 1 : ASSERT_NE(datasetArg, nullptr);
3274 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3275 :
3276 1 : auto extentArg = edit->GetArg("metadata");
3277 1 : ASSERT_NE(extentArg, nullptr);
3278 2 : extentArg->Set(std::vector<std::string>{"foo=bar"});
3279 :
3280 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3281 1 : CPLErrorReset();
3282 1 : EXPECT_FALSE(edit->Run());
3283 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3284 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3285 : "edit: SetMetadataItem('foo', 'bar') failed");
3286 : }
3287 :
3288 4 : TEST_F(test_gdal_algorithm, raster_edit_failures_unset_metadata)
3289 : {
3290 1 : auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
3291 2 : auto raster = singleton.Instantiate("raster");
3292 1 : ASSERT_NE(raster, nullptr);
3293 2 : auto edit = raster->InstantiateSubAlgorithm("edit");
3294 1 : ASSERT_NE(edit, nullptr);
3295 :
3296 : class MyDataset : public GDALDataset
3297 : {
3298 : public:
3299 1 : MyDataset()
3300 1 : {
3301 1 : eAccess = GA_Update;
3302 1 : }
3303 :
3304 1 : CPLErr SetMetadataItem(const char *, const char *,
3305 : const char *) override
3306 : {
3307 1 : return CE_Failure;
3308 : }
3309 : };
3310 :
3311 1 : auto datasetArg = edit->GetArg("dataset");
3312 1 : ASSERT_NE(datasetArg, nullptr);
3313 1 : datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
3314 :
3315 1 : auto extentArg = edit->GetArg("unset-metadata");
3316 1 : ASSERT_NE(extentArg, nullptr);
3317 2 : extentArg->Set(std::vector<std::string>{"foo"});
3318 :
3319 2 : CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
3320 1 : CPLErrorReset();
3321 1 : EXPECT_FALSE(edit->Run());
3322 1 : EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
3323 1 : EXPECT_STREQ(CPLGetLastErrorMsg(),
3324 : "edit: SetMetadataItem('foo', NULL) failed");
3325 : }
3326 :
3327 : } // namespace test_gdal_algorithm
|