LCOV - code coverage report
Current view: top level - autotest/cpp - test_cpl.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 3160 3232 97.8 %
Date: 2025-05-31 00:00:17 Functions: 419 424 98.8 %

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

Generated by: LCOV version 1.14