LCOV - code coverage report
Current view: top level - autotest/cpp - test_gdal_algorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2911 2916 99.8 %
Date: 2026-09-11 05:09:32 Functions: 585 586 99.8 %

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

Generated by: LCOV version 1.14