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

Generated by: LCOV version 1.14