LCOV - code coverage report
Current view: top level - autotest/cpp - test_cpl.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3014 3089 97.6 %
Date: 2026-03-05 10:33:42 Functions: 459 464 98.9 %

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

Generated by: LCOV version 1.14