LCOV - code coverage report
Current view: top level - autotest/cpp - test_cpl.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 2805 2876 97.5 %
Date: 2024-05-14 13:00:50 Functions: 360 365 98.6 %

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

Generated by: LCOV version 1.14