LCOV - code coverage report
Current view: top level - autotest/cpp - test_gdal_algorithm.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1789 1793 99.8 %
Date: 2025-01-18 12:42:00 Functions: 426 426 100.0 %

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

Generated by: LCOV version 1.14