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

Generated by: LCOV version 1.14