LCOV - code coverage report
Current view: top level - autotest/cpp - test_cpl.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3420 3495 97.9 %
Date: 2026-10-02 21:07:22 Functions: 511 516 99.0 %

          Line data    Source code
       1             : ///////////////////////////////////////////////////////////////////////////////
       2             : //
       3             : // Project:  C++ Test Suite for GDAL/OGR
       4             : // Purpose:  Test general CPL features.
       5             : // Author:   Mateusz Loskot <mateusz@loskot.net>
       6             : //
       7             : ///////////////////////////////////////////////////////////////////////////////
       8             : // Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
       9             : // Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
      10             : // Copyright (c) 2017, Dmitry Baryshnikov <polimax@mail.ru>
      11             : // Copyright (c) 2017, NextGIS <info@nextgis.com>
      12             : /*
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : #ifndef GDAL_COMPILATION
      17             : #define GDAL_COMPILATION
      18             : #endif
      19             : 
      20             : #include "gdal_unit_test.h"
      21             : 
      22             : #include "cpl_compressor.h"
      23             : #include "cpl_enumerate.h"
      24             : #include "cpl_error.h"
      25             : #include "cpl_error_internal.h"
      26             : #include "cpl_float.h"
      27             : #include "cpl_hash_set.h"
      28             : #include "cpl_levenshtein.h"
      29             : #include "cpl_list.h"
      30             : #include "cpl_mask.h"
      31             : #include "cpl_sha256.h"
      32             : #include "cpl_string.h"
      33             : #include "cpl_safemaths.hpp"
      34             : #include "cpl_time.h"
      35             : #include "cpl_json.h"
      36             : #include "cpl_json_streaming_parser.h"
      37             : #include "cpl_json_streaming_writer.h"
      38             : #include "cpl_mem_cache.h"
      39             : #include "cpl_http.h"
      40             : #include "cpl_auto_close.h"
      41             : #include "cpl_minixml.h"
      42             : #include "cpl_quad_tree.h"
      43             : #include "cpl_spawn.h"
      44             : #include "cpl_worker_thread_pool.h"
      45             : #include "cpl_vsi_virtual.h"
      46             : #include "cpl_threadsafe_queue.hpp"
      47             : 
      48             : #include <algorithm>
      49             : #include <atomic>
      50             : #include <charconv>
      51             : #include <cmath>
      52             : #include <limits>
      53             : #include <fstream>
      54             : #include <string>
      55             : 
      56             : #if !defined(_WIN32)
      57             : #include <unistd.h>
      58             : #endif
      59             : 
      60             : #include "test_data.h"
      61             : 
      62             : #include "gtest_include.h"
      63             : 
      64             : static bool gbGotError = false;
      65             : 
      66           2 : static void CPL_STDCALL myErrorHandler(CPLErr, CPLErrorNum, const char *)
      67             : {
      68           2 :     gbGotError = true;
      69           2 : }
      70             : 
      71             : namespace
      72             : {
      73             : 
      74             : // Common fixture with test data
      75             : struct test_cpl : public ::testing::Test
      76             : {
      77             :     std::string data_;
      78             : 
      79         112 :     test_cpl()
      80         112 :     {
      81             :         // Compose data path for test group
      82         112 :         data_ = tut::common::data_basedir;
      83         112 :     }
      84             : 
      85         112 :     void SetUp() override
      86             :     {
      87         112 :         CPLSetConfigOptions(nullptr);
      88         112 :         CPLSetThreadLocalConfigOptions(nullptr);
      89         112 :     }
      90             : };
      91             : 
      92             : // Test cpl_list API
      93           4 : TEST_F(test_cpl, CPLList)
      94             : {
      95             :     CPLList *list;
      96             : 
      97           1 :     list = CPLListInsert(nullptr, (void *)nullptr, 0);
      98           1 :     EXPECT_TRUE(CPLListCount(list) == 1);
      99           1 :     list = CPLListRemove(list, 2);
     100           1 :     EXPECT_TRUE(CPLListCount(list) == 1);
     101           1 :     list = CPLListRemove(list, 1);
     102           1 :     EXPECT_TRUE(CPLListCount(list) == 1);
     103           1 :     list = CPLListRemove(list, 0);
     104           1 :     EXPECT_TRUE(CPLListCount(list) == 0);
     105           1 :     list = nullptr;
     106             : 
     107           1 :     list = CPLListInsert(nullptr, (void *)nullptr, 2);
     108           1 :     EXPECT_TRUE(CPLListCount(list) == 3);
     109           1 :     list = CPLListRemove(list, 2);
     110           1 :     EXPECT_TRUE(CPLListCount(list) == 2);
     111           1 :     list = CPLListRemove(list, 1);
     112           1 :     EXPECT_TRUE(CPLListCount(list) == 1);
     113           1 :     list = CPLListRemove(list, 0);
     114           1 :     EXPECT_TRUE(CPLListCount(list) == 0);
     115           1 :     list = nullptr;
     116             : 
     117           1 :     list = CPLListAppend(list, (void *)1);
     118           1 :     EXPECT_TRUE(CPLListGet(list, 0) == list);
     119           1 :     EXPECT_TRUE(CPLListGet(list, 1) == nullptr);
     120           1 :     list = CPLListAppend(list, (void *)2);
     121           1 :     list = CPLListInsert(list, (void *)3, 2);
     122           1 :     EXPECT_TRUE(CPLListCount(list) == 3);
     123           1 :     CPLListDestroy(list);
     124           1 :     list = nullptr;
     125             : 
     126           1 :     list = CPLListAppend(list, (void *)1);
     127           1 :     list = CPLListAppend(list, (void *)2);
     128           1 :     list = CPLListInsert(list, (void *)4, 3);
     129           1 :     CPLListGet(list, 2)->pData = (void *)3;
     130           1 :     EXPECT_TRUE(CPLListCount(list) == 4);
     131           1 :     EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
     132           1 :     EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
     133           1 :     EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
     134           1 :     EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
     135           1 :     CPLListDestroy(list);
     136           1 :     list = nullptr;
     137             : 
     138           1 :     list = CPLListInsert(list, (void *)4, 1);
     139           1 :     CPLListGet(list, 0)->pData = (void *)2;
     140           1 :     list = CPLListInsert(list, (void *)1, 0);
     141           1 :     list = CPLListInsert(list, (void *)3, 2);
     142           1 :     EXPECT_TRUE(CPLListCount(list) == 4);
     143           1 :     EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
     144           1 :     EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
     145           1 :     EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
     146           1 :     EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
     147           1 :     list = CPLListRemove(list, 1);
     148           1 :     list = CPLListRemove(list, 1);
     149           1 :     list = CPLListRemove(list, 0);
     150           1 :     list = CPLListRemove(list, 0);
     151           1 :     EXPECT_TRUE(list == nullptr);
     152           1 : }
     153             : 
     154             : typedef struct
     155             : {
     156             :     const char *testString;
     157             :     CPLValueType expectedResult;
     158             : } TestStringStruct;
     159             : 
     160             : // Test CPLGetValueType
     161           4 : TEST_F(test_cpl, CPLGetValueType)
     162             : {
     163           1 :     TestStringStruct asTestStrings[] = {
     164             :         {"+25.e+3", CPL_VALUE_REAL},   {"-25.e-3", CPL_VALUE_REAL},
     165             :         {"25.e3", CPL_VALUE_REAL},     {"25e3", CPL_VALUE_REAL},
     166             :         {" 25e3 ", CPL_VALUE_REAL},    {".1e3", CPL_VALUE_REAL},
     167             : 
     168             :         {"25", CPL_VALUE_INTEGER},     {"-25", CPL_VALUE_INTEGER},
     169             :         {"+25", CPL_VALUE_INTEGER},
     170             : 
     171             :         {"25e 3", CPL_VALUE_STRING},   {"25e.3", CPL_VALUE_STRING},
     172             :         {"-2-5e3", CPL_VALUE_STRING},  {"2-5e3", CPL_VALUE_STRING},
     173             :         {"25.25.3", CPL_VALUE_STRING}, {"25e25e3", CPL_VALUE_STRING},
     174             :         {"25e2500", CPL_VALUE_STRING}, /* #6128 */
     175             : 
     176             :         {"d1", CPL_VALUE_STRING}, /* #6305 */
     177             : 
     178             :         {"01", CPL_VALUE_STRING},      {"0.1", CPL_VALUE_REAL},
     179             :         {"0", CPL_VALUE_INTEGER},
     180             :     };
     181             : 
     182          21 :     for (const auto &sText : asTestStrings)
     183             :     {
     184          20 :         EXPECT_EQ(CPLGetValueType(sText.testString), sText.expectedResult)
     185           0 :             << sText.testString;
     186             :     }
     187           1 : }
     188             : 
     189             : // Test cpl_hash_set API
     190           4 : TEST_F(test_cpl, CPLHashSet)
     191             : {
     192             :     CPLHashSet *set =
     193           1 :         CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
     194           1 :     EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("hello")) == TRUE);
     195           1 :     EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("good morning")) == TRUE);
     196           1 :     EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == TRUE);
     197           1 :     EXPECT_TRUE(CPLHashSetSize(set) == 3);
     198           1 :     EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == FALSE);
     199           1 :     EXPECT_TRUE(CPLHashSetSize(set) == 3);
     200           1 :     EXPECT_TRUE(CPLHashSetRemove(set, "bye bye") == TRUE);
     201           1 :     EXPECT_TRUE(CPLHashSetSize(set) == 2);
     202           1 :     EXPECT_TRUE(CPLHashSetRemove(set, "good afternoon") == FALSE);
     203           1 :     EXPECT_TRUE(CPLHashSetSize(set) == 2);
     204           1 :     CPLHashSetDestroy(set);
     205           1 : }
     206             : 
     207        1000 : static int sumValues(void *elt, void *user_data)
     208             : {
     209        1000 :     int *pnSum = (int *)user_data;
     210        1000 :     *pnSum += *(int *)elt;
     211        1000 :     return TRUE;
     212             : }
     213             : 
     214             : // Test cpl_hash_set API
     215           4 : TEST_F(test_cpl, CPLHashSet2)
     216             : {
     217           1 :     const int HASH_SET_SIZE = 1000;
     218             : 
     219             :     int data[HASH_SET_SIZE];
     220        1001 :     for (int i = 0; i < HASH_SET_SIZE; ++i)
     221             :     {
     222        1000 :         data[i] = i;
     223             :     }
     224             : 
     225           1 :     CPLHashSet *set = CPLHashSetNew(nullptr, nullptr, nullptr);
     226        1001 :     for (int i = 0; i < HASH_SET_SIZE; i++)
     227             :     {
     228        1000 :         EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == TRUE);
     229             :     }
     230           1 :     EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
     231             : 
     232        1001 :     for (int i = 0; i < HASH_SET_SIZE; i++)
     233             :     {
     234        1000 :         EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == FALSE);
     235             :     }
     236           1 :     EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
     237             : 
     238        1001 :     for (int i = 0; i < HASH_SET_SIZE; i++)
     239             :     {
     240        1000 :         EXPECT_TRUE(CPLHashSetLookup(set, (const void *)&data[i]) ==
     241             :                     (const void *)&data[i]);
     242             :     }
     243             : 
     244           1 :     int sum = 0;
     245           1 :     CPLHashSetForeach(set, sumValues, &sum);
     246           1 :     EXPECT_EQ(sum, (HASH_SET_SIZE - 1) * HASH_SET_SIZE / 2);
     247             : 
     248        1001 :     for (int i = 0; i < HASH_SET_SIZE; i++)
     249             :     {
     250        1000 :         EXPECT_TRUE(CPLHashSetRemove(set, (void *)&data[i]) == TRUE);
     251             :     }
     252           1 :     EXPECT_EQ(CPLHashSetSize(set), 0);
     253             : 
     254           1 :     CPLHashSetDestroy(set);
     255           1 : }
     256             : 
     257             : // Test cpl_string API
     258           4 : TEST_F(test_cpl, CSLTokenizeString2)
     259             : {
     260             :     {
     261             :         CPLStringList aosStringList(
     262           1 :             CSLTokenizeString2("one two three", " ", 0));
     263           1 :         ASSERT_EQ(aosStringList.size(), 3);
     264           1 :         EXPECT_STREQ(aosStringList[0], "one");
     265           1 :         EXPECT_STREQ(aosStringList[1], "two");
     266           1 :         EXPECT_STREQ(aosStringList[2], "three");
     267             : 
     268             :         // Test range-based for loop
     269           1 :         int i = 0;
     270           4 :         for (const char *pszVal : aosStringList)
     271             :         {
     272           3 :             EXPECT_STREQ(pszVal, aosStringList[i]);
     273           3 :             ++i;
     274             :         }
     275           1 :         EXPECT_EQ(i, 3);
     276             :     }
     277             :     {
     278           2 :         CPLStringList aosStringList;
     279             :         // Test range-based for loop on empty list
     280           1 :         int i = 0;
     281           1 :         for (const char *pszVal : aosStringList)
     282             :         {
     283           0 :             EXPECT_EQ(pszVal, nullptr);  // should not reach that point...
     284           0 :             ++i;
     285             :         }
     286           1 :         EXPECT_EQ(i, 0);
     287             :     }
     288             :     {
     289             :         CPLStringList aosStringList(
     290           1 :             CSLTokenizeString2(",one two, three;four,five; six,", " ;,", 0));
     291           1 :         ASSERT_EQ(aosStringList.size(), 6);
     292           1 :         EXPECT_STREQ(aosStringList[0], "one");
     293           1 :         EXPECT_STREQ(aosStringList[1], "two");
     294           1 :         EXPECT_STREQ(aosStringList[2], "three");
     295           1 :         EXPECT_STREQ(aosStringList[3], "four");
     296           1 :         EXPECT_STREQ(aosStringList[4], "five");
     297           1 :         EXPECT_STREQ(aosStringList[5], "six");
     298             :     }
     299             : 
     300             :     {
     301             :         CPLStringList aosStringList(CSLTokenizeString2(
     302           1 :             ",one two,,,five,six,", " ,", CSLT_ALLOWEMPTYTOKENS));
     303           1 :         ASSERT_EQ(aosStringList.size(), 8);
     304           1 :         EXPECT_STREQ(aosStringList[0], "");
     305           1 :         EXPECT_STREQ(aosStringList[1], "one");
     306           1 :         EXPECT_STREQ(aosStringList[2], "two");
     307           1 :         EXPECT_STREQ(aosStringList[3], "");
     308           1 :         EXPECT_STREQ(aosStringList[4], "");
     309           1 :         EXPECT_STREQ(aosStringList[5], "five");
     310           1 :         EXPECT_STREQ(aosStringList[6], "six");
     311           1 :         EXPECT_STREQ(aosStringList[7], "");
     312             :     }
     313             : 
     314             :     {
     315             :         CPLStringList aosStringList(CSLTokenizeString2(
     316           1 :             "one two,\"three,four ,\",five,six", " ,", CSLT_HONOURSTRINGS));
     317           1 :         ASSERT_EQ(aosStringList.size(), 5);
     318           1 :         EXPECT_STREQ(aosStringList[0], "one");
     319           1 :         EXPECT_STREQ(aosStringList[1], "two");
     320           1 :         EXPECT_STREQ(aosStringList[2], "three,four ,");
     321           1 :         EXPECT_STREQ(aosStringList[3], "five");
     322           1 :         EXPECT_STREQ(aosStringList[4], "six");
     323             :     }
     324             : 
     325             :     {
     326             :         CPLStringList aosStringList(CSLTokenizeString2(
     327           1 :             "one two,\"three,four ,\",five,six", " ,", CSLT_PRESERVEQUOTES));
     328           1 :         ASSERT_EQ(aosStringList.size(), 7);
     329           1 :         EXPECT_STREQ(aosStringList[0], "one");
     330           1 :         EXPECT_STREQ(aosStringList[1], "two");
     331           1 :         EXPECT_STREQ(aosStringList[2], "\"three");
     332           1 :         EXPECT_STREQ(aosStringList[3], "four");
     333           1 :         EXPECT_STREQ(aosStringList[4], "\"");
     334           1 :         EXPECT_STREQ(aosStringList[5], "five");
     335           1 :         EXPECT_STREQ(aosStringList[6], "six");
     336             :     }
     337             : 
     338             :     {
     339             :         CPLStringList aosStringList(
     340             :             CSLTokenizeString2("one two,\"three,four ,\",five,six", " ,",
     341           1 :                                CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES));
     342           1 :         ASSERT_EQ(aosStringList.size(), 5);
     343           1 :         EXPECT_STREQ(aosStringList[0], "one");
     344           1 :         EXPECT_STREQ(aosStringList[1], "two");
     345           1 :         EXPECT_STREQ(aosStringList[2], "\"three,four ,\"");
     346           1 :         EXPECT_STREQ(aosStringList[3], "five");
     347           1 :         EXPECT_STREQ(aosStringList[4], "six");
     348             :     }
     349             : 
     350             :     {
     351             :         CPLStringList aosStringList(
     352             :             CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
     353           1 :                                CSLT_PRESERVEESCAPES));
     354           1 :         ASSERT_EQ(aosStringList.size(), 7);
     355           1 :         EXPECT_STREQ(aosStringList[0], "one");
     356           1 :         EXPECT_STREQ(aosStringList[1], "\\two");
     357           1 :         EXPECT_STREQ(aosStringList[2], "\"three");
     358           1 :         EXPECT_STREQ(aosStringList[3], "\\four");
     359           1 :         EXPECT_STREQ(aosStringList[4], "\"");
     360           1 :         EXPECT_STREQ(aosStringList[5], "five");
     361           1 :         EXPECT_STREQ(aosStringList[6], "six");
     362             :     }
     363             : 
     364             :     {
     365             :         CPLStringList aosStringList(
     366             :             CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
     367           1 :                                CSLT_PRESERVEQUOTES | CSLT_PRESERVEESCAPES));
     368           1 :         ASSERT_EQ(aosStringList.size(), 7);
     369           1 :         EXPECT_STREQ(aosStringList[0], "one");
     370           1 :         EXPECT_STREQ(aosStringList[1], "\\two");
     371           1 :         EXPECT_STREQ(aosStringList[2], "\"three");
     372           1 :         EXPECT_STREQ(aosStringList[3], "\\four");
     373           1 :         EXPECT_STREQ(aosStringList[4], "\"");
     374           1 :         EXPECT_STREQ(aosStringList[5], "five");
     375           1 :         EXPECT_STREQ(aosStringList[6], "six");
     376             :     }
     377             : 
     378             :     {
     379             :         CPLStringList aosStringList(
     380           1 :             CSLTokenizeString2("one ,two, three, four ,five  ", ",", 0));
     381           1 :         ASSERT_EQ(aosStringList.size(), 5);
     382           1 :         EXPECT_STREQ(aosStringList[0], "one ");
     383           1 :         EXPECT_STREQ(aosStringList[1], "two");
     384           1 :         EXPECT_STREQ(aosStringList[2], " three");
     385           1 :         EXPECT_STREQ(aosStringList[3], " four ");
     386           1 :         EXPECT_STREQ(aosStringList[4], "five  ");
     387             :     }
     388             : 
     389             :     {
     390             :         CPLStringList aosStringList(CSLTokenizeString2(
     391           1 :             "one ,two, three, four ,five  ", ",", CSLT_STRIPLEADSPACES));
     392           1 :         ASSERT_EQ(aosStringList.size(), 5);
     393           1 :         EXPECT_STREQ(aosStringList[0], "one ");
     394           1 :         EXPECT_STREQ(aosStringList[1], "two");
     395           1 :         EXPECT_STREQ(aosStringList[2], "three");
     396           1 :         EXPECT_STREQ(aosStringList[3], "four ");
     397           1 :         EXPECT_STREQ(aosStringList[4], "five  ");
     398             :     }
     399             : 
     400             :     {
     401             :         CPLStringList aosStringList(CSLTokenizeString2(
     402           1 :             "one ,two, three, four ,five  ", ",", CSLT_STRIPENDSPACES));
     403           1 :         ASSERT_EQ(aosStringList.size(), 5);
     404           1 :         EXPECT_STREQ(aosStringList[0], "one");
     405           1 :         EXPECT_STREQ(aosStringList[1], "two");
     406           1 :         EXPECT_STREQ(aosStringList[2], " three");
     407           1 :         EXPECT_STREQ(aosStringList[3], " four");
     408           1 :         EXPECT_STREQ(aosStringList[4], "five");
     409             :     }
     410             : 
     411             :     {
     412             :         CPLStringList aosStringList(
     413             :             CSLTokenizeString2("one ,two, three, four ,five  ", ",",
     414           1 :                                CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
     415           1 :         ASSERT_EQ(aosStringList.size(), 5);
     416           1 :         EXPECT_STREQ(aosStringList[0], "one");
     417           1 :         EXPECT_STREQ(aosStringList[1], "two");
     418           1 :         EXPECT_STREQ(aosStringList[2], "three");
     419           1 :         EXPECT_STREQ(aosStringList[3], "four");
     420           1 :         EXPECT_STREQ(aosStringList[4], "five");
     421             :     }
     422             : 
     423             :     {
     424           5 :         const std::vector<std::string> oVector{"a", "bc"};
     425             :         // Test CPLStringList(const std::vector<std::string>&) constructor
     426           1 :         const CPLStringList aosList(oVector);
     427           1 :         ASSERT_EQ(aosList.size(), 2);
     428           1 :         EXPECT_STREQ(aosList[0], "a");
     429           1 :         EXPECT_STREQ(aosList[1], "bc");
     430           1 :         EXPECT_EQ(aosList[2], nullptr);
     431             : 
     432             :         // Test CPLStringList::operator std::vector<std::string>(void) const
     433           2 :         const std::vector<std::string> oVector2(aosList);
     434           1 :         EXPECT_EQ(oVector, oVector2);
     435             : 
     436           2 :         EXPECT_EQ(oVector, cpl::ToVector(aosList.List()));
     437             :     }
     438             : 
     439             :     {
     440           2 :         const CPLStringList aosList(std::vector<std::string>{});
     441           1 :         EXPECT_EQ(aosList.List(), nullptr);
     442             :     }
     443             : 
     444             :     {
     445             :         // Test CPLStringList(std::initializer_list<const char*>) constructor
     446           1 :         const CPLStringList aosList{"a", "bc"};
     447           1 :         ASSERT_EQ(aosList.size(), 2);
     448           1 :         EXPECT_STREQ(aosList[0], "a");
     449           1 :         EXPECT_STREQ(aosList[1], "bc");
     450           1 :         EXPECT_EQ(aosList[2], nullptr);
     451             : 
     452             :         // Test cpl::Iterate(CSLConstList)
     453           1 :         CSLConstList papszList = aosList.List();
     454           1 :         CPLStringList aosList2;
     455           3 :         for (const char *pszStr : cpl::Iterate(papszList))
     456             :         {
     457           2 :             aosList2.AddString(pszStr);
     458             :         }
     459           1 :         ASSERT_EQ(aosList2.size(), 2);
     460           1 :         EXPECT_STREQ(aosList2[0], "a");
     461           1 :         EXPECT_STREQ(aosList2[1], "bc");
     462           1 :         EXPECT_EQ(aosList2[2], nullptr);
     463             :     }
     464             : 
     465             :     {
     466             :         // Test cpl::Iterate() on a null list
     467           1 :         CSLConstList papszList = nullptr;
     468           1 :         auto oIteratorWrapper = cpl::Iterate(papszList);
     469           1 :         EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
     470             :     }
     471             : 
     472             :     {
     473             :         // Test cpl::IterateNameValue()
     474           1 :         const CPLStringList aosList{"foo=bar", "illegal", "bar=baz"};
     475           1 :         CSLConstList papszList = aosList.List();
     476           1 :         std::map<std::string, std::string> oMap;
     477           3 :         for (const auto &[name, value] : cpl::IterateNameValue(papszList))
     478             :         {
     479           2 :             oMap[name] = value;
     480             :         }
     481           1 :         ASSERT_EQ(oMap.size(), 2U);
     482           2 :         EXPECT_EQ(oMap["foo"], "bar");
     483           2 :         EXPECT_EQ(oMap["bar"], "baz");
     484             :     }
     485             : 
     486             :     {
     487             :         // Test cpl::IterateNameValue() on a list with only invalid values
     488           2 :         const CPLStringList aosList{"illegal"};
     489           1 :         CSLConstList papszList = aosList.List();
     490           1 :         auto oIteratorWrapper = cpl::IterateNameValue(papszList);
     491           1 :         EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
     492             :     }
     493             : 
     494             :     {
     495             :         // Test cpl::IterateNameValue() on a null list
     496           1 :         CSLConstList papszList = nullptr;
     497           1 :         auto oIteratorWrapper = cpl::IterateNameValue(papszList);
     498           1 :         EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
     499             :     }
     500             : 
     501             :     {
     502             :         // Test colon separator within string tokens
     503             :         CPLStringList aosList(CSLTokenizeString2(
     504             :             R"(one:"two:two_and_half":three:"four:four_and_half":five)", ":",
     505           2 :             CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES));
     506           1 :         EXPECT_EQ(aosList.size(), 5);
     507           1 :         EXPECT_STREQ(aosList[0], "one");
     508           1 :         EXPECT_STREQ(aosList[1], "\"two:two_and_half\"");
     509           1 :         EXPECT_STREQ(aosList[2], "three");
     510           1 :         EXPECT_STREQ(aosList[3], "\"four:four_and_half\"");
     511           1 :         EXPECT_STREQ(aosList[4], "five");
     512             :     }
     513             : }
     514             : 
     515             : typedef struct
     516             : {
     517             :     char szEncoding[24];
     518             :     char szString[1024 - 24];
     519             : } TestRecodeStruct;
     520             : 
     521             : // Test cpl_recode API
     522           4 : TEST_F(test_cpl, CPLRecode)
     523             : {
     524             :     /*
     525             :      * NOTE: This test will generally fail if iconv() is not
     526             :      *       linked in.
     527             :      *
     528             :      * CPLRecode() will be tested using the test file containing
     529             :      * a list of strings of the same text in different encoding. The
     530             :      * string is non-ASCII to avoid trivial transformations. Test file
     531             :      * has a simple binary format: a table of records, each record
     532             :      * is 1024 bytes long. The first 24 bytes of each record contain
     533             :      * encoding name (ASCII, zero padded), the last 1000 bytes contain
     534             :      * encoded string, zero padded.
     535             :      *
     536             :      * NOTE 1: We can't use a test file in human readable text format
     537             :      *         here because of multiple different encodings including
     538             :      *         multibyte ones.
     539             :      *
     540             :      * The test file could be generated with the following simple shell
     541             :      * script:
     542             :      *
     543             :      * #!/bin/sh
     544             :      *
     545             :      * # List of encodings to convert the test string into
     546             :      * ENCODINGS="UTF-8 CP1251 KOI8-R UCS-2 UCS-2BE UCS-2LE UCS-4 UCS-4BE
     547             :      * UCS-4LE UTF-16 UTF-32" # The test string itself in UTF-8 encoding. # This
     548             :      * means "Improving GDAL internationalization." in Russian.
     549             :      * TESTSTRING="\u0423\u043b\u0443\u0447\u0448\u0430\u0435\u043c
     550             :      * \u0438\u043d\u0442\u0435\u0440\u043d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e
     551             :      * GDAL."
     552             :      *
     553             :      * RECORDSIZE=1024
     554             :      * ENCSIZE=24
     555             :      *
     556             :      * i=0
     557             :      * for enc in ${ENCODINGS}; do
     558             :      *  env printf "${enc}" | dd ibs=${RECORDSIZE} conv=sync obs=1
     559             :      * seek=$((${RECORDSIZE}*${i})) of="recode-rus.dat" status=noxfer env printf
     560             :      * "${TESTSTRING}" | iconv -t ${enc} | dd ibs=${RECORDSIZE} conv=sync obs=1
     561             :      * seek=$((${RECORDSIZE}*${i}+${ENCSIZE})) of="recode-rus.dat" status=noxfer
     562             :      *  i=$((i+1))
     563             :      * done
     564             :      *
     565             :      * NOTE 2: The test string is encoded with the special format
     566             :      *         "\uXXXX" sequences, so we able to paste it here.
     567             :      *
     568             :      * NOTE 3: We need a printf utility from the coreutils because of
     569             :      *         that. "env printf" should work avoiding the shell
     570             :      *         built-in.
     571             :      *
     572             :      * NOTE 4: "iconv" utility without the "-f" option will work with
     573             :      *         encoding read from the current locale.
     574             :      *
     575             :      *  TODO: 1. Add more encodings maybe more test files.
     576             :      *        2. Add test for CPLRecodeFromWChar()/CPLRecodeToWChar().
     577             :      *        3. Test translation between each possible pair of
     578             :      *        encodings in file, not only into the UTF-8.
     579             :      */
     580             : 
     581           2 :     std::ifstream fin((data_ + SEP + "recode-rus.dat").c_str(),
     582           2 :                       std::ifstream::binary);
     583             :     TestRecodeStruct oReferenceString;
     584             : 
     585             :     // Read reference string (which is the first one in the file)
     586           1 :     fin.read(oReferenceString.szEncoding, sizeof(oReferenceString.szEncoding));
     587           1 :     oReferenceString.szEncoding[sizeof(oReferenceString.szEncoding) - 1] = '\0';
     588           1 :     fin.read(oReferenceString.szString, sizeof(oReferenceString.szString));
     589           1 :     oReferenceString.szString[sizeof(oReferenceString.szString) - 1] = '\0';
     590             : 
     591             :     while (true)
     592             :     {
     593             :         TestRecodeStruct oTestString;
     594             : 
     595          11 :         fin.read(oTestString.szEncoding, sizeof(oTestString.szEncoding));
     596          11 :         oTestString.szEncoding[sizeof(oTestString.szEncoding) - 1] = '\0';
     597          11 :         if (fin.eof())
     598           1 :             break;
     599          10 :         fin.read(oTestString.szString, sizeof(oTestString.szString));
     600          10 :         oTestString.szString[sizeof(oTestString.szString) - 1] = '\0';
     601             : 
     602             :         // Compare each string with the reference one
     603          10 :         CPLErrorReset();
     604             :         char *pszDecodedString =
     605          10 :             CPLRecode(oTestString.szString, oTestString.szEncoding,
     606             :                       oReferenceString.szEncoding);
     607          10 :         if (strstr(CPLGetLastErrorMsg(),
     608          20 :                    "Recode from CP1251 to UTF-8 not supported") != nullptr ||
     609          10 :             strstr(CPLGetLastErrorMsg(),
     610             :                    "Recode from KOI8-R to UTF-8 not supported") != nullptr)
     611             :         {
     612           0 :             CPLFree(pszDecodedString);
     613           0 :             break;
     614             :         }
     615             : 
     616          20 :         size_t nLength = std::min(strlen(pszDecodedString),
     617          10 :                                   sizeof(oReferenceString.szEncoding));
     618          10 :         bool bOK =
     619          10 :             (memcmp(pszDecodedString, oReferenceString.szString, nLength) == 0);
     620             :         // FIXME Some tests fail on Mac. Not sure why, but do not error out just
     621             :         // for that
     622          10 :         if (!bOK &&
     623           0 :             (strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") !=
     624           0 :                  nullptr ||
     625           0 :              strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
     626           0 :              getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
     627             :         {
     628           0 :             fprintf(stderr, "Recode from %s failed\n", oTestString.szEncoding);
     629             :         }
     630             :         else
     631             :         {
     632             : #ifdef CPL_MSB
     633             :             if (!bOK && strcmp(oTestString.szEncoding, "UCS-2") == 0)
     634             :             {
     635             :                 // Presumably the content in the test file is UCS-2LE, but
     636             :                 // there's no way to know the byte order without a BOM
     637             :                 fprintf(stderr, "Recode from %s failed\n",
     638             :                         oTestString.szEncoding);
     639             :             }
     640             :             else
     641             : #endif
     642             :             {
     643          10 :                 EXPECT_TRUE(bOK) << "Recode from " << oTestString.szEncoding;
     644             :             }
     645             :         }
     646          10 :         CPLFree(pszDecodedString);
     647          10 :     }
     648             : 
     649           1 :     fin.close();
     650           1 : }
     651             : 
     652             : /************************************************************************/
     653             : /*                         CPLStringList tests                          */
     654             : /************************************************************************/
     655           4 : TEST_F(test_cpl, CPLStringList_Base)
     656             : {
     657           1 :     CPLStringList oCSL;
     658             : 
     659           1 :     ASSERT_TRUE(oCSL.List() == nullptr);
     660             : 
     661           1 :     oCSL.AddString("def");
     662           1 :     oCSL.AddString("abc");
     663             : 
     664           1 :     ASSERT_EQ(oCSL.Count(), 2);
     665           1 :     ASSERT_TRUE(EQUAL(oCSL[0], "def"));
     666           1 :     ASSERT_TRUE(EQUAL(oCSL[1], "abc"));
     667           1 :     ASSERT_TRUE(oCSL[17] == nullptr);
     668           1 :     ASSERT_TRUE(oCSL[-1] == nullptr);
     669           1 :     ASSERT_EQ(oCSL.FindString("abc"), 1);
     670             : 
     671           1 :     oCSL.RemoveStrings(0, 1);
     672           1 :     ASSERT_EQ(oCSL.Count(), 1);
     673           1 :     ASSERT_EQ(oCSL.FindString("abc"), 0);
     674             : 
     675           1 :     CSLDestroy(oCSL.StealList());
     676           1 :     ASSERT_EQ(oCSL.Count(), 0);
     677           1 :     ASSERT_TRUE(oCSL.List() == nullptr);
     678             : 
     679             :     // Test that the list will make an internal copy when needed to
     680             :     // modify a read-only list.
     681             : 
     682           1 :     oCSL.AddString("def");
     683           1 :     oCSL.AddString("abc");
     684             : 
     685           1 :     CPLStringList oCopy(oCSL.List(), FALSE);
     686             : 
     687           1 :     ASSERT_EQ(oCSL.List(), oCopy.List());
     688           1 :     ASSERT_EQ(oCSL.Count(), oCopy.Count());
     689             : 
     690           1 :     oCopy.AddString("xyz");
     691           1 :     ASSERT_TRUE(oCSL.List() != oCopy.List());
     692           1 :     ASSERT_EQ(oCopy.Count(), 3);
     693           1 :     ASSERT_EQ(oCSL.Count(), 2);
     694           1 :     ASSERT_TRUE(EQUAL(oCopy[2], "xyz"));
     695             : }
     696             : 
     697           4 : TEST_F(test_cpl, CPLStringList_AddDouble)
     698             : {
     699           1 :     CPLStringList oCSL;
     700           1 :     oCSL.AddString(M_PI);
     701             : 
     702           1 :     const double dfPi = CPLStrtod(oCSL[0], nullptr);
     703           1 :     ASSERT_EQ(dfPi, M_PI);
     704             : }
     705             : 
     706             : // Test CSLRemoveStrings() with the special values of nFirstLineToDelete
     707             : // documented as meaning "remove the nNumToRemove last strings".
     708           4 : TEST_F(test_cpl, CSLRemoveStrings_last_strings)
     709             : {
     710           7 :     const auto MakeList = []()
     711             :     {
     712           7 :         char **papszList = nullptr;
     713           7 :         papszList = CSLAddString(papszList, "aaaa");
     714           7 :         papszList = CSLAddString(papszList, "bbbb");
     715           7 :         papszList = CSLAddString(papszList, "cccc");
     716           7 :         papszList = CSLAddString(papszList, "dddd");
     717           7 :         return papszList;
     718             :     };
     719             : 
     720             :     // nFirstLineToDelete == -1, discarding the removed strings.
     721             :     {
     722           1 :         char **papszList = MakeList();
     723           1 :         papszList = CSLRemoveStrings(papszList, -1, 2, nullptr);
     724           1 :         EXPECT_EQ(CSLCount(papszList), 2);
     725           1 :         if (CSLCount(papszList) == 2)
     726             :         {
     727           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     728           1 :             EXPECT_STREQ(papszList[1], "bbbb");
     729             :         }
     730           1 :         CSLDestroy(papszList);
     731             :     }
     732             : 
     733             :     // nFirstLineToDelete == -1, retrieving the removed strings.
     734             :     {
     735           1 :         char **papszList = MakeList();
     736           1 :         char **papszRemoved = nullptr;
     737           1 :         papszList = CSLRemoveStrings(papszList, -1, 2, &papszRemoved);
     738           1 :         EXPECT_EQ(CSLCount(papszList), 2);
     739           1 :         if (CSLCount(papszList) == 2)
     740             :         {
     741           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     742           1 :             EXPECT_STREQ(papszList[1], "bbbb");
     743             :         }
     744           1 :         EXPECT_EQ(CSLCount(papszRemoved), 2);
     745           1 :         if (CSLCount(papszRemoved) == 2)
     746             :         {
     747           1 :             EXPECT_STREQ(papszRemoved[0], "cccc");
     748           1 :             EXPECT_STREQ(papszRemoved[1], "dddd");
     749             :         }
     750           1 :         CSLDestroy(papszRemoved);
     751           1 :         CSLDestroy(papszList);
     752             :     }
     753             : 
     754             :     // nFirstLineToDelete larger than the number of strings.
     755             :     {
     756           1 :         char **papszList = MakeList();
     757           1 :         papszList = CSLRemoveStrings(papszList, 100, 2, nullptr);
     758           1 :         EXPECT_EQ(CSLCount(papszList), 2);
     759           1 :         if (CSLCount(papszList) == 2)
     760             :         {
     761           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     762           1 :             EXPECT_STREQ(papszList[1], "bbbb");
     763             :         }
     764           1 :         CSLDestroy(papszList);
     765             :     }
     766             : 
     767             :     // nFirstLineToDelete equal to the number of strings: the range to remove
     768             :     // would extend past the end of the list.
     769             :     {
     770           1 :         char **papszList = MakeList();
     771           1 :         papszList = CSLRemoveStrings(papszList, 4, 1, nullptr);
     772           1 :         EXPECT_EQ(CSLCount(papszList), 3);
     773           1 :         if (CSLCount(papszList) == 3)
     774             :         {
     775           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     776           1 :             EXPECT_STREQ(papszList[2], "cccc");
     777             :         }
     778           1 :         CSLDestroy(papszList);
     779             :     }
     780             : 
     781             :     // Valid nFirstLineToDelete, but nNumToRemove makes the range overrun the
     782             :     // end of the list by one.
     783             :     {
     784           1 :         char **papszList = MakeList();
     785           1 :         papszList = CSLRemoveStrings(papszList, 3, 2, nullptr);
     786           1 :         EXPECT_EQ(CSLCount(papszList), 2);
     787           1 :         if (CSLCount(papszList) == 2)
     788             :         {
     789           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     790           1 :             EXPECT_STREQ(papszList[1], "bbbb");
     791             :         }
     792           1 :         CSLDestroy(papszList);
     793             :     }
     794             : 
     795             :     // Removing from a well defined offset must be unaffected. Note that all
     796             :     // the removed strings must be freed, which ASAN/valgrind builds check.
     797             :     {
     798           1 :         char **papszList = MakeList();
     799           1 :         papszList = CSLRemoveStrings(papszList, 1, 2, nullptr);
     800           1 :         EXPECT_EQ(CSLCount(papszList), 2);
     801           1 :         if (CSLCount(papszList) == 2)
     802             :         {
     803           1 :             EXPECT_STREQ(papszList[0], "aaaa");
     804           1 :             EXPECT_STREQ(papszList[1], "dddd");
     805             :         }
     806           1 :         CSLDestroy(papszList);
     807             :     }
     808             : 
     809             :     // Same special values through the CPLStringList wrapper.
     810             :     {
     811           1 :         CPLStringList oCSL(MakeList());
     812           1 :         oCSL.RemoveStrings(-1, 2);
     813           1 :         ASSERT_EQ(oCSL.size(), 2);
     814           1 :         EXPECT_STREQ(oCSL[0], "aaaa");
     815           1 :         EXPECT_STREQ(oCSL[1], "bbbb");
     816             :     }
     817             : }
     818             : 
     819           4 : TEST_F(test_cpl, CPLStringList_SetString)
     820             : {
     821           1 :     CPLStringList oCSL;
     822             : 
     823           1 :     oCSL.AddString("abc");
     824           1 :     oCSL.AddString("def");
     825           1 :     oCSL.AddString("ghi");
     826             : 
     827           1 :     oCSL.Sort();
     828             : 
     829           1 :     CPLStringList oCSL2(oCSL.List(), false);
     830           1 :     oCSL2.Sort();
     831             : 
     832           1 :     oCSL2.SetString(0, "bcd");
     833           1 :     ASSERT_TRUE(EQUAL(oCSL[0], "abc"));
     834           1 :     ASSERT_TRUE(EQUAL(oCSL2[0], "bcd"));
     835           1 :     ASSERT_TRUE(oCSL2.IsSorted());
     836             : 
     837           1 :     oCSL2.SetString(1, std::string("efg"));
     838           1 :     ASSERT_TRUE(oCSL2.IsSorted());
     839             : 
     840           1 :     oCSL2.SetString(2, "hij");
     841           1 :     ASSERT_TRUE(oCSL2.IsSorted());
     842             : 
     843           4 :     for (int i = 0; i < oCSL.size(); i++)
     844             :     {
     845           3 :         CPLStringList oCopy(oCSL);
     846           3 :         oCopy.SetString(0, "xyz");
     847           3 :         ASSERT_FALSE(oCopy.IsSorted());
     848             :     }
     849             : }
     850             : 
     851           4 : TEST_F(test_cpl, CPLStringList_NameValue)
     852             : {
     853             :     // Test some name=value handling stuff.
     854           1 :     CPLStringList oNVL;
     855             : 
     856           1 :     oNVL.AddNameValue("KEY1", "VALUE1");
     857           1 :     oNVL.AddNameValue("2KEY", "VALUE2");
     858           1 :     ASSERT_EQ(oNVL.Count(), 2);
     859           1 :     ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE2"));
     860           1 :     ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
     861             : 
     862           1 :     oNVL.AddNameValue("KEY1", "VALUE3");
     863           1 :     ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("KEY1"), "VALUE1"));
     864           1 :     ASSERT_TRUE(EQUAL(oNVL[2], "KEY1=VALUE3"));
     865           1 :     ASSERT_TRUE(EQUAL(oNVL.FetchNameValueDef("MISSING", "X"), "X"));
     866             : 
     867           1 :     oNVL.SetNameValue("2KEY", "VALUE4");
     868           1 :     ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE4"));
     869           1 :     ASSERT_EQ(oNVL.Count(), 3);
     870             : 
     871             :     // make sure deletion works.
     872           1 :     oNVL.SetNameValue("2KEY", nullptr);
     873           1 :     ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
     874           1 :     ASSERT_EQ(oNVL.Count(), 2);
     875             : 
     876             :     // Test boolean support.
     877           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
     878           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), FALSE);
     879             : 
     880           1 :     oNVL.SetNameValue("BOOL", "YES");
     881           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
     882           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
     883             : 
     884           1 :     oNVL.SetNameValue("BOOL", "1");
     885           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
     886             : 
     887           1 :     oNVL.SetNameValue("BOOL", "0");
     888           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
     889             : 
     890           1 :     oNVL.SetNameValue("BOOL", "FALSE");
     891           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
     892             : 
     893           1 :     oNVL.SetNameValue("BOOL", "ON");
     894           1 :     ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
     895             : 
     896             :     // Test assignment operator.
     897           1 :     CPLStringList oCopy;
     898             : 
     899             :     {
     900           2 :         CPLStringList oTemp;
     901           1 :         oTemp.AddString("test");
     902           1 :         oCopy = oTemp;
     903           1 :         oTemp.AddString("avoid_coverity_scan_warning");
     904             :     }
     905           1 :     EXPECT_STREQ(oCopy[0], "test");
     906             : 
     907           1 :     auto &oCopyRef(oCopy);
     908           1 :     oCopy = oCopyRef;
     909           1 :     EXPECT_STREQ(oCopy[0], "test");
     910             : 
     911             :     // Test copy constructor.
     912           1 :     CPLStringList oCopy2(oCopy);
     913           1 :     EXPECT_EQ(oCopy2.Count(), oCopy.Count());
     914           1 :     oCopy.Clear();
     915           1 :     EXPECT_STREQ(oCopy2[0], "test");
     916             : 
     917             :     // Test move constructor
     918           1 :     CPLStringList oMoved(std::move(oCopy2));
     919           1 :     EXPECT_STREQ(oMoved[0], "test");
     920             : 
     921             :     // Test move assignment operator
     922           1 :     CPLStringList oMoved2;
     923           1 :     oMoved2 = std::move(oMoved);
     924           1 :     EXPECT_STREQ(oMoved2[0], "test");
     925             : 
     926             :     // Test sorting
     927           1 :     CPLStringList oTestSort;
     928           1 :     oTestSort.AddNameValue("Z", "1");
     929           1 :     oTestSort.AddNameValue("L", "2");
     930           1 :     oTestSort.AddNameValue("T", "3");
     931           1 :     oTestSort.AddNameValue("A", "4");
     932           1 :     oTestSort.Sort();
     933           1 :     EXPECT_STREQ(oTestSort[0], "A=4");
     934           1 :     EXPECT_STREQ(oTestSort[1], "L=2");
     935           1 :     EXPECT_STREQ(oTestSort[2], "T=3");
     936           1 :     EXPECT_STREQ(oTestSort[3], "Z=1");
     937           1 :     ASSERT_EQ(oTestSort[4], (const char *)nullptr);
     938             : 
     939             :     // Test FetchNameValue() in a sorted list
     940           1 :     EXPECT_STREQ(oTestSort.FetchNameValue("A"), "4");
     941           1 :     EXPECT_STREQ(oTestSort.FetchNameValue("L"), "2");
     942           1 :     EXPECT_STREQ(oTestSort.FetchNameValue("T"), "3");
     943           1 :     EXPECT_STREQ(oTestSort.FetchNameValue("Z"), "1");
     944             : 
     945             :     // Test AddNameValue() in a sorted list
     946           1 :     oTestSort.AddNameValue("B", "5");
     947           1 :     EXPECT_STREQ(oTestSort[0], "A=4");
     948           1 :     EXPECT_STREQ(oTestSort[1], "B=5");
     949           1 :     EXPECT_STREQ(oTestSort[2], "L=2");
     950           1 :     EXPECT_STREQ(oTestSort[3], "T=3");
     951           1 :     EXPECT_STREQ(oTestSort[4], "Z=1");
     952           1 :     ASSERT_EQ(oTestSort[5], (const char *)nullptr);
     953             : 
     954             :     // Test SetNameValue() of an existing item in a sorted list
     955           1 :     oTestSort.SetNameValue("Z", "6");
     956           1 :     EXPECT_STREQ(oTestSort[4], "Z=6");
     957             : 
     958             :     // Test SetNameValue() of a non-existing item in a sorted list
     959           1 :     oTestSort.SetNameValue("W", "7");
     960           1 :     EXPECT_STREQ(oTestSort[0], "A=4");
     961           1 :     EXPECT_STREQ(oTestSort[1], "B=5");
     962           1 :     EXPECT_STREQ(oTestSort[2], "L=2");
     963           1 :     EXPECT_STREQ(oTestSort[3], "T=3");
     964           1 :     EXPECT_STREQ(oTestSort[4], "W=7");
     965           1 :     EXPECT_STREQ(oTestSort[5], "Z=6");
     966           1 :     ASSERT_EQ(oTestSort[6], (const char *)nullptr);
     967             : }
     968             : 
     969           4 : TEST_F(test_cpl, CPLStringList_Sort)
     970             : {
     971             :     // Test some name=value handling stuff *with* sorting active.
     972           1 :     CPLStringList oNVL;
     973             : 
     974           1 :     oNVL.Sort();
     975             : 
     976           1 :     oNVL.AddNameValue("KEY1", "VALUE1");
     977           1 :     oNVL.AddNameValue("2KEY", "VALUE2");
     978           1 :     ASSERT_EQ(oNVL.Count(), 2);
     979           1 :     EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
     980           1 :     EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE2");
     981           1 :     ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
     982             : 
     983           1 :     oNVL.AddNameValue("KEY1", "VALUE3");
     984           1 :     ASSERT_EQ(oNVL.Count(), 3);
     985           1 :     EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
     986           1 :     EXPECT_STREQ(oNVL.FetchNameValueDef("MISSING", "X"), "X");
     987             : 
     988           1 :     oNVL.SetNameValue("2KEY", "VALUE4");
     989           1 :     EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE4");
     990           1 :     ASSERT_EQ(oNVL.Count(), 3);
     991             : 
     992             :     // make sure deletion works.
     993           1 :     oNVL.SetNameValue("2KEY", nullptr);
     994           1 :     ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
     995           1 :     ASSERT_EQ(oNVL.Count(), 2);
     996             : 
     997             :     // Test insertion logic pretty carefully.
     998           1 :     oNVL.Clear();
     999           1 :     ASSERT_TRUE(oNVL.IsSorted() == TRUE);
    1000             : 
    1001           1 :     oNVL.SetNameValue("B", "BB");
    1002           1 :     oNVL.SetNameValue("A", "AA");
    1003           1 :     oNVL.SetNameValue("D", "DD");
    1004           1 :     oNVL.SetNameValue("C", "CC");
    1005             : 
    1006             :     // items should be in sorted order.
    1007           1 :     EXPECT_STREQ(oNVL[0], "A=AA");
    1008           1 :     EXPECT_STREQ(oNVL[1], "B=BB");
    1009           1 :     EXPECT_STREQ(oNVL[2], "C=CC");
    1010           1 :     EXPECT_STREQ(oNVL[3], "D=DD");
    1011             : 
    1012           1 :     EXPECT_STREQ(oNVL.FetchNameValue("A"), "AA");
    1013           1 :     EXPECT_STREQ(oNVL.FetchNameValue("B"), "BB");
    1014           1 :     EXPECT_STREQ(oNVL.FetchNameValue("C"), "CC");
    1015           1 :     EXPECT_STREQ(oNVL.FetchNameValue("D"), "DD");
    1016             : }
    1017             : 
    1018           4 : TEST_F(test_cpl, URLEncode)
    1019             : {
    1020           2 :     EXPECT_STREQ(CPLString("AB").URLEncode(), "AB");
    1021           2 :     EXPECT_STREQ(CPLString("A/B").URLEncode(), "A/B");
    1022           2 :     EXPECT_STREQ(CPLString("A B").URLEncode(), "A%20B");
    1023             : 
    1024           1 :     const char *uriA =
    1025             :         "http://example.com/path with space%20/pipe|/query?param=1&val=A B";
    1026           1 :     const char *uriB = "http://example.com/path%20with%20space%20/pipe%7C/"
    1027             :                        "query?param=1&val=A%20B";
    1028           2 :     EXPECT_STREQ(CPLString(uriA).URLEncode(), uriB);
    1029           2 :     EXPECT_STREQ(CPLString(uriA).URLEncode(), CPLString(uriB).URLEncode());
    1030           2 :     EXPECT_STREQ(CPLString(uriA).URLEncode().URLEncode(), uriB);
    1031           1 : }
    1032             : 
    1033           4 : TEST_F(test_cpl, CPL_HMAC_SHA256)
    1034             : {
    1035             :     GByte abyDigest[CPL_SHA256_HASH_SIZE];
    1036             :     char szDigest[2 * CPL_SHA256_HASH_SIZE + 1];
    1037             : 
    1038           1 :     CPL_HMAC_SHA256("key", 3, "The quick brown fox jumps over the lazy dog",
    1039             :                     strlen("The quick brown fox jumps over the lazy dog"),
    1040             :                     abyDigest);
    1041          33 :     for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
    1042          32 :         snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
    1043          32 :                  abyDigest[i]);
    1044             :     // fprintf(stderr, "%s\n", szDigest);
    1045           1 :     EXPECT_STREQ(
    1046             :         szDigest,
    1047             :         "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
    1048             : 
    1049           1 :     CPL_HMAC_SHA256(
    1050             :         "mysupersupersupersupersupersupersupersupersupersupersupersupersupersup"
    1051             :         "ersupersupersupersupersupersuperlongkey",
    1052             :         strlen("mysupersupersupersupersupersupersupersupersupersupersupersupers"
    1053             :                "upersupersupersupersupersupersupersuperlongkey"),
    1054             :         "msg", 3, abyDigest);
    1055          33 :     for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
    1056          32 :         snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
    1057          32 :                  abyDigest[i]);
    1058             :     // fprintf(stderr, "%s\n", szDigest);
    1059           1 :     EXPECT_STREQ(
    1060             :         szDigest,
    1061             :         "a3051520761ed3cb43876b35ce2dd93ac5b332dc3bad898bb32086f7ac71ffc1");
    1062           1 : }
    1063             : 
    1064           4 : TEST_F(test_cpl, VSIMalloc)
    1065             : {
    1066           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    1067             : 
    1068             :     // The following tests will fail because of overflows
    1069             : 
    1070             :     {
    1071           1 :         CPLErrorReset();
    1072           1 :         void *ptr = VSIMalloc2(~(size_t)0, ~(size_t)0);
    1073           1 :         EXPECT_EQ(ptr, nullptr);
    1074           1 :         VSIFree(ptr);
    1075           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1076             :     }
    1077             : 
    1078             :     {
    1079           1 :         CPLErrorReset();
    1080           1 :         void *ptr = VSIMalloc3(1, ~(size_t)0, ~(size_t)0);
    1081           1 :         EXPECT_EQ(ptr, nullptr);
    1082           1 :         VSIFree(ptr);
    1083           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1084             :     }
    1085             : 
    1086             :     {
    1087           1 :         CPLErrorReset();
    1088           1 :         void *ptr = VSIMalloc3(~(size_t)0, 1, ~(size_t)0);
    1089           1 :         EXPECT_EQ(ptr, nullptr);
    1090           1 :         VSIFree(ptr);
    1091           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1092             :     }
    1093             : 
    1094             :     {
    1095           1 :         CPLErrorReset();
    1096           1 :         void *ptr = VSIMalloc3(~(size_t)0, ~(size_t)0, 1);
    1097           1 :         EXPECT_EQ(ptr, nullptr);
    1098           1 :         VSIFree(ptr);
    1099           1 :         EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1100             :     }
    1101             : 
    1102           1 :     if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
    1103             :     {
    1104             :         // The following tests will fail because such allocations cannot succeed
    1105             : #if SIZEOF_VOIDP == 8
    1106             :         {
    1107           1 :             CPLErrorReset();
    1108           1 :             void *ptr = VSIMalloc(~(size_t)0);
    1109           1 :             EXPECT_EQ(ptr, nullptr);
    1110           1 :             VSIFree(ptr);
    1111           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
    1112             :         }
    1113             : 
    1114             :         {
    1115           1 :             CPLErrorReset();
    1116           1 :             void *ptr = VSIMalloc2(~(size_t)0, 1);
    1117           1 :             EXPECT_EQ(ptr, nullptr);
    1118           1 :             VSIFree(ptr);
    1119           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1120             :         }
    1121             : 
    1122             :         {
    1123           1 :             CPLErrorReset();
    1124           1 :             void *ptr = VSIMalloc3(~(size_t)0, 1, 1);
    1125           1 :             EXPECT_EQ(ptr, nullptr);
    1126           1 :             VSIFree(ptr);
    1127           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1128             :         }
    1129             : 
    1130             :         {
    1131           1 :             CPLErrorReset();
    1132           1 :             void *ptr = VSICalloc(~(size_t)0, 1);
    1133           1 :             EXPECT_EQ(ptr, nullptr);
    1134           1 :             VSIFree(ptr);
    1135           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
    1136             :         }
    1137             : 
    1138             :         {
    1139           1 :             CPLErrorReset();
    1140           1 :             void *ptr = VSIRealloc(nullptr, ~(size_t)0);
    1141           1 :             EXPECT_EQ(ptr, nullptr);
    1142           1 :             VSIFree(ptr);
    1143           1 :             EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
    1144             :         }
    1145             : 
    1146             :         {
    1147           1 :             CPLErrorReset();
    1148           1 :             void *ptr = VSI_MALLOC_VERBOSE(~(size_t)0);
    1149           1 :             EXPECT_EQ(ptr, nullptr);
    1150           1 :             VSIFree(ptr);
    1151           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1152             :         }
    1153             : 
    1154             :         {
    1155           1 :             CPLErrorReset();
    1156           1 :             void *ptr = VSI_MALLOC2_VERBOSE(~(size_t)0, 1);
    1157           1 :             EXPECT_EQ(ptr, nullptr);
    1158           1 :             VSIFree(ptr);
    1159           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1160             :         }
    1161             : 
    1162             :         {
    1163           1 :             CPLErrorReset();
    1164           1 :             void *ptr = VSI_MALLOC3_VERBOSE(~(size_t)0, 1, 1);
    1165           1 :             EXPECT_EQ(ptr, nullptr);
    1166           1 :             VSIFree(ptr);
    1167           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1168             :         }
    1169             : 
    1170             :         {
    1171           1 :             CPLErrorReset();
    1172           1 :             void *ptr = VSI_CALLOC_VERBOSE(~(size_t)0, 1);
    1173           1 :             EXPECT_EQ(ptr, nullptr);
    1174           1 :             VSIFree(ptr);
    1175           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1176             :         }
    1177             : 
    1178             :         {
    1179           1 :             CPLErrorReset();
    1180           1 :             void *ptr = VSI_REALLOC_VERBOSE(nullptr, ~(size_t)0);
    1181           1 :             EXPECT_EQ(ptr, nullptr);
    1182           1 :             VSIFree(ptr);
    1183           1 :             EXPECT_NE(CPLGetLastErrorType(), CE_None);
    1184             :         }
    1185             : #endif
    1186             :     }
    1187             : 
    1188           1 :     CPLPopErrorHandler();
    1189             : 
    1190             :     // The following allocs will return NULL because of 0 byte alloc
    1191             :     {
    1192           1 :         CPLErrorReset();
    1193           1 :         void *ptr = VSIMalloc2(0, 1);
    1194           1 :         EXPECT_EQ(ptr, nullptr);
    1195           1 :         VSIFree(ptr);
    1196           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    1197             :     }
    1198             : 
    1199             :     {
    1200           1 :         void *ptr = VSIMalloc2(1, 0);
    1201           1 :         EXPECT_EQ(ptr, nullptr);
    1202           1 :         VSIFree(ptr);
    1203             :     }
    1204             : 
    1205             :     {
    1206           1 :         CPLErrorReset();
    1207           1 :         void *ptr = VSIMalloc3(0, 1, 1);
    1208           1 :         EXPECT_EQ(ptr, nullptr);
    1209           1 :         VSIFree(ptr);
    1210           1 :         EXPECT_EQ(CPLGetLastErrorType(), CE_None);
    1211             :     }
    1212             : 
    1213             :     {
    1214           1 :         void *ptr = VSIMalloc3(1, 0, 1);
    1215           1 :         EXPECT_EQ(ptr, nullptr);
    1216           1 :         VSIFree(ptr);
    1217             :     }
    1218             : 
    1219             :     {
    1220           1 :         void *ptr = VSIMalloc3(1, 1, 0);
    1221           1 :         EXPECT_EQ(ptr, nullptr);
    1222           1 :         VSIFree(ptr);
    1223             :     }
    1224           1 : }
    1225             : 
    1226           4 : TEST_F(test_cpl, CPLFormFilename)
    1227             : {
    1228           1 :     EXPECT_TRUE(strcmp(CPLFormFilename("a", "b", nullptr), "a/b") == 0 ||
    1229             :                 strcmp(CPLFormFilename("a", "b", nullptr), "a\\b") == 0);
    1230           1 :     EXPECT_TRUE(strcmp(CPLFormFilename("a/", "b", nullptr), "a/b") == 0 ||
    1231             :                 strcmp(CPLFormFilename("a/", "b", nullptr), "a\\b") == 0);
    1232           1 :     EXPECT_TRUE(strcmp(CPLFormFilename("a\\", "b", nullptr), "a/b") == 0 ||
    1233             :                 strcmp(CPLFormFilename("a\\", "b", nullptr), "a\\b") == 0);
    1234           1 :     EXPECT_STREQ(CPLFormFilename(nullptr, "a", "b"), "a.b");
    1235           1 :     EXPECT_STREQ(CPLFormFilename(nullptr, "a", ".b"), "a.b");
    1236           1 :     EXPECT_STREQ(CPLFormFilename("/a", "..", nullptr), "/");
    1237           1 :     EXPECT_STREQ(CPLFormFilename("/a/", "..", nullptr), "/");
    1238           1 :     EXPECT_STREQ(CPLFormFilename("/a/b", "..", nullptr), "/a");
    1239           1 :     EXPECT_STREQ(CPLFormFilename("/a/b/", "..", nullptr), "/a");
    1240           1 :     EXPECT_TRUE(EQUAL(CPLFormFilename("c:", "..", nullptr), "c:/..") ||
    1241             :                 EQUAL(CPLFormFilename("c:", "..", nullptr), "c:\\.."));
    1242           1 :     EXPECT_TRUE(EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:/..") ||
    1243             :                 EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:\\.."));
    1244           1 :     EXPECT_STREQ(CPLFormFilename("c:\\a", "..", nullptr), "c:");
    1245           1 :     EXPECT_STREQ(CPLFormFilename("c:\\a\\", "..", nullptr), "c:");
    1246           1 :     EXPECT_STREQ(CPLFormFilename("c:\\a\\b", "..", nullptr), "c:\\a");
    1247           1 :     EXPECT_STREQ(CPLFormFilename("\\\\$\\c:\\a", "..", nullptr), "\\\\$\\c:");
    1248           1 :     EXPECT_TRUE(
    1249             :         EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:/..") ||
    1250             :         EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:\\.."));
    1251           1 :     EXPECT_STREQ(CPLFormFilename("/a", "../", nullptr), "/");
    1252           1 :     EXPECT_STREQ(CPLFormFilename("/a/", "../", nullptr), "/");
    1253           1 :     EXPECT_STREQ(CPLFormFilename("/a", "../b", nullptr), "/b");
    1254           1 :     EXPECT_STREQ(CPLFormFilename("/a/", "../b", nullptr), "/b");
    1255           1 :     EXPECT_STREQ(CPLFormFilename("/a", "../b/c", nullptr), "/b/c");
    1256           1 :     EXPECT_STREQ(CPLFormFilename("/a/", "../b/c/d", nullptr), "/b/c/d");
    1257           1 :     EXPECT_STREQ(CPLFormFilename("/a/b", "../../c", nullptr), "/c");
    1258           1 :     EXPECT_STREQ(CPLFormFilename("/a/b/", "../../c/d", nullptr), "/c/d");
    1259           1 :     EXPECT_STREQ(CPLFormFilename("/a/b", "../..", nullptr), "/");
    1260           1 :     EXPECT_STREQ(CPLFormFilename("/a/b", "../../", nullptr), "/");
    1261           1 :     EXPECT_STREQ(CPLFormFilename("/a/b/c", "../../d", nullptr), "/a/d");
    1262           1 :     EXPECT_STREQ(CPLFormFilename("/a/b/c/", "../../d", nullptr), "/a/d");
    1263             :     // we could also just error out, but at least this preserves the original
    1264             :     // semantics
    1265           1 :     EXPECT_STREQ(CPLFormFilename("/a", "../../b", nullptr), "/a/../../b");
    1266           1 :     EXPECT_STREQ(
    1267             :         CPLFormFilename("/vsicurl/http://example.com?foo", "bar", nullptr),
    1268             :         "/vsicurl/http://example.com/bar?foo");
    1269           1 : }
    1270             : 
    1271           4 : TEST_F(test_cpl, CPLGetPath)
    1272             : {
    1273           1 :     EXPECT_STREQ(CPLGetPath("/foo/bar/"), "/foo/bar");
    1274           1 :     EXPECT_STREQ(CPLGetPath("/foo/bar"), "/foo");
    1275           1 :     EXPECT_STREQ(CPLGetPath("/vsicurl/http://example.com/foo/bar?suffix"),
    1276             :                  "/vsicurl/http://example.com/foo?suffix");
    1277           1 :     EXPECT_STREQ(
    1278             :         CPLGetPath(
    1279             :             "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
    1280             :             "2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
    1281             :         "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
    1282             :         "2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
    1283           1 : }
    1284             : 
    1285           4 : TEST_F(test_cpl, CPLGetDirname)
    1286             : {
    1287           1 :     EXPECT_STREQ(CPLGetDirname("/foo/bar/"), "/foo/bar");
    1288           1 :     EXPECT_STREQ(CPLGetDirname("/foo/bar"), "/foo");
    1289           1 :     EXPECT_STREQ(CPLGetDirname("/vsicurl/http://example.com/foo/bar?suffix"),
    1290             :                  "/vsicurl/http://example.com/foo?suffix");
    1291           1 :     EXPECT_STREQ(
    1292             :         CPLGetDirname(
    1293             :             "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
    1294             :             "2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
    1295             :         "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
    1296             :         "2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
    1297           1 : }
    1298             : 
    1299           4 : TEST_F(test_cpl, CPLLexicallyNormalize)
    1300             : {
    1301           2 :     EXPECT_STREQ(CPLLexicallyNormalize("", '/').c_str(), "");
    1302           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x", '/').c_str(), "x");
    1303           2 :     EXPECT_STREQ(CPLLexicallyNormalize("xy", '/').c_str(), "xy");
    1304           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/", '/').c_str(), "x/");
    1305           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/.", '/').c_str(), "x/");
    1306           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/./", '/').c_str(), "x/");
    1307           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/y", '/').c_str(), "x/y");
    1308           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/yz", '/').c_str(), "x/yz");
    1309           2 :     EXPECT_STREQ(CPLLexicallyNormalize("xy/z", '/').c_str(), "xy/z");
    1310           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x//y", '/').c_str(), "x/y");
    1311           2 :     EXPECT_STREQ(CPLLexicallyNormalize("/", '/').c_str(), "/");
    1312           2 :     EXPECT_STREQ(CPLLexicallyNormalize("/x", '/').c_str(), "/x");
    1313           2 :     EXPECT_STREQ(CPLLexicallyNormalize("../x", '/').c_str(), "../x");
    1314           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/../y", '/').c_str(), "y");
    1315           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/..", '/').c_str(), "");
    1316           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/../", '/').c_str(), "");
    1317           2 :     EXPECT_STREQ(CPLLexicallyNormalize("xy/../z", '/').c_str(), "z");
    1318           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/../yz", '/').c_str(), "yz");
    1319           2 :     EXPECT_STREQ(CPLLexicallyNormalize("x/../../yz", '/').c_str(), "../yz");
    1320           2 :     EXPECT_STREQ(CPLLexicallyNormalize("a/x/y/../../t", '/').c_str(), "a/t");
    1321           2 :     EXPECT_STREQ(CPLLexicallyNormalize("a\\x\\y\\..\\..\\t", '/', '\\').c_str(),
    1322             :                  "a\\t");
    1323           1 : }
    1324             : 
    1325           4 : TEST_F(test_cpl, CPLsscanf)
    1326             : {
    1327             :     double a, b, c;
    1328             : 
    1329           1 :     a = b = 0;
    1330           1 :     ASSERT_EQ(CPLsscanf("1 2", "%lf %lf", &a, &b), 2);
    1331           1 :     ASSERT_EQ(a, 1.0);
    1332           1 :     ASSERT_EQ(b, 2.0);
    1333             : 
    1334           1 :     a = b = 0;
    1335           1 :     ASSERT_EQ(CPLsscanf("1\t2", "%lf %lf", &a, &b), 2);
    1336           1 :     ASSERT_EQ(a, 1.0);
    1337           1 :     ASSERT_EQ(b, 2.0);
    1338             : 
    1339           1 :     a = b = 0;
    1340           1 :     ASSERT_EQ(CPLsscanf("1 2", "%lf\t%lf", &a, &b), 2);
    1341           1 :     ASSERT_EQ(a, 1.0);
    1342           1 :     ASSERT_EQ(b, 2.0);
    1343             : 
    1344           1 :     a = b = 0;
    1345           1 :     ASSERT_EQ(CPLsscanf("1  2", "%lf %lf", &a, &b), 2);
    1346           1 :     ASSERT_EQ(a, 1.0);
    1347           1 :     ASSERT_EQ(b, 2.0);
    1348             : 
    1349           1 :     a = b = 0;
    1350           1 :     ASSERT_EQ(CPLsscanf("1 2", "%lf  %lf", &a, &b), 2);
    1351           1 :     ASSERT_EQ(a, 1.0);
    1352           1 :     ASSERT_EQ(b, 2.0);
    1353             : 
    1354           1 :     a = b = c = 0;
    1355           1 :     ASSERT_EQ(CPLsscanf("1 2", "%lf %lf %lf", &a, &b, &c), 2);
    1356           1 :     ASSERT_EQ(a, 1.0);
    1357           1 :     ASSERT_EQ(b, 2.0);
    1358             : }
    1359             : 
    1360           4 : TEST_F(test_cpl, CPLsnprintf)
    1361             : {
    1362             :     {
    1363             :         char buf[32];
    1364           1 :         EXPECT_EQ(CPLsnprintf(buf, sizeof(buf), "a%.*fb", 1, 2.12), 5);
    1365           1 :         EXPECT_STREQ(buf, "a2.1b");
    1366             :     }
    1367           1 : }
    1368             : 
    1369           4 : TEST_F(test_cpl, CPLSetErrorHandler)
    1370             : {
    1371           1 :     CPLString oldVal = CPLGetConfigOption("CPL_DEBUG", "");
    1372           1 :     CPLSetConfigOption("CPL_DEBUG", "TEST");
    1373             : 
    1374           1 :     CPLErrorHandler oldHandler = CPLSetErrorHandler(myErrorHandler);
    1375           1 :     gbGotError = false;
    1376           1 :     CPLDebug("TEST", "Test");
    1377           1 :     ASSERT_EQ(gbGotError, true);
    1378           1 :     gbGotError = false;
    1379           1 :     CPLSetErrorHandler(oldHandler);
    1380             : 
    1381           1 :     CPLPushErrorHandler(myErrorHandler);
    1382           1 :     gbGotError = false;
    1383           1 :     CPLDebug("TEST", "Test");
    1384           1 :     ASSERT_EQ(gbGotError, true);
    1385           1 :     gbGotError = false;
    1386           1 :     CPLPopErrorHandler();
    1387             : 
    1388           1 :     oldHandler = CPLSetErrorHandler(myErrorHandler);
    1389           1 :     CPLSetCurrentErrorHandlerCatchDebug(FALSE);
    1390           1 :     gbGotError = false;
    1391           1 :     CPLDebug("TEST", "Test");
    1392           1 :     ASSERT_EQ(gbGotError, false);
    1393           1 :     gbGotError = false;
    1394           1 :     CPLSetErrorHandler(oldHandler);
    1395           1 :     CPLSetCurrentErrorHandlerCatchDebug(true);
    1396             : 
    1397           1 :     CPLPushErrorHandler(myErrorHandler);
    1398           1 :     CPLSetCurrentErrorHandlerCatchDebug(FALSE);
    1399           1 :     gbGotError = false;
    1400           1 :     CPLDebug("TEST", "Test");
    1401           1 :     ASSERT_EQ(gbGotError, false);
    1402           1 :     gbGotError = false;
    1403           1 :     CPLPopErrorHandler();
    1404             : 
    1405           1 :     CPLSetConfigOption("CPL_DEBUG", oldVal.size() ? oldVal.c_str() : nullptr);
    1406             : 
    1407           1 :     oldHandler = CPLSetErrorHandler(nullptr);
    1408           1 :     CPLDebug("TEST", "Test");
    1409           1 :     CPLError(CE_Failure, CPLE_AppDefined, "test");
    1410           1 :     CPLErrorHandler newOldHandler = CPLSetErrorHandler(nullptr);
    1411           1 :     ASSERT_EQ(newOldHandler, static_cast<CPLErrorHandler>(nullptr));
    1412           1 :     CPLDebug("TEST", "Test");
    1413           1 :     CPLError(CE_Failure, CPLE_AppDefined, "test");
    1414           1 :     CPLSetErrorHandler(oldHandler);
    1415             : }
    1416             : 
    1417           4 : TEST_F(test_cpl, global_error_handler_and_CPLSetCurrentErrorHandlerCatchDebug)
    1418             : {
    1419             :     static bool gbGotDebugMessage = false;
    1420             :     static bool gbGotExpectedUserData = false;
    1421             : 
    1422             :     struct MyStruct
    1423             :     {
    1424           1 :         static void CPL_STDCALL myErrorHandler(CPLErr eErr, CPLErrorNum,
    1425             :                                                const char *msg)
    1426             :         {
    1427           1 :             if (CPLGetErrorHandlerUserData() == &gbGotExpectedUserData)
    1428             :             {
    1429           1 :                 gbGotExpectedUserData = true;
    1430             :             }
    1431           1 :             if (eErr == CE_Debug && strcmp(msg, "TEST: my debug message") == 0)
    1432             :             {
    1433           1 :                 gbGotDebugMessage = true;
    1434             :             }
    1435           1 :         }
    1436             :     };
    1437             : 
    1438             :     CPLErrorHandler oldHandler =
    1439           1 :         CPLSetErrorHandlerEx(MyStruct::myErrorHandler, &gbGotExpectedUserData);
    1440             : 
    1441           2 :     CPLErrorAccumulator oAccumulator;
    1442             :     {
    1443           2 :         auto scopedAccumulator = oAccumulator.InstallForCurrentScope();
    1444           1 :         CPL_IGNORE_RET_VAL(scopedAccumulator);
    1445             : 
    1446           2 :         CPLConfigOptionSetter oSetter("CPL_DEBUG", "ON", false);
    1447           1 :         CPLDebug("TEST", "my debug message");
    1448             :     }
    1449             : 
    1450           1 :     EXPECT_TRUE(gbGotExpectedUserData);
    1451           1 :     EXPECT_TRUE(gbGotDebugMessage);
    1452             : 
    1453           1 :     CPLSetErrorHandler(oldHandler);
    1454           1 : }
    1455             : 
    1456             : /************************************************************************/
    1457             : /*                       CPLString::replaceAll()                        */
    1458             : /************************************************************************/
    1459             : 
    1460           4 : TEST_F(test_cpl, CPLString_replaceAll)
    1461             : {
    1462           1 :     CPLString osTest;
    1463           1 :     osTest = "foobarbarfoo";
    1464           1 :     osTest.replaceAll("bar", "was_bar");
    1465           1 :     ASSERT_EQ(osTest, "foowas_barwas_barfoo");
    1466             : 
    1467           1 :     osTest = "foobarbarfoo";
    1468           1 :     osTest.replaceAll("X", "was_bar");
    1469           1 :     ASSERT_EQ(osTest, "foobarbarfoo");
    1470             : 
    1471           1 :     osTest = "foobarbarfoo";
    1472           1 :     osTest.replaceAll("", "was_bar");
    1473           1 :     ASSERT_EQ(osTest, "foobarbarfoo");
    1474             : 
    1475           1 :     osTest = "foobarbarfoo";
    1476           1 :     osTest.replaceAll("bar", "");
    1477           1 :     ASSERT_EQ(osTest, "foofoo");
    1478             : 
    1479           1 :     osTest = "foobarbarfoo";
    1480           1 :     osTest.replaceAll('b', 'B');
    1481           1 :     ASSERT_EQ(osTest, "fooBarBarfoo");
    1482             : 
    1483           1 :     osTest = "foobarbarfoo";
    1484           1 :     osTest.replaceAll('b', "B");
    1485           1 :     ASSERT_EQ(osTest, "fooBarBarfoo");
    1486             : 
    1487           1 :     osTest = "foobarbarfoo";
    1488           1 :     osTest.replaceAll("b", 'B');
    1489           1 :     ASSERT_EQ(osTest, "fooBarBarfoo");
    1490             : }
    1491             : 
    1492             : /************************************************************************/
    1493             : /*                          VSIMallocAligned()                          */
    1494             : /************************************************************************/
    1495           4 : TEST_F(test_cpl, VSIMallocAligned)
    1496             : {
    1497           1 :     GByte *ptr = static_cast<GByte *>(VSIMallocAligned(sizeof(void *), 1));
    1498           1 :     ASSERT_TRUE(ptr != nullptr);
    1499           1 :     ASSERT_TRUE(((size_t)ptr % sizeof(void *)) == 0);
    1500           1 :     *ptr = 1;
    1501           1 :     VSIFreeAligned(ptr);
    1502             : 
    1503           1 :     ptr = static_cast<GByte *>(VSIMallocAligned(16, 1));
    1504           1 :     ASSERT_TRUE(ptr != nullptr);
    1505           1 :     ASSERT_TRUE(((size_t)ptr % 16) == 0);
    1506           1 :     *ptr = 1;
    1507           1 :     VSIFreeAligned(ptr);
    1508             : 
    1509           1 :     VSIFreeAligned(nullptr);
    1510             : 
    1511             : #ifndef _WIN32
    1512             :     // Illegal use of API. Returns non NULL on Windows
    1513           1 :     ptr = static_cast<GByte *>(VSIMallocAligned(2, 1));
    1514           1 :     EXPECT_TRUE(ptr == nullptr);
    1515           1 :     VSIFree(ptr);
    1516             : 
    1517             :     // Illegal use of API. Crashes on Windows
    1518           1 :     ptr = static_cast<GByte *>(VSIMallocAligned(5, 1));
    1519           1 :     EXPECT_TRUE(ptr == nullptr);
    1520           1 :     VSIFree(ptr);
    1521             : #endif
    1522             : 
    1523           1 :     if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
    1524             :     {
    1525             :         // The following tests will fail because such allocations cannot succeed
    1526             : #if SIZEOF_VOIDP == 8
    1527             :         ptr = static_cast<GByte *>(
    1528           1 :             VSIMallocAligned(sizeof(void *), ~((size_t)0)));
    1529           1 :         EXPECT_TRUE(ptr == nullptr);
    1530           1 :         VSIFree(ptr);
    1531             : 
    1532             :         ptr = static_cast<GByte *>(
    1533           1 :             VSIMallocAligned(sizeof(void *), (~((size_t)0)) - sizeof(void *)));
    1534           1 :         EXPECT_TRUE(ptr == nullptr);
    1535           1 :         VSIFree(ptr);
    1536             : #endif
    1537             :     }
    1538             : }
    1539             : 
    1540             : /************************************************************************/
    1541             : /*            CPLGetConfigOptions() / CPLSetConfigOptions()             */
    1542             : /************************************************************************/
    1543           4 : TEST_F(test_cpl, CPLGetConfigOptions)
    1544             : {
    1545           1 :     CPLSetConfigOption("FOOFOO", "BAR");
    1546           1 :     char **options = CPLGetConfigOptions();
    1547           1 :     EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
    1548           1 :     CPLSetConfigOptions(nullptr);
    1549           1 :     EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "i_dont_exist");
    1550           1 :     CPLSetConfigOptions(options);
    1551           1 :     EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "BAR");
    1552           1 :     CSLDestroy(options);
    1553           1 : }
    1554             : 
    1555             : /************************************************************************/
    1556             : /* CPLGetThreadLocalConfigOptions() / CPLSetThreadLocalConfigOptions()  */
    1557             : /************************************************************************/
    1558           4 : TEST_F(test_cpl, CPLGetThreadLocalConfigOptions)
    1559             : {
    1560           1 :     CPLSetThreadLocalConfigOption("FOOFOO", "BAR");
    1561           1 :     char **options = CPLGetThreadLocalConfigOptions();
    1562           1 :     EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
    1563           1 :     CPLSetThreadLocalConfigOptions(nullptr);
    1564           1 :     EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
    1565             :                  "i_dont_exist");
    1566           1 :     CPLSetThreadLocalConfigOptions(options);
    1567           1 :     EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
    1568             :                  "BAR");
    1569           1 :     CSLDestroy(options);
    1570           1 : }
    1571             : 
    1572           4 : TEST_F(test_cpl, CPLExpandTilde)
    1573             : {
    1574           1 :     EXPECT_STREQ(CPLExpandTilde("/foo/bar"), "/foo/bar");
    1575             : 
    1576           1 :     CPLSetConfigOption("HOME", "/foo");
    1577           1 :     ASSERT_TRUE(EQUAL(CPLExpandTilde("~/bar"), "/foo/bar") ||
    1578             :                 EQUAL(CPLExpandTilde("~/bar"), "/foo\\bar"));
    1579           1 :     CPLSetConfigOption("HOME", nullptr);
    1580             : }
    1581             : 
    1582           4 : TEST_F(test_cpl, CPLDeclareKnownConfigOption)
    1583             : {
    1584           2 :     CPLConfigOptionSetter oDebugSetter("CPL_DEBUG", "ON", false);
    1585             :     {
    1586           2 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1587           1 :         CPLErrorReset();
    1588             :         CPLConfigOptionSetter oDeclaredConfigOptionSetter("UNDECLARED_OPTION",
    1589           2 :                                                           "FOO", false);
    1590           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(),
    1591             :                      "Unknown configuration option 'UNDECLARED_OPTION'.");
    1592             :     }
    1593             :     {
    1594           1 :         CPLDeclareKnownConfigOption("DECLARED_OPTION", nullptr);
    1595             : 
    1596           2 :         const CPLStringList aosKnownConfigOptions(CPLGetKnownConfigOptions());
    1597           1 :         EXPECT_GE(aosKnownConfigOptions.FindString("CPL_DEBUG"), 0);
    1598           1 :         EXPECT_GE(aosKnownConfigOptions.FindString("DECLARED_OPTION"), 0);
    1599             : 
    1600           2 :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1601           1 :         CPLErrorReset();
    1602             :         CPLConfigOptionSetter oDeclaredConfigOptionSetter("DECLARED_OPTION",
    1603           2 :                                                           "FOO", false);
    1604           1 :         EXPECT_STREQ(CPLGetLastErrorMsg(), "");
    1605             :     }
    1606           1 : }
    1607             : 
    1608           4 : TEST_F(test_cpl, CPLErrorSetState)
    1609             : {
    1610             :     // NOTE: Assumes cpl_error.cpp defines DEFAULT_LAST_ERR_MSG_SIZE=500
    1611           1 :     char pszMsg[] = "0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1612             :                     "1abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1613             :                     "2abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1614             :                     "3abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1615             :                     "4abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1616             :                     "5abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1617             :                     "6abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1618             :                     "7abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1619             :                     "8abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
    1620             :                     "9abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"  // 500
    1621             :                     "0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"  // 550
    1622             :         ;
    1623             : 
    1624           1 :     CPLErrorReset();
    1625           1 :     CPLErrorSetState(CE_Warning, 1, pszMsg);
    1626           1 :     ASSERT_EQ(strlen(pszMsg) - 50 - 1,  // length - 50 - 1 (null-terminator)
    1627             :               strlen(CPLGetLastErrorMsg()));  // DEFAULT_LAST_ERR_MSG_SIZE - 1
    1628             : }
    1629             : 
    1630           4 : TEST_F(test_cpl, CPLUnescapeString)
    1631             : {
    1632           1 :     char *pszText = CPLUnescapeString(
    1633             :         "&lt;&gt;&amp;&apos;&quot;&#x3f;&#x3F;&#63;", nullptr, CPLES_XML);
    1634           1 :     EXPECT_STREQ(pszText, "<>&'\"???");
    1635           1 :     CPLFree(pszText);
    1636             : 
    1637             :     // Integer overflow
    1638           1 :     pszText = CPLUnescapeString("&10000000000000000;", nullptr, CPLES_XML);
    1639             :     // We do not really care about the return value
    1640           1 :     CPLFree(pszText);
    1641             : 
    1642             :     // Integer overflow
    1643           1 :     pszText = CPLUnescapeString("&#10000000000000000;", nullptr, CPLES_XML);
    1644             :     // We do not really care about the return value
    1645           1 :     CPLFree(pszText);
    1646             : 
    1647             :     // Error case
    1648           1 :     pszText = CPLUnescapeString("&foo", nullptr, CPLES_XML);
    1649           1 :     EXPECT_STREQ(pszText, "");
    1650           1 :     CPLFree(pszText);
    1651             : 
    1652             :     // Error case
    1653           1 :     pszText = CPLUnescapeString("&#x", nullptr, CPLES_XML);
    1654           1 :     EXPECT_STREQ(pszText, "");
    1655           1 :     CPLFree(pszText);
    1656             : 
    1657             :     // Error case
    1658           1 :     pszText = CPLUnescapeString("&#", nullptr, CPLES_XML);
    1659           1 :     EXPECT_STREQ(pszText, "");
    1660           1 :     CPLFree(pszText);
    1661           1 : }
    1662             : 
    1663             : // Test signed int safe maths
    1664           4 : TEST_F(test_cpl, CPLSM_signed)
    1665             : {
    1666           1 :     ASSERT_EQ((CPLSM(-2) + CPLSM(3)).v(), 1);
    1667           1 :     ASSERT_EQ((CPLSM(-2) + CPLSM(1)).v(), -1);
    1668           1 :     ASSERT_EQ((CPLSM(-2) + CPLSM(-1)).v(), -3);
    1669           1 :     ASSERT_EQ((CPLSM(2) + CPLSM(-3)).v(), -1);
    1670           1 :     ASSERT_EQ((CPLSM(2) + CPLSM(-1)).v(), 1);
    1671           1 :     ASSERT_EQ((CPLSM(2) + CPLSM(1)).v(), 3);
    1672           1 :     ASSERT_EQ((CPLSM(INT_MAX - 1) + CPLSM(1)).v(), INT_MAX);
    1673           1 :     ASSERT_EQ((CPLSM(1) + CPLSM(INT_MAX - 1)).v(), INT_MAX);
    1674           1 :     ASSERT_EQ((CPLSM(INT_MAX) + CPLSM(-1)).v(), INT_MAX - 1);
    1675           1 :     ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MAX)).v(), INT_MAX - 1);
    1676           1 :     ASSERT_EQ((CPLSM(INT_MIN + 1) + CPLSM(-1)).v(), INT_MIN);
    1677           1 :     ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MIN + 1)).v(), INT_MIN);
    1678             :     try
    1679             :     {
    1680           1 :         (CPLSM(INT_MAX) + CPLSM(1)).v();
    1681           0 :         ASSERT_TRUE(false);
    1682             :     }
    1683           1 :     catch (...)
    1684             :     {
    1685             :     }
    1686             :     try
    1687             :     {
    1688           1 :         (CPLSM(1) + CPLSM(INT_MAX)).v();
    1689           0 :         ASSERT_TRUE(false);
    1690             :     }
    1691           1 :     catch (...)
    1692             :     {
    1693             :     }
    1694             :     try
    1695             :     {
    1696           1 :         (CPLSM(INT_MIN) + CPLSM(-1)).v();
    1697           0 :         ASSERT_TRUE(false);
    1698             :     }
    1699           1 :     catch (...)
    1700             :     {
    1701             :     }
    1702             :     try
    1703             :     {
    1704           1 :         (CPLSM(-1) + CPLSM(INT_MIN)).v();
    1705           0 :         ASSERT_TRUE(false);
    1706             :     }
    1707           1 :     catch (...)
    1708             :     {
    1709             :     }
    1710             : 
    1711           1 :     ASSERT_EQ((CPLSM(-2) - CPLSM(1)).v(), -3);
    1712           1 :     ASSERT_EQ((CPLSM(-2) - CPLSM(-1)).v(), -1);
    1713           1 :     ASSERT_EQ((CPLSM(-2) - CPLSM(-3)).v(), 1);
    1714           1 :     ASSERT_EQ((CPLSM(2) - CPLSM(-1)).v(), 3);
    1715           1 :     ASSERT_EQ((CPLSM(2) - CPLSM(1)).v(), 1);
    1716           1 :     ASSERT_EQ((CPLSM(2) - CPLSM(3)).v(), -1);
    1717           1 :     ASSERT_EQ((CPLSM(INT_MAX) - CPLSM(1)).v(), INT_MAX - 1);
    1718           1 :     ASSERT_EQ((CPLSM(INT_MIN + 1) - CPLSM(1)).v(), INT_MIN);
    1719           1 :     ASSERT_EQ((CPLSM(0) - CPLSM(INT_MIN + 1)).v(), INT_MAX);
    1720           1 :     ASSERT_EQ((CPLSM(0) - CPLSM(INT_MAX)).v(), -INT_MAX);
    1721             :     try
    1722             :     {
    1723           1 :         (CPLSM(INT_MIN) - CPLSM(1)).v();
    1724           0 :         ASSERT_TRUE(false);
    1725             :     }
    1726           1 :     catch (...)
    1727             :     {
    1728             :     }
    1729             :     try
    1730             :     {
    1731           1 :         (CPLSM(0) - CPLSM(INT_MIN)).v();
    1732           0 :         ASSERT_TRUE(false);
    1733             :     }
    1734           1 :     catch (...)
    1735             :     {
    1736             :     }
    1737             :     try
    1738             :     {
    1739           1 :         (CPLSM(INT_MIN) - CPLSM(1)).v();
    1740           0 :         ASSERT_TRUE(false);
    1741             :     }
    1742           1 :     catch (...)
    1743             :     {
    1744             :     }
    1745             : 
    1746           1 :     ASSERT_EQ((CPLSM(INT_MIN + 1) * CPLSM(-1)).v(), INT_MAX);
    1747           1 :     ASSERT_EQ((CPLSM(-1) * CPLSM(INT_MIN + 1)).v(), INT_MAX);
    1748           1 :     ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(1)).v(), INT_MIN);
    1749           1 :     ASSERT_EQ((CPLSM(1) * CPLSM(INT_MIN)).v(), INT_MIN);
    1750           1 :     ASSERT_EQ((CPLSM(1) * CPLSM(INT_MAX)).v(), INT_MAX);
    1751           1 :     ASSERT_EQ((CPLSM(INT_MIN / 2) * CPLSM(2)).v(), INT_MIN);
    1752           1 :     ASSERT_EQ((CPLSM(INT_MAX / 2) * CPLSM(2)).v(), INT_MAX - 1);
    1753           1 :     ASSERT_EQ((CPLSM(INT_MAX / 2 + 1) * CPLSM(-2)).v(), INT_MIN);
    1754           1 :     ASSERT_EQ((CPLSM(0) * CPLSM(INT_MIN)).v(), 0);
    1755           1 :     ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(0)).v(), 0);
    1756           1 :     ASSERT_EQ((CPLSM(0) * CPLSM(INT_MAX)).v(), 0);
    1757           1 :     ASSERT_EQ((CPLSM(INT_MAX) * CPLSM(0)).v(), 0);
    1758             :     try
    1759             :     {
    1760           1 :         (CPLSM(INT_MAX / 2 + 1) * CPLSM(2)).v();
    1761           0 :         ASSERT_TRUE(false);
    1762             :     }
    1763           1 :     catch (...)
    1764             :     {
    1765             :     }
    1766             :     try
    1767             :     {
    1768           1 :         (CPLSM(2) * CPLSM(INT_MAX / 2 + 1)).v();
    1769           0 :         ASSERT_TRUE(false);
    1770             :     }
    1771           1 :     catch (...)
    1772             :     {
    1773             :     }
    1774             :     try
    1775             :     {
    1776           1 :         (CPLSM(INT_MIN) * CPLSM(-1)).v();
    1777           0 :         ASSERT_TRUE(false);
    1778             :     }
    1779           1 :     catch (...)
    1780             :     {
    1781             :     }
    1782             :     try
    1783             :     {
    1784           1 :         (CPLSM(INT_MIN) * CPLSM(2)).v();
    1785           0 :         ASSERT_TRUE(false);
    1786             :     }
    1787           1 :     catch (...)
    1788             :     {
    1789             :     }
    1790             :     try
    1791             :     {
    1792           1 :         (CPLSM(2) * CPLSM(INT_MIN)).v();
    1793           0 :         ASSERT_TRUE(false);
    1794             :     }
    1795           1 :     catch (...)
    1796             :     {
    1797             :     }
    1798             : 
    1799           1 :     ASSERT_EQ((CPLSM(4) / CPLSM(2)).v(), 2);
    1800           1 :     ASSERT_EQ((CPLSM(4) / CPLSM(-2)).v(), -2);
    1801           1 :     ASSERT_EQ((CPLSM(-4) / CPLSM(2)).v(), -2);
    1802           1 :     ASSERT_EQ((CPLSM(-4) / CPLSM(-2)).v(), 2);
    1803           1 :     ASSERT_EQ((CPLSM(0) / CPLSM(2)).v(), 0);
    1804           1 :     ASSERT_EQ((CPLSM(0) / CPLSM(-2)).v(), 0);
    1805           1 :     ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(1)).v(), INT_MAX);
    1806           1 :     ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(-1)).v(), -INT_MAX);
    1807           1 :     ASSERT_EQ((CPLSM(INT_MIN) / CPLSM(1)).v(), INT_MIN);
    1808             :     try
    1809             :     {
    1810           1 :         (CPLSM(-1) * CPLSM(INT_MIN)).v();
    1811           0 :         ASSERT_TRUE(false);
    1812             :     }
    1813           1 :     catch (...)
    1814             :     {
    1815             :     }
    1816             :     try
    1817             :     {
    1818           1 :         (CPLSM(INT_MIN) / CPLSM(-1)).v();
    1819           0 :         ASSERT_TRUE(false);
    1820             :     }
    1821           1 :     catch (...)
    1822             :     {
    1823             :     }
    1824             :     try
    1825             :     {
    1826           1 :         (CPLSM(1) / CPLSM(0)).v();
    1827           0 :         ASSERT_TRUE(false);
    1828             :     }
    1829           1 :     catch (...)
    1830             :     {
    1831             :     }
    1832             : 
    1833           1 :     ASSERT_EQ(CPLSM_TO_UNSIGNED(1).v(), 1U);
    1834             :     try
    1835             :     {
    1836           1 :         CPLSM_TO_UNSIGNED(-1);
    1837           0 :         ASSERT_TRUE(false);
    1838             :     }
    1839           1 :     catch (...)
    1840             :     {
    1841             :     }
    1842             : }
    1843             : 
    1844             : // Test unsigned int safe maths
    1845           4 : TEST_F(test_cpl, CPLSM_unsigned)
    1846             : {
    1847           1 :     ASSERT_EQ((CPLSM(2U) + CPLSM(3U)).v(), 5U);
    1848           1 :     ASSERT_EQ((CPLSM(UINT_MAX - 1) + CPLSM(1U)).v(), UINT_MAX);
    1849             :     try
    1850             :     {
    1851           1 :         (CPLSM(UINT_MAX) + CPLSM(1U)).v();
    1852           0 :         ASSERT_TRUE(false);
    1853             :     }
    1854           1 :     catch (...)
    1855             :     {
    1856             :     }
    1857             : 
    1858           1 :     ASSERT_EQ((CPLSM(4U) - CPLSM(3U)).v(), 1U);
    1859           1 :     ASSERT_EQ((CPLSM(4U) - CPLSM(4U)).v(), 0U);
    1860           1 :     ASSERT_EQ((CPLSM(UINT_MAX) - CPLSM(1U)).v(), UINT_MAX - 1);
    1861             :     try
    1862             :     {
    1863           1 :         (CPLSM(4U) - CPLSM(5U)).v();
    1864           0 :         ASSERT_TRUE(false);
    1865             :     }
    1866           1 :     catch (...)
    1867             :     {
    1868             :     }
    1869             : 
    1870           1 :     ASSERT_EQ((CPLSM(0U) * CPLSM(UINT_MAX)).v(), 0U);
    1871           1 :     ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(0U)).v(), 0U);
    1872           1 :     ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(1U)).v(), UINT_MAX);
    1873           1 :     ASSERT_EQ((CPLSM(1U) * CPLSM(UINT_MAX)).v(), UINT_MAX);
    1874             :     try
    1875             :     {
    1876           1 :         (CPLSM(UINT_MAX) * CPLSM(2U)).v();
    1877           0 :         ASSERT_TRUE(false);
    1878             :     }
    1879           1 :     catch (...)
    1880             :     {
    1881             :     }
    1882             :     try
    1883             :     {
    1884           1 :         (CPLSM(2U) * CPLSM(UINT_MAX)).v();
    1885           0 :         ASSERT_TRUE(false);
    1886             :     }
    1887           1 :     catch (...)
    1888             :     {
    1889             :     }
    1890             : 
    1891           1 :     ASSERT_EQ((CPLSM(4U) / CPLSM(2U)).v(), 2U);
    1892           1 :     ASSERT_EQ((CPLSM(UINT_MAX) / CPLSM(1U)).v(), UINT_MAX);
    1893             :     try
    1894             :     {
    1895           1 :         (CPLSM(1U) / CPLSM(0U)).v();
    1896           0 :         ASSERT_TRUE(false);
    1897             :     }
    1898           1 :     catch (...)
    1899             :     {
    1900             :     }
    1901             : 
    1902           1 :     ASSERT_EQ((CPLSM(static_cast<uint64_t>(2) * 1000 * 1000 * 1000) +
    1903             :                CPLSM(static_cast<uint64_t>(3) * 1000 * 1000 * 1000))
    1904             :                   .v(),
    1905             :               static_cast<uint64_t>(5) * 1000 * 1000 * 1000);
    1906           1 :     ASSERT_EQ((CPLSM(std::numeric_limits<uint64_t>::max() - 1) +
    1907             :                CPLSM(static_cast<uint64_t>(1)))
    1908             :                   .v(),
    1909             :               std::numeric_limits<uint64_t>::max());
    1910             :     try
    1911             :     {
    1912           2 :         (CPLSM(std::numeric_limits<uint64_t>::max()) +
    1913           3 :          CPLSM(static_cast<uint64_t>(1)));
    1914             :     }
    1915           1 :     catch (...)
    1916             :     {
    1917             :     }
    1918             : 
    1919           1 :     ASSERT_EQ((CPLSM(static_cast<uint64_t>(2) * 1000 * 1000 * 1000) *
    1920             :                CPLSM(static_cast<uint64_t>(3) * 1000 * 1000 * 1000))
    1921             :                   .v(),
    1922             :               static_cast<uint64_t>(6) * 1000 * 1000 * 1000 * 1000 * 1000 *
    1923             :                   1000);
    1924           1 :     ASSERT_EQ((CPLSM(std::numeric_limits<uint64_t>::max()) *
    1925             :                CPLSM(static_cast<uint64_t>(1)))
    1926             :                   .v(),
    1927             :               std::numeric_limits<uint64_t>::max());
    1928             :     try
    1929             :     {
    1930           2 :         (CPLSM(std::numeric_limits<uint64_t>::max()) *
    1931           3 :          CPLSM(static_cast<uint64_t>(2)));
    1932             :     }
    1933           1 :     catch (...)
    1934             :     {
    1935             :     }
    1936             : }
    1937             : 
    1938             : // Test CPLParseRFC822DateTime()
    1939           4 : TEST_F(test_cpl, CPLParseRFC822DateTime)
    1940             : {
    1941             :     int year, month, day, hour, min, sec, tz, weekday;
    1942           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("", &year, &month, &day, &hour, &min,
    1943             :                                         &sec, &tz, &weekday));
    1944             : 
    1945           1 :     ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", nullptr,
    1946             :                                      nullptr, nullptr, nullptr, nullptr,
    1947             :                                      nullptr, nullptr, nullptr),
    1948             :               TRUE);
    1949             : 
    1950           1 :     ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", &year,
    1951             :                                      &month, &day, &hour, &min, &sec, &tz,
    1952             :                                      &weekday),
    1953             :               TRUE);
    1954           1 :     ASSERT_EQ(year, 2017);
    1955           1 :     ASSERT_EQ(month, 1);
    1956           1 :     ASSERT_EQ(day, 15);
    1957           1 :     ASSERT_EQ(hour, 12);
    1958           1 :     ASSERT_EQ(min, 34);
    1959           1 :     ASSERT_EQ(sec, 56);
    1960           1 :     ASSERT_EQ(tz, 101);
    1961           1 :     ASSERT_EQ(weekday, 4);
    1962             : 
    1963           1 :     ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 GMT", &year,
    1964             :                                      &month, &day, &hour, &min, &sec, &tz,
    1965             :                                      &weekday),
    1966             :               TRUE);
    1967           1 :     ASSERT_EQ(year, 2017);
    1968           1 :     ASSERT_EQ(month, 1);
    1969           1 :     ASSERT_EQ(day, 15);
    1970           1 :     ASSERT_EQ(hour, 12);
    1971           1 :     ASSERT_EQ(min, 34);
    1972           1 :     ASSERT_EQ(sec, 56);
    1973           1 :     ASSERT_EQ(tz, 100);
    1974           1 :     ASSERT_EQ(weekday, 4);
    1975             : 
    1976             :     // Without day of week, second and timezone
    1977           1 :     ASSERT_EQ(CPLParseRFC822DateTime("15 Jan 2017 12:34", &year, &month, &day,
    1978             :                                      &hour, &min, &sec, &tz, &weekday),
    1979             :               TRUE);
    1980           1 :     ASSERT_EQ(year, 2017);
    1981           1 :     ASSERT_EQ(month, 1);
    1982           1 :     ASSERT_EQ(day, 15);
    1983           1 :     ASSERT_EQ(hour, 12);
    1984           1 :     ASSERT_EQ(min, 34);
    1985           1 :     ASSERT_EQ(sec, -1);
    1986           1 :     ASSERT_EQ(tz, 0);
    1987           1 :     ASSERT_EQ(weekday, 0);
    1988             : 
    1989           1 :     ASSERT_EQ(CPLParseRFC822DateTime("XXX, 15 Jan 2017 12:34:56 GMT", &year,
    1990             :                                      &month, &day, &hour, &min, &sec, &tz,
    1991             :                                      &weekday),
    1992             :               TRUE);
    1993           1 :     ASSERT_EQ(weekday, 0);
    1994             : 
    1995           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("Sun, 01 Jan 2017 12", &year, &month,
    1996             :                                         &day, &hour, &min, &sec, &tz,
    1997             :                                         &weekday));
    1998             : 
    1999           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("00 Jan 2017 12:34:56 GMT", &year,
    2000             :                                         &month, &day, &hour, &min, &sec, &tz,
    2001             :                                         &weekday));
    2002             : 
    2003           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("32 Jan 2017 12:34:56 GMT", &year,
    2004             :                                         &month, &day, &hour, &min, &sec, &tz,
    2005             :                                         &weekday));
    2006             : 
    2007           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 XXX 2017 12:34:56 GMT", &year,
    2008             :                                         &month, &day, &hour, &min, &sec, &tz,
    2009             :                                         &weekday));
    2010             : 
    2011           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 -1:34:56 GMT", &year,
    2012             :                                         &month, &day, &hour, &min, &sec, &tz,
    2013             :                                         &weekday));
    2014             : 
    2015           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 24:34:56 GMT", &year,
    2016             :                                         &month, &day, &hour, &min, &sec, &tz,
    2017             :                                         &weekday));
    2018             : 
    2019           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:-1:56 GMT", &year,
    2020             :                                         &month, &day, &hour, &min, &sec, &tz,
    2021             :                                         &weekday));
    2022             : 
    2023           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:60:56 GMT", &year,
    2024             :                                         &month, &day, &hour, &min, &sec, &tz,
    2025             :                                         &weekday));
    2026             : 
    2027           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:-1 GMT", &year,
    2028             :                                         &month, &day, &hour, &min, &sec, &tz,
    2029             :                                         &weekday));
    2030             : 
    2031           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:61 GMT", &year,
    2032             :                                         &month, &day, &hour, &min, &sec, &tz,
    2033             :                                         &weekday));
    2034             : 
    2035           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 XXX", &year,
    2036             :                                         &month, &day, &hour, &min, &sec, &tz,
    2037             :                                         &weekday));
    2038             : 
    2039           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +-100", &year,
    2040             :                                         &month, &day, &hour, &min, &sec, &tz,
    2041             :                                         &weekday));
    2042             : 
    2043           1 :     ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +9900", &year,
    2044             :                                         &month, &day, &hour, &min, &sec, &tz,
    2045             :                                         &weekday));
    2046             : }
    2047             : 
    2048             : // Test CPLParseMemorySize()
    2049           4 : TEST_F(test_cpl, CPLParseMemorySize)
    2050             : {
    2051             :     GIntBig nValue;
    2052             :     bool bUnitSpecified;
    2053             :     CPLErr result;
    2054             : 
    2055           1 :     result = CPLParseMemorySize("327mb", &nValue, &bUnitSpecified);
    2056           1 :     EXPECT_EQ(result, CE_None);
    2057           1 :     EXPECT_EQ(nValue, 327 * 1024 * 1024);
    2058           1 :     EXPECT_TRUE(bUnitSpecified);
    2059             : 
    2060           1 :     result = CPLParseMemorySize("327MB", &nValue, &bUnitSpecified);
    2061           1 :     EXPECT_EQ(result, CE_None);
    2062           1 :     EXPECT_EQ(nValue, 327 * 1024 * 1024);
    2063           1 :     EXPECT_TRUE(bUnitSpecified);
    2064             : 
    2065           1 :     result = CPLParseMemorySize("102.9K", &nValue, &bUnitSpecified);
    2066           1 :     EXPECT_EQ(result, CE_None);
    2067           1 :     EXPECT_EQ(nValue, static_cast<GIntBig>(102.9 * 1024));
    2068           1 :     EXPECT_TRUE(bUnitSpecified);
    2069             : 
    2070           1 :     result = CPLParseMemorySize("102.9 kB", &nValue, &bUnitSpecified);
    2071           1 :     EXPECT_EQ(result, CE_None);
    2072           1 :     EXPECT_EQ(nValue, static_cast<GIntBig>(102.9 * 1024));
    2073           1 :     EXPECT_TRUE(bUnitSpecified);
    2074             : 
    2075           1 :     result = CPLParseMemorySize("100%", &nValue, &bUnitSpecified);
    2076           1 :     EXPECT_EQ(result, CE_None);
    2077           1 :     EXPECT_GT(nValue, 100 * 1024 * 1024);
    2078           1 :     EXPECT_TRUE(bUnitSpecified);
    2079             : 
    2080           1 :     result = CPLParseMemorySize("0", &nValue, &bUnitSpecified);
    2081           1 :     EXPECT_EQ(result, CE_None);
    2082           1 :     EXPECT_EQ(nValue, 0);
    2083           1 :     EXPECT_FALSE(bUnitSpecified);
    2084             : 
    2085           1 :     result = CPLParseMemorySize("0MB", &nValue, &bUnitSpecified);
    2086           1 :     EXPECT_EQ(result, CE_None);
    2087           1 :     EXPECT_EQ(nValue, 0);
    2088           1 :     EXPECT_TRUE(bUnitSpecified);
    2089             : 
    2090           1 :     result = CPLParseMemorySize("  802  ", &nValue, &bUnitSpecified);
    2091           1 :     EXPECT_EQ(result, CE_None);
    2092           1 :     EXPECT_EQ(nValue, 802);
    2093           1 :     EXPECT_FALSE(bUnitSpecified);
    2094             : 
    2095           1 :     result = CPLParseMemorySize("110%", &nValue, &bUnitSpecified);
    2096           1 :     EXPECT_EQ(result, CE_Failure);
    2097             : 
    2098           1 :     result = CPLParseMemorySize("8kbit", &nValue, &bUnitSpecified);
    2099           1 :     EXPECT_EQ(result, CE_Failure);
    2100             : 
    2101           1 :     result = CPLParseMemorySize("8ZB", &nValue, &bUnitSpecified);
    2102           1 :     EXPECT_EQ(result, CE_Failure);
    2103             : 
    2104           1 :     result = CPLParseMemorySize("8Z", &nValue, &bUnitSpecified);
    2105           1 :     EXPECT_EQ(result, CE_Failure);
    2106             : 
    2107           1 :     result = CPLParseMemorySize("", &nValue, &bUnitSpecified);
    2108           1 :     EXPECT_EQ(result, CE_Failure);
    2109             : 
    2110           1 :     result = CPLParseMemorySize("  ", &nValue, &bUnitSpecified);
    2111           1 :     EXPECT_EQ(result, CE_Failure);
    2112             : 
    2113           1 :     result = CPLParseMemorySize("-100MB", &nValue, &bUnitSpecified);
    2114           1 :     EXPECT_EQ(result, CE_Failure);
    2115             : 
    2116           1 :     result = CPLParseMemorySize("nan", &nValue, &bUnitSpecified);
    2117           1 :     EXPECT_EQ(result, CE_Failure);
    2118           1 : }
    2119             : 
    2120             : // Test CPLCopyTree()
    2121           4 : TEST_F(test_cpl, CPLCopyTree)
    2122             : {
    2123           1 :     CPLString osTmpPath(CPLGetDirname(CPLGenerateTempFilename(nullptr)));
    2124           1 :     CPLString osSrcDir(CPLFormFilename(osTmpPath, "src_dir", nullptr));
    2125           1 :     CPLString osNewDir(CPLFormFilename(osTmpPath, "new_dir", nullptr));
    2126           1 :     CPLString osSrcFile(CPLFormFilename(osSrcDir, "my.bin", nullptr));
    2127           1 :     CPLString osNewFile(CPLFormFilename(osNewDir, "my.bin", nullptr));
    2128             : 
    2129             :     // Cleanup if previous test failed
    2130           1 :     VSIUnlink(osNewFile);
    2131           1 :     VSIRmdir(osNewDir);
    2132           1 :     VSIUnlink(osSrcFile);
    2133           1 :     VSIRmdir(osSrcDir);
    2134             : 
    2135           1 :     ASSERT_TRUE(VSIMkdir(osSrcDir, 0755) == 0);
    2136           1 :     VSILFILE *fp = VSIFOpenL(osSrcFile, "wb");
    2137           1 :     ASSERT_TRUE(fp != nullptr);
    2138           1 :     VSIFCloseL(fp);
    2139             : 
    2140           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2141           1 :     ASSERT_TRUE(CPLCopyTree(osNewDir, "/i/do_not/exist") < 0);
    2142           1 :     CPLPopErrorHandler();
    2143             : 
    2144           1 :     ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) == 0);
    2145             :     VSIStatBufL sStat;
    2146           1 :     ASSERT_TRUE(VSIStatL(osNewFile, &sStat) == 0);
    2147             : 
    2148           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2149           1 :     ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) < 0);
    2150           1 :     CPLPopErrorHandler();
    2151             : 
    2152           1 :     VSIUnlink(osNewFile);
    2153           1 :     VSIRmdir(osNewDir);
    2154           1 :     VSIUnlink(osSrcFile);
    2155           1 :     VSIRmdir(osSrcDir);
    2156             : }
    2157             : 
    2158             : class CPLJSonStreamingParserDump : public CPLJSonStreamingParser
    2159             : {
    2160             :     std::vector<bool> m_abFirstMember;
    2161             :     CPLString m_osSerialized;
    2162             :     CPLString m_osException;
    2163             : 
    2164             :   public:
    2165          76 :     CPLJSonStreamingParserDump()
    2166          76 :     {
    2167          76 :     }
    2168             : 
    2169          23 :     virtual void Reset() override
    2170             :     {
    2171          23 :         m_osSerialized.clear();
    2172          23 :         m_osException.clear();
    2173          23 :         CPLJSonStreamingParser::Reset();
    2174          23 :     }
    2175             : 
    2176             :     virtual void String(std::string_view s) override;
    2177             :     virtual void Number(std::string_view s) override;
    2178             :     virtual void Boolean(bool bVal) override;
    2179             :     virtual void Null() override;
    2180             : 
    2181             :     virtual void StartObject() override;
    2182             :     virtual void EndObject() override;
    2183             :     virtual void StartObjectMember(std::string_view key) override;
    2184             : 
    2185             :     virtual void StartArray() override;
    2186             :     virtual void EndArray() override;
    2187             :     virtual void StartArrayMember() override;
    2188             : 
    2189             :     virtual void Exception(const char *pszMessage) override;
    2190             : 
    2191          46 :     const CPLString &GetSerialized() const
    2192             :     {
    2193          46 :         return m_osSerialized;
    2194             :     }
    2195             : 
    2196          53 :     const CPLString &GetException() const
    2197             :     {
    2198          53 :         return m_osException;
    2199             :     }
    2200             : };
    2201             : 
    2202          19 : void CPLJSonStreamingParserDump::StartObject()
    2203             : {
    2204          19 :     m_osSerialized += "{";
    2205          19 :     m_abFirstMember.push_back(true);
    2206          19 : }
    2207             : 
    2208           8 : void CPLJSonStreamingParserDump::EndObject()
    2209             : {
    2210           8 :     m_osSerialized += "}";
    2211           8 :     m_abFirstMember.pop_back();
    2212           8 : }
    2213             : 
    2214          16 : void CPLJSonStreamingParserDump::StartObjectMember(std::string_view key)
    2215             : {
    2216          16 :     if (!m_abFirstMember.back())
    2217           4 :         m_osSerialized += ", ";
    2218          16 :     m_osSerialized += '"';
    2219          16 :     m_osSerialized += key;
    2220          16 :     m_osSerialized += "\": ";
    2221          16 :     m_abFirstMember.back() = false;
    2222          16 : }
    2223             : 
    2224          14 : void CPLJSonStreamingParserDump::String(std::string_view v)
    2225             : {
    2226          14 :     m_osSerialized += GetSerializedString(v);
    2227          14 : }
    2228             : 
    2229          22 : void CPLJSonStreamingParserDump::Number(std::string_view v)
    2230             : {
    2231          22 :     m_osSerialized += v;
    2232          22 : }
    2233             : 
    2234           8 : void CPLJSonStreamingParserDump::Boolean(bool bVal)
    2235             : {
    2236           8 :     m_osSerialized += bVal ? "true" : "false";
    2237           8 : }
    2238             : 
    2239           6 : void CPLJSonStreamingParserDump::Null()
    2240             : {
    2241           6 :     m_osSerialized += "null";
    2242           6 : }
    2243             : 
    2244          20 : void CPLJSonStreamingParserDump::StartArray()
    2245             : {
    2246          20 :     m_osSerialized += "[";
    2247          20 :     m_abFirstMember.push_back(true);
    2248          20 : }
    2249             : 
    2250          12 : void CPLJSonStreamingParserDump::EndArray()
    2251             : {
    2252          12 :     m_osSerialized += "]";
    2253          12 :     m_abFirstMember.pop_back();
    2254          12 : }
    2255             : 
    2256          14 : void CPLJSonStreamingParserDump::StartArrayMember()
    2257             : {
    2258          14 :     if (!m_abFirstMember.back())
    2259           2 :         m_osSerialized += ", ";
    2260          14 :     m_abFirstMember.back() = false;
    2261          14 : }
    2262             : 
    2263          53 : void CPLJSonStreamingParserDump::Exception(const char *pszMessage)
    2264             : {
    2265          53 :     m_osException = pszMessage;
    2266          53 : }
    2267             : 
    2268             : // Test CPLJSonStreamingParser()
    2269           4 : TEST_F(test_cpl, CPLJSonStreamingParser)
    2270             : {
    2271             :     // nominal cases
    2272             : 
    2273             :     const auto NominalCase =
    2274          23 :         [](const std::string &s, const std::string &sExpected = std::string())
    2275             :     {
    2276          46 :         CPLJSonStreamingParserDump oParser;
    2277          23 :         EXPECT_TRUE(oParser.Parse(s, true));
    2278          23 :         if (!sExpected.empty())
    2279           8 :             EXPECT_STREQ(oParser.GetSerialized(), sExpected.c_str());
    2280             :         else
    2281          15 :             EXPECT_STREQ(oParser.GetSerialized(), s.c_str());
    2282             : 
    2283          23 :         oParser.Reset();
    2284         257 :         for (size_t i = 0; i < s.size(); i++)
    2285             :         {
    2286         234 :             EXPECT_TRUE(oParser.Parse(std::string_view(s.c_str() + i, 1),
    2287             :                                       i + 1 == s.size()));
    2288             :         }
    2289          23 :         if (!sExpected.empty())
    2290           8 :             EXPECT_STREQ(oParser.GetSerialized(), sExpected.c_str());
    2291             :         else
    2292          15 :             EXPECT_STREQ(oParser.GetSerialized(), s.c_str());
    2293          23 :     };
    2294             : 
    2295           1 :     NominalCase("false");
    2296           1 :     NominalCase("true");
    2297           1 :     NominalCase("null");
    2298           1 :     NominalCase("10");
    2299           1 :     NominalCase("123eE-34");
    2300           1 :     NominalCase("\"\"");
    2301           1 :     NominalCase("\"simple string\"");
    2302           1 :     NominalCase("");
    2303           1 :     NominalCase("\"\\\\a\\b\\f\\n\\r\\t\\u0020\\u0001\\\"\"",
    2304             :                 "\"\\\\a\\b\\f\\n\\r\\t \\u0001\\\"\"");
    2305           1 :     NominalCase(
    2306             :         "\"\\u0001\\u0020\\ud834\\uDD1E\\uDD1E\\uD834\\uD834\\uD834\"",
    2307             :         "\"\\u0001 \xf0\x9d\x84\x9e\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\"");
    2308           1 :     NominalCase("\"\\ud834\"", "\"\xef\xbf\xbd\"");
    2309           1 :     NominalCase("\"\\ud834\\t\"", "\"\xef\xbf\xbd\\t\"");
    2310           1 :     NominalCase("\"\\u00e9\"", "\"\xc3\xa9\"");
    2311           1 :     NominalCase("{}");
    2312           1 :     NominalCase("[]");
    2313           1 :     NominalCase("[[]]");
    2314           1 :     NominalCase("[1]");
    2315           1 :     NominalCase("[1,2]", "[1, 2]");
    2316           1 :     NominalCase("{\"a\":null}", "{\"a\": null}");
    2317           1 :     NominalCase(" { \"a\" : null ,\r\n\t\"b\": {\"c\": 1}, \"d\": [1] }",
    2318             :                 "{\"a\": null, \"b\": {\"c\": 1}, \"d\": [1]}");
    2319           1 :     NominalCase("infinity");
    2320           1 :     NominalCase("-infinity");
    2321           1 :     NominalCase("nan");
    2322             : 
    2323             :     // errors
    2324             : 
    2325          49 :     const auto ErrorCase = [](const std::string &s)
    2326             :     {
    2327          98 :         CPLJSonStreamingParserDump oParser;
    2328          49 :         EXPECT_FALSE(oParser.Parse(s, true));
    2329          49 :         EXPECT_FALSE(oParser.GetException().empty());
    2330          49 :     };
    2331             : 
    2332           1 :     ErrorCase("tru");
    2333           1 :     ErrorCase("tru1");
    2334           1 :     ErrorCase("truxe");
    2335           1 :     ErrorCase("truex");
    2336           1 :     ErrorCase("fals");
    2337           1 :     ErrorCase("falsxe");
    2338           1 :     ErrorCase("falsex");
    2339           1 :     ErrorCase("nul");
    2340           1 :     ErrorCase("nulxl");
    2341           1 :     ErrorCase("nullx");
    2342           1 :     ErrorCase("na");
    2343           1 :     ErrorCase("nanx");
    2344           1 :     ErrorCase("naxn");
    2345           1 :     ErrorCase("infinit");
    2346           1 :     ErrorCase("infinityx");
    2347           1 :     ErrorCase("-infinit");
    2348           1 :     ErrorCase("-infinityx");
    2349           1 :     ErrorCase("true false");
    2350           1 :     ErrorCase("x");
    2351           1 :     ErrorCase("{");
    2352           1 :     ErrorCase("}");
    2353           1 :     ErrorCase("[");
    2354           1 :     ErrorCase("[1");
    2355           1 :     ErrorCase("[,");
    2356           1 :     ErrorCase("[|");
    2357           1 :     ErrorCase("]");
    2358           1 :     ErrorCase("{ :");
    2359           1 :     ErrorCase("{ ,");
    2360           1 :     ErrorCase("{ |");
    2361           1 :     ErrorCase("{ 1");
    2362           1 :     ErrorCase("{ \"x\"");
    2363           1 :     ErrorCase("{ \"x\": ");
    2364           1 :     ErrorCase("{ \"x\": 1 2");
    2365           1 :     ErrorCase("{ \"x\" }");
    2366           1 :     ErrorCase("{ \"x\", ");
    2367           1 :     ErrorCase("{ \"a\" x}");
    2368           1 :     ErrorCase("1x");
    2369           1 :     ErrorCase("\"");
    2370           1 :     ErrorCase("\"\\");
    2371           1 :     ErrorCase("\"\\x\"");
    2372           1 :     ErrorCase("\"\\u");
    2373           1 :     ErrorCase("\"\\ux");
    2374           1 :     ErrorCase("\"\\u000");
    2375           1 :     ErrorCase("\"\\uD834\\ux\"");
    2376           1 :     ErrorCase("\"\\\"");
    2377           1 :     ErrorCase("[,]");
    2378           1 :     ErrorCase("[true,]");
    2379           1 :     ErrorCase("[true,,true]");
    2380           1 :     ErrorCase("[true true]");
    2381             : 
    2382             :     {
    2383           1 :         CPLJSonStreamingParserDump oParser;
    2384           1 :         oParser.SetMaxStringSize(2);
    2385           1 :         ASSERT_TRUE(!oParser.Parse("\"too long\"", true));
    2386           1 :         ASSERT_TRUE(!oParser.GetException().empty());
    2387             :     }
    2388             :     {
    2389           1 :         CPLJSonStreamingParserDump oParser;
    2390           1 :         oParser.SetMaxStringSize(2);
    2391           1 :         ASSERT_TRUE(oParser.Parse("\"", false));
    2392           1 :         ASSERT_TRUE(!oParser.Parse("too long\"", true));
    2393           1 :         ASSERT_TRUE(!oParser.GetException().empty());
    2394             :     }
    2395             :     {
    2396           1 :         CPLJSonStreamingParserDump oParser;
    2397           1 :         oParser.SetMaxDepth(1);
    2398           1 :         ASSERT_TRUE(!oParser.Parse("[[]]", true));
    2399           1 :         ASSERT_TRUE(!oParser.GetException().empty());
    2400             :     }
    2401             :     {
    2402           1 :         CPLJSonStreamingParserDump oParser;
    2403           1 :         oParser.SetMaxDepth(1);
    2404           1 :         ASSERT_TRUE(!oParser.Parse("{ \"x\": {} }", true));
    2405           1 :         ASSERT_TRUE(!oParser.GetException().empty());
    2406             :     }
    2407             : }
    2408             : 
    2409             : // Test cpl_mem_cache
    2410           4 : TEST_F(test_cpl, cpl_mem_cache)
    2411             : {
    2412           1 :     lru11::Cache<int, int> cache(2, 1);
    2413           1 :     ASSERT_EQ(cache.size(), 0U);
    2414           1 :     ASSERT_TRUE(cache.empty());
    2415           1 :     cache.clear();
    2416             :     int val;
    2417           1 :     ASSERT_TRUE(!cache.tryGet(0, val));
    2418             :     try
    2419             :     {
    2420           1 :         cache.get(0);
    2421           0 :         ASSERT_TRUE(false);
    2422             :     }
    2423           2 :     catch (const lru11::KeyNotFound &)
    2424             :     {
    2425           1 :         ASSERT_TRUE(true);
    2426             :     }
    2427           1 :     ASSERT_TRUE(!cache.remove(0));
    2428           1 :     ASSERT_TRUE(!cache.contains(0));
    2429           1 :     ASSERT_EQ(cache.getMaxSize(), 2U);
    2430           1 :     ASSERT_EQ(cache.getElasticity(), 1U);
    2431           1 :     ASSERT_EQ(cache.getMaxAllowedSize(), 3U);
    2432             :     int out;
    2433           1 :     ASSERT_TRUE(!cache.removeAndRecycleOldestEntry(out));
    2434             : 
    2435           1 :     cache.insert(0, 1);
    2436           1 :     val = 0;
    2437           1 :     ASSERT_TRUE(cache.tryGet(0, val));
    2438           1 :     int *ptr = cache.getPtr(0);
    2439           1 :     ASSERT_TRUE(ptr);
    2440           1 :     ASSERT_EQ(*ptr, 1);
    2441           1 :     ASSERT_TRUE(cache.getPtr(-1) == nullptr);
    2442           1 :     ASSERT_EQ(val, 1);
    2443           1 :     ASSERT_EQ(cache.get(0), 1);
    2444           1 :     ASSERT_EQ(cache.getCopy(0), 1);
    2445           1 :     ASSERT_EQ(cache.size(), 1U);
    2446           1 :     ASSERT_TRUE(!cache.empty());
    2447           1 :     ASSERT_TRUE(cache.contains(0));
    2448           1 :     bool visited = false;
    2449           2 :     auto lambda = [&visited](const lru11::KeyValuePair<int, int> &kv)
    2450             :     {
    2451           1 :         if (kv.key == 0 && kv.value == 1)
    2452           1 :             visited = true;
    2453           2 :     };
    2454           1 :     cache.cwalk(lambda);
    2455           1 :     ASSERT_TRUE(visited);
    2456             : 
    2457           1 :     out = -1;
    2458           1 :     ASSERT_TRUE(cache.removeAndRecycleOldestEntry(out));
    2459           1 :     ASSERT_EQ(out, 1);
    2460             : 
    2461           1 :     cache.insert(0, 1);
    2462           1 :     cache.insert(0, 2);
    2463           1 :     ASSERT_EQ(cache.get(0), 2);
    2464           1 :     ASSERT_EQ(cache.size(), 1U);
    2465           1 :     cache.insert(1, 3);
    2466           1 :     cache.insert(2, 4);
    2467           1 :     ASSERT_EQ(cache.size(), 3U);
    2468           1 :     cache.insert(3, 5);
    2469           1 :     ASSERT_EQ(cache.size(), 2U);
    2470           1 :     ASSERT_TRUE(cache.contains(2));
    2471           1 :     ASSERT_TRUE(cache.contains(3));
    2472           1 :     ASSERT_TRUE(!cache.contains(0));
    2473           1 :     ASSERT_TRUE(!cache.contains(1));
    2474           1 :     ASSERT_TRUE(cache.remove(2));
    2475           1 :     ASSERT_TRUE(!cache.contains(2));
    2476           1 :     ASSERT_EQ(cache.size(), 1U);
    2477             : 
    2478             :     {
    2479             :         // Check that MyObj copy constructor and copy-assignment operator
    2480             :         // are not needed
    2481             :         struct MyObj
    2482             :         {
    2483             :             int m_v;
    2484             : 
    2485           4 :             MyObj(int v) : m_v(v)
    2486             :             {
    2487           4 :             }
    2488             : 
    2489             :             MyObj(const MyObj &) = delete;
    2490             :             MyObj &operator=(const MyObj &) = delete;
    2491             :             MyObj(MyObj &&) = default;
    2492             :             MyObj &operator=(MyObj &&) = default;
    2493             :         };
    2494             : 
    2495           1 :         lru11::Cache<int, MyObj> cacheMyObj(2, 0);
    2496           1 :         ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
    2497           1 :         cacheMyObj.getPtr(0);
    2498           1 :         ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
    2499           1 :         ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
    2500           1 :         MyObj outObj(-1);
    2501           1 :         cacheMyObj.removeAndRecycleOldestEntry(outObj);
    2502             :     }
    2503             : 
    2504             :     {
    2505             :         // Check that MyObj copy constructor and copy-assignment operator
    2506             :         // are not triggered
    2507             :         struct MyObj
    2508             :         {
    2509             :             int m_v;
    2510             : 
    2511           4 :             MyObj(int v) : m_v(v)
    2512             :             {
    2513           4 :             }
    2514             : 
    2515             :             static void should_not_happen()
    2516             :             {
    2517             :                 ASSERT_TRUE(false);
    2518             :             }
    2519             : 
    2520             :             MyObj(const MyObj &) : m_v(-1)
    2521             :             {
    2522             :                 should_not_happen();
    2523             :             }
    2524             : 
    2525             :             MyObj &operator=(const MyObj &)
    2526             :             {
    2527             :                 should_not_happen();
    2528             :                 return *this;
    2529             :             }
    2530             : 
    2531             :             MyObj(MyObj &&) = default;
    2532             :             MyObj &operator=(MyObj &&) = default;
    2533             :         };
    2534             : 
    2535           1 :         lru11::Cache<int, MyObj> cacheMyObj(2, 0);
    2536           1 :         ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
    2537           1 :         cacheMyObj.getPtr(0);
    2538           1 :         ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
    2539           1 :         ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
    2540           1 :         MyObj outObj(-1);
    2541           1 :         cacheMyObj.removeAndRecycleOldestEntry(outObj);
    2542             :     }
    2543             : }
    2544             : 
    2545             : // Test CPLJSONDocument
    2546           4 : TEST_F(test_cpl, CPLJSONDocument)
    2547             : {
    2548             :     {
    2549             :         // Test Json document LoadUrl
    2550           1 :         CPLJSONDocument oDocument;
    2551           1 :         const char *options[5] = {"CONNECTTIMEOUT=15", "TIMEOUT=20",
    2552             :                                   "MAX_RETRY=5", "RETRY_DELAY=1", nullptr};
    2553             : 
    2554           1 :         oDocument.GetRoot().Add("foo", "bar");
    2555             : 
    2556           1 :         if (CPLHTTPEnabled())
    2557             :         {
    2558           1 :             CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", "YES");
    2559           1 :             VSILFILE *fpTmp = VSIFOpenL("/vsimem/test.json", "wb");
    2560           1 :             const char *pszContent = "{ \"foo\": \"bar\" }";
    2561           1 :             VSIFWriteL(pszContent, 1, strlen(pszContent), fpTmp);
    2562           1 :             VSIFCloseL(fpTmp);
    2563           1 :             ASSERT_TRUE(oDocument.LoadUrl("/vsimem/test.json",
    2564             :                                           const_cast<char **>(options)));
    2565           1 :             CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", nullptr);
    2566           1 :             VSIUnlink("/vsimem/test.json");
    2567             : 
    2568           1 :             CPLJSONObject oJsonRoot = oDocument.GetRoot();
    2569           1 :             ASSERT_TRUE(oJsonRoot.IsValid());
    2570             : 
    2571           2 :             CPLString value = oJsonRoot.GetString("foo", "");
    2572           1 :             ASSERT_STRNE(value, "bar");  // not equal
    2573             :         }
    2574             :     }
    2575             :     {
    2576             :         // Test Json document LoadChunks
    2577           1 :         CPLJSONDocument oDocument;
    2578             : 
    2579           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2580           1 :         ASSERT_TRUE(!oDocument.LoadChunks("/i_do/not/exist", 512));
    2581           1 :         CPLPopErrorHandler();
    2582             : 
    2583           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2584           1 :         ASSERT_TRUE(!oDocument.LoadChunks("test_cpl.cpp", 512));
    2585           1 :         CPLPopErrorHandler();
    2586             : 
    2587           1 :         oDocument.GetRoot().Add("foo", "bar");
    2588             : 
    2589           1 :         ASSERT_TRUE(
    2590             :             oDocument.LoadChunks((data_ + SEP + "test.json").c_str(), 512));
    2591             : 
    2592           1 :         CPLJSONObject oJsonRoot = oDocument.GetRoot();
    2593           1 :         ASSERT_TRUE(oJsonRoot.IsValid());
    2594           1 :         ASSERT_EQ(oJsonRoot.GetInteger("resource/id", 10), 0);
    2595             : 
    2596           2 :         CPLJSONObject oJsonResource = oJsonRoot.GetObj("resource");
    2597           1 :         ASSERT_TRUE(oJsonResource.IsValid());
    2598           1 :         std::vector<CPLJSONObject> children = oJsonResource.GetChildren();
    2599           1 :         ASSERT_TRUE(children.size() == 11);
    2600             : 
    2601           2 :         CPLJSONArray oaScopes = oJsonRoot.GetArray("resource/scopes");
    2602           1 :         ASSERT_TRUE(oaScopes.IsValid());
    2603           1 :         ASSERT_EQ(oaScopes.Size(), 2);
    2604             : 
    2605           2 :         CPLJSONObject oHasChildren = oJsonRoot.GetObj("resource/children");
    2606           1 :         ASSERT_TRUE(oHasChildren.IsValid());
    2607           1 :         ASSERT_EQ(oHasChildren.ToBool(), true);
    2608             : 
    2609           1 :         ASSERT_EQ(oJsonResource.GetBool("children", false), true);
    2610             : 
    2611           2 :         CPLJSONObject oJsonId = oJsonRoot["resource/owner_user/id"];
    2612           1 :         ASSERT_TRUE(oJsonId.IsValid());
    2613             :     }
    2614             :     {
    2615           1 :         CPLJSONDocument oDocument;
    2616           1 :         ASSERT_TRUE(!oDocument.LoadMemory(nullptr, 0));
    2617           1 :         ASSERT_TRUE(!oDocument.LoadMemory(CPLString()));
    2618           1 :         ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
    2619           1 :         ASSERT_TRUE(oDocument.GetRoot().GetType() ==
    2620             :                     CPLJSONObject::Type::Boolean);
    2621           1 :         ASSERT_TRUE(oDocument.GetRoot().ToBool());
    2622           1 :         ASSERT_TRUE(oDocument.LoadMemory(std::string("false")));
    2623           1 :         ASSERT_TRUE(oDocument.GetRoot().GetType() ==
    2624             :                     CPLJSONObject::Type::Boolean);
    2625           1 :         ASSERT_TRUE(!oDocument.GetRoot().ToBool());
    2626             :     }
    2627             :     {
    2628             :         // Copy constructor
    2629           1 :         CPLJSONDocument oDocument;
    2630           1 :         ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
    2631           1 :         oDocument.GetRoot();
    2632           1 :         CPLJSONDocument oDocument2(oDocument);
    2633           1 :         CPLJSONObject oObj(oDocument.GetRoot());
    2634           1 :         ASSERT_TRUE(oObj.ToBool());
    2635           1 :         CPLJSONObject oObj2(oObj);
    2636           1 :         ASSERT_TRUE(oObj2.ToBool());
    2637             :         // Assignment operator
    2638           1 :         oDocument2 = oDocument;
    2639           1 :         oDocument.GetRoot();  // avoid Coverity Scan warning
    2640           1 :         auto &oDocument2Ref(oDocument2);
    2641           1 :         oDocument2 = oDocument2Ref;
    2642           1 :         oObj2 = oObj;
    2643           1 :         oObj.GetType();  // avoid Coverity Scan warning
    2644           1 :         auto &oObj2Ref(oObj2);
    2645           1 :         oObj2 = oObj2Ref;
    2646           1 :         CPLJSONObject oObj3(std::move(oObj2));
    2647           1 :         ASSERT_TRUE(oObj3.ToBool());
    2648           1 :         CPLJSONObject oObj4;
    2649           1 :         oObj4 = std::move(oObj3);
    2650           1 :         ASSERT_TRUE(oObj4.ToBool());
    2651             :     }
    2652             :     {
    2653             :         // Move constructor
    2654           2 :         CPLJSONDocument oDocument;
    2655           1 :         oDocument.GetRoot();
    2656           1 :         CPLJSONDocument oDocument2(std::move(oDocument));
    2657             :     }
    2658             :     {
    2659             :         // Move assignment
    2660           2 :         CPLJSONDocument oDocument;
    2661           1 :         oDocument.GetRoot();
    2662           2 :         CPLJSONDocument oDocument2;
    2663           1 :         oDocument2 = std::move(oDocument);
    2664             :     }
    2665             :     {
    2666             :         // Save
    2667           1 :         CPLJSONDocument oDocument;
    2668           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    2669           1 :         ASSERT_TRUE(!oDocument.Save("/i_do/not/exist"));
    2670           1 :         CPLPopErrorHandler();
    2671             :     }
    2672             :     {
    2673           2 :         CPLJSONObject oObj(nullptr);
    2674           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Null);
    2675             :     }
    2676             :     {
    2677           2 :         CPLJSONObject oObj(true);
    2678           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Boolean);
    2679           1 :         EXPECT_EQ(oObj.ToBool(), true);
    2680             :     }
    2681             :     {
    2682           2 :         CPLJSONObject oObj(1);
    2683           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Integer);
    2684           1 :         EXPECT_EQ(oObj.ToInteger(), 1);
    2685             :     }
    2686             :     {
    2687           2 :         CPLJSONObject oObj(static_cast<int64_t>(123) * 1024 * 1024 * 1024);
    2688           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Long);
    2689           1 :         EXPECT_EQ(oObj.ToLong(),
    2690             :                   static_cast<int64_t>(123) * 1024 * 1024 * 1024);
    2691             :     }
    2692             :     {
    2693           2 :         CPLJSONObject oObj(static_cast<uint64_t>(123) * 1024 * 1024 * 1024);
    2694             :         // Might be a string with older libjson versions
    2695           1 :         if (oObj.GetType() == CPLJSONObject::Type::Long)
    2696             :         {
    2697           1 :             EXPECT_EQ(oObj.ToLong(),
    2698             :                       static_cast<int64_t>(123) * 1024 * 1024 * 1024);
    2699             :         }
    2700             :     }
    2701             :     {
    2702           2 :         CPLJSONObject oObj(1.5);
    2703           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Double);
    2704           1 :         EXPECT_EQ(oObj.ToDouble(), 1.5);
    2705             :     }
    2706             :     {
    2707           2 :         CPLJSONObject oObj("ab");
    2708           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::String);
    2709           2 :         EXPECT_STREQ(oObj.ToString().c_str(), "ab");
    2710             :     }
    2711             :     {
    2712           3 :         CPLJSONObject oObj(std::string("ab"));
    2713           1 :         EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::String);
    2714           2 :         EXPECT_STREQ(oObj.ToString().c_str(), "ab");
    2715             :     }
    2716             :     {
    2717           1 :         CPLJSONObject oObj;
    2718           1 :         oObj.Add("string", std::string("my_string"));
    2719           2 :         ASSERT_EQ(oObj.GetString("string"), std::string("my_string"));
    2720           2 :         ASSERT_EQ(oObj.GetString("inexisting_string", "default"),
    2721             :                   std::string("default"));
    2722           1 :         oObj.Add("const_char_star", nullptr);
    2723           1 :         oObj.Add("const_char_star", "my_const_char_star");
    2724           1 :         ASSERT_TRUE(oObj.GetObj("const_char_star").GetType() ==
    2725             :                     CPLJSONObject::Type::String);
    2726           1 :         oObj.Add("int", 1);
    2727           1 :         ASSERT_EQ(oObj.GetInteger("int"), 1);
    2728           1 :         ASSERT_EQ(oObj.GetInteger("inexisting_int", -987), -987);
    2729           1 :         ASSERT_TRUE(oObj.GetObj("int").GetType() ==
    2730             :                     CPLJSONObject::Type::Integer);
    2731           1 :         oObj.Add("int64", GINT64_MAX);
    2732           2 :         ASSERT_EQ(oObj.GetLong("int64"), GINT64_MAX);
    2733           2 :         ASSERT_EQ(oObj.GetLong("inexisting_int64", GINT64_MIN), GINT64_MIN);
    2734           1 :         ASSERT_TRUE(oObj.GetObj("int64").GetType() ==
    2735             :                     CPLJSONObject::Type::Long);
    2736           1 :         oObj.Add("double", 1.25);
    2737           1 :         ASSERT_EQ(oObj.GetDouble("double"), 1.25);
    2738           1 :         ASSERT_EQ(oObj.GetDouble("inexisting_double", -987.0), -987.0);
    2739           1 :         ASSERT_TRUE(oObj.GetObj("double").GetType() ==
    2740             :                     CPLJSONObject::Type::Double);
    2741           1 :         oObj.Add("array", CPLJSONArray());
    2742           1 :         ASSERT_TRUE(oObj.GetObj("array").GetType() ==
    2743             :                     CPLJSONObject::Type::Array);
    2744           1 :         oObj.Add("obj", CPLJSONObject());
    2745           1 :         ASSERT_TRUE(oObj.GetObj("obj").GetType() ==
    2746             :                     CPLJSONObject::Type::Object);
    2747           1 :         oObj.Add("bool", true);
    2748           1 :         ASSERT_EQ(oObj.GetBool("bool"), true);
    2749           1 :         ASSERT_EQ(oObj.GetBool("inexisting_bool", false), false);
    2750           1 :         ASSERT_TRUE(oObj.GetObj("bool").GetType() ==
    2751             :                     CPLJSONObject::Type::Boolean);
    2752           1 :         oObj.AddNull("null_field");
    2753           1 :         ASSERT_TRUE(oObj.GetObj("null_field").GetType() ==
    2754             :                     CPLJSONObject::Type::Null);
    2755           1 :         ASSERT_TRUE(oObj.GetObj("inexisting").GetType() ==
    2756             :                     CPLJSONObject::Type::Unknown);
    2757           1 :         oObj.Set("string", std::string("my_string"));
    2758           1 :         oObj.Set("const_char_star", nullptr);
    2759           1 :         oObj.Set("const_char_star", "my_const_char_star");
    2760           1 :         oObj.Set("int", 1);
    2761           1 :         oObj.Set("int64", GINT64_MAX);
    2762           1 :         oObj.Set("double", 1.25);
    2763             :         // oObj.Set("array", CPLJSONArray());
    2764             :         // oObj.Set("obj", CPLJSONObject());
    2765           1 :         oObj.Set("bool", true);
    2766           1 :         oObj.SetNull("null_field");
    2767           1 :         ASSERT_TRUE(CPLJSONArray().GetChildren().empty());
    2768           1 :         oObj.ToArray();
    2769             :     }
    2770             :     {
    2771           2 :         CPLJSONObject oObj;
    2772           1 :         oObj.Set("foo", "bar");
    2773           2 :         EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Spaced).c_str(),
    2774             :                      "{ \"foo\": \"bar\" }");
    2775           2 :         EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Pretty).c_str(),
    2776             :                      "{\n  \"foo\":\"bar\"\n}");
    2777           2 :         EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Plain).c_str(),
    2778             :                      "{\"foo\":\"bar\"}");
    2779             :     }
    2780             :     {
    2781           2 :         CPLJSONArray oArrayConstructorString(std::string("foo"));
    2782           1 :         CPLJSONArray oArray;
    2783           1 :         oArray.Add(CPLJSONObject());
    2784           1 :         oArray.Add(std::string("str"));
    2785           1 :         oArray.Add("const_char_star");
    2786           1 :         oArray.Add(1.25);
    2787           1 :         oArray.Add(1);
    2788           1 :         oArray.Add(GINT64_MAX);
    2789           1 :         oArray.Add(true);
    2790           1 :         oArray.AddNull();
    2791           1 :         ASSERT_EQ(oArray.Size(), 8);
    2792             : 
    2793           1 :         int nCount = 0;
    2794           9 :         for (const auto &obj : oArray)
    2795             :         {
    2796           8 :             ASSERT_EQ(obj.GetInternalHandle(),
    2797             :                       oArray[nCount].GetInternalHandle());
    2798           8 :             nCount++;
    2799             :         }
    2800           1 :         ASSERT_EQ(nCount, 8);
    2801             :     }
    2802             :     {
    2803           1 :         CPLJSONDocument oDocument;
    2804           1 :         ASSERT_TRUE(oDocument.LoadMemory(CPLString("{ \"/foo\" : \"bar\" }")));
    2805           2 :         ASSERT_EQ(oDocument.GetRoot().GetString("/foo"), std::string("bar"));
    2806             :     }
    2807             : }
    2808             : 
    2809             : // Test CPLRecodeIconv() with re-allocation
    2810             : // (this test also passed on Windows using its native recoding API)
    2811           4 : TEST_F(test_cpl, CPLRecodeIconv)
    2812             : {
    2813             : #if defined(CPL_RECODE_ICONV) || defined(_WIN32)
    2814           1 :     int N = 32800;
    2815           1 :     char *pszIn = static_cast<char *>(CPLMalloc(N + 1));
    2816       32801 :     for (int i = 0; i < N; i++)
    2817       32800 :         pszIn[i] = '\xA1';
    2818           1 :     pszIn[N] = 0;
    2819           1 :     char *pszExpected = static_cast<char *>(CPLMalloc(N * 2 + 1));
    2820       32801 :     for (int i = 0; i < N; i++)
    2821             :     {
    2822       32800 :         pszExpected[2 * i] = '\xD0';
    2823       32800 :         pszExpected[2 * i + 1] = '\x81';
    2824             :     }
    2825           1 :     pszExpected[N * 2] = 0;
    2826           1 :     char *pszRet = CPLRecode(pszIn, "ISO-8859-5", CPL_ENC_UTF8);
    2827           1 :     EXPECT_EQ(memcmp(pszExpected, pszRet, N * 2 + 1), 0);
    2828           1 :     CPLFree(pszIn);
    2829           1 :     CPLFree(pszRet);
    2830           1 :     CPLFree(pszExpected);
    2831             : #else
    2832             :     GTEST_SKIP() << "CPL_RECODE_ICONV missing";
    2833             : #endif
    2834           1 : }
    2835             : 
    2836             : // Test CP1252 to UTF-8
    2837           4 : TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_strict_alloc)
    2838             : {
    2839           1 :     CPLClearRecodeWarningFlags();
    2840           1 :     CPLErrorReset();
    2841           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2842             :     // Euro character expands to 3-bytes
    2843           1 :     char *pszRet = CPLRecode("\x80", "CP1252", CPL_ENC_UTF8);
    2844           1 :     CPLPopErrorHandler();
    2845           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(), "");
    2846           1 :     EXPECT_EQ(memcmp(pszRet, "\xE2\x82\xAC\x00", 4), 0);
    2847           1 :     CPLFree(pszRet);
    2848           1 : }
    2849             : 
    2850             : // Test CP1252 to UTF-8
    2851           4 : TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_with_ascii)
    2852             : {
    2853           1 :     CPLClearRecodeWarningFlags();
    2854           1 :     CPLErrorReset();
    2855           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2856           1 :     char *pszRet = CPLRecode("x\x80y", "CP1252", CPL_ENC_UTF8);
    2857           1 :     CPLPopErrorHandler();
    2858           1 :     EXPECT_STREQ(CPLGetLastErrorMsg(), "");
    2859           1 :     EXPECT_EQ(memcmp(pszRet, "x\xE2\x82\xACy\x00", 6), 0);
    2860           1 :     CPLFree(pszRet);
    2861           1 : }
    2862             : 
    2863             : // Test CP1252 to UTF-8
    2864           4 : TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_with_warning)
    2865             : {
    2866           1 :     CPLClearRecodeWarningFlags();
    2867           1 :     CPLErrorReset();
    2868           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2869             :     // \x90 is an invalid CP1252 character. Will be skipped
    2870           1 :     char *pszRet = CPLRecode("\x90\x80", "CP1252", CPL_ENC_UTF8);
    2871           1 :     CPLPopErrorHandler();
    2872           1 :     EXPECT_STREQ(
    2873             :         CPLGetLastErrorMsg(),
    2874             :         "One or several characters couldn't be converted correctly from CP1252 "
    2875             :         "to UTF-8. This warning will not be emitted anymore");
    2876           1 :     EXPECT_EQ(memcmp(pszRet, "\xE2\x82\xAC\x00", 4), 0);
    2877           1 :     CPLFree(pszRet);
    2878           1 : }
    2879             : 
    2880             : // Test CPLHTTPParseMultipartMime()
    2881           4 : TEST_F(test_cpl, CPLHTTPParseMultipartMime)
    2882             : {
    2883             :     CPLHTTPResult *psResult;
    2884             : 
    2885             :     psResult =
    2886           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2887           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2888           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2889           1 :     CPLPopErrorHandler();
    2890           1 :     CPLHTTPDestroyResult(psResult);
    2891             : 
    2892             :     // Missing boundary value
    2893             :     psResult =
    2894           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2895           1 :     psResult->pszContentType = CPLStrdup("multipart/form-data; boundary=");
    2896           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2897           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2898           1 :     CPLPopErrorHandler();
    2899           1 :     CPLHTTPDestroyResult(psResult);
    2900             : 
    2901             :     // No content
    2902             :     psResult =
    2903           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2904           1 :     psResult->pszContentType =
    2905           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2906           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2907           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2908           1 :     CPLPopErrorHandler();
    2909           1 :     CPLHTTPDestroyResult(psResult);
    2910             : 
    2911             :     // No part
    2912             :     psResult =
    2913           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2914           1 :     psResult->pszContentType =
    2915           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2916             :     {
    2917           1 :         const char *pszText = "--myboundary  some junk\r\n";
    2918           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    2919           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    2920             :     }
    2921           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2922           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2923           1 :     CPLPopErrorHandler();
    2924           1 :     CPLHTTPDestroyResult(psResult);
    2925             : 
    2926             :     // Missing end boundary
    2927             :     psResult =
    2928           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2929           1 :     psResult->pszContentType =
    2930           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2931             :     {
    2932           1 :         const char *pszText = "--myboundary  some junk\r\n"
    2933             :                               "\r\n"
    2934             :                               "Bla";
    2935           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    2936           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    2937             :     }
    2938           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2939           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2940           1 :     CPLPopErrorHandler();
    2941           1 :     CPLHTTPDestroyResult(psResult);
    2942             : 
    2943             :     // Truncated header
    2944             :     psResult =
    2945           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2946           1 :     psResult->pszContentType =
    2947           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2948             :     {
    2949           1 :         const char *pszText = "--myboundary  some junk\r\n"
    2950             :                               "Content-Type: foo";
    2951           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    2952           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    2953             :     }
    2954           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2955           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2956           1 :     CPLPopErrorHandler();
    2957           1 :     CPLHTTPDestroyResult(psResult);
    2958             : 
    2959             :     // Invalid end boundary
    2960             :     psResult =
    2961           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2962           1 :     psResult->pszContentType =
    2963           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2964             :     {
    2965           1 :         const char *pszText = "--myboundary  some junk\r\n"
    2966             :                               "\r\n"
    2967             :                               "Bla"
    2968             :                               "\r\n"
    2969             :                               "--myboundary";
    2970           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    2971           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    2972             :     }
    2973           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2974           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2975           1 :     CPLPopErrorHandler();
    2976           1 :     CPLHTTPDestroyResult(psResult);
    2977             : 
    2978             :     // Invalid end boundary
    2979             :     psResult =
    2980           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    2981           1 :     psResult->pszContentType =
    2982           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    2983             :     {
    2984           1 :         const char *pszText = "--myboundary  some junk\r\n"
    2985             :                               "\r\n"
    2986             :                               "Bla"
    2987             :                               "\r\n"
    2988             :                               "--myboundary";
    2989           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    2990           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    2991             :     }
    2992           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    2993           1 :     EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    2994           1 :     CPLPopErrorHandler();
    2995           1 :     CPLHTTPDestroyResult(psResult);
    2996             : 
    2997             :     // Valid single part, no header
    2998             :     psResult =
    2999           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    3000           1 :     psResult->pszContentType =
    3001           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    3002             :     {
    3003           1 :         const char *pszText = "--myboundary  some junk\r\n"
    3004             :                               "\r\n"
    3005             :                               "Bla"
    3006             :                               "\r\n"
    3007             :                               "--myboundary--\r\n";
    3008           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    3009           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    3010             :     }
    3011           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3012           1 :     EXPECT_EQ(psResult->nMimePartCount, 1);
    3013           1 :     if (psResult->nMimePartCount == 1)
    3014             :     {
    3015           1 :         EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
    3016             :                   static_cast<char **>(nullptr));
    3017           1 :         EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
    3018           1 :         EXPECT_TRUE(
    3019             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
    3020             :                     "Bla", 3) == 0);
    3021             :     }
    3022           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3023           1 :     CPLHTTPDestroyResult(psResult);
    3024             : 
    3025             :     // Valid single part, with header
    3026             :     psResult =
    3027           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    3028           1 :     psResult->pszContentType =
    3029           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    3030             :     {
    3031           1 :         const char *pszText = "--myboundary  some junk\r\n"
    3032             :                               "Content-Type: bla\r\n"
    3033             :                               "\r\n"
    3034             :                               "Bla"
    3035             :                               "\r\n"
    3036             :                               "--myboundary--\r\n";
    3037           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    3038           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    3039             :     }
    3040           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3041           1 :     EXPECT_EQ(psResult->nMimePartCount, 1);
    3042           1 :     if (psResult->nMimePartCount == 1)
    3043             :     {
    3044           1 :         EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 1);
    3045           1 :         EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
    3046             :                      "Content-Type=bla");
    3047           1 :         EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
    3048           1 :         EXPECT_TRUE(
    3049             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
    3050             :                     "Bla", 3) == 0);
    3051             :     }
    3052           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3053           1 :     CPLHTTPDestroyResult(psResult);
    3054             : 
    3055             :     // Valid single part, 2 headers
    3056             :     psResult =
    3057           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    3058           1 :     psResult->pszContentType =
    3059           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    3060             :     {
    3061           1 :         const char *pszText = "--myboundary  some junk\r\n"
    3062             :                               "Content-Type: bla\r\n"
    3063             :                               "Content-Disposition: bar\r\n"
    3064             :                               "\r\n"
    3065             :                               "Bla"
    3066             :                               "\r\n"
    3067             :                               "--myboundary--\r\n";
    3068           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    3069           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    3070             :     }
    3071           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3072           1 :     EXPECT_EQ(psResult->nMimePartCount, 1);
    3073           1 :     if (psResult->nMimePartCount == 1)
    3074             :     {
    3075           1 :         EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 2);
    3076           1 :         EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
    3077             :                      "Content-Type=bla");
    3078           1 :         EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[1],
    3079             :                      "Content-Disposition=bar");
    3080           1 :         EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
    3081           1 :         EXPECT_TRUE(
    3082             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
    3083             :                     "Bla", 3) == 0);
    3084             :     }
    3085           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3086           1 :     CPLHTTPDestroyResult(psResult);
    3087             : 
    3088             :     // Single part, but with header without extra terminating \r\n
    3089             :     // (invalid normally, but apparently necessary for some ArcGIS WCS
    3090             :     // implementations)
    3091             :     psResult =
    3092           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    3093           1 :     psResult->pszContentType =
    3094           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    3095             :     {
    3096           1 :         const char *pszText = "--myboundary  some junk\r\n"
    3097             :                               "Content-Type: bla\r\n"
    3098             :                               "Bla"
    3099             :                               "\r\n"
    3100             :                               "--myboundary--\r\n";
    3101           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    3102           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    3103             :     }
    3104           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3105           1 :     EXPECT_EQ(psResult->nMimePartCount, 1);
    3106           1 :     if (psResult->nMimePartCount == 1)
    3107             :     {
    3108           1 :         EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
    3109             :                      "Content-Type=bla");
    3110           1 :         EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
    3111           1 :         EXPECT_TRUE(
    3112             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
    3113             :                     "Bla", 3) == 0);
    3114             :     }
    3115           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3116           1 :     CPLHTTPDestroyResult(psResult);
    3117             : 
    3118             :     // Valid 2 parts, no header
    3119             :     psResult =
    3120           1 :         static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
    3121           1 :     psResult->pszContentType =
    3122           1 :         CPLStrdup("multipart/form-data; boundary=myboundary");
    3123             :     {
    3124           1 :         const char *pszText = "--myboundary  some junk\r\n"
    3125             :                               "\r\n"
    3126             :                               "Bla"
    3127             :                               "\r\n"
    3128             :                               "--myboundary\r\n"
    3129             :                               "\r\n"
    3130             :                               "second part"
    3131             :                               "\r\n"
    3132             :                               "--myboundary--\r\n";
    3133           1 :         psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
    3134           1 :         psResult->nDataLen = static_cast<int>(strlen(pszText));
    3135             :     }
    3136           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3137           1 :     EXPECT_EQ(psResult->nMimePartCount, 2);
    3138           1 :     if (psResult->nMimePartCount == 2)
    3139             :     {
    3140           1 :         EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
    3141             :                   static_cast<char **>(nullptr));
    3142           1 :         EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
    3143           1 :         EXPECT_TRUE(
    3144             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
    3145             :                     "Bla", 3) == 0);
    3146           1 :         EXPECT_EQ(psResult->pasMimePart[1].nDataLen, 11);
    3147           1 :         EXPECT_TRUE(
    3148             :             strncmp(reinterpret_cast<char *>(psResult->pasMimePart[1].pabyData),
    3149             :                     "second part", 11) == 0);
    3150             :     }
    3151           1 :     EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
    3152           1 :     CPLHTTPDestroyResult(psResult);
    3153           1 : }
    3154             : 
    3155             : // Test cpl::down_cast
    3156           4 : TEST_F(test_cpl, down_cast)
    3157             : {
    3158             :     struct Base
    3159             :     {
    3160           2 :         virtual ~Base()
    3161           2 :         {
    3162           2 :         }
    3163             :     };
    3164             : 
    3165             :     struct Derived : public Base
    3166             :     {
    3167             :     };
    3168             : 
    3169           0 :     Base b;
    3170           0 :     Derived d;
    3171           1 :     Base *p_b_d = &d;
    3172             : 
    3173             : #ifdef wont_compile
    3174             :     struct OtherBase
    3175             :     {
    3176             :     };
    3177             : 
    3178             :     OtherBase ob;
    3179             :     ASSERT_EQ(cpl::down_cast<OtherBase *>(p_b_d), &ob);
    3180             : #endif
    3181             : #ifdef compile_with_warning
    3182             :     ASSERT_EQ(cpl::down_cast<Base *>(p_b_d), p_b_d);
    3183             : #endif
    3184           1 :     ASSERT_EQ(cpl::down_cast<Derived *>(p_b_d), &d);
    3185           1 :     ASSERT_EQ(cpl::down_cast<Derived *>(static_cast<Base *>(nullptr)),
    3186             :               static_cast<Derived *>(nullptr));
    3187             : }
    3188             : 
    3189             : // Test CPLPrintTime() in particular case of RFC822 formatting in C locale
    3190           4 : TEST_F(test_cpl, CPLPrintTime_RFC822)
    3191             : {
    3192             :     char szDate[64];
    3193             :     struct tm tm;
    3194           1 :     tm.tm_sec = 56;
    3195           1 :     tm.tm_min = 34;
    3196           1 :     tm.tm_hour = 12;
    3197           1 :     tm.tm_mday = 20;
    3198           1 :     tm.tm_mon = 6 - 1;
    3199           1 :     tm.tm_year = 2018 - 1900;
    3200           1 :     tm.tm_wday = 3;   // Wednesday
    3201           1 :     tm.tm_yday = 0;   // unused
    3202           1 :     tm.tm_isdst = 0;  // unused
    3203           1 :     int nRet = CPLPrintTime(szDate, sizeof(szDate) - 1,
    3204             :                             "%a, %d %b %Y %H:%M:%S GMT", &tm, "C");
    3205           1 :     szDate[nRet] = 0;
    3206           1 :     ASSERT_STREQ(szDate, "Wed, 20 Jun 2018 12:34:56 GMT");
    3207             : }
    3208             : 
    3209             : // Test CPLAutoClose
    3210           4 : TEST_F(test_cpl, CPLAutoClose)
    3211             : {
    3212             :     static int counter = 0;
    3213             : 
    3214             :     class AutoCloseTest
    3215             :     {
    3216             :       public:
    3217           2 :         AutoCloseTest()
    3218           2 :         {
    3219           2 :             counter += 222;
    3220           2 :         }
    3221             : 
    3222           4 :         virtual ~AutoCloseTest()
    3223           2 :         {
    3224           2 :             counter -= 22;
    3225           4 :         }
    3226             : 
    3227           2 :         static AutoCloseTest *Create()
    3228             :         {
    3229           2 :             return new AutoCloseTest;
    3230             :         }
    3231             : 
    3232           2 :         static void Destroy(AutoCloseTest *p)
    3233             :         {
    3234           2 :             delete p;
    3235           2 :         }
    3236             :     };
    3237             : 
    3238             :     {
    3239           1 :         AutoCloseTest *p1 = AutoCloseTest::Create();
    3240           2 :         CPL_AUTO_CLOSE_WRAP(p1, AutoCloseTest::Destroy);
    3241             : 
    3242           1 :         AutoCloseTest *p2 = AutoCloseTest::Create();
    3243           1 :         CPL_AUTO_CLOSE_WRAP(p2, AutoCloseTest::Destroy);
    3244             :     }
    3245           1 :     ASSERT_EQ(counter, 400);
    3246             : }
    3247             : 
    3248             : // Test cpl_minixml
    3249           4 : TEST_F(test_cpl, cpl_minixml)
    3250             : {
    3251           1 :     CPLXMLNode *psRoot = CPLCreateXMLNode(nullptr, CXT_Element, "Root");
    3252           1 :     CPLXMLNode *psElt = CPLCreateXMLElementAndValue(psRoot, "Elt", "value");
    3253           1 :     CPLAddXMLAttributeAndValue(psElt, "attr1", "val1");
    3254           1 :     CPLAddXMLAttributeAndValue(psElt, "attr2", "val2");
    3255           1 :     EXPECT_GE(CPLXMLNodeGetRAMUsageEstimate(psRoot), 0);
    3256           1 :     char *str = CPLSerializeXMLTree(psRoot);
    3257           1 :     CPLDestroyXMLNode(psRoot);
    3258           1 :     ASSERT_STREQ(
    3259             :         str,
    3260             :         "<Root>\n  <Elt attr1=\"val1\" attr2=\"val2\">value</Elt>\n</Root>\n");
    3261           1 :     CPLFree(str);
    3262             : }
    3263             : 
    3264             : // Test CPLCharUniquePtr
    3265           4 : TEST_F(test_cpl, CPLCharUniquePtr)
    3266             : {
    3267           0 :     CPLCharUniquePtr x;
    3268           1 :     ASSERT_TRUE(x.get() == nullptr);
    3269           1 :     x.reset(CPLStrdup("foo"));
    3270           1 :     ASSERT_STREQ(x.get(), "foo");
    3271             : }
    3272             : 
    3273             : // Test CPLJSonStreamingWriter
    3274           4 : TEST_F(test_cpl, CPLJSonStreamingWriter)
    3275             : {
    3276             :     {
    3277           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3278           2 :         ASSERT_EQ(x.GetString(), std::string());
    3279             :     }
    3280             :     {
    3281           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3282           1 :         x.Add(true);
    3283           2 :         ASSERT_EQ(x.GetString(), std::string("true"));
    3284             :     }
    3285             :     {
    3286           1 :         std::string res;
    3287             : 
    3288             :         struct MyCallback
    3289             :         {
    3290           1 :             static void f(const char *pszText, void *user_data)
    3291             :             {
    3292           1 :                 *static_cast<std::string *>(user_data) += pszText;
    3293           1 :             }
    3294             :         };
    3295             : 
    3296           1 :         CPLJSonStreamingWriter x(&MyCallback::f, &res);
    3297           1 :         x.Add(true);
    3298           2 :         ASSERT_EQ(x.GetString(), std::string());
    3299           2 :         ASSERT_EQ(res, std::string("true"));
    3300             :     }
    3301             :     {
    3302           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3303           1 :         x.Add(false);
    3304           2 :         ASSERT_EQ(x.GetString(), std::string("false"));
    3305             :     }
    3306             :     {
    3307           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3308           1 :         x.AddNull();
    3309           2 :         ASSERT_EQ(x.GetString(), std::string("null"));
    3310             :     }
    3311             :     {
    3312           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3313           1 :         x.Add(1);
    3314           2 :         ASSERT_EQ(x.GetString(), std::string("1"));
    3315             :     }
    3316             :     {
    3317           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3318           1 :         x.Add(4200000000U);
    3319           2 :         ASSERT_EQ(x.GetString(), std::string("4200000000"));
    3320             :     }
    3321             :     {
    3322           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3323           1 :         x.Add(static_cast<std::int64_t>(-10000) * 1000000);
    3324           2 :         ASSERT_EQ(x.GetString(), std::string("-10000000000"));
    3325             :     }
    3326             :     {
    3327           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3328           1 :         x.Add(static_cast<std::uint64_t>(10000) * 1000000);
    3329           2 :         ASSERT_EQ(x.GetString(), std::string("10000000000"));
    3330             :     }
    3331             :     {
    3332           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3333           1 :         x.Add(1.5f);
    3334           2 :         ASSERT_EQ(x.GetString(), std::string("1.5"));
    3335             :     }
    3336             :     {
    3337           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3338           1 :         x.Add(std::numeric_limits<float>::quiet_NaN());
    3339           2 :         ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
    3340             :     }
    3341             :     {
    3342           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3343           1 :         x.Add(std::numeric_limits<float>::infinity());
    3344           2 :         ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
    3345             :     }
    3346             :     {
    3347           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3348           1 :         x.Add(-std::numeric_limits<float>::infinity());
    3349           2 :         ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
    3350             :     }
    3351             :     {
    3352           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3353           1 :         x.Add(1.25);
    3354           2 :         ASSERT_EQ(x.GetString(), std::string("1.25"));
    3355             :     }
    3356             :     {
    3357           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3358           1 :         x.Add(std::numeric_limits<double>::quiet_NaN());
    3359           2 :         ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
    3360             :     }
    3361             :     {
    3362           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3363           1 :         x.Add(std::numeric_limits<double>::infinity());
    3364           2 :         ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
    3365             :     }
    3366             :     {
    3367           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3368           1 :         x.Add(-std::numeric_limits<double>::infinity());
    3369           2 :         ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
    3370             :     }
    3371             :     {
    3372           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3373           1 :         x.Add(std::string("foo\\bar\"baz\b\f\n\r\t"
    3374             :                           "\x01"
    3375             :                           "boo"));
    3376           2 :         ASSERT_EQ(
    3377             :             x.GetString(),
    3378             :             std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
    3379             :     }
    3380             :     {
    3381           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3382           1 :         x.Add("foo\\bar\"baz\b\f\n\r\t"
    3383             :               "\x01"
    3384             :               "boo");
    3385           2 :         ASSERT_EQ(
    3386             :             x.GetString(),
    3387             :             std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
    3388             :     }
    3389             :     {
    3390           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3391           1 :         x.SetPrettyFormatting(false);
    3392             :         {
    3393           1 :             auto ctxt(x.MakeObjectContext());
    3394             :         }
    3395           2 :         ASSERT_EQ(x.GetString(), std::string("{}"));
    3396             :     }
    3397             :     {
    3398           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3399             :         {
    3400           1 :             auto ctxt(x.MakeObjectContext());
    3401             :         }
    3402           2 :         ASSERT_EQ(x.GetString(), std::string("{}"));
    3403             :     }
    3404             :     {
    3405           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3406           1 :         x.SetPrettyFormatting(false);
    3407             :         {
    3408           2 :             auto ctxt(x.MakeObjectContext());
    3409           1 :             x.AddObjKey("key");
    3410           1 :             x.Add("value");
    3411             :         }
    3412           2 :         ASSERT_EQ(x.GetString(), std::string("{\"key\":\"value\"}"));
    3413             :     }
    3414             :     {
    3415           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3416             :         {
    3417           2 :             auto ctxt(x.MakeObjectContext());
    3418           1 :             x.AddObjKey("key");
    3419           1 :             x.Add("value");
    3420             :         }
    3421           2 :         ASSERT_EQ(x.GetString(), std::string("{\n  \"key\": \"value\"\n}"));
    3422             :     }
    3423             :     {
    3424           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3425             :         {
    3426           2 :             auto ctxt(x.MakeObjectContext());
    3427           1 :             x.AddObjKey("key");
    3428           1 :             x.Add("value");
    3429           1 :             x.AddObjKey("key2");
    3430           1 :             x.Add("value2");
    3431             :         }
    3432           2 :         ASSERT_EQ(
    3433             :             x.GetString(),
    3434             :             std::string("{\n  \"key\": \"value\",\n  \"key2\": \"value2\"\n}"));
    3435             :     }
    3436             :     {
    3437           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3438             :         {
    3439           1 :             auto ctxt(x.MakeArrayContext());
    3440             :         }
    3441           2 :         ASSERT_EQ(x.GetString(), std::string("[]"));
    3442             :     }
    3443             :     {
    3444           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3445             :         {
    3446           2 :             auto ctxt(x.MakeArrayContext());
    3447           1 :             x.Add(1);
    3448             :         }
    3449           2 :         ASSERT_EQ(x.GetString(), std::string("[\n  1\n]"));
    3450             :     }
    3451             :     {
    3452           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3453             :         {
    3454           2 :             auto ctxt(x.MakeArrayContext());
    3455           1 :             x.Add(1);
    3456           1 :             x.Add(2);
    3457             :         }
    3458           2 :         ASSERT_EQ(x.GetString(), std::string("[\n  1,\n  2\n]"));
    3459             :     }
    3460             :     {
    3461           1 :         CPLJSonStreamingWriter x(nullptr, nullptr);
    3462             :         {
    3463           2 :             auto ctxt(x.MakeArrayContext(true));
    3464           1 :             x.Add(1);
    3465           1 :             x.Add(2);
    3466             :         }
    3467           2 :         ASSERT_EQ(x.GetString(), std::string("[1, 2]"));
    3468             :     }
    3469             : }
    3470             : 
    3471             : // Test CPLWorkerThreadPool
    3472           4 : TEST_F(test_cpl, CPLWorkerThreadPool)
    3473             : {
    3474           1 :     CPLWorkerThreadPool oPool;
    3475           1 :     ASSERT_TRUE(oPool.Setup(2, nullptr, nullptr, false));
    3476             : 
    3477        3000 :     const auto myJob = [](void *pData) { (*static_cast<int *>(pData))++; };
    3478             : 
    3479             :     {
    3480           1 :         std::vector<int> res(1000);
    3481        1001 :         for (int i = 0; i < 1000; i++)
    3482             :         {
    3483        1000 :             res[i] = i;
    3484        1000 :             oPool.SubmitJob(myJob, &res[i]);
    3485             :         }
    3486           1 :         oPool.WaitCompletion();
    3487        1001 :         for (int i = 0; i < 1000; i++)
    3488             :         {
    3489        1000 :             ASSERT_EQ(res[i], i + 1);
    3490             :         }
    3491             :     }
    3492             : 
    3493             :     {
    3494           1 :         std::vector<int> res(1000);
    3495           1 :         std::vector<void *> resPtr(1000);
    3496        1001 :         for (int i = 0; i < 1000; i++)
    3497             :         {
    3498        1000 :             res[i] = i;
    3499        1000 :             resPtr[i] = res.data() + i;
    3500             :         }
    3501           1 :         oPool.SubmitJobs(myJob, resPtr);
    3502           1 :         oPool.WaitEvent();
    3503           1 :         oPool.WaitCompletion();
    3504        1001 :         for (int i = 0; i < 1000; i++)
    3505             :         {
    3506        1000 :             ASSERT_EQ(res[i], i + 1);
    3507             :         }
    3508             :     }
    3509             : 
    3510             :     {
    3511           1 :         auto jobQueue1 = oPool.CreateJobQueue();
    3512           1 :         auto jobQueue2 = oPool.CreateJobQueue();
    3513             : 
    3514           1 :         ASSERT_EQ(jobQueue1->GetPool(), &oPool);
    3515             : 
    3516           1 :         std::vector<int> res(1000);
    3517        1001 :         for (int i = 0; i < 1000; i++)
    3518             :         {
    3519        1000 :             res[i] = i;
    3520        1000 :             if (i % 2)
    3521         500 :                 jobQueue1->SubmitJob(myJob, &res[i]);
    3522             :             else
    3523         500 :                 jobQueue2->SubmitJob(myJob, &res[i]);
    3524             :         }
    3525           1 :         jobQueue1->WaitCompletion();
    3526           1 :         jobQueue2->WaitCompletion();
    3527        1001 :         for (int i = 0; i < 1000; i++)
    3528             :         {
    3529        1000 :             ASSERT_EQ(res[i], i + 1);
    3530             :         }
    3531             :     }
    3532             : }
    3533             : 
    3534             : // Test CPLHTTPFetch
    3535           4 : TEST_F(test_cpl, CPLHTTPFetch)
    3536             : {
    3537             : #ifdef HAVE_CURL
    3538           2 :     CPLStringList oOptions;
    3539           1 :     oOptions.AddNameValue("FORM_ITEM_COUNT", "5");
    3540           1 :     oOptions.AddNameValue("FORM_KEY_0", "qqq");
    3541           1 :     oOptions.AddNameValue("FORM_VALUE_0", "www");
    3542           1 :     CPLHTTPResult *pResult = CPLHTTPFetch("http://example.com", oOptions);
    3543           1 :     EXPECT_EQ(pResult->nStatus, 34);
    3544           1 :     CPLHTTPDestroyResult(pResult);
    3545           1 :     pResult = nullptr;
    3546           1 :     oOptions.Clear();
    3547             : 
    3548           1 :     oOptions.AddNameValue("FORM_FILE_PATH", "not_existed");
    3549           1 :     pResult = CPLHTTPFetch("http://example.com", oOptions);
    3550           1 :     EXPECT_EQ(pResult->nStatus, 34);
    3551           1 :     CPLHTTPDestroyResult(pResult);
    3552             : #else
    3553             :     GTEST_SKIP() << "CURL not available";
    3554             : #endif  // HAVE_CURL
    3555           1 : }
    3556             : 
    3557             : // Test CPLHTTPPushFetchCallback
    3558           4 : TEST_F(test_cpl, CPLHTTPPushFetchCallback)
    3559             : {
    3560             :     struct myCbkUserDataStruct
    3561             :     {
    3562             :         CPLString osURL{};
    3563             :         CSLConstList papszOptions = nullptr;
    3564             :         GDALProgressFunc pfnProgress = nullptr;
    3565             :         void *pProgressArg = nullptr;
    3566             :         CPLHTTPFetchWriteFunc pfnWrite = nullptr;
    3567             :         void *pWriteArg = nullptr;
    3568             :     };
    3569             : 
    3570           1 :     const auto myCbk = [](const char *pszURL, CSLConstList papszOptions,
    3571             :                           GDALProgressFunc pfnProgress, void *pProgressArg,
    3572             :                           CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
    3573             :                           void *pUserData)
    3574             :     {
    3575           1 :         myCbkUserDataStruct *pCbkUserData =
    3576             :             static_cast<myCbkUserDataStruct *>(pUserData);
    3577           1 :         pCbkUserData->osURL = pszURL;
    3578           1 :         pCbkUserData->papszOptions = papszOptions;
    3579           1 :         pCbkUserData->pfnProgress = pfnProgress;
    3580           1 :         pCbkUserData->pProgressArg = pProgressArg;
    3581           1 :         pCbkUserData->pfnWrite = pfnWrite;
    3582           1 :         pCbkUserData->pWriteArg = pWriteArg;
    3583             :         auto psResult =
    3584           1 :             static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
    3585           1 :         psResult->nStatus = 123;
    3586           1 :         return psResult;
    3587             :     };
    3588             : 
    3589           1 :     myCbkUserDataStruct userData;
    3590           1 :     EXPECT_TRUE(CPLHTTPPushFetchCallback(myCbk, &userData));
    3591             : 
    3592           1 :     int progressArg = 0;
    3593           0 :     const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
    3594           0 :     { return 0; };
    3595           1 :     int writeCbkArg = 00;
    3596             : 
    3597           1 :     CPLStringList aosOptions;
    3598           1 :     GDALProgressFunc pfnProgress = GDALTermProgress;
    3599           1 :     CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
    3600             :     CPLHTTPResult *pResult =
    3601           1 :         CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
    3602             :                        &progressArg, pfnWriteCbk, &writeCbkArg);
    3603           1 :     ASSERT_TRUE(pResult != nullptr);
    3604           1 :     EXPECT_EQ(pResult->nStatus, 123);
    3605           1 :     CPLHTTPDestroyResult(pResult);
    3606             : 
    3607           1 :     EXPECT_TRUE(CPLHTTPPopFetchCallback());
    3608           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3609           1 :     EXPECT_TRUE(!CPLHTTPPopFetchCallback());
    3610           1 :     CPLPopErrorHandler();
    3611             : 
    3612           1 :     EXPECT_STREQ(userData.osURL, "http://example.com");
    3613           1 :     EXPECT_EQ(userData.papszOptions, aosOptions.List());
    3614           1 :     EXPECT_EQ(userData.pfnProgress, pfnProgress);
    3615           1 :     EXPECT_EQ(userData.pProgressArg, &progressArg);
    3616           1 :     EXPECT_EQ(userData.pfnWrite, pfnWriteCbk);
    3617           1 :     EXPECT_EQ(userData.pWriteArg, &writeCbkArg);
    3618             : }
    3619             : 
    3620             : // Test CPLHTTPSetFetchCallback
    3621           4 : TEST_F(test_cpl, CPLHTTPSetFetchCallback)
    3622             : {
    3623             :     struct myCbkUserDataStruct
    3624             :     {
    3625             :         CPLString osURL{};
    3626             :         CSLConstList papszOptions = nullptr;
    3627             :         GDALProgressFunc pfnProgress = nullptr;
    3628             :         void *pProgressArg = nullptr;
    3629             :         CPLHTTPFetchWriteFunc pfnWrite = nullptr;
    3630             :         void *pWriteArg = nullptr;
    3631             :     };
    3632             : 
    3633           1 :     const auto myCbk2 = [](const char *pszURL, CSLConstList papszOptions,
    3634             :                            GDALProgressFunc pfnProgress, void *pProgressArg,
    3635             :                            CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
    3636             :                            void *pUserData)
    3637             :     {
    3638           1 :         myCbkUserDataStruct *pCbkUserData =
    3639             :             static_cast<myCbkUserDataStruct *>(pUserData);
    3640           1 :         pCbkUserData->osURL = pszURL;
    3641           1 :         pCbkUserData->papszOptions = papszOptions;
    3642           1 :         pCbkUserData->pfnProgress = pfnProgress;
    3643           1 :         pCbkUserData->pProgressArg = pProgressArg;
    3644           1 :         pCbkUserData->pfnWrite = pfnWrite;
    3645           1 :         pCbkUserData->pWriteArg = pWriteArg;
    3646             :         auto psResult =
    3647           1 :             static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
    3648           1 :         psResult->nStatus = 124;
    3649           1 :         return psResult;
    3650             :     };
    3651           1 :     myCbkUserDataStruct userData2;
    3652           1 :     CPLHTTPSetFetchCallback(myCbk2, &userData2);
    3653             : 
    3654           1 :     int progressArg = 0;
    3655           0 :     const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
    3656           0 :     { return 0; };
    3657           1 :     int writeCbkArg = 00;
    3658             : 
    3659           1 :     CPLStringList aosOptions;
    3660           1 :     GDALProgressFunc pfnProgress = GDALTermProgress;
    3661           1 :     CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
    3662             :     CPLHTTPResult *pResult =
    3663           1 :         CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
    3664             :                        &progressArg, pfnWriteCbk, &writeCbkArg);
    3665           1 :     ASSERT_TRUE(pResult != nullptr);
    3666           1 :     EXPECT_EQ(pResult->nStatus, 124);
    3667           1 :     CPLHTTPDestroyResult(pResult);
    3668             : 
    3669           1 :     CPLHTTPSetFetchCallback(nullptr, nullptr);
    3670             : 
    3671           1 :     EXPECT_STREQ(userData2.osURL, "http://example.com");
    3672           1 :     EXPECT_EQ(userData2.papszOptions, aosOptions.List());
    3673           1 :     EXPECT_EQ(userData2.pfnProgress, pfnProgress);
    3674           1 :     EXPECT_EQ(userData2.pProgressArg, &progressArg);
    3675           1 :     EXPECT_EQ(userData2.pfnWrite, pfnWriteCbk);
    3676           1 :     EXPECT_EQ(userData2.pWriteArg, &writeCbkArg);
    3677             : }
    3678             : 
    3679             : // Test CPLLoadConfigOptionsFromFile() and
    3680             : // CPLLoadConfigOptionsFromPredefinedFiles()
    3681           4 : TEST_F(test_cpl, CPLLoadConfigOptionsFromFile)
    3682             : {
    3683           1 :     CPLLoadConfigOptionsFromFile("/i/do/not/exist", false);
    3684             : 
    3685           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
    3686           1 :     VSIFPrintfL(fp, "# some comment\n");
    3687           1 :     VSIFPrintfL(fp, "\n");    // blank line
    3688           1 :     VSIFPrintfL(fp, "  \n");  // blank line
    3689           1 :     VSIFPrintfL(fp, "[configoptions]\n");
    3690           1 :     VSIFPrintfL(fp, "# some comment\n");
    3691           1 :     VSIFPrintfL(fp, "FOO_CONFIGOPTION=BAR\n");
    3692           1 :     VSIFCloseL(fp);
    3693             : 
    3694             :     // Try CPLLoadConfigOptionsFromFile()
    3695           1 :     CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
    3696           1 :     ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
    3697           1 :     CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
    3698             : 
    3699             :     // Try CPLLoadConfigOptionsFromPredefinedFiles() with GDAL_CONFIG_FILE set
    3700           1 :     CPLSetConfigOption("GDAL_CONFIG_FILE", "/vsimem/.gdal/gdalrc");
    3701           1 :     CPLLoadConfigOptionsFromPredefinedFiles();
    3702           1 :     ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
    3703           1 :     CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
    3704             : 
    3705             :     // Try CPLLoadConfigOptionsFromPredefinedFiles() with $HOME/.gdal/gdalrc
    3706             :     // file
    3707             : #ifdef _WIN32
    3708             :     const char *pszHOMEEnvVarName = "USERPROFILE";
    3709             : #else
    3710           1 :     const char *pszHOMEEnvVarName = "HOME";
    3711             : #endif
    3712           1 :     CPLString osOldVal(CPLGetConfigOption(pszHOMEEnvVarName, ""));
    3713           1 :     CPLSetConfigOption(pszHOMEEnvVarName, "/vsimem/");
    3714           1 :     CPLLoadConfigOptionsFromPredefinedFiles();
    3715           1 :     ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
    3716           1 :     CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
    3717           1 :     if (!osOldVal.empty())
    3718           1 :         CPLSetConfigOption(pszHOMEEnvVarName, osOldVal.c_str());
    3719             :     else
    3720           0 :         CPLSetConfigOption(pszHOMEEnvVarName, nullptr);
    3721             : 
    3722           1 :     VSIUnlink("/vsimem/.gdal/gdalrc");
    3723             : }
    3724             : 
    3725             : // Test decompressor side of cpl_compressor.h
    3726           4 : TEST_F(test_cpl, decompressor)
    3727             : {
    3728             :     const auto compressionLambda =
    3729           0 :         [](const void * /* input_data */, size_t /* input_size */,
    3730             :            void ** /* output_data */, size_t * /* output_size */,
    3731             :            CSLConstList /* options */, void * /* compressor_user_data */)
    3732           0 :     { return false; };
    3733           1 :     int dummy = 0;
    3734             : 
    3735             :     CPLCompressor sComp;
    3736           1 :     sComp.nStructVersion = 1;
    3737           1 :     sComp.eType = CCT_COMPRESSOR;
    3738           1 :     sComp.pszId = "my_comp";
    3739           1 :     const char *const apszMetadata[] = {"FOO=BAR", nullptr};
    3740           1 :     sComp.papszMetadata = apszMetadata;
    3741           1 :     sComp.pfnFunc = compressionLambda;
    3742           1 :     sComp.user_data = &dummy;
    3743             : 
    3744           1 :     ASSERT_TRUE(CPLRegisterDecompressor(&sComp));
    3745             : 
    3746           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3747           1 :     ASSERT_TRUE(!CPLRegisterDecompressor(&sComp));
    3748           1 :     CPLPopErrorHandler();
    3749             : 
    3750           1 :     char **decompressors = CPLGetDecompressors();
    3751           1 :     ASSERT_TRUE(decompressors != nullptr);
    3752           1 :     EXPECT_TRUE(CSLFindString(decompressors, sComp.pszId) >= 0);
    3753           9 :     for (auto iter = decompressors; *iter; ++iter)
    3754             :     {
    3755           8 :         const auto pCompressor = CPLGetDecompressor(*iter);
    3756           8 :         EXPECT_TRUE(pCompressor);
    3757           8 :         if (pCompressor)
    3758             :         {
    3759             :             const char *pszOptions =
    3760           8 :                 CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
    3761           8 :             if (pszOptions)
    3762             :             {
    3763           3 :                 auto psNode = CPLParseXMLString(pszOptions);
    3764           3 :                 EXPECT_TRUE(psNode);
    3765           3 :                 CPLDestroyXMLNode(psNode);
    3766             :             }
    3767             :             else
    3768             :             {
    3769           5 :                 CPLDebug("TEST", "Decompressor %s has no OPTIONS", *iter);
    3770             :             }
    3771             :         }
    3772             :     }
    3773           1 :     CSLDestroy(decompressors);
    3774             : 
    3775           1 :     EXPECT_TRUE(CPLGetDecompressor("invalid") == nullptr);
    3776           1 :     const auto pCompressor = CPLGetDecompressor(sComp.pszId);
    3777           1 :     ASSERT_TRUE(pCompressor);
    3778           1 :     EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
    3779           1 :     EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
    3780             :               CSLCount(sComp.papszMetadata));
    3781           1 :     EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
    3782           1 :     EXPECT_EQ(pCompressor->user_data, sComp.user_data);
    3783             : 
    3784           1 :     CPLDestroyCompressorRegistry();
    3785           1 :     EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
    3786             : }
    3787             : 
    3788             : // Test compressor side of cpl_compressor.h
    3789           4 : TEST_F(test_cpl, compressor)
    3790             : {
    3791             :     const auto compressionLambda =
    3792           0 :         [](const void * /* input_data */, size_t /* input_size */,
    3793             :            void ** /* output_data */, size_t * /* output_size */,
    3794             :            CSLConstList /* options */, void * /* compressor_user_data */)
    3795           0 :     { return false; };
    3796           1 :     int dummy = 0;
    3797             : 
    3798             :     CPLCompressor sComp;
    3799           1 :     sComp.nStructVersion = 1;
    3800           1 :     sComp.eType = CCT_COMPRESSOR;
    3801           1 :     sComp.pszId = "my_comp";
    3802           1 :     const char *const apszMetadata[] = {"FOO=BAR", nullptr};
    3803           1 :     sComp.papszMetadata = apszMetadata;
    3804           1 :     sComp.pfnFunc = compressionLambda;
    3805           1 :     sComp.user_data = &dummy;
    3806             : 
    3807           1 :     ASSERT_TRUE(CPLRegisterCompressor(&sComp));
    3808             : 
    3809           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    3810           1 :     ASSERT_TRUE(!CPLRegisterCompressor(&sComp));
    3811           1 :     CPLPopErrorHandler();
    3812             : 
    3813           1 :     char **compressors = CPLGetCompressors();
    3814           1 :     ASSERT_TRUE(compressors != nullptr);
    3815           1 :     EXPECT_TRUE(CSLFindString(compressors, sComp.pszId) >= 0);
    3816           9 :     for (auto iter = compressors; *iter; ++iter)
    3817             :     {
    3818           8 :         const auto pCompressor = CPLGetCompressor(*iter);
    3819           8 :         EXPECT_TRUE(pCompressor);
    3820           8 :         if (pCompressor)
    3821             :         {
    3822             :             const char *pszOptions =
    3823           8 :                 CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
    3824           8 :             if (pszOptions)
    3825             :             {
    3826           7 :                 auto psNode = CPLParseXMLString(pszOptions);
    3827           7 :                 EXPECT_TRUE(psNode);
    3828           7 :                 CPLDestroyXMLNode(psNode);
    3829             :             }
    3830             :             else
    3831             :             {
    3832           1 :                 CPLDebug("TEST", "Compressor %s has no OPTIONS", *iter);
    3833             :             }
    3834             :         }
    3835             :     }
    3836           1 :     CSLDestroy(compressors);
    3837             : 
    3838           1 :     EXPECT_TRUE(CPLGetCompressor("invalid") == nullptr);
    3839           1 :     const auto pCompressor = CPLGetCompressor(sComp.pszId);
    3840           1 :     ASSERT_TRUE(pCompressor);
    3841           1 :     if (pCompressor == nullptr)
    3842           0 :         return;
    3843           1 :     EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
    3844           1 :     EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
    3845             :               CSLCount(sComp.papszMetadata));
    3846           1 :     EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
    3847           1 :     EXPECT_EQ(pCompressor->user_data, sComp.user_data);
    3848             : 
    3849           1 :     CPLDestroyCompressorRegistry();
    3850           1 :     EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
    3851             : }
    3852             : 
    3853             : // Test builtin compressors/decompressor
    3854           4 : TEST_F(test_cpl, builtin_compressors)
    3855             : {
    3856           7 :     for (const char *id : {"blosc", "zlib", "gzip", "lzma", "zstd", "lz4"})
    3857             :     {
    3858           6 :         const auto pCompressor = CPLGetCompressor(id);
    3859           6 :         if (pCompressor == nullptr)
    3860             :         {
    3861           0 :             CPLDebug("TEST", "%s not available", id);
    3862           0 :             if (strcmp(id, "zlib") == 0 || strcmp(id, "gzip") == 0)
    3863             :             {
    3864           0 :                 ASSERT_TRUE(false);
    3865             :             }
    3866           0 :             continue;
    3867             :         }
    3868           6 :         CPLDebug("TEST", "Testing %s", id);
    3869             : 
    3870           6 :         const char my_str[] = "my string to compress";
    3871           6 :         const char *const options[] = {"TYPESIZE=1", nullptr};
    3872             : 
    3873             :         // Compressor side
    3874             : 
    3875             :         // Just get output size
    3876           6 :         size_t out_size = 0;
    3877           6 :         ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), nullptr,
    3878             :                                          &out_size, options,
    3879             :                                          pCompressor->user_data));
    3880           6 :         ASSERT_TRUE(out_size != 0);
    3881             : 
    3882             :         // Let it alloc the output buffer
    3883           6 :         void *out_buffer2 = nullptr;
    3884           6 :         size_t out_size2 = 0;
    3885           6 :         ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), &out_buffer2,
    3886             :                                          &out_size2, options,
    3887             :                                          pCompressor->user_data));
    3888           6 :         ASSERT_TRUE(out_buffer2 != nullptr);
    3889           6 :         ASSERT_TRUE(out_size2 != 0);
    3890           6 :         ASSERT_TRUE(out_size2 <= out_size);
    3891             : 
    3892           6 :         std::vector<GByte> out_buffer3(out_size);
    3893             : 
    3894             :         // Provide not large enough buffer size
    3895           6 :         size_t out_size3 = 1;
    3896           6 :         void *out_buffer3_ptr = &out_buffer3[0];
    3897           6 :         ASSERT_TRUE(!(pCompressor->pfnFunc(my_str, strlen(my_str),
    3898             :                                            &out_buffer3_ptr, &out_size3,
    3899             :                                            options, pCompressor->user_data)));
    3900             : 
    3901             :         // Provide the output buffer
    3902           6 :         out_size3 = out_buffer3.size();
    3903           6 :         out_buffer3_ptr = &out_buffer3[0];
    3904           6 :         ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str),
    3905             :                                          &out_buffer3_ptr, &out_size3, options,
    3906             :                                          pCompressor->user_data));
    3907           6 :         ASSERT_TRUE(out_buffer3_ptr != nullptr);
    3908           6 :         ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
    3909           6 :         ASSERT_TRUE(out_size3 != 0);
    3910           6 :         ASSERT_EQ(out_size3, out_size2);
    3911             : 
    3912           6 :         out_buffer3.resize(out_size3);
    3913           6 :         out_buffer3_ptr = &out_buffer3[0];
    3914             : 
    3915           6 :         ASSERT_TRUE(memcmp(out_buffer3_ptr, out_buffer2, out_size2) == 0);
    3916             : 
    3917           6 :         CPLFree(out_buffer2);
    3918             : 
    3919           6 :         const std::vector<GByte> compressedData(out_buffer3);
    3920             : 
    3921             :         // Decompressor side
    3922           6 :         const auto pDecompressor = CPLGetDecompressor(id);
    3923           6 :         ASSERT_TRUE(pDecompressor != nullptr);
    3924             : 
    3925           6 :         out_size = 0;
    3926           6 :         ASSERT_TRUE(pDecompressor->pfnFunc(
    3927             :             compressedData.data(), compressedData.size(), nullptr, &out_size,
    3928             :             nullptr, pDecompressor->user_data));
    3929           6 :         ASSERT_TRUE(out_size != 0);
    3930           6 :         ASSERT_TRUE(out_size >= strlen(my_str));
    3931             : 
    3932           6 :         out_buffer2 = nullptr;
    3933           6 :         out_size2 = 0;
    3934           6 :         ASSERT_TRUE(pDecompressor->pfnFunc(
    3935             :             compressedData.data(), compressedData.size(), &out_buffer2,
    3936             :             &out_size2, options, pDecompressor->user_data));
    3937           6 :         ASSERT_TRUE(out_buffer2 != nullptr);
    3938           6 :         ASSERT_TRUE(out_size2 != 0);
    3939           6 :         ASSERT_EQ(out_size2, strlen(my_str));
    3940           6 :         ASSERT_TRUE(memcmp(out_buffer2, my_str, strlen(my_str)) == 0);
    3941           6 :         CPLFree(out_buffer2);
    3942             : 
    3943           6 :         out_buffer3.clear();
    3944           6 :         out_buffer3.resize(out_size);
    3945           6 :         out_size3 = out_buffer3.size();
    3946           6 :         out_buffer3_ptr = &out_buffer3[0];
    3947           6 :         ASSERT_TRUE(pDecompressor->pfnFunc(
    3948             :             compressedData.data(), compressedData.size(), &out_buffer3_ptr,
    3949             :             &out_size3, options, pDecompressor->user_data));
    3950           6 :         ASSERT_TRUE(out_buffer3_ptr != nullptr);
    3951           6 :         ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
    3952           6 :         ASSERT_EQ(out_size3, strlen(my_str));
    3953           6 :         ASSERT_TRUE(memcmp(out_buffer3.data(), my_str, strlen(my_str)) == 0);
    3954             :     }
    3955             : }
    3956             : 
    3957             : // Test builtin compressors/decompressor
    3958           4 : TEST_F(test_cpl, builtin_compressors_zlib_high_compression_rate)
    3959             : {
    3960           1 :     const auto pCompressor = CPLGetCompressor("zlib");
    3961           1 :     ASSERT_TRUE(pCompressor != nullptr);
    3962             : 
    3963           1 :     std::vector<GByte> abyInput(1024 * 1024, 0x01);
    3964             : 
    3965             :     // Compressor side
    3966             : 
    3967             :     // Let it alloc the output buffer
    3968           1 :     void *out_buffer = nullptr;
    3969           1 :     size_t out_size = 0;
    3970           1 :     ASSERT_TRUE(pCompressor->pfnFunc(abyInput.data(), abyInput.size(),
    3971             :                                      &out_buffer, &out_size, nullptr,
    3972             :                                      pCompressor->user_data));
    3973           1 :     ASSERT_TRUE(out_buffer != nullptr);
    3974           1 :     ASSERT_TRUE(out_size != 0);
    3975             : 
    3976             :     // Decompressor side
    3977           1 :     const auto pDecompressor = CPLGetDecompressor("zlib");
    3978           1 :     ASSERT_TRUE(pDecompressor != nullptr);
    3979             : 
    3980           1 :     void *out_buffer2 = nullptr;
    3981           1 :     size_t out_size2 = 0;
    3982           1 :     ASSERT_TRUE(pDecompressor->pfnFunc(out_buffer, out_size, &out_buffer2,
    3983             :                                        &out_size2, nullptr,
    3984             :                                        pDecompressor->user_data));
    3985           1 :     CPLFree(out_buffer);
    3986             : 
    3987           1 :     ASSERT_TRUE(out_buffer2 != nullptr);
    3988           1 :     ASSERT_TRUE(out_size2 != 0);
    3989           1 :     ASSERT_EQ(out_size2, abyInput.size());
    3990           1 :     ASSERT_TRUE(memcmp(out_buffer2, abyInput.data(), abyInput.size()) == 0);
    3991           1 :     CPLFree(out_buffer2);
    3992             : }
    3993             : 
    3994             : template <class T> struct TesterDelta
    3995             : {
    3996          24 :     static void test(const char *dtypeOption)
    3997             :     {
    3998          24 :         const auto pCompressor = CPLGetCompressor("delta");
    3999          24 :         ASSERT_TRUE(pCompressor);
    4000          24 :         if (pCompressor == nullptr)
    4001           0 :             return;
    4002          24 :         const auto pDecompressor = CPLGetDecompressor("delta");
    4003          24 :         ASSERT_TRUE(pDecompressor);
    4004          24 :         if (pDecompressor == nullptr)
    4005           0 :             return;
    4006             : 
    4007          24 :         const T tabIn[] = {static_cast<T>(-2), 3, 1};
    4008             :         T tabCompress[3];
    4009             :         T tabOut[3];
    4010          24 :         const char *const apszOptions[] = {dtypeOption, nullptr};
    4011             : 
    4012          24 :         void *outPtr = &tabCompress[0];
    4013          24 :         size_t outSize = sizeof(tabCompress);
    4014          24 :         ASSERT_TRUE(pCompressor->pfnFunc(&tabIn[0], sizeof(tabIn), &outPtr,
    4015             :                                          &outSize, apszOptions,
    4016             :                                          pCompressor->user_data));
    4017          24 :         ASSERT_EQ(outSize, sizeof(tabCompress));
    4018             : 
    4019             :         // ASSERT_EQ(tabCompress[0], 2);
    4020             :         // ASSERT_EQ(tabCompress[1], 1);
    4021             :         // ASSERT_EQ(tabCompress[2], -2);
    4022             : 
    4023          24 :         outPtr = &tabOut[0];
    4024          24 :         outSize = sizeof(tabOut);
    4025          24 :         ASSERT_TRUE(pDecompressor->pfnFunc(&tabCompress[0], sizeof(tabCompress),
    4026             :                                            &outPtr, &outSize, apszOptions,
    4027             :                                            pDecompressor->user_data));
    4028          24 :         ASSERT_EQ(outSize, sizeof(tabOut));
    4029          24 :         ASSERT_EQ(tabOut[0], tabIn[0]);
    4030          24 :         ASSERT_EQ(tabOut[1], tabIn[1]);
    4031          24 :         ASSERT_EQ(tabOut[2], tabIn[2]);
    4032             :     }
    4033             : };
    4034             : 
    4035             : // Test delta compressor/decompressor
    4036           4 : TEST_F(test_cpl, delta_compressor)
    4037             : {
    4038           1 :     TesterDelta<int8_t>::test("DTYPE=i1");
    4039             : 
    4040           1 :     TesterDelta<uint8_t>::test("DTYPE=u1");
    4041             : 
    4042           1 :     TesterDelta<int16_t>::test("DTYPE=i2");
    4043           1 :     TesterDelta<int16_t>::test("DTYPE=<i2");
    4044           1 :     TesterDelta<int16_t>::test("DTYPE=>i2");
    4045             : 
    4046           1 :     TesterDelta<uint16_t>::test("DTYPE=u2");
    4047           1 :     TesterDelta<uint16_t>::test("DTYPE=<u2");
    4048           1 :     TesterDelta<uint16_t>::test("DTYPE=>u2");
    4049             : 
    4050           1 :     TesterDelta<int32_t>::test("DTYPE=i4");
    4051           1 :     TesterDelta<int32_t>::test("DTYPE=<i4");
    4052           1 :     TesterDelta<int32_t>::test("DTYPE=>i4");
    4053             : 
    4054           1 :     TesterDelta<uint32_t>::test("DTYPE=u4");
    4055           1 :     TesterDelta<uint32_t>::test("DTYPE=<u4");
    4056           1 :     TesterDelta<uint32_t>::test("DTYPE=>u4");
    4057             : 
    4058           1 :     TesterDelta<int64_t>::test("DTYPE=i8");
    4059           1 :     TesterDelta<int64_t>::test("DTYPE=<i8");
    4060           1 :     TesterDelta<int64_t>::test("DTYPE=>i8");
    4061             : 
    4062           1 :     TesterDelta<uint64_t>::test("DTYPE=u8");
    4063           1 :     TesterDelta<uint64_t>::test("DTYPE=<u8");
    4064           1 :     TesterDelta<uint64_t>::test("DTYPE=>u8");
    4065             : 
    4066           1 :     TesterDelta<float>::test("DTYPE=f4");
    4067             : #ifdef CPL_MSB
    4068             :     TesterDelta<float>::test("DTYPE=>f4");
    4069             : #else
    4070           1 :     TesterDelta<float>::test("DTYPE=<f4");
    4071             : #endif
    4072             : 
    4073           1 :     TesterDelta<double>::test("DTYPE=f8");
    4074             : #ifdef CPL_MSB
    4075             :     TesterDelta<double>::test("DTYPE=>f8");
    4076             : #else
    4077           1 :     TesterDelta<double>::test("DTYPE=<f8");
    4078             : #endif
    4079           1 : }
    4080             : 
    4081             : // Test CPLQuadTree
    4082           4 : TEST_F(test_cpl, CPLQuadTree)
    4083             : {
    4084           1 :     unsigned next = 0;
    4085             : 
    4086           4 :     const auto DummyRandInit = [&next](unsigned initValue)
    4087           5 :     { next = initValue; };
    4088             : 
    4089           1 :     constexpr int MAX_RAND_VAL = 32767;
    4090             : 
    4091             :     // Slightly improved version of https://xkcd.com/221/, as suggested by
    4092             :     // "man srand"
    4093       16000 :     const auto DummyRand = [&]()
    4094             :     {
    4095       16000 :         next = next * 1103515245 + 12345;
    4096       16000 :         return ((unsigned)(next / 65536) % (MAX_RAND_VAL + 1));
    4097           1 :     };
    4098             : 
    4099             :     CPLRectObj globalbounds;
    4100           1 :     globalbounds.minx = 0;
    4101           1 :     globalbounds.miny = 0;
    4102           1 :     globalbounds.maxx = 1;
    4103           1 :     globalbounds.maxy = 1;
    4104             : 
    4105           1 :     auto hTree = CPLQuadTreeCreate(&globalbounds, nullptr);
    4106           1 :     ASSERT_TRUE(hTree != nullptr);
    4107             : 
    4108        4000 :     const auto GenerateRandomRect = [&](CPLRectObj &rect)
    4109             :     {
    4110        4000 :         rect.minx = double(DummyRand()) / MAX_RAND_VAL;
    4111        4000 :         rect.miny = double(DummyRand()) / MAX_RAND_VAL;
    4112        4000 :         rect.maxx =
    4113        4000 :             rect.minx + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.minx);
    4114        4000 :         rect.maxy =
    4115        4000 :             rect.miny + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.miny);
    4116        4001 :     };
    4117             : 
    4118           3 :     for (int j = 0; j < 2; j++)
    4119             :     {
    4120           2 :         DummyRandInit(j);
    4121        2002 :         for (int i = 0; i < 1000; i++)
    4122             :         {
    4123             :             CPLRectObj rect;
    4124        2000 :             GenerateRandomRect(rect);
    4125        2000 :             void *hFeature =
    4126        2000 :                 reinterpret_cast<void *>(static_cast<uintptr_t>(i));
    4127        2000 :             CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
    4128             :         }
    4129             : 
    4130             :         {
    4131           2 :             int nFeatureCount = 0;
    4132           2 :             CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
    4133           2 :             ASSERT_EQ(nFeatureCount, 1000);
    4134             :         }
    4135             : 
    4136           2 :         DummyRandInit(j);
    4137        2002 :         for (int i = 0; i < 1000; i++)
    4138             :         {
    4139             :             CPLRectObj rect;
    4140        2000 :             GenerateRandomRect(rect);
    4141        2000 :             void *hFeature =
    4142        2000 :                 reinterpret_cast<void *>(static_cast<uintptr_t>(i));
    4143        2000 :             CPLQuadTreeRemove(hTree, hFeature, &rect);
    4144             :         }
    4145             : 
    4146             :         {
    4147           2 :             int nFeatureCount = 0;
    4148           2 :             CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
    4149           2 :             ASSERT_EQ(nFeatureCount, 0);
    4150             :         }
    4151             :     }
    4152             : 
    4153           1 :     CPLQuadTreeDestroy(hTree);
    4154             : }
    4155             : 
    4156             : // Removing features must not degrade the tree structure: features inserted
    4157             : // after removals should still descend and split, not accumulate in the
    4158             : // bucket of a node whose subnodes were partially destroyed.
    4159           4 : TEST_F(test_cpl, CPLQuadTreeRemoveThenReinsert)
    4160             : {
    4161             :     CPLRectObj globalbounds;
    4162           1 :     globalbounds.minx = 0;
    4163           1 :     globalbounds.miny = 0;
    4164           1 :     globalbounds.maxx = 1;
    4165           1 :     globalbounds.maxy = 1;
    4166             : 
    4167           1 :     CPLQuadTree *hTree = CPLQuadTreeCreate(&globalbounds, nullptr);
    4168             : 
    4169             :     static constexpr int N = 32;
    4170        2048 :     const auto featRect = [](int i)
    4171             :     {
    4172             :         CPLRectObj rect;
    4173        2048 :         rect.minx = (0.25 + (i % N)) / N;
    4174        2048 :         rect.miny = (0.25 + (i / N)) / N;
    4175        2048 :         rect.maxx = rect.minx + 0.5 / N;
    4176        2048 :         rect.maxy = rect.miny + 0.5 / N;
    4177        2048 :         return rect;
    4178             :     };
    4179             :     // offset by 1 so no feature handle is nullptr
    4180        2048 :     const auto feat = [](int i)
    4181        2048 :     { return reinterpret_cast<void *>(static_cast<uintptr_t>(i + 1)); };
    4182             : 
    4183        1025 :     for (int i = 0; i < N * N; i++)
    4184             :     {
    4185        1024 :         CPLRectObj rect = featRect(i);
    4186        1024 :         CPLQuadTreeInsertWithBounds(hTree, feat(i), &rect);
    4187             :     }
    4188             : 
    4189             :     // Empty out the left half of the domain, then reinsert the same
    4190             :     // features.
    4191        1025 :     for (int i = 0; i < N * N; i++)
    4192             :     {
    4193        1024 :         if (i % N < N / 2)
    4194             :         {
    4195         512 :             CPLRectObj rect = featRect(i);
    4196         512 :             CPLQuadTreeRemove(hTree, feat(i), &rect);
    4197             :         }
    4198             :     }
    4199        1025 :     for (int i = 0; i < N * N; i++)
    4200             :     {
    4201        1024 :         if (i % N < N / 2)
    4202             :         {
    4203         512 :             CPLRectObj rect = featRect(i);
    4204         512 :             CPLQuadTreeInsertWithBounds(hTree, feat(i), &rect);
    4205             :         }
    4206             :     }
    4207             : 
    4208           1 :     int nFeatureCount = 0;
    4209           1 :     int nNodeCount = 0;
    4210           1 :     int nMaxDepth = 0;
    4211           1 :     int nMaxBucketCapacity = 0;
    4212           1 :     CPLQuadTreeGetStats(hTree, &nFeatureCount, &nNodeCount, &nMaxDepth,
    4213             :                         &nMaxBucketCapacity);
    4214           1 :     EXPECT_EQ(nFeatureCount, N * N);
    4215           1 :     EXPECT_LE(nMaxBucketCapacity, 16);
    4216             : 
    4217           1 :     int nSearchCount = 0;
    4218           1 :     CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nSearchCount));
    4219           1 :     EXPECT_EQ(nSearchCount, N * N);
    4220             : 
    4221           1 :     CPLQuadTreeDestroy(hTree);
    4222           1 : }
    4223             : 
    4224             : // Test bUnlinkAndSize on VSIGetMemFileBuffer
    4225           4 : TEST_F(test_cpl, VSIGetMemFileBuffer_unlink_and_size)
    4226             : {
    4227             :     VSIVirtualHandleUniquePtr fp(
    4228           1 :         VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "wb"));
    4229           1 :     VSIFWriteL("test", 5, 1, fp.get());
    4230             :     std::unique_ptr<GByte, VSIFreeReleaser> pRawData(VSIGetMemFileBuffer(
    4231           1 :         "/vsimem/test_unlink_and_seize.tif", nullptr, true));
    4232           1 :     ASSERT_TRUE(EQUAL(reinterpret_cast<const char *>(pRawData.get()), "test"));
    4233           1 :     ASSERT_TRUE(VSIGetMemFileBuffer("/vsimem/test_unlink_and_seize.tif",
    4234             :                                     nullptr, false) == nullptr);
    4235             :     VSIVirtualHandleUniquePtr fp2(
    4236           1 :         VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "r"));
    4237           1 :     ASSERT_TRUE(fp2.get() == nullptr);
    4238           1 :     ASSERT_TRUE(VSIFReadL(pRawData.get(), 5, 1, fp.get()) == 0);
    4239           1 :     ASSERT_TRUE(VSIFWriteL(pRawData.get(), 5, 1, fp.get()) == 0);
    4240           1 :     ASSERT_TRUE(VSIFSeekL(fp.get(), 0, SEEK_END) == 0);
    4241             : }
    4242             : 
    4243             : // Test CPLLoadConfigOptionsFromFile() for VSI credentials
    4244           4 : TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials)
    4245             : {
    4246           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
    4247           1 :     VSIFPrintfL(fp, "[credentials]\n");
    4248           1 :     VSIFPrintfL(fp, "\n");
    4249           1 :     VSIFPrintfL(fp, "[.my_subsection]\n");
    4250           1 :     VSIFPrintfL(fp, "path=/vsi_test/foo/bar\n");
    4251           1 :     VSIFPrintfL(fp, "FOO=BAR\n");
    4252           1 :     VSIFPrintfL(fp, "FOO2=BAR2\n");
    4253           1 :     VSIFPrintfL(fp, "\n");
    4254           1 :     VSIFPrintfL(fp, "[.my_subsection2]\n");
    4255           1 :     VSIFPrintfL(fp, "path=/vsi_test/bar/baz\n");
    4256           1 :     VSIFPrintfL(fp, "BAR=BAZ\n");
    4257           1 :     VSIFPrintfL(fp, "[configoptions]\n");
    4258           1 :     VSIFPrintfL(fp, "configoptions_FOO=BAR\n");
    4259           1 :     VSIFCloseL(fp);
    4260             : 
    4261           1 :     CPLErrorReset();
    4262           1 :     CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
    4263           1 :     ASSERT_EQ(CPLGetLastErrorType(), CE_None);
    4264             : 
    4265             :     {
    4266             :         const char *pszVal =
    4267           1 :             VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO", nullptr);
    4268           1 :         ASSERT_TRUE(pszVal != nullptr);
    4269           2 :         ASSERT_EQ(std::string(pszVal), std::string("BAR"));
    4270             :     }
    4271             : 
    4272             :     {
    4273             :         const char *pszVal =
    4274           1 :             VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO2", nullptr);
    4275           1 :         ASSERT_TRUE(pszVal != nullptr);
    4276           2 :         ASSERT_EQ(std::string(pszVal), std::string("BAR2"));
    4277             :     }
    4278             : 
    4279             :     {
    4280             :         const char *pszVal =
    4281           1 :             VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
    4282           1 :         ASSERT_TRUE(pszVal != nullptr);
    4283           2 :         ASSERT_EQ(std::string(pszVal), std::string("BAZ"));
    4284             :     }
    4285             : 
    4286             :     {
    4287           1 :         const char *pszVal = CPLGetConfigOption("configoptions_FOO", nullptr);
    4288           1 :         ASSERT_TRUE(pszVal != nullptr);
    4289           2 :         ASSERT_EQ(std::string(pszVal), std::string("BAR"));
    4290             :     }
    4291             : 
    4292           1 :     VSIClearPathSpecificOptions("/vsi_test/bar/baz");
    4293           1 :     CPLSetConfigOption("configoptions_FOO", nullptr);
    4294             : 
    4295             :     {
    4296             :         const char *pszVal =
    4297           1 :             VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
    4298           1 :         ASSERT_TRUE(pszVal == nullptr);
    4299             :     }
    4300             : 
    4301           1 :     VSIUnlink("/vsimem/credentials.txt");
    4302             : }
    4303             : 
    4304             : // Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
    4305           4 : TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials_warning)
    4306             : {
    4307           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
    4308           1 :     VSIFPrintfL(fp, "[credentials]\n");
    4309           1 :     VSIFPrintfL(fp, "\n");
    4310           1 :     VSIFPrintfL(fp, "FOO=BAR\n");  // content outside of subsection
    4311           1 :     VSIFCloseL(fp);
    4312             : 
    4313           1 :     CPLErrorReset();
    4314           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    4315           1 :     CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
    4316           1 :     CPLPopErrorHandler();
    4317           1 :     ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
    4318             : 
    4319           1 :     VSIUnlink("/vsimem/credentials.txt");
    4320             : }
    4321             : 
    4322             : // Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
    4323           4 : TEST_F(test_cpl,
    4324             :        CPLLoadConfigOptionsFromFile_VSI_credentials_subsection_warning)
    4325             : {
    4326           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
    4327           1 :     VSIFPrintfL(fp, "[credentials]\n");
    4328           1 :     VSIFPrintfL(fp, "[.subsection]\n");
    4329           1 :     VSIFPrintfL(fp, "FOO=BAR\n");  // first key is not 'path'
    4330           1 :     VSIFCloseL(fp);
    4331             : 
    4332           1 :     CPLErrorReset();
    4333           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    4334           1 :     CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
    4335           1 :     CPLPopErrorHandler();
    4336           1 :     ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
    4337             : 
    4338           1 :     VSIUnlink("/vsimem/credentials.txt");
    4339             : }
    4340             : 
    4341             : // Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
    4342           4 : TEST_F(test_cpl,
    4343             :        CPLLoadConfigOptionsFromFile_VSI_credentials_warning_path_specific)
    4344             : {
    4345           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
    4346           1 :     VSIFPrintfL(fp, "[credentials]\n");
    4347           1 :     VSIFPrintfL(fp, "[.subsection]\n");
    4348           1 :     VSIFPrintfL(fp, "path=/vsi_test/foo\n");
    4349           1 :     VSIFPrintfL(fp, "path=/vsi_test/bar\n");  // duplicated path
    4350           1 :     VSIFPrintfL(fp, "FOO=BAR\n");             // first key is not 'path'
    4351           1 :     VSIFPrintfL(fp, "[unrelated_section]");
    4352           1 :     VSIFPrintfL(fp, "BAR=BAZ\n");  // first key is not 'path'
    4353           1 :     VSIFCloseL(fp);
    4354             : 
    4355           1 :     CPLErrorReset();
    4356           1 :     CPLPushErrorHandler(CPLQuietErrorHandler);
    4357           1 :     CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
    4358           1 :     CPLPopErrorHandler();
    4359           1 :     ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
    4360             : 
    4361             :     {
    4362             :         const char *pszVal =
    4363           1 :             VSIGetPathSpecificOption("/vsi_test/foo", "FOO", nullptr);
    4364           1 :         ASSERT_TRUE(pszVal != nullptr);
    4365             :     }
    4366             : 
    4367             :     {
    4368             :         const char *pszVal =
    4369           1 :             VSIGetPathSpecificOption("/vsi_test/foo", "BAR", nullptr);
    4370           1 :         ASSERT_TRUE(pszVal == nullptr);
    4371             :     }
    4372             : 
    4373           1 :     VSIUnlink("/vsimem/credentials.txt");
    4374             : }
    4375             : 
    4376             : // Test CPLRecodeFromWCharIconv() with 2 bytes/char source encoding
    4377           4 : TEST_F(test_cpl, CPLRecodeFromWCharIconv_2byte_source_encoding)
    4378             : {
    4379             : #ifdef CPL_RECODE_ICONV
    4380           1 :     int N = 2048;
    4381             :     wchar_t *pszIn =
    4382           1 :         static_cast<wchar_t *>(CPLMalloc((N + 1) * sizeof(wchar_t)));
    4383        2049 :     for (int i = 0; i < N; i++)
    4384        2048 :         pszIn[i] = L'A';
    4385           1 :     pszIn[N] = L'\0';
    4386           1 :     char *pszExpected = static_cast<char *>(CPLMalloc(N + 1));
    4387        2049 :     for (int i = 0; i < N; i++)
    4388        2048 :         pszExpected[i] = 'A';
    4389           1 :     pszExpected[N] = '\0';
    4390           1 :     char *pszRet = CPLRecodeFromWChar(pszIn, CPL_ENC_UTF16, CPL_ENC_UTF8);
    4391           1 :     const bool bOK = memcmp(pszExpected, pszRet, N + 1) == 0;
    4392             :     // FIXME Some tests fail on Mac. Not sure why, but do not error out just for
    4393             :     // that
    4394           1 :     if (!bOK &&
    4395           0 :         (strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") != nullptr ||
    4396           0 :          strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
    4397           0 :          getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
    4398             :     {
    4399           0 :         fprintf(stderr, "Recode from CPL_ENC_UTF16 to CPL_ENC_UTF8 failed\n");
    4400             :     }
    4401             :     else
    4402             :     {
    4403           1 :         EXPECT_TRUE(bOK);
    4404             :     }
    4405           1 :     CPLFree(pszIn);
    4406           1 :     CPLFree(pszRet);
    4407           1 :     CPLFree(pszExpected);
    4408             : #else
    4409             :     GTEST_SKIP() << "iconv support missing";
    4410             : #endif
    4411           1 : }
    4412             : 
    4413             : // VERY MINIMAL testing of VSI plugin functionality
    4414           4 : TEST_F(test_cpl, VSI_plugin_minimal_testing)
    4415             : {
    4416           1 :     auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
    4417           3 :     psCallbacks->open = [](void *pUserData, const char *pszFilename,
    4418             :                            const char *pszAccess) -> void *
    4419             :     {
    4420             :         (void)pUserData;
    4421           2 :         if (strcmp(pszFilename, "test") == 0 && strcmp(pszAccess, "rb") == 0)
    4422           1 :             return const_cast<char *>("ok");
    4423           1 :         return nullptr;
    4424           1 :     };
    4425           1 :     EXPECT_EQ(VSIInstallPluginHandler("/vsimyplugin/", psCallbacks), 0);
    4426           1 :     VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
    4427           1 :     VSILFILE *fp = VSIFOpenL("/vsimyplugin/test", "rb");
    4428           1 :     EXPECT_TRUE(fp != nullptr);
    4429             : 
    4430             :     // Check it doesn't crash
    4431           1 :     vsi_l_offset nOffset = 5;
    4432           1 :     size_t nSize = 10;
    4433           1 :     reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
    4434             : 
    4435           1 :     VSIFCloseL(fp);
    4436           1 :     EXPECT_TRUE(VSIVirtualHandleUniquePtr(
    4437             :                     VSIFOpenL("/vsimyplugin/i_dont_exist", "rb")) == nullptr);
    4438             : 
    4439             :     // Check that we can remove the handler
    4440           1 :     VSIRemovePluginHandler("/vsimyplugin/");
    4441             : 
    4442           1 :     EXPECT_TRUE(VSIVirtualHandleUniquePtr(
    4443             :                     VSIFOpenL("/vsimyplugin/test", "rb")) == nullptr);
    4444           1 :     EXPECT_TRUE(VSIVirtualHandleUniquePtr(
    4445             :                     VSIFOpenL("/vsimyplugin/i_dont_exist", "rb")) == nullptr);
    4446             : 
    4447             :     // Removing a non-existing handler is a no-op
    4448           1 :     VSIRemovePluginHandler("/vsimyplugin/");
    4449           1 :     VSIRemovePluginHandler("/vsifoobar/");
    4450           1 : }
    4451             : 
    4452           4 : TEST_F(test_cpl, VSI_plugin_advise_read)
    4453             : {
    4454           1 :     auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
    4455             : 
    4456             :     struct UserData
    4457             :     {
    4458             :         int nRanges = 0;
    4459             :         const vsi_l_offset *panOffsets = nullptr;
    4460             :         const size_t *panSizes = nullptr;
    4461             :     };
    4462             : 
    4463           1 :     UserData userData;
    4464             : 
    4465           1 :     psCallbacks->pUserData = &userData;
    4466           2 :     psCallbacks->open = [](void *pUserData, const char * /*pszFilename*/,
    4467             :                            const char * /*pszAccess*/) -> void *
    4468           2 :     { return pUserData; };
    4469             : 
    4470           2 :     psCallbacks->advise_read = [](void *pFile, int nRanges,
    4471             :                                   const vsi_l_offset *panOffsets,
    4472             :                                   const size_t *panSizes)
    4473             :     {
    4474           1 :         static_cast<UserData *>(pFile)->nRanges = nRanges;
    4475           1 :         static_cast<UserData *>(pFile)->panOffsets = panOffsets;
    4476           1 :         static_cast<UserData *>(pFile)->panSizes = panSizes;
    4477           2 :     };
    4478           1 :     EXPECT_EQ(VSIInstallPluginHandler("/VSI_plugin_advise_read/", psCallbacks),
    4479             :               0);
    4480           1 :     VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
    4481           1 :     VSILFILE *fp = VSIFOpenL("/VSI_plugin_advise_read/test", "rb");
    4482           1 :     EXPECT_TRUE(fp != nullptr);
    4483             : 
    4484           1 :     vsi_l_offset nOffset = 5;
    4485           1 :     size_t nSize = 10;
    4486           1 :     reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
    4487           1 :     EXPECT_EQ(userData.nRanges, 1);
    4488           1 :     EXPECT_EQ(userData.panOffsets, &nOffset);
    4489           1 :     EXPECT_EQ(userData.panSizes, &nSize);
    4490             : 
    4491           1 :     VSIFCloseL(fp);
    4492           1 : }
    4493             : 
    4494             : // Test CPLIsASCII()
    4495           4 : TEST_F(test_cpl, CPLIsASCII)
    4496             : {
    4497           1 :     ASSERT_TRUE(CPLIsASCII("foo", 3));
    4498           1 :     ASSERT_TRUE(CPLIsASCII("foo", static_cast<size_t>(-1)));
    4499           1 :     ASSERT_TRUE(!CPLIsASCII("\xFF", 1));
    4500             : }
    4501             : 
    4502             : // Test VSIIsLocal()
    4503           4 : TEST_F(test_cpl, VSIIsLocal)
    4504             : {
    4505           1 :     ASSERT_TRUE(VSIIsLocal("/vsimem/"));
    4506           1 :     ASSERT_TRUE(VSIIsLocal("/vsigzip//vsimem/tmp.gz"));
    4507             : #ifdef HAVE_CURL
    4508           1 :     ASSERT_TRUE(!VSIIsLocal("/vsicurl/http://example.com"));
    4509             : #endif
    4510             :     VSIStatBufL sStat;
    4511             : #ifdef _WIN32
    4512             :     if (VSIStatL("c:\\", &sStat) == 0)
    4513             :     {
    4514             :         ASSERT_TRUE(VSIIsLocal("c:\\i_do_not_exist"));
    4515             :     }
    4516             : #else
    4517           1 :     if (VSIStatL("/tmp", &sStat) == 0)
    4518             :     {
    4519           1 :         ASSERT_TRUE(VSIIsLocal("/tmp/i_do_not_exist"));
    4520             :     }
    4521             : #endif
    4522             : }
    4523             : 
    4524             : // Test VSISupportsSequentialWrite()
    4525           4 : TEST_F(test_cpl, VSISupportsSequentialWrite)
    4526             : {
    4527           1 :     ASSERT_TRUE(VSISupportsSequentialWrite("/vsimem/", false));
    4528             : #ifdef HAVE_CURL
    4529           1 :     ASSERT_TRUE(
    4530             :         !VSISupportsSequentialWrite("/vsicurl/http://example.com", false));
    4531           1 :     ASSERT_TRUE(VSISupportsSequentialWrite("/vsis3/test_bucket/", false));
    4532             : #endif
    4533           1 :     ASSERT_TRUE(VSISupportsSequentialWrite("/vsigzip//vsimem/tmp.gz", false));
    4534             : #ifdef HAVE_CURL
    4535           1 :     ASSERT_TRUE(!VSISupportsSequentialWrite(
    4536             :         "/vsigzip//vsicurl/http://example.com/tmp.gz", false));
    4537             : #endif
    4538             :     VSIStatBufL sStat;
    4539             : #ifdef _WIN32
    4540             :     if (VSIStatL("c:\\", &sStat) == 0)
    4541             :     {
    4542             :         ASSERT_TRUE(VSISupportsSequentialWrite("c:\\", false));
    4543             :     }
    4544             : #else
    4545           1 :     if (VSIStatL("/tmp", &sStat) == 0)
    4546             :     {
    4547           1 :         ASSERT_TRUE(VSISupportsSequentialWrite("/tmp/i_do_not_exist", false));
    4548             :     }
    4549             : #endif
    4550             : }
    4551             : 
    4552             : // Test VSISupportsRandomWrite()
    4553           4 : TEST_F(test_cpl, VSISupportsRandomWrite)
    4554             : {
    4555           1 :     ASSERT_TRUE(VSISupportsRandomWrite("/vsimem/", false));
    4556             : #ifdef HAVE_CURL
    4557           1 :     ASSERT_TRUE(!VSISupportsRandomWrite("/vsicurl/http://example.com", false));
    4558           1 :     ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", false));
    4559           1 :     ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", true));
    4560           1 :     CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", "YES");
    4561           1 :     ASSERT_TRUE(VSISupportsRandomWrite("/vsis3/test_bucket/", true));
    4562           1 :     CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", nullptr);
    4563             : #endif
    4564           1 :     ASSERT_TRUE(!VSISupportsRandomWrite("/vsigzip//vsimem/tmp.gz", false));
    4565             : #ifdef HAVE_CURL
    4566           1 :     ASSERT_TRUE(!VSISupportsRandomWrite(
    4567             :         "/vsigzip//vsicurl/http://example.com/tmp.gz", false));
    4568             : #endif
    4569             :     VSIStatBufL sStat;
    4570             : #ifdef _WIN32
    4571             :     if (VSIStatL("c:\\", &sStat) == 0)
    4572             :     {
    4573             :         ASSERT_TRUE(VSISupportsRandomWrite("c:\\", false));
    4574             :     }
    4575             : #else
    4576           1 :     if (VSIStatL("/tmp", &sStat) == 0)
    4577             :     {
    4578           1 :         ASSERT_TRUE(VSISupportsRandomWrite("/tmp", false));
    4579             :     }
    4580             : #endif
    4581             : }
    4582             : 
    4583             : // Test ignore-env-vars = yes of configuration file
    4584           4 : TEST_F(test_cpl, config_file_ignore_env_vars)
    4585             : {
    4586           1 :     char szEnvVar[] = "SOME_ENV_VAR_FOR_TEST_CPL_61=FOO";
    4587           1 :     putenv(szEnvVar);
    4588           1 :     ASSERT_STREQ(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr),
    4589             :                  "FOO");
    4590             : 
    4591           1 :     VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
    4592           1 :     VSIFPrintfL(fp, "[directives]\n");
    4593           1 :     VSIFPrintfL(fp, "ignore-env-vars=yes\n");
    4594           1 :     VSIFPrintfL(fp, "[configoptions]\n");
    4595           1 :     VSIFPrintfL(fp, "CONFIG_OPTION_FOR_TEST_CPL_61=BAR\n");
    4596           1 :     VSIFCloseL(fp);
    4597             : 
    4598             :     // Load configuration file
    4599           1 :     constexpr bool bOverrideEnvVars = false;
    4600           1 :     CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", bOverrideEnvVars);
    4601             : 
    4602             :     // Check that reading configuration option works
    4603           1 :     ASSERT_STREQ(CPLGetConfigOption("CONFIG_OPTION_FOR_TEST_CPL_61", ""),
    4604             :                  "BAR");
    4605             : 
    4606             :     // Check that environment variables are not read as configuration options
    4607           1 :     ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) ==
    4608             :                 nullptr);
    4609             : 
    4610             :     // Reset ignore-env-vars=no
    4611           1 :     fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
    4612           1 :     VSIFPrintfL(fp, "[directives]\n");
    4613           1 :     VSIFPrintfL(fp, "ignore-env-vars=no\n");
    4614           1 :     VSIFPrintfL(fp, "[configoptions]\n");
    4615           1 :     VSIFPrintfL(fp, "SOME_ENV_VAR_FOR_TEST_CPL_61=BAR\n");
    4616           1 :     VSIFCloseL(fp);
    4617             : 
    4618             :     // Reload configuration file
    4619           1 :     CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
    4620             : 
    4621             :     // Check that environment variables override configuration options defined
    4622             :     // in the file (config file was loaded with bOverrideEnvVars = false)
    4623           1 :     ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) !=
    4624             :                 nullptr);
    4625           1 :     ASSERT_STREQ(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", ""), "FOO");
    4626             : 
    4627           1 :     VSIUnlink("/vsimem/.gdal/gdalrc");
    4628             : }
    4629             : 
    4630             : // Test that explicitly defined configuration options override environment variables
    4631             : // with the same name
    4632           4 : TEST_F(test_cpl, test_config_overrides_environment)
    4633             : {
    4634           1 :     char szEnvVar[] = "TEST_CONFIG_OVERRIDES_ENVIRONMENT=123";
    4635           1 :     putenv(szEnvVar);
    4636             : 
    4637           1 :     constexpr const char *key = "TEST_CONFIG_OVERRIDES_ENVIRONMENT";
    4638             : 
    4639           1 :     ASSERT_STREQ(CPLGetConfigOption(key, nullptr), "123");
    4640             : 
    4641           1 :     CPLSetConfigOption(key, "456");
    4642             : 
    4643           1 :     ASSERT_STREQ(CPLGetConfigOption(key, nullptr), "456");
    4644             : 
    4645           1 :     CPLSetConfigOption(key, nullptr);
    4646             : 
    4647           1 :     ASSERT_STREQ(CPLGetConfigOption(key, nullptr), "123");
    4648             : 
    4649           1 :     CPLSetConfigOption(key, CPL_NULL_VALUE);
    4650             : 
    4651           1 :     ASSERT_EQ(CPLGetConfigOption(key, nullptr), nullptr);
    4652             : 
    4653           1 :     CPLSetConfigOption(key, nullptr);
    4654             : 
    4655           1 :     ASSERT_STREQ(CPLGetConfigOption(key, nullptr), "123");
    4656             : 
    4657             :     {
    4658           1 :         CPLConfigOptionSetter oSetter(key, nullptr, false);
    4659             : 
    4660           1 :         ASSERT_EQ(CPLGetConfigOption(key, nullptr), nullptr);
    4661             :     }
    4662             : 
    4663           1 :     ASSERT_STREQ(CPLGetConfigOption(key, nullptr), "123");
    4664             : 
    4665           1 :     CPLSetThreadLocalConfigOption(key, CPL_NULL_VALUE);
    4666             : 
    4667           1 :     ASSERT_EQ(CPLGetConfigOption(key, nullptr), nullptr);
    4668             : 
    4669           1 :     ASSERT_EQ(CPLGetThreadLocalConfigOption(key, nullptr), nullptr);
    4670             : }
    4671             : 
    4672             : // Test CPLWorkerThreadPool recursion
    4673           4 : TEST_F(test_cpl, CPLWorkerThreadPool_recursion)
    4674             : {
    4675             :     struct Context
    4676             :     {
    4677             :         CPLWorkerThreadPool oThreadPool{};
    4678             :         std::atomic<int> nCounter{0};
    4679             :         std::mutex mutex{};
    4680             :         std::condition_variable cv{};
    4681             :         bool you_can_leave = false;
    4682             :         int threadStarted = 0;
    4683             : 
    4684           1 :         void notifyYouCanLeave()
    4685             :         {
    4686           2 :             std::lock_guard<std::mutex> guard(mutex);
    4687           1 :             you_can_leave = true;
    4688           1 :             cv.notify_one();
    4689           1 :         }
    4690             : 
    4691           1 :         void waitYouCanLeave()
    4692             :         {
    4693           2 :             std::unique_lock<std::mutex> guard(mutex);
    4694           1 :             while (!you_can_leave)
    4695             :             {
    4696           0 :                 cv.wait(guard);
    4697             :             }
    4698           1 :         }
    4699             :     };
    4700             : 
    4701           1 :     Context ctxt;
    4702           1 :     ctxt.oThreadPool.Setup(2, nullptr, nullptr, /* waitAllStarted = */ true);
    4703             : 
    4704             :     struct Data
    4705             :     {
    4706             :         Context *psCtxt;
    4707             :         int iJob;
    4708             :         GIntBig nThreadLambda = 0;
    4709             : 
    4710           3 :         Data(Context *psCtxtIn, int iJobIn) : psCtxt(psCtxtIn), iJob(iJobIn)
    4711             :         {
    4712           3 :         }
    4713             : 
    4714             :         Data(const Data &) = default;
    4715             :     };
    4716             : 
    4717           3 :     const auto lambda = [](void *pData)
    4718             :     {
    4719           3 :         auto psData = static_cast<Data *>(pData);
    4720           3 :         if (psData->iJob > 0)
    4721             :         {
    4722             :             // wait for both threads to be started
    4723           4 :             std::unique_lock<std::mutex> guard(psData->psCtxt->mutex);
    4724           2 :             psData->psCtxt->threadStarted++;
    4725           2 :             psData->psCtxt->cv.notify_one();
    4726           3 :             while (psData->psCtxt->threadStarted < 2)
    4727             :             {
    4728           1 :                 psData->psCtxt->cv.wait(guard);
    4729             :             }
    4730             :         }
    4731             : 
    4732           3 :         psData->nThreadLambda = CPLGetPID();
    4733             :         // fprintf(stderr, "lambda %d: " CPL_FRMT_GIB "\n",
    4734             :         //         psData->iJob, psData->nThreadLambda);
    4735           9 :         const auto lambda2 = [](void *pData2)
    4736             :         {
    4737           9 :             const auto psData2 = static_cast<Data *>(pData2);
    4738           9 :             const int iJob = psData2->iJob;
    4739           9 :             const int nCounter = psData2->psCtxt->nCounter++;
    4740           9 :             CPL_IGNORE_RET_VAL(nCounter);
    4741           9 :             const auto nThreadLambda2 = CPLGetPID();
    4742             :             // fprintf(stderr, "lambda2 job=%d, counter(before)=%d, thread="
    4743             :             // CPL_FRMT_GIB "\n", iJob, nCounter, nThreadLambda2);
    4744           9 :             if (iJob == 100 + 0)
    4745             :             {
    4746           1 :                 ASSERT_TRUE(nThreadLambda2 != psData2->nThreadLambda);
    4747             :                 // make sure that job 0 run in the other thread
    4748             :                 // takes sufficiently long that job 2 has been submitted
    4749             :                 // before it completes
    4750           1 :                 psData2->psCtxt->waitYouCanLeave();
    4751             :             }
    4752           8 :             else if (iJob == 100 + 1 || iJob == 100 + 2)
    4753             :             {
    4754           2 :                 ASSERT_TRUE(nThreadLambda2 == psData2->nThreadLambda);
    4755             :             }
    4756             :         };
    4757           6 :         auto poQueue = psData->psCtxt->oThreadPool.CreateJobQueue();
    4758           3 :         Data d0(*psData);
    4759           3 :         d0.iJob = 100 + d0.iJob * 3 + 0;
    4760           3 :         Data d1(*psData);
    4761           3 :         d1.iJob = 100 + d1.iJob * 3 + 1;
    4762           3 :         Data d2(*psData);
    4763           3 :         d2.iJob = 100 + d2.iJob * 3 + 2;
    4764           3 :         poQueue->SubmitJob(lambda2, &d0);
    4765           3 :         poQueue->SubmitJob(lambda2, &d1);
    4766           3 :         poQueue->SubmitJob(lambda2, &d2);
    4767           3 :         if (psData->iJob == 0)
    4768             :         {
    4769           1 :             psData->psCtxt->notifyYouCanLeave();
    4770             :         }
    4771           3 :     };
    4772             :     {
    4773           2 :         auto poQueue = ctxt.oThreadPool.CreateJobQueue();
    4774           1 :         Data data0(&ctxt, 0);
    4775           1 :         poQueue->SubmitJob(lambda, &data0);
    4776             :     }
    4777             :     {
    4778           2 :         auto poQueue = ctxt.oThreadPool.CreateJobQueue();
    4779           1 :         Data data1(&ctxt, 1);
    4780           1 :         Data data2(&ctxt, 2);
    4781           1 :         poQueue->SubmitJob(lambda, &data1);
    4782           1 :         poQueue->SubmitJob(lambda, &data2);
    4783             :     }
    4784           1 :     ASSERT_EQ(ctxt.nCounter, 3 * 3);
    4785             : }
    4786             : 
    4787             : // Test /vsimem/ PRead() implementation
    4788           4 : TEST_F(test_cpl, vsimem_pread)
    4789             : {
    4790           1 :     char szContent[] = "abcd";
    4791           1 :     VSILFILE *fp = VSIFileFromMemBuffer(
    4792             :         "", reinterpret_cast<GByte *>(szContent), 4, FALSE);
    4793           1 :     VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
    4794           1 :     ASSERT_TRUE(poHandle->HasPRead());
    4795             :     {
    4796           1 :         char szBuffer[5] = {0};
    4797           1 :         ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
    4798           2 :         ASSERT_EQ(std::string(szBuffer), std::string("bc"));
    4799             :     }
    4800             :     {
    4801           1 :         char szBuffer[5] = {0};
    4802           1 :         ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
    4803           2 :         ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
    4804             :     }
    4805             :     {
    4806           1 :         char szBuffer[5] = {0};
    4807           1 :         ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
    4808           2 :         ASSERT_EQ(std::string(szBuffer), std::string());
    4809             :     }
    4810           1 :     VSIFCloseL(fp);
    4811             : }
    4812             : 
    4813             : // Test regular file system PRead() implementation
    4814           4 : TEST_F(test_cpl, file_system_pread)
    4815             : {
    4816           1 :     VSILFILE *fp = VSIFOpenL("temp_test_64.bin", "wb+");
    4817           1 :     if (fp == nullptr)
    4818           0 :         return;
    4819           1 :     VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
    4820           1 :     poHandle->Write("abcd", 4, 1);
    4821           1 :     if (poHandle->HasPRead())
    4822             :     {
    4823           1 :         poHandle->Flush();
    4824             :         {
    4825           1 :             char szBuffer[5] = {0};
    4826           1 :             ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
    4827           2 :             ASSERT_EQ(std::string(szBuffer), std::string("bc"));
    4828             :         }
    4829             :         {
    4830           1 :             char szBuffer[5] = {0};
    4831           1 :             ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
    4832           2 :             ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
    4833             :         }
    4834             :         {
    4835           1 :             char szBuffer[5] = {0};
    4836           1 :             ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
    4837           2 :             ASSERT_EQ(std::string(szBuffer), std::string());
    4838             :         }
    4839             :     }
    4840           1 :     VSIFCloseL(fp);
    4841           1 :     VSIUnlink("temp_test_64.bin");
    4842             : }
    4843             : 
    4844             : // Test CPLMask implementation
    4845           4 : TEST_F(test_cpl, CPLMask)
    4846             : {
    4847           1 :     constexpr std::size_t sz = 71;
    4848           1 :     auto m = CPLMaskCreate(sz, true);
    4849             : 
    4850             :     // Mask is set by default
    4851          72 :     for (std::size_t i = 0; i < sz; i++)
    4852             :     {
    4853          71 :         EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
    4854             :     }
    4855             : 
    4856           1 :     VSIFree(m);
    4857           1 :     m = CPLMaskCreate(sz, false);
    4858           1 :     auto m2 = CPLMaskCreate(sz, false);
    4859             : 
    4860             :     // Mask is unset by default
    4861          72 :     for (std::size_t i = 0; i < sz; i++)
    4862             :     {
    4863          71 :         EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
    4864             :     }
    4865             : 
    4866             :     // Set a few bits
    4867           1 :     CPLMaskSet(m, 10);
    4868           1 :     CPLMaskSet(m, 33);
    4869           1 :     CPLMaskSet(m, 70);
    4870             : 
    4871             :     // Check all bits
    4872          72 :     for (std::size_t i = 0; i < sz; i++)
    4873             :     {
    4874          71 :         if (i == 10 || i == 33 || i == 70)
    4875             :         {
    4876           3 :             EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
    4877             :         }
    4878             :         else
    4879             :         {
    4880          68 :             EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
    4881             :         }
    4882             :     }
    4883             : 
    4884             :     // Unset some bits
    4885           1 :     CPLMaskClear(m, 10);
    4886           1 :     CPLMaskClear(m, 70);
    4887             : 
    4888             :     // Check all bits
    4889          72 :     for (std::size_t i = 0; i < sz; i++)
    4890             :     {
    4891          71 :         if (i == 33)
    4892             :         {
    4893           1 :             EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
    4894             :         }
    4895             :         else
    4896             :         {
    4897          70 :             EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
    4898             :         }
    4899             :     }
    4900             : 
    4901           1 :     CPLMaskSet(m2, 36);
    4902           1 :     CPLMaskMerge(m2, m, sz);
    4903             : 
    4904             :     // Check all bits
    4905          72 :     for (std::size_t i = 0; i < sz; i++)
    4906             :     {
    4907          71 :         if (i == 36 || i == 33)
    4908             :         {
    4909           4 :             ASSERT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
    4910             :         }
    4911             :         else
    4912             :         {
    4913          69 :             ASSERT_EQ(CPLMaskGet(m2, i), false) << "bit " << i;
    4914             :         }
    4915             :     }
    4916             : 
    4917           1 :     CPLMaskClearAll(m, sz);
    4918           1 :     CPLMaskSetAll(m2, sz);
    4919             : 
    4920             :     // Check all bits
    4921          72 :     for (std::size_t i = 0; i < sz; i++)
    4922             :     {
    4923          71 :         EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
    4924          71 :         EXPECT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
    4925             :     }
    4926             : 
    4927           1 :     VSIFree(m);
    4928           1 :     VSIFree(m2);
    4929             : }
    4930             : 
    4931             : // Test cpl::ThreadSafeQueue
    4932           4 : TEST_F(test_cpl, ThreadSafeQueue)
    4933             : {
    4934           1 :     cpl::ThreadSafeQueue<int> queue;
    4935           1 :     ASSERT_TRUE(queue.empty());
    4936           1 :     ASSERT_EQ(queue.size(), 0U);
    4937           1 :     queue.push(1);
    4938           1 :     ASSERT_TRUE(!queue.empty());
    4939           1 :     ASSERT_EQ(queue.size(), 1U);
    4940           1 :     queue.clear();
    4941           1 :     ASSERT_TRUE(queue.empty());
    4942           1 :     ASSERT_EQ(queue.size(), 0U);
    4943           1 :     int val = 10;
    4944           1 :     queue.push(std::move(val));
    4945           1 :     ASSERT_TRUE(!queue.empty());
    4946           1 :     ASSERT_EQ(queue.size(), 1U);
    4947           1 :     ASSERT_EQ(queue.get_and_pop_front(), 10);
    4948           1 :     ASSERT_TRUE(queue.empty());
    4949             : }
    4950             : 
    4951           4 : TEST_F(test_cpl, CPLGetExecPath)
    4952             : {
    4953           1 :     std::vector<char> achBuffer(1024, 'x');
    4954           1 :     if (!CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())))
    4955             :     {
    4956           0 :         GTEST_SKIP() << "CPLGetExecPath() not implemented for this platform";
    4957             :     }
    4958             :     else
    4959             :     {
    4960           1 :         bool bFoundNulTerminatedChar = false;
    4961          71 :         for (char ch : achBuffer)
    4962             :         {
    4963          71 :             if (ch == '\0')
    4964             :             {
    4965           1 :                 bFoundNulTerminatedChar = true;
    4966           1 :                 break;
    4967             :             }
    4968             :         }
    4969           1 :         ASSERT_TRUE(bFoundNulTerminatedChar);
    4970             : 
    4971             :         // Check that the file exists
    4972             :         VSIStatBufL sStat;
    4973           1 :         EXPECT_EQ(VSIStatL(achBuffer.data(), &sStat), 0);
    4974             : 
    4975           2 :         const std::string osStrBefore(achBuffer.data());
    4976             : 
    4977             :         // Resize the buffer to just the minimum size
    4978           1 :         achBuffer.resize(strlen(achBuffer.data()) + 1);
    4979           1 :         EXPECT_TRUE(CPLGetExecPath(achBuffer.data(),
    4980             :                                    static_cast<int>(achBuffer.size())));
    4981             : 
    4982           1 :         EXPECT_STREQ(osStrBefore.c_str(), achBuffer.data());
    4983             : 
    4984             :         // Too small buffer
    4985           1 :         achBuffer.resize(achBuffer.size() - 1);
    4986           1 :         EXPECT_FALSE(CPLGetExecPath(achBuffer.data(),
    4987             :                                     static_cast<int>(achBuffer.size())));
    4988             :     }
    4989             : }
    4990             : 
    4991           4 : TEST_F(test_cpl, VSIDuplicateFileSystemHandler)
    4992             : {
    4993             :     {
    4994           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    4995           1 :         EXPECT_FALSE(VSIDuplicateFileSystemHandler(
    4996             :             "/vsi_i_dont_exist/", "/vsi_i_will_not_be_created/"));
    4997             :     }
    4998             :     {
    4999           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    5000           1 :         EXPECT_FALSE(
    5001             :             VSIDuplicateFileSystemHandler("/", "/vsi_i_will_not_be_created/"));
    5002             :     }
    5003           1 :     EXPECT_EQ(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
    5004             :               VSIFileManager::GetHandler("/"));
    5005           1 :     EXPECT_TRUE(
    5006             :         VSIDuplicateFileSystemHandler("/vsimem/", "/vsi_test_clone_vsimem/"));
    5007           1 :     EXPECT_NE(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
    5008             :               VSIFileManager::GetHandler("/"));
    5009             :     {
    5010           2 :         CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
    5011           1 :         EXPECT_FALSE(VSIDuplicateFileSystemHandler("/vsimem/",
    5012             :                                                    "/vsi_test_clone_vsimem/"));
    5013             :     }
    5014           1 : }
    5015             : 
    5016           4 : TEST_F(test_cpl, CPLAtoGIntBigEx)
    5017             : {
    5018             :     {
    5019           1 :         int bOverflow = 0;
    5020           1 :         EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775807", false, &bOverflow),
    5021             :                   std::numeric_limits<int64_t>::max());
    5022           1 :         EXPECT_EQ(bOverflow, FALSE);
    5023             :     }
    5024             :     {
    5025           1 :         int bOverflow = 0;
    5026           1 :         EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775808", false, &bOverflow),
    5027             :                   std::numeric_limits<int64_t>::max());
    5028           1 :         EXPECT_EQ(bOverflow, TRUE);
    5029             :     }
    5030             :     {
    5031           1 :         int bOverflow = 0;
    5032           1 :         EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775808", false, &bOverflow),
    5033             :                   std::numeric_limits<int64_t>::min());
    5034           1 :         EXPECT_EQ(bOverflow, FALSE);
    5035             :     }
    5036             :     {
    5037           1 :         int bOverflow = 0;
    5038           1 :         EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775809", false, &bOverflow),
    5039             :                   std::numeric_limits<int64_t>::min());
    5040           1 :         EXPECT_EQ(bOverflow, TRUE);
    5041             :     }
    5042           1 : }
    5043             : 
    5044           4 : TEST_F(test_cpl, CPLSubscribeToSetConfigOption)
    5045             : {
    5046             :     struct Event
    5047             :     {
    5048             :         std::string osKey;
    5049             :         std::string osValue;
    5050             :         bool bThreadLocal;
    5051             :     };
    5052             : 
    5053           2 :     std::vector<Event> events;
    5054           4 :     const auto cbk = +[](const char *pszKey, const char *pszValue,
    5055             :                          bool bThreadLocal, void *pUserData)
    5056             :     {
    5057           3 :         std::vector<Event> *pEvents =
    5058             :             static_cast<std::vector<Event> *>(pUserData);
    5059           6 :         Event ev;
    5060           3 :         ev.osKey = pszKey;
    5061           3 :         ev.osValue = pszValue ? pszValue : "";
    5062           3 :         ev.bThreadLocal = bThreadLocal;
    5063           3 :         pEvents->emplace_back(ev);
    5064           3 :     };
    5065             : 
    5066             :     // Subscribe and unsubscribe immediately
    5067             :     {
    5068           1 :         int nId = CPLSubscribeToSetConfigOption(cbk, &events);
    5069           1 :         CPLSetConfigOption("CPLSubscribeToSetConfigOption", "bar");
    5070           1 :         EXPECT_EQ(events.size(), 1U);
    5071           1 :         if (!events.empty())
    5072             :         {
    5073           1 :             EXPECT_STREQ(events[0].osKey.c_str(),
    5074             :                          "CPLSubscribeToSetConfigOption");
    5075           1 :             EXPECT_STREQ(events[0].osValue.c_str(), "bar");
    5076           1 :             EXPECT_FALSE(events[0].bThreadLocal);
    5077             :         }
    5078           1 :         CPLUnsubscribeToSetConfigOption(nId);
    5079             :     }
    5080           1 :     events.clear();
    5081             : 
    5082             :     // Subscribe and unsubscribe in non-nested order
    5083             :     {
    5084           1 :         int nId1 = CPLSubscribeToSetConfigOption(cbk, &events);
    5085           1 :         int nId2 = CPLSubscribeToSetConfigOption(cbk, &events);
    5086           1 :         CPLUnsubscribeToSetConfigOption(nId1);
    5087           1 :         int nId3 = CPLSubscribeToSetConfigOption(cbk, &events);
    5088             : 
    5089           1 :         CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
    5090           1 :         EXPECT_EQ(events.size(), 2U);
    5091             : 
    5092           1 :         CPLUnsubscribeToSetConfigOption(nId2);
    5093           1 :         CPLUnsubscribeToSetConfigOption(nId3);
    5094             : 
    5095           1 :         CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
    5096           1 :         EXPECT_EQ(events.size(), 2U);
    5097             :     }
    5098           1 : }
    5099             : 
    5100           4 : TEST_F(test_cpl, VSIGetCanonicalFilename)
    5101             : {
    5102           2 :     std::string osTmp = CPLGenerateTempFilename(nullptr);
    5103           1 :     if (!CPLIsFilenameRelative(osTmp.c_str()))
    5104             :     {
    5105             :         // Get the canonical filename of the base temporary file
    5106             :         // to be able to test afterwards just the differences on the case
    5107             :         // of the extension
    5108           0 :         VSILFILE *fp = VSIFOpenL(osTmp.c_str(), "wb");
    5109           0 :         EXPECT_TRUE(fp != nullptr);
    5110           0 :         if (fp)
    5111             :         {
    5112           0 :             VSIFCloseL(fp);
    5113           0 :             char *pszRes = VSIGetCanonicalFilename(osTmp.c_str());
    5114           0 :             osTmp = pszRes;
    5115           0 :             CPLFree(pszRes);
    5116           0 :             VSIUnlink(osTmp.c_str());
    5117             :         }
    5118             :     }
    5119             : 
    5120           2 :     std::string osLC = osTmp + ".tmp";
    5121           2 :     std::string osUC = osTmp + ".TMP";
    5122             :     // Create a file in lower case
    5123           1 :     VSILFILE *fp = VSIFOpenL(osLC.c_str(), "wb");
    5124           1 :     EXPECT_TRUE(fp != nullptr);
    5125           1 :     if (fp)
    5126             :     {
    5127           1 :         VSIFCloseL(fp);
    5128             :         VSIStatBufL sStat;
    5129             :         // And try to stat it in upper case
    5130           1 :         if (VSIStatL(osUC.c_str(), &sStat) == 0)
    5131             :         {
    5132           0 :             char *pszRes = VSIGetCanonicalFilename(osUC.c_str());
    5133           0 :             EXPECT_TRUE(pszRes);
    5134           0 :             if (pszRes)
    5135             :             {
    5136             : #if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__))
    5137             :                 // On Windows or Mac, we should get the real canonical name,
    5138             :                 // i.e. in lower case
    5139             :                 EXPECT_STREQ(pszRes, osLC.c_str());
    5140             : #else
    5141             :                 // On other operating systems, VSIGetCanonicalFilename()
    5142             :                 // could not be implemented, so be laxer in the check
    5143           0 :                 EXPECT_STREQ(CPLString(pszRes).tolower().c_str(),
    5144             :                              CPLString(osLC).tolower().c_str());
    5145             : #endif
    5146             :             }
    5147           0 :             CPLFree(pszRes);
    5148             :         }
    5149             : 
    5150             :         {
    5151           1 :             char *pszRes = VSIGetCanonicalFilename(osLC.c_str());
    5152           1 :             EXPECT_TRUE(pszRes);
    5153           1 :             if (pszRes)
    5154             :             {
    5155           1 :                 EXPECT_STREQ(pszRes, osLC.c_str());
    5156             :             }
    5157           1 :             CPLFree(pszRes);
    5158             :         }
    5159             :     }
    5160           1 :     VSIUnlink(osLC.c_str());
    5161           1 : }
    5162             : 
    5163           4 : TEST_F(test_cpl, CPLStrtod)
    5164             : {
    5165             :     {
    5166           1 :         const char *pszVal = "5";
    5167           1 :         char *pszEnd = nullptr;
    5168           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 5.0);
    5169           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5170             :     }
    5171             : 
    5172             :     {
    5173           1 :         const char *pszVal = "-5 foo";
    5174           1 :         char *pszEnd = nullptr;
    5175           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), -5.0);
    5176           1 :         EXPECT_EQ(pszEnd, pszVal + 2);
    5177             :     }
    5178             : 
    5179             :     {
    5180           1 :         const char *pszVal = "3.1415";
    5181           1 :         char *pszEnd = nullptr;
    5182           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 3.1415);
    5183           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5184             :     }
    5185             : 
    5186             :     {
    5187           1 :         const char *pszVal = "6.022e23";
    5188           1 :         char *pszEnd = nullptr;
    5189           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 6.022e23);
    5190           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5191             :     }
    5192             : 
    5193             :     {
    5194           1 :         const char *pszVal = "  +6.022E+23";
    5195           1 :         char *pszEnd = nullptr;
    5196           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 6.022e23);
    5197           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5198             :     }
    5199             :     {
    5200           1 :         const char *pszVal = "1.e-23";
    5201           1 :         char *pszEnd = nullptr;
    5202           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 1.0e-23);
    5203           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5204             :     }
    5205             : 
    5206             :     {
    5207           1 :         const char *pszVal = "foo";
    5208           1 :         char *pszEnd = nullptr;
    5209           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 0.0);
    5210           1 :         EXPECT_EQ(pszEnd, pszVal);
    5211             :     }
    5212             : 
    5213             :     {
    5214           1 :         const char *pszVal = "-inf";
    5215           1 :         char *pszEnd = nullptr;
    5216           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5217             :                   -std::numeric_limits<double>::infinity());
    5218           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5219             :     }
    5220             :     {
    5221           1 :         const char *pszVal = "-Inf";
    5222           1 :         char *pszEnd = nullptr;
    5223           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5224             :                   -std::numeric_limits<double>::infinity());
    5225           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5226             :     }
    5227             :     {
    5228           1 :         const char *pszVal = "-INF";
    5229           1 :         char *pszEnd = nullptr;
    5230           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5231             :                   -std::numeric_limits<double>::infinity());
    5232           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5233             :     }
    5234             :     {
    5235           1 :         const char *pszVal = "-Infinity";
    5236           1 :         char *pszEnd = nullptr;
    5237           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5238             :                   -std::numeric_limits<double>::infinity());
    5239           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5240             :     }
    5241             :     {
    5242           1 :         const char *pszVal = "-1.#INF";
    5243           1 :         char *pszEnd = nullptr;
    5244           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5245             :                   -std::numeric_limits<double>::infinity());
    5246           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5247             :     }
    5248             : 
    5249             :     {
    5250           1 :         const char *pszVal = "inf";
    5251           1 :         char *pszEnd = nullptr;
    5252           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5253             :                   std::numeric_limits<double>::infinity());
    5254           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5255             :     }
    5256             :     {
    5257           1 :         const char *pszVal = "Inf";
    5258           1 :         char *pszEnd = nullptr;
    5259           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5260             :                   std::numeric_limits<double>::infinity());
    5261           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5262             :     }
    5263             :     {
    5264           1 :         const char *pszVal = "INF";
    5265           1 :         char *pszEnd = nullptr;
    5266           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5267             :                   std::numeric_limits<double>::infinity());
    5268           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5269             :     }
    5270             :     {
    5271           1 :         const char *pszVal = "Infinity";
    5272           1 :         char *pszEnd = nullptr;
    5273           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5274             :                   std::numeric_limits<double>::infinity());
    5275           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5276             :     }
    5277             :     {
    5278           1 :         const char *pszVal = "1.#INF";
    5279           1 :         char *pszEnd = nullptr;
    5280           1 :         EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
    5281             :                   std::numeric_limits<double>::infinity());
    5282           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5283             :     }
    5284             : 
    5285             :     {
    5286           1 :         const char *pszVal = "-1.#QNAN";
    5287           1 :         char *pszEnd = nullptr;
    5288           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5289           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5290             :     }
    5291             :     {
    5292           1 :         const char *pszVal = "-1.#IND";
    5293           1 :         char *pszEnd = nullptr;
    5294           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5295           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5296             :     }
    5297             :     {
    5298           1 :         const char *pszVal = "1.#QNAN";
    5299           1 :         char *pszEnd = nullptr;
    5300           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5301           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5302             :     }
    5303             :     {
    5304           1 :         const char *pszVal = "1.#SNAN";
    5305           1 :         char *pszEnd = nullptr;
    5306           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5307           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5308             :     }
    5309             :     {
    5310           1 :         const char *pszVal = "NaN";
    5311           1 :         char *pszEnd = nullptr;
    5312           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5313           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5314             :     }
    5315             :     {
    5316           1 :         const char *pszVal = "nan";
    5317           1 :         char *pszEnd = nullptr;
    5318           1 :         EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
    5319           1 :         EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
    5320             :     }
    5321           1 : }
    5322             : 
    5323           4 : TEST_F(test_cpl, CPLStringToComplex)
    5324             : {
    5325           5 :     const auto EXPECT_PARSED = [](const char *str, double real, double imag)
    5326             :     {
    5327           5 :         double r = -999;
    5328           5 :         double i = -999;
    5329           5 :         EXPECT_EQ(CPLStringToComplex(str, &r, &i), CE_None)
    5330           0 :             << "Unexpected error parsing " << str;
    5331           5 :         EXPECT_EQ(r, real);
    5332           5 :         EXPECT_EQ(i, imag);
    5333           5 :     };
    5334          10 :     const auto EXPECT_ERROR = [](const char *str)
    5335             :     {
    5336          20 :         CPLErrorStateBackuper state(CPLQuietErrorHandler);
    5337             :         double r, i;
    5338          10 :         EXPECT_EQ(CPLStringToComplex(str, &r, &i), CE_Failure)
    5339           0 :             << "Did not receive expected error parsing " << str;
    5340          10 :     };
    5341             : 
    5342           1 :     EXPECT_PARSED("05401", 5401, 0);
    5343           1 :     EXPECT_PARSED("-5401", -5401, 0);
    5344           1 :     EXPECT_PARSED(" 611.2-38.4i", 611.2, -38.4);
    5345           1 :     EXPECT_PARSED("611.2+38.4i ", 611.2, 38.4);
    5346           1 :     EXPECT_PARSED("-611.2+38.4i", -611.2, 38.4);
    5347             : 
    5348           1 :     EXPECT_ERROR("-611.2+38.4ii");
    5349           1 :     EXPECT_ERROR("-611.2+38.4ji");
    5350           1 :     EXPECT_ERROR("-611.2+38.4ij");
    5351           1 :     EXPECT_ERROR("f-611.2+38.4i");
    5352           1 :     EXPECT_ERROR("-611.2+f38.4i");
    5353           1 :     EXPECT_ERROR("-611.2+-38.4i");
    5354           1 :     EXPECT_ERROR("611.2+38.4");
    5355           1 :     EXPECT_ERROR("38.4i");
    5356           1 :     EXPECT_ERROR("38.4x");
    5357           1 :     EXPECT_ERROR("invalid");
    5358           1 : }
    5359             : 
    5360           4 : TEST_F(test_cpl, CPLForceToASCII)
    5361             : {
    5362             :     {
    5363           1 :         char *pszOut = CPLForceToASCII("foo", -1, '_');
    5364           1 :         EXPECT_STREQ(pszOut, "foo");
    5365           1 :         CPLFree(pszOut);
    5366             :     }
    5367             :     {
    5368           1 :         char *pszOut = CPLForceToASCII("foo", 1, '_');
    5369           1 :         EXPECT_STREQ(pszOut, "f");
    5370           1 :         CPLFree(pszOut);
    5371             :     }
    5372             :     {
    5373           1 :         char *pszOut = CPLForceToASCII("foo\xFF", -1, '_');
    5374           1 :         EXPECT_STREQ(pszOut, "foo_");
    5375           1 :         CPLFree(pszOut);
    5376             :     }
    5377           1 : }
    5378             : 
    5379           4 : TEST_F(test_cpl, CPLUTF8ForceToASCII)
    5380             : {
    5381             :     {
    5382           1 :         char *pszOut = CPLUTF8ForceToASCII("foo", '_');
    5383           1 :         EXPECT_STREQ(pszOut, "foo");
    5384           1 :         CPLFree(pszOut);
    5385             :     }
    5386             :     {
    5387             :         // Truncated UTF-8 character
    5388           1 :         char *pszOut = CPLUTF8ForceToASCII("foo\xC0", '_');
    5389           1 :         EXPECT_STREQ(pszOut, "foo");
    5390           1 :         CPLFree(pszOut);
    5391             :     }
    5392             :     {
    5393           1 :         char *pszOut = CPLUTF8ForceToASCII("foo\xc2\x80", '_');
    5394           1 :         EXPECT_STREQ(pszOut, "foo_");
    5395           1 :         CPLFree(pszOut);
    5396             :     }
    5397             :     {
    5398           1 :         char *pszOut = CPLUTF8ForceToASCII("foo\xc2\x80x", '_');
    5399           1 :         EXPECT_STREQ(pszOut, "foo_x");
    5400           1 :         CPLFree(pszOut);
    5401             :     }
    5402             :     {
    5403           1 :         std::string s;
    5404             :         {
    5405             :             VSILFILE *f =
    5406           1 :                 VSIFOpenL((data_ + SEP + "utf8accents.txt").c_str(), "rb");
    5407           1 :             ASSERT_NE(f, nullptr);
    5408           1 :             VSIFSeekL(f, 0, SEEK_END);
    5409           1 :             s.resize(static_cast<size_t>(VSIFTellL(f)));
    5410           1 :             VSIFSeekL(f, 0, SEEK_SET);
    5411           1 :             VSIFReadL(&s[0], 1, s.size(), f);
    5412           1 :             VSIFCloseL(f);
    5413           2 :             while (!s.empty() && s.back() == '\n')
    5414           1 :                 s.pop_back();
    5415             :         }
    5416           1 :         std::string sRef;
    5417             :         {
    5418           1 :             VSILFILE *f = VSIFOpenL(
    5419           2 :                 (data_ + SEP + "utf8accents_ascii.txt").c_str(), "rb");
    5420           1 :             ASSERT_NE(f, nullptr);
    5421           1 :             VSIFSeekL(f, 0, SEEK_END);
    5422           1 :             sRef.resize(static_cast<size_t>(VSIFTellL(f)));
    5423           1 :             VSIFSeekL(f, 0, SEEK_SET);
    5424           1 :             VSIFReadL(&sRef[0], 1, sRef.size(), f);
    5425           1 :             VSIFCloseL(f);
    5426           2 :             while (!sRef.empty() && sRef.back() == '\n')
    5427           1 :                 sRef.pop_back();
    5428             :         }
    5429           1 :         char *pszOut = CPLUTF8ForceToASCII(s.c_str(), '_');
    5430           1 :         EXPECT_STREQ(pszOut, sRef.c_str());
    5431           1 :         CPLFree(pszOut);
    5432             :     }
    5433             : }
    5434             : 
    5435             : #ifndef _WIN32
    5436           4 : TEST_F(test_cpl, CPLSpawn)
    5437             : {
    5438             :     VSIStatBufL sStatBuf;
    5439           1 :     if (VSIStatL("/bin/true", &sStatBuf) == 0)
    5440             :     {
    5441           1 :         const char *const apszArgs[] = {"/bin/true", nullptr};
    5442           1 :         EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), 0);
    5443             :     }
    5444           1 :     if (VSIStatL("/bin/false", &sStatBuf) == 0)
    5445             :     {
    5446           1 :         const char *const apszArgs[] = {"/bin/false", nullptr};
    5447           1 :         EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), 1);
    5448             :     }
    5449             : 
    5450             :     {
    5451           1 :         const char *const apszArgs[] = {"/i_do/not/exist", nullptr};
    5452           1 :         CPLPushErrorHandler(CPLQuietErrorHandler);
    5453           1 :         EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), -1);
    5454           1 :         CPLPopErrorHandler();
    5455             :     }
    5456           1 : }
    5457             : #endif
    5458             : 
    5459           2 : static bool ENDS_WITH(const char *pszStr, const char *pszEnd)
    5460             : {
    5461           4 :     return strlen(pszStr) >= strlen(pszEnd) &&
    5462           4 :            strcmp(pszStr + strlen(pszStr) - strlen(pszEnd), pszEnd) == 0;
    5463             : }
    5464             : 
    5465           4 : TEST_F(test_cpl, VSIMemGenerateHiddenFilename)
    5466             : {
    5467             :     {
    5468             :         // Initial cleanup
    5469           1 :         VSIRmdirRecursive("/vsimem/");
    5470           1 :         VSIRmdirRecursive("/vsimem/.#!HIDDEN!#.");
    5471             : 
    5472             :         // Generate unlisted filename
    5473           2 :         const std::string osFilename1 = VSIMemGenerateHiddenFilename(nullptr);
    5474           1 :         const char *pszFilename1 = osFilename1.c_str();
    5475           1 :         EXPECT_TRUE(STARTS_WITH(pszFilename1, "/vsimem/.#!HIDDEN!#./"));
    5476           1 :         EXPECT_TRUE(ENDS_WITH(pszFilename1, "/unnamed"));
    5477             : 
    5478             :         {
    5479             :             // Check the file doesn't exist yet
    5480             :             VSIStatBufL sStat;
    5481           1 :             EXPECT_EQ(VSIStatL(pszFilename1, &sStat), -1);
    5482             :         }
    5483             : 
    5484             :         // Create the file with some content
    5485           1 :         GByte abyDummyData[1] = {0};
    5486           1 :         VSIFCloseL(VSIFileFromMemBuffer(pszFilename1, abyDummyData,
    5487             :                                         sizeof(abyDummyData), false));
    5488             : 
    5489             :         {
    5490             :             // Check the file exists now
    5491             :             VSIStatBufL sStat;
    5492           1 :             EXPECT_EQ(VSIStatL(pszFilename1, &sStat), 0);
    5493             :         }
    5494             : 
    5495             :         // Gets back content
    5496           1 :         EXPECT_EQ(VSIGetMemFileBuffer(pszFilename1, nullptr, false),
    5497             :                   abyDummyData);
    5498             : 
    5499             :         {
    5500             :             // Check the hidden file doesn't popup
    5501           2 :             const CPLStringList aosFiles(VSIReadDir("/vsimem/"));
    5502           1 :             EXPECT_EQ(aosFiles.size(), 0);
    5503             :         }
    5504             : 
    5505             :         {
    5506             :             // Check that we can list the below directory if we know it exists
    5507             :             // and there's just one subdir
    5508           2 :             const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
    5509           1 :             EXPECT_EQ(aosFiles.size(), 1);
    5510             :         }
    5511             : 
    5512             :         {
    5513             :             // but that it is not an explicit directory
    5514             :             VSIStatBufL sStat;
    5515           1 :             EXPECT_EQ(VSIStatL("/vsimem/.#!HIDDEN!#.", &sStat), -1);
    5516             :         }
    5517             : 
    5518             :         // Creates second file
    5519           2 :         const std::string osFilename2 = VSIMemGenerateHiddenFilename(nullptr);
    5520           1 :         const char *pszFilename2 = osFilename2.c_str();
    5521           1 :         EXPECT_TRUE(strcmp(pszFilename1, pszFilename2) != 0);
    5522             : 
    5523             :         // Create it
    5524           1 :         VSIFCloseL(VSIFileFromMemBuffer(pszFilename2, abyDummyData,
    5525             :                                         sizeof(abyDummyData), false));
    5526             : 
    5527             :         {
    5528             :             // Check that we can list the root hidden dir if we know it exists
    5529           2 :             const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
    5530           1 :             EXPECT_EQ(aosFiles.size(), 2);
    5531             :         }
    5532             : 
    5533             :         {
    5534             :             // Create an explicit subdirectory in a hidden directory
    5535             :             const std::string osBaseName =
    5536           2 :                 VSIMemGenerateHiddenFilename(nullptr);
    5537             :             const std::string osSubDir =
    5538           2 :                 CPLFormFilename(osBaseName.c_str(), "mysubdir", nullptr);
    5539           1 :             EXPECT_EQ(VSIMkdir(osSubDir.c_str(), 0), 0);
    5540             : 
    5541             :             // Check the subdirectory exists
    5542             :             {
    5543             :                 VSIStatBufL sStat;
    5544           1 :                 EXPECT_EQ(VSIStatL(osSubDir.c_str(), &sStat), 0);
    5545             :             }
    5546             : 
    5547             :             // but not its hidden parent
    5548             :             {
    5549             :                 VSIStatBufL sStat;
    5550           1 :                 EXPECT_EQ(VSIStatL(osBaseName.c_str(), &sStat), -1);
    5551             :             }
    5552             : 
    5553             :             // Create file within the subdirectory
    5554           1 :             VSIFCloseL(VSIFileFromMemBuffer(
    5555             :                 CPLFormFilename(osSubDir.c_str(), "my.bin", nullptr),
    5556             :                 abyDummyData, sizeof(abyDummyData), false));
    5557             : 
    5558             :             {
    5559             :                 // Check that we can list the subdirectory
    5560           2 :                 const CPLStringList aosFiles(VSIReadDir(osSubDir.c_str()));
    5561           1 :                 EXPECT_EQ(aosFiles.size(), 1);
    5562             :             }
    5563             : 
    5564             :             {
    5565             :                 // Check that we can list the root hidden dir if we know it exists
    5566             :                 const CPLStringList aosFiles(
    5567           2 :                     VSIReadDir("/vsimem/.#!HIDDEN!#."));
    5568           1 :                 EXPECT_EQ(aosFiles.size(), 3);
    5569             :             }
    5570             :         }
    5571             : 
    5572             :         // Directly create a directory with the return of VSIMemGenerateHiddenFilename()
    5573             :         {
    5574           2 :             const std::string osDirname = VSIMemGenerateHiddenFilename(nullptr);
    5575           1 :             EXPECT_EQ(VSIMkdir(osDirname.c_str(), 0), 0);
    5576             : 
    5577             :             // Check the subdirectory exists
    5578             :             {
    5579             :                 VSIStatBufL sStat;
    5580           1 :                 EXPECT_EQ(VSIStatL(osDirname.c_str(), &sStat), 0);
    5581             :             }
    5582             : 
    5583             :             // Create file within the subdirectory
    5584           1 :             VSIFCloseL(VSIFileFromMemBuffer(
    5585             :                 CPLFormFilename(osDirname.c_str(), "my.bin", nullptr),
    5586             :                 abyDummyData, sizeof(abyDummyData), false));
    5587             : 
    5588             :             {
    5589             :                 // Check there's a file in this subdirectory
    5590           2 :                 const CPLStringList aosFiles(VSIReadDir(osDirname.c_str()));
    5591           1 :                 EXPECT_EQ(aosFiles.size(), 1);
    5592             :             }
    5593             : 
    5594           1 :             EXPECT_EQ(VSIRmdirRecursive(osDirname.c_str()), 0);
    5595             : 
    5596             :             {
    5597             :                 // Check there's no longer any file in this subdirectory
    5598           2 :                 const CPLStringList aosFiles(VSIReadDir(osDirname.c_str()));
    5599           1 :                 EXPECT_EQ(aosFiles.size(), 0);
    5600             :             }
    5601             : 
    5602             :             {
    5603             :                 // Check that it no longer exists
    5604             :                 VSIStatBufL sStat;
    5605           1 :                 EXPECT_EQ(VSIStatL(osDirname.c_str(), &sStat), -1);
    5606             :             }
    5607             :         }
    5608             : 
    5609             :         // Check that operations on "/vsimem/" do not interfere with hidden files
    5610             :         {
    5611             :             // Create regular file
    5612           1 :             VSIFCloseL(VSIFileFromMemBuffer("/vsimem/regular_file",
    5613             :                                             abyDummyData, sizeof(abyDummyData),
    5614             :                                             false));
    5615             : 
    5616             :             // Check it is visible
    5617           1 :             EXPECT_EQ(CPLStringList(VSIReadDir("/vsimem/")).size(), 1);
    5618             : 
    5619             :             // Clean root /vsimem/
    5620           1 :             VSIRmdirRecursive("/vsimem/");
    5621             : 
    5622             :             // No more user files
    5623           1 :             EXPECT_TRUE(CPLStringList(VSIReadDir("/vsimem/")).empty());
    5624             : 
    5625             :             // But still hidden files
    5626           1 :             EXPECT_TRUE(
    5627             :                 !CPLStringList(VSIReadDir("/vsimem/.#!HIDDEN!#.")).empty());
    5628             :         }
    5629             : 
    5630             :         // Clean-up hidden files
    5631           1 :         EXPECT_EQ(VSIRmdirRecursive("/vsimem/.#!HIDDEN!#."), 0);
    5632             : 
    5633             :         {
    5634             :             // Check the root hidden dir is empty
    5635           2 :             const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
    5636           1 :             EXPECT_TRUE(aosFiles.empty());
    5637             :         }
    5638             : 
    5639           1 :         EXPECT_EQ(VSIRmdirRecursive("/vsimem/.#!HIDDEN!#."), 0);
    5640             :     }
    5641             : 
    5642             :     {
    5643           2 :         const std::string osFilename = VSIMemGenerateHiddenFilename("foo.bar");
    5644           1 :         const char *pszFilename = osFilename.c_str();
    5645           1 :         EXPECT_TRUE(STARTS_WITH(pszFilename, "/vsimem/.#!HIDDEN!#./"));
    5646           1 :         EXPECT_TRUE(ENDS_WITH(pszFilename, "/foo.bar"));
    5647             :     }
    5648           1 : }
    5649             : 
    5650           4 : TEST_F(test_cpl, VSIGlob)
    5651             : {
    5652           1 :     GByte abyDummyData[1] = {0};
    5653           1 :     const std::string osFilenameRadix = VSIMemGenerateHiddenFilename("");
    5654           1 :     const std::string osFilename = osFilenameRadix + "trick";
    5655           1 :     VSIFCloseL(VSIFileFromMemBuffer(osFilename.c_str(), abyDummyData,
    5656             :                                     sizeof(abyDummyData), false));
    5657             : 
    5658             :     {
    5659             :         CPLStringList aosRes(
    5660           1 :             VSIGlob(osFilename.c_str(), nullptr, nullptr, nullptr));
    5661           1 :         ASSERT_EQ(aosRes.size(), 1);
    5662           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5663             :     }
    5664             : 
    5665             :     {
    5666             :         CPLStringList aosRes(
    5667           1 :             VSIGlob(osFilename.substr(0, osFilename.size() - 1).c_str(),
    5668           1 :                     nullptr, nullptr, nullptr));
    5669           1 :         ASSERT_EQ(aosRes.size(), 0);
    5670             :     }
    5671             : 
    5672             :     {
    5673             :         CPLStringList aosRes(
    5674           1 :             VSIGlob(std::string(osFilenameRadix).append("?rick").c_str(),
    5675           1 :                     nullptr, nullptr, nullptr));
    5676           1 :         ASSERT_EQ(aosRes.size(), 1);
    5677           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5678             :     }
    5679             : 
    5680             :     {
    5681             :         CPLStringList aosRes(
    5682           1 :             VSIGlob(std::string(osFilenameRadix).append("?rack").c_str(),
    5683           1 :                     nullptr, nullptr, nullptr));
    5684           1 :         ASSERT_EQ(aosRes.size(), 0);
    5685             :     }
    5686             : 
    5687             :     {
    5688             :         CPLStringList aosRes(
    5689           1 :             VSIGlob(std::string(osFilenameRadix).append("*ick").c_str(),
    5690           1 :                     nullptr, nullptr, nullptr));
    5691           1 :         ASSERT_EQ(aosRes.size(), 1);
    5692           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5693             :     }
    5694             : 
    5695             :     {
    5696             :         CPLStringList aosRes(
    5697           1 :             VSIGlob(std::string(osFilenameRadix).append("*").c_str(), nullptr,
    5698           1 :                     nullptr, nullptr));
    5699           1 :         ASSERT_EQ(aosRes.size(), 1);
    5700           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5701             :     }
    5702             : 
    5703             :     {
    5704             :         CPLStringList aosRes(
    5705           1 :             VSIGlob(std::string(osFilenameRadix).append("*ack").c_str(),
    5706           1 :                     nullptr, nullptr, nullptr));
    5707           1 :         ASSERT_EQ(aosRes.size(), 0);
    5708             :     }
    5709             : 
    5710             :     {
    5711             :         CPLStringList aosRes(
    5712           1 :             VSIGlob(std::string(osFilenameRadix).append("*ic*").c_str(),
    5713           1 :                     nullptr, nullptr, nullptr));
    5714           1 :         ASSERT_EQ(aosRes.size(), 1);
    5715           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5716             :     }
    5717             : 
    5718             :     {
    5719             :         CPLStringList aosRes(
    5720           1 :             VSIGlob(std::string(osFilenameRadix).append("*ac*").c_str(),
    5721           1 :                     nullptr, nullptr, nullptr));
    5722           1 :         ASSERT_EQ(aosRes.size(), 0);
    5723             :     }
    5724             : 
    5725             :     {
    5726             :         CPLStringList aosRes(VSIGlob(
    5727           1 :             std::string(osFilenameRadix).append("[st][!s]ic[j-l]").c_str(),
    5728           1 :             nullptr, nullptr, nullptr));
    5729           1 :         ASSERT_EQ(aosRes.size(), 1);
    5730           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5731             :     }
    5732             : 
    5733             :     {
    5734             :         CPLStringList aosRes(
    5735           1 :             VSIGlob(std::string(osFilenameRadix).append("[!s]rick").c_str(),
    5736           1 :                     nullptr, nullptr, nullptr));
    5737           1 :         ASSERT_EQ(aosRes.size(), 1);
    5738           1 :         EXPECT_STREQ(aosRes[0], osFilename.c_str());
    5739             :     }
    5740             : 
    5741             :     {
    5742             :         CPLStringList aosRes(
    5743           1 :             VSIGlob(std::string(osFilenameRadix).append("[").c_str(), nullptr,
    5744           1 :                     nullptr, nullptr));
    5745           1 :         ASSERT_EQ(aosRes.size(), 0);
    5746             :     }
    5747             : 
    5748           1 :     const std::string osFilenameWithSpecialChars = osFilenameRadix + "[!-]";
    5749           1 :     VSIFCloseL(VSIFileFromMemBuffer(osFilenameWithSpecialChars.c_str(),
    5750             :                                     abyDummyData, sizeof(abyDummyData), false));
    5751             : 
    5752             :     {
    5753             :         CPLStringList aosRes(VSIGlob(
    5754           1 :             std::string(osFilenameRadix).append("[[][!]a-][-][]]").c_str(),
    5755           1 :             nullptr, nullptr, nullptr));
    5756           1 :         ASSERT_EQ(aosRes.size(), 1);
    5757           1 :         EXPECT_STREQ(aosRes[0], osFilenameWithSpecialChars.c_str());
    5758             :     }
    5759             : 
    5760           1 :     const std::string osFilename2 = osFilenameRadix + "truck/track";
    5761           1 :     VSIFCloseL(VSIFileFromMemBuffer(osFilename2.c_str(), abyDummyData,
    5762             :                                     sizeof(abyDummyData), false));
    5763             : 
    5764             :     {
    5765             :         CPLStringList aosRes(
    5766           1 :             VSIGlob(std::string(osFilenameRadix).append("*uc*/track").c_str(),
    5767           1 :                     nullptr, nullptr, nullptr));
    5768           1 :         ASSERT_EQ(aosRes.size(), 1);
    5769           1 :         EXPECT_STREQ(aosRes[0], osFilename2.c_str());
    5770             :     }
    5771             : 
    5772             :     {
    5773             :         CPLStringList aosRes(
    5774           1 :             VSIGlob(std::string(osFilenameRadix).append("*uc*/truck").c_str(),
    5775           1 :                     nullptr, nullptr, nullptr));
    5776           1 :         ASSERT_EQ(aosRes.size(), 0);
    5777             :     }
    5778             : 
    5779             :     {
    5780             :         CPLStringList aosRes(
    5781           1 :             VSIGlob(std::string(osFilenameRadix).append("**/track").c_str(),
    5782           1 :                     nullptr, nullptr, nullptr));
    5783           1 :         ASSERT_EQ(aosRes.size(), 1);
    5784           1 :         EXPECT_STREQ(aosRes[0], osFilename2.c_str());
    5785             :     }
    5786             : 
    5787           1 :     VSIUnlink(osFilename.c_str());
    5788           1 :     VSIUnlink(osFilenameWithSpecialChars.c_str());
    5789           1 :     VSIUnlink(osFilename2.c_str());
    5790           1 :     VSIUnlink(osFilenameRadix.c_str());
    5791             : 
    5792             : #if !defined(_WIN32)
    5793             :     {
    5794           1 :         std::string osCurDir;
    5795           1 :         osCurDir.resize(4096);
    5796           1 :         getcwd(&osCurDir[0], osCurDir.size());
    5797           1 :         osCurDir.resize(strlen(osCurDir.c_str()));
    5798           1 :         ASSERT_EQ(chdir(TUT_ROOT_DATA_DIR), 0);
    5799           1 :         CPLStringList aosRes(VSIGlob("byte*.tif", nullptr, nullptr, nullptr));
    5800           1 :         chdir(osCurDir.c_str());
    5801           1 :         ASSERT_EQ(aosRes.size(), 1);
    5802           1 :         EXPECT_STREQ(aosRes[0], "byte.tif");
    5803             :     }
    5804             : #endif
    5805             : }
    5806             : 
    5807           4 : TEST_F(test_cpl, CPLGreatestCommonDivisor)
    5808             : {
    5809           2 :     CPLErrorStateBackuper state(CPLQuietErrorHandler);
    5810             : 
    5811             :     // These tests serve to document the current behavior.
    5812             :     // In some cases the results are dependent on various
    5813             :     // hardcoded epsilons and it may be appropriate to change
    5814             :     // the expected results.
    5815             : 
    5816           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(0.0, 1.0), 0.0);
    5817           1 :     EXPECT_EQ(
    5818             :         CPLGreatestCommonDivisor(std::numeric_limits<double>::quiet_NaN(), 1.0),
    5819             :         0.0);
    5820           1 :     EXPECT_EQ(
    5821             :         CPLGreatestCommonDivisor(std::numeric_limits<double>::infinity(), 1.0),
    5822             :         0.0);
    5823           1 :     EXPECT_EQ(
    5824             :         CPLGreatestCommonDivisor(-std::numeric_limits<double>::infinity(), 1.0),
    5825             :         0.0);
    5826           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(1.0, 0.0), 0.0);
    5827           1 :     EXPECT_EQ(
    5828             :         CPLGreatestCommonDivisor(1.0, std::numeric_limits<double>::quiet_NaN()),
    5829             :         0.0);
    5830           1 :     EXPECT_EQ(
    5831             :         CPLGreatestCommonDivisor(1.0, std::numeric_limits<double>::infinity()),
    5832             :         0.0);
    5833           1 :     EXPECT_EQ(
    5834             :         CPLGreatestCommonDivisor(1.0, -std::numeric_limits<double>::infinity()),
    5835             :         0.0);
    5836             : 
    5837           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(std::numeric_limits<double>::min(),
    5838             :                                        std::numeric_limits<double>::max()),
    5839             :               0.0);
    5840             : 
    5841           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(-2.0, 4.0), -2.0);
    5842           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(-2.0, -4.0), -2.0);
    5843           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(2.0, -4.0), 2.0);
    5844             : 
    5845           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(3.0, 5.0), 1.0);
    5846           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(3.0 / 3600, 5.0 / 3600), 1.0 / 3600);
    5847           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(5.0 / 3600, 2.5 / 3600), 2.5 / 3600);
    5848           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 10, 1.0), 1.0 / 10);
    5849           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 17, 1.0 / 13), 1.0 / 221);
    5850           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 17, 1.0 / 3600), 1.0 / 61200);
    5851             : 
    5852             :     // GLO-90 resolutoins
    5853           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(3.0 / 3600, 4.5 / 3600), 1.5 / 3600);
    5854             : 
    5855             :     // WorldDEM resolutions
    5856           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(0.4 / 3600, 0.6 / 3600), 0.2 / 3600);
    5857           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(0.6 / 3600, 0.8 / 3600), 0.2 / 3600);
    5858             : 
    5859           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(M_PI, M_PI / 6), M_PI / 6);
    5860           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(M_PI / 5, M_PI / 6),
    5861             :               0);  // Ideally we would get M_PI / 30
    5862             : 
    5863           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(2.999999, 3.0), 0);
    5864           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(2.9999999, 3.0), 0);
    5865           1 :     EXPECT_EQ(CPLGreatestCommonDivisor(2.99999999, 3.0), 2.99999999);
    5866           1 : }
    5867             : 
    5868           4 : TEST_F(test_cpl, CPLLevenshteinDistance)
    5869             : {
    5870           1 :     EXPECT_EQ(CPLLevenshteinDistance("", "", false), 0);
    5871           1 :     EXPECT_EQ(CPLLevenshteinDistance("a", "a", false), 0);
    5872           1 :     EXPECT_EQ(CPLLevenshteinDistance("a", "b", false), 1);
    5873           1 :     EXPECT_EQ(CPLLevenshteinDistance("a", "", false), 1);
    5874           1 :     EXPECT_EQ(CPLLevenshteinDistance("abc", "ac", false), 1);
    5875           1 :     EXPECT_EQ(CPLLevenshteinDistance("ac", "abc", false), 1);
    5876           1 :     EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0xy1", false), 2);
    5877           1 :     EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0xy1", true), 2);
    5878           1 :     EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0ba1", false), 2);
    5879           1 :     EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0ba1", true), 1);
    5880             : 
    5881           2 :     std::string longStr(32768, 'x');
    5882           1 :     EXPECT_EQ(CPLLevenshteinDistance(longStr.c_str(), longStr.c_str(), true),
    5883             :               0);
    5884           1 :     EXPECT_EQ(CPLLevenshteinDistance(longStr.c_str(), "another_one", true),
    5885             :               std::numeric_limits<size_t>::max());
    5886           1 : }
    5887             : 
    5888           4 : TEST_F(test_cpl, CPLLockFileEx)
    5889             : {
    5890           1 :     const std::string osLockFilename = CPLGenerateTempFilename(".lock");
    5891             : 
    5892           1 :     ASSERT_EQ(CPLLockFileEx(nullptr, nullptr, nullptr), CLFS_API_MISUSE);
    5893             : 
    5894           1 :     ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), nullptr, nullptr),
    5895             :               CLFS_API_MISUSE);
    5896             : 
    5897           1 :     CPLLockFileHandle hLockFileHandle = nullptr;
    5898             : 
    5899           1 :     ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), &hLockFileHandle, nullptr),
    5900             :               CLFS_OK);
    5901           1 :     ASSERT_NE(hLockFileHandle, nullptr);
    5902             : 
    5903             :     // Check the lock file has been created
    5904             :     VSIStatBufL sStat;
    5905           1 :     ASSERT_EQ(VSIStatL(osLockFilename.c_str(), &sStat), 0);
    5906             : 
    5907             :     {
    5908           1 :         CPLStringList aosOptions;
    5909           1 :         aosOptions.SetNameValue("WAIT_TIME", "0.1");
    5910           1 :         CPLLockFileHandle hLockFileHandle2 = nullptr;
    5911           1 :         ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), &hLockFileHandle2,
    5912             :                                 aosOptions.List()),
    5913             :                   CLFS_LOCK_BUSY);
    5914             :     }
    5915             : 
    5916           1 :     CPLUnlockFileEx(hLockFileHandle);
    5917             : 
    5918             :     // Check the lock file has been deleted
    5919           1 :     ASSERT_EQ(VSIStatL(osLockFilename.c_str(), &sStat), -1);
    5920             : 
    5921           1 :     CPLUnlockFileEx(nullptr);
    5922             : }
    5923             : 
    5924           4 : TEST_F(test_cpl, CPLFormatReadableFileSize)
    5925             : {
    5926           2 :     EXPECT_STREQ(CPLFormatReadableFileSize(1.23e18).c_str(), "1.23 HB");
    5927           2 :     EXPECT_STREQ(CPLFormatReadableFileSize(1.23e15).c_str(), "1.23 PB");
    5928           2 :     EXPECT_STREQ(CPLFormatReadableFileSize(1.23e12).c_str(), "1.23 TB");
    5929           2 :     EXPECT_STREQ(CPLFormatReadableFileSize(1.23e9).c_str(), "1.23 GB");
    5930           2 :     EXPECT_STREQ(CPLFormatReadableFileSize(1.23e6).c_str(), "1.23 MB");
    5931           2 :     EXPECT_STREQ(
    5932             :         CPLFormatReadableFileSize(static_cast<uint64_t>(123456)).c_str(),
    5933             :         "123,456 bytes");
    5934           1 : }
    5935             : 
    5936           4 : TEST_F(test_cpl, CPLStrlenUTF8)
    5937             : {
    5938           1 :     EXPECT_EQ(CPLStrlenUTF8("a"), 1);
    5939           1 :     EXPECT_EQ(CPLStrlenUTF8("a"
    5940             :                             "\xC3\xA9"
    5941             :                             "b"),
    5942             :               3);
    5943           1 : }
    5944             : 
    5945           4 : TEST_F(test_cpl, CPLStrlenUTF8Ex)
    5946             : {
    5947           1 :     EXPECT_EQ(CPLStrlenUTF8Ex("a"), 1);
    5948           1 :     EXPECT_EQ(CPLStrlenUTF8Ex("a"
    5949             :                               "\xC3\xA9"
    5950             :                               "b"),
    5951             :               3);
    5952           1 : }
    5953             : 
    5954           4 : TEST_F(test_cpl, CPLGetRemainingFileDescriptorCount)
    5955             : {
    5956             : #ifdef _WIN32
    5957             :     EXPECT_EQ(CPLGetRemainingFileDescriptorCount(), -1);
    5958             : #else
    5959           1 :     EXPECT_GE(CPLGetRemainingFileDescriptorCount(), 0);
    5960             : #endif
    5961           1 : }
    5962             : 
    5963           4 : TEST_F(test_cpl, CPLGetCurrentThreadCount)
    5964             : {
    5965             : #if defined(_WIN32) || defined(__linux) || defined(__FreeBSD__) ||             \
    5966             :     defined(__NetBSD__) || (defined(__APPLE__) && defined(__MACH__))
    5967             :     // Not sure why it returns 0 on those, whereas it works fine on build-windows-msys2-mingw
    5968           1 :     if (strstr(CPLGetConfigOption("BUILD_NAME", ""), "build-windows-conda") !=
    5969           1 :             nullptr &&
    5970           0 :         strstr(CPLGetConfigOption("BUILD_NAME", ""), "build-windows-minimum") !=
    5971             :             nullptr)
    5972             :     {
    5973           0 :         EXPECT_GE(CPLGetCurrentThreadCount(), 1);
    5974             :     }
    5975             : #else
    5976             :     EXPECT_EQ(CPLGetCurrentThreadCount(), 0);
    5977             : #endif
    5978           1 : }
    5979             : 
    5980           4 : TEST_F(test_cpl, CPLHasPathTraversal)
    5981             : {
    5982           1 :     EXPECT_TRUE(CPLHasPathTraversal("a/../b"));
    5983           1 :     EXPECT_TRUE(CPLHasPathTraversal("a/../"));
    5984           1 :     EXPECT_TRUE(CPLHasPathTraversal("a/.."));
    5985           1 :     EXPECT_TRUE(CPLHasPathTraversal("a\\..\\b"));
    5986           1 :     EXPECT_FALSE(CPLHasPathTraversal("a/b"));
    5987             :     {
    5988             :         CPLConfigOptionSetter oSetter("CPL_ENABLE_PATH_TRAVERSAL_DETECTION",
    5989           2 :                                       "NO", true);
    5990           1 :         EXPECT_FALSE(CPLHasPathTraversal("a/../b"));
    5991           1 :         EXPECT_FALSE(CPLHasPathTraversal("a\\..\\b"));
    5992             :     }
    5993           1 : }
    5994             : 
    5995           4 : TEST_F(test_cpl, CPLHasUnbalancedPathTraversal)
    5996             : {
    5997           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a"));
    5998           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("."));
    5999           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("./"));
    6000           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a/.."));
    6001           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a/../b"));
    6002           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("./a/../b"));
    6003           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a\\..\\b"));
    6004           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a/../b/../"));
    6005           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a/../b/.."));
    6006           1 :     EXPECT_FALSE(CPLHasUnbalancedPathTraversal("a/b"));
    6007             : 
    6008           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal(".."));
    6009           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("../"));
    6010           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("../b"));
    6011           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("a/../../"));
    6012           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("a/../b/../.."));
    6013           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("a/../b/../../"));
    6014           1 :     EXPECT_TRUE(CPLHasUnbalancedPathTraversal("a\\..\\..\\"));
    6015           1 : }
    6016             : 
    6017           4 : TEST_F(test_cpl, cpl_enumerate)
    6018             : {
    6019             :     {
    6020           1 :         size_t expectedIdx = 0;
    6021           1 :         const int tab[] = {3, 1, 2};
    6022           4 :         for (auto [idx, val] : cpl::enumerate(tab))
    6023             :         {
    6024           3 :             EXPECT_EQ(idx, expectedIdx);
    6025           3 :             EXPECT_EQ(val, tab[idx]);
    6026           3 :             ++expectedIdx;
    6027             :         }
    6028           1 :         EXPECT_EQ(expectedIdx, 3U);
    6029             :     }
    6030             :     {
    6031           1 :         int tab[] = {3, 1, 2};
    6032           4 :         for (auto [idx, val] : cpl::enumerate(tab))
    6033             :         {
    6034             :             (void)idx;
    6035           3 :             ++val;
    6036             :         }
    6037           1 :         EXPECT_EQ(tab[0], 4);
    6038           1 :         EXPECT_EQ(tab[1], 2);
    6039           1 :         EXPECT_EQ(tab[2], 3);
    6040             :     }
    6041           1 : }
    6042             : 
    6043           4 : TEST_F(test_cpl, CPL_SWAP)
    6044             : {
    6045             :     {
    6046           1 :         uint8_t x = 1;
    6047           1 :         EXPECT_EQ(CPL_SWAP(x), x);
    6048           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6049             :     }
    6050             :     {
    6051           1 :         int8_t x = 1;
    6052           1 :         EXPECT_EQ(CPL_SWAP(x), x);
    6053           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6054             :     }
    6055             :     {
    6056           1 :         uint16_t x = 0x0123;
    6057           1 :         auto y = CPL_SWAP(x);
    6058           1 :         EXPECT_NE(y, x);
    6059           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6060             : #if CPL_IS_LSB
    6061           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6062             : #else
    6063             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6064             : #endif
    6065             :     }
    6066             :     {
    6067           1 :         int16_t x = 0x0123;
    6068           1 :         auto y = CPL_SWAP(x);
    6069           1 :         EXPECT_NE(y, x);
    6070           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6071             : #if CPL_IS_LSB
    6072           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6073             : #else
    6074             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6075             : #endif
    6076             :     }
    6077             :     {
    6078           1 :         uint32_t x = 0x01234567;
    6079           1 :         auto y = CPL_SWAP(x);
    6080           1 :         EXPECT_NE(y, x);
    6081           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6082             : #if CPL_IS_LSB
    6083           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6084             : #else
    6085             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6086             : #endif
    6087             :     }
    6088             :     {
    6089           1 :         int32_t x = 0x01234567;
    6090           1 :         auto y = CPL_SWAP(x);
    6091           1 :         EXPECT_NE(y, x);
    6092           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6093             : #if CPL_IS_LSB
    6094           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6095             : #else
    6096             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6097             : #endif
    6098             :     }
    6099             :     {
    6100           1 :         uint64_t x = (static_cast<uint64_t>(0x01234567) << 32) | 0x89ABCDEF;
    6101           1 :         auto y = CPL_SWAP(x);
    6102           1 :         EXPECT_NE(y, x);
    6103           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6104             : #if CPL_IS_LSB
    6105           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6106             : #else
    6107             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6108             : #endif
    6109             :     }
    6110             :     {
    6111           1 :         int64_t x = (static_cast<int64_t>(0x01234567) << 32) | 0x89ABCDEF;
    6112           1 :         auto y = CPL_SWAP(x);
    6113           1 :         EXPECT_NE(y, x);
    6114           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6115             : #if CPL_IS_LSB
    6116           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6117             : #else
    6118             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6119             : #endif
    6120             :     }
    6121             :     {
    6122           1 :         float x = 1.5;
    6123           1 :         auto y = CPL_SWAP(x);
    6124           1 :         EXPECT_NE(y, x);
    6125           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6126             : #if CPL_IS_LSB
    6127           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6128             : #else
    6129             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6130             : #endif
    6131             :     }
    6132             :     {
    6133           1 :         double x = 1.5;
    6134           1 :         auto y = CPL_SWAP(x);
    6135           1 :         EXPECT_NE(y, x);
    6136           1 :         EXPECT_EQ(CPL_SWAP(y), x);
    6137             : #if CPL_IS_LSB
    6138           1 :         EXPECT_EQ(CPL_AS_LSB(x), x);
    6139             : #else
    6140             :         EXPECT_EQ(CPL_AS_LSB(x), CPL_SWAP(x));
    6141             : #endif
    6142             :     }
    6143           1 : }
    6144             : 
    6145           4 : TEST_F(test_cpl, CPLLaunderForFilenameSafe)
    6146             : {
    6147           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("").c_str(), "");
    6148           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a").c_str(), "a");
    6149           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a<b").c_str(), "a_b");
    6150           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a>b").c_str(), "a_b");
    6151           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a:b").c_str(), "a_b");
    6152           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a\"b").c_str(), "a_b");
    6153           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a/b").c_str(), "a_b");
    6154           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a\\b").c_str(), "a_b");
    6155           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a|b").c_str(), "a_b");
    6156           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a?b").c_str(), "a_b");
    6157           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a*b").c_str(), "a_b");
    6158           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a^b").c_str(), "a_b");
    6159           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe(".").c_str(), "._");
    6160           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("..").c_str(), ".._");
    6161           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a.b").c_str(), "a.b");
    6162           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe(" a ").c_str(), " a _");
    6163           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe(".a.").c_str(), ".a._");
    6164           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe(" a ", ';').c_str(), " a ;");
    6165           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("CON").c_str(), "CON_");
    6166           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a<b c", ';', " ").c_str(), "a;b;c");
    6167           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("a<b c", '\0', " ").c_str(), "abc");
    6168           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("CON", '\0', nullptr).c_str(),
    6169             :                  "CON_");
    6170           2 :     EXPECT_STREQ(CPLLaunderForFilenameSafe("CON", ';').c_str(), "CON;");
    6171           1 : }
    6172             : 
    6173           4 : TEST_F(test_cpl, cpl_starts_with)
    6174             : {
    6175           2 :     std::string str = "abc";
    6176           1 :     const char *cstr = "abc";
    6177           1 :     std::string_view sv = std::string_view(str).substr(0, 2);
    6178             : 
    6179           1 :     EXPECT_TRUE(cpl::starts_with(str, "ab"));
    6180           1 :     EXPECT_TRUE(cpl::starts_with(cstr, "ab"));
    6181           1 :     EXPECT_TRUE(cpl::starts_with(sv, "ab"));
    6182           1 :     EXPECT_TRUE(cpl::starts_with(sv, ""));
    6183             : 
    6184           1 :     EXPECT_TRUE(cpl::starts_with_ci(str, "ab"));
    6185           1 :     EXPECT_TRUE(cpl::starts_with_ci(str, "aB"));
    6186           1 :     EXPECT_TRUE(cpl::starts_with_ci(str, ""));
    6187             : 
    6188           1 :     EXPECT_FALSE(cpl::starts_with(str, "ac"));
    6189           1 :     EXPECT_FALSE(cpl::starts_with(str, "abcd"));
    6190           1 :     EXPECT_FALSE(cpl::starts_with(str, "aB"));
    6191           1 :     EXPECT_FALSE(cpl::starts_with(std::string_view(str).substr(0, 1), "ab"));
    6192           1 : }
    6193             : 
    6194           4 : TEST_F(test_cpl, cpl_ends_with)
    6195             : {
    6196           2 :     std::string str = "abc";
    6197           1 :     const char *cstr = "abc";
    6198           1 :     std::string_view sv = std::string_view(str).substr(0, 3);
    6199             : 
    6200           1 :     EXPECT_TRUE(cpl::ends_with(str, "bc"));
    6201           1 :     EXPECT_TRUE(cpl::ends_with(cstr, "bc"));
    6202           1 :     EXPECT_TRUE(cpl::ends_with(sv, "bc"));
    6203           1 :     EXPECT_TRUE(cpl::ends_with(sv, ""));
    6204             : 
    6205           1 :     EXPECT_TRUE(cpl::ends_with_ci(str, "bc"));
    6206           1 :     EXPECT_TRUE(cpl::ends_with_ci(str, "bC"));
    6207           1 :     EXPECT_TRUE(cpl::ends_with_ci(str, ""));
    6208             : 
    6209           1 :     EXPECT_FALSE(cpl::ends_with(str, "ac"));
    6210           1 :     EXPECT_FALSE(cpl::ends_with(str, "abcd"));
    6211           1 :     EXPECT_FALSE(cpl::ends_with(str, "Bc"));
    6212           1 :     EXPECT_FALSE(cpl::ends_with(std::string_view(str).substr(0, 2), "bc"));
    6213           1 : }
    6214             : 
    6215           4 : TEST_F(test_cpl, cpl_equals)
    6216             : {
    6217           2 :     std::string str = "abc";
    6218           1 :     const char *cstr = "abc";
    6219           1 :     std::string_view sv = str;
    6220             : 
    6221           1 :     EXPECT_TRUE(cpl::equals(str, cstr));
    6222           1 :     EXPECT_TRUE(cpl::equals(cstr, sv));
    6223           1 :     EXPECT_TRUE(cpl::equals(sv, str));
    6224             : 
    6225           1 :     EXPECT_FALSE(cpl::equals(str, ""));
    6226           1 :     EXPECT_FALSE(cpl::equals(str, std::string_view(str).substr(0, 2)));
    6227             : 
    6228           1 :     EXPECT_FALSE(cpl::equals(str, "abC"));
    6229           1 :     EXPECT_TRUE(cpl::equals_ci(str, "abC"));
    6230           1 :     EXPECT_FALSE(cpl::equals_ci(str, ""));
    6231           1 : }
    6232             : 
    6233           4 : TEST_F(test_cpl, trim)
    6234             : {
    6235             :     // input type: const char*
    6236             :     {
    6237           1 :         const char *str = "";
    6238           1 :         const auto trimmed = cpl::trim(str);
    6239           1 :         EXPECT_EQ(trimmed, "");
    6240           1 :         EXPECT_EQ(trimmed.data(), str);  // trimmed points within str
    6241             : 
    6242           1 :         const auto ltrimmed = cpl::ltrim(str);
    6243           1 :         EXPECT_EQ(ltrimmed, "");
    6244           1 :         EXPECT_EQ(ltrimmed.data(), str);
    6245             : 
    6246           1 :         const auto rtrimmed = cpl::rtrim(str);
    6247           1 :         EXPECT_EQ(rtrimmed, "");
    6248           1 :         EXPECT_EQ(rtrimmed.data(), str);
    6249             :     }
    6250             : 
    6251             :     // input type: std::string
    6252             :     {
    6253           2 :         const std::string str = "\r\n\t    \r\n\t";
    6254           1 :         const auto trimmed = cpl::trim(str);
    6255             : 
    6256           1 :         EXPECT_EQ(trimmed, "");
    6257           1 :         EXPECT_EQ(trimmed.data(), str.data() + str.size());
    6258             : 
    6259           1 :         const auto ltrimmed = cpl::ltrim(str);
    6260             : 
    6261           1 :         EXPECT_EQ(ltrimmed, "");
    6262           1 :         EXPECT_EQ(ltrimmed.data(), str.data() + str.size());
    6263             : 
    6264           1 :         const auto rtrimmed = cpl::rtrim(str);
    6265             : 
    6266           1 :         EXPECT_EQ(rtrimmed, "");
    6267           1 :         EXPECT_EQ(rtrimmed.data(), str.data());
    6268             :     }
    6269             : 
    6270             :     // input type: std::string_view
    6271             :     {
    6272           1 :         const std::string_view str = "  abc\t  ";
    6273           1 :         const auto trimmed = cpl::trim(str);
    6274             : 
    6275           1 :         EXPECT_EQ(trimmed, "abc");
    6276           1 :         EXPECT_EQ(trimmed.data(), str.data() + 2);  // trimmed points within str
    6277             : 
    6278           1 :         const auto ltrimmed = cpl::ltrim(str);
    6279             : 
    6280           1 :         EXPECT_EQ(ltrimmed, "abc\t  ");
    6281           1 :         EXPECT_EQ(ltrimmed.data(), str.data() + 2);
    6282             : 
    6283           1 :         const auto rtrimmed = cpl::rtrim(str);
    6284             : 
    6285           1 :         EXPECT_EQ(rtrimmed, "  abc");
    6286           1 :         EXPECT_EQ(rtrimmed.data(), str.data());
    6287             :     }
    6288             : 
    6289             :     // input_type: std::string_view&&
    6290             :     {
    6291             :         // line below should not compile
    6292             :         //auto trimmed = cpl::trim(std::string("abc"));
    6293             :     }
    6294           1 : }
    6295             : 
    6296           4 : TEST_F(test_cpl, parse_name_value)
    6297             : {
    6298             :     {
    6299           2 :         std::string input = "OPT_NAME=VALUE";
    6300           1 :         auto [name, value] = cpl::parse_name_value(input);
    6301             : 
    6302           1 :         EXPECT_EQ(name, "OPT_NAME");
    6303           1 :         EXPECT_EQ(value, "VALUE");
    6304             :     }
    6305             : 
    6306             :     {
    6307           1 :         const char *input = "  OPT_NAME =   VALUE WITH SPACE ";
    6308           1 :         auto [name, value] = cpl::parse_name_value(input);
    6309             : 
    6310           1 :         EXPECT_EQ(name, "OPT_NAME");
    6311           1 :         EXPECT_EQ(value, "VALUE WITH SPACE");
    6312             :     }
    6313             : 
    6314             :     {
    6315           2 :         std::string input = "OPT_NAME=";
    6316           1 :         auto [name, value] = cpl::parse_name_value(input);
    6317             : 
    6318           1 :         EXPECT_EQ(name, "OPT_NAME");
    6319           1 :         EXPECT_EQ(value, "");
    6320             :     }
    6321             : 
    6322             :     {
    6323           2 :         std::string input = "=VALUE";
    6324           1 :         auto [name, value] = cpl::parse_name_value(input);
    6325             : 
    6326           1 :         EXPECT_EQ(name, "");
    6327           1 :         EXPECT_EQ(value, "");
    6328             :     }
    6329             : 
    6330             :     {
    6331           2 :         std::string input = "INVALID";
    6332           1 :         auto [name, value] = cpl::parse_name_value(input);
    6333             : 
    6334           1 :         EXPECT_EQ(name, "");
    6335           1 :         EXPECT_EQ(value, "");
    6336             :     }
    6337             : 
    6338             :     {
    6339           1 :         const char *input = "";
    6340           1 :         auto [name, value] = cpl::parse_name_value(input);
    6341             : 
    6342           1 :         EXPECT_EQ(name, "");
    6343           1 :         EXPECT_EQ(value, "");
    6344             :     }
    6345           1 : }
    6346             : 
    6347           4 : TEST_F(test_cpl, strict_parse)
    6348             : {
    6349           1 :     EXPECT_EQ(cpl::strict_parse<bool>("YES"), true);
    6350           1 :     EXPECT_EQ(cpl::strict_parse<bool>("1 "), true);
    6351           1 :     EXPECT_EQ(cpl::strict_parse<bool>(" ON "), true);
    6352           1 :     EXPECT_EQ(cpl::strict_parse<bool>("\tyes "), true);
    6353           1 :     EXPECT_EQ(cpl::strict_parse<bool>("TRUEE"), std::nullopt);
    6354           1 :     EXPECT_EQ(cpl::strict_parse<bool>(""), std::nullopt);
    6355             : 
    6356           1 :     EXPECT_EQ(cpl::strict_parse<bool>("NO"), false);
    6357           1 :     EXPECT_EQ(cpl::strict_parse<bool>("0 "), false);
    6358           1 :     EXPECT_EQ(cpl::strict_parse<bool>(" OFF "), false);
    6359           1 :     EXPECT_EQ(cpl::strict_parse<bool>("\tno "), false);
    6360           1 :     EXPECT_EQ(cpl::strict_parse<bool>("NON"), std::nullopt);
    6361             : 
    6362           1 :     EXPECT_EQ(cpl::strict_parse<int>("123"), 123);
    6363           1 :     EXPECT_EQ(cpl::strict_parse<int>("0123"), 123);
    6364           1 :     EXPECT_EQ(cpl::strict_parse<int>(" -456"), -456);
    6365           1 :     EXPECT_EQ(cpl::strict_parse<int>(" - 456"), std::nullopt);
    6366           1 :     EXPECT_EQ(cpl::strict_parse<int>("789."), 789);
    6367           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0"), 789);
    6368           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0.0"), std::nullopt);
    6369           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.1"), std::nullopt);
    6370           1 :     EXPECT_EQ(cpl::strict_parse<int>("789e3"), 789000);
    6371           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.e3"), 789000);
    6372           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0e3"), 789000);
    6373           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0e3f"), std::nullopt);
    6374           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0e3.2"), std::nullopt);
    6375           1 :     EXPECT_EQ(cpl::strict_parse<int>("789.0e30"), std::nullopt);
    6376           1 :     EXPECT_EQ(cpl::strict_parse<int>("50000000000000000"), std::nullopt);
    6377           1 :     EXPECT_EQ(cpl::strict_parse<int>(""), std::nullopt);
    6378           1 :     EXPECT_EQ(cpl::strict_parse<int>("123c"), std::nullopt);
    6379           1 :     EXPECT_EQ(cpl::strict_parse<int>("Q"), std::nullopt);
    6380             : 
    6381           1 :     EXPECT_EQ(cpl::strict_parse<double>("3.141569"), 3.141569);
    6382           1 :     EXPECT_EQ(cpl::strict_parse<double>("3,141569"), std::nullopt);
    6383           1 :     EXPECT_EQ(cpl::strict_parse<double>("-8.33e-2"), -8.33e-2);
    6384           1 :     EXPECT_EQ(cpl::strict_parse<double>("06.022e23"), 6.022e23);
    6385           1 :     EXPECT_EQ(cpl::strict_parse<double>("6.022E23"), 6.022e23);
    6386           1 :     EXPECT_EQ(cpl::strict_parse<double>("6.022e+23"), 6.022e23);
    6387           1 :     EXPECT_EQ(cpl::strict_parse<double>("6.022e+23"), 6.022e23);
    6388           1 :     EXPECT_EQ(cpl::strict_parse<double>(""), std::nullopt);
    6389           1 :     EXPECT_EQ(cpl::strict_parse<double>("  "), std::nullopt);
    6390           1 :     EXPECT_EQ(cpl::strict_parse<double>(" -"), std::nullopt);
    6391           1 :     EXPECT_EQ(cpl::strict_parse<double>("123.2e"), std::nullopt);
    6392           1 :     EXPECT_EQ(cpl::strict_parse<double>("q"), std::nullopt);
    6393             : 
    6394           1 :     EXPECT_EQ(cpl::strict_parse<double>("inf"),
    6395             :               std::numeric_limits<double>::infinity());
    6396           1 :     EXPECT_EQ(cpl::strict_parse<double>("-inf"),
    6397             :               -std::numeric_limits<double>::infinity());
    6398             : 
    6399           1 :     EXPECT_EQ(std::isnan(cpl::strict_parse<double>("nan").value()), true);
    6400           1 :     EXPECT_EQ(std::isnan(cpl::strict_parse<double>("NaN").value()), true);
    6401           1 :     EXPECT_EQ(std::isnan(cpl::strict_parse<double>("NAN").value()), true);
    6402           1 :     EXPECT_EQ(cpl::strict_parse<double>("NANA"), std::nullopt);
    6403             : 
    6404           1 :     EXPECT_EQ(cpl::strict_parse<float>("3.4e39"), std::nullopt);
    6405           1 :     EXPECT_EQ(cpl::strict_parse<float>("-3.4e39"), std::nullopt);
    6406           1 :     EXPECT_EQ(cpl::strict_parse<float>("1e-39"), std::nullopt);
    6407           1 :     EXPECT_EQ(cpl::strict_parse<float>("-1e-39"), std::nullopt);
    6408           1 : }
    6409             : 
    6410             : }  // namespace

Generated by: LCOV version 1.14