gdal/autotest/cpp/test_cpl.cpp

4649 строки
152 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Project: C++ Test Suite for GDAL/OGR
// Purpose: Test general CPL features.
// Author: Mateusz Loskot <mateusz@loskot.net>
//
///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
// Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
// Copyright (c) 2017, Dmitry Baryshnikov <polimax@mail.ru>
// Copyright (c) 2017, NextGIS <info@nextgis.com>
/*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#ifndef GDAL_COMPILATION
#define GDAL_COMPILATION
#endif
#include "gdal_unit_test.h"
#include "cpl_compressor.h"
#include "cpl_error.h"
#include "cpl_hash_set.h"
#include "cpl_list.h"
#include "cpl_mask.h"
#include "cpl_sha256.h"
#include "cpl_string.h"
#include "cpl_safemaths.hpp"
#include "cpl_time.h"
#include "cpl_json.h"
#include "cpl_json_streaming_parser.h"
#include "cpl_json_streaming_writer.h"
#include "cpl_mem_cache.h"
#include "cpl_http.h"
#include "cpl_auto_close.h"
#include "cpl_minixml.h"
#include "cpl_quad_tree.h"
#include "cpl_worker_thread_pool.h"
#include "cpl_vsi_virtual.h"
#include "cpl_threadsafe_queue.hpp"
#include <atomic>
#include <limits>
#include <fstream>
#include <string>
#include "gtest_include.h"
static bool gbGotError = false;
static void CPL_STDCALL myErrorHandler(CPLErr, CPLErrorNum, const char *)
{
gbGotError = true;
}
namespace
{
// Common fixture with test data
struct test_cpl : public ::testing::Test
{
std::string data_;
test_cpl()
{
// Compose data path for test group
data_ = tut::common::data_basedir;
}
void SetUp() override
{
CPLSetConfigOptions(nullptr);
CPLSetThreadLocalConfigOptions(nullptr);
}
};
// Test cpl_list API
TEST_F(test_cpl, CPLList)
{
CPLList *list;
list = CPLListInsert(nullptr, (void *)nullptr, 0);
EXPECT_TRUE(CPLListCount(list) == 1);
list = CPLListRemove(list, 2);
EXPECT_TRUE(CPLListCount(list) == 1);
list = CPLListRemove(list, 1);
EXPECT_TRUE(CPLListCount(list) == 1);
list = CPLListRemove(list, 0);
EXPECT_TRUE(CPLListCount(list) == 0);
list = nullptr;
list = CPLListInsert(nullptr, (void *)nullptr, 2);
EXPECT_TRUE(CPLListCount(list) == 3);
list = CPLListRemove(list, 2);
EXPECT_TRUE(CPLListCount(list) == 2);
list = CPLListRemove(list, 1);
EXPECT_TRUE(CPLListCount(list) == 1);
list = CPLListRemove(list, 0);
EXPECT_TRUE(CPLListCount(list) == 0);
list = nullptr;
list = CPLListAppend(list, (void *)1);
EXPECT_TRUE(CPLListGet(list, 0) == list);
EXPECT_TRUE(CPLListGet(list, 1) == nullptr);
list = CPLListAppend(list, (void *)2);
list = CPLListInsert(list, (void *)3, 2);
EXPECT_TRUE(CPLListCount(list) == 3);
CPLListDestroy(list);
list = nullptr;
list = CPLListAppend(list, (void *)1);
list = CPLListAppend(list, (void *)2);
list = CPLListInsert(list, (void *)4, 3);
CPLListGet(list, 2)->pData = (void *)3;
EXPECT_TRUE(CPLListCount(list) == 4);
EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
CPLListDestroy(list);
list = nullptr;
list = CPLListInsert(list, (void *)4, 1);
CPLListGet(list, 0)->pData = (void *)2;
list = CPLListInsert(list, (void *)1, 0);
list = CPLListInsert(list, (void *)3, 2);
EXPECT_TRUE(CPLListCount(list) == 4);
EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
list = CPLListRemove(list, 1);
list = CPLListRemove(list, 1);
list = CPLListRemove(list, 0);
list = CPLListRemove(list, 0);
EXPECT_TRUE(list == nullptr);
}
typedef struct
{
const char *testString;
CPLValueType expectedResult;
} TestStringStruct;
// Test CPLGetValueType
TEST_F(test_cpl, CPLGetValueType)
{
TestStringStruct apszTestStrings[] = {
{"+25.e+3", CPL_VALUE_REAL}, {"-25.e-3", CPL_VALUE_REAL},
{"25.e3", CPL_VALUE_REAL}, {"25e3", CPL_VALUE_REAL},
{" 25e3 ", CPL_VALUE_REAL}, {".1e3", CPL_VALUE_REAL},
{"25", CPL_VALUE_INTEGER}, {"-25", CPL_VALUE_INTEGER},
{"+25", CPL_VALUE_INTEGER},
{"25e 3", CPL_VALUE_STRING}, {"25e.3", CPL_VALUE_STRING},
{"-2-5e3", CPL_VALUE_STRING}, {"2-5e3", CPL_VALUE_STRING},
{"25.25.3", CPL_VALUE_STRING}, {"25e25e3", CPL_VALUE_STRING},
{"25e2500", CPL_VALUE_STRING}, /* #6128 */
{"d1", CPL_VALUE_STRING} /* #6305 */
};
size_t i;
for (i = 0; i < sizeof(apszTestStrings) / sizeof(apszTestStrings[0]); i++)
{
EXPECT_EQ(CPLGetValueType(apszTestStrings[i].testString),
apszTestStrings[i].expectedResult)
<< apszTestStrings[i].testString;
}
}
// Test cpl_hash_set API
TEST_F(test_cpl, CPLHashSet)
{
CPLHashSet *set =
CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("hello")) == TRUE);
EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("good morning")) == TRUE);
EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == TRUE);
EXPECT_TRUE(CPLHashSetSize(set) == 3);
EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == FALSE);
EXPECT_TRUE(CPLHashSetSize(set) == 3);
EXPECT_TRUE(CPLHashSetRemove(set, "bye bye") == TRUE);
EXPECT_TRUE(CPLHashSetSize(set) == 2);
EXPECT_TRUE(CPLHashSetRemove(set, "good afternoon") == FALSE);
EXPECT_TRUE(CPLHashSetSize(set) == 2);
CPLHashSetDestroy(set);
}
static int sumValues(void *elt, void *user_data)
{
int *pnSum = (int *)user_data;
*pnSum += *(int *)elt;
return TRUE;
}
// Test cpl_hash_set API
TEST_F(test_cpl, CPLHashSet2)
{
const int HASH_SET_SIZE = 1000;
int data[HASH_SET_SIZE];
for (int i = 0; i < HASH_SET_SIZE; ++i)
{
data[i] = i;
}
CPLHashSet *set = CPLHashSetNew(nullptr, nullptr, nullptr);
for (int i = 0; i < HASH_SET_SIZE; i++)
{
EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == TRUE);
}
EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
for (int i = 0; i < HASH_SET_SIZE; i++)
{
EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == FALSE);
}
EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
for (int i = 0; i < HASH_SET_SIZE; i++)
{
EXPECT_TRUE(CPLHashSetLookup(set, (const void *)&data[i]) ==
(const void *)&data[i]);
}
int sum = 0;
CPLHashSetForeach(set, sumValues, &sum);
EXPECT_EQ(sum, (HASH_SET_SIZE - 1) * HASH_SET_SIZE / 2);
for (int i = 0; i < HASH_SET_SIZE; i++)
{
EXPECT_TRUE(CPLHashSetRemove(set, (void *)&data[i]) == TRUE);
}
EXPECT_EQ(CPLHashSetSize(set), 0);
CPLHashSetDestroy(set);
}
// Test cpl_string API
TEST_F(test_cpl, CSLTokenizeString2)
{
{
CPLStringList aosStringList(
CSLTokenizeString2("one two three", " ", 0));
ASSERT_EQ(aosStringList.size(), 3);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "three"));
// Test range-based for loop
int i = 0;
for (const char *pszVal : aosStringList)
{
EXPECT_STREQ(pszVal, aosStringList[i]);
++i;
}
EXPECT_EQ(i, 3);
}
{
CPLStringList aosStringList;
// Test range-based for loop on empty list
int i = 0;
for (const char *pszVal : aosStringList)
{
EXPECT_EQ(pszVal, nullptr); // should not reach that point...
++i;
}
EXPECT_EQ(i, 0);
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one two, three;four,five; six", " ;,", 0));
ASSERT_EQ(aosStringList.size(), 6);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "five"));
ASSERT_TRUE(EQUAL(aosStringList[5], "six"));
}
{
CPLStringList aosStringList(CSLTokenizeString2(
"one two,,,five,six", " ,", CSLT_ALLOWEMPTYTOKENS));
ASSERT_EQ(aosStringList.size(), 6);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], ""));
ASSERT_TRUE(EQUAL(aosStringList[3], ""));
ASSERT_TRUE(EQUAL(aosStringList[4], "five"));
ASSERT_TRUE(EQUAL(aosStringList[5], "six"));
}
{
CPLStringList aosStringList(CSLTokenizeString2(
"one two,\"three,four ,\",five,six", " ,", CSLT_HONOURSTRINGS));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "three,four ,"));
ASSERT_TRUE(EQUAL(aosStringList[3], "five"));
ASSERT_TRUE(EQUAL(aosStringList[4], "six"));
}
{
CPLStringList aosStringList(CSLTokenizeString2(
"one two,\"three,four ,\",five,six", " ,", CSLT_PRESERVEQUOTES));
ASSERT_EQ(aosStringList.size(), 7);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "\"three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "\""));
ASSERT_TRUE(EQUAL(aosStringList[5], "five"));
ASSERT_TRUE(EQUAL(aosStringList[6], "six"));
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one two,\"three,four ,\",five,six", " ,",
CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "\"three,four ,\""));
ASSERT_TRUE(EQUAL(aosStringList[3], "five"));
ASSERT_TRUE(EQUAL(aosStringList[4], "six"));
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
CSLT_PRESERVEESCAPES));
ASSERT_EQ(aosStringList.size(), 7);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "\\two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "\"three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "\\four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "\""));
ASSERT_TRUE(EQUAL(aosStringList[5], "five"));
ASSERT_TRUE(EQUAL(aosStringList[6], "six"));
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
CSLT_PRESERVEQUOTES | CSLT_PRESERVEESCAPES));
ASSERT_EQ(aosStringList.size(), 7);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "\\two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "\"three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "\\four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "\""));
ASSERT_TRUE(EQUAL(aosStringList[5], "five"));
ASSERT_TRUE(EQUAL(aosStringList[6], "six"));
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one ,two, three, four ,five ", ",", 0));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one "));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], " three"));
ASSERT_TRUE(EQUAL(aosStringList[3], " four "));
ASSERT_TRUE(EQUAL(aosStringList[4], "five "));
}
{
CPLStringList aosStringList(CSLTokenizeString2(
"one ,two, three, four ,five ", ",", CSLT_STRIPLEADSPACES));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one "));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "four "));
ASSERT_TRUE(EQUAL(aosStringList[4], "five "));
}
{
CPLStringList aosStringList(CSLTokenizeString2(
"one ,two, three, four ,five ", ",", CSLT_STRIPENDSPACES));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], " three"));
ASSERT_TRUE(EQUAL(aosStringList[3], " four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "five"));
}
{
CPLStringList aosStringList(
CSLTokenizeString2("one ,two, three, four ,five ", ",",
CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
ASSERT_EQ(aosStringList.size(), 5);
ASSERT_TRUE(EQUAL(aosStringList[0], "one"));
ASSERT_TRUE(EQUAL(aosStringList[1], "two"));
ASSERT_TRUE(EQUAL(aosStringList[2], "three"));
ASSERT_TRUE(EQUAL(aosStringList[3], "four"));
ASSERT_TRUE(EQUAL(aosStringList[4], "five"));
}
}
typedef struct
{
char szEncoding[24];
char szString[1024 - 24];
} TestRecodeStruct;
// Test cpl_recode API
TEST_F(test_cpl, CPLRecode)
{
/*
* NOTE: This test will generally fail if iconv() is not
* linked in.
*
* CPLRecode() will be tested using the test file containing
* a list of strings of the same text in different encoding. The
* string is non-ASCII to avoid trivial transformations. Test file
* has a simple binary format: a table of records, each record
* is 1024 bytes long. The first 24 bytes of each record contain
* encoding name (ASCII, zero padded), the last 1000 bytes contain
* encoded string, zero padded.
*
* NOTE 1: We can't use a test file in human readable text format
* here because of multiple different encodings including
* multibyte ones.
*
* The test file could be generated with the following simple shell
* script:
*
* #!/bin/sh
*
* # List of encodings to convert the test string into
* ENCODINGS="UTF-8 CP1251 KOI8-R UCS-2 UCS-2BE UCS-2LE UCS-4 UCS-4BE
* UCS-4LE UTF-16 UTF-32" # The test string itself in UTF-8 encoding. # This
* means "Improving GDAL internationalization." in Russian.
* TESTSTRING="\u0423\u043b\u0443\u0447\u0448\u0430\u0435\u043c
* \u0438\u043d\u0442\u0435\u0440\u043d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e
* GDAL."
*
* RECORDSIZE=1024
* ENCSIZE=24
*
* i=0
* for enc in ${ENCODINGS}; do
* env printf "${enc}" | dd ibs=${RECORDSIZE} conv=sync obs=1
* seek=$((${RECORDSIZE}*${i})) of="recode-rus.dat" status=noxfer env printf
* "${TESTSTRING}" | iconv -t ${enc} | dd ibs=${RECORDSIZE} conv=sync obs=1
* seek=$((${RECORDSIZE}*${i}+${ENCSIZE})) of="recode-rus.dat" status=noxfer
* i=$((i+1))
* done
*
* NOTE 2: The test string is encoded with the special format
* "\uXXXX" sequences, so we able to paste it here.
*
* NOTE 3: We need a printf utility from the coreutils because of
* that. "env printf" should work avoiding the shell
* built-in.
*
* NOTE 4: "iconv" utility without the "-f" option will work with
* encoding read from the current locale.
*
* TODO: 1. Add more encodings maybe more test files.
* 2. Add test for CPLRecodeFromWChar()/CPLRecodeToWChar().
* 3. Test translation between each possible pair of
* encodings in file, not only into the UTF-8.
*/
std::ifstream fin((data_ + SEP + "recode-rus.dat").c_str(),
std::ifstream::binary);
TestRecodeStruct oReferenceString;
// Read reference string (which is the first one in the file)
fin.read(oReferenceString.szEncoding, sizeof(oReferenceString.szEncoding));
oReferenceString.szEncoding[sizeof(oReferenceString.szEncoding) - 1] = '\0';
fin.read(oReferenceString.szString, sizeof(oReferenceString.szString));
oReferenceString.szString[sizeof(oReferenceString.szString) - 1] = '\0';
while (true)
{
TestRecodeStruct oTestString;
fin.read(oTestString.szEncoding, sizeof(oTestString.szEncoding));
oTestString.szEncoding[sizeof(oTestString.szEncoding) - 1] = '\0';
if (fin.eof())
break;
fin.read(oTestString.szString, sizeof(oTestString.szString));
oTestString.szString[sizeof(oTestString.szString) - 1] = '\0';
// Compare each string with the reference one
CPLErrorReset();
char *pszDecodedString =
CPLRecode(oTestString.szString, oTestString.szEncoding,
oReferenceString.szEncoding);
if (strstr(CPLGetLastErrorMsg(),
"Recode from CP1251 to UTF-8 not supported") != nullptr ||
strstr(CPLGetLastErrorMsg(),
"Recode from KOI8-R to UTF-8 not supported") != nullptr)
{
CPLFree(pszDecodedString);
break;
}
size_t nLength =
MIN(strlen(pszDecodedString), sizeof(oReferenceString.szEncoding));
bool bOK =
(memcmp(pszDecodedString, oReferenceString.szString, nLength) == 0);
// FIXME Some tests fail on Mac. Not sure why, but do not error out just
// for that
if (!bOK &&
(strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") !=
nullptr ||
strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
{
fprintf(stderr, "Recode from %s failed\n", oTestString.szEncoding);
}
else
{
#ifdef CPL_MSB
if (!bOK && strcmp(oTestString.szEncoding, "UCS-2") == 0)
{
// Presumably the content in the test file is UCS-2LE, but
// there's no way to know the byte order without a BOM
fprintf(stderr, "Recode from %s failed\n",
oTestString.szEncoding);
}
else
#endif
{
EXPECT_TRUE(bOK) << "Recode from " << oTestString.szEncoding;
}
}
CPLFree(pszDecodedString);
}
fin.close();
}
/************************************************************************/
/* CPLStringList tests */
/************************************************************************/
TEST_F(test_cpl, CPLStringList_Base)
{
CPLStringList oCSL;
ASSERT_TRUE(oCSL.List() == nullptr);
oCSL.AddString("def");
oCSL.AddString("abc");
ASSERT_EQ(oCSL.Count(), 2);
ASSERT_TRUE(EQUAL(oCSL[0], "def"));
ASSERT_TRUE(EQUAL(oCSL[1], "abc"));
ASSERT_TRUE(oCSL[17] == nullptr);
ASSERT_TRUE(oCSL[-1] == nullptr);
ASSERT_EQ(oCSL.FindString("abc"), 1);
CSLDestroy(oCSL.StealList());
ASSERT_EQ(oCSL.Count(), 0);
ASSERT_TRUE(oCSL.List() == nullptr);
// Test that the list will make an internal copy when needed to
// modify a read-only list.
oCSL.AddString("def");
oCSL.AddString("abc");
CPLStringList oCopy(oCSL.List(), FALSE);
ASSERT_EQ(oCSL.List(), oCopy.List());
ASSERT_EQ(oCSL.Count(), oCopy.Count());
oCopy.AddString("xyz");
ASSERT_TRUE(oCSL.List() != oCopy.List());
ASSERT_EQ(oCopy.Count(), 3);
ASSERT_EQ(oCSL.Count(), 2);
ASSERT_TRUE(EQUAL(oCopy[2], "xyz"));
}
TEST_F(test_cpl, CPLStringList_NameValue)
{
// Test some name=value handling stuff.
CPLStringList oNVL;
oNVL.AddNameValue("KEY1", "VALUE1");
oNVL.AddNameValue("2KEY", "VALUE2");
ASSERT_EQ(oNVL.Count(), 2);
ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE2"));
ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
oNVL.AddNameValue("KEY1", "VALUE3");
ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("KEY1"), "VALUE1"));
ASSERT_TRUE(EQUAL(oNVL[2], "KEY1=VALUE3"));
ASSERT_TRUE(EQUAL(oNVL.FetchNameValueDef("MISSING", "X"), "X"));
oNVL.SetNameValue("2KEY", "VALUE4");
ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE4"));
ASSERT_EQ(oNVL.Count(), 3);
// make sure deletion works.
oNVL.SetNameValue("2KEY", nullptr);
ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
ASSERT_EQ(oNVL.Count(), 2);
// Test boolean support.
ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), FALSE);
oNVL.SetNameValue("BOOL", "YES");
ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
oNVL.SetNameValue("BOOL", "1");
ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
oNVL.SetNameValue("BOOL", "0");
ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
oNVL.SetNameValue("BOOL", "FALSE");
ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
oNVL.SetNameValue("BOOL", "ON");
ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
// Test assignment operator.
CPLStringList oCopy;
{
CPLStringList oTemp;
oTemp.AddString("test");
oCopy = oTemp;
}
EXPECT_STREQ(oCopy[0], "test");
auto &oCopyRef(oCopy);
oCopy = oCopyRef;
EXPECT_STREQ(oCopy[0], "test");
// Test copy constructor.
CPLStringList oCopy2(oCopy);
EXPECT_EQ(oCopy2.Count(), oCopy.Count());
oCopy.Clear();
EXPECT_STREQ(oCopy2[0], "test");
// Test move constructor
CPLStringList oMoved(std::move(oCopy2));
EXPECT_STREQ(oMoved[0], "test");
// Test move assignment operator
CPLStringList oMoved2;
oMoved2 = std::move(oMoved);
EXPECT_STREQ(oMoved2[0], "test");
// Test sorting
CPLStringList oTestSort;
oTestSort.AddNameValue("Z", "1");
oTestSort.AddNameValue("L", "2");
oTestSort.AddNameValue("T", "3");
oTestSort.AddNameValue("A", "4");
oTestSort.Sort();
EXPECT_STREQ(oTestSort[0], "A=4");
EXPECT_STREQ(oTestSort[1], "L=2");
EXPECT_STREQ(oTestSort[2], "T=3");
EXPECT_STREQ(oTestSort[3], "Z=1");
ASSERT_EQ(oTestSort[4], (const char *)nullptr);
// Test FetchNameValue() in a sorted list
EXPECT_STREQ(oTestSort.FetchNameValue("A"), "4");
EXPECT_STREQ(oTestSort.FetchNameValue("L"), "2");
EXPECT_STREQ(oTestSort.FetchNameValue("T"), "3");
EXPECT_STREQ(oTestSort.FetchNameValue("Z"), "1");
// Test AddNameValue() in a sorted list
oTestSort.AddNameValue("B", "5");
EXPECT_STREQ(oTestSort[0], "A=4");
EXPECT_STREQ(oTestSort[1], "B=5");
EXPECT_STREQ(oTestSort[2], "L=2");
EXPECT_STREQ(oTestSort[3], "T=3");
EXPECT_STREQ(oTestSort[4], "Z=1");
ASSERT_EQ(oTestSort[5], (const char *)nullptr);
// Test SetNameValue() of an existing item in a sorted list
oTestSort.SetNameValue("Z", "6");
EXPECT_STREQ(oTestSort[4], "Z=6");
// Test SetNameValue() of a non-existing item in a sorted list
oTestSort.SetNameValue("W", "7");
EXPECT_STREQ(oTestSort[0], "A=4");
EXPECT_STREQ(oTestSort[1], "B=5");
EXPECT_STREQ(oTestSort[2], "L=2");
EXPECT_STREQ(oTestSort[3], "T=3");
EXPECT_STREQ(oTestSort[4], "W=7");
EXPECT_STREQ(oTestSort[5], "Z=6");
ASSERT_EQ(oTestSort[6], (const char *)nullptr);
}
TEST_F(test_cpl, CPLStringList_Sort)
{
// Test some name=value handling stuff *with* sorting active.
CPLStringList oNVL;
oNVL.Sort();
oNVL.AddNameValue("KEY1", "VALUE1");
oNVL.AddNameValue("2KEY", "VALUE2");
ASSERT_EQ(oNVL.Count(), 2);
EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE2");
ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
oNVL.AddNameValue("KEY1", "VALUE3");
ASSERT_EQ(oNVL.Count(), 3);
EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
EXPECT_STREQ(oNVL.FetchNameValueDef("MISSING", "X"), "X");
oNVL.SetNameValue("2KEY", "VALUE4");
EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE4");
ASSERT_EQ(oNVL.Count(), 3);
// make sure deletion works.
oNVL.SetNameValue("2KEY", nullptr);
ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
ASSERT_EQ(oNVL.Count(), 2);
// Test insertion logic pretty carefully.
oNVL.Clear();
ASSERT_TRUE(oNVL.IsSorted() == TRUE);
oNVL.SetNameValue("B", "BB");
oNVL.SetNameValue("A", "AA");
oNVL.SetNameValue("D", "DD");
oNVL.SetNameValue("C", "CC");
// items should be in sorted order.
EXPECT_STREQ(oNVL[0], "A=AA");
EXPECT_STREQ(oNVL[1], "B=BB");
EXPECT_STREQ(oNVL[2], "C=CC");
EXPECT_STREQ(oNVL[3], "D=DD");
EXPECT_STREQ(oNVL.FetchNameValue("A"), "AA");
EXPECT_STREQ(oNVL.FetchNameValue("B"), "BB");
EXPECT_STREQ(oNVL.FetchNameValue("C"), "CC");
EXPECT_STREQ(oNVL.FetchNameValue("D"), "DD");
}
TEST_F(test_cpl, CPL_HMAC_SHA256)
{
GByte abyDigest[CPL_SHA256_HASH_SIZE];
char szDigest[2 * CPL_SHA256_HASH_SIZE + 1];
CPL_HMAC_SHA256("key", 3, "The quick brown fox jumps over the lazy dog",
strlen("The quick brown fox jumps over the lazy dog"),
abyDigest);
for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
abyDigest[i]);
// fprintf(stderr, "%s\n", szDigest);
EXPECT_STREQ(
szDigest,
"f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
CPL_HMAC_SHA256(
"mysupersupersupersupersupersupersupersupersupersupersupersupersupersup"
"ersupersupersupersupersupersuperlongkey",
strlen("mysupersupersupersupersupersupersupersupersupersupersupersupers"
"upersupersupersupersupersupersupersuperlongkey"),
"msg", 3, abyDigest);
for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
abyDigest[i]);
// fprintf(stderr, "%s\n", szDigest);
EXPECT_STREQ(
szDigest,
"a3051520761ed3cb43876b35ce2dd93ac5b332dc3bad898bb32086f7ac71ffc1");
}
TEST_F(test_cpl, VSIMalloc)
{
CPLPushErrorHandler(CPLQuietErrorHandler);
// The following tests will fail because of overflows
CPLErrorReset();
ASSERT_TRUE(VSIMalloc2(~(size_t)0, ~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSIMalloc3(1, ~(size_t)0, ~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSIMalloc3(~(size_t)0, 1, ~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSIMalloc3(~(size_t)0, ~(size_t)0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
{
// The following tests will fail because such allocations cannot succeed
#if SIZEOF_VOIDP == 8
CPLErrorReset();
ASSERT_TRUE(VSIMalloc(~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() == CE_None); /* no error reported */
CPLErrorReset();
ASSERT_TRUE(VSIMalloc2(~(size_t)0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSIMalloc3(~(size_t)0, 1, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSICalloc(~(size_t)0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() == CE_None); /* no error reported */
CPLErrorReset();
ASSERT_TRUE(VSIRealloc(nullptr, ~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() == CE_None); /* no error reported */
CPLErrorReset();
ASSERT_TRUE(VSI_MALLOC_VERBOSE(~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSI_MALLOC2_VERBOSE(~(size_t)0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSI_MALLOC3_VERBOSE(~(size_t)0, 1, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSI_CALLOC_VERBOSE(~(size_t)0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
CPLErrorReset();
ASSERT_TRUE(VSI_REALLOC_VERBOSE(nullptr, ~(size_t)0) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() != CE_None);
#endif
}
CPLPopErrorHandler();
// The following allocs will return NULL because of 0 byte alloc
CPLErrorReset();
ASSERT_TRUE(VSIMalloc2(0, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() == CE_None);
ASSERT_TRUE(VSIMalloc2(1, 0) == nullptr);
CPLErrorReset();
ASSERT_TRUE(VSIMalloc3(0, 1, 1) == nullptr);
ASSERT_TRUE(CPLGetLastErrorType() == CE_None);
ASSERT_TRUE(VSIMalloc3(1, 0, 1) == nullptr);
ASSERT_TRUE(VSIMalloc3(1, 1, 0) == nullptr);
}
TEST_F(test_cpl, CPLFormFilename)
{
EXPECT_TRUE(strcmp(CPLFormFilename("a", "b", nullptr), "a/b") == 0 ||
strcmp(CPLFormFilename("a", "b", nullptr), "a\\b") == 0);
EXPECT_TRUE(strcmp(CPLFormFilename("a/", "b", nullptr), "a/b") == 0 ||
strcmp(CPLFormFilename("a/", "b", nullptr), "a\\b") == 0);
EXPECT_TRUE(strcmp(CPLFormFilename("a\\", "b", nullptr), "a/b") == 0 ||
strcmp(CPLFormFilename("a\\", "b", nullptr), "a\\b") == 0);
EXPECT_STREQ(CPLFormFilename(nullptr, "a", "b"), "a.b");
EXPECT_STREQ(CPLFormFilename(nullptr, "a", ".b"), "a.b");
EXPECT_STREQ(CPLFormFilename("/a", "..", nullptr), "/");
EXPECT_STREQ(CPLFormFilename("/a/", "..", nullptr), "/");
EXPECT_STREQ(CPLFormFilename("/a/b", "..", nullptr), "/a");
EXPECT_STREQ(CPLFormFilename("/a/b/", "..", nullptr), "/a");
EXPECT_TRUE(EQUAL(CPLFormFilename("c:", "..", nullptr), "c:/..") ||
EQUAL(CPLFormFilename("c:", "..", nullptr), "c:\\.."));
EXPECT_TRUE(EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:/..") ||
EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:\\.."));
EXPECT_STREQ(CPLFormFilename("c:\\a", "..", nullptr), "c:");
EXPECT_STREQ(CPLFormFilename("c:\\a\\", "..", nullptr), "c:");
EXPECT_STREQ(CPLFormFilename("c:\\a\\b", "..", nullptr), "c:\\a");
EXPECT_STREQ(CPLFormFilename("\\\\$\\c:\\a", "..", nullptr), "\\\\$\\c:");
EXPECT_TRUE(
EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:/..") ||
EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:\\.."));
}
TEST_F(test_cpl, VSIGetDiskFreeSpace)
{
ASSERT_TRUE(VSIGetDiskFreeSpace("/vsimem/") > 0);
ASSERT_TRUE(VSIGetDiskFreeSpace(".") == -1 ||
VSIGetDiskFreeSpace(".") >= 0);
}
TEST_F(test_cpl, CPLsscanf)
{
double a, b, c;
a = b = 0;
ASSERT_EQ(CPLsscanf("1 2", "%lf %lf", &a, &b), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
a = b = 0;
ASSERT_EQ(CPLsscanf("1\t2", "%lf %lf", &a, &b), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
a = b = 0;
ASSERT_EQ(CPLsscanf("1 2", "%lf\t%lf", &a, &b), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
a = b = 0;
ASSERT_EQ(CPLsscanf("1 2", "%lf %lf", &a, &b), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
a = b = 0;
ASSERT_EQ(CPLsscanf("1 2", "%lf %lf", &a, &b), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
a = b = c = 0;
ASSERT_EQ(CPLsscanf("1 2", "%lf %lf %lf", &a, &b, &c), 2);
ASSERT_EQ(a, 1.0);
ASSERT_EQ(b, 2.0);
}
TEST_F(test_cpl, CPLSetErrorHandler)
{
CPLString oldVal = CPLGetConfigOption("CPL_DEBUG", "");
CPLSetConfigOption("CPL_DEBUG", "TEST");
CPLErrorHandler oldHandler = CPLSetErrorHandler(myErrorHandler);
gbGotError = false;
CPLDebug("TEST", "Test");
ASSERT_EQ(gbGotError, true);
gbGotError = false;
CPLSetErrorHandler(oldHandler);
CPLPushErrorHandler(myErrorHandler);
gbGotError = false;
CPLDebug("TEST", "Test");
ASSERT_EQ(gbGotError, true);
gbGotError = false;
CPLPopErrorHandler();
oldHandler = CPLSetErrorHandler(myErrorHandler);
CPLSetCurrentErrorHandlerCatchDebug(FALSE);
gbGotError = false;
CPLDebug("TEST", "Test");
ASSERT_EQ(gbGotError, false);
gbGotError = false;
CPLSetErrorHandler(oldHandler);
CPLPushErrorHandler(myErrorHandler);
CPLSetCurrentErrorHandlerCatchDebug(FALSE);
gbGotError = false;
CPLDebug("TEST", "Test");
ASSERT_EQ(gbGotError, false);
gbGotError = false;
CPLPopErrorHandler();
CPLSetConfigOption("CPL_DEBUG", oldVal.size() ? oldVal.c_str() : nullptr);
oldHandler = CPLSetErrorHandler(nullptr);
CPLDebug("TEST", "Test");
CPLError(CE_Failure, CPLE_AppDefined, "test");
CPLErrorHandler newOldHandler = CPLSetErrorHandler(nullptr);
ASSERT_EQ(newOldHandler, static_cast<CPLErrorHandler>(nullptr));
CPLDebug("TEST", "Test");
CPLError(CE_Failure, CPLE_AppDefined, "test");
CPLSetErrorHandler(oldHandler);
}
/************************************************************************/
/* CPLString::replaceAll() */
/************************************************************************/
TEST_F(test_cpl, CPLString_replaceAll)
{
CPLString osTest;
osTest = "foobarbarfoo";
osTest.replaceAll("bar", "was_bar");
ASSERT_EQ(osTest, "foowas_barwas_barfoo");
osTest = "foobarbarfoo";
osTest.replaceAll("X", "was_bar");
ASSERT_EQ(osTest, "foobarbarfoo");
osTest = "foobarbarfoo";
osTest.replaceAll("", "was_bar");
ASSERT_EQ(osTest, "foobarbarfoo");
osTest = "foobarbarfoo";
osTest.replaceAll("bar", "");
ASSERT_EQ(osTest, "foofoo");
osTest = "foobarbarfoo";
osTest.replaceAll('b', 'B');
ASSERT_EQ(osTest, "fooBarBarfoo");
osTest = "foobarbarfoo";
osTest.replaceAll('b', "B");
ASSERT_EQ(osTest, "fooBarBarfoo");
osTest = "foobarbarfoo";
osTest.replaceAll("b", 'B');
ASSERT_EQ(osTest, "fooBarBarfoo");
}
/************************************************************************/
/* VSIMallocAligned() */
/************************************************************************/
TEST_F(test_cpl, VSIMallocAligned)
{
GByte *ptr = static_cast<GByte *>(VSIMallocAligned(sizeof(void *), 1));
ASSERT_TRUE(ptr != nullptr);
ASSERT_TRUE(((size_t)ptr % sizeof(void *)) == 0);
*ptr = 1;
VSIFreeAligned(ptr);
ptr = static_cast<GByte *>(VSIMallocAligned(16, 1));
ASSERT_TRUE(ptr != nullptr);
ASSERT_TRUE(((size_t)ptr % 16) == 0);
*ptr = 1;
VSIFreeAligned(ptr);
VSIFreeAligned(nullptr);
#ifndef WIN32
// Illegal use of API. Returns non NULL on Windows
ptr = static_cast<GByte *>(VSIMallocAligned(2, 1));
EXPECT_TRUE(ptr == nullptr);
VSIFree(ptr);
// Illegal use of API. Crashes on Windows
ptr = static_cast<GByte *>(VSIMallocAligned(5, 1));
EXPECT_TRUE(ptr == nullptr);
VSIFree(ptr);
#endif
if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
{
// The following tests will fail because such allocations cannot succeed
#if SIZEOF_VOIDP == 8
ptr = static_cast<GByte *>(
VSIMallocAligned(sizeof(void *), ~((size_t)0)));
EXPECT_TRUE(ptr == nullptr);
VSIFree(ptr);
ptr = static_cast<GByte *>(
VSIMallocAligned(sizeof(void *), (~((size_t)0)) - sizeof(void *)));
EXPECT_TRUE(ptr == nullptr);
VSIFree(ptr);
#endif
}
}
/************************************************************************/
/* CPLGetConfigOptions() / CPLSetConfigOptions() */
/************************************************************************/
TEST_F(test_cpl, CPLGetConfigOptions)
{
CPLSetConfigOption("FOOFOO", "BAR");
char **options = CPLGetConfigOptions();
EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
CPLSetConfigOptions(nullptr);
EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "i_dont_exist");
CPLSetConfigOptions(options);
EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "BAR");
CSLDestroy(options);
}
/************************************************************************/
/* CPLGetThreadLocalConfigOptions() / CPLSetThreadLocalConfigOptions() */
/************************************************************************/
TEST_F(test_cpl, CPLGetThreadLocalConfigOptions)
{
CPLSetThreadLocalConfigOption("FOOFOO", "BAR");
char **options = CPLGetThreadLocalConfigOptions();
EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
CPLSetThreadLocalConfigOptions(nullptr);
EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
"i_dont_exist");
CPLSetThreadLocalConfigOptions(options);
EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
"BAR");
CSLDestroy(options);
}
TEST_F(test_cpl, CPLExpandTilde)
{
EXPECT_STREQ(CPLExpandTilde("/foo/bar"), "/foo/bar");
CPLSetConfigOption("HOME", "/foo");
ASSERT_TRUE(EQUAL(CPLExpandTilde("~/bar"), "/foo/bar") ||
EQUAL(CPLExpandTilde("~/bar"), "/foo\\bar"));
CPLSetConfigOption("HOME", nullptr);
}
TEST_F(test_cpl, CPLString_constructors)
{
// CPLString(std::string) constructor
ASSERT_STREQ(CPLString(std::string("abc")).c_str(), "abc");
// CPLString(const char*) constructor
ASSERT_STREQ(CPLString("abc").c_str(), "abc");
// CPLString(const char*, n) constructor
ASSERT_STREQ(CPLString("abc", 1).c_str(), "a");
}
TEST_F(test_cpl, CPLErrorSetState)
{
// NOTE: Assumes cpl_error.cpp defines DEFAULT_LAST_ERR_MSG_SIZE=500
char pszMsg[] = "0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"1abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"2abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"3abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"4abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"5abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"6abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"7abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"8abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
"9abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|" // 500
"0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|" // 550
;
CPLErrorReset();
CPLErrorSetState(CE_Warning, 1, pszMsg);
ASSERT_EQ(strlen(pszMsg) - 50 - 1, // length - 50 - 1 (null-terminator)
strlen(CPLGetLastErrorMsg())); // DEFAULT_LAST_ERR_MSG_SIZE - 1
}
TEST_F(test_cpl, CPLUnescapeString)
{
char *pszText = CPLUnescapeString(
"&lt;&gt;&amp;&apos;&quot;&#x3f;&#x3F;&#63;", nullptr, CPLES_XML);
EXPECT_STREQ(pszText, "<>&'\"???");
CPLFree(pszText);
// Integer overflow
pszText = CPLUnescapeString("&10000000000000000;", nullptr, CPLES_XML);
// We do not really care about the return value
CPLFree(pszText);
// Integer overflow
pszText = CPLUnescapeString("&#10000000000000000;", nullptr, CPLES_XML);
// We do not really care about the return value
CPLFree(pszText);
// Error case
pszText = CPLUnescapeString("&foo", nullptr, CPLES_XML);
EXPECT_STREQ(pszText, "");
CPLFree(pszText);
// Error case
pszText = CPLUnescapeString("&#x", nullptr, CPLES_XML);
EXPECT_STREQ(pszText, "");
CPLFree(pszText);
// Error case
pszText = CPLUnescapeString("&#", nullptr, CPLES_XML);
EXPECT_STREQ(pszText, "");
CPLFree(pszText);
}
// Test signed int safe maths
TEST_F(test_cpl, CPLSM_signed)
{
ASSERT_EQ((CPLSM(-2) + CPLSM(3)).v(), 1);
ASSERT_EQ((CPLSM(-2) + CPLSM(1)).v(), -1);
ASSERT_EQ((CPLSM(-2) + CPLSM(-1)).v(), -3);
ASSERT_EQ((CPLSM(2) + CPLSM(-3)).v(), -1);
ASSERT_EQ((CPLSM(2) + CPLSM(-1)).v(), 1);
ASSERT_EQ((CPLSM(2) + CPLSM(1)).v(), 3);
ASSERT_EQ((CPLSM(INT_MAX - 1) + CPLSM(1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(1) + CPLSM(INT_MAX - 1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(INT_MAX) + CPLSM(-1)).v(), INT_MAX - 1);
ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MAX)).v(), INT_MAX - 1);
ASSERT_EQ((CPLSM(INT_MIN + 1) + CPLSM(-1)).v(), INT_MIN);
ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MIN + 1)).v(), INT_MIN);
try
{
(CPLSM(INT_MAX) + CPLSM(1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(1) + CPLSM(INT_MAX)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(INT_MIN) + CPLSM(-1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(-1) + CPLSM(INT_MIN)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(-2) - CPLSM(1)).v(), -3);
ASSERT_EQ((CPLSM(-2) - CPLSM(-1)).v(), -1);
ASSERT_EQ((CPLSM(-2) - CPLSM(-3)).v(), 1);
ASSERT_EQ((CPLSM(2) - CPLSM(-1)).v(), 3);
ASSERT_EQ((CPLSM(2) - CPLSM(1)).v(), 1);
ASSERT_EQ((CPLSM(2) - CPLSM(3)).v(), -1);
ASSERT_EQ((CPLSM(INT_MAX) - CPLSM(1)).v(), INT_MAX - 1);
ASSERT_EQ((CPLSM(INT_MIN + 1) - CPLSM(1)).v(), INT_MIN);
ASSERT_EQ((CPLSM(0) - CPLSM(INT_MIN + 1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(0) - CPLSM(INT_MAX)).v(), -INT_MAX);
try
{
(CPLSM(INT_MIN) - CPLSM(1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(0) - CPLSM(INT_MIN)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(INT_MIN) - CPLSM(1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(INT_MIN + 1) * CPLSM(-1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(-1) * CPLSM(INT_MIN + 1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(1)).v(), INT_MIN);
ASSERT_EQ((CPLSM(1) * CPLSM(INT_MIN)).v(), INT_MIN);
ASSERT_EQ((CPLSM(1) * CPLSM(INT_MAX)).v(), INT_MAX);
ASSERT_EQ((CPLSM(INT_MIN / 2) * CPLSM(2)).v(), INT_MIN);
ASSERT_EQ((CPLSM(INT_MAX / 2) * CPLSM(2)).v(), INT_MAX - 1);
ASSERT_EQ((CPLSM(INT_MAX / 2 + 1) * CPLSM(-2)).v(), INT_MIN);
ASSERT_EQ((CPLSM(0) * CPLSM(INT_MIN)).v(), 0);
ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(0)).v(), 0);
ASSERT_EQ((CPLSM(0) * CPLSM(INT_MAX)).v(), 0);
ASSERT_EQ((CPLSM(INT_MAX) * CPLSM(0)).v(), 0);
try
{
(CPLSM(INT_MAX / 2 + 1) * CPLSM(2)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(2) * CPLSM(INT_MAX / 2 + 1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(INT_MIN) * CPLSM(-1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(INT_MIN) * CPLSM(2)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(2) * CPLSM(INT_MIN)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(4) / CPLSM(2)).v(), 2);
ASSERT_EQ((CPLSM(4) / CPLSM(-2)).v(), -2);
ASSERT_EQ((CPLSM(-4) / CPLSM(2)).v(), -2);
ASSERT_EQ((CPLSM(-4) / CPLSM(-2)).v(), 2);
ASSERT_EQ((CPLSM(0) / CPLSM(2)).v(), 0);
ASSERT_EQ((CPLSM(0) / CPLSM(-2)).v(), 0);
ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(1)).v(), INT_MAX);
ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(-1)).v(), -INT_MAX);
ASSERT_EQ((CPLSM(INT_MIN) / CPLSM(1)).v(), INT_MIN);
try
{
(CPLSM(-1) * CPLSM(INT_MIN)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(INT_MIN) / CPLSM(-1)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(1) / CPLSM(0)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ(CPLSM_TO_UNSIGNED(1).v(), 1U);
try
{
CPLSM_TO_UNSIGNED(-1);
ASSERT_TRUE(false);
}
catch (...)
{
}
}
// Test unsigned int safe maths
TEST_F(test_cpl, CPLSM_unsigned)
{
ASSERT_EQ((CPLSM(2U) + CPLSM(3U)).v(), 5U);
ASSERT_EQ((CPLSM(UINT_MAX - 1) + CPLSM(1U)).v(), UINT_MAX);
try
{
(CPLSM(UINT_MAX) + CPLSM(1U)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(4U) - CPLSM(3U)).v(), 1U);
ASSERT_EQ((CPLSM(4U) - CPLSM(4U)).v(), 0U);
ASSERT_EQ((CPLSM(UINT_MAX) - CPLSM(1U)).v(), UINT_MAX - 1);
try
{
(CPLSM(4U) - CPLSM(5U)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(0U) * CPLSM(UINT_MAX)).v(), 0U);
ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(0U)).v(), 0U);
ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(1U)).v(), UINT_MAX);
ASSERT_EQ((CPLSM(1U) * CPLSM(UINT_MAX)).v(), UINT_MAX);
try
{
(CPLSM(UINT_MAX) * CPLSM(2U)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
try
{
(CPLSM(2U) * CPLSM(UINT_MAX)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(4U) / CPLSM(2U)).v(), 2U);
ASSERT_EQ((CPLSM(UINT_MAX) / CPLSM(1U)).v(), UINT_MAX);
try
{
(CPLSM(1U) / CPLSM(0U)).v();
ASSERT_TRUE(false);
}
catch (...)
{
}
ASSERT_EQ((CPLSM(static_cast<GUInt64>(2) * 1000 * 1000 * 1000) +
CPLSM(static_cast<GUInt64>(3) * 1000 * 1000 * 1000))
.v(),
static_cast<GUInt64>(5) * 1000 * 1000 * 1000);
ASSERT_EQ((CPLSM(std::numeric_limits<GUInt64>::max() - 1) +
CPLSM(static_cast<GUInt64>(1)))
.v(),
std::numeric_limits<GUInt64>::max());
try
{
(CPLSM(std::numeric_limits<GUInt64>::max()) +
CPLSM(static_cast<GUInt64>(1)));
}
catch (...)
{
}
ASSERT_EQ((CPLSM(static_cast<GUInt64>(2) * 1000 * 1000 * 1000) *
CPLSM(static_cast<GUInt64>(3) * 1000 * 1000 * 1000))
.v(),
static_cast<GUInt64>(6) * 1000 * 1000 * 1000 * 1000 * 1000 *
1000);
ASSERT_EQ((CPLSM(std::numeric_limits<GUInt64>::max()) *
CPLSM(static_cast<GUInt64>(1)))
.v(),
std::numeric_limits<GUInt64>::max());
try
{
(CPLSM(std::numeric_limits<GUInt64>::max()) *
CPLSM(static_cast<GUInt64>(2)));
}
catch (...)
{
}
}
// Test CPLParseRFC822DateTime()
TEST_F(test_cpl, CPLParseRFC822DateTime)
{
int year, month, day, hour, min, sec, tz, weekday;
ASSERT_TRUE(!CPLParseRFC822DateTime("", &year, &month, &day, &hour, &min,
&sec, &tz, &weekday));
ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", nullptr,
nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, nullptr),
TRUE);
ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday),
TRUE);
ASSERT_EQ(year, 2017);
ASSERT_EQ(month, 1);
ASSERT_EQ(day, 15);
ASSERT_EQ(hour, 12);
ASSERT_EQ(min, 34);
ASSERT_EQ(sec, 56);
ASSERT_EQ(tz, 101);
ASSERT_EQ(weekday, 4);
ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday),
TRUE);
ASSERT_EQ(year, 2017);
ASSERT_EQ(month, 1);
ASSERT_EQ(day, 15);
ASSERT_EQ(hour, 12);
ASSERT_EQ(min, 34);
ASSERT_EQ(sec, 56);
ASSERT_EQ(tz, 100);
ASSERT_EQ(weekday, 4);
// Without day of week, second and timezone
ASSERT_EQ(CPLParseRFC822DateTime("15 Jan 2017 12:34", &year, &month, &day,
&hour, &min, &sec, &tz, &weekday),
TRUE);
ASSERT_EQ(year, 2017);
ASSERT_EQ(month, 1);
ASSERT_EQ(day, 15);
ASSERT_EQ(hour, 12);
ASSERT_EQ(min, 34);
ASSERT_EQ(sec, -1);
ASSERT_EQ(tz, 0);
ASSERT_EQ(weekday, 0);
ASSERT_EQ(CPLParseRFC822DateTime("XXX, 15 Jan 2017 12:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday),
TRUE);
ASSERT_EQ(weekday, 0);
ASSERT_TRUE(!CPLParseRFC822DateTime("Sun, 01 Jan 2017 12", &year, &month,
&day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("00 Jan 2017 12:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("32 Jan 2017 12:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 XXX 2017 12:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 -1:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 24:34:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:-1:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:60:56 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:-1 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:61 GMT", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 XXX", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +-100", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +9900", &year,
&month, &day, &hour, &min, &sec, &tz,
&weekday));
}
// Test CPLCopyTree()
TEST_F(test_cpl, CPLCopyTree)
{
CPLString osTmpPath(CPLGetDirname(CPLGenerateTempFilename(nullptr)));
CPLString osSrcDir(CPLFormFilename(osTmpPath, "src_dir", nullptr));
CPLString osNewDir(CPLFormFilename(osTmpPath, "new_dir", nullptr));
ASSERT_TRUE(VSIMkdir(osSrcDir, 0755) == 0);
CPLString osSrcFile(CPLFormFilename(osSrcDir, "my.bin", nullptr));
VSILFILE *fp = VSIFOpenL(osSrcFile, "wb");
ASSERT_TRUE(fp != nullptr);
VSIFCloseL(fp);
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(CPLCopyTree(osNewDir, "/i/do_not/exist") < 0);
CPLPopErrorHandler();
ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) == 0);
VSIStatBufL sStat;
CPLString osNewFile(CPLFormFilename(osNewDir, "my.bin", nullptr));
ASSERT_TRUE(VSIStatL(osNewFile, &sStat) == 0);
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) < 0);
CPLPopErrorHandler();
VSIUnlink(osNewFile);
VSIRmdir(osNewDir);
VSIUnlink(osSrcFile);
VSIRmdir(osSrcDir);
}
class CPLJSonStreamingParserDump : public CPLJSonStreamingParser
{
std::vector<bool> m_abFirstMember;
CPLString m_osSerialized;
CPLString m_osException;
public:
CPLJSonStreamingParserDump()
{
}
virtual void Reset() CPL_OVERRIDE
{
m_osSerialized.clear();
m_osException.clear();
CPLJSonStreamingParser::Reset();
}
virtual void String(const char *pszValue, size_t) CPL_OVERRIDE;
virtual void Number(const char *pszValue, size_t) CPL_OVERRIDE;
virtual void Boolean(bool bVal) CPL_OVERRIDE;
virtual void Null() CPL_OVERRIDE;
virtual void StartObject() CPL_OVERRIDE;
virtual void EndObject() CPL_OVERRIDE;
virtual void StartObjectMember(const char *pszKey, size_t) CPL_OVERRIDE;
virtual void StartArray() CPL_OVERRIDE;
virtual void EndArray() CPL_OVERRIDE;
virtual void StartArrayMember() CPL_OVERRIDE;
virtual void Exception(const char *pszMessage) CPL_OVERRIDE;
const CPLString &GetSerialized() const
{
return m_osSerialized;
}
const CPLString &GetException() const
{
return m_osException;
}
};
void CPLJSonStreamingParserDump::StartObject()
{
m_osSerialized += "{";
m_abFirstMember.push_back(true);
}
void CPLJSonStreamingParserDump::EndObject()
{
m_osSerialized += "}";
m_abFirstMember.pop_back();
}
void CPLJSonStreamingParserDump::StartObjectMember(const char *pszKey, size_t)
{
if (!m_abFirstMember.back())
m_osSerialized += ", ";
m_osSerialized += CPLSPrintf("\"%s\": ", pszKey);
m_abFirstMember.back() = false;
}
void CPLJSonStreamingParserDump::String(const char *pszValue, size_t)
{
m_osSerialized += GetSerializedString(pszValue);
}
void CPLJSonStreamingParserDump::Number(const char *pszValue, size_t)
{
m_osSerialized += pszValue;
}
void CPLJSonStreamingParserDump::Boolean(bool bVal)
{
m_osSerialized += bVal ? "true" : "false";
}
void CPLJSonStreamingParserDump::Null()
{
m_osSerialized += "null";
}
void CPLJSonStreamingParserDump::StartArray()
{
m_osSerialized += "[";
m_abFirstMember.push_back(true);
}
void CPLJSonStreamingParserDump::EndArray()
{
m_osSerialized += "]";
m_abFirstMember.pop_back();
}
void CPLJSonStreamingParserDump::StartArrayMember()
{
if (!m_abFirstMember.back())
m_osSerialized += ", ";
m_abFirstMember.back() = false;
}
void CPLJSonStreamingParserDump::Exception(const char *pszMessage)
{
m_osException = pszMessage;
}
// Test CPLJSonStreamingParser()
TEST_F(test_cpl, CPLJSonStreamingParser)
{
// nominal cases
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "true";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "false";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "null";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "10";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "123eE-34";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\\\a\\b\\f\\n\\r\\t\\u0020\\u0001\\\"\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(),
"\"\\\\a\\b\\f\\n\\r\\t \\u0001\\\"\"");
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(),
"\"\\\\a\\b\\f\\n\\r\\t \\u0001\\\"\"");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] =
"\"\\u0001\\u0020\\ud834\\uDD1E\\uDD1E\\uD834\\uD834\\uD834\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(
oParser.GetSerialized(),
"\"\\u0001 \xf0\x9d\x84\x9e\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\"");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\ud834\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), "\"\xef\xbf\xbd\"");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\ud834\\t\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), "\"\xef\xbf\xbd\\t\"");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\u00e9\"";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), "\"\xc3\xa9\"");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{}";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[]";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[[]]";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[1]";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[1,2]";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), "[1, 2]");
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), "[1, 2]");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{\"a\":null}";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), "{\"a\": null}");
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), "{\"a\": null}");
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] =
" { \"a\" : null ,\r\n\t\"b\": {\"c\": 1}, \"d\": [1] }";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
const char sExpected[] = "{\"a\": null, \"b\": {\"c\": 1}, \"d\": [1]}";
ASSERT_EQ(oParser.GetSerialized(), sExpected);
oParser.Reset();
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sExpected);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sExpected);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "infinity";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "-infinity";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "nan";
ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
ASSERT_EQ(oParser.GetSerialized(), sText);
oParser.Reset();
for (size_t i = 0; sText[i]; i++)
ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
ASSERT_EQ(oParser.GetSerialized(), sText);
}
// errors
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "tru";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "tru1";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "truxe";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "truex";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "fals";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "falsxe";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "falsex";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "nul";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "nulxl";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "nullx";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "na";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "nanx";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "infinit";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "infinityx";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "-infinit";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "-infinityx";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "true false";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "x";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "}";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[1";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[,";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[|";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "]";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ :";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ ,";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ |";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ 1";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\"";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\": ";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\": 1 2";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\", ";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\" }";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{\"a\" x}";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "1x";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\x\"";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\u";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\ux";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\u000";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\uD834\\ux\"";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"\\\"";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "\"too long\"";
oParser.SetMaxStringSize(2);
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[[]]";
oParser.SetMaxDepth(1);
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "{ \"x\": {} }";
oParser.SetMaxDepth(1);
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[,]";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[true,]";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[true,,true]";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
{
CPLJSonStreamingParserDump oParser;
const char sText[] = "[true true]";
ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
ASSERT_TRUE(!oParser.GetException().empty());
}
}
// Test cpl_mem_cache
TEST_F(test_cpl, cpl_mem_cache)
{
lru11::Cache<int, int> cache(2, 1);
ASSERT_EQ(cache.size(), 0U);
ASSERT_TRUE(cache.empty());
cache.clear();
int val;
ASSERT_TRUE(!cache.tryGet(0, val));
try
{
cache.get(0);
ASSERT_TRUE(false);
}
catch (const lru11::KeyNotFound &)
{
ASSERT_TRUE(true);
}
ASSERT_TRUE(!cache.remove(0));
ASSERT_TRUE(!cache.contains(0));
ASSERT_EQ(cache.getMaxSize(), 2U);
ASSERT_EQ(cache.getElasticity(), 1U);
ASSERT_EQ(cache.getMaxAllowedSize(), 3U);
int out;
ASSERT_TRUE(!cache.removeAndRecycleOldestEntry(out));
cache.insert(0, 1);
val = 0;
ASSERT_TRUE(cache.tryGet(0, val));
int *ptr = cache.getPtr(0);
ASSERT_TRUE(ptr);
ASSERT_EQ(*ptr, 1);
ASSERT_TRUE(cache.getPtr(-1) == nullptr);
ASSERT_EQ(val, 1);
ASSERT_EQ(cache.get(0), 1);
ASSERT_EQ(cache.getCopy(0), 1);
ASSERT_EQ(cache.size(), 1U);
ASSERT_TRUE(!cache.empty());
ASSERT_TRUE(cache.contains(0));
bool visited = false;
auto lambda = [&visited](const lru11::KeyValuePair<int, int> &kv)
{
if (kv.key == 0 && kv.value == 1)
visited = true;
};
cache.cwalk(lambda);
ASSERT_TRUE(visited);
out = -1;
ASSERT_TRUE(cache.removeAndRecycleOldestEntry(out));
ASSERT_EQ(out, 1);
cache.insert(0, 1);
cache.insert(0, 2);
ASSERT_EQ(cache.get(0), 2);
ASSERT_EQ(cache.size(), 1U);
cache.insert(1, 3);
cache.insert(2, 4);
ASSERT_EQ(cache.size(), 3U);
cache.insert(3, 5);
ASSERT_EQ(cache.size(), 2U);
ASSERT_TRUE(cache.contains(2));
ASSERT_TRUE(cache.contains(3));
ASSERT_TRUE(!cache.contains(0));
ASSERT_TRUE(!cache.contains(1));
ASSERT_TRUE(cache.remove(2));
ASSERT_TRUE(!cache.contains(2));
ASSERT_EQ(cache.size(), 1U);
{
// Check that MyObj copy constructor and copy-assignment operator
// are not needed
struct MyObj
{
int m_v;
MyObj(int v) : m_v(v)
{
}
MyObj(const MyObj &) = delete;
MyObj &operator=(const MyObj &) = delete;
MyObj(MyObj &&) = default;
MyObj &operator=(MyObj &&) = default;
};
lru11::Cache<int, MyObj> cacheMyObj(2, 0);
ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
cacheMyObj.getPtr(0);
ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
MyObj outObj(-1);
cacheMyObj.removeAndRecycleOldestEntry(outObj);
}
{
// Check that MyObj copy constructor and copy-assignment operator
// are not triggered
struct MyObj
{
int m_v;
MyObj(int v) : m_v(v)
{
}
static void should_not_happen()
{
ASSERT_TRUE(false);
}
MyObj(const MyObj &) : m_v(-1)
{
should_not_happen();
}
MyObj &operator=(const MyObj &)
{
should_not_happen();
return *this;
}
MyObj(MyObj &&) = default;
MyObj &operator=(MyObj &&) = default;
};
lru11::Cache<int, MyObj> cacheMyObj(2, 0);
ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
cacheMyObj.getPtr(0);
ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
MyObj outObj(-1);
cacheMyObj.removeAndRecycleOldestEntry(outObj);
}
}
// Test CPLJSONDocument
TEST_F(test_cpl, CPLJSONDocument)
{
{
// Test Json document LoadUrl
CPLJSONDocument oDocument;
const char *options[5] = {"CONNECTTIMEOUT=15", "TIMEOUT=20",
"MAX_RETRY=5", "RETRY_DELAY=1", nullptr};
oDocument.GetRoot().Add("foo", "bar");
if (CPLHTTPEnabled())
{
CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", "YES");
VSILFILE *fpTmp = VSIFOpenL("/vsimem/test.json", "wb");
const char *pszContent = "{ \"foo\": \"bar\" }";
VSIFWriteL(pszContent, 1, strlen(pszContent), fpTmp);
VSIFCloseL(fpTmp);
ASSERT_TRUE(oDocument.LoadUrl("/vsimem/test.json",
const_cast<char **>(options)));
CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", nullptr);
VSIUnlink("/vsimem/test.json");
CPLJSONObject oJsonRoot = oDocument.GetRoot();
ASSERT_TRUE(oJsonRoot.IsValid());
CPLString value = oJsonRoot.GetString("foo", "");
ASSERT_STRNE(value, "bar"); // not equal
}
}
{
// Test Json document LoadChunks
CPLJSONDocument oDocument;
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(!oDocument.LoadChunks("/i_do/not/exist", 512));
CPLPopErrorHandler();
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(!oDocument.LoadChunks("test_cpl.cpp", 512));
CPLPopErrorHandler();
oDocument.GetRoot().Add("foo", "bar");
ASSERT_TRUE(
oDocument.LoadChunks((data_ + SEP + "test.json").c_str(), 512));
CPLJSONObject oJsonRoot = oDocument.GetRoot();
ASSERT_TRUE(oJsonRoot.IsValid());
ASSERT_EQ(oJsonRoot.GetInteger("resource/id", 10), 0);
CPLJSONObject oJsonResource = oJsonRoot.GetObj("resource");
ASSERT_TRUE(oJsonResource.IsValid());
std::vector<CPLJSONObject> children = oJsonResource.GetChildren();
ASSERT_TRUE(children.size() == 11);
CPLJSONArray oaScopes = oJsonRoot.GetArray("resource/scopes");
ASSERT_TRUE(oaScopes.IsValid());
ASSERT_EQ(oaScopes.Size(), 2);
CPLJSONObject oHasChildren = oJsonRoot.GetObj("resource/children");
ASSERT_TRUE(oHasChildren.IsValid());
ASSERT_EQ(oHasChildren.ToBool(), true);
ASSERT_EQ(oJsonResource.GetBool("children", false), true);
CPLJSONObject oJsonId = oJsonRoot["resource/owner_user/id"];
ASSERT_TRUE(oJsonId.IsValid());
}
{
CPLJSONDocument oDocument;
ASSERT_TRUE(!oDocument.LoadMemory(nullptr, 0));
ASSERT_TRUE(!oDocument.LoadMemory(CPLString()));
ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
ASSERT_TRUE(oDocument.GetRoot().GetType() ==
CPLJSONObject::Type::Boolean);
ASSERT_TRUE(oDocument.GetRoot().ToBool());
ASSERT_TRUE(oDocument.LoadMemory(std::string("false")));
ASSERT_TRUE(oDocument.GetRoot().GetType() ==
CPLJSONObject::Type::Boolean);
ASSERT_TRUE(!oDocument.GetRoot().ToBool());
}
{
// Copy constructor
CPLJSONDocument oDocument;
ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
oDocument.GetRoot();
CPLJSONDocument oDocument2(oDocument);
CPLJSONObject oObj(oDocument.GetRoot());
ASSERT_TRUE(oObj.ToBool());
CPLJSONObject oObj2(oObj);
ASSERT_TRUE(oObj2.ToBool());
// Assignment operator
oDocument2 = oDocument;
auto &oDocument2Ref(oDocument2);
oDocument2 = oDocument2Ref;
oObj2 = oObj;
auto &oObj2Ref(oObj2);
oObj2 = oObj2Ref;
CPLJSONObject oObj3(std::move(oObj2));
ASSERT_TRUE(oObj3.ToBool());
CPLJSONObject oObj4;
oObj4 = std::move(oObj3);
ASSERT_TRUE(oObj4.ToBool());
}
{
// Move constructor
CPLJSONDocument oDocument;
oDocument.GetRoot();
CPLJSONDocument oDocument2(std::move(oDocument));
}
{
// Move assignment
CPLJSONDocument oDocument;
oDocument.GetRoot();
CPLJSONDocument oDocument2;
oDocument2 = std::move(oDocument);
}
{
// Save
CPLJSONDocument oDocument;
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(!oDocument.Save("/i_do/not/exist"));
CPLPopErrorHandler();
}
{
CPLJSONObject oObj;
oObj.Add("string", std::string("my_string"));
ASSERT_EQ(oObj.GetString("string"), std::string("my_string"));
ASSERT_EQ(oObj.GetString("inexisting_string", "default"),
std::string("default"));
oObj.Add("const_char_star", nullptr);
oObj.Add("const_char_star", "my_const_char_star");
ASSERT_TRUE(oObj.GetObj("const_char_star").GetType() ==
CPLJSONObject::Type::String);
oObj.Add("int", 1);
ASSERT_EQ(oObj.GetInteger("int"), 1);
ASSERT_EQ(oObj.GetInteger("inexisting_int", -987), -987);
ASSERT_TRUE(oObj.GetObj("int").GetType() ==
CPLJSONObject::Type::Integer);
oObj.Add("int64", GINT64_MAX);
ASSERT_EQ(oObj.GetLong("int64"), GINT64_MAX);
ASSERT_EQ(oObj.GetLong("inexisting_int64", GINT64_MIN), GINT64_MIN);
ASSERT_TRUE(oObj.GetObj("int64").GetType() ==
CPLJSONObject::Type::Long);
oObj.Add("double", 1.25);
ASSERT_EQ(oObj.GetDouble("double"), 1.25);
ASSERT_EQ(oObj.GetDouble("inexisting_double", -987.0), -987.0);
ASSERT_TRUE(oObj.GetObj("double").GetType() ==
CPLJSONObject::Type::Double);
oObj.Add("array", CPLJSONArray());
ASSERT_TRUE(oObj.GetObj("array").GetType() ==
CPLJSONObject::Type::Array);
oObj.Add("obj", CPLJSONObject());
ASSERT_TRUE(oObj.GetObj("obj").GetType() ==
CPLJSONObject::Type::Object);
oObj.Add("bool", true);
ASSERT_EQ(oObj.GetBool("bool"), true);
ASSERT_EQ(oObj.GetBool("inexisting_bool", false), false);
ASSERT_TRUE(oObj.GetObj("bool").GetType() ==
CPLJSONObject::Type::Boolean);
oObj.AddNull("null_field");
ASSERT_TRUE(oObj.GetObj("null_field").GetType() ==
CPLJSONObject::Type::Null);
ASSERT_TRUE(oObj.GetObj("inexisting").GetType() ==
CPLJSONObject::Type::Unknown);
oObj.Set("string", std::string("my_string"));
oObj.Set("const_char_star", nullptr);
oObj.Set("const_char_star", "my_const_char_star");
oObj.Set("int", 1);
oObj.Set("int64", GINT64_MAX);
oObj.Set("double", 1.25);
// oObj.Set("array", CPLJSONArray());
// oObj.Set("obj", CPLJSONObject());
oObj.Set("bool", true);
oObj.SetNull("null_field");
ASSERT_TRUE(CPLJSONArray().GetChildren().empty());
oObj.ToArray();
ASSERT_EQ(CPLJSONObject().Format(CPLJSONObject::PrettyFormat::Spaced),
std::string("{ }"));
ASSERT_EQ(CPLJSONObject().Format(CPLJSONObject::PrettyFormat::Pretty),
std::string("{\n}"));
ASSERT_EQ(CPLJSONObject().Format(CPLJSONObject::PrettyFormat::Plain),
std::string("{}"));
}
{
CPLJSONArray oArrayConstructorString(std::string("foo"));
CPLJSONArray oArray;
oArray.Add(CPLJSONObject());
oArray.Add(std::string("str"));
oArray.Add("const_char_star");
oArray.Add(1.25);
oArray.Add(1);
oArray.Add(GINT64_MAX);
oArray.Add(true);
ASSERT_EQ(oArray.Size(), 7);
int nCount = 0;
for (const auto &obj : oArray)
{
ASSERT_EQ(obj.GetInternalHandle(),
oArray[nCount].GetInternalHandle());
nCount++;
}
ASSERT_EQ(nCount, 7);
}
{
CPLJSONDocument oDocument;
ASSERT_TRUE(oDocument.LoadMemory(CPLString("{ \"/foo\" : \"bar\" }")));
ASSERT_EQ(oDocument.GetRoot().GetString("/foo"), std::string("bar"));
}
}
// Test CPLRecodeIconv() with re-allocation
TEST_F(test_cpl, CPLRecodeIconv)
{
#ifdef CPL_RECODE_ICONV
int N = 32800;
char *pszIn = static_cast<char *>(CPLMalloc(N + 1));
for (int i = 0; i < N; i++)
pszIn[i] = '\xE9';
pszIn[N] = 0;
char *pszExpected = static_cast<char *>(CPLMalloc(N * 2 + 1));
for (int i = 0; i < N; i++)
{
pszExpected[2 * i] = '\xC3';
pszExpected[2 * i + 1] = '\xA9';
}
pszExpected[N * 2] = 0;
char *pszRet = CPLRecode(pszIn, "ISO-8859-2", CPL_ENC_UTF8);
EXPECT_EQ(memcmp(pszExpected, pszRet, N * 2 + 1), 0);
CPLFree(pszIn);
CPLFree(pszRet);
CPLFree(pszExpected);
#else
GTEST_SKIP() << "CPL_RECODE_ICONV missing";
#endif
}
// Test CPLHTTPParseMultipartMime()
TEST_F(test_cpl, CPLHTTPParseMultipartMime)
{
CPLHTTPResult *psResult;
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Missing boundary value
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType = CPLStrdup("multipart/form-data; boundary=");
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// No content
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// No part
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Missing end boundary
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"\r\n"
"Bla";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Truncated header
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"Content-Type: foo";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Invalid end boundary
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Invalid end boundary
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLPopErrorHandler();
CPLHTTPDestroyResult(psResult);
// Valid single part, no header
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary--\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
EXPECT_EQ(psResult->nMimePartCount, 1);
if (psResult->nMimePartCount == 1)
{
EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
static_cast<char **>(nullptr));
EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
"Bla", 3) == 0);
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLHTTPDestroyResult(psResult);
// Valid single part, with header
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"Content-Type: bla\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary--\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
EXPECT_EQ(psResult->nMimePartCount, 1);
if (psResult->nMimePartCount == 1)
{
EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 1);
EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
"Content-Type=bla");
EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
"Bla", 3) == 0);
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLHTTPDestroyResult(psResult);
// Valid single part, 2 headers
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"Content-Type: bla\r\n"
"Content-Disposition: bar\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary--\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
EXPECT_EQ(psResult->nMimePartCount, 1);
if (psResult->nMimePartCount == 1)
{
EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 2);
EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
"Content-Type=bla");
EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[1],
"Content-Disposition=bar");
EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
"Bla", 3) == 0);
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLHTTPDestroyResult(psResult);
// Single part, but with header without extra terminating \r\n
// (invalid normally, but apparently necessary for some ArcGIS WCS
// implementations)
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"Content-Type: bla\r\n"
"Bla"
"\r\n"
"--myboundary--\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
EXPECT_EQ(psResult->nMimePartCount, 1);
if (psResult->nMimePartCount == 1)
{
EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
"Content-Type=bla");
EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
"Bla", 3) == 0);
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLHTTPDestroyResult(psResult);
// Valid 2 parts, no header
psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
psResult->pszContentType =
CPLStrdup("multipart/form-data; boundary=myboundary");
{
const char *pszText = "--myboundary some junk\r\n"
"\r\n"
"Bla"
"\r\n"
"--myboundary\r\n"
"\r\n"
"second part"
"\r\n"
"--myboundary--\r\n";
psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
psResult->nDataLen = static_cast<int>(strlen(pszText));
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
EXPECT_EQ(psResult->nMimePartCount, 2);
if (psResult->nMimePartCount == 2)
{
EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
static_cast<char **>(nullptr));
EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
"Bla", 3) == 0);
EXPECT_EQ(psResult->pasMimePart[1].nDataLen, 11);
EXPECT_TRUE(
strncmp(reinterpret_cast<char *>(psResult->pasMimePart[1].pabyData),
"second part", 11) == 0);
}
EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
CPLHTTPDestroyResult(psResult);
}
// Test cpl::down_cast
TEST_F(test_cpl, down_cast)
{
struct Base
{
virtual ~Base()
{
}
};
struct Derived : public Base
{
};
Base b;
Derived d;
Base *p_b_d = &d;
#ifdef wont_compile
struct OtherBase
{
};
OtherBase ob;
ASSERT_EQ(cpl::down_cast<OtherBase *>(p_b_d), &ob);
#endif
#ifdef compile_with_warning
ASSERT_EQ(cpl::down_cast<Base *>(p_b_d), p_b_d);
#endif
ASSERT_EQ(cpl::down_cast<Derived *>(p_b_d), &d);
ASSERT_EQ(cpl::down_cast<Derived *>(static_cast<Base *>(nullptr)),
static_cast<Derived *>(nullptr));
}
// Test CPLPrintTime() in particular case of RFC822 formatting in C locale
TEST_F(test_cpl, CPLPrintTime_RFC822)
{
char szDate[64];
struct tm tm;
tm.tm_sec = 56;
tm.tm_min = 34;
tm.tm_hour = 12;
tm.tm_mday = 20;
tm.tm_mon = 6 - 1;
tm.tm_year = 2018 - 1900;
tm.tm_wday = 3; // Wednesday
tm.tm_yday = 0; // unused
tm.tm_isdst = 0; // unused
int nRet = CPLPrintTime(szDate, sizeof(szDate) - 1,
"%a, %d %b %Y %H:%M:%S GMT", &tm, "C");
szDate[nRet] = 0;
ASSERT_STREQ(szDate, "Wed, 20 Jun 2018 12:34:56 GMT");
}
// Test CPLAutoClose
TEST_F(test_cpl, CPLAutoClose)
{
static int counter = 0;
class AutoCloseTest
{
public:
AutoCloseTest()
{
counter += 222;
}
virtual ~AutoCloseTest()
{
counter -= 22;
}
static AutoCloseTest *Create()
{
return new AutoCloseTest;
}
static void Destroy(AutoCloseTest *p)
{
delete p;
}
};
{
AutoCloseTest *p1 = AutoCloseTest::Create();
CPL_AUTO_CLOSE_WARP(p1, AutoCloseTest::Destroy);
AutoCloseTest *p2 = AutoCloseTest::Create();
CPL_AUTO_CLOSE_WARP(p2, AutoCloseTest::Destroy);
}
ASSERT_EQ(counter, 400);
}
// Test cpl_minixml
TEST_F(test_cpl, cpl_minixml)
{
CPLXMLNode *psRoot = CPLCreateXMLNode(nullptr, CXT_Element, "Root");
CPLXMLNode *psElt = CPLCreateXMLElementAndValue(psRoot, "Elt", "value");
CPLAddXMLAttributeAndValue(psElt, "attr1", "val1");
CPLAddXMLAttributeAndValue(psElt, "attr2", "val2");
char *str = CPLSerializeXMLTree(psRoot);
CPLDestroyXMLNode(psRoot);
ASSERT_STREQ(
str,
"<Root>\n <Elt attr1=\"val1\" attr2=\"val2\">value</Elt>\n</Root>\n");
CPLFree(str);
}
// Test CPLCharUniquePtr
TEST_F(test_cpl, CPLCharUniquePtr)
{
CPLCharUniquePtr x;
ASSERT_TRUE(x.get() == nullptr);
x.reset(CPLStrdup("foo"));
ASSERT_STREQ(x.get(), "foo");
}
// Test CPLJSonStreamingWriter
TEST_F(test_cpl, CPLJSonStreamingWriter)
{
{
CPLJSonStreamingWriter x(nullptr, nullptr);
ASSERT_EQ(x.GetString(), std::string());
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(true);
ASSERT_EQ(x.GetString(), std::string("true"));
}
{
std::string res;
struct MyCallback
{
static void f(const char *pszText, void *user_data)
{
*static_cast<std::string *>(user_data) += pszText;
}
};
CPLJSonStreamingWriter x(&MyCallback::f, &res);
x.Add(true);
ASSERT_EQ(x.GetString(), std::string());
ASSERT_EQ(res, std::string("true"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(false);
ASSERT_EQ(x.GetString(), std::string("false"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.AddNull();
ASSERT_EQ(x.GetString(), std::string("null"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(1);
ASSERT_EQ(x.GetString(), std::string("1"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(4200000000U);
ASSERT_EQ(x.GetString(), std::string("4200000000"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(static_cast<std::int64_t>(-10000) * 1000000);
ASSERT_EQ(x.GetString(), std::string("-10000000000"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(static_cast<std::uint64_t>(10000) * 1000000);
ASSERT_EQ(x.GetString(), std::string("10000000000"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(1.5f);
ASSERT_EQ(x.GetString(), std::string("1.5"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(std::numeric_limits<float>::quiet_NaN());
ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(std::numeric_limits<float>::infinity());
ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(-std::numeric_limits<float>::infinity());
ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(1.25);
ASSERT_EQ(x.GetString(), std::string("1.25"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(std::numeric_limits<double>::quiet_NaN());
ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(std::numeric_limits<double>::infinity());
ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(-std::numeric_limits<double>::infinity());
ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add(std::string("foo\\bar\"baz\b\f\n\r\t"
"\x01"
"boo"));
ASSERT_EQ(
x.GetString(),
std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.Add("foo\\bar\"baz\b\f\n\r\t"
"\x01"
"boo");
ASSERT_EQ(
x.GetString(),
std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.SetPrettyFormatting(false);
{
auto ctxt(x.MakeObjectContext());
}
ASSERT_EQ(x.GetString(), std::string("{}"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeObjectContext());
}
ASSERT_EQ(x.GetString(), std::string("{}"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
x.SetPrettyFormatting(false);
{
auto ctxt(x.MakeObjectContext());
x.AddObjKey("key");
x.Add("value");
}
ASSERT_EQ(x.GetString(), std::string("{\"key\":\"value\"}"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeObjectContext());
x.AddObjKey("key");
x.Add("value");
}
ASSERT_EQ(x.GetString(), std::string("{\n \"key\": \"value\"\n}"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeObjectContext());
x.AddObjKey("key");
x.Add("value");
x.AddObjKey("key2");
x.Add("value2");
}
ASSERT_EQ(
x.GetString(),
std::string("{\n \"key\": \"value\",\n \"key2\": \"value2\"\n}"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeArrayContext());
}
ASSERT_EQ(x.GetString(), std::string("[]"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeArrayContext());
x.Add(1);
}
ASSERT_EQ(x.GetString(), std::string("[\n 1\n]"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeArrayContext());
x.Add(1);
x.Add(2);
}
ASSERT_EQ(x.GetString(), std::string("[\n 1,\n 2\n]"));
}
{
CPLJSonStreamingWriter x(nullptr, nullptr);
{
auto ctxt(x.MakeArrayContext(true));
x.Add(1);
x.Add(2);
}
ASSERT_EQ(x.GetString(), std::string("[1, 2]"));
}
}
// Test CPLWorkerThreadPool
TEST_F(test_cpl, CPLWorkerThreadPool)
{
CPLWorkerThreadPool oPool;
ASSERT_TRUE(oPool.Setup(2, nullptr, nullptr, false));
const auto myJob = [](void *pData) { (*static_cast<int *>(pData))++; };
{
std::vector<int> res(1000);
for (int i = 0; i < 1000; i++)
{
res[i] = i;
oPool.SubmitJob(myJob, &res[i]);
}
oPool.WaitCompletion();
for (int i = 0; i < 1000; i++)
{
ASSERT_EQ(res[i], i + 1);
}
}
{
std::vector<int> res(1000);
std::vector<void *> resPtr(1000);
for (int i = 0; i < 1000; i++)
{
res[i] = i;
resPtr[i] = res.data() + i;
}
oPool.SubmitJobs(myJob, resPtr);
oPool.WaitEvent();
oPool.WaitCompletion();
for (int i = 0; i < 1000; i++)
{
ASSERT_EQ(res[i], i + 1);
}
}
{
auto jobQueue1 = oPool.CreateJobQueue();
auto jobQueue2 = oPool.CreateJobQueue();
ASSERT_EQ(jobQueue1->GetPool(), &oPool);
std::vector<int> res(1000);
for (int i = 0; i < 1000; i++)
{
res[i] = i;
if (i % 2)
jobQueue1->SubmitJob(myJob, &res[i]);
else
jobQueue2->SubmitJob(myJob, &res[i]);
}
jobQueue1->WaitCompletion();
jobQueue2->WaitCompletion();
for (int i = 0; i < 1000; i++)
{
ASSERT_EQ(res[i], i + 1);
}
}
}
// Test CPLHTTPFetch
TEST_F(test_cpl, CPLHTTPFetch)
{
#ifdef HAVE_CURL
CPLStringList oOptions;
oOptions.AddNameValue("FORM_ITEM_COUNT", "5");
oOptions.AddNameValue("FORM_KEY_0", "qqq");
oOptions.AddNameValue("FORM_VALUE_0", "www");
CPLHTTPResult *pResult = CPLHTTPFetch("http://example.com", oOptions);
EXPECT_EQ(pResult->nStatus, 34);
CPLHTTPDestroyResult(pResult);
pResult = nullptr;
oOptions.Clear();
oOptions.AddNameValue("FORM_FILE_PATH", "not_existed");
pResult = CPLHTTPFetch("http://example.com", oOptions);
EXPECT_EQ(pResult->nStatus, 34);
CPLHTTPDestroyResult(pResult);
#else
GTEST_SKIP() << "CURL not available";
#endif // HAVE_CURL
}
// Test CPLHTTPPushFetchCallback
TEST_F(test_cpl, CPLHTTPPushFetchCallback)
{
struct myCbkUserDataStruct
{
CPLString osURL{};
CSLConstList papszOptions = nullptr;
GDALProgressFunc pfnProgress = nullptr;
void *pProgressArg = nullptr;
CPLHTTPFetchWriteFunc pfnWrite = nullptr;
void *pWriteArg = nullptr;
};
const auto myCbk = [](const char *pszURL, CSLConstList papszOptions,
GDALProgressFunc pfnProgress, void *pProgressArg,
CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
void *pUserData)
{
myCbkUserDataStruct *pCbkUserData =
static_cast<myCbkUserDataStruct *>(pUserData);
pCbkUserData->osURL = pszURL;
pCbkUserData->papszOptions = papszOptions;
pCbkUserData->pfnProgress = pfnProgress;
pCbkUserData->pProgressArg = pProgressArg;
pCbkUserData->pfnWrite = pfnWrite;
pCbkUserData->pWriteArg = pWriteArg;
auto psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
psResult->nStatus = 123;
return psResult;
};
myCbkUserDataStruct userData;
EXPECT_TRUE(CPLHTTPPushFetchCallback(myCbk, &userData));
int progressArg = 0;
const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
{ return 0; };
int writeCbkArg = 00;
CPLStringList aosOptions;
GDALProgressFunc pfnProgress = GDALTermProgress;
CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
CPLHTTPResult *pResult =
CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
&progressArg, pfnWriteCbk, &writeCbkArg);
ASSERT_TRUE(pResult != nullptr);
EXPECT_EQ(pResult->nStatus, 123);
CPLHTTPDestroyResult(pResult);
EXPECT_TRUE(CPLHTTPPopFetchCallback());
CPLPushErrorHandler(CPLQuietErrorHandler);
EXPECT_TRUE(!CPLHTTPPopFetchCallback());
CPLPopErrorHandler();
EXPECT_STREQ(userData.osURL, "http://example.com");
EXPECT_EQ(userData.papszOptions, aosOptions.List());
EXPECT_EQ(userData.pfnProgress, pfnProgress);
EXPECT_EQ(userData.pProgressArg, &progressArg);
EXPECT_EQ(userData.pfnWrite, pfnWriteCbk);
EXPECT_EQ(userData.pWriteArg, &writeCbkArg);
}
// Test CPLHTTPSetFetchCallback
TEST_F(test_cpl, CPLHTTPSetFetchCallback)
{
struct myCbkUserDataStruct
{
CPLString osURL{};
CSLConstList papszOptions = nullptr;
GDALProgressFunc pfnProgress = nullptr;
void *pProgressArg = nullptr;
CPLHTTPFetchWriteFunc pfnWrite = nullptr;
void *pWriteArg = nullptr;
};
const auto myCbk2 = [](const char *pszURL, CSLConstList papszOptions,
GDALProgressFunc pfnProgress, void *pProgressArg,
CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
void *pUserData)
{
myCbkUserDataStruct *pCbkUserData =
static_cast<myCbkUserDataStruct *>(pUserData);
pCbkUserData->osURL = pszURL;
pCbkUserData->papszOptions = papszOptions;
pCbkUserData->pfnProgress = pfnProgress;
pCbkUserData->pProgressArg = pProgressArg;
pCbkUserData->pfnWrite = pfnWrite;
pCbkUserData->pWriteArg = pWriteArg;
auto psResult =
static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
psResult->nStatus = 124;
return psResult;
};
myCbkUserDataStruct userData2;
CPLHTTPSetFetchCallback(myCbk2, &userData2);
int progressArg = 0;
const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
{ return 0; };
int writeCbkArg = 00;
CPLStringList aosOptions;
GDALProgressFunc pfnProgress = GDALTermProgress;
CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
CPLHTTPResult *pResult =
CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
&progressArg, pfnWriteCbk, &writeCbkArg);
ASSERT_TRUE(pResult != nullptr);
EXPECT_EQ(pResult->nStatus, 124);
CPLHTTPDestroyResult(pResult);
CPLHTTPSetFetchCallback(nullptr, nullptr);
EXPECT_STREQ(userData2.osURL, "http://example.com");
EXPECT_EQ(userData2.papszOptions, aosOptions.List());
EXPECT_EQ(userData2.pfnProgress, pfnProgress);
EXPECT_EQ(userData2.pProgressArg, &progressArg);
EXPECT_EQ(userData2.pfnWrite, pfnWriteCbk);
EXPECT_EQ(userData2.pWriteArg, &writeCbkArg);
}
// Test CPLLoadConfigOptionsFromFile() and
// CPLLoadConfigOptionsFromPredefinedFiles()
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile)
{
CPLLoadConfigOptionsFromFile("/i/do/not/exist", false);
VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
VSIFPrintfL(fp, "# some comment\n");
VSIFPrintfL(fp, "\n"); // blank line
VSIFPrintfL(fp, " \n"); // blank line
VSIFPrintfL(fp, "[configoptions]\n");
VSIFPrintfL(fp, "# some comment\n");
VSIFPrintfL(fp, "FOO_CONFIGOPTION=BAR\n");
VSIFCloseL(fp);
// Try CPLLoadConfigOptionsFromFile()
CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
// Try CPLLoadConfigOptionsFromPredefinedFiles() with GDAL_CONFIG_FILE set
CPLSetConfigOption("GDAL_CONFIG_FILE", "/vsimem/.gdal/gdalrc");
CPLLoadConfigOptionsFromPredefinedFiles();
ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
// Try CPLLoadConfigOptionsFromPredefinedFiles() with $HOME/.gdal/gdalrc
// file
#ifdef WIN32
const char *pszHOMEEnvVarName = "USERPROFILE";
#else
const char *pszHOMEEnvVarName = "HOME";
#endif
CPLString osOldVal(CPLGetConfigOption(pszHOMEEnvVarName, ""));
CPLSetConfigOption(pszHOMEEnvVarName, "/vsimem/");
CPLLoadConfigOptionsFromPredefinedFiles();
ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
if (!osOldVal.empty())
CPLSetConfigOption(pszHOMEEnvVarName, osOldVal.c_str());
else
CPLSetConfigOption(pszHOMEEnvVarName, nullptr);
VSIUnlink("/vsimem/.gdal/gdalrc");
}
// Test decompressor side of cpl_compressor.h
TEST_F(test_cpl, decompressor)
{
const auto compressionLambda =
[](const void * /* input_data */, size_t /* input_size */,
void ** /* output_data */, size_t * /* output_size */,
CSLConstList /* options */, void * /* compressor_user_data */)
{ return false; };
int dummy = 0;
CPLCompressor sComp;
sComp.nStructVersion = 1;
sComp.eType = CCT_COMPRESSOR;
sComp.pszId = "my_comp";
const char *const apszMetadata[] = {"FOO=BAR", nullptr};
sComp.papszMetadata = apszMetadata;
sComp.pfnFunc = compressionLambda;
sComp.user_data = &dummy;
ASSERT_TRUE(CPLRegisterDecompressor(&sComp));
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(!CPLRegisterDecompressor(&sComp));
CPLPopErrorHandler();
char **decompressors = CPLGetDecompressors();
ASSERT_TRUE(decompressors != nullptr);
EXPECT_TRUE(CSLFindString(decompressors, sComp.pszId) >= 0);
for (auto iter = decompressors; *iter; ++iter)
{
const auto pCompressor = CPLGetDecompressor(*iter);
EXPECT_TRUE(pCompressor);
if (pCompressor)
{
const char *pszOptions =
CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
if (pszOptions)
{
auto psNode = CPLParseXMLString(pszOptions);
EXPECT_TRUE(psNode);
CPLDestroyXMLNode(psNode);
}
else
{
CPLDebug("TEST", "Decompressor %s has no OPTIONS", *iter);
}
}
}
CSLDestroy(decompressors);
EXPECT_TRUE(CPLGetDecompressor("invalid") == nullptr);
const auto pCompressor = CPLGetDecompressor(sComp.pszId);
ASSERT_TRUE(pCompressor);
EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
CSLCount(sComp.papszMetadata));
EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
EXPECT_EQ(pCompressor->user_data, sComp.user_data);
CPLDestroyCompressorRegistry();
EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
}
// Test compressor side of cpl_compressor.h
TEST_F(test_cpl, compressor)
{
const auto compressionLambda =
[](const void * /* input_data */, size_t /* input_size */,
void ** /* output_data */, size_t * /* output_size */,
CSLConstList /* options */, void * /* compressor_user_data */)
{ return false; };
int dummy = 0;
CPLCompressor sComp;
sComp.nStructVersion = 1;
sComp.eType = CCT_COMPRESSOR;
sComp.pszId = "my_comp";
const char *const apszMetadata[] = {"FOO=BAR", nullptr};
sComp.papszMetadata = apszMetadata;
sComp.pfnFunc = compressionLambda;
sComp.user_data = &dummy;
ASSERT_TRUE(CPLRegisterCompressor(&sComp));
CPLPushErrorHandler(CPLQuietErrorHandler);
ASSERT_TRUE(!CPLRegisterCompressor(&sComp));
CPLPopErrorHandler();
char **compressors = CPLGetCompressors();
ASSERT_TRUE(compressors != nullptr);
EXPECT_TRUE(CSLFindString(compressors, sComp.pszId) >= 0);
for (auto iter = compressors; *iter; ++iter)
{
const auto pCompressor = CPLGetCompressor(*iter);
EXPECT_TRUE(pCompressor);
if (pCompressor)
{
const char *pszOptions =
CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
if (pszOptions)
{
auto psNode = CPLParseXMLString(pszOptions);
EXPECT_TRUE(psNode);
CPLDestroyXMLNode(psNode);
}
else
{
CPLDebug("TEST", "Compressor %s has no OPTIONS", *iter);
}
}
}
CSLDestroy(compressors);
EXPECT_TRUE(CPLGetCompressor("invalid") == nullptr);
const auto pCompressor = CPLGetCompressor(sComp.pszId);
ASSERT_TRUE(pCompressor);
if (pCompressor == nullptr)
return;
EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
CSLCount(sComp.papszMetadata));
EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
EXPECT_EQ(pCompressor->user_data, sComp.user_data);
CPLDestroyCompressorRegistry();
EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
}
// Test builtin compressors/decompressor
TEST_F(test_cpl, builtin_compressors)
{
for (const char *id : {"blosc", "zlib", "gzip", "lzma", "zstd", "lz4"})
{
const auto pCompressor = CPLGetCompressor(id);
if (pCompressor == nullptr)
{
CPLDebug("TEST", "%s not available", id);
if (strcmp(id, "zlib") == 0 || strcmp(id, "gzip") == 0)
{
ASSERT_TRUE(false);
}
continue;
}
CPLDebug("TEST", "Testing %s", id);
const char my_str[] = "my string to compress";
const char *const options[] = {"TYPESIZE=1", nullptr};
// Compressor side
// Just get output size
size_t out_size = 0;
ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), nullptr,
&out_size, options,
pCompressor->user_data));
ASSERT_TRUE(out_size != 0);
// Let it alloc the output buffer
void *out_buffer2 = nullptr;
size_t out_size2 = 0;
ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), &out_buffer2,
&out_size2, options,
pCompressor->user_data));
ASSERT_TRUE(out_buffer2 != nullptr);
ASSERT_TRUE(out_size2 != 0);
ASSERT_TRUE(out_size2 <= out_size);
std::vector<GByte> out_buffer3(out_size);
// Provide not large enough buffer size
size_t out_size3 = 1;
void *out_buffer3_ptr = &out_buffer3[0];
ASSERT_TRUE(!(pCompressor->pfnFunc(my_str, strlen(my_str),
&out_buffer3_ptr, &out_size3,
options, pCompressor->user_data)));
// Provide the output buffer
out_size3 = out_buffer3.size();
out_buffer3_ptr = &out_buffer3[0];
ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str),
&out_buffer3_ptr, &out_size3, options,
pCompressor->user_data));
ASSERT_TRUE(out_buffer3_ptr != nullptr);
ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
ASSERT_TRUE(out_size3 != 0);
ASSERT_EQ(out_size3, out_size2);
out_buffer3.resize(out_size3);
out_buffer3_ptr = &out_buffer3[0];
ASSERT_TRUE(memcmp(out_buffer3_ptr, out_buffer2, out_size2) == 0);
CPLFree(out_buffer2);
const std::vector<GByte> compressedData(out_buffer3);
// Decompressor side
const auto pDecompressor = CPLGetDecompressor(id);
ASSERT_TRUE(pDecompressor != nullptr);
out_size = 0;
ASSERT_TRUE(pDecompressor->pfnFunc(
compressedData.data(), compressedData.size(), nullptr, &out_size,
nullptr, pDecompressor->user_data));
ASSERT_TRUE(out_size != 0);
ASSERT_TRUE(out_size >= strlen(my_str));
out_buffer2 = nullptr;
out_size2 = 0;
ASSERT_TRUE(pDecompressor->pfnFunc(
compressedData.data(), compressedData.size(), &out_buffer2,
&out_size2, options, pDecompressor->user_data));
ASSERT_TRUE(out_buffer2 != nullptr);
ASSERT_TRUE(out_size2 != 0);
ASSERT_EQ(out_size2, strlen(my_str));
ASSERT_TRUE(memcmp(out_buffer2, my_str, strlen(my_str)) == 0);
CPLFree(out_buffer2);
out_buffer3.clear();
out_buffer3.resize(out_size);
out_size3 = out_buffer3.size();
out_buffer3_ptr = &out_buffer3[0];
ASSERT_TRUE(pDecompressor->pfnFunc(
compressedData.data(), compressedData.size(), &out_buffer3_ptr,
&out_size3, options, pDecompressor->user_data));
ASSERT_TRUE(out_buffer3_ptr != nullptr);
ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
ASSERT_EQ(out_size3, strlen(my_str));
ASSERT_TRUE(memcmp(out_buffer3.data(), my_str, strlen(my_str)) == 0);
}
}
template <class T> struct TesterDelta
{
static void test(const char *dtypeOption)
{
const auto pCompressor = CPLGetCompressor("delta");
ASSERT_TRUE(pCompressor);
if (pCompressor == nullptr)
return;
const auto pDecompressor = CPLGetDecompressor("delta");
ASSERT_TRUE(pDecompressor);
if (pDecompressor == nullptr)
return;
const T tabIn[] = {static_cast<T>(-2), 3, 1};
T tabCompress[3];
T tabOut[3];
const char *const apszOptions[] = {dtypeOption, nullptr};
void *outPtr = &tabCompress[0];
size_t outSize = sizeof(tabCompress);
ASSERT_TRUE(pCompressor->pfnFunc(&tabIn[0], sizeof(tabIn), &outPtr,
&outSize, apszOptions,
pCompressor->user_data));
ASSERT_EQ(outSize, sizeof(tabCompress));
// ASSERT_EQ(tabCompress[0], 2);
// ASSERT_EQ(tabCompress[1], 1);
// ASSERT_EQ(tabCompress[2], -2);
outPtr = &tabOut[0];
outSize = sizeof(tabOut);
ASSERT_TRUE(pDecompressor->pfnFunc(&tabCompress[0], sizeof(tabCompress),
&outPtr, &outSize, apszOptions,
pDecompressor->user_data));
ASSERT_EQ(outSize, sizeof(tabOut));
ASSERT_EQ(tabOut[0], tabIn[0]);
ASSERT_EQ(tabOut[1], tabIn[1]);
ASSERT_EQ(tabOut[2], tabIn[2]);
}
};
// Test delta compressor/decompressor
TEST_F(test_cpl, delta_compressor)
{
TesterDelta<int8_t>::test("DTYPE=i1");
TesterDelta<uint8_t>::test("DTYPE=u1");
TesterDelta<int16_t>::test("DTYPE=i2");
TesterDelta<int16_t>::test("DTYPE=<i2");
TesterDelta<int16_t>::test("DTYPE=>i2");
TesterDelta<uint16_t>::test("DTYPE=u2");
TesterDelta<uint16_t>::test("DTYPE=<u2");
TesterDelta<uint16_t>::test("DTYPE=>u2");
TesterDelta<int32_t>::test("DTYPE=i4");
TesterDelta<int32_t>::test("DTYPE=<i4");
TesterDelta<int32_t>::test("DTYPE=>i4");
TesterDelta<uint32_t>::test("DTYPE=u4");
TesterDelta<uint32_t>::test("DTYPE=<u4");
TesterDelta<uint32_t>::test("DTYPE=>u4");
TesterDelta<int64_t>::test("DTYPE=i8");
TesterDelta<int64_t>::test("DTYPE=<i8");
TesterDelta<int64_t>::test("DTYPE=>i8");
TesterDelta<uint64_t>::test("DTYPE=u8");
TesterDelta<uint64_t>::test("DTYPE=<u8");
TesterDelta<uint64_t>::test("DTYPE=>u8");
TesterDelta<float>::test("DTYPE=f4");
#ifdef CPL_MSB
TesterDelta<float>::test("DTYPE=>f4");
#else
TesterDelta<float>::test("DTYPE=<f4");
#endif
TesterDelta<double>::test("DTYPE=f8");
#ifdef CPL_MSB
TesterDelta<double>::test("DTYPE=>f8");
#else
TesterDelta<double>::test("DTYPE=<f8");
#endif
}
// Test CPLQuadTree
TEST_F(test_cpl, CPLQuadTree)
{
unsigned next = 0;
const auto DummyRandInit = [&next](unsigned initValue)
{ next = initValue; };
constexpr int MAX_RAND_VAL = 32767;
// Slightly improved version of https://xkcd.com/221/, as suggested by
// "man srand"
const auto DummyRand = [&]()
{
next = next * 1103515245 + 12345;
return ((unsigned)(next / 65536) % (MAX_RAND_VAL + 1));
};
CPLRectObj globalbounds;
globalbounds.minx = 0;
globalbounds.miny = 0;
globalbounds.maxx = 1;
globalbounds.maxy = 1;
auto hTree = CPLQuadTreeCreate(&globalbounds, nullptr);
ASSERT_TRUE(hTree != nullptr);
const auto GenerateRandomRect = [&](CPLRectObj &rect)
{
rect.minx = double(DummyRand()) / MAX_RAND_VAL;
rect.miny = double(DummyRand()) / MAX_RAND_VAL;
rect.maxx =
rect.minx + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.minx);
rect.maxy =
rect.miny + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.miny);
};
for (int j = 0; j < 2; j++)
{
DummyRandInit(j);
for (int i = 0; i < 1000; i++)
{
CPLRectObj rect;
GenerateRandomRect(rect);
void *hFeature =
reinterpret_cast<void *>(static_cast<uintptr_t>(i));
CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
}
{
int nFeatureCount = 0;
CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
ASSERT_EQ(nFeatureCount, 1000);
}
DummyRandInit(j);
for (int i = 0; i < 1000; i++)
{
CPLRectObj rect;
GenerateRandomRect(rect);
void *hFeature =
reinterpret_cast<void *>(static_cast<uintptr_t>(i));
CPLQuadTreeRemove(hTree, hFeature, &rect);
}
{
int nFeatureCount = 0;
CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
ASSERT_EQ(nFeatureCount, 0);
}
}
CPLQuadTreeDestroy(hTree);
}
// Test bUnlinkAndSize on VSIGetMemFileBuffer
TEST_F(test_cpl, VSIGetMemFileBuffer_unlink_and_size)
{
VSILFILE *fp = VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "wb");
VSIFWriteL("test", 5, 1, fp);
GByte *pRawData =
VSIGetMemFileBuffer("/vsimem/test_unlink_and_seize.tif", nullptr, true);
ASSERT_TRUE(EQUAL(reinterpret_cast<const char *>(pRawData), "test"));
ASSERT_TRUE(VSIGetMemFileBuffer("/vsimem/test_unlink_and_seize.tif",
nullptr, false) == nullptr);
ASSERT_TRUE(VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "r") == nullptr);
ASSERT_TRUE(VSIFReadL(pRawData, 5, 1, fp) == 0);
ASSERT_TRUE(VSIFWriteL(pRawData, 5, 1, fp) == 0);
ASSERT_TRUE(VSIFSeekL(fp, 0, SEEK_END) == 0);
CPLFree(pRawData);
VSIFCloseL(fp);
}
// Test CPLLoadConfigOptionsFromFile() for VSI credentials
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials)
{
VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
VSIFPrintfL(fp, "[credentials]\n");
VSIFPrintfL(fp, "\n");
VSIFPrintfL(fp, "[.my_subsection]\n");
VSIFPrintfL(fp, "path=/vsi_test/foo/bar\n");
VSIFPrintfL(fp, "FOO=BAR\n");
VSIFPrintfL(fp, "FOO2=BAR2\n");
VSIFPrintfL(fp, "\n");
VSIFPrintfL(fp, "[.my_subsection2]\n");
VSIFPrintfL(fp, "path=/vsi_test/bar/baz\n");
VSIFPrintfL(fp, "BAR=BAZ\n");
VSIFPrintfL(fp, "[configoptions]\n");
VSIFPrintfL(fp, "configoptions_FOO=BAR\n");
VSIFCloseL(fp);
CPLErrorReset();
CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
ASSERT_EQ(CPLGetLastErrorType(), CE_None);
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO", nullptr);
ASSERT_TRUE(pszVal != nullptr);
ASSERT_EQ(std::string(pszVal), std::string("BAR"));
}
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO2", nullptr);
ASSERT_TRUE(pszVal != nullptr);
ASSERT_EQ(std::string(pszVal), std::string("BAR2"));
}
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
ASSERT_TRUE(pszVal != nullptr);
ASSERT_EQ(std::string(pszVal), std::string("BAZ"));
}
{
const char *pszVal = CPLGetConfigOption("configoptions_FOO", nullptr);
ASSERT_TRUE(pszVal != nullptr);
ASSERT_EQ(std::string(pszVal), std::string("BAR"));
}
VSIClearPathSpecificOptions("/vsi_test/bar/baz");
CPLSetConfigOption("configoptions_FOO", nullptr);
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
ASSERT_TRUE(pszVal == nullptr);
}
VSIUnlink("/vsimem/credentials.txt");
}
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials_warning)
{
VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
VSIFPrintfL(fp, "[credentials]\n");
VSIFPrintfL(fp, "\n");
VSIFPrintfL(fp, "FOO=BAR\n"); // content outside of subsection
VSIFCloseL(fp);
CPLErrorReset();
CPLPushErrorHandler(CPLQuietErrorHandler);
CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
CPLPopErrorHandler();
ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
VSIUnlink("/vsimem/credentials.txt");
}
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
TEST_F(test_cpl,
CPLLoadConfigOptionsFromFile_VSI_credentials_subsection_warning)
{
VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
VSIFPrintfL(fp, "[credentials]\n");
VSIFPrintfL(fp, "[.subsection]\n");
VSIFPrintfL(fp, "FOO=BAR\n"); // first key is not 'path'
VSIFCloseL(fp);
CPLErrorReset();
CPLPushErrorHandler(CPLQuietErrorHandler);
CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
CPLPopErrorHandler();
ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
VSIUnlink("/vsimem/credentials.txt");
}
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
TEST_F(test_cpl,
CPLLoadConfigOptionsFromFile_VSI_credentials_warning_path_specific)
{
VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
VSIFPrintfL(fp, "[credentials]\n");
VSIFPrintfL(fp, "[.subsection]\n");
VSIFPrintfL(fp, "path=/vsi_test/foo\n");
VSIFPrintfL(fp, "path=/vsi_test/bar\n"); // duplicated path
VSIFPrintfL(fp, "FOO=BAR\n"); // first key is not 'path'
VSIFPrintfL(fp, "[unrelated_section]");
VSIFPrintfL(fp, "BAR=BAZ\n"); // first key is not 'path'
VSIFCloseL(fp);
CPLErrorReset();
CPLPushErrorHandler(CPLQuietErrorHandler);
CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
CPLPopErrorHandler();
ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/foo", "FOO", nullptr);
ASSERT_TRUE(pszVal != nullptr);
}
{
const char *pszVal =
VSIGetPathSpecificOption("/vsi_test/foo", "BAR", nullptr);
ASSERT_TRUE(pszVal == nullptr);
}
VSIUnlink("/vsimem/credentials.txt");
}
// Test CPLRecodeFromWCharIconv() with 2 bytes/char source encoding
TEST_F(test_cpl, CPLRecodeFromWCharIconv_2byte_source_encoding)
{
#ifdef CPL_RECODE_ICONV
int N = 2048;
wchar_t *pszIn =
static_cast<wchar_t *>(CPLMalloc((N + 1) * sizeof(wchar_t)));
for (int i = 0; i < N; i++)
pszIn[i] = L'A';
pszIn[N] = L'\0';
char *pszExpected = static_cast<char *>(CPLMalloc(N + 1));
for (int i = 0; i < N; i++)
pszExpected[i] = 'A';
pszExpected[N] = '\0';
char *pszRet = CPLRecodeFromWChar(pszIn, CPL_ENC_UTF16, CPL_ENC_UTF8);
const bool bOK = memcmp(pszExpected, pszRet, N + 1) == 0;
// FIXME Some tests fail on Mac. Not sure why, but do not error out just for
// that
if (!bOK &&
(strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") != nullptr ||
strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
{
fprintf(stderr, "Recode from CPL_ENC_UTF16 to CPL_ENC_UTF8 failed\n");
}
else
{
EXPECT_TRUE(bOK);
}
CPLFree(pszIn);
CPLFree(pszRet);
CPLFree(pszExpected);
#else
GTEST_SKIP() << "iconv support missing";
#endif
}
// VERY MINIMAL testing of VSI plugin functionality
TEST_F(test_cpl, VSI_plugin_minimal_testing)
{
auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
psCallbacks->open = [](void *pUserData, const char *pszFilename,
const char *pszAccess) -> void *
{
(void)pUserData;
if (strcmp(pszFilename, "test") == 0 && strcmp(pszAccess, "rb") == 0)
return const_cast<char *>("ok");
return nullptr;
};
EXPECT_EQ(VSIInstallPluginHandler("/vsimyplugin/", psCallbacks), 0);
VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
VSILFILE *fp = VSIFOpenL("/vsimyplugin/test", "rb");
EXPECT_TRUE(fp != nullptr);
// Check it doesn't crash
vsi_l_offset nOffset = 5;
size_t nSize = 10;
reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
VSIFCloseL(fp);
EXPECT_TRUE(VSIFOpenL("/vsimyplugin/i_dont_exist", "rb") == nullptr);
}
TEST_F(test_cpl, VSI_plugin_advise_read)
{
auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
struct UserData
{
int nRanges = 0;
const vsi_l_offset *panOffsets = nullptr;
const size_t *panSizes = nullptr;
};
UserData userData;
psCallbacks->pUserData = &userData;
psCallbacks->open = [](void *pUserData, const char * /*pszFilename*/,
const char * /*pszAccess*/) -> void *
{ return pUserData; };
psCallbacks->advise_read = [](void *pFile, int nRanges,
const vsi_l_offset *panOffsets,
const size_t *panSizes)
{
static_cast<UserData *>(pFile)->nRanges = nRanges;
static_cast<UserData *>(pFile)->panOffsets = panOffsets;
static_cast<UserData *>(pFile)->panSizes = panSizes;
};
EXPECT_EQ(VSIInstallPluginHandler("/VSI_plugin_advise_read/", psCallbacks),
0);
VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
VSILFILE *fp = VSIFOpenL("/VSI_plugin_advise_read/test", "rb");
EXPECT_TRUE(fp != nullptr);
vsi_l_offset nOffset = 5;
size_t nSize = 10;
reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
EXPECT_EQ(userData.nRanges, 1);
EXPECT_EQ(userData.panOffsets, &nOffset);
EXPECT_EQ(userData.panSizes, &nSize);
VSIFCloseL(fp);
}
// Test CPLIsASCII()
TEST_F(test_cpl, CPLIsASCII)
{
ASSERT_TRUE(CPLIsASCII("foo", 3));
ASSERT_TRUE(CPLIsASCII("foo", static_cast<size_t>(-1)));
ASSERT_TRUE(!CPLIsASCII("\xFF", 1));
}
// Test VSIIsLocal()
TEST_F(test_cpl, VSIIsLocal)
{
ASSERT_TRUE(VSIIsLocal("/vsimem/"));
ASSERT_TRUE(VSIIsLocal("/vsigzip//vsimem/tmp.gz"));
#ifdef HAVE_CURL
ASSERT_TRUE(!VSIIsLocal("/vsicurl/http://example.com"));
#endif
VSIStatBufL sStat;
#ifdef _WIN32
if (VSIStatL("c:\\", &sStat) == 0)
{
ASSERT_TRUE(VSIIsLocal("c:\\i_do_not_exist"));
}
#else
if (VSIStatL("/tmp", &sStat) == 0)
{
ASSERT_TRUE(VSIIsLocal("/tmp/i_do_not_exist"));
}
#endif
}
// Test VSISupportsSequentialWrite()
TEST_F(test_cpl, VSISupportsSequentialWrite)
{
ASSERT_TRUE(VSISupportsSequentialWrite("/vsimem/", false));
#ifdef HAVE_CURL
ASSERT_TRUE(
!VSISupportsSequentialWrite("/vsicurl/http://example.com", false));
ASSERT_TRUE(VSISupportsSequentialWrite("/vsis3/test_bucket/", false));
#endif
ASSERT_TRUE(VSISupportsSequentialWrite("/vsigzip//vsimem/tmp.gz", false));
#ifdef HAVE_CURL
ASSERT_TRUE(!VSISupportsSequentialWrite(
"/vsigzip//vsicurl/http://example.com/tmp.gz", false));
#endif
VSIStatBufL sStat;
#ifdef _WIN32
if (VSIStatL("c:\\", &sStat) == 0)
{
ASSERT_TRUE(VSISupportsSequentialWrite("c:\\", false));
}
#else
if (VSIStatL("/tmp", &sStat) == 0)
{
ASSERT_TRUE(VSISupportsSequentialWrite("/tmp/i_do_not_exist", false));
}
#endif
}
// Test VSISupportsRandomWrite()
TEST_F(test_cpl, VSISupportsRandomWrite)
{
ASSERT_TRUE(VSISupportsRandomWrite("/vsimem/", false));
#ifdef HAVE_CURL
ASSERT_TRUE(!VSISupportsRandomWrite("/vsicurl/http://example.com", false));
ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", false));
ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", true));
CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", "YES");
ASSERT_TRUE(VSISupportsRandomWrite("/vsis3/test_bucket/", true));
CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", nullptr);
#endif
ASSERT_TRUE(!VSISupportsRandomWrite("/vsigzip//vsimem/tmp.gz", false));
#ifdef HAVE_CURL
ASSERT_TRUE(!VSISupportsRandomWrite(
"/vsigzip//vsicurl/http://example.com/tmp.gz", false));
#endif
VSIStatBufL sStat;
#ifdef _WIN32
if (VSIStatL("c:\\", &sStat) == 0)
{
ASSERT_TRUE(VSISupportsRandomWrite("c:\\", false));
}
#else
if (VSIStatL("/tmp", &sStat) == 0)
{
ASSERT_TRUE(VSISupportsRandomWrite("/tmp", false));
}
#endif
}
// Test ignore-env-vars = yes of configuration file
TEST_F(test_cpl, config_file_ignore_env_vars)
{
char szEnvVar[] = "SOME_ENV_VAR_FOR_TEST_CPL_61=FOO";
putenv(szEnvVar);
ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) !=
nullptr);
VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
VSIFPrintfL(fp, "[directives]\n");
VSIFPrintfL(fp, "ignore-env-vars=yes\n");
VSIFPrintfL(fp, "[configoptions]\n");
VSIFPrintfL(fp, "CONFIG_OPTION_FOR_TEST_CPL_61=BAR\n");
VSIFCloseL(fp);
// Load configuration file
CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
// Check that reading configuration option works
ASSERT_TRUE(
EQUAL(CPLGetConfigOption("CONFIG_OPTION_FOR_TEST_CPL_61", ""), "BAR"));
// Check that environment variables are not read as configuration options
ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) ==
nullptr);
// Reset ignore-env-vars=no
fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
VSIFPrintfL(fp, "[directives]\n");
VSIFPrintfL(fp, "ignore-env-vars=no\n");
VSIFPrintfL(fp, "[configoptions]\n");
VSIFPrintfL(fp, "SOME_ENV_VAR_FOR_TEST_CPL_61=BAR\n");
VSIFCloseL(fp);
// Reload configuration file
CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
// Check that environment variables are read as configuration options
// and override configuration options
ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) !=
nullptr);
ASSERT_EQ(
std::string(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", "")),
std::string("FOO"));
VSIUnlink("/vsimem/.gdal/gdalrc");
}
// Test CPLWorkerThreadPool recursion
TEST_F(test_cpl, CPLWorkerThreadPool_recursion)
{
struct Context
{
CPLWorkerThreadPool oThreadPool{};
std::atomic<int> nCounter{0};
std::mutex mutex{};
std::condition_variable cv{};
bool you_can_leave = false;
int threadStarted = 0;
};
Context ctxt;
ctxt.oThreadPool.Setup(2, nullptr, nullptr, /* waitAllStarted = */ true);
struct Data
{
Context *psCtxt;
int iJob;
GIntBig nThreadLambda = 0;
Data(Context *psCtxtIn, int iJobIn) : psCtxt(psCtxtIn), iJob(iJobIn)
{
}
Data(const Data &) = default;
};
const auto lambda = [](void *pData)
{
auto psData = static_cast<Data *>(pData);
if (psData->iJob > 0)
{
// wait for both threads to be started
std::unique_lock<std::mutex> guard(psData->psCtxt->mutex);
psData->psCtxt->threadStarted++;
psData->psCtxt->cv.notify_one();
while (psData->psCtxt->threadStarted < 2)
{
psData->psCtxt->cv.wait(guard);
}
}
psData->nThreadLambda = CPLGetPID();
// fprintf(stderr, "lambda %d: " CPL_FRMT_GIB "\n",
// psData->iJob, psData->nThreadLambda);
const auto lambda2 = [](void *pData2)
{
const auto psData2 = static_cast<Data *>(pData2);
const int iJob = psData2->iJob;
const int nCounter = psData2->psCtxt->nCounter++;
CPL_IGNORE_RET_VAL(nCounter);
const auto nThreadLambda2 = CPLGetPID();
// fprintf(stderr, "lambda2 job=%d, counter(before)=%d, thread="
// CPL_FRMT_GIB "\n", iJob, nCounter, nThreadLambda2);
if (iJob == 100 + 0)
{
ASSERT_TRUE(nThreadLambda2 != psData2->nThreadLambda);
// make sure that job 0 run in the other thread
// takes sufficiently long that job 2 has been submitted
// before it completes
std::unique_lock<std::mutex> guard(psData2->psCtxt->mutex);
while (!psData2->psCtxt->you_can_leave)
{
psData2->psCtxt->cv.wait(guard);
}
}
else if (iJob == 100 + 1 || iJob == 100 + 2)
{
ASSERT_TRUE(nThreadLambda2 == psData2->nThreadLambda);
}
};
auto poQueue = psData->psCtxt->oThreadPool.CreateJobQueue();
Data d0(*psData);
d0.iJob = 100 + d0.iJob * 3 + 0;
Data d1(*psData);
d1.iJob = 100 + d1.iJob * 3 + 1;
Data d2(*psData);
d2.iJob = 100 + d2.iJob * 3 + 2;
poQueue->SubmitJob(lambda2, &d0);
poQueue->SubmitJob(lambda2, &d1);
poQueue->SubmitJob(lambda2, &d2);
if (psData->iJob == 0)
{
std::lock_guard<std::mutex> guard(psData->psCtxt->mutex);
psData->psCtxt->you_can_leave = true;
psData->psCtxt->cv.notify_one();
}
};
{
auto poQueue = ctxt.oThreadPool.CreateJobQueue();
Data data0(&ctxt, 0);
poQueue->SubmitJob(lambda, &data0);
}
{
auto poQueue = ctxt.oThreadPool.CreateJobQueue();
Data data1(&ctxt, 1);
Data data2(&ctxt, 2);
poQueue->SubmitJob(lambda, &data1);
poQueue->SubmitJob(lambda, &data2);
}
ASSERT_EQ(ctxt.nCounter, 3 * 3);
}
// Test /vsimem/ PRead() implementation
TEST_F(test_cpl, vsimem_pread)
{
char szContent[] = "abcd";
VSILFILE *fp = VSIFileFromMemBuffer(
"", reinterpret_cast<GByte *>(szContent), 4, FALSE);
VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
ASSERT_TRUE(poHandle->HasPRead());
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
ASSERT_EQ(std::string(szBuffer), std::string("bc"));
}
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
}
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
ASSERT_EQ(std::string(szBuffer), std::string());
}
VSIFCloseL(fp);
}
// Test regular file system PRead() implementation
TEST_F(test_cpl, file_system_pread)
{
VSILFILE *fp = VSIFOpenL("temp_test_64.bin", "wb+");
if (fp == nullptr)
return;
VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
poHandle->Write("abcd", 4, 1);
if (poHandle->HasPRead())
{
poHandle->Flush();
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
ASSERT_EQ(std::string(szBuffer), std::string("bc"));
}
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
}
{
char szBuffer[5] = {0};
ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
ASSERT_EQ(std::string(szBuffer), std::string());
}
}
VSIFCloseL(fp);
VSIUnlink("temp_test_64.bin");
}
// Test CPLMask implementation
TEST_F(test_cpl, CPLMask)
{
constexpr std::size_t sz = 71;
auto m = CPLMaskCreate(sz, true);
// Mask is set by default
for (std::size_t i = 0; i < sz; i++)
{
EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
}
VSIFree(m);
m = CPLMaskCreate(sz, false);
auto m2 = CPLMaskCreate(sz, false);
// Mask is unset by default
for (std::size_t i = 0; i < sz; i++)
{
EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
}
// Set a few bits
CPLMaskSet(m, 10);
CPLMaskSet(m, 33);
CPLMaskSet(m, 70);
// Check all bits
for (std::size_t i = 0; i < sz; i++)
{
if (i == 10 || i == 33 || i == 70)
{
EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
}
else
{
EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
}
}
// Unset some bits
CPLMaskClear(m, 10);
CPLMaskClear(m, 70);
// Check all bits
for (std::size_t i = 0; i < sz; i++)
{
if (i == 33)
{
EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
}
else
{
EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
}
}
CPLMaskSet(m2, 36);
CPLMaskMerge(m2, m, sz);
// Check all bits
for (std::size_t i = 0; i < sz; i++)
{
if (i == 36 || i == 33)
{
ASSERT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
}
else
{
ASSERT_EQ(CPLMaskGet(m2, i), false) << "bit " << i;
}
}
CPLMaskClearAll(m, sz);
CPLMaskSetAll(m2, sz);
// Check all bits
for (std::size_t i = 0; i < sz; i++)
{
EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
EXPECT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
}
VSIFree(m);
VSIFree(m2);
}
// Test cpl::ThreadSafeQueue
TEST_F(test_cpl, ThreadSafeQueue)
{
cpl::ThreadSafeQueue<int> queue;
ASSERT_TRUE(queue.empty());
ASSERT_EQ(queue.size(), 0U);
queue.push(1);
ASSERT_TRUE(!queue.empty());
ASSERT_EQ(queue.size(), 1U);
queue.clear();
ASSERT_TRUE(queue.empty());
ASSERT_EQ(queue.size(), 0U);
int val = 10;
queue.push(std::move(val));
ASSERT_TRUE(!queue.empty());
ASSERT_EQ(queue.size(), 1U);
ASSERT_EQ(queue.get_and_pop_front(), 10);
ASSERT_TRUE(queue.empty());
}
TEST_F(test_cpl, CPLGetExecPath)
{
std::vector<char> achBuffer(1024, 'x');
if (!CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())))
{
GTEST_SKIP() << "CPLGetExecPath() not implemented for this platform";
return;
}
bool bFoundNulTerminatedChar = false;
for (char ch : achBuffer)
{
if (ch == '\0')
{
bFoundNulTerminatedChar = true;
break;
}
}
ASSERT_TRUE(bFoundNulTerminatedChar);
// Check that the file exists
VSIStatBufL sStat;
EXPECT_EQ(VSIStatL(achBuffer.data(), &sStat), 0);
const std::string osStrBefore(achBuffer.data());
// Resize the buffer to just the minimum size
achBuffer.resize(strlen(achBuffer.data()) + 1);
EXPECT_TRUE(
CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())));
EXPECT_STREQ(osStrBefore.c_str(), achBuffer.data());
// Too small buffer
achBuffer.resize(achBuffer.size() - 1);
EXPECT_FALSE(
CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())));
}
TEST_F(test_cpl, VSIDuplicateFileSystemHandler)
{
{
CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
EXPECT_FALSE(VSIDuplicateFileSystemHandler(
"/vsi_i_dont_exist/", "/vsi_i_will_not_be_created/"));
}
{
CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
EXPECT_FALSE(
VSIDuplicateFileSystemHandler("/", "/vsi_i_will_not_be_created/"));
}
EXPECT_EQ(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
VSIFileManager::GetHandler("/"));
EXPECT_TRUE(
VSIDuplicateFileSystemHandler("/vsimem/", "/vsi_test_clone_vsimem/"));
EXPECT_NE(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
VSIFileManager::GetHandler("/"));
{
CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
EXPECT_FALSE(VSIDuplicateFileSystemHandler("/vsimem/",
"/vsi_test_clone_vsimem/"));
}
}
TEST_F(test_cpl, CPLAtoGIntBigEx)
{
{
int bOverflow = 0;
EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775807", false, &bOverflow),
std::numeric_limits<int64_t>::max());
EXPECT_EQ(bOverflow, FALSE);
}
{
int bOverflow = 0;
EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775808", false, &bOverflow),
std::numeric_limits<int64_t>::max());
EXPECT_EQ(bOverflow, TRUE);
}
{
int bOverflow = 0;
EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775808", false, &bOverflow),
std::numeric_limits<int64_t>::min());
EXPECT_EQ(bOverflow, FALSE);
}
{
int bOverflow = 0;
EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775809", false, &bOverflow),
std::numeric_limits<int64_t>::min());
EXPECT_EQ(bOverflow, TRUE);
}
}
TEST_F(test_cpl, CPLSubscribeToSetConfigOption)
{
struct Event
{
std::string osKey;
std::string osValue;
bool bThreadLocal;
};
std::vector<Event> events;
const auto cbk = +[](const char *pszKey, const char *pszValue,
bool bThreadLocal, void *pUserData)
{
std::vector<Event> *pEvents =
static_cast<std::vector<Event> *>(pUserData);
Event ev;
ev.osKey = pszKey;
ev.osValue = pszValue ? pszValue : "";
ev.bThreadLocal = bThreadLocal;
pEvents->emplace_back(ev);
};
// Subscribe and unsubscribe immediately
{
int nId = CPLSubscribeToSetConfigOption(cbk, &events);
CPLSetConfigOption("CPLSubscribeToSetConfigOption", "bar");
EXPECT_EQ(events.size(), 1U);
if (!events.empty())
{
EXPECT_STREQ(events[0].osKey.c_str(),
"CPLSubscribeToSetConfigOption");
EXPECT_STREQ(events[0].osValue.c_str(), "bar");
EXPECT_FALSE(events[0].bThreadLocal);
}
CPLUnsubscribeToSetConfigOption(nId);
}
events.clear();
// Subscribe and unsubscribe in non-nested order
{
int nId1 = CPLSubscribeToSetConfigOption(cbk, &events);
int nId2 = CPLSubscribeToSetConfigOption(cbk, &events);
CPLUnsubscribeToSetConfigOption(nId1);
int nId3 = CPLSubscribeToSetConfigOption(cbk, &events);
CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
EXPECT_EQ(events.size(), 2U);
CPLUnsubscribeToSetConfigOption(nId2);
CPLUnsubscribeToSetConfigOption(nId3);
CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
EXPECT_EQ(events.size(), 2U);
}
}
} // namespace