LCOV - code coverage report
Current view: top level - autotest/cpp - test_gdal_algorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2569 2574 99.8 %
Date: 2025-07-09 17:50:03 Functions: 536 537 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         208 :     MyAlgorithmWithDummyRun(const std::string &name = "test",
     119             :                             const std::string &description = "",
     120             :                             const std::string &url = "https://example.com")
     121         208 :         : GDALAlgorithm(name, description, url)
     122             :     {
     123         208 :     }
     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["), 0);
     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.SetDatasetInputFlags(GADV_NAME);
     657           1 :         decl.SetDatasetOutputFlags(GADV_OBJECT);
     658           2 :         auto arg = GDALAlgorithmArg(decl, &val);
     659             : 
     660             :         {
     661           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     662           1 :             CPLErrorReset();
     663           1 :             arg.Set(static_cast<GDALDataset *>(nullptr));
     664           1 :             EXPECT_TRUE(strstr(
     665             :                 CPLGetLastErrorMsg(),
     666             :                 "is created by algorithm and cannot be set as an input"));
     667           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     668             :         }
     669             : 
     670             :         {
     671           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     672           1 :             CPLErrorReset();
     673           1 :             arg.Set(std::unique_ptr<GDALDataset>(nullptr));
     674           1 :             EXPECT_TRUE(strstr(
     675             :                 CPLGetLastErrorMsg(),
     676             :                 "is created by algorithm and cannot be set as an input"));
     677           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     678             :         }
     679             : 
     680             :         {
     681           2 :             GDALArgDatasetValue val2;
     682           1 :             val2.Set(std::unique_ptr<GDALDataset>(
     683             :                 GetGDALDriverManager()->GetDriverByName("MEM")->Create(
     684             :                     "", 1, 1, 1, GDT_Byte, nullptr)));
     685             : 
     686           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     687           1 :             CPLErrorReset();
     688           1 :             arg.SetFrom(val2);
     689           1 :             EXPECT_TRUE(strstr(
     690             :                 CPLGetLastErrorMsg(),
     691             :                 "is created by algorithm and cannot be set as an input"));
     692           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     693             :         }
     694             :     }
     695             : 
     696             :     {
     697           2 :         GDALArgDatasetValue val;
     698           3 :         auto decl = GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET);
     699           1 :         decl.SetDatasetInputFlags(0);
     700           1 :         decl.SetDatasetOutputFlags(0);
     701           2 :         auto arg = GDALAlgorithmArg(decl, &val);
     702             : 
     703             :         {
     704           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     705           1 :             CPLErrorReset();
     706           1 :             arg.Set(static_cast<GDALDataset *>(nullptr));
     707           1 :             EXPECT_TRUE(
     708             :                 strstr(CPLGetLastErrorMsg(),
     709             :                        "A dataset cannot be set as an input argument of"));
     710           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     711             :         }
     712             :     }
     713             : 
     714             :     {
     715             :         class MyAlgorithm : public MyAlgorithmWithDummyRun
     716             :         {
     717             :           public:
     718           1 :             MyAlgorithm()
     719           1 :             {
     720           2 :                 GDALArgDatasetValue val;
     721           1 :                 AddArg("", 0, "", &val).SetDatasetInputFlags(0);
     722             : 
     723           2 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     724           1 :                 CPLErrorReset();
     725             : 
     726           1 :                 val.Set(std::unique_ptr<GDALDataset>(
     727             :                     GetGDALDriverManager()->GetDriverByName("MEM")->Create(
     728             :                         "", 1, 1, 1, GDT_Byte, nullptr)));
     729             : 
     730           1 :                 Run();
     731             : 
     732           1 :                 EXPECT_TRUE(
     733             :                     strstr(CPLGetLastErrorMsg(),
     734             :                            "A dataset cannot be set as an input argument of"));
     735           1 :                 EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     736           1 :             }
     737             :         };
     738             : 
     739           1 :         MyAlgorithm alg;
     740             :     }
     741             : 
     742             :     {
     743           1 :         std::vector<std::string> val;
     744             :         auto arg = GDALAlgorithmArg(
     745           2 :             GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val);
     746             :         {
     747           6 :             const std::vector<std::string> expected{"foo", "bar"};
     748           1 :             arg.Set(expected);
     749           1 :             EXPECT_EQ(arg.Get<std::vector<std::string>>(), expected);
     750           1 :             EXPECT_EQ(val, expected);
     751             : 
     752           2 :             std::vector<std::string> val2;
     753             :             auto arg2 = GDALAlgorithmArg(
     754           3 :                 GDALAlgorithmArgDecl("", 0, "", GAAT_STRING_LIST), &val2);
     755           1 :             arg2.SetFrom(arg);
     756           1 :             EXPECT_EQ(arg2.Get<std::vector<std::string>>(), expected);
     757             : 
     758             :             {
     759           2 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     760           1 :                 CPLErrorReset();
     761           1 :                 arg.Set(true);
     762           1 :                 EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     763           1 :                 EXPECT_EQ(val, expected);
     764             :             }
     765             :         }
     766             : 
     767             :         {
     768           1 :             arg.Set(1);
     769           5 :             const std::vector<std::string> expected{"1"};
     770           1 :             EXPECT_EQ(val, expected);
     771             :         }
     772             : 
     773             :         {
     774           1 :             arg.Set("1");
     775           5 :             const std::vector<std::string> expected{"1"};
     776           1 :             EXPECT_EQ(val, expected);
     777             :         }
     778             : 
     779             :         {
     780           1 :             arg.Set(std::vector<int>{1, 2});
     781           6 :             const std::vector<std::string> expected{"1", "2"};
     782           1 :             EXPECT_EQ(val, expected);
     783             :         }
     784             : 
     785             :         {
     786           1 :             arg.Set(3.5);
     787           1 :             ASSERT_EQ(val.size(), 1);
     788           1 :             EXPECT_EQ(CPLAtof(val[0].c_str()), 3.5);
     789             :         }
     790             : 
     791             :         {
     792           1 :             arg.Set(std::vector<double>{1.5, 2.5});
     793           1 :             ASSERT_EQ(val.size(), 2);
     794           1 :             EXPECT_EQ(CPLAtof(val[0].c_str()), 1.5);
     795           1 :             EXPECT_EQ(CPLAtof(val[1].c_str()), 2.5);
     796             :         }
     797             : 
     798             :         {
     799           3 :             arg = std::vector<std::string>{"foo", "bar"};
     800           6 :             const std::vector<std::string> expected{"foo", "bar"};
     801           1 :             EXPECT_EQ(val, expected);
     802             :         }
     803             :     }
     804             :     {
     805           2 :         std::vector<int> val;
     806             :         auto arg = GDALAlgorithmArg(
     807           3 :             GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val);
     808             :         {
     809           2 :             const std::vector<int> expected{1, 2};
     810           1 :             arg.Set(expected);
     811           1 :             EXPECT_EQ(arg.Get<std::vector<int>>(), expected);
     812           1 :             EXPECT_EQ(val, expected);
     813             : 
     814           2 :             std::vector<int> val2;
     815             :             auto arg2 = GDALAlgorithmArg(
     816           3 :                 GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER_LIST), &val2);
     817           1 :             arg2.SetFrom(arg);
     818           1 :             EXPECT_EQ(arg2.Get<std::vector<int>>(), expected);
     819             : 
     820             :             {
     821           2 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     822           1 :                 CPLErrorReset();
     823           1 :                 arg.Set(true);
     824           1 :                 EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     825           1 :                 EXPECT_EQ(val, expected);
     826             :             }
     827             :         }
     828             : 
     829             :         {
     830           1 :             arg.Set(3);
     831           2 :             const std::vector<int> expected{3};
     832           1 :             EXPECT_EQ(val, expected);
     833             :         }
     834             : 
     835             :         {
     836           1 :             arg.Set(4.0);
     837           2 :             const std::vector<int> expected{4};
     838           1 :             EXPECT_EQ(val, expected);
     839             :         }
     840             : 
     841             :         {
     842           1 :             arg.Set("5");
     843           2 :             const std::vector<int> expected{5};
     844           1 :             EXPECT_EQ(val, expected);
     845             :         }
     846             : 
     847             :         {
     848           1 :             arg.Set(std::vector<double>{6.0});
     849           2 :             const std::vector<int> expected{6};
     850           1 :             EXPECT_EQ(val, expected);
     851             :         }
     852             : 
     853             :         {
     854           2 :             arg.Set(std::vector<std::string>{"7"});
     855           2 :             const std::vector<int> expected{7};
     856           1 :             EXPECT_EQ(val, expected);
     857             :         }
     858             : 
     859             :         {
     860           1 :             arg = std::vector<int>{4, 5};
     861           2 :             const std::vector<int> expected{4, 5};
     862           1 :             EXPECT_EQ(val, expected);
     863             :         }
     864             : 
     865             :         {
     866           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     867           1 :             CPLErrorReset();
     868             : 
     869           1 :             arg.Set(6.5);
     870           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     871             :         }
     872             : 
     873             :         {
     874           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     875           1 :             CPLErrorReset();
     876             : 
     877           1 :             arg.Set("foo");
     878           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     879             :         }
     880             : 
     881             :         {
     882           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     883           1 :             CPLErrorReset();
     884             : 
     885           1 :             arg.Set("12345679812346798123456");
     886           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     887             :         }
     888             : 
     889             :         {
     890           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     891           1 :             CPLErrorReset();
     892             : 
     893           1 :             arg.Set(std::vector<double>{6.5});
     894           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     895             :         }
     896             : 
     897             :         {
     898           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     899           1 :             CPLErrorReset();
     900             : 
     901           2 :             arg.Set(std::vector<std::string>{"foo"});
     902           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     903             :         }
     904             : 
     905             :         {
     906           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     907           1 :             CPLErrorReset();
     908             : 
     909           2 :             arg.Set(std::vector<std::string>{"12345679812346798123456"});
     910           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     911             :         }
     912             :     }
     913             :     {
     914           2 :         std::vector<double> val;
     915             :         auto arg = GDALAlgorithmArg(
     916           3 :             GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val);
     917             :         {
     918           2 :             const std::vector<double> expected{1.5, 2.5};
     919           1 :             arg.Set(expected);
     920           1 :             EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
     921           1 :             EXPECT_EQ(val, expected);
     922             : 
     923           2 :             std::vector<double> val2;
     924             :             auto arg2 = GDALAlgorithmArg(
     925           3 :                 GDALAlgorithmArgDecl("", 0, "", GAAT_REAL_LIST), &val2);
     926           1 :             arg2.SetFrom(arg);
     927           1 :             EXPECT_EQ(arg2.Get<std::vector<double>>(), expected);
     928             : 
     929             :             {
     930           2 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     931           1 :                 CPLErrorReset();
     932           1 :                 arg.Set(true);
     933           1 :                 EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     934           1 :                 EXPECT_EQ(arg.Get<std::vector<double>>(), expected);
     935             :             }
     936             :         }
     937             : 
     938             :         {
     939           1 :             arg.Set(3);
     940           2 :             const std::vector<double> expected{3.0};
     941           1 :             EXPECT_EQ(val, expected);
     942             :         }
     943             : 
     944             :         {
     945           1 :             arg.Set("4.5");
     946           2 :             const std::vector<double> expected{4.5};
     947           1 :             EXPECT_EQ(val, expected);
     948             :         }
     949             : 
     950             :         {
     951           1 :             arg.Set(std::vector<int>{5});
     952           2 :             const std::vector<double> expected{5.0};
     953           1 :             EXPECT_EQ(val, expected);
     954             :         }
     955             : 
     956             :         {
     957           1 :             arg.Set(std::vector<double>{6.5});
     958           2 :             const std::vector<double> expected{6.5};
     959           1 :             EXPECT_EQ(val, expected);
     960             :         }
     961             : 
     962             :         {
     963           2 :             arg.Set(std::vector<std::string>{"7.5"});
     964           2 :             const std::vector<double> expected{7.5};
     965           1 :             EXPECT_EQ(val, expected);
     966             :         }
     967             : 
     968             :         {
     969           1 :             arg = std::vector<double>{4, 5};
     970           2 :             const std::vector<double> expected{4, 5};
     971           1 :             EXPECT_EQ(val, expected);
     972             :         }
     973             : 
     974             :         {
     975           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     976           1 :             CPLErrorReset();
     977             : 
     978           1 :             arg.Set("foo");
     979           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     980             :         }
     981             : 
     982             :         {
     983           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     984           1 :             CPLErrorReset();
     985             : 
     986           2 :             arg.Set(std::vector<std::string>{"foo"});
     987           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
     988             :         }
     989             :     }
     990             :     {
     991           2 :         std::vector<GDALArgDatasetValue> val;
     992             :         auto arg = GDALAlgorithmArg(
     993           3 :             GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val);
     994             :         {
     995           2 :             std::vector<GDALArgDatasetValue> val2;
     996           1 :             val2.emplace_back(GDALArgDatasetValue("foo"));
     997           1 :             val2.emplace_back(GDALArgDatasetValue("bar"));
     998           1 :             arg.Set(std::move(val2));
     999           1 :             EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
    1000           1 :             EXPECT_EQ(val.size(), 2U);
    1001             :         }
    1002             : 
    1003           2 :         std::vector<GDALArgDatasetValue> val2;
    1004             :         auto arg2 = GDALAlgorithmArg(
    1005           3 :             GDALAlgorithmArgDecl("", 0, "", GAAT_DATASET_LIST), &val2);
    1006           1 :         arg2.SetFrom(arg);
    1007           1 :         EXPECT_EQ(arg2.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
    1008             : 
    1009             :         {
    1010           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1011           1 :             CPLErrorReset();
    1012           1 :             arg.Set(true);
    1013           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1014           1 :             EXPECT_EQ(arg.Get<std::vector<GDALArgDatasetValue>>().size(), 2U);
    1015             :         }
    1016             :     }
    1017             : }
    1018             : 
    1019           4 : TEST_F(test_gdal_algorithm, RunValidationActions)
    1020             : {
    1021           1 :     int val = 0;
    1022             :     auto arg = GDALInConstructionAlgorithmArg(
    1023           3 :         nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
    1024           3 :     arg.AddValidationAction([&arg]() { return arg.Get<int>() == 1; });
    1025           1 :     EXPECT_TRUE(arg.Set(1));
    1026           1 :     EXPECT_FALSE(arg.Set(2));
    1027           1 : }
    1028             : 
    1029           4 : TEST_F(test_gdal_algorithm, SetIsCRSArg_wrong_type)
    1030             : {
    1031           1 :     int val = 0;
    1032             :     auto arg = GDALInConstructionAlgorithmArg(
    1033           3 :         nullptr, GDALAlgorithmArgDecl("", 0, "", GAAT_INTEGER), &val);
    1034             :     {
    1035           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1036           1 :         CPLErrorReset();
    1037           1 :         arg.SetIsCRSArg();
    1038           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1039             :     }
    1040           1 : }
    1041             : 
    1042           4 : TEST_F(test_gdal_algorithm, wrong_long_name_dash)
    1043             : {
    1044             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1045             :     {
    1046             :       public:
    1047             :         bool m_flag = false;
    1048             : 
    1049           1 :         MyAlgorithm()
    1050           1 :         {
    1051           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1052           1 :             CPLErrorReset();
    1053           1 :             AddArg("-", 0, "", &m_flag);
    1054           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1055           1 :         }
    1056             :     };
    1057             : 
    1058           1 :     MyAlgorithm alg;
    1059           1 :     CPL_IGNORE_RET_VAL(alg.Run());
    1060           1 : }
    1061             : 
    1062           4 : TEST_F(test_gdal_algorithm, wrong_long_name_contains_equal)
    1063             : {
    1064             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1065             :     {
    1066             :       public:
    1067             :         bool m_flag = false;
    1068             : 
    1069           1 :         MyAlgorithm()
    1070           1 :         {
    1071           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1072           1 :             CPLErrorReset();
    1073           1 :             AddArg("foo=bar", 0, "", &m_flag);
    1074           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1075           1 :         }
    1076             :     };
    1077             : 
    1078           1 :     MyAlgorithm alg;
    1079           1 : }
    1080             : 
    1081           4 : TEST_F(test_gdal_algorithm, long_name_duplicated)
    1082             : {
    1083             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1084             :     {
    1085             :       public:
    1086             :         bool m_flag = false;
    1087             : 
    1088           1 :         MyAlgorithm()
    1089           1 :         {
    1090           1 :             AddArg("foo", 0, "", &m_flag);
    1091           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1092           1 :             CPLErrorReset();
    1093           1 :             AddArg("foo", 0, "", &m_flag);
    1094           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1095           1 :         }
    1096             :     };
    1097             : 
    1098           1 :     MyAlgorithm alg;
    1099           1 : }
    1100             : 
    1101           4 : TEST_F(test_gdal_algorithm, wrong_short_name)
    1102             : {
    1103             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1104             :     {
    1105             :       public:
    1106             :         bool m_flag = false;
    1107             : 
    1108           1 :         MyAlgorithm()
    1109           1 :         {
    1110           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1111           1 :             CPLErrorReset();
    1112           1 :             AddArg("", '-', "", &m_flag);
    1113           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1114           1 :         }
    1115             :     };
    1116             : 
    1117           1 :     MyAlgorithm alg;
    1118           1 : }
    1119             : 
    1120           4 : TEST_F(test_gdal_algorithm, short_name_duplicated)
    1121             : {
    1122             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1123             :     {
    1124             :       public:
    1125             :         bool m_flag = false;
    1126             : 
    1127           1 :         MyAlgorithm()
    1128           1 :         {
    1129           1 :             AddArg("", 'x', "", &m_flag);
    1130           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1131           1 :             CPLErrorReset();
    1132           1 :             AddArg("", 'x', "", &m_flag);
    1133           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1134           1 :         }
    1135             :     };
    1136             : 
    1137           1 :     MyAlgorithm alg;
    1138           1 : }
    1139             : 
    1140           4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias)
    1141             : {
    1142             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1143             :     {
    1144             :       public:
    1145             :         bool m_flag = false;
    1146             : 
    1147           1 :         MyAlgorithm()
    1148           1 :         {
    1149           1 :             AddArg("flag", 'f', "boolean flag", &m_flag).AddAlias("alias");
    1150           1 :         }
    1151             :     };
    1152             : 
    1153           2 :     MyAlgorithm alg;
    1154           1 :     alg.GetUsageForCLI(false);
    1155           1 :     EXPECT_NE(alg.GetArg("flag"), nullptr);
    1156           1 :     EXPECT_NE(alg.GetArg("--flag"), nullptr);
    1157           1 :     EXPECT_NE(alg.GetArg("-f"), nullptr);
    1158           1 :     EXPECT_NE(alg.GetArg("f"), nullptr);
    1159           1 :     EXPECT_NE(alg.GetArg("alias"), nullptr);
    1160           1 :     EXPECT_EQ(alg.GetArg("invalid"), nullptr);
    1161           1 :     EXPECT_EQ(alg.GetArg("-"), nullptr);
    1162             : 
    1163           2 :     EXPECT_STREQ(alg["flag"].GetName().c_str(), "flag");
    1164             : 
    1165           1 :     alg["flag"] = true;
    1166           1 :     EXPECT_EQ(alg.m_flag, true);
    1167             : 
    1168           2 :     EXPECT_STREQ(const_cast<const MyAlgorithm &>(alg)["flag"].GetName().c_str(),
    1169             :                  "flag");
    1170             : 
    1171             :     {
    1172           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1173           1 :         CPLErrorReset();
    1174           2 :         EXPECT_STREQ(alg["invalid"].GetName().c_str(), "dummy");
    1175           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1176             :     }
    1177             :     {
    1178           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1179           1 :         CPLErrorReset();
    1180           2 :         EXPECT_STREQ(
    1181             :             const_cast<const MyAlgorithm &>(alg)["invalid"].GetName().c_str(),
    1182             :             "dummy");
    1183           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1184             :     }
    1185             : 
    1186             :     {
    1187           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1188           1 :         CPLErrorReset();
    1189           1 :         EXPECT_EQ(alg.GetArg("flig"), nullptr);
    1190           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    1191             :                      "Argument 'flig' is unknown. Do you mean 'flag'?");
    1192             :     }
    1193             : 
    1194             :     {
    1195           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1196           1 :         CPLErrorReset();
    1197           1 :         EXPECT_EQ(alg.GetArg("flga"), nullptr);
    1198           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    1199             :                      "Argument 'flga' is unknown. Do you mean 'flag'?");
    1200             :     }
    1201           1 : }
    1202             : 
    1203           4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddAlias_redundant)
    1204             : {
    1205             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1206             :     {
    1207             :       public:
    1208             :         bool m_flag = false;
    1209             :         bool m_flag2 = false;
    1210             : 
    1211           1 :         MyAlgorithm()
    1212           1 :         {
    1213           1 :             AddArg("flag", 'F', "boolean flag", &m_flag).AddAlias("alias");
    1214           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1215           1 :             CPLErrorReset();
    1216           1 :             AddArg("flag2", '9', "boolean flag2", &m_flag2).AddAlias("alias");
    1217           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1218           1 :         }
    1219             :     };
    1220             : 
    1221           2 :     MyAlgorithm alg;
    1222           1 :     EXPECT_NE(alg.GetArg("alias"), nullptr);
    1223           1 : }
    1224             : 
    1225           4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_AddHiddenAlias)
    1226             : {
    1227             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1228             :     {
    1229             :       public:
    1230             :         bool m_flag = false;
    1231             : 
    1232           1 :         MyAlgorithm()
    1233           1 :         {
    1234           2 :             AddArg("flag", 'f', "boolean flag", &m_flag)
    1235           1 :                 .AddHiddenAlias("hidden_alias");
    1236           1 :         }
    1237             :     };
    1238             : 
    1239           2 :     MyAlgorithm alg;
    1240           1 :     EXPECT_NE(alg.GetArg("hidden_alias"), nullptr);
    1241           1 : }
    1242             : 
    1243           4 : TEST_F(test_gdal_algorithm, GDALInConstructionAlgorithmArg_SetPositional)
    1244             : {
    1245             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1246             :     {
    1247             :       public:
    1248             :         int m_val = 0;
    1249             : 
    1250           1 :         MyAlgorithm()
    1251           1 :         {
    1252           1 :             AddArg("option", 0, "option", &m_val).SetPositional();
    1253           1 :         }
    1254             :     };
    1255             : 
    1256           2 :     MyAlgorithm alg;
    1257           1 :     EXPECT_TRUE(alg.GetArg("option")->IsPositional());
    1258           1 : }
    1259             : 
    1260           4 : TEST_F(test_gdal_algorithm, GDALArgDatasetValue)
    1261             : {
    1262             :     {
    1263             :         auto poDS = std::unique_ptr<GDALDataset>(
    1264             :             GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    1265           2 :                 "", 1, 1, 1, GDT_Byte, nullptr));
    1266           1 :         auto poDSRaw = poDS.get();
    1267           2 :         GDALArgDatasetValue value(poDS.release());
    1268           1 :         EXPECT_EQ(value.GetDatasetRef(), poDSRaw);
    1269           1 :         EXPECT_STREQ(value.GetName().c_str(), poDSRaw->GetDescription());
    1270             : 
    1271           2 :         GDALArgDatasetValue value2;
    1272           1 :         value2 = std::move(value);
    1273           1 :         EXPECT_STREQ(value2.GetName().c_str(), poDSRaw->GetDescription());
    1274             : 
    1275           1 :         poDSRaw->ReleaseRef();
    1276             :     }
    1277             :     {
    1278           3 :         GDALArgDatasetValue value("foo");
    1279           1 :         EXPECT_STREQ(value.GetName().c_str(), "foo");
    1280             : 
    1281           2 :         GDALArgDatasetValue value2;
    1282           1 :         value2 = std::move(value);
    1283           1 :         EXPECT_STREQ(value2.GetName().c_str(), "foo");
    1284             :     }
    1285           1 : }
    1286             : 
    1287           4 : TEST_F(test_gdal_algorithm, bool_flag)
    1288             : {
    1289             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1290             :     {
    1291             :       public:
    1292             :         bool m_flag = false;
    1293             :         std::string m_dummy{};
    1294             : 
    1295          14 :         MyAlgorithm()
    1296          14 :         {
    1297          14 :             AddArg("flag", 'f', "boolean flag", &m_flag);
    1298          14 :             AddArg("of", 0, "", &m_dummy);
    1299          14 :         }
    1300             :     };
    1301             : 
    1302             :     {
    1303           2 :         MyAlgorithm alg;
    1304           1 :         alg.GetUsageForCLI(true);
    1305           1 :         alg.GetUsageForCLI(false);
    1306           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    1307           1 :         EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "test");
    1308             :     }
    1309             : 
    1310             :     {
    1311           2 :         MyAlgorithm alg;
    1312           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
    1313           1 :         EXPECT_TRUE(alg.m_flag);
    1314             :     }
    1315             : 
    1316             :     {
    1317           2 :         MyAlgorithm alg;
    1318           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=true"}));
    1319           1 :         EXPECT_TRUE(alg.m_flag);
    1320             :     }
    1321             : 
    1322             :     {
    1323           2 :         MyAlgorithm alg;
    1324           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag=false"}));
    1325           1 :         EXPECT_FALSE(alg.m_flag);
    1326             :     }
    1327             : 
    1328             :     {
    1329           2 :         MyAlgorithm alg;
    1330           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1331           1 :         CPLErrorReset();
    1332           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag=invalid"}));
    1333           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1334             :     }
    1335             : 
    1336             :     {
    1337           2 :         MyAlgorithm alg;
    1338           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1339           1 :         CPLErrorReset();
    1340           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag", "--flag"}));
    1341           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1342             :     }
    1343             : 
    1344             :     {
    1345           2 :         MyAlgorithm alg;
    1346           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1347           1 :         CPLErrorReset();
    1348           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--flig=invalid"}));
    1349           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1350           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "test: Option '--flig' is "
    1351             :                                            "unknown. Do you mean '--flag'?");
    1352             :     }
    1353             : 
    1354             :     {
    1355           2 :         MyAlgorithm alg;
    1356           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1357           1 :         CPLErrorReset();
    1358           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-x", "foo"}));
    1359           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1360           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "test: Short name option 'x' is "
    1361             :                                            "unknown.");
    1362             :     }
    1363             : 
    1364             :     {
    1365           2 :         MyAlgorithm alg;
    1366           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1367           1 :         CPLErrorReset();
    1368           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-of", "foo"}));
    1369           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1370           1 :         EXPECT_STREQ(
    1371             :             CPLGetLastErrorMsg(),
    1372             :             "test: Short name option 'o' is "
    1373             :             "unknown. Do you mean '--of' (with leading double dash) ?");
    1374             :     }
    1375             : 
    1376             :     {
    1377           2 :         MyAlgorithm alg;
    1378           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1379           1 :         CPLErrorReset();
    1380           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-ofx", "foo"}));
    1381           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1382           1 :         EXPECT_STREQ(
    1383             :             CPLGetLastErrorMsg(),
    1384             :             "test: Short name option 'o' is "
    1385             :             "unknown. Do you mean '--of' (with leading double dash) ?");
    1386             :     }
    1387             : 
    1388             :     {
    1389           2 :         MyAlgorithm alg;
    1390           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1391           1 :         CPLErrorReset();
    1392           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--invalid"}));
    1393           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1394           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    1395             :                      "test: Option '--invalid' is unknown.");
    1396             :     }
    1397             : 
    1398             :     {
    1399           2 :         MyAlgorithm alg;
    1400           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1401           1 :         CPLErrorReset();
    1402           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-"}));
    1403           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1404             :     }
    1405             : 
    1406             :     {
    1407           2 :         MyAlgorithm alg;
    1408           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1409           1 :         CPLErrorReset();
    1410           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-x"}));
    1411           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1412             :     }
    1413             : 
    1414             :     {
    1415           2 :         MyAlgorithm alg;
    1416           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    1417           1 :         CPLErrorReset();
    1418           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"-xy"}));
    1419           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1420             :     }
    1421           1 : }
    1422             : 
    1423           4 : TEST_F(test_gdal_algorithm, int)
    1424             : {
    1425             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1426             :     {
    1427             :       public:
    1428             :         int m_val = 0;
    1429             : 
    1430           5 :         MyAlgorithm()
    1431           5 :         {
    1432           5 :             AddArg("val", 0, "", &m_val);
    1433           5 :         }
    1434             :     };
    1435             : 
    1436             :     {
    1437           2 :         MyAlgorithm alg;
    1438           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
    1439           1 :         EXPECT_EQ(alg.m_val, 5);
    1440             :     }
    1441             : 
    1442             :     {
    1443           2 :         MyAlgorithm alg;
    1444           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1445           1 :         CPLErrorReset();
    1446             :         // Missing value
    1447           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val"}));
    1448           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1449           1 :         EXPECT_EQ(alg.m_val, 0);
    1450             :     }
    1451             : 
    1452             :     {
    1453           2 :         MyAlgorithm alg;
    1454           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1455           1 :         CPLErrorReset();
    1456           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
    1457           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1458           1 :         EXPECT_EQ(alg.m_val, 0);
    1459             :     }
    1460             : 
    1461             :     {
    1462           2 :         MyAlgorithm alg;
    1463           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1464           1 :         CPLErrorReset();
    1465           3 :         EXPECT_FALSE(
    1466             :             alg.ParseCommandLineArguments({"--val=12345679812346798123456"}));
    1467           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1468           1 :         EXPECT_EQ(alg.m_val, 0);
    1469             :     }
    1470             : 
    1471             :     {
    1472           2 :         MyAlgorithm alg;
    1473           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1474           1 :         CPLErrorReset();
    1475           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1.5"}));
    1476           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1477           1 :         EXPECT_EQ(alg.m_val, 0);
    1478             :     }
    1479           1 : }
    1480             : 
    1481           4 : TEST_F(test_gdal_algorithm, int_min_val_included)
    1482             : {
    1483             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1484             :     {
    1485             :       public:
    1486             :         int m_val = 0;
    1487             : 
    1488           2 :         MyAlgorithm()
    1489           2 :         {
    1490           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMinValueIncluded(0);
    1491           2 :             const auto [minVal, minValIncluded] = arg.GetMinValue();
    1492           2 :             EXPECT_EQ(minVal, 0);
    1493           2 :             EXPECT_TRUE(minValIncluded);
    1494           2 :         }
    1495             :     };
    1496             : 
    1497             :     {
    1498           2 :         MyAlgorithm alg;
    1499           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0"}));
    1500           1 :         EXPECT_EQ(alg.m_val, 0);
    1501             :     }
    1502             : 
    1503             :     {
    1504           2 :         MyAlgorithm alg;
    1505           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1506           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=-1"}));
    1507             :     }
    1508           1 : }
    1509             : 
    1510           4 : TEST_F(test_gdal_algorithm, int_min_val_excluded)
    1511             : {
    1512             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1513             :     {
    1514             :       public:
    1515             :         int m_val = 0;
    1516             : 
    1517           2 :         MyAlgorithm()
    1518           2 :         {
    1519           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMinValueExcluded(0);
    1520           2 :             const auto [minVal, minValIncluded] = arg.GetMinValue();
    1521           2 :             EXPECT_EQ(minVal, 0);
    1522           2 :             EXPECT_FALSE(minValIncluded);
    1523           2 :         }
    1524             :     };
    1525             : 
    1526             :     {
    1527           2 :         MyAlgorithm alg;
    1528           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1"}));
    1529           1 :         EXPECT_EQ(alg.m_val, 1);
    1530             :     }
    1531             : 
    1532             :     {
    1533           2 :         MyAlgorithm alg;
    1534           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1535           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=0"}));
    1536             :     }
    1537           1 : }
    1538             : 
    1539           4 : TEST_F(test_gdal_algorithm, int_max_val_included)
    1540             : {
    1541             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1542             :     {
    1543             :       public:
    1544             :         int m_val = 0;
    1545             : 
    1546           2 :         MyAlgorithm()
    1547           2 :         {
    1548           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueIncluded(5);
    1549           2 :             const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
    1550           2 :             EXPECT_EQ(maxVal, 5);
    1551           2 :             EXPECT_TRUE(maxValIncluded);
    1552           2 :         }
    1553             :     };
    1554             : 
    1555             :     {
    1556           2 :         MyAlgorithm alg;
    1557           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
    1558           1 :         EXPECT_EQ(alg.m_val, 5);
    1559             :     }
    1560             : 
    1561             :     {
    1562           2 :         MyAlgorithm alg;
    1563           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1564           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=6"}));
    1565             :     }
    1566           1 : }
    1567             : 
    1568           4 : TEST_F(test_gdal_algorithm, int_max_val_excluded)
    1569             : {
    1570             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1571             :     {
    1572             :       public:
    1573             :         int m_val = 0;
    1574             : 
    1575           2 :         MyAlgorithm()
    1576           2 :         {
    1577           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueExcluded(5);
    1578           2 :             const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
    1579           2 :             EXPECT_EQ(maxVal, 5);
    1580           2 :             EXPECT_FALSE(maxValIncluded);
    1581           2 :         }
    1582             :     };
    1583             : 
    1584             :     {
    1585           2 :         MyAlgorithm alg;
    1586           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=4"}));
    1587           1 :         EXPECT_EQ(alg.m_val, 4);
    1588             :     }
    1589             : 
    1590             :     {
    1591           2 :         MyAlgorithm alg;
    1592           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1593           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5"}));
    1594             :     }
    1595           1 : }
    1596             : 
    1597           4 : TEST_F(test_gdal_algorithm, double_min_val_included)
    1598             : {
    1599             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1600             :     {
    1601             :       public:
    1602             :         double m_val = 0;
    1603             : 
    1604           2 :         MyAlgorithm()
    1605           2 :         {
    1606           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMinValueIncluded(0);
    1607           2 :             const auto [minVal, minValIncluded] = arg.GetMinValue();
    1608           2 :             EXPECT_EQ(minVal, 0);
    1609           2 :             EXPECT_TRUE(minValIncluded);
    1610           2 :         }
    1611             :     };
    1612             : 
    1613             :     {
    1614           2 :         MyAlgorithm alg;
    1615           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0"}));
    1616           1 :         EXPECT_EQ(alg.m_val, 0);
    1617             :     }
    1618             : 
    1619             :     {
    1620           2 :         MyAlgorithm alg;
    1621           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1622           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=-0.1"}));
    1623             :     }
    1624           1 : }
    1625             : 
    1626           4 : TEST_F(test_gdal_algorithm, double_min_val_excluded)
    1627             : {
    1628             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1629             :     {
    1630             :       public:
    1631             :         double m_val = 0;
    1632             : 
    1633           2 :         MyAlgorithm()
    1634           2 :         {
    1635           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMinValueExcluded(0);
    1636           2 :             const auto [minVal, minValIncluded] = arg.GetMinValue();
    1637           2 :             EXPECT_EQ(minVal, 0);
    1638           2 :             EXPECT_FALSE(minValIncluded);
    1639           2 :         }
    1640             :     };
    1641             : 
    1642             :     {
    1643           2 :         MyAlgorithm alg;
    1644           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=0.1"}));
    1645           1 :         EXPECT_EQ(alg.m_val, 0.1);
    1646             :     }
    1647             : 
    1648             :     {
    1649           2 :         MyAlgorithm alg;
    1650           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1651           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=0"}));
    1652             :     }
    1653           1 : }
    1654             : 
    1655           4 : TEST_F(test_gdal_algorithm, double_max_val_included)
    1656             : {
    1657             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1658             :     {
    1659             :       public:
    1660             :         double m_val = 0;
    1661             : 
    1662           2 :         MyAlgorithm()
    1663           2 :         {
    1664           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueIncluded(5);
    1665           2 :             const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
    1666           2 :             EXPECT_EQ(maxVal, 5);
    1667           2 :             EXPECT_TRUE(maxValIncluded);
    1668           2 :         }
    1669             :     };
    1670             : 
    1671             :     {
    1672           2 :         MyAlgorithm alg;
    1673           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5"}));
    1674           1 :         EXPECT_EQ(alg.m_val, 5);
    1675             :     }
    1676             : 
    1677             :     {
    1678           2 :         MyAlgorithm alg;
    1679           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1680           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5.1"}));
    1681             :     }
    1682           1 : }
    1683             : 
    1684           4 : TEST_F(test_gdal_algorithm, double_max_val_excluded)
    1685             : {
    1686             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1687             :     {
    1688             :       public:
    1689             :         double m_val = 0;
    1690             : 
    1691           2 :         MyAlgorithm()
    1692           2 :         {
    1693           2 :             auto &arg = AddArg("val", 0, "", &m_val).SetMaxValueExcluded(5);
    1694           2 :             const auto [maxVal, maxValIncluded] = arg.GetMaxValue();
    1695           2 :             EXPECT_EQ(maxVal, 5);
    1696           2 :             EXPECT_FALSE(maxValIncluded);
    1697           2 :         }
    1698             :     };
    1699             : 
    1700             :     {
    1701           2 :         MyAlgorithm alg;
    1702           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=4.9"}));
    1703           1 :         EXPECT_EQ(alg.m_val, 4.9);
    1704             :     }
    1705             : 
    1706             :     {
    1707           2 :         MyAlgorithm alg;
    1708           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1709           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5"}));
    1710             :     }
    1711           1 : }
    1712             : 
    1713           4 : TEST_F(test_gdal_algorithm, string_min_char_count)
    1714             : {
    1715             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1716             :     {
    1717             :       public:
    1718             :         std::string m_val{};
    1719             : 
    1720           2 :         MyAlgorithm()
    1721           2 :         {
    1722           2 :             AddArg("val", 0, "", &m_val).SetMinCharCount(2);
    1723           2 :         }
    1724             :     };
    1725             : 
    1726             :     {
    1727           2 :         MyAlgorithm alg;
    1728           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=ab"}));
    1729           1 :         EXPECT_STREQ(alg.m_val.c_str(), "ab");
    1730             :     }
    1731             : 
    1732             :     {
    1733           2 :         MyAlgorithm alg;
    1734           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1735           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=a"}));
    1736             :     }
    1737           1 : }
    1738             : 
    1739           4 : TEST_F(test_gdal_algorithm, string_vector_min_char_count)
    1740             : {
    1741             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1742             :     {
    1743             :       public:
    1744             :         std::vector<std::string> m_val{};
    1745             : 
    1746           2 :         MyAlgorithm()
    1747           2 :         {
    1748           2 :             AddArg("val", 0, "", &m_val).SetMinCharCount(2);
    1749           2 :         }
    1750             :     };
    1751             : 
    1752             :     {
    1753           2 :         MyAlgorithm alg;
    1754           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=ab"}));
    1755           1 :         EXPECT_STREQ(alg.m_val[0].c_str(), "ab");
    1756             :     }
    1757             : 
    1758             :     {
    1759           2 :         MyAlgorithm alg;
    1760           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1761           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=a"}));
    1762             :     }
    1763           1 : }
    1764             : 
    1765           4 : TEST_F(test_gdal_algorithm, SetDisplayInJSONUsage)
    1766             : {
    1767             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1768             :     {
    1769             :       public:
    1770           1 :         MyAlgorithm()
    1771           1 :         {
    1772           1 :             SetDisplayInJSONUsage(false);
    1773           1 :         }
    1774             :     };
    1775             : 
    1776             :     {
    1777           1 :         MyAlgorithm alg;
    1778           1 :         alg.GetUsageForCLI(false);
    1779           1 :         alg.GetUsageAsJSON();
    1780             :     }
    1781           1 : }
    1782             : 
    1783           4 : TEST_F(test_gdal_algorithm, int_with_default)
    1784             : {
    1785             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1786             :     {
    1787             :       public:
    1788             :         int m_val = 0;
    1789             : 
    1790           1 :         MyAlgorithm()
    1791           1 :         {
    1792           1 :             AddArg("val", 0, "", &m_val).SetDefault(3);
    1793           1 :         }
    1794             :     };
    1795             : 
    1796             :     {
    1797           2 :         MyAlgorithm alg;
    1798           1 :         alg.GetUsageForCLI(false);
    1799           1 :         alg.GetUsageAsJSON();
    1800           1 :         EXPECT_TRUE(alg.ValidateArguments());
    1801           1 :         EXPECT_EQ(alg.m_val, 3);
    1802             :     }
    1803           1 : }
    1804             : 
    1805           4 : TEST_F(test_gdal_algorithm, double)
    1806             : {
    1807             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1808             :     {
    1809             :       public:
    1810             :         double m_val = 0;
    1811             : 
    1812           2 :         MyAlgorithm()
    1813           2 :         {
    1814           2 :             AddArg("val", 0, "", &m_val);
    1815           2 :         }
    1816             :     };
    1817             : 
    1818             :     {
    1819           2 :         MyAlgorithm alg;
    1820           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5"}));
    1821           1 :         EXPECT_EQ(alg.m_val, 1.5);
    1822             :     }
    1823             : 
    1824             :     {
    1825           2 :         MyAlgorithm alg;
    1826           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1827           1 :         CPLErrorReset();
    1828           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=invalid"}));
    1829           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1830           1 :         EXPECT_EQ(alg.m_val, 0);
    1831             :     }
    1832           1 : }
    1833             : 
    1834           4 : TEST_F(test_gdal_algorithm, double_with_default)
    1835             : {
    1836             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1837             :     {
    1838             :       public:
    1839             :         double m_val = 0;
    1840             : 
    1841           1 :         MyAlgorithm()
    1842           1 :         {
    1843           1 :             AddArg("val", 0, "", &m_val).SetDefault(3.5);
    1844           1 :         }
    1845             :     };
    1846             : 
    1847             :     {
    1848           2 :         MyAlgorithm alg;
    1849           1 :         alg.GetUsageForCLI(false);
    1850           1 :         alg.GetUsageAsJSON();
    1851           1 :         EXPECT_TRUE(alg.ValidateArguments());
    1852           1 :         EXPECT_EQ(alg.m_val, 3.5);
    1853             :     }
    1854           1 : }
    1855             : 
    1856           4 : TEST_F(test_gdal_algorithm, string_with_default)
    1857             : {
    1858             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1859             :     {
    1860             :       public:
    1861             :         std::string m_val{};
    1862             : 
    1863           1 :         MyAlgorithm()
    1864           1 :         {
    1865           1 :             AddArg("val", 0, "", &m_val).SetDefault("foo");
    1866           1 :         }
    1867             :     };
    1868             : 
    1869             :     {
    1870           2 :         MyAlgorithm alg;
    1871           1 :         alg.GetUsageForCLI(false);
    1872           1 :         alg.GetUsageAsJSON();
    1873           1 :         EXPECT_TRUE(alg.ValidateArguments());
    1874           1 :         EXPECT_STREQ(alg.m_val.c_str(), "foo");
    1875             :     }
    1876           1 : }
    1877             : 
    1878           4 : TEST_F(test_gdal_algorithm, dataset)
    1879             : {
    1880             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1881             :     {
    1882             :       public:
    1883             :         GDALArgDatasetValue m_val{};
    1884             : 
    1885           5 :         MyAlgorithm()
    1886           5 :         {
    1887           5 :             AddArg("val", 0, "", &m_val).SetRequired();
    1888           5 :         }
    1889             :     };
    1890             : 
    1891             :     {
    1892           2 :         MyAlgorithm alg;
    1893           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    1894             :             {"--val=" GCORE_DATA_DIR "byte.tif"}));
    1895           1 :         EXPECT_NE(alg.m_val.GetDatasetRef(), nullptr);
    1896             :     }
    1897             : 
    1898             :     {
    1899           2 :         MyAlgorithm alg;
    1900             :         auto poDS = std::unique_ptr<GDALDataset>(
    1901             :             GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    1902           2 :                 "", 1, 1, 1, GDT_Byte, nullptr));
    1903           1 :         auto poDSRaw = poDS.get();
    1904           1 :         alg.GetArg("val")->Set(poDS.release());
    1905           1 :         EXPECT_EQ(alg.m_val.GetDatasetRef(), poDSRaw);
    1906           1 :         poDSRaw->ReleaseRef();
    1907             :     }
    1908             : 
    1909             :     {
    1910           2 :         MyAlgorithm alg;
    1911           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1912           1 :         CPLErrorReset();
    1913           3 :         EXPECT_FALSE(
    1914             :             alg.ParseCommandLineArguments({"--val=i_do_not_exist.tif"}));
    1915           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1916             :     }
    1917             : 
    1918             :     {
    1919           2 :         MyAlgorithm alg;
    1920           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1921           1 :         CPLErrorReset();
    1922           1 :         EXPECT_FALSE(alg.Run());
    1923           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1924             :     }
    1925             : 
    1926             :     {
    1927           2 :         MyAlgorithm alg;
    1928           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    1929           1 :         CPLErrorReset();
    1930           2 :         GDALArgDatasetValue value;
    1931           1 :         alg.GetArg("val")->SetFrom(value);
    1932           1 :         EXPECT_FALSE(alg.Run());
    1933           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    1934             :     }
    1935           1 : }
    1936             : 
    1937           4 : TEST_F(test_gdal_algorithm, input_update)
    1938             : {
    1939             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1940             :     {
    1941             :       public:
    1942             :         GDALArgDatasetValue m_input{};
    1943             :         GDALArgDatasetValue m_output{};
    1944             :         bool m_update = false;
    1945             : 
    1946           1 :         MyAlgorithm()
    1947           1 :         {
    1948           1 :             AddInputDatasetArg(&m_input);
    1949           1 :             AddUpdateArg(&m_update);
    1950           1 :         }
    1951             :     };
    1952             : 
    1953             :     {
    1954           1 :         auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
    1955           1 :         if (!poDriver)
    1956             :         {
    1957           0 :             GTEST_SKIP() << "GPKG support missing";
    1958             :         }
    1959             :         else
    1960             :         {
    1961             :             std::string osTmpFilename =
    1962           1 :                 VSIMemGenerateHiddenFilename("temp.gpkg");
    1963             :             auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
    1964           1 :                 osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
    1965           1 :             poDS->CreateLayer("foo");
    1966           1 :             poDS.reset();
    1967             : 
    1968           1 :             MyAlgorithm alg;
    1969           1 :             EXPECT_TRUE(!alg.GetUsageAsJSON().empty());
    1970           4 :             EXPECT_TRUE(
    1971             :                 alg.ParseCommandLineArguments({"--update", osTmpFilename}));
    1972           1 :             ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
    1973           1 :             EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
    1974             : 
    1975           1 :             alg.Finalize();
    1976             : 
    1977           1 :             VSIUnlink(osTmpFilename.c_str());
    1978             :         }
    1979             :     }
    1980             : }
    1981             : 
    1982           4 : TEST_F(test_gdal_algorithm, same_input_output_dataset_sqlite)
    1983             : {
    1984             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    1985             :     {
    1986             :       public:
    1987             :         GDALArgDatasetValue m_input{};
    1988             :         GDALArgDatasetValue m_output{};
    1989             :         bool m_update = false;
    1990             : 
    1991           1 :         MyAlgorithm()
    1992           1 :         {
    1993           1 :             AddInputDatasetArg(&m_input);
    1994           1 :             AddOutputDatasetArg(&m_output).SetDatasetInputFlags(GADV_NAME |
    1995           1 :                                                                 GADV_OBJECT);
    1996           1 :             AddUpdateArg(&m_update);
    1997           1 :         }
    1998             :     };
    1999             : 
    2000             :     {
    2001           1 :         auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
    2002           1 :         if (!poDriver)
    2003             :         {
    2004           0 :             GTEST_SKIP() << "GPKG support missing";
    2005             :         }
    2006             :         else
    2007             :         {
    2008             :             std::string osTmpFilename =
    2009           1 :                 VSIMemGenerateHiddenFilename("temp.gpkg");
    2010             :             auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
    2011           1 :                 osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
    2012           1 :             poDS->CreateLayer("foo");
    2013           1 :             poDS.reset();
    2014             : 
    2015           1 :             MyAlgorithm alg;
    2016           5 :             EXPECT_TRUE(alg.ParseCommandLineArguments(
    2017             :                 {"--update", osTmpFilename, osTmpFilename}));
    2018           1 :             ASSERT_NE(alg.m_input.GetDatasetRef(), nullptr);
    2019           1 :             EXPECT_NE(alg.m_output.GetDatasetRef(), nullptr);
    2020           1 :             EXPECT_EQ(alg.m_input.GetDatasetRef(),
    2021             :                       alg.m_output.GetDatasetRef());
    2022           1 :             EXPECT_EQ(alg.m_input.GetDatasetRef()->GetAccess(), GA_Update);
    2023             : 
    2024           1 :             alg.Finalize();
    2025             : 
    2026           1 :             VSIUnlink(osTmpFilename.c_str());
    2027             :         }
    2028             :     }
    2029             : }
    2030             : 
    2031           4 : TEST_F(test_gdal_algorithm, output_dataset_created_by_alg)
    2032             : {
    2033             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2034             :     {
    2035             :       public:
    2036             :         GDALArgDatasetValue m_output{};
    2037             : 
    2038           2 :         MyAlgorithm()
    2039           2 :         {
    2040           2 :             AddOutputDatasetArg(&m_output)
    2041           2 :                 .SetDatasetInputFlags(GADV_NAME)
    2042           2 :                 .SetDatasetOutputFlags(GADV_OBJECT);
    2043           2 :         }
    2044             :     };
    2045             : 
    2046             :     {
    2047           1 :         MyAlgorithm alg;
    2048           1 :         alg.GetUsageForCLI(false);
    2049             :     }
    2050             : 
    2051             :     {
    2052           2 :         MyAlgorithm alg;
    2053           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--output=-"}));
    2054           1 :         EXPECT_STREQ(alg.m_output.GetName().c_str(), "/vsistdout/");
    2055             :     }
    2056           1 : }
    2057             : 
    2058           4 : TEST_F(test_gdal_algorithm, string_choices)
    2059             : {
    2060             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2061             :     {
    2062             :       public:
    2063             :         std::string m_val{};
    2064             : 
    2065           4 :         MyAlgorithm()
    2066           4 :         {
    2067           8 :             AddArg("val", 0, "", &m_val)
    2068           4 :                 .SetChoices("foo", "bar")
    2069           4 :                 .SetHiddenChoices("baz");
    2070           4 :         }
    2071             :     };
    2072             : 
    2073             :     {
    2074           2 :         MyAlgorithm alg;
    2075           1 :         alg.GetUsageForCLI(false);
    2076             : 
    2077           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo"}));
    2078           1 :         EXPECT_STREQ(alg.m_val.c_str(), "foo");
    2079             :     }
    2080             : 
    2081             :     {
    2082           2 :         MyAlgorithm alg;
    2083           1 :         alg.GetUsageForCLI(false);
    2084             : 
    2085           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=FOO"}));
    2086           1 :         EXPECT_STREQ(alg.m_val.c_str(), "foo");
    2087             :     }
    2088             : 
    2089             :     {
    2090           2 :         MyAlgorithm alg;
    2091           1 :         alg.GetUsageForCLI(false);
    2092             : 
    2093           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=baz"}));
    2094           1 :         EXPECT_STREQ(alg.m_val.c_str(), "baz");
    2095             :     }
    2096             : 
    2097             :     {
    2098           2 :         MyAlgorithm alg;
    2099           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2100           1 :         CPLErrorReset();
    2101           1 :         EXPECT_FALSE(alg.GetArg("val")->Set("invalid"));
    2102           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2103             :     }
    2104           1 : }
    2105             : 
    2106           4 : TEST_F(test_gdal_algorithm, vector_int)
    2107             : {
    2108             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2109             :     {
    2110             :       public:
    2111             :         std::vector<int> m_val{};
    2112             : 
    2113           6 :         MyAlgorithm()
    2114           6 :         {
    2115           6 :             AddArg("val", 0, "", &m_val);
    2116           6 :         }
    2117             :     };
    2118             : 
    2119             :     {
    2120           2 :         MyAlgorithm alg;
    2121           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=5,6"}));
    2122           2 :         auto expected = std::vector<int>{5, 6};
    2123           1 :         EXPECT_EQ(alg.m_val, expected);
    2124             :     }
    2125             : 
    2126             :     {
    2127           2 :         MyAlgorithm alg;
    2128           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2129           1 :         CPLErrorReset();
    2130           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
    2131           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2132           1 :         EXPECT_TRUE(alg.m_val.empty());
    2133             :     }
    2134             : 
    2135             :     {
    2136           2 :         MyAlgorithm alg;
    2137           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2138           1 :         CPLErrorReset();
    2139           3 :         EXPECT_FALSE(
    2140             :             alg.ParseCommandLineArguments({"--val=1,12345679812346798123456"}));
    2141           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2142           1 :         EXPECT_TRUE(alg.m_val.empty());
    2143             :     }
    2144             : 
    2145             :     {
    2146           2 :         MyAlgorithm alg;
    2147           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2148           1 :         CPLErrorReset();
    2149           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
    2150           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2151           1 :         EXPECT_TRUE(alg.m_val.empty());
    2152             :     }
    2153             : 
    2154             :     {
    2155           2 :         MyAlgorithm alg;
    2156           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2157           1 :         CPLErrorReset();
    2158           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3, ,4"}));
    2159           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2160           1 :         EXPECT_TRUE(alg.m_val.empty());
    2161             :     }
    2162             : 
    2163             :     {
    2164           2 :         MyAlgorithm alg;
    2165           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2166           1 :         CPLErrorReset();
    2167           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3,,4"}));
    2168           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2169           1 :         EXPECT_TRUE(alg.m_val.empty());
    2170             :     }
    2171           1 : }
    2172             : 
    2173           4 : TEST_F(test_gdal_algorithm, vector_int_validation_fails)
    2174             : {
    2175             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2176             :     {
    2177             :       public:
    2178             :         std::vector<int> m_val{};
    2179             : 
    2180           1 :         MyAlgorithm()
    2181           1 :         {
    2182           2 :             AddArg("val", 0, "", &m_val)
    2183             :                 .AddValidationAction(
    2184           1 :                     []()
    2185             :                     {
    2186           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2187             :                                  "validation failed");
    2188           1 :                         return false;
    2189           1 :                     });
    2190           1 :         }
    2191             :     };
    2192             : 
    2193             :     {
    2194           2 :         MyAlgorithm alg;
    2195           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2196           1 :         CPLErrorReset();
    2197           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
    2198           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
    2199             :     }
    2200           1 : }
    2201             : 
    2202           4 : TEST_F(test_gdal_algorithm, vector_double)
    2203             : {
    2204             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2205             :     {
    2206             :       public:
    2207             :         std::vector<double> m_val{};
    2208             : 
    2209           5 :         MyAlgorithm()
    2210           5 :         {
    2211           5 :             AddArg("val", 0, "", &m_val);
    2212           5 :         }
    2213             :     };
    2214             : 
    2215             :     {
    2216           2 :         MyAlgorithm alg;
    2217           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=1.5,2.5"}));
    2218           2 :         auto expected = std::vector<double>{1.5, 2.5};
    2219           1 :         EXPECT_EQ(alg.m_val, expected);
    2220             :     }
    2221             : 
    2222             :     {
    2223           2 :         MyAlgorithm alg;
    2224           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2225           1 :         CPLErrorReset();
    2226           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1,foo"}));
    2227           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2228           1 :         EXPECT_TRUE(alg.m_val.empty());
    2229             :     }
    2230             : 
    2231             :     {
    2232           2 :         MyAlgorithm alg;
    2233           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2234           1 :         CPLErrorReset();
    2235           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3, ,4"}));
    2236           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2237           1 :         EXPECT_TRUE(alg.m_val.empty());
    2238             :     }
    2239             : 
    2240             :     {
    2241           2 :         MyAlgorithm alg;
    2242           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2243           1 :         CPLErrorReset();
    2244           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=3,,4"}));
    2245           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2246           1 :         EXPECT_TRUE(alg.m_val.empty());
    2247             :     }
    2248             : 
    2249             :     {
    2250           2 :         MyAlgorithm alg;
    2251           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2252           1 :         CPLErrorReset();
    2253           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=1", "--val=foo"}));
    2254           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2255           1 :         EXPECT_TRUE(alg.m_val.empty());
    2256             :     }
    2257           1 : }
    2258             : 
    2259           4 : TEST_F(test_gdal_algorithm, vector_double_validation_fails)
    2260             : {
    2261             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2262             :     {
    2263             :       public:
    2264             :         std::vector<double> m_val{};
    2265             : 
    2266           1 :         MyAlgorithm()
    2267           1 :         {
    2268           2 :             AddArg("val", 0, "", &m_val)
    2269             :                 .AddValidationAction(
    2270           1 :                     []()
    2271             :                     {
    2272           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2273             :                                  "validation failed");
    2274           1 :                         return false;
    2275           1 :                     });
    2276           1 :         }
    2277             :     };
    2278             : 
    2279             :     {
    2280           2 :         MyAlgorithm alg;
    2281           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2282           1 :         CPLErrorReset();
    2283           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=5", "--val=6"}));
    2284           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
    2285             :     }
    2286           1 : }
    2287             : 
    2288           4 : TEST_F(test_gdal_algorithm, vector_string)
    2289             : {
    2290             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2291             :     {
    2292             :       public:
    2293             :         std::vector<std::string> m_val{};
    2294             : 
    2295           1 :         MyAlgorithm()
    2296           1 :         {
    2297           1 :             AddArg("val", 0, "", &m_val);
    2298           1 :         }
    2299             :     };
    2300             : 
    2301             :     {
    2302           2 :         MyAlgorithm alg;
    2303           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
    2304           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    2305           1 :         EXPECT_EQ(alg.m_val, expected);
    2306             :     }
    2307           1 : }
    2308             : 
    2309           4 : TEST_F(test_gdal_algorithm, vector_string_validation_fails)
    2310             : {
    2311             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2312             :     {
    2313             :       public:
    2314             :         std::vector<std::string> m_val{};
    2315             : 
    2316           1 :         MyAlgorithm()
    2317           1 :         {
    2318           2 :             AddArg("val", 0, "", &m_val)
    2319             :                 .AddValidationAction(
    2320           1 :                     []()
    2321             :                     {
    2322           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2323             :                                  "validation failed");
    2324           1 :                         return false;
    2325           1 :                     });
    2326           1 :         }
    2327             :     };
    2328             : 
    2329             :     {
    2330           2 :         MyAlgorithm alg;
    2331           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2332           1 :         CPLErrorReset();
    2333           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
    2334           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
    2335             :     }
    2336           1 : }
    2337             : 
    2338           4 : TEST_F(test_gdal_algorithm, vector_string_choices)
    2339             : {
    2340             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2341             :     {
    2342             :       public:
    2343             :         std::vector<std::string> m_val{};
    2344             : 
    2345           4 :         MyAlgorithm()
    2346           4 :         {
    2347           4 :             AddArg("val", 0, "", &m_val).SetChoices("foo", "bar");
    2348           4 :         }
    2349             :     };
    2350             : 
    2351             :     {
    2352           2 :         MyAlgorithm alg;
    2353           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=foo,bar"}));
    2354           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    2355           1 :         EXPECT_EQ(alg.m_val, expected);
    2356             :     }
    2357             : 
    2358             :     {
    2359           2 :         MyAlgorithm alg;
    2360           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--val=FOO,BAR"}));
    2361           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    2362           1 :         EXPECT_EQ(alg.m_val, expected);
    2363             :     }
    2364             : 
    2365             :     {
    2366           2 :         MyAlgorithm alg;
    2367           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2368           1 :         CPLErrorReset();
    2369           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo,invalid"}));
    2370           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2371             :     }
    2372             : 
    2373             :     {
    2374           2 :         MyAlgorithm alg;
    2375           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2376           1 :         CPLErrorReset();
    2377           4 :         EXPECT_FALSE(
    2378             :             alg.ParseCommandLineArguments({"--val=foo", "--val=invalid"}));
    2379           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2380             :     }
    2381           1 : }
    2382             : 
    2383           4 : TEST_F(test_gdal_algorithm, vector_dataset)
    2384             : {
    2385             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2386             :     {
    2387             :       public:
    2388             :         std::vector<GDALArgDatasetValue> m_val{};
    2389             : 
    2390           3 :         MyAlgorithm()
    2391           3 :         {
    2392           3 :             AddArg("val", 0, "", &m_val);
    2393           3 :         }
    2394             :     };
    2395             : 
    2396             :     {
    2397           1 :         MyAlgorithm alg;
    2398           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    2399             :             {"--val=" GCORE_DATA_DIR "byte.tif"}));
    2400           1 :         ASSERT_EQ(alg.m_val.size(), 1U);
    2401           1 :         EXPECT_NE(alg.m_val[0].GetDatasetRef(), nullptr);
    2402             :     }
    2403             : 
    2404             :     {
    2405           1 :         MyAlgorithm alg;
    2406           1 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2407           1 :         CPLErrorReset();
    2408           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=non_existing.tif"}));
    2409           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2410           1 :         ASSERT_EQ(alg.m_val.size(), 1U);
    2411           1 :         EXPECT_EQ(alg.m_val[0].GetDatasetRef(), nullptr);
    2412             :     }
    2413             : 
    2414             :     {
    2415           2 :         MyAlgorithm alg;
    2416           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2417           1 :         CPLErrorReset();
    2418           1 :         alg.GetArg("val")->Set(std::vector<GDALArgDatasetValue>(1));
    2419           1 :         EXPECT_FALSE(alg.Run());
    2420           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2421             :     }
    2422             : }
    2423             : 
    2424           4 : TEST_F(test_gdal_algorithm, vector_dataset_validation_fails)
    2425             : {
    2426             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2427             :     {
    2428             :       public:
    2429             :         std::vector<GDALArgDatasetValue> m_val{};
    2430             : 
    2431           1 :         MyAlgorithm()
    2432           1 :         {
    2433           2 :             AddArg("val", 0, "", &m_val)
    2434             :                 .AddValidationAction(
    2435           1 :                     []()
    2436             :                     {
    2437           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    2438             :                                  "validation failed");
    2439           1 :                         return false;
    2440           1 :                     });
    2441           1 :         }
    2442             :     };
    2443             : 
    2444             :     {
    2445           2 :         MyAlgorithm alg;
    2446           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2447           1 :         CPLErrorReset();
    2448           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--val=foo", "--val=bar"}));
    2449           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
    2450             :     }
    2451           1 : }
    2452             : 
    2453           4 : TEST_F(test_gdal_algorithm, vector_input)
    2454             : {
    2455             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2456             :     {
    2457             :       public:
    2458             :         std::vector<GDALArgDatasetValue> m_input{};
    2459             :         std::vector<std::string> m_oo{};
    2460             :         std::vector<std::string> m_if{};
    2461             :         bool m_update = false;
    2462             : 
    2463           1 :         MyAlgorithm()
    2464           1 :         {
    2465           1 :             AddInputDatasetArg(&m_input);
    2466           1 :             AddOpenOptionsArg(&m_oo);
    2467           1 :             AddInputFormatsArg(&m_if);
    2468           1 :             AddUpdateArg(&m_update);
    2469           1 :         }
    2470             :     };
    2471             : 
    2472             :     {
    2473           1 :         auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
    2474           1 :         if (!poDriver)
    2475             :         {
    2476           0 :             GTEST_SKIP() << "GPKG support missing";
    2477             :         }
    2478             :         else
    2479             :         {
    2480             :             std::string osTmpFilename =
    2481           1 :                 VSIMemGenerateHiddenFilename("temp.gpkg");
    2482             :             auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
    2483           1 :                 osTmpFilename.c_str(), 0, 0, 0, GDT_Unknown, nullptr));
    2484           1 :             poDS->CreateLayer("foo");
    2485           1 :             poDS.reset();
    2486             : 
    2487           1 :             MyAlgorithm alg;
    2488           6 :             EXPECT_TRUE(alg.ParseCommandLineArguments(
    2489             :                 {"--update", "--oo=LIST_ALL_TABLES=YES", "--if=GPKG",
    2490             :                  osTmpFilename}));
    2491           1 :             ASSERT_EQ(alg.m_input.size(), 1U);
    2492           1 :             ASSERT_NE(alg.m_input[0].GetDatasetRef(), nullptr);
    2493           1 :             EXPECT_EQ(alg.m_input[0].GetDatasetRef()->GetAccess(), GA_Update);
    2494             : 
    2495           1 :             alg.Finalize();
    2496             : 
    2497           1 :             VSIUnlink(osTmpFilename.c_str());
    2498             :         }
    2499             :     }
    2500             : }
    2501             : 
    2502           4 : TEST_F(test_gdal_algorithm, several_values)
    2503             : {
    2504             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2505             :     {
    2506             :       public:
    2507             :         std::vector<std::string> m_co{};
    2508             : 
    2509           5 :         MyAlgorithm()
    2510           5 :         {
    2511           5 :             AddArg("co", 0, "creation options", &m_co);
    2512           5 :         }
    2513             :     };
    2514             : 
    2515             :     {
    2516           2 :         MyAlgorithm alg;
    2517           1 :         alg.GetUsageForCLI(false);
    2518             : 
    2519           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    2520             :     }
    2521             : 
    2522             :     {
    2523           2 :         MyAlgorithm alg;
    2524           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "FOO=BAR"}));
    2525           4 :         EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
    2526             :     }
    2527             : 
    2528             :     {
    2529           2 :         MyAlgorithm alg;
    2530           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR"}));
    2531           4 :         EXPECT_EQ(alg.m_co, std::vector<std::string>{"FOO=BAR"});
    2532             :     }
    2533             : 
    2534             :     {
    2535           2 :         MyAlgorithm alg;
    2536           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co=FOO=BAR,BAR=BAZ"}));
    2537           6 :         auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
    2538           1 :         EXPECT_EQ(alg.m_co, expected);
    2539             :     }
    2540             : 
    2541             :     {
    2542           2 :         MyAlgorithm alg;
    2543           5 :         EXPECT_TRUE(
    2544             :             alg.ParseCommandLineArguments({"--co=FOO=BAR", "--co", "BAR=BAZ"}));
    2545           6 :         auto expected = std::vector<std::string>{"FOO=BAR", "BAR=BAZ"};
    2546           1 :         EXPECT_EQ(alg.m_co, expected);
    2547             :     }
    2548           1 : }
    2549             : 
    2550           4 : TEST_F(test_gdal_algorithm, required_arg)
    2551             : {
    2552             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2553             :     {
    2554             :       public:
    2555             :         std::string m_arg{};
    2556             : 
    2557           2 :         MyAlgorithm()
    2558           2 :         {
    2559           2 :             AddArg("arg", 0, "required arg", &m_arg).SetRequired();
    2560           2 :         }
    2561             :     };
    2562             : 
    2563             :     {
    2564           2 :         MyAlgorithm alg;
    2565           1 :         alg.GetUsageForCLI(false);
    2566             : 
    2567           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2568           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2569             :     }
    2570             : 
    2571             :     {
    2572           2 :         MyAlgorithm alg;
    2573           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg", "foo"}));
    2574           1 :         EXPECT_STREQ(alg.m_arg.c_str(), "foo");
    2575             :     }
    2576           1 : }
    2577             : 
    2578           4 : TEST_F(test_gdal_algorithm, single_positional_arg)
    2579             : {
    2580             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2581             :     {
    2582             :       public:
    2583             :         std::string m_value{};
    2584             : 
    2585           4 :         MyAlgorithm()
    2586           4 :         {
    2587           4 :             AddArg("input", 0, "input value", &m_value).SetPositional();
    2588           4 :         }
    2589             :     };
    2590             : 
    2591             :     {
    2592           2 :         MyAlgorithm alg;
    2593           1 :         alg.GetUsageForCLI(false);
    2594             : 
    2595           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    2596             :     }
    2597             : 
    2598             :     {
    2599           2 :         MyAlgorithm alg;
    2600           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
    2601           1 :         EXPECT_TRUE(alg.GetArg("input")->IsExplicitlySet());
    2602           2 :         EXPECT_STREQ(alg.GetArg("input")->Get<std::string>().c_str(),
    2603             :                      "my_input");
    2604             :     }
    2605             : 
    2606             :     {
    2607           2 :         MyAlgorithm alg;
    2608           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--input", "my_input"}));
    2609           1 :         EXPECT_STREQ(alg.m_value.c_str(), "my_input");
    2610             :     }
    2611             : 
    2612             :     {
    2613           2 :         MyAlgorithm alg;
    2614           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
    2615           1 :         EXPECT_STREQ(alg.m_value.c_str(), "my_input");
    2616             :     }
    2617           1 : }
    2618             : 
    2619           4 : TEST_F(test_gdal_algorithm, single_positional_arg_required)
    2620             : {
    2621             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2622             :     {
    2623             :       public:
    2624             :         std::string m_value{};
    2625             : 
    2626           2 :         MyAlgorithm()
    2627           2 :         {
    2628           4 :             AddArg("input", 0, "input value", &m_value)
    2629           2 :                 .SetPositional()
    2630           2 :                 .SetRequired();
    2631           2 :         }
    2632             :     };
    2633             : 
    2634             :     {
    2635           2 :         MyAlgorithm alg;
    2636           1 :         alg.GetUsageForCLI(false);
    2637             : 
    2638           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2639           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2640             :     }
    2641             : 
    2642             :     {
    2643           2 :         MyAlgorithm alg;
    2644           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=my_input"}));
    2645           1 :         EXPECT_STREQ(alg.m_value.c_str(), "my_input");
    2646             :     }
    2647           1 : }
    2648             : 
    2649           4 : TEST_F(test_gdal_algorithm, two_positional_arg)
    2650             : {
    2651             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2652             :     {
    2653             :       public:
    2654             :         std::string m_input_value{};
    2655             :         std::string m_output_value{};
    2656             : 
    2657          12 :         MyAlgorithm()
    2658          12 :         {
    2659          12 :             AddArg("input", 'i', "input value", &m_input_value).SetPositional();
    2660          24 :             AddArg("output", 'o', "output value", &m_output_value)
    2661          12 :                 .SetPositional();
    2662          12 :         }
    2663             :     };
    2664             : 
    2665             :     {
    2666           2 :         MyAlgorithm alg;
    2667           1 :         alg.GetUsageForCLI(false);
    2668             : 
    2669           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    2670             :     }
    2671             : 
    2672             :     {
    2673           2 :         MyAlgorithm alg;
    2674           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input"}));
    2675           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2676             :     }
    2677             : 
    2678             :     {
    2679           2 :         MyAlgorithm alg;
    2680           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"-i", "my_input"}));
    2681           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2682             :     }
    2683             : 
    2684             :     {
    2685           2 :         MyAlgorithm alg;
    2686           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input", "my_output"}));
    2687           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2688           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2689             :     }
    2690             : 
    2691             :     {
    2692           2 :         MyAlgorithm alg;
    2693           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    2694             :             {"--input", "my_input", "-o", "my_output"}));
    2695           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2696           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2697             :     }
    2698             : 
    2699             :     {
    2700           2 :         MyAlgorithm alg;
    2701           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    2702             :             {"-o", "my_output", "--input", "my_input"}));
    2703           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2704           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2705             :     }
    2706             : 
    2707             :     {
    2708           2 :         MyAlgorithm alg;
    2709           5 :         EXPECT_TRUE(
    2710             :             alg.ParseCommandLineArguments({"-o", "my_output", "my_input"}));
    2711           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2712           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2713             :     }
    2714             : 
    2715             :     {
    2716           2 :         MyAlgorithm alg;
    2717           5 :         EXPECT_TRUE(
    2718             :             alg.ParseCommandLineArguments({"my_input", "-o", "my_output"}));
    2719           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2720           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2721             :     }
    2722             : 
    2723             :     {
    2724           2 :         MyAlgorithm alg;
    2725           1 :         alg.GetArg("input")->Set("my_input");
    2726           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"my_output"}));
    2727           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2728           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2729             :     }
    2730             : 
    2731             :     {
    2732           2 :         MyAlgorithm alg;
    2733           1 :         alg.GetArg("input")->Set("my_input");
    2734           1 :         alg.GetArg("output")->Set("my_output");
    2735           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    2736           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2737           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2738             :     }
    2739             : 
    2740             :     {
    2741           2 :         MyAlgorithm alg;
    2742           1 :         alg.GetArg("input")->Set("my_input");
    2743           1 :         alg.GetArg("output")->Set("my_output");
    2744           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2745           1 :         CPLErrorReset();
    2746           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"unexpected"}));
    2747           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2748             :     }
    2749             : 
    2750             :     {
    2751           2 :         MyAlgorithm alg;
    2752           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2753           1 :         CPLErrorReset();
    2754           5 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"foo", "bar", "baz"}));
    2755           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2756             :     }
    2757           1 : }
    2758             : 
    2759           4 : TEST_F(test_gdal_algorithm, two_positional_arg_first_two_values)
    2760             : {
    2761             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2762             :     {
    2763             :       public:
    2764             :         std::vector<int> m_input_value{};
    2765             :         std::string m_output_value{};
    2766             : 
    2767           3 :         MyAlgorithm()
    2768           3 :         {
    2769           6 :             AddArg("input", 'i', "input value", &m_input_value)
    2770           3 :                 .SetPositional()
    2771           3 :                 .SetMinCount(2)
    2772           3 :                 .SetMaxCount(2)
    2773           3 :                 .SetDisplayHintAboutRepetition(false);
    2774           6 :             AddArg("output", 'o', "output value", &m_output_value)
    2775           3 :                 .SetPositional();
    2776           3 :         }
    2777             :     };
    2778             : 
    2779             :     {
    2780           2 :         MyAlgorithm alg;
    2781           1 :         alg.GetUsageForCLI(false);
    2782             : 
    2783           5 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"1", "2", "baz"}));
    2784           2 :         auto expected = std::vector<int>{1, 2};
    2785           1 :         EXPECT_EQ(alg.m_input_value, expected);
    2786           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "baz");
    2787             :     }
    2788             : 
    2789             :     {
    2790           2 :         MyAlgorithm alg;
    2791           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2792           1 :         CPLErrorReset();
    2793           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"1"}));
    2794           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2795             :     }
    2796             : 
    2797             :     {
    2798           2 :         MyAlgorithm alg;
    2799           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2800           1 :         CPLErrorReset();
    2801           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"1", "foo"}));
    2802           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    2803             :     }
    2804           1 : }
    2805             : 
    2806           4 : TEST_F(test_gdal_algorithm, unlimited_input_single_output)
    2807             : {
    2808             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2809             :     {
    2810             :       public:
    2811             :         std::vector<std::string> m_input_values{};
    2812             :         std::string m_output_value{};
    2813             : 
    2814           2 :         MyAlgorithm()
    2815           2 :         {
    2816           4 :             AddArg("input", 'i', "input value", &m_input_values)
    2817           2 :                 .SetPositional();
    2818           4 :             AddArg("output", 'o', "output value", &m_output_value)
    2819           2 :                 .SetPositional()
    2820           2 :                 .SetRequired();
    2821           2 :         }
    2822             :     };
    2823             : 
    2824             :     {
    2825           2 :         MyAlgorithm alg;
    2826           1 :         alg.GetUsageForCLI(false);
    2827             : 
    2828           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2829           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2830             :     }
    2831             : 
    2832             :     {
    2833           2 :         MyAlgorithm alg;
    2834           5 :         EXPECT_TRUE(
    2835             :             alg.ParseCommandLineArguments({"input1", "input2", "my_output"}));
    2836           6 :         auto expected = std::vector<std::string>{"input1", "input2"};
    2837           1 :         EXPECT_EQ(alg.m_input_values, expected);
    2838           1 :         EXPECT_STREQ(alg.m_output_value.c_str(), "my_output");
    2839             :     }
    2840           1 : }
    2841             : 
    2842           4 : TEST_F(test_gdal_algorithm, single_input_unlimited_outputs)
    2843             : {
    2844             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2845             :     {
    2846             :       public:
    2847             :         std::string m_input_value{};
    2848             :         std::vector<std::string> m_output_values{};
    2849             : 
    2850           2 :         MyAlgorithm()
    2851           2 :         {
    2852           4 :             AddArg("input", 'i', "input value", &m_input_value)
    2853           2 :                 .SetPositional()
    2854           2 :                 .SetRequired();
    2855           4 :             AddArg("output", 'o', "output value", &m_output_values)
    2856           2 :                 .SetPositional();
    2857           2 :         }
    2858             :     };
    2859             : 
    2860             :     {
    2861           2 :         MyAlgorithm alg;
    2862           1 :         alg.GetUsageForCLI(false);
    2863             : 
    2864           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2865           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2866             :     }
    2867             : 
    2868             :     {
    2869           2 :         MyAlgorithm alg;
    2870           5 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    2871             :             {"my_input", "my_output1", "my_output2"}));
    2872           1 :         EXPECT_STREQ(alg.m_input_value.c_str(), "my_input");
    2873           6 :         auto expected = std::vector<std::string>{"my_output1", "my_output2"};
    2874           1 :         EXPECT_EQ(alg.m_output_values, expected);
    2875             :     }
    2876           1 : }
    2877             : 
    2878           4 : TEST_F(test_gdal_algorithm, min_max_count)
    2879             : {
    2880             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2881             :     {
    2882             :       public:
    2883             :         std::vector<std::string> m_arg{};
    2884             : 
    2885           5 :         MyAlgorithm()
    2886           5 :         {
    2887          10 :             AddArg("arg", 0, "arg", &m_arg)
    2888           5 :                 .SetRequired()
    2889           5 :                 .SetMinCount(2)
    2890           5 :                 .SetMaxCount(3);
    2891           5 :         }
    2892             :     };
    2893             : 
    2894             :     {
    2895           2 :         MyAlgorithm alg;
    2896           1 :         alg.GetUsageForCLI(false);
    2897             : 
    2898           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2899           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2900             :     }
    2901             : 
    2902             :     {
    2903           2 :         MyAlgorithm alg;
    2904           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2905           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo"}));
    2906             :     }
    2907             : 
    2908             :     {
    2909           2 :         MyAlgorithm alg;
    2910           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2911           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=1,2,3,4"}));
    2912             :     }
    2913             : 
    2914             :     {
    2915           2 :         MyAlgorithm alg;
    2916           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
    2917           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    2918           1 :         EXPECT_EQ(alg.m_arg, expected);
    2919             :     }
    2920             : 
    2921             :     {
    2922           2 :         MyAlgorithm alg;
    2923           8 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    2924             :             {"--arg", "foo", "--arg", "bar", "--arg", "baz"}));
    2925           7 :         auto expected = std::vector<std::string>{"foo", "bar", "baz"};
    2926           1 :         EXPECT_EQ(alg.m_arg, expected);
    2927             :     }
    2928           1 : }
    2929             : 
    2930           4 : TEST_F(test_gdal_algorithm, min_max_count_equal)
    2931             : {
    2932             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2933             :     {
    2934             :       public:
    2935             :         std::vector<std::string> m_arg{};
    2936             : 
    2937           2 :         MyAlgorithm()
    2938           2 :         {
    2939           4 :             AddArg("arg", 0, "arg", &m_arg)
    2940           2 :                 .SetRequired()
    2941           2 :                 .SetMinCount(2)
    2942           2 :                 .SetMaxCount(2);
    2943           2 :         }
    2944             :     };
    2945             : 
    2946             :     {
    2947           2 :         MyAlgorithm alg;
    2948           1 :         alg.GetUsageForCLI(false);
    2949             : 
    2950           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2951           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    2952             :     }
    2953             : 
    2954             :     {
    2955           2 :         MyAlgorithm alg;
    2956           2 :         alg.GetArg("arg")->Set(std::vector<std::string>{"foo"});
    2957           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    2958           1 :         EXPECT_FALSE(alg.ValidateArguments());
    2959           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    2960             :                      "test: 1 value has been specified for argument 'arg', "
    2961             :                      "whereas exactly 2 were expected.");
    2962             :     }
    2963           1 : }
    2964             : 
    2965           4 : TEST_F(test_gdal_algorithm, repeated_arg_allowed_false)
    2966             : {
    2967             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    2968             :     {
    2969             :       public:
    2970             :         std::vector<std::string> m_arg{};
    2971             : 
    2972           3 :         MyAlgorithm()
    2973           3 :         {
    2974           6 :             AddArg("arg", 0, "arg", &m_arg)
    2975           3 :                 .SetRepeatedArgAllowed(false)
    2976           3 :                 .SetMinCount(2)
    2977           3 :                 .SetMaxCount(3);
    2978           3 :         }
    2979             :     };
    2980             : 
    2981             :     {
    2982           2 :         MyAlgorithm alg;
    2983           1 :         alg.GetUsageForCLI(false);
    2984             : 
    2985           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    2986             :     }
    2987             : 
    2988             :     {
    2989           2 :         MyAlgorithm alg;
    2990           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
    2991           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    2992           1 :         EXPECT_EQ(alg.m_arg, expected);
    2993             :     }
    2994             : 
    2995             :     {
    2996           2 :         MyAlgorithm alg;
    2997           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    2998           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
    2999             :     }
    3000           1 : }
    3001             : 
    3002           4 : TEST_F(test_gdal_algorithm, ambiguous_positional_unlimited_and_then_varying)
    3003             : {
    3004             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3005             :     {
    3006             :       public:
    3007             :         std::vector<std::string> m_input_values{};
    3008             :         std::vector<std::string> m_output_values{};
    3009             : 
    3010           1 :         MyAlgorithm()
    3011           1 :         {
    3012           2 :             AddArg("input", 'i', "input value", &m_input_values)
    3013           1 :                 .SetPositional();
    3014           2 :             AddArg("output", 'o', "output value", &m_output_values)
    3015           1 :                 .SetPositional()
    3016           1 :                 .SetMinCount(2)
    3017           1 :                 .SetMaxCount(3);
    3018           1 :         }
    3019             :     };
    3020             : 
    3021             :     {
    3022           2 :         MyAlgorithm alg;
    3023           1 :         alg.GetUsageForCLI(false);
    3024             : 
    3025           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3026           1 :         CPLErrorReset();
    3027           5 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3028             :             {"my_input", "my_output1", "my_output2"}));
    3029           1 :         EXPECT_STREQ(
    3030             :             CPLGetLastErrorMsg(),
    3031             :             "test: Ambiguity in definition of positional argument 'output' "
    3032             :             "given it has a varying number of values, but follows argument "
    3033             :             "'input' which also has a varying number of values");
    3034             :     }
    3035           1 : }
    3036             : 
    3037           4 : TEST_F(test_gdal_algorithm,
    3038             :        ambiguous_positional_unlimited_and_then_non_required)
    3039             : {
    3040             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3041             :     {
    3042             :       public:
    3043             :         std::vector<std::string> m_input_values{};
    3044             :         std::string m_output_value{};
    3045             : 
    3046           1 :         MyAlgorithm()
    3047           1 :         {
    3048           2 :             AddArg("input", 'i', "input value", &m_input_values)
    3049           1 :                 .SetPositional();
    3050           2 :             AddArg("output", 'o', "output value", &m_output_value)
    3051           1 :                 .SetPositional();
    3052           1 :         }
    3053             :     };
    3054             : 
    3055             :     {
    3056           2 :         MyAlgorithm alg;
    3057           1 :         alg.GetUsageForCLI(false);
    3058             : 
    3059           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3060           1 :         CPLErrorReset();
    3061           5 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3062             :             {"my_input1", "my_input2", "my_output"}));
    3063           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3064             :                      "test: Ambiguity in definition of positional argument "
    3065             :                      "'output', given it is not required but follows argument "
    3066             :                      "'input' which has a varying number of values");
    3067             :     }
    3068           1 : }
    3069             : 
    3070           4 : TEST_F(test_gdal_algorithm,
    3071             :        ambiguous_positional_fixed_then_unlimited_then_fixed)
    3072             : {
    3073             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3074             :     {
    3075             :       public:
    3076             :         std::string m_input_value{};
    3077             :         std::vector<std::string> m_something{};
    3078             :         std::string m_output_value{};
    3079             : 
    3080           1 :         MyAlgorithm()
    3081           1 :         {
    3082           1 :             AddArg("input", 'i', "input value", &m_input_value).SetPositional();
    3083           1 :             AddArg("something", 0, "something", &m_something).SetPositional();
    3084           2 :             AddArg("output", 'o', "output value", &m_output_value)
    3085           1 :                 .SetPositional();
    3086           1 :         }
    3087             :     };
    3088             : 
    3089             :     {
    3090           2 :         MyAlgorithm alg;
    3091           1 :         alg.GetUsageForCLI(false);
    3092             : 
    3093           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3094           1 :         CPLErrorReset();
    3095           5 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3096             :             {"my_input", "something", "my_output"}));
    3097             :         // Actually this is not ambiguous here, but our parser does not support
    3098             :         // that for now
    3099           1 :         EXPECT_STREQ(
    3100             :             CPLGetLastErrorMsg(),
    3101             :             "test: Ambiguity in definition of positional arguments: arguments "
    3102             :             "with varying number of values must be first or last one.");
    3103             :     }
    3104           1 : }
    3105             : 
    3106           4 : TEST_F(test_gdal_algorithm, positional_unlimited_and_then_2)
    3107             : {
    3108             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3109             :     {
    3110             :       public:
    3111             :         std::vector<std::string> m_input_values{};
    3112             :         std::vector<std::string> m_output_values{};
    3113             : 
    3114           2 :         MyAlgorithm()
    3115           2 :         {
    3116           4 :             AddArg("input", 'i', "input value", &m_input_values)
    3117           2 :                 .SetPositional();
    3118           4 :             AddArg("output", 'o', "output value", &m_output_values)
    3119           2 :                 .SetPositional()
    3120           2 :                 .SetMinCount(2)
    3121           2 :                 .SetMaxCount(2);
    3122           2 :         }
    3123             :     };
    3124             : 
    3125             :     {
    3126           2 :         MyAlgorithm alg;
    3127           1 :         alg.GetUsageForCLI(false);
    3128             : 
    3129           7 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
    3130             :                                                    "my_input3", "my_output1",
    3131             :                                                    "my_output2"}));
    3132           1 :         EXPECT_EQ(alg.m_input_values.size(), 3U);
    3133           1 :         EXPECT_EQ(alg.m_output_values.size(), 2U);
    3134             :     }
    3135             : 
    3136             :     {
    3137           2 :         MyAlgorithm alg;
    3138             : 
    3139           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3140           1 :         CPLErrorReset();
    3141           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"my_output1"}));
    3142           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3143             :                      "test: Not enough positional values.");
    3144             :     }
    3145           1 : }
    3146             : 
    3147           4 : TEST_F(test_gdal_algorithm, positional_unlimited_validation_error_and_then_2)
    3148             : {
    3149             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3150             :     {
    3151             :       public:
    3152             :         std::vector<std::string> m_input_values{};
    3153             :         std::vector<std::string> m_output_values{};
    3154             : 
    3155           1 :         MyAlgorithm()
    3156           1 :         {
    3157           2 :             AddArg("input", 'i', "input value", &m_input_values)
    3158           1 :                 .SetPositional()
    3159             :                 .AddValidationAction(
    3160           1 :                     []()
    3161             :                     {
    3162           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    3163             :                                  "validation failed");
    3164           1 :                         return false;
    3165           1 :                     });
    3166           2 :             AddArg("output", 'o', "output value", &m_output_values)
    3167           1 :                 .SetPositional()
    3168           1 :                 .SetMinCount(2)
    3169           1 :                 .SetMaxCount(2);
    3170           1 :         }
    3171             :     };
    3172             : 
    3173             :     {
    3174           2 :         MyAlgorithm alg;
    3175             : 
    3176           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3177           1 :         CPLErrorReset();
    3178           7 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"my_input1", "my_input2",
    3179             :                                                     "my_input3", "my_output1",
    3180             :                                                     "my_output2"}));
    3181           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "validation failed");
    3182             :     }
    3183           1 : }
    3184             : 
    3185           4 : TEST_F(test_gdal_algorithm,
    3186             :        positional_unlimited_validation_error_and_then_required)
    3187             : {
    3188             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3189             :     {
    3190             :       public:
    3191             :         std::vector<std::string> m_input_values{};
    3192             :         std::string m_output_value{};
    3193             : 
    3194           1 :         MyAlgorithm()
    3195           1 :         {
    3196           2 :             AddArg("input", 'i', "input value", &m_input_values)
    3197           1 :                 .SetPositional()
    3198           1 :                 .SetChoices("foo");
    3199           2 :             AddArg("output", 'o', "output value", &m_output_value)
    3200           1 :                 .SetPositional()
    3201           1 :                 .SetRequired();
    3202           1 :         }
    3203             :     };
    3204             : 
    3205             :     {
    3206           2 :         MyAlgorithm alg;
    3207             : 
    3208           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3209           1 :         CPLErrorReset();
    3210           5 :         EXPECT_FALSE(
    3211             :             alg.ParseCommandLineArguments({"foo", "bar", "my_output"}));
    3212           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3213             :                      "Invalid value 'bar' for string argument 'input'. "
    3214             :                      "Should be one among 'foo'.");
    3215             :     }
    3216           1 : }
    3217             : 
    3218           4 : TEST_F(test_gdal_algorithm,
    3219             :        positional_required_and_then_unlimited_validation_error)
    3220             : {
    3221             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3222             :     {
    3223             :       public:
    3224             :         std::string m_input_value{};
    3225             :         std::vector<std::string> m_output_values{};
    3226             : 
    3227           1 :         MyAlgorithm()
    3228           1 :         {
    3229           2 :             AddArg("input", 'i', "input value", &m_input_value)
    3230           1 :                 .SetPositional()
    3231           1 :                 .SetRequired();
    3232           2 :             AddArg("output", 'o', "output values", &m_output_values)
    3233           1 :                 .SetPositional()
    3234           1 :                 .SetChoices("foo");
    3235           1 :         }
    3236             :     };
    3237             : 
    3238             :     {
    3239           2 :         MyAlgorithm alg;
    3240             : 
    3241           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3242           1 :         CPLErrorReset();
    3243           5 :         EXPECT_FALSE(
    3244             :             alg.ParseCommandLineArguments({"something", "foo", "bar"}));
    3245           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3246             :                      "Invalid value 'bar' for string argument 'output'. "
    3247             :                      "Should be one among 'foo'.");
    3248             :     }
    3249           1 : }
    3250             : 
    3251           4 : TEST_F(test_gdal_algorithm,
    3252             :        positional_required_then_unlimited_required_then_positional_required)
    3253             : {
    3254             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3255             :     {
    3256             :       public:
    3257             :         std::string m_input_value{};
    3258             :         std::vector<std::string> m_something{};
    3259             :         std::string m_output_value{};
    3260             : 
    3261           6 :         MyAlgorithm()
    3262           6 :         {
    3263          12 :             AddArg("input", 'i', "input value", &m_input_value)
    3264           6 :                 .SetMinCharCount(2)
    3265           6 :                 .SetPositional()
    3266           6 :                 .SetRequired();
    3267          12 :             AddArg("something", 0, "something", &m_something)
    3268           6 :                 .SetMinCharCount(2)
    3269           6 :                 .SetPositional()
    3270           6 :                 .SetMinCount(1);
    3271          12 :             AddArg("output", 'o', "output value", &m_output_value)
    3272           6 :                 .SetMinCharCount(2)
    3273           6 :                 .SetPositional()
    3274           6 :                 .SetRequired();
    3275           6 :         }
    3276             :     };
    3277             : 
    3278             :     {
    3279           2 :         MyAlgorithm alg;
    3280             : 
    3281           5 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3282             :             {"my_input", "something", "my_output"}));
    3283             :     }
    3284             : 
    3285             :     {
    3286           2 :         MyAlgorithm alg;
    3287             : 
    3288           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3289             :             {"my_input", "something", "else", "my_output"}));
    3290             :     }
    3291             : 
    3292             :     {
    3293           2 :         MyAlgorithm alg;
    3294             : 
    3295           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3296           1 :         CPLErrorReset();
    3297           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"input", "output"}));
    3298           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3299             :                      "test: Not enough positional values.");
    3300             :     }
    3301             : 
    3302             :     {
    3303           2 :         MyAlgorithm alg;
    3304             : 
    3305           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3306           1 :         CPLErrorReset();
    3307           5 :         EXPECT_FALSE(
    3308             :             alg.ParseCommandLineArguments({"x", "something", "output"}));
    3309           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3310             :                      "Value of argument 'input' is 'x', but should have at "
    3311             :                      "least 2 character(s)");
    3312             :     }
    3313             : 
    3314             :     {
    3315           2 :         MyAlgorithm alg;
    3316             : 
    3317           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3318           1 :         CPLErrorReset();
    3319           5 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"input", "x", "output"}));
    3320           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3321             :                      "Value of argument 'something' is 'x', but should have at "
    3322             :                      "least 2 character(s)");
    3323             :     }
    3324             : 
    3325             :     {
    3326           2 :         MyAlgorithm alg;
    3327             : 
    3328           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3329           1 :         CPLErrorReset();
    3330           5 :         EXPECT_FALSE(
    3331             :             alg.ParseCommandLineArguments({"input", "something", "x"}));
    3332           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    3333             :                      "Value of argument 'output' is 'x', but should have at "
    3334             :                      "least 2 character(s)");
    3335             :     }
    3336           1 : }
    3337             : 
    3338           4 : TEST_F(test_gdal_algorithm, packed_values_allowed_false)
    3339             : {
    3340             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3341             :     {
    3342             :       public:
    3343             :         std::vector<std::string> m_arg{};
    3344             : 
    3345           3 :         MyAlgorithm()
    3346           3 :         {
    3347           6 :             AddArg("arg", 0, "arg", &m_arg)
    3348           3 :                 .SetPackedValuesAllowed(false)
    3349           3 :                 .SetMinCount(2)
    3350           3 :                 .SetMaxCount(3);
    3351           3 :         }
    3352             :     };
    3353             : 
    3354             :     {
    3355           2 :         MyAlgorithm alg;
    3356           1 :         alg.GetUsageForCLI(false);
    3357             : 
    3358           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    3359             :     }
    3360             : 
    3361             :     {
    3362           2 :         MyAlgorithm alg;
    3363           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--arg=foo", "--arg=bar"}));
    3364           6 :         auto expected = std::vector<std::string>{"foo", "bar"};
    3365           1 :         EXPECT_EQ(alg.m_arg, expected);
    3366             : 
    3367           2 :         std::string serialized;
    3368           1 :         EXPECT_TRUE(alg.GetArg("arg")->Serialize(serialized));
    3369           1 :         EXPECT_STREQ(serialized.c_str(), "--arg foo --arg bar");
    3370             :     }
    3371             : 
    3372             :     {
    3373           2 :         MyAlgorithm alg;
    3374           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3375           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--arg=foo,bar"}));
    3376             :     }
    3377           1 : }
    3378             : 
    3379           4 : TEST_F(test_gdal_algorithm, actions)
    3380             : {
    3381             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3382             :     {
    3383             :       public:
    3384             :         bool m_flag = false;
    3385             :         bool m_flagSpecified = false;
    3386             : 
    3387           1 :         MyAlgorithm()
    3388           1 :         {
    3389           2 :             AddArg("flag", 'f', "boolean flag", &m_flag)
    3390           1 :                 .AddAction([this]() { m_flagSpecified = true; });
    3391           1 :         }
    3392             :     };
    3393             : 
    3394             :     {
    3395           2 :         MyAlgorithm alg;
    3396           1 :         alg.GetUsageForCLI(false);
    3397             : 
    3398           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag"}));
    3399           1 :         EXPECT_TRUE(alg.m_flag);
    3400           1 :         EXPECT_TRUE(alg.m_flagSpecified);
    3401             :     }
    3402           1 : }
    3403             : 
    3404           4 : TEST_F(test_gdal_algorithm, various)
    3405             : {
    3406             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3407             :     {
    3408             :       public:
    3409             :         bool m_flag = false;
    3410             : 
    3411           6 :         MyAlgorithm()
    3412           6 :         {
    3413           6 :             AddProgressArg();
    3414           6 :         }
    3415             :     };
    3416             : 
    3417             :     {
    3418           2 :         MyAlgorithm alg;
    3419           1 :         alg.GetUsageForCLI(false);
    3420             : 
    3421           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    3422             :         // Parse again
    3423           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3424           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    3425             :     }
    3426             : 
    3427             :     {
    3428           2 :         MyAlgorithm alg;
    3429           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"-h"}));
    3430           1 :         EXPECT_TRUE(alg.IsHelpRequested());
    3431             :     }
    3432             : 
    3433             :     {
    3434           2 :         MyAlgorithm alg;
    3435           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--help"}));
    3436           1 :         EXPECT_TRUE(alg.IsHelpRequested());
    3437             :     }
    3438             : 
    3439             :     {
    3440           2 :         MyAlgorithm alg;
    3441           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"help"}));
    3442           1 :         EXPECT_TRUE(alg.IsHelpRequested());
    3443             :     }
    3444             : 
    3445             :     {
    3446           2 :         MyAlgorithm alg;
    3447           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--json-usage"}));
    3448           1 :         EXPECT_TRUE(alg.IsJSONUsageRequested());
    3449             :     }
    3450             : 
    3451             :     {
    3452           2 :         MyAlgorithm alg;
    3453           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--progress"}));
    3454           1 :         EXPECT_TRUE(alg.IsProgressBarRequested());
    3455             :     }
    3456           1 : }
    3457             : 
    3458           4 : TEST_F(test_gdal_algorithm, mutually_exclusive)
    3459             : {
    3460             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3461             :     {
    3462             :       public:
    3463             :         bool m_flag1 = false;
    3464             :         bool m_flag2 = false;
    3465             :         bool m_flag3 = false;
    3466             : 
    3467           4 :         MyAlgorithm()
    3468           4 :         {
    3469           8 :             AddArg("flag1", 0, "", &m_flag1)
    3470           4 :                 .SetMutualExclusionGroup("my_group");
    3471           8 :             AddArg("flag2", 0, "", &m_flag2)
    3472           4 :                 .SetMutualExclusionGroup("my_group");
    3473           8 :             AddArg("flag3", 0, "", &m_flag3)
    3474           4 :                 .SetMutualExclusionGroup("my_group");
    3475           4 :         }
    3476             :     };
    3477             : 
    3478             :     {
    3479           2 :         MyAlgorithm alg;
    3480           1 :         alg.GetUsageForCLI(false);
    3481             : 
    3482           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    3483             :     }
    3484             : 
    3485             :     {
    3486           2 :         MyAlgorithm alg;
    3487           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag1"}));
    3488             :     }
    3489             : 
    3490             :     {
    3491           2 :         MyAlgorithm alg;
    3492           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--flag2"}));
    3493             :     }
    3494             : 
    3495             :     {
    3496           2 :         MyAlgorithm alg;
    3497           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3498           1 :         CPLErrorReset();
    3499           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--flag1", "--flag2"}));
    3500           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3501             :     }
    3502           1 : }
    3503             : 
    3504           4 : TEST_F(test_gdal_algorithm, invalid_input_format)
    3505             : {
    3506             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3507             :     {
    3508             :       public:
    3509             :         std::vector<std::string> m_if{};
    3510             : 
    3511           2 :         MyAlgorithm()
    3512           2 :         {
    3513           2 :             AddInputFormatsArg(&m_if).AddMetadataItem(
    3514           4 :                 GAAMDI_REQUIRED_CAPABILITIES, {GDAL_DCAP_VECTOR});
    3515           2 :         }
    3516             :     };
    3517             : 
    3518             :     {
    3519           2 :         MyAlgorithm alg;
    3520           1 :         alg.GetUsageForCLI(false);
    3521             : 
    3522           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3523           1 :         CPLErrorReset();
    3524           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=I_DO_NOT_EXIST"}));
    3525           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3526             :     }
    3527             : 
    3528             :     {
    3529           2 :         MyAlgorithm alg;
    3530           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    3531           1 :         CPLErrorReset();
    3532           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--if=GTIFF"}));
    3533           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3534             :     }
    3535           1 : }
    3536             : 
    3537           4 : TEST_F(test_gdal_algorithm, arg_layer_name_single)
    3538             : {
    3539             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3540             :     {
    3541             :       public:
    3542             :         std::string m_layerName{};
    3543             : 
    3544           1 :         MyAlgorithm()
    3545           1 :         {
    3546           1 :             AddLayerNameArg(&m_layerName);
    3547           1 :         }
    3548             :     };
    3549             : 
    3550             :     {
    3551           2 :         MyAlgorithm alg;
    3552           1 :         alg.GetUsageForCLI(false);
    3553             : 
    3554           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo"}));
    3555           1 :         EXPECT_STREQ(alg.m_layerName.c_str(), "foo");
    3556             :     }
    3557           1 : }
    3558             : 
    3559           4 : TEST_F(test_gdal_algorithm, arg_layer_name_multiple)
    3560             : {
    3561             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3562             :     {
    3563             :       public:
    3564             :         std::vector<std::string> m_layerNames{};
    3565             : 
    3566           1 :         MyAlgorithm()
    3567           1 :         {
    3568           1 :             AddLayerNameArg(&m_layerNames);
    3569           1 :         }
    3570             :     };
    3571             : 
    3572             :     {
    3573           2 :         MyAlgorithm alg;
    3574           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"-l", "foo", "-l", "bar"}));
    3575           1 :         EXPECT_EQ(alg.m_layerNames.size(), 2U);
    3576             :     }
    3577           1 : }
    3578             : 
    3579           4 : TEST_F(test_gdal_algorithm, arg_co)
    3580             : {
    3581             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3582             :     {
    3583             :       public:
    3584             :         std::vector<std::string> m_co{};
    3585             : 
    3586           7 :         MyAlgorithm()
    3587           7 :         {
    3588           7 :             AddCreationOptionsArg(&m_co);
    3589           7 :         }
    3590             :     };
    3591             : 
    3592             :     {
    3593           2 :         MyAlgorithm alg;
    3594           1 :         alg.GetUsageForCLI(false);
    3595             : 
    3596           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3597             :             {"--co", "foo=bar", "--co", "bar=baz"}));
    3598           6 :         const std::vector<std::string> expected{"foo=bar", "bar=baz"};
    3599           1 :         EXPECT_EQ(alg.m_co, expected);
    3600             :     }
    3601             : 
    3602             :     {
    3603           2 :         MyAlgorithm alg;
    3604           1 :         alg.GetUsageForCLI(false);
    3605             : 
    3606           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,bar=baz"}));
    3607           6 :         const std::vector<std::string> expected{"foo=bar", "bar=baz"};
    3608           1 :         EXPECT_EQ(alg.m_co, expected);
    3609             :     }
    3610             : 
    3611             :     {
    3612           2 :         MyAlgorithm alg;
    3613           1 :         alg.GetUsageForCLI(false);
    3614             : 
    3615           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,baz"}));
    3616           5 :         const std::vector<std::string> expected{"foo=bar,baz"};
    3617           1 :         EXPECT_EQ(alg.m_co, expected);
    3618             :     }
    3619             : 
    3620             :     {
    3621           2 :         MyAlgorithm alg;
    3622           1 :         alg.GetUsageForCLI(false);
    3623             : 
    3624           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar=,a"}));
    3625           5 :         const std::vector<std::string> expected{"foo=bar=,a"};
    3626           1 :         EXPECT_EQ(alg.m_co, expected);
    3627             :     }
    3628             : 
    3629             :     {
    3630           2 :         MyAlgorithm alg;
    3631           1 :         alg.GetUsageForCLI(false);
    3632             : 
    3633           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--co", "foo=bar,,"}));
    3634           5 :         const std::vector<std::string> expected{"foo=bar,,"};
    3635           1 :         EXPECT_EQ(alg.m_co, expected);
    3636             :     }
    3637             : 
    3638             :     {
    3639           2 :         MyAlgorithm alg;
    3640           1 :         alg.GetUsageForCLI(false);
    3641             : 
    3642           4 :         EXPECT_TRUE(
    3643             :             alg.ParseCommandLineArguments({"--co", "foo=bar,\"foo=baz\""}));
    3644           5 :         const std::vector<std::string> expected{"foo=bar,\"foo=baz\""};
    3645           1 :         EXPECT_EQ(alg.m_co, expected);
    3646             :     }
    3647             : 
    3648             :     {
    3649           2 :         MyAlgorithm alg;
    3650           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3651           1 :         CPLErrorReset();
    3652           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--co", "foo"}));
    3653           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3654             :     }
    3655           1 : }
    3656             : 
    3657           4 : TEST_F(test_gdal_algorithm, arg_lco)
    3658             : {
    3659             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3660             :     {
    3661             :       public:
    3662             :         std::vector<std::string> m_lco{};
    3663             : 
    3664           2 :         MyAlgorithm()
    3665           2 :         {
    3666           2 :             AddLayerCreationOptionsArg(&m_lco);
    3667           2 :         }
    3668             :     };
    3669             : 
    3670             :     {
    3671           2 :         MyAlgorithm alg;
    3672           1 :         alg.GetUsageForCLI(false);
    3673             : 
    3674           6 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3675             :             {"--lco", "foo=bar", "--lco", "bar=baz"}));
    3676           1 :         EXPECT_EQ(alg.m_lco.size(), 2U);
    3677             :     }
    3678             : 
    3679             :     {
    3680           2 :         MyAlgorithm alg;
    3681           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3682           1 :         CPLErrorReset();
    3683           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--lco", "foo"}));
    3684           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3685             :     }
    3686           1 : }
    3687             : 
    3688           4 : TEST_F(test_gdal_algorithm, arg_band)
    3689             : {
    3690             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3691             :     {
    3692             :       public:
    3693             :         int m_band{};
    3694             : 
    3695           2 :         MyAlgorithm()
    3696           2 :         {
    3697           2 :             AddBandArg(&m_band);
    3698           2 :         }
    3699             :     };
    3700             : 
    3701             :     {
    3702           2 :         MyAlgorithm alg;
    3703           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--band=1"}));
    3704           1 :         EXPECT_EQ(alg.m_band, 1);
    3705             :     }
    3706             : 
    3707             :     {
    3708           2 :         MyAlgorithm alg;
    3709           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3710           1 :         CPLErrorReset();
    3711           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--band=0"}));
    3712           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3713             :     }
    3714           1 : }
    3715             : 
    3716           4 : TEST_F(test_gdal_algorithm, arg_band_with_input_dataset)
    3717             : {
    3718             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3719             :     {
    3720             :       public:
    3721             :         GDALArgDatasetValue m_input{};
    3722             :         int m_band{};
    3723             : 
    3724           3 :         MyAlgorithm()
    3725           3 :         {
    3726           3 :             AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false);
    3727           3 :             AddBandArg(&m_band);
    3728           3 :         }
    3729             :     };
    3730             : 
    3731             :     {
    3732           2 :         MyAlgorithm alg;
    3733           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3734             :             {std::string("--input=")
    3735             :                  .append(tut::common::data_basedir)
    3736             :                  .append(SEP)
    3737             :                  .append("byte.tif"),
    3738             :              "--band=1"}));
    3739           1 :         EXPECT_EQ(alg.m_band, 1);
    3740             :     }
    3741             : 
    3742             :     {
    3743           2 :         MyAlgorithm alg;
    3744           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3745           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3746             :             {std::string("--input=")
    3747             :                  .append(tut::common::data_basedir)
    3748             :                  .append(SEP)
    3749             :                  .append("byte.tif"),
    3750             :              "--band=2"}));
    3751             :     }
    3752             : 
    3753             :     {
    3754           2 :         MyAlgorithm alg;
    3755           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3756           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3757             :             {"--input=i_do_not_exist", "--band=1"}));
    3758             :     }
    3759           1 : }
    3760             : 
    3761           4 : TEST_F(test_gdal_algorithm, AddInputDatasetArg_single)
    3762             : {
    3763             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3764             :     {
    3765             :       public:
    3766             :         GDALArgDatasetValue m_input{};
    3767             : 
    3768           1 :         MyAlgorithm()
    3769           1 :         {
    3770           1 :             AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false)
    3771           1 :                 .SetAutoOpenDataset(false);
    3772           1 :         }
    3773             :     };
    3774             : 
    3775             :     {
    3776           2 :         MyAlgorithm alg;
    3777           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=-"}));
    3778           1 :         EXPECT_STREQ(alg.m_input.GetName().c_str(), "/vsistdin/");
    3779             :     }
    3780           1 : }
    3781             : 
    3782           4 : TEST_F(test_gdal_algorithm, AddInputDatasetArg_several)
    3783             : {
    3784             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3785             :     {
    3786             :       public:
    3787             :         std::vector<GDALArgDatasetValue> m_input{};
    3788             : 
    3789           1 :         MyAlgorithm()
    3790           1 :         {
    3791           1 :             AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false)
    3792           1 :                 .SetAutoOpenDataset(false);
    3793           1 :         }
    3794             :     };
    3795             : 
    3796             :     {
    3797           1 :         MyAlgorithm alg;
    3798           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--input=-"}));
    3799           1 :         ASSERT_EQ(alg.m_input.size(), 1);
    3800           1 :         EXPECT_STREQ(alg.m_input[0].GetName().c_str(), "/vsistdin/");
    3801             :     }
    3802             : }
    3803             : 
    3804           4 : TEST_F(test_gdal_algorithm, arg_band_vector)
    3805             : {
    3806             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3807             :     {
    3808             :       public:
    3809             :         std::vector<int> m_band{};
    3810             : 
    3811           2 :         MyAlgorithm()
    3812           2 :         {
    3813           2 :             AddBandArg(&m_band);
    3814           2 :         }
    3815             :     };
    3816             : 
    3817             :     {
    3818           2 :         MyAlgorithm alg;
    3819           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--band=1,2"}));
    3820           2 :         const std::vector<int> expected{1, 2};
    3821           1 :         EXPECT_EQ(alg.m_band, expected);
    3822             :     }
    3823             : 
    3824             :     {
    3825           2 :         MyAlgorithm alg;
    3826           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3827           1 :         CPLErrorReset();
    3828           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--band=1,0"}));
    3829           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3830             :     }
    3831           1 : }
    3832             : 
    3833           4 : TEST_F(test_gdal_algorithm, arg_band_vector_with_input_dataset)
    3834             : {
    3835             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3836             :     {
    3837             :       public:
    3838             :         GDALArgDatasetValue m_input{};
    3839             :         std::vector<int> m_band{};
    3840             : 
    3841           3 :         MyAlgorithm()
    3842           3 :         {
    3843           3 :             AddInputDatasetArg(&m_input, GDAL_OF_RASTER, false);
    3844           3 :             AddBandArg(&m_band);
    3845           3 :         }
    3846             :     };
    3847             : 
    3848             :     {
    3849           2 :         MyAlgorithm alg;
    3850           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments(
    3851             :             {std::string("--input=")
    3852             :                  .append(tut::common::data_basedir)
    3853             :                  .append(SEP)
    3854             :                  .append("byte.tif"),
    3855             :              "--band=1"}));
    3856           2 :         const std::vector<int> expected{1};
    3857           1 :         EXPECT_EQ(alg.m_band, expected);
    3858             :     }
    3859             : 
    3860             :     {
    3861           2 :         MyAlgorithm alg;
    3862           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3863           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3864             :             {std::string("--input=")
    3865             :                  .append(tut::common::data_basedir)
    3866             :                  .append(SEP)
    3867             :                  .append("byte.tif"),
    3868             :              "--band=2"}));
    3869             :     }
    3870             : 
    3871             :     {
    3872           2 :         MyAlgorithm alg;
    3873           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3874           4 :         EXPECT_FALSE(alg.ParseCommandLineArguments(
    3875             :             {"--input=i_do_not_exist", "--band=1"}));
    3876             :     }
    3877           1 : }
    3878             : 
    3879           4 : TEST_F(test_gdal_algorithm, SetHiddenForCLI)
    3880             : {
    3881             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3882             :     {
    3883             :       public:
    3884             :         bool m_b = false;
    3885             : 
    3886           1 :         MyAlgorithm()
    3887           1 :         {
    3888           2 :             AddArg("flag", 0, "", &m_b)
    3889           1 :                 .SetHiddenForCLI()
    3890           1 :                 .SetCategory(GAAC_ESOTERIC);
    3891           1 :         }
    3892             :     };
    3893             : 
    3894           1 :     MyAlgorithm alg;
    3895           1 :     alg.GetUsageForCLI(false);
    3896           1 : }
    3897             : 
    3898           4 : TEST_F(test_gdal_algorithm, SetOnlyForCLI)
    3899             : {
    3900             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3901             :     {
    3902             :       public:
    3903             :         bool m_b = false;
    3904             : 
    3905           1 :         MyAlgorithm()
    3906           1 :         {
    3907           2 :             AddArg("flag", 0, "", &m_b)
    3908           1 :                 .SetOnlyForCLI()
    3909           1 :                 .SetCategory("my category");
    3910           1 :             m_longDescription = "long description";
    3911           1 :         }
    3912             :     };
    3913             : 
    3914           1 :     MyAlgorithm alg;
    3915           1 :     alg.GetUsageForCLI(false);
    3916           1 : }
    3917             : 
    3918           4 : TEST_F(test_gdal_algorithm, SetSkipIfAlreadySet)
    3919             : {
    3920             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3921             :     {
    3922             :       public:
    3923             :         int m_val = 0;
    3924             : 
    3925           2 :         MyAlgorithm()
    3926           2 :         {
    3927           2 :             AddArg("option", 0, "option", &m_val).SetPositional();
    3928           2 :         }
    3929             :     };
    3930             : 
    3931             :     {
    3932           2 :         MyAlgorithm alg;
    3933           1 :         alg.GetArg("option")->Set(1);
    3934           1 :         alg.GetArg("option")->SetSkipIfAlreadySet();
    3935           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--option=1"}));
    3936             :     }
    3937             : 
    3938             :     {
    3939           2 :         MyAlgorithm alg;
    3940           1 :         alg.GetArg("option")->Set(1);
    3941           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    3942           1 :         CPLErrorReset();
    3943           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"--option=1"}));
    3944           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    3945             :     }
    3946           1 : }
    3947             : 
    3948           4 : TEST_F(test_gdal_algorithm, alg_with_aliases)
    3949             : {
    3950             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3951             :     {
    3952             :       public:
    3953             :         int m_val = 0;
    3954             : 
    3955           1 :         MyAlgorithm()
    3956           1 :         {
    3957           1 :             m_aliases.push_back("one_alias");
    3958           1 :             m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
    3959           1 :             m_aliases.push_back("hidden_alias");
    3960           1 :         }
    3961             :     };
    3962             : 
    3963           2 :     MyAlgorithm alg;
    3964           1 :     alg.GetUsageForCLI(false);
    3965           1 :     EXPECT_EQ(alg.GetAliases().size(), 3U);
    3966           1 : }
    3967             : 
    3968           4 : TEST_F(test_gdal_algorithm, subalgorithms)
    3969             : {
    3970           1 :     bool hasRun = false;
    3971             : 
    3972             :     class SubAlgorithm : public GDALAlgorithm
    3973             :     {
    3974             :       public:
    3975             :         bool &m_bHasRun;
    3976             :         bool m_flag = false;
    3977             : 
    3978           4 :         SubAlgorithm(bool &lHasRun)
    3979           4 :             : GDALAlgorithm("subalg", "", "https://example.com"),
    3980           4 :               m_bHasRun(lHasRun)
    3981             :         {
    3982           4 :             AddProgressArg();
    3983           4 :             m_aliases.push_back("one_alias");
    3984           4 :             m_aliases.push_back(GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR);
    3985           4 :             m_aliases.push_back("hidden_alias");
    3986           4 :         }
    3987             : 
    3988           1 :         bool RunImpl(GDALProgressFunc, void *) override
    3989             :         {
    3990           1 :             m_bHasRun = true;
    3991           1 :             return true;
    3992             :         }
    3993             :     };
    3994             : 
    3995             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    3996             :     {
    3997             :       public:
    3998           5 :         MyAlgorithm(bool &lHasRun)
    3999           5 :         {
    4000          10 :             GDALAlgorithmRegistry::AlgInfo info;
    4001           5 :             info.m_name = "subalg";
    4002           4 :             info.m_creationFunc = [&lHasRun]()
    4003           9 :             { return std::make_unique<SubAlgorithm>(lHasRun); };
    4004           5 :             RegisterSubAlgorithm(info);
    4005             :             // RegisterSubAlgorithm(SubAlgorithm);
    4006           5 :         }
    4007             :     };
    4008             : 
    4009             :     {
    4010           2 :         MyAlgorithm alg(hasRun);
    4011           1 :         alg.GetUsageForCLI(false);
    4012             : 
    4013           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4014           1 :         CPLErrorReset();
    4015           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    4016           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4017             :     }
    4018             : 
    4019             :     {
    4020           2 :         MyAlgorithm alg(hasRun);
    4021           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4022           1 :         CPLErrorReset();
    4023           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"invalid_subcommand"}));
    4024           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4025             :     }
    4026             : 
    4027             :     {
    4028           1 :         MyAlgorithm alg(hasRun);
    4029           2 :         alg.SetCallPath(std::vector<std::string>{"main"});
    4030           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg"}));
    4031           1 :         EXPECT_STREQ(alg.GetActualAlgorithm().GetName().c_str(), "subalg");
    4032           1 :         EXPECT_TRUE(alg.ValidateArguments());
    4033           1 :         EXPECT_TRUE(alg.Run());
    4034           1 :         EXPECT_TRUE(hasRun);
    4035           1 :         EXPECT_TRUE(alg.Finalize());
    4036           1 :         alg.GetUsageForCLI(false);
    4037             :     }
    4038             : 
    4039             :     {
    4040           1 :         MyAlgorithm alg(hasRun);
    4041           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "-h"}));
    4042           1 :         EXPECT_TRUE(alg.IsHelpRequested());
    4043           1 :         EXPECT_TRUE(alg.ValidateArguments());
    4044           1 :         alg.GetUsageForCLI(false);
    4045             :     }
    4046             : 
    4047             :     {
    4048           1 :         MyAlgorithm alg(hasRun);
    4049           4 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"subalg", "--progress"}));
    4050           1 :         EXPECT_TRUE(alg.IsProgressBarRequested());
    4051           1 :         EXPECT_TRUE(alg.ValidateArguments());
    4052           1 :         alg.GetUsageForCLI(false);
    4053             :     }
    4054           1 : }
    4055             : 
    4056             : class MyRedundantRasterAlgorithm : public MyAlgorithmWithDummyRun
    4057             : {
    4058             :   public:
    4059             :     static constexpr const char *NAME = "raster";
    4060             :     static constexpr const char *DESCRIPTION =
    4061             :         "redundant with existing raster!!!";
    4062             :     static constexpr const char *HELP_URL = "";
    4063             : };
    4064             : 
    4065             : class MyAlgorithmWithAlias : public MyAlgorithmWithDummyRun
    4066             : {
    4067             :   public:
    4068             :     static constexpr const char *NAME = "MyAlgorithmWithAlias";
    4069             :     static constexpr const char *DESCRIPTION = "";
    4070             :     static constexpr const char *HELP_URL = "";
    4071             : 
    4072           1 :     static std::vector<std::string> GetAliasesStatic()
    4073             :     {
    4074             :         return {"alias", GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR,
    4075           4 :                 "hidden_alias"};
    4076             :     }
    4077             : };
    4078             : 
    4079             : class MyAlgorithmWithRedundantAlias : public MyAlgorithmWithDummyRun
    4080             : {
    4081             :   public:
    4082             :     static constexpr const char *NAME = "MyAlgorithmWithRedundantAlias";
    4083             :     static constexpr const char *DESCRIPTION = "";
    4084             :     static constexpr const char *HELP_URL = "";
    4085             : 
    4086           1 :     static std::vector<std::string> GetAliasesStatic()
    4087             :     {
    4088           2 :         return {"alias"};
    4089             :     }
    4090             : };
    4091             : 
    4092             : class MyAlgorithmWithRedundantHiddenAlias : public MyAlgorithmWithDummyRun
    4093             : {
    4094             :   public:
    4095             :     static constexpr const char *NAME = "MyAlgorithmWithRedundantHiddenAlias";
    4096             :     static constexpr const char *DESCRIPTION = "";
    4097             :     static constexpr const char *HELP_URL = "";
    4098             : 
    4099           1 :     static std::vector<std::string> GetAliasesStatic()
    4100             :     {
    4101           3 :         return {GDALAlgorithmRegistry::HIDDEN_ALIAS_SEPARATOR, "hidden_alias"};
    4102             :     }
    4103             : };
    4104             : 
    4105           4 : TEST_F(test_gdal_algorithm, GDALGlobalAlgorithmRegistry)
    4106             : {
    4107           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4108           1 :     EXPECT_NE(singleton.GetInfo("raster"), nullptr);
    4109           1 :     EXPECT_EQ(singleton.GetInfo("not_existing"), nullptr);
    4110           2 :     auto alg = singleton.Instantiate("raster");
    4111           1 :     ASSERT_NE(alg, nullptr);
    4112           1 :     EXPECT_TRUE(!alg->GetUsageAsJSON().empty());
    4113             : 
    4114             :     {
    4115           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4116           1 :         EXPECT_FALSE(singleton.Register<MyRedundantRasterAlgorithm>());
    4117             :     }
    4118             : 
    4119           1 :     EXPECT_TRUE(singleton.Register<MyAlgorithmWithAlias>());
    4120             :     {
    4121           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4122           1 :         EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantAlias>());
    4123             :     }
    4124             :     {
    4125           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4126           1 :         EXPECT_FALSE(singleton.Register<MyAlgorithmWithRedundantHiddenAlias>());
    4127             :     }
    4128             : }
    4129             : 
    4130           4 : TEST_F(test_gdal_algorithm, vector_pipeline_GetUsageForCLI)
    4131             : {
    4132           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4133           2 :     auto vector = singleton.Instantiate("vector");
    4134           1 :     ASSERT_NE(vector, nullptr);
    4135           2 :     auto pipeline = vector->InstantiateSubAlgorithm("pipeline");
    4136           1 :     ASSERT_NE(pipeline, nullptr);
    4137           1 :     pipeline->GetUsageForCLI(false);
    4138           1 :     pipeline->GetUsageForCLI(true);
    4139             : }
    4140             : 
    4141           4 : TEST_F(test_gdal_algorithm, raster_pipeline_GetUsageForCLI)
    4142             : {
    4143           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4144           2 :     auto raster = singleton.Instantiate("raster");
    4145           1 :     ASSERT_NE(raster, nullptr);
    4146           2 :     auto pipeline = raster->InstantiateSubAlgorithm("pipeline");
    4147           1 :     ASSERT_NE(pipeline, nullptr);
    4148           1 :     pipeline->GetUsageForCLI(false);
    4149           1 :     pipeline->GetUsageForCLI(true);
    4150             : 
    4151             :     {
    4152           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4153           1 :         CPLErrorReset();
    4154           2 :         EXPECT_EQ(raster->InstantiateSubAlgorithm("pipline"), nullptr);
    4155           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    4156             :                      "Algorithm 'pipline' is unknown. Do you mean 'pipeline'?");
    4157             :     }
    4158             : 
    4159             :     {
    4160           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4161           1 :         CPLErrorReset();
    4162           2 :         EXPECT_EQ(raster->InstantiateSubAlgorithm("pipleine"), nullptr);
    4163           1 :         EXPECT_STREQ(
    4164             :             CPLGetLastErrorMsg(),
    4165             :             "Algorithm 'pipleine' is unknown. Do you mean 'pipeline'?");
    4166             :     }
    4167             : }
    4168             : 
    4169           4 : TEST_F(test_gdal_algorithm, registry_c_api)
    4170             : {
    4171           1 :     auto reg = GDALGetGlobalAlgorithmRegistry();
    4172           1 :     ASSERT_NE(reg, nullptr);
    4173           1 :     char **names = GDALAlgorithmRegistryGetAlgNames(reg);
    4174           1 :     EXPECT_GE(CSLCount(names), 2);
    4175           1 :     CSLDestroy(names);
    4176           1 :     auto alg = GDALAlgorithmRegistryInstantiateAlg(reg, "raster");
    4177           1 :     ASSERT_NE(alg, nullptr);
    4178           1 :     EXPECT_EQ(GDALAlgorithmRegistryInstantiateAlg(reg, "not_existing"),
    4179             :               nullptr);
    4180           1 :     GDALAlgorithmRelease(alg);
    4181           1 :     GDALAlgorithmRegistryRelease(reg);
    4182             : }
    4183             : 
    4184           4 : TEST_F(test_gdal_algorithm, algorithm_c_api)
    4185             : {
    4186             :     class MyAlgorithm : public GDALAlgorithm
    4187             :     {
    4188             :       public:
    4189             :         bool m_flag = false;
    4190             :         std::string m_str{};
    4191             :         int m_int = 0;
    4192             :         double m_double = 0;
    4193             :         std::vector<std::string> m_strlist{};
    4194             :         std::vector<int> m_intlist{};
    4195             :         std::vector<double> m_doublelist{};
    4196             :         GDALArgDatasetValue m_dsValue{};
    4197             : 
    4198             :         bool m_hasParsedCommandLinearguments = false;
    4199             :         bool m_hasRun = false;
    4200             :         bool m_hasFinalized = false;
    4201             : 
    4202           1 :         MyAlgorithm()
    4203           1 :             : GDALAlgorithm("test", "description", "http://example.com")
    4204             :         {
    4205           1 :             m_longDescription = "long description";
    4206           1 :             AddArg("flag", 'f', "boolean flag", &m_flag);
    4207           1 :             AddArg("str", 0, "str", &m_str);
    4208           1 :             AddArg("int", 0, "int", &m_int);
    4209           1 :             AddArg("double", 0, "double", &m_double);
    4210           1 :             AddArg("strlist", 0, "strlist", &m_strlist);
    4211           1 :             AddArg("doublelist", 0, "doublelist", &m_doublelist);
    4212           1 :             AddArg("intlist", 0, "intlist", &m_intlist);
    4213           1 :             AddArg("dataset", 0, "dataset", &m_dsValue);
    4214           1 :         }
    4215             : 
    4216             :         bool
    4217           1 :         ParseCommandLineArguments(const std::vector<std::string> &args) override
    4218             :         {
    4219           1 :             m_hasParsedCommandLinearguments = true;
    4220           1 :             return GDALAlgorithm::ParseCommandLineArguments(args);
    4221             :         }
    4222             : 
    4223           1 :         bool RunImpl(GDALProgressFunc, void *) override
    4224             :         {
    4225           1 :             m_hasRun = true;
    4226           1 :             return true;
    4227             :         }
    4228             : 
    4229           1 :         bool Finalize() override
    4230             :         {
    4231           1 :             m_hasFinalized = true;
    4232           1 :             return GDALAlgorithm::Finalize();
    4233             :         }
    4234             :     };
    4235             : 
    4236             :     auto hAlg =
    4237           1 :         std::make_unique<GDALAlgorithmHS>(std::make_unique<MyAlgorithm>());
    4238           1 :     MyAlgorithm *pAlg = cpl::down_cast<MyAlgorithm *>(hAlg->ptr);
    4239           1 :     EXPECT_STREQ(GDALAlgorithmGetName(hAlg.get()), "test");
    4240           1 :     EXPECT_STREQ(GDALAlgorithmGetDescription(hAlg.get()), "description");
    4241           1 :     EXPECT_STREQ(GDALAlgorithmGetLongDescription(hAlg.get()),
    4242             :                  "long description");
    4243           1 :     EXPECT_STREQ(GDALAlgorithmGetHelpFullURL(hAlg.get()), "http://example.com");
    4244           1 :     EXPECT_FALSE(GDALAlgorithmHasSubAlgorithms(hAlg.get()));
    4245           1 :     EXPECT_EQ(GDALAlgorithmGetSubAlgorithmNames(hAlg.get()), nullptr);
    4246           1 :     EXPECT_EQ(GDALAlgorithmInstantiateSubAlgorithm(hAlg.get(), "not_existing"),
    4247             :               nullptr);
    4248           3 :     EXPECT_TRUE(GDALAlgorithmParseCommandLineArguments(
    4249             :         hAlg.get(), CPLStringList(std::vector<std::string>({"-f"})).List()));
    4250           1 :     EXPECT_TRUE(pAlg->m_hasParsedCommandLinearguments);
    4251           1 :     EXPECT_TRUE(GDALAlgorithmRun(hAlg.get(), nullptr, nullptr));
    4252           1 :     EXPECT_TRUE(pAlg->m_hasRun);
    4253           1 :     EXPECT_TRUE(GDALAlgorithmFinalize(hAlg.get()));
    4254           1 :     EXPECT_TRUE(pAlg->m_hasFinalized);
    4255           1 :     char *jsonUsage = GDALAlgorithmGetUsageAsJSON(hAlg.get());
    4256           1 :     EXPECT_NE(jsonUsage, nullptr);
    4257           1 :     CPLFree(jsonUsage);
    4258             : 
    4259           1 :     char **argNames = GDALAlgorithmGetArgNames(hAlg.get());
    4260           1 :     ASSERT_NE(argNames, nullptr);
    4261           1 :     EXPECT_EQ(CSLCount(argNames), 12);
    4262           1 :     CSLDestroy(argNames);
    4263             : 
    4264           1 :     EXPECT_EQ(GDALAlgorithmGetArg(hAlg.get(), "non_existing"), nullptr);
    4265             :     {
    4266           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "flag");
    4267           1 :         ASSERT_NE(hArg, nullptr);
    4268           1 :         GDALAlgorithmArgSetAsBoolean(hArg, true);
    4269           1 :         EXPECT_TRUE(GDALAlgorithmArgGetAsBoolean(hArg));
    4270           1 :         GDALAlgorithmArgRelease(hArg);
    4271             :     }
    4272             :     {
    4273           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "str");
    4274           1 :         ASSERT_NE(hArg, nullptr);
    4275           1 :         GDALAlgorithmArgSetAsString(hArg, "foo");
    4276           1 :         EXPECT_STREQ(GDALAlgorithmArgGetAsString(hArg), "foo");
    4277           1 :         GDALAlgorithmArgRelease(hArg);
    4278             :     }
    4279             :     {
    4280           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "int");
    4281           1 :         ASSERT_NE(hArg, nullptr);
    4282           1 :         GDALAlgorithmArgSetAsInteger(hArg, 2);
    4283           1 :         EXPECT_EQ(GDALAlgorithmArgGetAsInteger(hArg), 2);
    4284           1 :         GDALAlgorithmArgRelease(hArg);
    4285             :     }
    4286             :     {
    4287           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "double");
    4288           1 :         ASSERT_NE(hArg, nullptr);
    4289           1 :         GDALAlgorithmArgSetAsDouble(hArg, 2.5);
    4290           1 :         EXPECT_EQ(GDALAlgorithmArgGetAsDouble(hArg), 2.5);
    4291           1 :         GDALAlgorithmArgRelease(hArg);
    4292             :     }
    4293             :     {
    4294           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "strlist");
    4295           1 :         ASSERT_NE(hArg, nullptr);
    4296           6 :         const CPLStringList list(std::vector<std::string>({"foo", "bar"}));
    4297           1 :         GDALAlgorithmArgSetAsStringList(hArg, list.List());
    4298           1 :         char **ret = GDALAlgorithmArgGetAsStringList(hArg);
    4299           1 :         EXPECT_EQ(CSLCount(ret), 2);
    4300           1 :         CSLDestroy(ret);
    4301           1 :         GDALAlgorithmArgRelease(hArg);
    4302             :     }
    4303             :     {
    4304           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "intlist");
    4305           1 :         ASSERT_NE(hArg, nullptr);
    4306           1 :         std::vector<int> vals{2, 3};
    4307           1 :         GDALAlgorithmArgSetAsIntegerList(hArg, vals.size(), vals.data());
    4308           1 :         size_t nCount = 0;
    4309           1 :         const int *ret = GDALAlgorithmArgGetAsIntegerList(hArg, &nCount);
    4310           1 :         ASSERT_EQ(nCount, 2);
    4311           1 :         ASSERT_NE(ret, nullptr);
    4312           1 :         EXPECT_EQ(ret[0], 2);
    4313           1 :         EXPECT_EQ(ret[1], 3);
    4314           1 :         GDALAlgorithmArgRelease(hArg);
    4315             :     }
    4316             :     {
    4317           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "doublelist");
    4318           1 :         ASSERT_NE(hArg, nullptr);
    4319           1 :         std::vector<double> vals{2.5, 3.5};
    4320           1 :         GDALAlgorithmArgSetAsDoubleList(hArg, vals.size(), vals.data());
    4321           1 :         size_t nCount = 0;
    4322           1 :         const double *ret = GDALAlgorithmArgGetAsDoubleList(hArg, &nCount);
    4323           1 :         ASSERT_EQ(nCount, 2);
    4324           1 :         ASSERT_NE(ret, nullptr);
    4325           1 :         EXPECT_EQ(ret[0], 2.5);
    4326           1 :         EXPECT_EQ(ret[1], 3.5);
    4327           1 :         GDALAlgorithmArgRelease(hArg);
    4328             :     }
    4329             :     {
    4330           1 :         auto hArg = GDALAlgorithmGetArg(hAlg.get(), "dataset");
    4331           1 :         ASSERT_NE(hArg, nullptr);
    4332           1 :         EXPECT_EQ(GDALAlgorithmArgGetDatasetType(hArg),
    4333             :                   GDAL_OF_RASTER | GDAL_OF_VECTOR | GDAL_OF_MULTIDIM_RASTER);
    4334           1 :         EXPECT_EQ(GDALAlgorithmArgGetDatasetInputFlags(hArg),
    4335             :                   GADV_NAME | GADV_OBJECT);
    4336           1 :         EXPECT_EQ(GDALAlgorithmArgGetDatasetOutputFlags(hArg), GADV_OBJECT);
    4337           1 :         GDALArgDatasetValueH hVal = GDALArgDatasetValueCreate();
    4338           1 :         GDALArgDatasetValueSetName(hVal, "foo");
    4339             : 
    4340             :         {
    4341             :             auto poDS = std::unique_ptr<GDALDataset>(
    4342             :                 GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    4343           2 :                     "", 1, 1, 1, GDT_Byte, nullptr));
    4344           1 :             GDALArgDatasetValueSetDataset(hVal, poDS.release());
    4345             :         }
    4346             : 
    4347           1 :         GDALAlgorithmArgSetAsDatasetValue(hArg, hVal);
    4348           1 :         GDALArgDatasetValueRelease(hVal);
    4349             : 
    4350           1 :         hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
    4351           1 :         ASSERT_NE(hVal, nullptr);
    4352           1 :         auto hDS = GDALArgDatasetValueGetDatasetRef(hVal);
    4353           1 :         EXPECT_NE(hDS, nullptr);
    4354             :         {
    4355           1 :             auto hDS2 = GDALArgDatasetValueGetDatasetIncreaseRefCount(hVal);
    4356           1 :             EXPECT_EQ(hDS2, hDS);
    4357           1 :             GDALReleaseDataset(hDS2);
    4358             :         }
    4359           1 :         GDALArgDatasetValueRelease(hVal);
    4360             : 
    4361           1 :         GDALAlgorithmArgSetDataset(hArg, nullptr);
    4362             : 
    4363           1 :         hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
    4364           1 :         ASSERT_NE(hVal, nullptr);
    4365           1 :         EXPECT_EQ(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
    4366           1 :         GDALArgDatasetValueRelease(hVal);
    4367             : 
    4368             :         {
    4369             :             auto poDS = std::unique_ptr<GDALDataset>(
    4370             :                 GetGDALDriverManager()->GetDriverByName("MEM")->Create(
    4371           2 :                     "", 1, 1, 1, GDT_Byte, nullptr));
    4372           1 :             GDALAlgorithmArgSetDataset(hArg, poDS.release());
    4373             :         }
    4374             : 
    4375           1 :         hVal = GDALAlgorithmArgGetAsDatasetValue(hArg);
    4376           1 :         ASSERT_NE(hVal, nullptr);
    4377           1 :         EXPECT_NE(GDALArgDatasetValueGetDatasetRef(hVal), nullptr);
    4378           1 :         GDALArgDatasetValueRelease(hVal);
    4379             : 
    4380           1 :         GDALAlgorithmArgRelease(hArg);
    4381             :     }
    4382             : }
    4383             : 
    4384           4 : TEST_F(test_gdal_algorithm, DispatcherGetUsageForCLI)
    4385             : {
    4386           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4387             :     {
    4388           2 :         auto info = singleton.Instantiate("info");
    4389           1 :         info->GetUsageForCLI(false);
    4390             :     }
    4391             :     {
    4392           2 :         auto info = singleton.Instantiate("info");
    4393           3 :         EXPECT_TRUE(info->ParseCommandLineArguments(
    4394             :             std::vector<std::string>{GCORE_DATA_DIR "byte.tif"}));
    4395           1 :         info->GetUsageForCLI(false);
    4396             :     }
    4397             :     {
    4398           1 :         auto poDriver = GetGDALDriverManager()->GetDriverByName("GPKG");
    4399           1 :         if (!poDriver)
    4400             :         {
    4401           0 :             GTEST_SKIP() << "GPKG support missing";
    4402             :         }
    4403             :         else
    4404             :         {
    4405             :             std::string osTmpFilename =
    4406           2 :                 VSIMemGenerateHiddenFilename("temp.gpkg");
    4407             :             auto poDS = std::unique_ptr<GDALDataset>(poDriver->Create(
    4408           2 :                 osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, nullptr));
    4409           1 :             double adfGT[] = {1, 1, 0, 1, 0, -1};
    4410           1 :             poDS->SetGeoTransform(adfGT);
    4411           1 :             poDS->CreateLayer("foo");
    4412           1 :             poDS.reset();
    4413             : 
    4414           3 :             auto info = singleton.Instantiate("info");
    4415           2 :             CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4416           3 :             EXPECT_FALSE(info->ParseCommandLineArguments(
    4417             :                 std::vector<std::string>{osTmpFilename.c_str()}));
    4418           1 :             info->GetUsageForCLI(false);
    4419             : 
    4420           1 :             VSIUnlink(osTmpFilename.c_str());
    4421             :         }
    4422             :     }
    4423             : }
    4424             : 
    4425           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_dataset_0_0)
    4426             : {
    4427           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4428           2 :     auto raster = singleton.Instantiate("raster");
    4429           1 :     ASSERT_NE(raster, nullptr);
    4430           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4431           1 :     ASSERT_NE(edit, nullptr);
    4432             : 
    4433             :     class MyDataset : public GDALDataset
    4434             :     {
    4435             :       public:
    4436           1 :         MyDataset()
    4437           1 :         {
    4438           1 :             nRasterXSize = 0;
    4439           1 :             nRasterYSize = 0;
    4440           1 :             eAccess = GA_Update;
    4441           1 :         }
    4442             :     };
    4443             : 
    4444           1 :     auto datasetArg = edit->GetArg("dataset");
    4445           1 :     ASSERT_NE(datasetArg, nullptr);
    4446           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4447             : 
    4448           1 :     auto extentArg = edit->GetArg("bbox");
    4449           1 :     ASSERT_NE(extentArg, nullptr);
    4450           1 :     extentArg->Set(std::vector<double>{2, 49, 3, 50});
    4451             : 
    4452           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4453           1 :     CPLErrorReset();
    4454           1 :     EXPECT_FALSE(edit->Run());
    4455           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4456           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Cannot set extent because one of "
    4457             :                                        "dataset height or width is null");
    4458             : }
    4459             : 
    4460           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_none)
    4461             : {
    4462           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4463           2 :     auto raster = singleton.Instantiate("raster");
    4464           1 :     ASSERT_NE(raster, nullptr);
    4465           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4466           1 :     ASSERT_NE(edit, nullptr);
    4467             : 
    4468             :     class MyDataset : public GDALDataset
    4469             :     {
    4470             :       public:
    4471           1 :         MyDataset()
    4472           1 :         {
    4473           1 :             eAccess = GA_Update;
    4474           1 :         }
    4475             : 
    4476           1 :         CPLErr SetSpatialRef(const OGRSpatialReference *) override
    4477             :         {
    4478           1 :             return CE_Failure;
    4479             :         }
    4480             :     };
    4481             : 
    4482           1 :     auto datasetArg = edit->GetArg("dataset");
    4483           1 :     ASSERT_NE(datasetArg, nullptr);
    4484           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4485             : 
    4486           1 :     auto crsArg = edit->GetArg("crs");
    4487           1 :     ASSERT_NE(crsArg, nullptr);
    4488           1 :     crsArg->Set("none");
    4489             : 
    4490           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4491           1 :     CPLErrorReset();
    4492           1 :     EXPECT_FALSE(edit->Run());
    4493           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4494           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: SetSpatialRef(none) failed");
    4495             : }
    4496             : 
    4497           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_spatial_ref_regular)
    4498             : {
    4499           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4500           2 :     auto raster = singleton.Instantiate("raster");
    4501           1 :     ASSERT_NE(raster, nullptr);
    4502           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4503           1 :     ASSERT_NE(edit, nullptr);
    4504             : 
    4505             :     class MyDataset : public GDALDataset
    4506             :     {
    4507             :       public:
    4508           1 :         MyDataset()
    4509           1 :         {
    4510           1 :             eAccess = GA_Update;
    4511           1 :         }
    4512             : 
    4513           1 :         CPLErr SetSpatialRef(const OGRSpatialReference *) override
    4514             :         {
    4515           1 :             return CE_Failure;
    4516             :         }
    4517             :     };
    4518             : 
    4519           1 :     auto datasetArg = edit->GetArg("dataset");
    4520           1 :     ASSERT_NE(datasetArg, nullptr);
    4521           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4522             : 
    4523           1 :     auto crsArg = edit->GetArg("crs");
    4524           1 :     ASSERT_NE(crsArg, nullptr);
    4525           1 :     crsArg->Set("EPSG:32632");
    4526             : 
    4527           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4528           1 :     CPLErrorReset();
    4529           1 :     EXPECT_FALSE(edit->Run());
    4530           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4531           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(),
    4532             :                  "edit: SetSpatialRef(EPSG:32632) failed");
    4533             : }
    4534             : 
    4535           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_geo_transform)
    4536             : {
    4537           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4538           2 :     auto raster = singleton.Instantiate("raster");
    4539           1 :     ASSERT_NE(raster, nullptr);
    4540           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4541           1 :     ASSERT_NE(edit, nullptr);
    4542             : 
    4543             :     class MyDataset : public GDALDataset
    4544             :     {
    4545             :       public:
    4546           1 :         MyDataset()
    4547           1 :         {
    4548           1 :             eAccess = GA_Update;
    4549           1 :         }
    4550             : 
    4551           1 :         CPLErr SetGeoTransform(const GDALGeoTransform &) override
    4552             :         {
    4553           1 :             return CE_Failure;
    4554             :         }
    4555             :     };
    4556             : 
    4557           1 :     auto datasetArg = edit->GetArg("dataset");
    4558           1 :     ASSERT_NE(datasetArg, nullptr);
    4559           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4560             : 
    4561           1 :     auto extentArg = edit->GetArg("bbox");
    4562           1 :     ASSERT_NE(extentArg, nullptr);
    4563           1 :     extentArg->Set(std::vector<double>{2, 49, 3, 50});
    4564             : 
    4565           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4566           1 :     CPLErrorReset();
    4567           1 :     EXPECT_FALSE(edit->Run());
    4568           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4569           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(), "edit: Setting extent failed");
    4570             : }
    4571             : 
    4572           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_set_metadata)
    4573             : {
    4574           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4575           2 :     auto raster = singleton.Instantiate("raster");
    4576           1 :     ASSERT_NE(raster, nullptr);
    4577           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4578           1 :     ASSERT_NE(edit, nullptr);
    4579             : 
    4580             :     class MyDataset : public GDALDataset
    4581             :     {
    4582             :       public:
    4583           1 :         MyDataset()
    4584           1 :         {
    4585           1 :             eAccess = GA_Update;
    4586           1 :         }
    4587             : 
    4588           1 :         CPLErr SetMetadataItem(const char *, const char *,
    4589             :                                const char *) override
    4590             :         {
    4591           1 :             return CE_Failure;
    4592             :         }
    4593             :     };
    4594             : 
    4595           1 :     auto datasetArg = edit->GetArg("dataset");
    4596           1 :     ASSERT_NE(datasetArg, nullptr);
    4597           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4598             : 
    4599           1 :     auto extentArg = edit->GetArg("metadata");
    4600           1 :     ASSERT_NE(extentArg, nullptr);
    4601           2 :     extentArg->Set(std::vector<std::string>{"foo=bar"});
    4602             : 
    4603           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4604           1 :     CPLErrorReset();
    4605           1 :     EXPECT_FALSE(edit->Run());
    4606           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4607           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(),
    4608             :                  "edit: SetMetadataItem('foo', 'bar') failed");
    4609             : }
    4610             : 
    4611           4 : TEST_F(test_gdal_algorithm, raster_edit_failures_unset_metadata)
    4612             : {
    4613           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4614           2 :     auto raster = singleton.Instantiate("raster");
    4615           1 :     ASSERT_NE(raster, nullptr);
    4616           2 :     auto edit = raster->InstantiateSubAlgorithm("edit");
    4617           1 :     ASSERT_NE(edit, nullptr);
    4618             : 
    4619             :     class MyDataset : public GDALDataset
    4620             :     {
    4621             :       public:
    4622           1 :         MyDataset()
    4623           1 :         {
    4624           1 :             eAccess = GA_Update;
    4625           1 :         }
    4626             : 
    4627           1 :         CPLErr SetMetadataItem(const char *, const char *,
    4628             :                                const char *) override
    4629             :         {
    4630           1 :             return CE_Failure;
    4631             :         }
    4632             :     };
    4633             : 
    4634           1 :     auto datasetArg = edit->GetArg("dataset");
    4635           1 :     ASSERT_NE(datasetArg, nullptr);
    4636           1 :     datasetArg->Get<GDALArgDatasetValue>().Set(std::make_unique<MyDataset>());
    4637             : 
    4638           1 :     auto extentArg = edit->GetArg("unset-metadata");
    4639           1 :     ASSERT_NE(extentArg, nullptr);
    4640           2 :     extentArg->Set(std::vector<std::string>{"foo"});
    4641             : 
    4642           2 :     CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4643           1 :     CPLErrorReset();
    4644           1 :     EXPECT_FALSE(edit->Run());
    4645           1 :     EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4646           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(),
    4647             :                  "edit: SetMetadataItem('foo', NULL) failed");
    4648             : }
    4649             : 
    4650           4 : TEST_F(test_gdal_algorithm, register_plugin_algorithms)
    4651             : {
    4652           1 :     auto &singleton = GDALGlobalAlgorithmRegistry::GetSingleton();
    4653           1 :     bool flag = false;
    4654           3 :     singleton.DeclareAlgorithm(
    4655             :         {"foo", "bar"},
    4656           5 :         [&flag]() -> std::unique_ptr<GDALAlgorithm>
    4657             :         {
    4658           5 :             flag = true;
    4659           5 :             return std::make_unique<GDALContainerAlgorithm>("dummy");
    4660           2 :         });
    4661             : 
    4662             :     {
    4663           2 :         EXPECT_NE(singleton.Instantiate("foo"), nullptr);
    4664           1 :         EXPECT_FALSE(flag);
    4665             :     }
    4666             : 
    4667             :     {
    4668           5 :         auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal"});
    4669           1 :         EXPECT_TRUE(std::find(got.begin(), got.end(), "foo") != got.end());
    4670           1 :         EXPECT_FALSE(flag);
    4671             :     }
    4672             : 
    4673             :     {
    4674           5 :         auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal", "foo"});
    4675           1 :         EXPECT_TRUE(std::find(got.begin(), got.end(), "bar") != got.end());
    4676           1 :         EXPECT_TRUE(flag);
    4677           1 :         flag = false;
    4678             :     }
    4679             : 
    4680             :     {
    4681             :         auto got =
    4682           7 :             singleton.GetDeclaredSubAlgorithmNames({"gdal", "foo", "bar"});
    4683           1 :         EXPECT_TRUE(got.empty());
    4684           1 :         EXPECT_FALSE(flag);
    4685             :     }
    4686             : 
    4687             :     {
    4688           6 :         auto got = singleton.GetDeclaredSubAlgorithmNames({"gdal", "bar"});
    4689           1 :         EXPECT_TRUE(got.empty());
    4690           1 :         EXPECT_FALSE(flag);
    4691             :     }
    4692             : 
    4693             :     {
    4694           5 :         auto alg = singleton.InstantiateDeclaredSubAlgorithm({"gdal", "foo"});
    4695           1 :         ASSERT_NE(alg, nullptr);
    4696           1 :         EXPECT_TRUE(alg->HasSubAlgorithms());
    4697           1 :         EXPECT_EQ(alg->GetSubAlgorithmNames().size(), 1);
    4698           1 :         EXPECT_TRUE(flag);
    4699           1 :         flag = false;
    4700             :     }
    4701             : 
    4702             :     {
    4703             :         auto alg =
    4704           6 :             singleton.InstantiateDeclaredSubAlgorithm({"gdal", "foo", "bar"});
    4705           1 :         ASSERT_NE(alg, nullptr);
    4706           1 :         EXPECT_TRUE(flag);
    4707           1 :         flag = false;
    4708             :     }
    4709             : 
    4710             :     {
    4711           2 :         auto alg = singleton.Instantiate("foo")->InstantiateSubAlgorithm("bar");
    4712           1 :         ASSERT_NE(alg, nullptr);
    4713           1 :         EXPECT_TRUE(flag);
    4714             :     }
    4715             : 
    4716             :     {
    4717           5 :         auto alg = singleton.InstantiateDeclaredSubAlgorithm({"gdal", "bar"});
    4718           1 :         ASSERT_EQ(alg, nullptr);
    4719             :     }
    4720             : 
    4721           3 :     singleton.DeclareAlgorithm({"foo", "bar"},
    4722           0 :                                []() -> std::unique_ptr<GDALAlgorithm>
    4723           2 :                                { return nullptr; });
    4724             : }
    4725             : 
    4726           4 : TEST_F(test_gdal_algorithm, AddNumThreadsArg)
    4727             : {
    4728             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    4729             :     {
    4730             :       public:
    4731             :         int m_numThreads = 0;
    4732             :         std::string m_numThreadsStr{"ALL_CPUS"};
    4733             : 
    4734           8 :         MyAlgorithm()
    4735           8 :         {
    4736           8 :             AddNumThreadsArg(&m_numThreads, &m_numThreadsStr);
    4737           8 :         }
    4738             :     };
    4739             : 
    4740             :     {
    4741           2 :         MyAlgorithm alg;
    4742           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    4743           1 :         EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
    4744             :     }
    4745             : 
    4746             :     {
    4747           2 :         CPLConfigOptionSetter oSetter("GDAL_NUM_THREADS", "1", false);
    4748           2 :         MyAlgorithm alg;
    4749           1 :         EXPECT_TRUE(alg.ParseCommandLineArguments({}));
    4750           1 :         EXPECT_EQ(alg.m_numThreads, 1);
    4751             :     }
    4752             : 
    4753             :     {
    4754           2 :         MyAlgorithm alg;
    4755           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--num-threads=1"}));
    4756           1 :         EXPECT_EQ(alg.m_numThreads, 1);
    4757             :     }
    4758             : 
    4759             :     {
    4760           2 :         MyAlgorithm alg;
    4761           3 :         EXPECT_TRUE(
    4762             :             alg.ParseCommandLineArguments({"--num-threads=2147483647"}));
    4763           1 :         EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
    4764             :     }
    4765             : 
    4766             :     {
    4767           2 :         MyAlgorithm alg;
    4768           3 :         EXPECT_TRUE(alg.ParseCommandLineArguments({"--num-threads=ALL_CPUS"}));
    4769           1 :         EXPECT_EQ(alg.m_numThreads, CPLGetNumCPUs());
    4770             :     }
    4771             : 
    4772             :     {
    4773           2 :         MyAlgorithm alg;
    4774           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    4775           1 :         CPLErrorReset();
    4776           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=invalid"}));
    4777           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4778             :     }
    4779             : 
    4780             :     {
    4781           2 :         MyAlgorithm alg;
    4782           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    4783           1 :         CPLErrorReset();
    4784           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=-1"}));
    4785           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4786             :     }
    4787             : 
    4788             :     {
    4789           2 :         MyAlgorithm alg;
    4790           2 :         CPLErrorStateBackuper oErrorHandler(CPLQuietErrorHandler);
    4791           1 :         CPLErrorReset();
    4792           3 :         EXPECT_FALSE(alg.ParseCommandLineArguments({"num-threads=2147483648"}));
    4793           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_Failure);
    4794             :     }
    4795           1 : }
    4796             : 
    4797           4 : TEST_F(test_gdal_algorithm, AddAppendLayerArg_without_update)
    4798             : {
    4799             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    4800             :     {
    4801             :       public:
    4802             :         bool m_boolean = false;
    4803             : 
    4804           1 :         MyAlgorithm()
    4805           1 :         {
    4806           1 :             AddAppendLayerArg(&m_boolean);
    4807           1 :         }
    4808             :     };
    4809             : 
    4810             :     {
    4811           2 :         MyAlgorithm alg;
    4812           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4813           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    4814           1 :         EXPECT_STREQ(
    4815             :             CPLGetLastErrorMsg(),
    4816             :             "test: --update argument must exist for --append, even if hidden");
    4817             :     }
    4818           1 : }
    4819             : 
    4820           4 : TEST_F(test_gdal_algorithm, AddOverwriteLayerArg_without_update)
    4821             : {
    4822             :     class MyAlgorithm : public MyAlgorithmWithDummyRun
    4823             :     {
    4824             :       public:
    4825             :         bool m_boolean = false;
    4826             : 
    4827           1 :         MyAlgorithm()
    4828           1 :         {
    4829           1 :             AddOverwriteLayerArg(&m_boolean);
    4830           1 :         }
    4831             :     };
    4832             : 
    4833             :     {
    4834           2 :         MyAlgorithm alg;
    4835           2 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
    4836           1 :         EXPECT_FALSE(alg.ParseCommandLineArguments({}));
    4837           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    4838             :                      "test: --update argument must exist for "
    4839             :                      "--overwrite-layer, even if hidden");
    4840             :     }
    4841           1 : }
    4842             : 
    4843             : }  // namespace test_gdal_algorithm
    4844             : 
    4845             : #if defined(__clang__)
    4846             : #pragma clang diagnostic pop
    4847             : #endif

Generated by: LCOV version 1.14