• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15899162844

26 Jun 2025 10:14AM UTC coverage: 71.088% (+0.004%) from 71.084%
15899162844

Pull #12623

github

web-flow
Merge c704a8392 into f5cb024d4
Pull Request #12623: gdal raster overview add: add a --overview-src option

209 of 244 new or added lines in 5 files covered. (85.66%)

96 existing lines in 44 files now uncovered.

574014 of 807474 relevant lines covered (71.09%)

250815.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.75
/autotest/cpp/test_cpl.cpp
1
///////////////////////////////////////////////////////////////////////////////
2
//
3
// Project:  C++ Test Suite for GDAL/OGR
4
// Purpose:  Test general CPL features.
5
// Author:   Mateusz Loskot <mateusz@loskot.net>
6
//
7
///////////////////////////////////////////////////////////////////////////////
8
// Copyright (c) 2006, Mateusz Loskot <mateusz@loskot.net>
9
// Copyright (c) 2008-2012, Even Rouault <even dot rouault at spatialys.com>
10
// Copyright (c) 2017, Dmitry Baryshnikov <polimax@mail.ru>
11
// Copyright (c) 2017, NextGIS <info@nextgis.com>
12
/*
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15

16
#ifndef GDAL_COMPILATION
17
#define GDAL_COMPILATION
18
#endif
19

20
#include "gdal_unit_test.h"
21

22
#include "cpl_compressor.h"
23
#include "cpl_error.h"
24
#include "cpl_float.h"
25
#include "cpl_hash_set.h"
26
#include "cpl_levenshtein.h"
27
#include "cpl_list.h"
28
#include "cpl_mask.h"
29
#include "cpl_sha256.h"
30
#include "cpl_string.h"
31
#include "cpl_safemaths.hpp"
32
#include "cpl_time.h"
33
#include "cpl_json.h"
34
#include "cpl_json_streaming_parser.h"
35
#include "cpl_json_streaming_writer.h"
36
#include "cpl_mem_cache.h"
37
#include "cpl_http.h"
38
#include "cpl_auto_close.h"
39
#include "cpl_minixml.h"
40
#include "cpl_quad_tree.h"
41
#include "cpl_spawn.h"
42
#include "cpl_worker_thread_pool.h"
43
#include "cpl_vsi_virtual.h"
44
#include "cpl_threadsafe_queue.hpp"
45

46
#include <atomic>
47
#include <cmath>
48
#include <limits>
49
#include <fstream>
50
#include <string>
51

52
#include "gtest_include.h"
53

54
static bool gbGotError = false;
55

56
static void CPL_STDCALL myErrorHandler(CPLErr, CPLErrorNum, const char *)
2✔
57
{
58
    gbGotError = true;
2✔
59
}
2✔
60

61
namespace
62
{
63

64
// Common fixture with test data
65
struct test_cpl : public ::testing::Test
66
{
67
    std::string data_;
68

69
    test_cpl()
93✔
70
    {
93✔
71
        // Compose data path for test group
72
        data_ = tut::common::data_basedir;
93✔
73
    }
93✔
74

75
    void SetUp() override
93✔
76
    {
77
        CPLSetConfigOptions(nullptr);
93✔
78
        CPLSetThreadLocalConfigOptions(nullptr);
93✔
79
    }
93✔
80
};
81

82
// Test cpl_list API
83
TEST_F(test_cpl, CPLList)
4✔
84
{
85
    CPLList *list;
86

87
    list = CPLListInsert(nullptr, (void *)nullptr, 0);
1✔
88
    EXPECT_TRUE(CPLListCount(list) == 1);
1✔
89
    list = CPLListRemove(list, 2);
1✔
90
    EXPECT_TRUE(CPLListCount(list) == 1);
1✔
91
    list = CPLListRemove(list, 1);
1✔
92
    EXPECT_TRUE(CPLListCount(list) == 1);
1✔
93
    list = CPLListRemove(list, 0);
1✔
94
    EXPECT_TRUE(CPLListCount(list) == 0);
1✔
95
    list = nullptr;
1✔
96

97
    list = CPLListInsert(nullptr, (void *)nullptr, 2);
1✔
98
    EXPECT_TRUE(CPLListCount(list) == 3);
1✔
99
    list = CPLListRemove(list, 2);
1✔
100
    EXPECT_TRUE(CPLListCount(list) == 2);
1✔
101
    list = CPLListRemove(list, 1);
1✔
102
    EXPECT_TRUE(CPLListCount(list) == 1);
1✔
103
    list = CPLListRemove(list, 0);
1✔
104
    EXPECT_TRUE(CPLListCount(list) == 0);
1✔
105
    list = nullptr;
1✔
106

107
    list = CPLListAppend(list, (void *)1);
1✔
108
    EXPECT_TRUE(CPLListGet(list, 0) == list);
1✔
109
    EXPECT_TRUE(CPLListGet(list, 1) == nullptr);
1✔
110
    list = CPLListAppend(list, (void *)2);
1✔
111
    list = CPLListInsert(list, (void *)3, 2);
1✔
112
    EXPECT_TRUE(CPLListCount(list) == 3);
1✔
113
    CPLListDestroy(list);
1✔
114
    list = nullptr;
1✔
115

116
    list = CPLListAppend(list, (void *)1);
1✔
117
    list = CPLListAppend(list, (void *)2);
1✔
118
    list = CPLListInsert(list, (void *)4, 3);
1✔
119
    CPLListGet(list, 2)->pData = (void *)3;
1✔
120
    EXPECT_TRUE(CPLListCount(list) == 4);
1✔
121
    EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
1✔
122
    EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
1✔
123
    EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
1✔
124
    EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
1✔
125
    CPLListDestroy(list);
1✔
126
    list = nullptr;
1✔
127

128
    list = CPLListInsert(list, (void *)4, 1);
1✔
129
    CPLListGet(list, 0)->pData = (void *)2;
1✔
130
    list = CPLListInsert(list, (void *)1, 0);
1✔
131
    list = CPLListInsert(list, (void *)3, 2);
1✔
132
    EXPECT_TRUE(CPLListCount(list) == 4);
1✔
133
    EXPECT_TRUE(CPLListGet(list, 0)->pData == (void *)1);
1✔
134
    EXPECT_TRUE(CPLListGet(list, 1)->pData == (void *)2);
1✔
135
    EXPECT_TRUE(CPLListGet(list, 2)->pData == (void *)3);
1✔
136
    EXPECT_TRUE(CPLListGet(list, 3)->pData == (void *)4);
1✔
137
    list = CPLListRemove(list, 1);
1✔
138
    list = CPLListRemove(list, 1);
1✔
139
    list = CPLListRemove(list, 0);
1✔
140
    list = CPLListRemove(list, 0);
1✔
141
    EXPECT_TRUE(list == nullptr);
1✔
142
}
1✔
143

144
typedef struct
145
{
146
    const char *testString;
147
    CPLValueType expectedResult;
148
} TestStringStruct;
149

150
// Test CPLGetValueType
151
TEST_F(test_cpl, CPLGetValueType)
4✔
152
{
153
    TestStringStruct asTestStrings[] = {
1✔
154
        {"+25.e+3", CPL_VALUE_REAL},   {"-25.e-3", CPL_VALUE_REAL},
155
        {"25.e3", CPL_VALUE_REAL},     {"25e3", CPL_VALUE_REAL},
156
        {" 25e3 ", CPL_VALUE_REAL},    {".1e3", CPL_VALUE_REAL},
157

158
        {"25", CPL_VALUE_INTEGER},     {"-25", CPL_VALUE_INTEGER},
159
        {"+25", CPL_VALUE_INTEGER},
160

161
        {"25e 3", CPL_VALUE_STRING},   {"25e.3", CPL_VALUE_STRING},
162
        {"-2-5e3", CPL_VALUE_STRING},  {"2-5e3", CPL_VALUE_STRING},
163
        {"25.25.3", CPL_VALUE_STRING}, {"25e25e3", CPL_VALUE_STRING},
164
        {"25e2500", CPL_VALUE_STRING}, /* #6128 */
165

166
        {"d1", CPL_VALUE_STRING}, /* #6305 */
167

168
        {"01", CPL_VALUE_STRING},      {"0.1", CPL_VALUE_REAL},
169
        {"0", CPL_VALUE_INTEGER},
170
    };
171

172
    for (const auto &sText : asTestStrings)
21✔
173
    {
174
        EXPECT_EQ(CPLGetValueType(sText.testString), sText.expectedResult)
20✔
175
            << sText.testString;
×
176
    }
177
}
1✔
178

179
// Test cpl_hash_set API
180
TEST_F(test_cpl, CPLHashSet)
4✔
181
{
182
    CPLHashSet *set =
183
        CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
1✔
184
    EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("hello")) == TRUE);
1✔
185
    EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("good morning")) == TRUE);
1✔
186
    EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == TRUE);
1✔
187
    EXPECT_TRUE(CPLHashSetSize(set) == 3);
1✔
188
    EXPECT_TRUE(CPLHashSetInsert(set, CPLStrdup("bye bye")) == FALSE);
1✔
189
    EXPECT_TRUE(CPLHashSetSize(set) == 3);
1✔
190
    EXPECT_TRUE(CPLHashSetRemove(set, "bye bye") == TRUE);
1✔
191
    EXPECT_TRUE(CPLHashSetSize(set) == 2);
1✔
192
    EXPECT_TRUE(CPLHashSetRemove(set, "good afternoon") == FALSE);
1✔
193
    EXPECT_TRUE(CPLHashSetSize(set) == 2);
1✔
194
    CPLHashSetDestroy(set);
1✔
195
}
1✔
196

197
static int sumValues(void *elt, void *user_data)
1,000✔
198
{
199
    int *pnSum = (int *)user_data;
1,000✔
200
    *pnSum += *(int *)elt;
1,000✔
201
    return TRUE;
1,000✔
202
}
203

204
// Test cpl_hash_set API
205
TEST_F(test_cpl, CPLHashSet2)
4✔
206
{
207
    const int HASH_SET_SIZE = 1000;
1✔
208

209
    int data[HASH_SET_SIZE];
210
    for (int i = 0; i < HASH_SET_SIZE; ++i)
1,001✔
211
    {
212
        data[i] = i;
1,000✔
213
    }
214

215
    CPLHashSet *set = CPLHashSetNew(nullptr, nullptr, nullptr);
1✔
216
    for (int i = 0; i < HASH_SET_SIZE; i++)
1,001✔
217
    {
218
        EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == TRUE);
1,000✔
219
    }
220
    EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
1✔
221

222
    for (int i = 0; i < HASH_SET_SIZE; i++)
1,001✔
223
    {
224
        EXPECT_TRUE(CPLHashSetInsert(set, (void *)&data[i]) == FALSE);
1,000✔
225
    }
226
    EXPECT_EQ(CPLHashSetSize(set), HASH_SET_SIZE);
1✔
227

228
    for (int i = 0; i < HASH_SET_SIZE; i++)
1,001✔
229
    {
230
        EXPECT_TRUE(CPLHashSetLookup(set, (const void *)&data[i]) ==
1,000✔
231
                    (const void *)&data[i]);
232
    }
233

234
    int sum = 0;
1✔
235
    CPLHashSetForeach(set, sumValues, &sum);
1✔
236
    EXPECT_EQ(sum, (HASH_SET_SIZE - 1) * HASH_SET_SIZE / 2);
1✔
237

238
    for (int i = 0; i < HASH_SET_SIZE; i++)
1,001✔
239
    {
240
        EXPECT_TRUE(CPLHashSetRemove(set, (void *)&data[i]) == TRUE);
1,000✔
241
    }
242
    EXPECT_EQ(CPLHashSetSize(set), 0);
1✔
243

244
    CPLHashSetDestroy(set);
1✔
245
}
1✔
246

247
// Test cpl_string API
248
TEST_F(test_cpl, CSLTokenizeString2)
4✔
249
{
250
    {
251
        CPLStringList aosStringList(
252
            CSLTokenizeString2("one two three", " ", 0));
1✔
253
        ASSERT_EQ(aosStringList.size(), 3);
1✔
254
        EXPECT_STREQ(aosStringList[0], "one");
1✔
255
        EXPECT_STREQ(aosStringList[1], "two");
1✔
256
        EXPECT_STREQ(aosStringList[2], "three");
1✔
257

258
        // Test range-based for loop
259
        int i = 0;
1✔
260
        for (const char *pszVal : aosStringList)
4✔
261
        {
262
            EXPECT_STREQ(pszVal, aosStringList[i]);
3✔
263
            ++i;
3✔
264
        }
265
        EXPECT_EQ(i, 3);
1✔
266
    }
267
    {
268
        CPLStringList aosStringList;
2✔
269
        // Test range-based for loop on empty list
270
        int i = 0;
1✔
271
        for (const char *pszVal : aosStringList)
1✔
272
        {
273
            EXPECT_EQ(pszVal, nullptr);  // should not reach that point...
×
274
            ++i;
×
275
        }
276
        EXPECT_EQ(i, 0);
1✔
277
    }
278
    {
279
        CPLStringList aosStringList(
280
            CSLTokenizeString2("one two, three;four,five; six", " ;,", 0));
1✔
281
        ASSERT_EQ(aosStringList.size(), 6);
1✔
282
        EXPECT_STREQ(aosStringList[0], "one");
1✔
283
        EXPECT_STREQ(aosStringList[1], "two");
1✔
284
        EXPECT_STREQ(aosStringList[2], "three");
1✔
285
        EXPECT_STREQ(aosStringList[3], "four");
1✔
286
        EXPECT_STREQ(aosStringList[4], "five");
1✔
287
        EXPECT_STREQ(aosStringList[5], "six");
1✔
288
    }
289

290
    {
291
        CPLStringList aosStringList(CSLTokenizeString2(
292
            "one two,,,five,six", " ,", CSLT_ALLOWEMPTYTOKENS));
1✔
293
        ASSERT_EQ(aosStringList.size(), 6);
1✔
294
        EXPECT_STREQ(aosStringList[0], "one");
1✔
295
        EXPECT_STREQ(aosStringList[1], "two");
1✔
296
        EXPECT_STREQ(aosStringList[2], "");
1✔
297
        EXPECT_STREQ(aosStringList[3], "");
1✔
298
        EXPECT_STREQ(aosStringList[4], "five");
1✔
299
        EXPECT_STREQ(aosStringList[5], "six");
1✔
300
    }
301

302
    {
303
        CPLStringList aosStringList(CSLTokenizeString2(
304
            "one two,\"three,four ,\",five,six", " ,", CSLT_HONOURSTRINGS));
1✔
305
        ASSERT_EQ(aosStringList.size(), 5);
1✔
306
        EXPECT_STREQ(aosStringList[0], "one");
1✔
307
        EXPECT_STREQ(aosStringList[1], "two");
1✔
308
        EXPECT_STREQ(aosStringList[2], "three,four ,");
1✔
309
        EXPECT_STREQ(aosStringList[3], "five");
1✔
310
        EXPECT_STREQ(aosStringList[4], "six");
1✔
311
    }
312

313
    {
314
        CPLStringList aosStringList(CSLTokenizeString2(
315
            "one two,\"three,four ,\",five,six", " ,", CSLT_PRESERVEQUOTES));
1✔
316
        ASSERT_EQ(aosStringList.size(), 7);
1✔
317
        EXPECT_STREQ(aosStringList[0], "one");
1✔
318
        EXPECT_STREQ(aosStringList[1], "two");
1✔
319
        EXPECT_STREQ(aosStringList[2], "\"three");
1✔
320
        EXPECT_STREQ(aosStringList[3], "four");
1✔
321
        EXPECT_STREQ(aosStringList[4], "\"");
1✔
322
        EXPECT_STREQ(aosStringList[5], "five");
1✔
323
        EXPECT_STREQ(aosStringList[6], "six");
1✔
324
    }
325

326
    {
327
        CPLStringList aosStringList(
328
            CSLTokenizeString2("one two,\"three,four ,\",five,six", " ,",
329
                               CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES));
1✔
330
        ASSERT_EQ(aosStringList.size(), 5);
1✔
331
        EXPECT_STREQ(aosStringList[0], "one");
1✔
332
        EXPECT_STREQ(aosStringList[1], "two");
1✔
333
        EXPECT_STREQ(aosStringList[2], "\"three,four ,\"");
1✔
334
        EXPECT_STREQ(aosStringList[3], "five");
1✔
335
        EXPECT_STREQ(aosStringList[4], "six");
1✔
336
    }
337

338
    {
339
        CPLStringList aosStringList(
340
            CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
341
                               CSLT_PRESERVEESCAPES));
1✔
342
        ASSERT_EQ(aosStringList.size(), 7);
1✔
343
        EXPECT_STREQ(aosStringList[0], "one");
1✔
344
        EXPECT_STREQ(aosStringList[1], "\\two");
1✔
345
        EXPECT_STREQ(aosStringList[2], "\"three");
1✔
346
        EXPECT_STREQ(aosStringList[3], "\\four");
1✔
347
        EXPECT_STREQ(aosStringList[4], "\"");
1✔
348
        EXPECT_STREQ(aosStringList[5], "five");
1✔
349
        EXPECT_STREQ(aosStringList[6], "six");
1✔
350
    }
351

352
    {
353
        CPLStringList aosStringList(
354
            CSLTokenizeString2("one \\two,\"three,\\four ,\",five,six", " ,",
355
                               CSLT_PRESERVEQUOTES | CSLT_PRESERVEESCAPES));
1✔
356
        ASSERT_EQ(aosStringList.size(), 7);
1✔
357
        EXPECT_STREQ(aosStringList[0], "one");
1✔
358
        EXPECT_STREQ(aosStringList[1], "\\two");
1✔
359
        EXPECT_STREQ(aosStringList[2], "\"three");
1✔
360
        EXPECT_STREQ(aosStringList[3], "\\four");
1✔
361
        EXPECT_STREQ(aosStringList[4], "\"");
1✔
362
        EXPECT_STREQ(aosStringList[5], "five");
1✔
363
        EXPECT_STREQ(aosStringList[6], "six");
1✔
364
    }
365

366
    {
367
        CPLStringList aosStringList(
368
            CSLTokenizeString2("one ,two, three, four ,five  ", ",", 0));
1✔
369
        ASSERT_EQ(aosStringList.size(), 5);
1✔
370
        EXPECT_STREQ(aosStringList[0], "one ");
1✔
371
        EXPECT_STREQ(aosStringList[1], "two");
1✔
372
        EXPECT_STREQ(aosStringList[2], " three");
1✔
373
        EXPECT_STREQ(aosStringList[3], " four ");
1✔
374
        EXPECT_STREQ(aosStringList[4], "five  ");
1✔
375
    }
376

377
    {
378
        CPLStringList aosStringList(CSLTokenizeString2(
379
            "one ,two, three, four ,five  ", ",", CSLT_STRIPLEADSPACES));
1✔
380
        ASSERT_EQ(aosStringList.size(), 5);
1✔
381
        EXPECT_STREQ(aosStringList[0], "one ");
1✔
382
        EXPECT_STREQ(aosStringList[1], "two");
1✔
383
        EXPECT_STREQ(aosStringList[2], "three");
1✔
384
        EXPECT_STREQ(aosStringList[3], "four ");
1✔
385
        EXPECT_STREQ(aosStringList[4], "five  ");
1✔
386
    }
387

388
    {
389
        CPLStringList aosStringList(CSLTokenizeString2(
390
            "one ,two, three, four ,five  ", ",", CSLT_STRIPENDSPACES));
1✔
391
        ASSERT_EQ(aosStringList.size(), 5);
1✔
392
        EXPECT_STREQ(aosStringList[0], "one");
1✔
393
        EXPECT_STREQ(aosStringList[1], "two");
1✔
394
        EXPECT_STREQ(aosStringList[2], " three");
1✔
395
        EXPECT_STREQ(aosStringList[3], " four");
1✔
396
        EXPECT_STREQ(aosStringList[4], "five");
1✔
397
    }
398

399
    {
400
        CPLStringList aosStringList(
401
            CSLTokenizeString2("one ,two, three, four ,five  ", ",",
402
                               CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
1✔
403
        ASSERT_EQ(aosStringList.size(), 5);
1✔
404
        EXPECT_STREQ(aosStringList[0], "one");
1✔
405
        EXPECT_STREQ(aosStringList[1], "two");
1✔
406
        EXPECT_STREQ(aosStringList[2], "three");
1✔
407
        EXPECT_STREQ(aosStringList[3], "four");
1✔
408
        EXPECT_STREQ(aosStringList[4], "five");
1✔
409
    }
410

411
    {
412
        const std::vector<std::string> oVector{"a", "bc"};
5✔
413
        // Test CPLStringList(const std::vector<std::string>&) constructor
414
        const CPLStringList aosList(oVector);
1✔
415
        ASSERT_EQ(aosList.size(), 2);
1✔
416
        EXPECT_STREQ(aosList[0], "a");
1✔
417
        EXPECT_STREQ(aosList[1], "bc");
1✔
418
        EXPECT_EQ(aosList[2], nullptr);
1✔
419

420
        // Test CPLStringList::operator std::vector<std::string>(void) const
421
        const std::vector<std::string> oVector2(aosList);
2✔
422
        EXPECT_EQ(oVector, oVector2);
1✔
423

424
        EXPECT_EQ(oVector, cpl::ToVector(aosList.List()));
2✔
425
    }
426

427
    {
428
        const CPLStringList aosList(std::vector<std::string>{});
2✔
429
        EXPECT_EQ(aosList.List(), nullptr);
1✔
430
    }
431

432
    {
433
        // Test CPLStringList(std::initializer_list<const char*>) constructor
434
        const CPLStringList aosList{"a", "bc"};
1✔
435
        ASSERT_EQ(aosList.size(), 2);
1✔
436
        EXPECT_STREQ(aosList[0], "a");
1✔
437
        EXPECT_STREQ(aosList[1], "bc");
1✔
438
        EXPECT_EQ(aosList[2], nullptr);
1✔
439

440
        // Test cpl::Iterate(CSLConstList)
441
        CSLConstList papszList = aosList.List();
1✔
442
        CPLStringList aosList2;
1✔
443
        for (const char *pszStr : cpl::Iterate(papszList))
3✔
444
        {
445
            aosList2.AddString(pszStr);
2✔
446
        }
447
        ASSERT_EQ(aosList2.size(), 2);
1✔
448
        EXPECT_STREQ(aosList2[0], "a");
1✔
449
        EXPECT_STREQ(aosList2[1], "bc");
1✔
450
        EXPECT_EQ(aosList2[2], nullptr);
1✔
451
    }
452

453
    {
454
        // Test cpl::Iterate() on a null list
455
        CSLConstList papszList = nullptr;
1✔
456
        auto oIteratorWrapper = cpl::Iterate(papszList);
1✔
457
        EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
1✔
458
    }
459

460
    {
461
        // Test cpl::IterateNameValue()
462
        const CPLStringList aosList{"foo=bar", "illegal", "bar=baz"};
1✔
463
        CSLConstList papszList = aosList.List();
1✔
464
        std::map<std::string, std::string> oMap;
1✔
465
        for (const auto &[name, value] : cpl::IterateNameValue(papszList))
3✔
466
        {
467
            oMap[name] = value;
2✔
468
        }
469
        ASSERT_EQ(oMap.size(), 2);
1✔
470
        EXPECT_EQ(oMap["foo"], "bar");
2✔
471
        EXPECT_EQ(oMap["bar"], "baz");
2✔
472
    }
473

474
    {
475
        // Test cpl::IterateNameValue() on a list with only invalid values
476
        const CPLStringList aosList{"illegal"};
2✔
477
        CSLConstList papszList = aosList.List();
1✔
478
        auto oIteratorWrapper = cpl::IterateNameValue(papszList);
1✔
479
        EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
1✔
480
    }
481

482
    {
483
        // Test cpl::IterateNameValue() on a null list
484
        CSLConstList papszList = nullptr;
1✔
485
        auto oIteratorWrapper = cpl::IterateNameValue(papszList);
1✔
486
        EXPECT_TRUE(oIteratorWrapper.begin() == oIteratorWrapper.end());
1✔
487
    }
488
}
489

490
typedef struct
491
{
492
    char szEncoding[24];
493
    char szString[1024 - 24];
494
} TestRecodeStruct;
495

496
// Test cpl_recode API
497
TEST_F(test_cpl, CPLRecode)
4✔
498
{
499
    /*
500
     * NOTE: This test will generally fail if iconv() is not
501
     *       linked in.
502
     *
503
     * CPLRecode() will be tested using the test file containing
504
     * a list of strings of the same text in different encoding. The
505
     * string is non-ASCII to avoid trivial transformations. Test file
506
     * has a simple binary format: a table of records, each record
507
     * is 1024 bytes long. The first 24 bytes of each record contain
508
     * encoding name (ASCII, zero padded), the last 1000 bytes contain
509
     * encoded string, zero padded.
510
     *
511
     * NOTE 1: We can't use a test file in human readable text format
512
     *         here because of multiple different encodings including
513
     *         multibyte ones.
514
     *
515
     * The test file could be generated with the following simple shell
516
     * script:
517
     *
518
     * #!/bin/sh
519
     *
520
     * # List of encodings to convert the test string into
521
     * ENCODINGS="UTF-8 CP1251 KOI8-R UCS-2 UCS-2BE UCS-2LE UCS-4 UCS-4BE
522
     * UCS-4LE UTF-16 UTF-32" # The test string itself in UTF-8 encoding. # This
523
     * means "Improving GDAL internationalization." in Russian.
524
     * TESTSTRING="\u0423\u043b\u0443\u0447\u0448\u0430\u0435\u043c
525
     * \u0438\u043d\u0442\u0435\u0440\u043d\u0430\u0446\u0438\u043e\u043d\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e
526
     * GDAL."
527
     *
528
     * RECORDSIZE=1024
529
     * ENCSIZE=24
530
     *
531
     * i=0
532
     * for enc in ${ENCODINGS}; do
533
     *  env printf "${enc}" | dd ibs=${RECORDSIZE} conv=sync obs=1
534
     * seek=$((${RECORDSIZE}*${i})) of="recode-rus.dat" status=noxfer env printf
535
     * "${TESTSTRING}" | iconv -t ${enc} | dd ibs=${RECORDSIZE} conv=sync obs=1
536
     * seek=$((${RECORDSIZE}*${i}+${ENCSIZE})) of="recode-rus.dat" status=noxfer
537
     *  i=$((i+1))
538
     * done
539
     *
540
     * NOTE 2: The test string is encoded with the special format
541
     *         "\uXXXX" sequences, so we able to paste it here.
542
     *
543
     * NOTE 3: We need a printf utility from the coreutils because of
544
     *         that. "env printf" should work avoiding the shell
545
     *         built-in.
546
     *
547
     * NOTE 4: "iconv" utility without the "-f" option will work with
548
     *         encoding read from the current locale.
549
     *
550
     *  TODO: 1. Add more encodings maybe more test files.
551
     *        2. Add test for CPLRecodeFromWChar()/CPLRecodeToWChar().
552
     *        3. Test translation between each possible pair of
553
     *        encodings in file, not only into the UTF-8.
554
     */
555

556
    std::ifstream fin((data_ + SEP + "recode-rus.dat").c_str(),
2✔
557
                      std::ifstream::binary);
2✔
558
    TestRecodeStruct oReferenceString;
559

560
    // Read reference string (which is the first one in the file)
561
    fin.read(oReferenceString.szEncoding, sizeof(oReferenceString.szEncoding));
1✔
562
    oReferenceString.szEncoding[sizeof(oReferenceString.szEncoding) - 1] = '\0';
1✔
563
    fin.read(oReferenceString.szString, sizeof(oReferenceString.szString));
1✔
564
    oReferenceString.szString[sizeof(oReferenceString.szString) - 1] = '\0';
1✔
565

566
    while (true)
567
    {
568
        TestRecodeStruct oTestString;
569

570
        fin.read(oTestString.szEncoding, sizeof(oTestString.szEncoding));
11✔
571
        oTestString.szEncoding[sizeof(oTestString.szEncoding) - 1] = '\0';
11✔
572
        if (fin.eof())
11✔
573
            break;
1✔
574
        fin.read(oTestString.szString, sizeof(oTestString.szString));
10✔
575
        oTestString.szString[sizeof(oTestString.szString) - 1] = '\0';
10✔
576

577
        // Compare each string with the reference one
578
        CPLErrorReset();
10✔
579
        char *pszDecodedString =
580
            CPLRecode(oTestString.szString, oTestString.szEncoding,
10✔
581
                      oReferenceString.szEncoding);
582
        if (strstr(CPLGetLastErrorMsg(),
10✔
583
                   "Recode from CP1251 to UTF-8 not supported") != nullptr ||
20✔
584
            strstr(CPLGetLastErrorMsg(),
10✔
585
                   "Recode from KOI8-R to UTF-8 not supported") != nullptr)
586
        {
587
            CPLFree(pszDecodedString);
×
588
            break;
×
589
        }
590

591
        size_t nLength =
10✔
592
            MIN(strlen(pszDecodedString), sizeof(oReferenceString.szEncoding));
10✔
593
        bool bOK =
10✔
594
            (memcmp(pszDecodedString, oReferenceString.szString, nLength) == 0);
10✔
595
        // FIXME Some tests fail on Mac. Not sure why, but do not error out just
596
        // for that
597
        if (!bOK &&
10✔
598
            (strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") !=
×
599
                 nullptr ||
×
600
             strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
×
601
             getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
×
602
        {
603
            fprintf(stderr, "Recode from %s failed\n", oTestString.szEncoding);
×
604
        }
605
        else
606
        {
607
#ifdef CPL_MSB
608
            if (!bOK && strcmp(oTestString.szEncoding, "UCS-2") == 0)
609
            {
610
                // Presumably the content in the test file is UCS-2LE, but
611
                // there's no way to know the byte order without a BOM
612
                fprintf(stderr, "Recode from %s failed\n",
613
                        oTestString.szEncoding);
614
            }
615
            else
616
#endif
617
            {
618
                EXPECT_TRUE(bOK) << "Recode from " << oTestString.szEncoding;
10✔
619
            }
620
        }
621
        CPLFree(pszDecodedString);
10✔
622
    }
10✔
623

624
    fin.close();
1✔
625
}
1✔
626

627
/************************************************************************/
628
/*                         CPLStringList tests                          */
629
/************************************************************************/
630
TEST_F(test_cpl, CPLStringList_Base)
4✔
631
{
632
    CPLStringList oCSL;
1✔
633

634
    ASSERT_TRUE(oCSL.List() == nullptr);
1✔
635

636
    oCSL.AddString("def");
1✔
637
    oCSL.AddString("abc");
1✔
638

639
    ASSERT_EQ(oCSL.Count(), 2);
1✔
640
    ASSERT_TRUE(EQUAL(oCSL[0], "def"));
1✔
641
    ASSERT_TRUE(EQUAL(oCSL[1], "abc"));
1✔
642
    ASSERT_TRUE(oCSL[17] == nullptr);
1✔
643
    ASSERT_TRUE(oCSL[-1] == nullptr);
1✔
644
    ASSERT_EQ(oCSL.FindString("abc"), 1);
1✔
645

646
    CSLDestroy(oCSL.StealList());
1✔
647
    ASSERT_EQ(oCSL.Count(), 0);
1✔
648
    ASSERT_TRUE(oCSL.List() == nullptr);
1✔
649

650
    // Test that the list will make an internal copy when needed to
651
    // modify a read-only list.
652

653
    oCSL.AddString("def");
1✔
654
    oCSL.AddString("abc");
1✔
655

656
    CPLStringList oCopy(oCSL.List(), FALSE);
1✔
657

658
    ASSERT_EQ(oCSL.List(), oCopy.List());
1✔
659
    ASSERT_EQ(oCSL.Count(), oCopy.Count());
1✔
660

661
    oCopy.AddString("xyz");
1✔
662
    ASSERT_TRUE(oCSL.List() != oCopy.List());
1✔
663
    ASSERT_EQ(oCopy.Count(), 3);
1✔
664
    ASSERT_EQ(oCSL.Count(), 2);
1✔
665
    ASSERT_TRUE(EQUAL(oCopy[2], "xyz"));
1✔
666
}
667

668
TEST_F(test_cpl, CPLStringList_NameValue)
4✔
669
{
670
    // Test some name=value handling stuff.
671
    CPLStringList oNVL;
1✔
672

673
    oNVL.AddNameValue("KEY1", "VALUE1");
1✔
674
    oNVL.AddNameValue("2KEY", "VALUE2");
1✔
675
    ASSERT_EQ(oNVL.Count(), 2);
1✔
676
    ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE2"));
1✔
677
    ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
1✔
678

679
    oNVL.AddNameValue("KEY1", "VALUE3");
1✔
680
    ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("KEY1"), "VALUE1"));
1✔
681
    ASSERT_TRUE(EQUAL(oNVL[2], "KEY1=VALUE3"));
1✔
682
    ASSERT_TRUE(EQUAL(oNVL.FetchNameValueDef("MISSING", "X"), "X"));
1✔
683

684
    oNVL.SetNameValue("2KEY", "VALUE4");
1✔
685
    ASSERT_TRUE(EQUAL(oNVL.FetchNameValue("2KEY"), "VALUE4"));
1✔
686
    ASSERT_EQ(oNVL.Count(), 3);
1✔
687

688
    // make sure deletion works.
689
    oNVL.SetNameValue("2KEY", nullptr);
1✔
690
    ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
1✔
691
    ASSERT_EQ(oNVL.Count(), 2);
1✔
692

693
    // Test boolean support.
694
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
1✔
695
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), FALSE);
1✔
696

697
    oNVL.SetNameValue("BOOL", "YES");
1✔
698
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), TRUE);
1✔
699
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
1✔
700

701
    oNVL.SetNameValue("BOOL", "1");
1✔
702
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
1✔
703

704
    oNVL.SetNameValue("BOOL", "0");
1✔
705
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
1✔
706

707
    oNVL.SetNameValue("BOOL", "FALSE");
1✔
708
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", TRUE), FALSE);
1✔
709

710
    oNVL.SetNameValue("BOOL", "ON");
1✔
711
    ASSERT_EQ(oNVL.FetchBoolean("BOOL", FALSE), TRUE);
1✔
712

713
    // Test assignment operator.
714
    CPLStringList oCopy;
1✔
715

716
    {
717
        CPLStringList oTemp;
2✔
718
        oTemp.AddString("test");
1✔
719
        oCopy = oTemp;
1✔
720
        oTemp.AddString("avoid_coverity_scan_warning");
1✔
721
    }
722
    EXPECT_STREQ(oCopy[0], "test");
1✔
723

724
    auto &oCopyRef(oCopy);
1✔
725
    oCopy = oCopyRef;
1✔
726
    EXPECT_STREQ(oCopy[0], "test");
1✔
727

728
    // Test copy constructor.
729
    CPLStringList oCopy2(oCopy);
1✔
730
    EXPECT_EQ(oCopy2.Count(), oCopy.Count());
1✔
731
    oCopy.Clear();
1✔
732
    EXPECT_STREQ(oCopy2[0], "test");
1✔
733

734
    // Test move constructor
735
    CPLStringList oMoved(std::move(oCopy2));
1✔
736
    EXPECT_STREQ(oMoved[0], "test");
1✔
737

738
    // Test move assignment operator
739
    CPLStringList oMoved2;
1✔
740
    oMoved2 = std::move(oMoved);
1✔
741
    EXPECT_STREQ(oMoved2[0], "test");
1✔
742

743
    // Test sorting
744
    CPLStringList oTestSort;
1✔
745
    oTestSort.AddNameValue("Z", "1");
1✔
746
    oTestSort.AddNameValue("L", "2");
1✔
747
    oTestSort.AddNameValue("T", "3");
1✔
748
    oTestSort.AddNameValue("A", "4");
1✔
749
    oTestSort.Sort();
1✔
750
    EXPECT_STREQ(oTestSort[0], "A=4");
1✔
751
    EXPECT_STREQ(oTestSort[1], "L=2");
1✔
752
    EXPECT_STREQ(oTestSort[2], "T=3");
1✔
753
    EXPECT_STREQ(oTestSort[3], "Z=1");
1✔
754
    ASSERT_EQ(oTestSort[4], (const char *)nullptr);
1✔
755

756
    // Test FetchNameValue() in a sorted list
757
    EXPECT_STREQ(oTestSort.FetchNameValue("A"), "4");
1✔
758
    EXPECT_STREQ(oTestSort.FetchNameValue("L"), "2");
1✔
759
    EXPECT_STREQ(oTestSort.FetchNameValue("T"), "3");
1✔
760
    EXPECT_STREQ(oTestSort.FetchNameValue("Z"), "1");
1✔
761

762
    // Test AddNameValue() in a sorted list
763
    oTestSort.AddNameValue("B", "5");
1✔
764
    EXPECT_STREQ(oTestSort[0], "A=4");
1✔
765
    EXPECT_STREQ(oTestSort[1], "B=5");
1✔
766
    EXPECT_STREQ(oTestSort[2], "L=2");
1✔
767
    EXPECT_STREQ(oTestSort[3], "T=3");
1✔
768
    EXPECT_STREQ(oTestSort[4], "Z=1");
1✔
769
    ASSERT_EQ(oTestSort[5], (const char *)nullptr);
1✔
770

771
    // Test SetNameValue() of an existing item in a sorted list
772
    oTestSort.SetNameValue("Z", "6");
1✔
773
    EXPECT_STREQ(oTestSort[4], "Z=6");
1✔
774

775
    // Test SetNameValue() of a non-existing item in a sorted list
776
    oTestSort.SetNameValue("W", "7");
1✔
777
    EXPECT_STREQ(oTestSort[0], "A=4");
1✔
778
    EXPECT_STREQ(oTestSort[1], "B=5");
1✔
779
    EXPECT_STREQ(oTestSort[2], "L=2");
1✔
780
    EXPECT_STREQ(oTestSort[3], "T=3");
1✔
781
    EXPECT_STREQ(oTestSort[4], "W=7");
1✔
782
    EXPECT_STREQ(oTestSort[5], "Z=6");
1✔
783
    ASSERT_EQ(oTestSort[6], (const char *)nullptr);
1✔
784
}
785

786
TEST_F(test_cpl, CPLStringList_Sort)
4✔
787
{
788
    // Test some name=value handling stuff *with* sorting active.
789
    CPLStringList oNVL;
1✔
790

791
    oNVL.Sort();
1✔
792

793
    oNVL.AddNameValue("KEY1", "VALUE1");
1✔
794
    oNVL.AddNameValue("2KEY", "VALUE2");
1✔
795
    ASSERT_EQ(oNVL.Count(), 2);
1✔
796
    EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
1✔
797
    EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE2");
1✔
798
    ASSERT_TRUE(oNVL.FetchNameValue("MISSING") == nullptr);
1✔
799

800
    oNVL.AddNameValue("KEY1", "VALUE3");
1✔
801
    ASSERT_EQ(oNVL.Count(), 3);
1✔
802
    EXPECT_STREQ(oNVL.FetchNameValue("KEY1"), "VALUE1");
1✔
803
    EXPECT_STREQ(oNVL.FetchNameValueDef("MISSING", "X"), "X");
1✔
804

805
    oNVL.SetNameValue("2KEY", "VALUE4");
1✔
806
    EXPECT_STREQ(oNVL.FetchNameValue("2KEY"), "VALUE4");
1✔
807
    ASSERT_EQ(oNVL.Count(), 3);
1✔
808

809
    // make sure deletion works.
810
    oNVL.SetNameValue("2KEY", nullptr);
1✔
811
    ASSERT_TRUE(oNVL.FetchNameValue("2KEY") == nullptr);
1✔
812
    ASSERT_EQ(oNVL.Count(), 2);
1✔
813

814
    // Test insertion logic pretty carefully.
815
    oNVL.Clear();
1✔
816
    ASSERT_TRUE(oNVL.IsSorted() == TRUE);
1✔
817

818
    oNVL.SetNameValue("B", "BB");
1✔
819
    oNVL.SetNameValue("A", "AA");
1✔
820
    oNVL.SetNameValue("D", "DD");
1✔
821
    oNVL.SetNameValue("C", "CC");
1✔
822

823
    // items should be in sorted order.
824
    EXPECT_STREQ(oNVL[0], "A=AA");
1✔
825
    EXPECT_STREQ(oNVL[1], "B=BB");
1✔
826
    EXPECT_STREQ(oNVL[2], "C=CC");
1✔
827
    EXPECT_STREQ(oNVL[3], "D=DD");
1✔
828

829
    EXPECT_STREQ(oNVL.FetchNameValue("A"), "AA");
1✔
830
    EXPECT_STREQ(oNVL.FetchNameValue("B"), "BB");
1✔
831
    EXPECT_STREQ(oNVL.FetchNameValue("C"), "CC");
1✔
832
    EXPECT_STREQ(oNVL.FetchNameValue("D"), "DD");
1✔
833
}
834

835
TEST_F(test_cpl, CPL_HMAC_SHA256)
4✔
836
{
837
    GByte abyDigest[CPL_SHA256_HASH_SIZE];
838
    char szDigest[2 * CPL_SHA256_HASH_SIZE + 1];
839

840
    CPL_HMAC_SHA256("key", 3, "The quick brown fox jumps over the lazy dog",
1✔
841
                    strlen("The quick brown fox jumps over the lazy dog"),
842
                    abyDigest);
843
    for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
33✔
844
        snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
32✔
845
                 abyDigest[i]);
32✔
846
    // fprintf(stderr, "%s\n", szDigest);
847
    EXPECT_STREQ(
1✔
848
        szDigest,
849
        "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8");
850

851
    CPL_HMAC_SHA256(
1✔
852
        "mysupersupersupersupersupersupersupersupersupersupersupersupersupersup"
853
        "ersupersupersupersupersupersuperlongkey",
854
        strlen("mysupersupersupersupersupersupersupersupersupersupersupersupers"
855
               "upersupersupersupersupersupersupersuperlongkey"),
856
        "msg", 3, abyDigest);
857
    for (int i = 0; i < CPL_SHA256_HASH_SIZE; i++)
33✔
858
        snprintf(szDigest + 2 * i, sizeof(szDigest) - 2 * i, "%02x",
32✔
859
                 abyDigest[i]);
32✔
860
    // fprintf(stderr, "%s\n", szDigest);
861
    EXPECT_STREQ(
1✔
862
        szDigest,
863
        "a3051520761ed3cb43876b35ce2dd93ac5b332dc3bad898bb32086f7ac71ffc1");
864
}
1✔
865

866
TEST_F(test_cpl, VSIMalloc)
4✔
867
{
868
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
869

870
    // The following tests will fail because of overflows
871

872
    {
873
        CPLErrorReset();
1✔
874
        void *ptr = VSIMalloc2(~(size_t)0, ~(size_t)0);
1✔
875
        EXPECT_EQ(ptr, nullptr);
1✔
876
        VSIFree(ptr);
1✔
877
        EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
878
    }
879

880
    {
881
        CPLErrorReset();
1✔
882
        void *ptr = VSIMalloc3(1, ~(size_t)0, ~(size_t)0);
1✔
883
        EXPECT_EQ(ptr, nullptr);
1✔
884
        VSIFree(ptr);
1✔
885
        EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
886
    }
887

888
    {
889
        CPLErrorReset();
1✔
890
        void *ptr = VSIMalloc3(~(size_t)0, 1, ~(size_t)0);
1✔
891
        EXPECT_EQ(ptr, nullptr);
1✔
892
        VSIFree(ptr);
1✔
893
        EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
894
    }
895

896
    {
897
        CPLErrorReset();
1✔
898
        void *ptr = VSIMalloc3(~(size_t)0, ~(size_t)0, 1);
1✔
899
        EXPECT_EQ(ptr, nullptr);
1✔
900
        VSIFree(ptr);
1✔
901
        EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
902
    }
903

904
    if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
1✔
905
    {
906
        // The following tests will fail because such allocations cannot succeed
907
#if SIZEOF_VOIDP == 8
908
        {
909
            CPLErrorReset();
1✔
910
            void *ptr = VSIMalloc(~(size_t)0);
1✔
911
            EXPECT_EQ(ptr, nullptr);
1✔
912
            VSIFree(ptr);
1✔
913
            EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
1✔
914
        }
915

916
        {
917
            CPLErrorReset();
1✔
918
            void *ptr = VSIMalloc2(~(size_t)0, 1);
1✔
919
            EXPECT_EQ(ptr, nullptr);
1✔
920
            VSIFree(ptr);
1✔
921
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
922
        }
923

924
        {
925
            CPLErrorReset();
1✔
926
            void *ptr = VSIMalloc3(~(size_t)0, 1, 1);
1✔
927
            EXPECT_EQ(ptr, nullptr);
1✔
928
            VSIFree(ptr);
1✔
929
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
930
        }
931

932
        {
933
            CPLErrorReset();
1✔
934
            void *ptr = VSICalloc(~(size_t)0, 1);
1✔
935
            EXPECT_EQ(ptr, nullptr);
1✔
936
            VSIFree(ptr);
1✔
937
            EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
1✔
938
        }
939

940
        {
941
            CPLErrorReset();
1✔
942
            void *ptr = VSIRealloc(nullptr, ~(size_t)0);
1✔
943
            EXPECT_EQ(ptr, nullptr);
1✔
944
            VSIFree(ptr);
1✔
945
            EXPECT_EQ(CPLGetLastErrorType(), CE_None); /* no error reported */
1✔
946
        }
947

948
        {
949
            CPLErrorReset();
1✔
950
            void *ptr = VSI_MALLOC_VERBOSE(~(size_t)0);
1✔
951
            EXPECT_EQ(ptr, nullptr);
1✔
952
            VSIFree(ptr);
1✔
953
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
954
        }
955

956
        {
957
            CPLErrorReset();
1✔
958
            void *ptr = VSI_MALLOC2_VERBOSE(~(size_t)0, 1);
1✔
959
            EXPECT_EQ(ptr, nullptr);
1✔
960
            VSIFree(ptr);
1✔
961
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
962
        }
963

964
        {
965
            CPLErrorReset();
1✔
966
            void *ptr = VSI_MALLOC3_VERBOSE(~(size_t)0, 1, 1);
1✔
967
            EXPECT_EQ(ptr, nullptr);
1✔
968
            VSIFree(ptr);
1✔
969
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
970
        }
971

972
        {
973
            CPLErrorReset();
1✔
974
            void *ptr = VSI_CALLOC_VERBOSE(~(size_t)0, 1);
1✔
975
            EXPECT_EQ(ptr, nullptr);
1✔
976
            VSIFree(ptr);
1✔
977
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
978
        }
979

980
        {
981
            CPLErrorReset();
1✔
982
            void *ptr = VSI_REALLOC_VERBOSE(nullptr, ~(size_t)0);
1✔
983
            EXPECT_EQ(ptr, nullptr);
1✔
984
            VSIFree(ptr);
1✔
985
            EXPECT_NE(CPLGetLastErrorType(), CE_None);
1✔
986
        }
987
#endif
988
    }
989

990
    CPLPopErrorHandler();
1✔
991

992
    // The following allocs will return NULL because of 0 byte alloc
993
    {
994
        CPLErrorReset();
1✔
995
        void *ptr = VSIMalloc2(0, 1);
1✔
996
        EXPECT_EQ(ptr, nullptr);
1✔
997
        VSIFree(ptr);
1✔
998
        EXPECT_EQ(CPLGetLastErrorType(), CE_None);
1✔
999
    }
1000

1001
    {
1002
        void *ptr = VSIMalloc2(1, 0);
1✔
1003
        EXPECT_EQ(ptr, nullptr);
1✔
1004
        VSIFree(ptr);
1✔
1005
    }
1006

1007
    {
1008
        CPLErrorReset();
1✔
1009
        void *ptr = VSIMalloc3(0, 1, 1);
1✔
1010
        EXPECT_EQ(ptr, nullptr);
1✔
1011
        VSIFree(ptr);
1✔
1012
        EXPECT_EQ(CPLGetLastErrorType(), CE_None);
1✔
1013
    }
1014

1015
    {
1016
        void *ptr = VSIMalloc3(1, 0, 1);
1✔
1017
        EXPECT_EQ(ptr, nullptr);
1✔
1018
        VSIFree(ptr);
1✔
1019
    }
1020

1021
    {
1022
        void *ptr = VSIMalloc3(1, 1, 0);
1✔
1023
        EXPECT_EQ(ptr, nullptr);
1✔
1024
        VSIFree(ptr);
1✔
1025
    }
1026
}
1✔
1027

1028
TEST_F(test_cpl, CPLFormFilename)
4✔
1029
{
1030
    EXPECT_TRUE(strcmp(CPLFormFilename("a", "b", nullptr), "a/b") == 0 ||
1✔
1031
                strcmp(CPLFormFilename("a", "b", nullptr), "a\\b") == 0);
1032
    EXPECT_TRUE(strcmp(CPLFormFilename("a/", "b", nullptr), "a/b") == 0 ||
1✔
1033
                strcmp(CPLFormFilename("a/", "b", nullptr), "a\\b") == 0);
1034
    EXPECT_TRUE(strcmp(CPLFormFilename("a\\", "b", nullptr), "a/b") == 0 ||
1✔
1035
                strcmp(CPLFormFilename("a\\", "b", nullptr), "a\\b") == 0);
1036
    EXPECT_STREQ(CPLFormFilename(nullptr, "a", "b"), "a.b");
1✔
1037
    EXPECT_STREQ(CPLFormFilename(nullptr, "a", ".b"), "a.b");
1✔
1038
    EXPECT_STREQ(CPLFormFilename("/a", "..", nullptr), "/");
1✔
1039
    EXPECT_STREQ(CPLFormFilename("/a/", "..", nullptr), "/");
1✔
1040
    EXPECT_STREQ(CPLFormFilename("/a/b", "..", nullptr), "/a");
1✔
1041
    EXPECT_STREQ(CPLFormFilename("/a/b/", "..", nullptr), "/a");
1✔
1042
    EXPECT_TRUE(EQUAL(CPLFormFilename("c:", "..", nullptr), "c:/..") ||
1✔
1043
                EQUAL(CPLFormFilename("c:", "..", nullptr), "c:\\.."));
1044
    EXPECT_TRUE(EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:/..") ||
1✔
1045
                EQUAL(CPLFormFilename("c:\\", "..", nullptr), "c:\\.."));
1046
    EXPECT_STREQ(CPLFormFilename("c:\\a", "..", nullptr), "c:");
1✔
1047
    EXPECT_STREQ(CPLFormFilename("c:\\a\\", "..", nullptr), "c:");
1✔
1048
    EXPECT_STREQ(CPLFormFilename("c:\\a\\b", "..", nullptr), "c:\\a");
1✔
1049
    EXPECT_STREQ(CPLFormFilename("\\\\$\\c:\\a", "..", nullptr), "\\\\$\\c:");
1✔
1050
    EXPECT_TRUE(
1✔
1051
        EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:/..") ||
1052
        EQUAL(CPLFormFilename("\\\\$\\c:", "..", nullptr), "\\\\$\\c:\\.."));
1053
    EXPECT_STREQ(CPLFormFilename("/a", "../", nullptr), "/");
1✔
1054
    EXPECT_STREQ(CPLFormFilename("/a/", "../", nullptr), "/");
1✔
1055
    EXPECT_STREQ(CPLFormFilename("/a", "../b", nullptr), "/b");
1✔
1056
    EXPECT_STREQ(CPLFormFilename("/a/", "../b", nullptr), "/b");
1✔
1057
    EXPECT_STREQ(CPLFormFilename("/a", "../b/c", nullptr), "/b/c");
1✔
1058
    EXPECT_STREQ(CPLFormFilename("/a/", "../b/c/d", nullptr), "/b/c/d");
1✔
1059
    EXPECT_STREQ(CPLFormFilename("/a/b", "../../c", nullptr), "/c");
1✔
1060
    EXPECT_STREQ(CPLFormFilename("/a/b/", "../../c/d", nullptr), "/c/d");
1✔
1061
    EXPECT_STREQ(CPLFormFilename("/a/b", "../..", nullptr), "/");
1✔
1062
    EXPECT_STREQ(CPLFormFilename("/a/b", "../../", nullptr), "/");
1✔
1063
    EXPECT_STREQ(CPLFormFilename("/a/b/c", "../../d", nullptr), "/a/d");
1✔
1064
    EXPECT_STREQ(CPLFormFilename("/a/b/c/", "../../d", nullptr), "/a/d");
1✔
1065
    // we could also just error out, but at least this preserves the original
1066
    // semantics
1067
    EXPECT_STREQ(CPLFormFilename("/a", "../../b", nullptr), "/a/../../b");
1✔
1068
    EXPECT_STREQ(
1✔
1069
        CPLFormFilename("/vsicurl/http://example.com?foo", "bar", nullptr),
1070
        "/vsicurl/http://example.com/bar?foo");
1071
}
1✔
1072

1073
TEST_F(test_cpl, CPLGetPath)
4✔
1074
{
1075
    EXPECT_STREQ(CPLGetPath("/foo/bar/"), "/foo/bar");
1✔
1076
    EXPECT_STREQ(CPLGetPath("/foo/bar"), "/foo");
1✔
1077
    EXPECT_STREQ(CPLGetPath("/vsicurl/http://example.com/foo/bar?suffix"),
1✔
1078
                 "/vsicurl/http://example.com/foo?suffix");
1079
    EXPECT_STREQ(
1✔
1080
        CPLGetPath(
1081
            "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
1082
            "2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
1083
        "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
1084
        "2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
1085
}
1✔
1086

1087
TEST_F(test_cpl, CPLGetDirname)
4✔
1088
{
1089
    EXPECT_STREQ(CPLGetDirname("/foo/bar/"), "/foo/bar");
1✔
1090
    EXPECT_STREQ(CPLGetDirname("/foo/bar"), "/foo");
1✔
1091
    EXPECT_STREQ(CPLGetDirname("/vsicurl/http://example.com/foo/bar?suffix"),
1✔
1092
                 "/vsicurl/http://example.com/foo?suffix");
1093
    EXPECT_STREQ(
1✔
1094
        CPLGetDirname(
1095
            "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%"
1096
            "2FOSGeo%2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata%2Fpoly.shp"),
1097
        "/vsicurl?foo=bar&url=https%3A%2F%2Fraw.githubusercontent.com%2FOSGeo%"
1098
        "2Fgdal%2Fmaster%2Fautotest%2Fogr%2Fdata");
1099
}
1✔
1100

1101
TEST_F(test_cpl, VSIGetDiskFreeSpace)
4✔
1102
{
1103
    ASSERT_TRUE(VSIGetDiskFreeSpace("/vsimem/") > 0);
1✔
1104
    ASSERT_TRUE(VSIGetDiskFreeSpace(".") == -1 ||
1✔
1105
                VSIGetDiskFreeSpace(".") >= 0);
1106
}
1107

1108
TEST_F(test_cpl, CPLsscanf)
4✔
1109
{
1110
    double a, b, c;
1111

1112
    a = b = 0;
1✔
1113
    ASSERT_EQ(CPLsscanf("1 2", "%lf %lf", &a, &b), 2);
1✔
1114
    ASSERT_EQ(a, 1.0);
1✔
1115
    ASSERT_EQ(b, 2.0);
1✔
1116

1117
    a = b = 0;
1✔
1118
    ASSERT_EQ(CPLsscanf("1\t2", "%lf %lf", &a, &b), 2);
1✔
1119
    ASSERT_EQ(a, 1.0);
1✔
1120
    ASSERT_EQ(b, 2.0);
1✔
1121

1122
    a = b = 0;
1✔
1123
    ASSERT_EQ(CPLsscanf("1 2", "%lf\t%lf", &a, &b), 2);
1✔
1124
    ASSERT_EQ(a, 1.0);
1✔
1125
    ASSERT_EQ(b, 2.0);
1✔
1126

1127
    a = b = 0;
1✔
1128
    ASSERT_EQ(CPLsscanf("1  2", "%lf %lf", &a, &b), 2);
1✔
1129
    ASSERT_EQ(a, 1.0);
1✔
1130
    ASSERT_EQ(b, 2.0);
1✔
1131

1132
    a = b = 0;
1✔
1133
    ASSERT_EQ(CPLsscanf("1 2", "%lf  %lf", &a, &b), 2);
1✔
1134
    ASSERT_EQ(a, 1.0);
1✔
1135
    ASSERT_EQ(b, 2.0);
1✔
1136

1137
    a = b = c = 0;
1✔
1138
    ASSERT_EQ(CPLsscanf("1 2", "%lf %lf %lf", &a, &b, &c), 2);
1✔
1139
    ASSERT_EQ(a, 1.0);
1✔
1140
    ASSERT_EQ(b, 2.0);
1✔
1141
}
1142

1143
TEST_F(test_cpl, CPLsnprintf)
4✔
1144
{
1145
    {
1146
        char buf[32];
1147
        EXPECT_EQ(CPLsnprintf(buf, sizeof(buf), "a%.*fb", 1, 2.12), 5);
1✔
1148
        EXPECT_STREQ(buf, "a2.1b");
1✔
1149
    }
1150
}
1✔
1151

1152
TEST_F(test_cpl, CPLSetErrorHandler)
4✔
1153
{
1154
    CPLString oldVal = CPLGetConfigOption("CPL_DEBUG", "");
1✔
1155
    CPLSetConfigOption("CPL_DEBUG", "TEST");
1✔
1156

1157
    CPLErrorHandler oldHandler = CPLSetErrorHandler(myErrorHandler);
1✔
1158
    gbGotError = false;
1✔
1159
    CPLDebug("TEST", "Test");
1✔
1160
    ASSERT_EQ(gbGotError, true);
1✔
1161
    gbGotError = false;
1✔
1162
    CPLSetErrorHandler(oldHandler);
1✔
1163

1164
    CPLPushErrorHandler(myErrorHandler);
1✔
1165
    gbGotError = false;
1✔
1166
    CPLDebug("TEST", "Test");
1✔
1167
    ASSERT_EQ(gbGotError, true);
1✔
1168
    gbGotError = false;
1✔
1169
    CPLPopErrorHandler();
1✔
1170

1171
    oldHandler = CPLSetErrorHandler(myErrorHandler);
1✔
1172
    CPLSetCurrentErrorHandlerCatchDebug(FALSE);
1✔
1173
    gbGotError = false;
1✔
1174
    CPLDebug("TEST", "Test");
1✔
1175
    ASSERT_EQ(gbGotError, false);
1✔
1176
    gbGotError = false;
1✔
1177
    CPLSetErrorHandler(oldHandler);
1✔
1178

1179
    CPLPushErrorHandler(myErrorHandler);
1✔
1180
    CPLSetCurrentErrorHandlerCatchDebug(FALSE);
1✔
1181
    gbGotError = false;
1✔
1182
    CPLDebug("TEST", "Test");
1✔
1183
    ASSERT_EQ(gbGotError, false);
1✔
1184
    gbGotError = false;
1✔
1185
    CPLPopErrorHandler();
1✔
1186

1187
    CPLSetConfigOption("CPL_DEBUG", oldVal.size() ? oldVal.c_str() : nullptr);
1✔
1188

1189
    oldHandler = CPLSetErrorHandler(nullptr);
1✔
1190
    CPLDebug("TEST", "Test");
1✔
1191
    CPLError(CE_Failure, CPLE_AppDefined, "test");
1✔
1192
    CPLErrorHandler newOldHandler = CPLSetErrorHandler(nullptr);
1✔
1193
    ASSERT_EQ(newOldHandler, static_cast<CPLErrorHandler>(nullptr));
1✔
1194
    CPLDebug("TEST", "Test");
1✔
1195
    CPLError(CE_Failure, CPLE_AppDefined, "test");
1✔
1196
    CPLSetErrorHandler(oldHandler);
1✔
1197
}
1198

1199
/************************************************************************/
1200
/*                         CPLString::replaceAll()                      */
1201
/************************************************************************/
1202

1203
TEST_F(test_cpl, CPLString_replaceAll)
4✔
1204
{
1205
    CPLString osTest;
1✔
1206
    osTest = "foobarbarfoo";
1✔
1207
    osTest.replaceAll("bar", "was_bar");
1✔
1208
    ASSERT_EQ(osTest, "foowas_barwas_barfoo");
1✔
1209

1210
    osTest = "foobarbarfoo";
1✔
1211
    osTest.replaceAll("X", "was_bar");
1✔
1212
    ASSERT_EQ(osTest, "foobarbarfoo");
1✔
1213

1214
    osTest = "foobarbarfoo";
1✔
1215
    osTest.replaceAll("", "was_bar");
1✔
1216
    ASSERT_EQ(osTest, "foobarbarfoo");
1✔
1217

1218
    osTest = "foobarbarfoo";
1✔
1219
    osTest.replaceAll("bar", "");
1✔
1220
    ASSERT_EQ(osTest, "foofoo");
1✔
1221

1222
    osTest = "foobarbarfoo";
1✔
1223
    osTest.replaceAll('b', 'B');
1✔
1224
    ASSERT_EQ(osTest, "fooBarBarfoo");
1✔
1225

1226
    osTest = "foobarbarfoo";
1✔
1227
    osTest.replaceAll('b', "B");
1✔
1228
    ASSERT_EQ(osTest, "fooBarBarfoo");
1✔
1229

1230
    osTest = "foobarbarfoo";
1✔
1231
    osTest.replaceAll("b", 'B');
1✔
1232
    ASSERT_EQ(osTest, "fooBarBarfoo");
1✔
1233
}
1234

1235
/************************************************************************/
1236
/*                        VSIMallocAligned()                            */
1237
/************************************************************************/
1238
TEST_F(test_cpl, VSIMallocAligned)
4✔
1239
{
1240
    GByte *ptr = static_cast<GByte *>(VSIMallocAligned(sizeof(void *), 1));
1✔
1241
    ASSERT_TRUE(ptr != nullptr);
1✔
1242
    ASSERT_TRUE(((size_t)ptr % sizeof(void *)) == 0);
1✔
1243
    *ptr = 1;
1✔
1244
    VSIFreeAligned(ptr);
1✔
1245

1246
    ptr = static_cast<GByte *>(VSIMallocAligned(16, 1));
1✔
1247
    ASSERT_TRUE(ptr != nullptr);
1✔
1248
    ASSERT_TRUE(((size_t)ptr % 16) == 0);
1✔
1249
    *ptr = 1;
1✔
1250
    VSIFreeAligned(ptr);
1✔
1251

1252
    VSIFreeAligned(nullptr);
1✔
1253

1254
#ifndef _WIN32
1255
    // Illegal use of API. Returns non NULL on Windows
1256
    ptr = static_cast<GByte *>(VSIMallocAligned(2, 1));
1✔
1257
    EXPECT_TRUE(ptr == nullptr);
1✔
1258
    VSIFree(ptr);
1✔
1259

1260
    // Illegal use of API. Crashes on Windows
1261
    ptr = static_cast<GByte *>(VSIMallocAligned(5, 1));
1✔
1262
    EXPECT_TRUE(ptr == nullptr);
1✔
1263
    VSIFree(ptr);
1✔
1264
#endif
1265

1266
    if (!CSLTestBoolean(CPLGetConfigOption("SKIP_MEM_INTENSIVE_TEST", "NO")))
1✔
1267
    {
1268
        // The following tests will fail because such allocations cannot succeed
1269
#if SIZEOF_VOIDP == 8
1270
        ptr = static_cast<GByte *>(
1271
            VSIMallocAligned(sizeof(void *), ~((size_t)0)));
1✔
1272
        EXPECT_TRUE(ptr == nullptr);
1✔
1273
        VSIFree(ptr);
1✔
1274

1275
        ptr = static_cast<GByte *>(
1276
            VSIMallocAligned(sizeof(void *), (~((size_t)0)) - sizeof(void *)));
1✔
1277
        EXPECT_TRUE(ptr == nullptr);
1✔
1278
        VSIFree(ptr);
1✔
1279
#endif
1280
    }
1281
}
1282

1283
/************************************************************************/
1284
/*             CPLGetConfigOptions() / CPLSetConfigOptions()            */
1285
/************************************************************************/
1286
TEST_F(test_cpl, CPLGetConfigOptions)
4✔
1287
{
1288
    CPLSetConfigOption("FOOFOO", "BAR");
1✔
1289
    char **options = CPLGetConfigOptions();
1✔
1290
    EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
1✔
1291
    CPLSetConfigOptions(nullptr);
1✔
1292
    EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "i_dont_exist");
1✔
1293
    CPLSetConfigOptions(options);
1✔
1294
    EXPECT_STREQ(CPLGetConfigOption("FOOFOO", "i_dont_exist"), "BAR");
1✔
1295
    CSLDestroy(options);
1✔
1296
}
1✔
1297

1298
/************************************************************************/
1299
/*  CPLGetThreadLocalConfigOptions() / CPLSetThreadLocalConfigOptions() */
1300
/************************************************************************/
1301
TEST_F(test_cpl, CPLGetThreadLocalConfigOptions)
4✔
1302
{
1303
    CPLSetThreadLocalConfigOption("FOOFOO", "BAR");
1✔
1304
    char **options = CPLGetThreadLocalConfigOptions();
1✔
1305
    EXPECT_STREQ(CSLFetchNameValue(options, "FOOFOO"), "BAR");
1✔
1306
    CPLSetThreadLocalConfigOptions(nullptr);
1✔
1307
    EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
1✔
1308
                 "i_dont_exist");
1309
    CPLSetThreadLocalConfigOptions(options);
1✔
1310
    EXPECT_STREQ(CPLGetThreadLocalConfigOption("FOOFOO", "i_dont_exist"),
1✔
1311
                 "BAR");
1312
    CSLDestroy(options);
1✔
1313
}
1✔
1314

1315
TEST_F(test_cpl, CPLExpandTilde)
4✔
1316
{
1317
    EXPECT_STREQ(CPLExpandTilde("/foo/bar"), "/foo/bar");
1✔
1318

1319
    CPLSetConfigOption("HOME", "/foo");
1✔
1320
    ASSERT_TRUE(EQUAL(CPLExpandTilde("~/bar"), "/foo/bar") ||
1✔
1321
                EQUAL(CPLExpandTilde("~/bar"), "/foo\\bar"));
1322
    CPLSetConfigOption("HOME", nullptr);
1✔
1323
}
1324

1325
TEST_F(test_cpl, CPLDeclareKnownConfigOption)
4✔
1326
{
1327
    CPLConfigOptionSetter oDebugSetter("CPL_DEBUG", "ON", false);
2✔
1328
    {
1329
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2✔
1330
        CPLErrorReset();
1✔
1331
        CPLConfigOptionSetter oDeclaredConfigOptionSetter("UNDECLARED_OPTION",
1332
                                                          "FOO", false);
2✔
1333
        EXPECT_STREQ(CPLGetLastErrorMsg(),
1✔
1334
                     "Unknown configuration option 'UNDECLARED_OPTION'.");
1335
    }
1336
    {
1337
        CPLDeclareKnownConfigOption("DECLARED_OPTION", nullptr);
1✔
1338

1339
        const CPLStringList aosKnownConfigOptions(CPLGetKnownConfigOptions());
2✔
1340
        EXPECT_GE(aosKnownConfigOptions.FindString("CPL_DEBUG"), 0);
1✔
1341
        EXPECT_GE(aosKnownConfigOptions.FindString("DECLARED_OPTION"), 0);
1✔
1342

1343
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2✔
1344
        CPLErrorReset();
1✔
1345
        CPLConfigOptionSetter oDeclaredConfigOptionSetter("DECLARED_OPTION",
1346
                                                          "FOO", false);
2✔
1347
        EXPECT_STREQ(CPLGetLastErrorMsg(), "");
1✔
1348
    }
1349
}
1✔
1350

1351
TEST_F(test_cpl, CPLErrorSetState)
4✔
1352
{
1353
    // NOTE: Assumes cpl_error.cpp defines DEFAULT_LAST_ERR_MSG_SIZE=500
1354
    char pszMsg[] = "0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1✔
1355
                    "1abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1356
                    "2abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1357
                    "3abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1358
                    "4abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1359
                    "5abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1360
                    "6abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1361
                    "7abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1362
                    "8abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"
1363
                    "9abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"  // 500
1364
                    "0abcdefghijklmnopqrstuvwxyz0123456789!@#$%&*()_+=|"  // 550
1365
        ;
1366

1367
    CPLErrorReset();
1✔
1368
    CPLErrorSetState(CE_Warning, 1, pszMsg);
1✔
1369
    ASSERT_EQ(strlen(pszMsg) - 50 - 1,  // length - 50 - 1 (null-terminator)
1✔
1370
              strlen(CPLGetLastErrorMsg()));  // DEFAULT_LAST_ERR_MSG_SIZE - 1
1371
}
1372

1373
TEST_F(test_cpl, CPLUnescapeString)
4✔
1374
{
1375
    char *pszText = CPLUnescapeString(
1✔
1376
        "&lt;&gt;&amp;&apos;&quot;&#x3f;&#x3F;&#63;", nullptr, CPLES_XML);
1377
    EXPECT_STREQ(pszText, "<>&'\"???");
1✔
1378
    CPLFree(pszText);
1✔
1379

1380
    // Integer overflow
1381
    pszText = CPLUnescapeString("&10000000000000000;", nullptr, CPLES_XML);
1✔
1382
    // We do not really care about the return value
1383
    CPLFree(pszText);
1✔
1384

1385
    // Integer overflow
1386
    pszText = CPLUnescapeString("&#10000000000000000;", nullptr, CPLES_XML);
1✔
1387
    // We do not really care about the return value
1388
    CPLFree(pszText);
1✔
1389

1390
    // Error case
1391
    pszText = CPLUnescapeString("&foo", nullptr, CPLES_XML);
1✔
1392
    EXPECT_STREQ(pszText, "");
1✔
1393
    CPLFree(pszText);
1✔
1394

1395
    // Error case
1396
    pszText = CPLUnescapeString("&#x", nullptr, CPLES_XML);
1✔
1397
    EXPECT_STREQ(pszText, "");
1✔
1398
    CPLFree(pszText);
1✔
1399

1400
    // Error case
1401
    pszText = CPLUnescapeString("&#", nullptr, CPLES_XML);
1✔
1402
    EXPECT_STREQ(pszText, "");
1✔
1403
    CPLFree(pszText);
1✔
1404
}
1✔
1405

1406
// Test signed int safe maths
1407
TEST_F(test_cpl, CPLSM_signed)
4✔
1408
{
1409
    ASSERT_EQ((CPLSM(-2) + CPLSM(3)).v(), 1);
1✔
1410
    ASSERT_EQ((CPLSM(-2) + CPLSM(1)).v(), -1);
1✔
1411
    ASSERT_EQ((CPLSM(-2) + CPLSM(-1)).v(), -3);
1✔
1412
    ASSERT_EQ((CPLSM(2) + CPLSM(-3)).v(), -1);
1✔
1413
    ASSERT_EQ((CPLSM(2) + CPLSM(-1)).v(), 1);
1✔
1414
    ASSERT_EQ((CPLSM(2) + CPLSM(1)).v(), 3);
1✔
1415
    ASSERT_EQ((CPLSM(INT_MAX - 1) + CPLSM(1)).v(), INT_MAX);
1✔
1416
    ASSERT_EQ((CPLSM(1) + CPLSM(INT_MAX - 1)).v(), INT_MAX);
1✔
1417
    ASSERT_EQ((CPLSM(INT_MAX) + CPLSM(-1)).v(), INT_MAX - 1);
1✔
1418
    ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MAX)).v(), INT_MAX - 1);
1✔
1419
    ASSERT_EQ((CPLSM(INT_MIN + 1) + CPLSM(-1)).v(), INT_MIN);
1✔
1420
    ASSERT_EQ((CPLSM(-1) + CPLSM(INT_MIN + 1)).v(), INT_MIN);
1✔
1421
    try
1422
    {
1423
        (CPLSM(INT_MAX) + CPLSM(1)).v();
1✔
1424
        ASSERT_TRUE(false);
×
1425
    }
1426
    catch (...)
1✔
1427
    {
1428
    }
1429
    try
1430
    {
1431
        (CPLSM(1) + CPLSM(INT_MAX)).v();
1✔
1432
        ASSERT_TRUE(false);
×
1433
    }
1434
    catch (...)
1✔
1435
    {
1436
    }
1437
    try
1438
    {
1439
        (CPLSM(INT_MIN) + CPLSM(-1)).v();
1✔
1440
        ASSERT_TRUE(false);
×
1441
    }
1442
    catch (...)
1✔
1443
    {
1444
    }
1445
    try
1446
    {
1447
        (CPLSM(-1) + CPLSM(INT_MIN)).v();
1✔
1448
        ASSERT_TRUE(false);
×
1449
    }
1450
    catch (...)
1✔
1451
    {
1452
    }
1453

1454
    ASSERT_EQ((CPLSM(-2) - CPLSM(1)).v(), -3);
1✔
1455
    ASSERT_EQ((CPLSM(-2) - CPLSM(-1)).v(), -1);
1✔
1456
    ASSERT_EQ((CPLSM(-2) - CPLSM(-3)).v(), 1);
1✔
1457
    ASSERT_EQ((CPLSM(2) - CPLSM(-1)).v(), 3);
1✔
1458
    ASSERT_EQ((CPLSM(2) - CPLSM(1)).v(), 1);
1✔
1459
    ASSERT_EQ((CPLSM(2) - CPLSM(3)).v(), -1);
1✔
1460
    ASSERT_EQ((CPLSM(INT_MAX) - CPLSM(1)).v(), INT_MAX - 1);
1✔
1461
    ASSERT_EQ((CPLSM(INT_MIN + 1) - CPLSM(1)).v(), INT_MIN);
1✔
1462
    ASSERT_EQ((CPLSM(0) - CPLSM(INT_MIN + 1)).v(), INT_MAX);
1✔
1463
    ASSERT_EQ((CPLSM(0) - CPLSM(INT_MAX)).v(), -INT_MAX);
1✔
1464
    try
1465
    {
1466
        (CPLSM(INT_MIN) - CPLSM(1)).v();
1✔
1467
        ASSERT_TRUE(false);
×
1468
    }
1469
    catch (...)
1✔
1470
    {
1471
    }
1472
    try
1473
    {
1474
        (CPLSM(0) - CPLSM(INT_MIN)).v();
1✔
1475
        ASSERT_TRUE(false);
×
1476
    }
1477
    catch (...)
1✔
1478
    {
1479
    }
1480
    try
1481
    {
1482
        (CPLSM(INT_MIN) - CPLSM(1)).v();
1✔
1483
        ASSERT_TRUE(false);
×
1484
    }
1485
    catch (...)
1✔
1486
    {
1487
    }
1488

1489
    ASSERT_EQ((CPLSM(INT_MIN + 1) * CPLSM(-1)).v(), INT_MAX);
1✔
1490
    ASSERT_EQ((CPLSM(-1) * CPLSM(INT_MIN + 1)).v(), INT_MAX);
1✔
1491
    ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(1)).v(), INT_MIN);
1✔
1492
    ASSERT_EQ((CPLSM(1) * CPLSM(INT_MIN)).v(), INT_MIN);
1✔
1493
    ASSERT_EQ((CPLSM(1) * CPLSM(INT_MAX)).v(), INT_MAX);
1✔
1494
    ASSERT_EQ((CPLSM(INT_MIN / 2) * CPLSM(2)).v(), INT_MIN);
1✔
1495
    ASSERT_EQ((CPLSM(INT_MAX / 2) * CPLSM(2)).v(), INT_MAX - 1);
1✔
1496
    ASSERT_EQ((CPLSM(INT_MAX / 2 + 1) * CPLSM(-2)).v(), INT_MIN);
1✔
1497
    ASSERT_EQ((CPLSM(0) * CPLSM(INT_MIN)).v(), 0);
1✔
1498
    ASSERT_EQ((CPLSM(INT_MIN) * CPLSM(0)).v(), 0);
1✔
1499
    ASSERT_EQ((CPLSM(0) * CPLSM(INT_MAX)).v(), 0);
1✔
1500
    ASSERT_EQ((CPLSM(INT_MAX) * CPLSM(0)).v(), 0);
1✔
1501
    try
1502
    {
1503
        (CPLSM(INT_MAX / 2 + 1) * CPLSM(2)).v();
1✔
1504
        ASSERT_TRUE(false);
×
1505
    }
1506
    catch (...)
1✔
1507
    {
1508
    }
1509
    try
1510
    {
1511
        (CPLSM(2) * CPLSM(INT_MAX / 2 + 1)).v();
1✔
1512
        ASSERT_TRUE(false);
×
1513
    }
1514
    catch (...)
1✔
1515
    {
1516
    }
1517
    try
1518
    {
1519
        (CPLSM(INT_MIN) * CPLSM(-1)).v();
1✔
1520
        ASSERT_TRUE(false);
×
1521
    }
1522
    catch (...)
1✔
1523
    {
1524
    }
1525
    try
1526
    {
1527
        (CPLSM(INT_MIN) * CPLSM(2)).v();
1✔
1528
        ASSERT_TRUE(false);
×
1529
    }
1530
    catch (...)
1✔
1531
    {
1532
    }
1533
    try
1534
    {
1535
        (CPLSM(2) * CPLSM(INT_MIN)).v();
1✔
1536
        ASSERT_TRUE(false);
×
1537
    }
1538
    catch (...)
1✔
1539
    {
1540
    }
1541

1542
    ASSERT_EQ((CPLSM(4) / CPLSM(2)).v(), 2);
1✔
1543
    ASSERT_EQ((CPLSM(4) / CPLSM(-2)).v(), -2);
1✔
1544
    ASSERT_EQ((CPLSM(-4) / CPLSM(2)).v(), -2);
1✔
1545
    ASSERT_EQ((CPLSM(-4) / CPLSM(-2)).v(), 2);
1✔
1546
    ASSERT_EQ((CPLSM(0) / CPLSM(2)).v(), 0);
1✔
1547
    ASSERT_EQ((CPLSM(0) / CPLSM(-2)).v(), 0);
1✔
1548
    ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(1)).v(), INT_MAX);
1✔
1549
    ASSERT_EQ((CPLSM(INT_MAX) / CPLSM(-1)).v(), -INT_MAX);
1✔
1550
    ASSERT_EQ((CPLSM(INT_MIN) / CPLSM(1)).v(), INT_MIN);
1✔
1551
    try
1552
    {
1553
        (CPLSM(-1) * CPLSM(INT_MIN)).v();
1✔
1554
        ASSERT_TRUE(false);
×
1555
    }
1556
    catch (...)
1✔
1557
    {
1558
    }
1559
    try
1560
    {
1561
        (CPLSM(INT_MIN) / CPLSM(-1)).v();
1✔
1562
        ASSERT_TRUE(false);
×
1563
    }
1564
    catch (...)
1✔
1565
    {
1566
    }
1567
    try
1568
    {
1569
        (CPLSM(1) / CPLSM(0)).v();
1✔
1570
        ASSERT_TRUE(false);
×
1571
    }
1572
    catch (...)
1✔
1573
    {
1574
    }
1575

1576
    ASSERT_EQ(CPLSM_TO_UNSIGNED(1).v(), 1U);
1✔
1577
    try
1578
    {
1579
        CPLSM_TO_UNSIGNED(-1);
1✔
1580
        ASSERT_TRUE(false);
×
1581
    }
1582
    catch (...)
1✔
1583
    {
1584
    }
1585
}
1586

1587
// Test unsigned int safe maths
1588
TEST_F(test_cpl, CPLSM_unsigned)
4✔
1589
{
1590
    ASSERT_EQ((CPLSM(2U) + CPLSM(3U)).v(), 5U);
1✔
1591
    ASSERT_EQ((CPLSM(UINT_MAX - 1) + CPLSM(1U)).v(), UINT_MAX);
1✔
1592
    try
1593
    {
1594
        (CPLSM(UINT_MAX) + CPLSM(1U)).v();
1✔
1595
        ASSERT_TRUE(false);
×
1596
    }
1597
    catch (...)
1✔
1598
    {
1599
    }
1600

1601
    ASSERT_EQ((CPLSM(4U) - CPLSM(3U)).v(), 1U);
1✔
1602
    ASSERT_EQ((CPLSM(4U) - CPLSM(4U)).v(), 0U);
1✔
1603
    ASSERT_EQ((CPLSM(UINT_MAX) - CPLSM(1U)).v(), UINT_MAX - 1);
1✔
1604
    try
1605
    {
1606
        (CPLSM(4U) - CPLSM(5U)).v();
1✔
1607
        ASSERT_TRUE(false);
×
1608
    }
1609
    catch (...)
1✔
1610
    {
1611
    }
1612

1613
    ASSERT_EQ((CPLSM(0U) * CPLSM(UINT_MAX)).v(), 0U);
1✔
1614
    ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(0U)).v(), 0U);
1✔
1615
    ASSERT_EQ((CPLSM(UINT_MAX) * CPLSM(1U)).v(), UINT_MAX);
1✔
1616
    ASSERT_EQ((CPLSM(1U) * CPLSM(UINT_MAX)).v(), UINT_MAX);
1✔
1617
    try
1618
    {
1619
        (CPLSM(UINT_MAX) * CPLSM(2U)).v();
1✔
1620
        ASSERT_TRUE(false);
×
1621
    }
1622
    catch (...)
1✔
1623
    {
1624
    }
1625
    try
1626
    {
1627
        (CPLSM(2U) * CPLSM(UINT_MAX)).v();
1✔
1628
        ASSERT_TRUE(false);
×
1629
    }
1630
    catch (...)
1✔
1631
    {
1632
    }
1633

1634
    ASSERT_EQ((CPLSM(4U) / CPLSM(2U)).v(), 2U);
1✔
1635
    ASSERT_EQ((CPLSM(UINT_MAX) / CPLSM(1U)).v(), UINT_MAX);
1✔
1636
    try
1637
    {
1638
        (CPLSM(1U) / CPLSM(0U)).v();
1✔
1639
        ASSERT_TRUE(false);
×
1640
    }
1641
    catch (...)
1✔
1642
    {
1643
    }
1644

1645
    ASSERT_EQ((CPLSM(static_cast<uint64_t>(2) * 1000 * 1000 * 1000) +
1✔
1646
               CPLSM(static_cast<uint64_t>(3) * 1000 * 1000 * 1000))
1647
                  .v(),
1648
              static_cast<uint64_t>(5) * 1000 * 1000 * 1000);
1649
    ASSERT_EQ((CPLSM(std::numeric_limits<uint64_t>::max() - 1) +
1✔
1650
               CPLSM(static_cast<uint64_t>(1)))
1651
                  .v(),
1652
              std::numeric_limits<uint64_t>::max());
1653
    try
1654
    {
1655
        (CPLSM(std::numeric_limits<uint64_t>::max()) +
2✔
1656
         CPLSM(static_cast<uint64_t>(1)));
3✔
1657
    }
1658
    catch (...)
1✔
1659
    {
1660
    }
1661

1662
    ASSERT_EQ((CPLSM(static_cast<uint64_t>(2) * 1000 * 1000 * 1000) *
1✔
1663
               CPLSM(static_cast<uint64_t>(3) * 1000 * 1000 * 1000))
1664
                  .v(),
1665
              static_cast<uint64_t>(6) * 1000 * 1000 * 1000 * 1000 * 1000 *
1666
                  1000);
1667
    ASSERT_EQ((CPLSM(std::numeric_limits<uint64_t>::max()) *
1✔
1668
               CPLSM(static_cast<uint64_t>(1)))
1669
                  .v(),
1670
              std::numeric_limits<uint64_t>::max());
1671
    try
1672
    {
1673
        (CPLSM(std::numeric_limits<uint64_t>::max()) *
2✔
1674
         CPLSM(static_cast<uint64_t>(2)));
3✔
1675
    }
1676
    catch (...)
1✔
1677
    {
1678
    }
1679
}
1680

1681
// Test CPLParseRFC822DateTime()
1682
TEST_F(test_cpl, CPLParseRFC822DateTime)
4✔
1683
{
1684
    int year, month, day, hour, min, sec, tz, weekday;
1685
    ASSERT_TRUE(!CPLParseRFC822DateTime("", &year, &month, &day, &hour, &min,
1✔
1686
                                        &sec, &tz, &weekday));
1687

1688
    ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", nullptr,
1✔
1689
                                     nullptr, nullptr, nullptr, nullptr,
1690
                                     nullptr, nullptr, nullptr),
1691
              TRUE);
1692

1693
    ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 +0015", &year,
1✔
1694
                                     &month, &day, &hour, &min, &sec, &tz,
1695
                                     &weekday),
1696
              TRUE);
1697
    ASSERT_EQ(year, 2017);
1✔
1698
    ASSERT_EQ(month, 1);
1✔
1699
    ASSERT_EQ(day, 15);
1✔
1700
    ASSERT_EQ(hour, 12);
1✔
1701
    ASSERT_EQ(min, 34);
1✔
1702
    ASSERT_EQ(sec, 56);
1✔
1703
    ASSERT_EQ(tz, 101);
1✔
1704
    ASSERT_EQ(weekday, 4);
1✔
1705

1706
    ASSERT_EQ(CPLParseRFC822DateTime("Thu, 15 Jan 2017 12:34:56 GMT", &year,
1✔
1707
                                     &month, &day, &hour, &min, &sec, &tz,
1708
                                     &weekday),
1709
              TRUE);
1710
    ASSERT_EQ(year, 2017);
1✔
1711
    ASSERT_EQ(month, 1);
1✔
1712
    ASSERT_EQ(day, 15);
1✔
1713
    ASSERT_EQ(hour, 12);
1✔
1714
    ASSERT_EQ(min, 34);
1✔
1715
    ASSERT_EQ(sec, 56);
1✔
1716
    ASSERT_EQ(tz, 100);
1✔
1717
    ASSERT_EQ(weekday, 4);
1✔
1718

1719
    // Without day of week, second and timezone
1720
    ASSERT_EQ(CPLParseRFC822DateTime("15 Jan 2017 12:34", &year, &month, &day,
1✔
1721
                                     &hour, &min, &sec, &tz, &weekday),
1722
              TRUE);
1723
    ASSERT_EQ(year, 2017);
1✔
1724
    ASSERT_EQ(month, 1);
1✔
1725
    ASSERT_EQ(day, 15);
1✔
1726
    ASSERT_EQ(hour, 12);
1✔
1727
    ASSERT_EQ(min, 34);
1✔
1728
    ASSERT_EQ(sec, -1);
1✔
1729
    ASSERT_EQ(tz, 0);
1✔
1730
    ASSERT_EQ(weekday, 0);
1✔
1731

1732
    ASSERT_EQ(CPLParseRFC822DateTime("XXX, 15 Jan 2017 12:34:56 GMT", &year,
1✔
1733
                                     &month, &day, &hour, &min, &sec, &tz,
1734
                                     &weekday),
1735
              TRUE);
1736
    ASSERT_EQ(weekday, 0);
1✔
1737

1738
    ASSERT_TRUE(!CPLParseRFC822DateTime("Sun, 01 Jan 2017 12", &year, &month,
1✔
1739
                                        &day, &hour, &min, &sec, &tz,
1740
                                        &weekday));
1741

1742
    ASSERT_TRUE(!CPLParseRFC822DateTime("00 Jan 2017 12:34:56 GMT", &year,
1✔
1743
                                        &month, &day, &hour, &min, &sec, &tz,
1744
                                        &weekday));
1745

1746
    ASSERT_TRUE(!CPLParseRFC822DateTime("32 Jan 2017 12:34:56 GMT", &year,
1✔
1747
                                        &month, &day, &hour, &min, &sec, &tz,
1748
                                        &weekday));
1749

1750
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 XXX 2017 12:34:56 GMT", &year,
1✔
1751
                                        &month, &day, &hour, &min, &sec, &tz,
1752
                                        &weekday));
1753

1754
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 -1:34:56 GMT", &year,
1✔
1755
                                        &month, &day, &hour, &min, &sec, &tz,
1756
                                        &weekday));
1757

1758
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 24:34:56 GMT", &year,
1✔
1759
                                        &month, &day, &hour, &min, &sec, &tz,
1760
                                        &weekday));
1761

1762
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:-1:56 GMT", &year,
1✔
1763
                                        &month, &day, &hour, &min, &sec, &tz,
1764
                                        &weekday));
1765

1766
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:60:56 GMT", &year,
1✔
1767
                                        &month, &day, &hour, &min, &sec, &tz,
1768
                                        &weekday));
1769

1770
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:-1 GMT", &year,
1✔
1771
                                        &month, &day, &hour, &min, &sec, &tz,
1772
                                        &weekday));
1773

1774
    ASSERT_TRUE(!CPLParseRFC822DateTime("01 Jan 2017 12:34:61 GMT", &year,
1✔
1775
                                        &month, &day, &hour, &min, &sec, &tz,
1776
                                        &weekday));
1777

1778
    ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 XXX", &year,
1✔
1779
                                        &month, &day, &hour, &min, &sec, &tz,
1780
                                        &weekday));
1781

1782
    ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +-100", &year,
1✔
1783
                                        &month, &day, &hour, &min, &sec, &tz,
1784
                                        &weekday));
1785

1786
    ASSERT_TRUE(!CPLParseRFC822DateTime("15 Jan 2017 12:34:56 +9900", &year,
1✔
1787
                                        &month, &day, &hour, &min, &sec, &tz,
1788
                                        &weekday));
1789
}
1790

1791
// Test CPLParseMemorySize()
1792
TEST_F(test_cpl, CPLParseMemorySize)
4✔
1793
{
1794
    GIntBig nValue;
1795
    bool bUnitSpecified;
1796
    CPLErr result;
1797

1798
    result = CPLParseMemorySize("327mb", &nValue, &bUnitSpecified);
1✔
1799
    EXPECT_EQ(result, CE_None);
1✔
1800
    EXPECT_EQ(nValue, 327 * 1024 * 1024);
1✔
1801
    EXPECT_TRUE(bUnitSpecified);
1✔
1802

1803
    result = CPLParseMemorySize("327MB", &nValue, &bUnitSpecified);
1✔
1804
    EXPECT_EQ(result, CE_None);
1✔
1805
    EXPECT_EQ(nValue, 327 * 1024 * 1024);
1✔
1806
    EXPECT_TRUE(bUnitSpecified);
1✔
1807

1808
    result = CPLParseMemorySize("102.9K", &nValue, &bUnitSpecified);
1✔
1809
    EXPECT_EQ(result, CE_None);
1✔
1810
    EXPECT_EQ(nValue, static_cast<GIntBig>(102.9 * 1024));
1✔
1811
    EXPECT_TRUE(bUnitSpecified);
1✔
1812

1813
    result = CPLParseMemorySize("102.9 kB", &nValue, &bUnitSpecified);
1✔
1814
    EXPECT_EQ(result, CE_None);
1✔
1815
    EXPECT_EQ(nValue, static_cast<GIntBig>(102.9 * 1024));
1✔
1816
    EXPECT_TRUE(bUnitSpecified);
1✔
1817

1818
    result = CPLParseMemorySize("100%", &nValue, &bUnitSpecified);
1✔
1819
    EXPECT_EQ(result, CE_None);
1✔
1820
    EXPECT_GT(nValue, 100 * 1024 * 1024);
1✔
1821
    EXPECT_TRUE(bUnitSpecified);
1✔
1822

1823
    result = CPLParseMemorySize("0", &nValue, &bUnitSpecified);
1✔
1824
    EXPECT_EQ(result, CE_None);
1✔
1825
    EXPECT_EQ(nValue, 0);
1✔
1826
    EXPECT_FALSE(bUnitSpecified);
1✔
1827

1828
    result = CPLParseMemorySize("0MB", &nValue, &bUnitSpecified);
1✔
1829
    EXPECT_EQ(result, CE_None);
1✔
1830
    EXPECT_EQ(nValue, 0);
1✔
1831
    EXPECT_TRUE(bUnitSpecified);
1✔
1832

1833
    result = CPLParseMemorySize("  802  ", &nValue, &bUnitSpecified);
1✔
1834
    EXPECT_EQ(result, CE_None);
1✔
1835
    EXPECT_EQ(nValue, 802);
1✔
1836
    EXPECT_FALSE(bUnitSpecified);
1✔
1837

1838
    result = CPLParseMemorySize("110%", &nValue, &bUnitSpecified);
1✔
1839
    EXPECT_EQ(result, CE_Failure);
1✔
1840

1841
    result = CPLParseMemorySize("8kbit", &nValue, &bUnitSpecified);
1✔
1842
    EXPECT_EQ(result, CE_Failure);
1✔
1843

1844
    result = CPLParseMemorySize("8ZB", &nValue, &bUnitSpecified);
1✔
1845
    EXPECT_EQ(result, CE_Failure);
1✔
1846

1847
    result = CPLParseMemorySize("8Z", &nValue, &bUnitSpecified);
1✔
1848
    EXPECT_EQ(result, CE_Failure);
1✔
1849

1850
    result = CPLParseMemorySize("", &nValue, &bUnitSpecified);
1✔
1851
    EXPECT_EQ(result, CE_Failure);
1✔
1852

1853
    result = CPLParseMemorySize("  ", &nValue, &bUnitSpecified);
1✔
1854
    EXPECT_EQ(result, CE_Failure);
1✔
1855

1856
    result = CPLParseMemorySize("-100MB", &nValue, &bUnitSpecified);
1✔
1857
    EXPECT_EQ(result, CE_Failure);
1✔
1858

1859
    result = CPLParseMemorySize("nan", &nValue, &bUnitSpecified);
1✔
1860
    EXPECT_EQ(result, CE_Failure);
1✔
1861
}
1✔
1862

1863
// Test CPLCopyTree()
1864
TEST_F(test_cpl, CPLCopyTree)
4✔
1865
{
1866
    CPLString osTmpPath(CPLGetDirname(CPLGenerateTempFilename(nullptr)));
1✔
1867
    CPLString osSrcDir(CPLFormFilename(osTmpPath, "src_dir", nullptr));
1✔
1868
    CPLString osNewDir(CPLFormFilename(osTmpPath, "new_dir", nullptr));
1✔
1869
    CPLString osSrcFile(CPLFormFilename(osSrcDir, "my.bin", nullptr));
1✔
1870
    CPLString osNewFile(CPLFormFilename(osNewDir, "my.bin", nullptr));
1✔
1871

1872
    // Cleanup if previous test failed
1873
    VSIUnlink(osNewFile);
1✔
1874
    VSIRmdir(osNewDir);
1✔
1875
    VSIUnlink(osSrcFile);
1✔
1876
    VSIRmdir(osSrcDir);
1✔
1877

1878
    ASSERT_TRUE(VSIMkdir(osSrcDir, 0755) == 0);
1✔
1879
    VSILFILE *fp = VSIFOpenL(osSrcFile, "wb");
1✔
1880
    ASSERT_TRUE(fp != nullptr);
1✔
1881
    VSIFCloseL(fp);
1✔
1882

1883
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
1884
    ASSERT_TRUE(CPLCopyTree(osNewDir, "/i/do_not/exist") < 0);
1✔
1885
    CPLPopErrorHandler();
1✔
1886

1887
    ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) == 0);
1✔
1888
    VSIStatBufL sStat;
1889
    ASSERT_TRUE(VSIStatL(osNewFile, &sStat) == 0);
1✔
1890

1891
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
1892
    ASSERT_TRUE(CPLCopyTree(osNewDir, osSrcDir) < 0);
1✔
1893
    CPLPopErrorHandler();
1✔
1894

1895
    VSIUnlink(osNewFile);
1✔
1896
    VSIRmdir(osNewDir);
1✔
1897
    VSIUnlink(osSrcFile);
1✔
1898
    VSIRmdir(osSrcDir);
1✔
1899
}
1900

1901
class CPLJSonStreamingParserDump : public CPLJSonStreamingParser
1902
{
1903
    std::vector<bool> m_abFirstMember;
1904
    CPLString m_osSerialized;
1905
    CPLString m_osException;
1906

1907
  public:
1908
    CPLJSonStreamingParserDump()
72✔
1909
    {
72✔
1910
    }
72✔
1911

1912
    virtual void Reset() CPL_OVERRIDE
18✔
1913
    {
1914
        m_osSerialized.clear();
18✔
1915
        m_osException.clear();
18✔
1916
        CPLJSonStreamingParser::Reset();
18✔
1917
    }
18✔
1918

1919
    virtual void String(const char *pszValue, size_t) CPL_OVERRIDE;
1920
    virtual void Number(const char *pszValue, size_t) CPL_OVERRIDE;
1921
    virtual void Boolean(bool bVal) CPL_OVERRIDE;
1922
    virtual void Null() CPL_OVERRIDE;
1923

1924
    virtual void StartObject() CPL_OVERRIDE;
1925
    virtual void EndObject() CPL_OVERRIDE;
1926
    virtual void StartObjectMember(const char *pszKey, size_t) CPL_OVERRIDE;
1927

1928
    virtual void StartArray() CPL_OVERRIDE;
1929
    virtual void EndArray() CPL_OVERRIDE;
1930
    virtual void StartArrayMember() CPL_OVERRIDE;
1931

1932
    virtual void Exception(const char *pszMessage) CPL_OVERRIDE;
1933

1934
    const CPLString &GetSerialized() const
39✔
1935
    {
1936
        return m_osSerialized;
39✔
1937
    }
1938

1939
    const CPLString &GetException() const
51✔
1940
    {
1941
        return m_osException;
51✔
1942
    }
1943
};
1944

1945
void CPLJSonStreamingParserDump::StartObject()
21✔
1946
{
1947
    m_osSerialized += "{";
21✔
1948
    m_abFirstMember.push_back(true);
21✔
1949
}
21✔
1950

1951
void CPLJSonStreamingParserDump::EndObject()
10✔
1952
{
1953
    m_osSerialized += "}";
10✔
1954
    m_abFirstMember.pop_back();
10✔
1955
}
10✔
1956

1957
void CPLJSonStreamingParserDump::StartObjectMember(const char *pszKey, size_t)
20✔
1958
{
1959
    if (!m_abFirstMember.back())
20✔
1960
        m_osSerialized += ", ";
6✔
1961
    m_osSerialized += CPLSPrintf("\"%s\": ", pszKey);
20✔
1962
    m_abFirstMember.back() = false;
20✔
1963
}
20✔
1964

1965
void CPLJSonStreamingParserDump::String(const char *pszValue, size_t)
8✔
1966
{
1967
    m_osSerialized += GetSerializedString(pszValue);
8✔
1968
}
8✔
1969

1970
void CPLJSonStreamingParserDump::Number(const char *pszValue, size_t)
24✔
1971
{
1972
    m_osSerialized += pszValue;
24✔
1973
}
24✔
1974

1975
void CPLJSonStreamingParserDump::Boolean(bool bVal)
8✔
1976
{
1977
    m_osSerialized += bVal ? "true" : "false";
8✔
1978
}
8✔
1979

1980
void CPLJSonStreamingParserDump::Null()
7✔
1981
{
1982
    m_osSerialized += "null";
7✔
1983
}
7✔
1984

1985
void CPLJSonStreamingParserDump::StartArray()
21✔
1986
{
1987
    m_osSerialized += "[";
21✔
1988
    m_abFirstMember.push_back(true);
21✔
1989
}
21✔
1990

1991
void CPLJSonStreamingParserDump::EndArray()
13✔
1992
{
1993
    m_osSerialized += "]";
13✔
1994
    m_abFirstMember.pop_back();
13✔
1995
}
13✔
1996

1997
void CPLJSonStreamingParserDump::StartArrayMember()
15✔
1998
{
1999
    if (!m_abFirstMember.back())
15✔
2000
        m_osSerialized += ", ";
2✔
2001
    m_abFirstMember.back() = false;
15✔
2002
}
15✔
2003

2004
void CPLJSonStreamingParserDump::Exception(const char *pszMessage)
51✔
2005
{
2006
    m_osException = pszMessage;
51✔
2007
}
51✔
2008

2009
// Test CPLJSonStreamingParser()
2010
TEST_F(test_cpl, CPLJSonStreamingParser)
4✔
2011
{
2012
    // nominal cases
2013
    {
2014
        CPLJSonStreamingParserDump oParser;
1✔
2015
        const char sText[] = "true";
1✔
2016
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2017
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2018

2019
        oParser.Reset();
1✔
2020
        for (size_t i = 0; sText[i]; i++)
5✔
2021
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
4✔
2022
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2023
    }
2024
    {
2025
        CPLJSonStreamingParserDump oParser;
1✔
2026
        const char sText[] = "false";
1✔
2027
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2028
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2029

2030
        oParser.Reset();
1✔
2031
        for (size_t i = 0; sText[i]; i++)
6✔
2032
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
5✔
2033
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2034
    }
2035
    {
2036
        CPLJSonStreamingParserDump oParser;
1✔
2037
        const char sText[] = "null";
1✔
2038
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2039
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2040

2041
        oParser.Reset();
1✔
2042
        for (size_t i = 0; sText[i]; i++)
5✔
2043
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
4✔
2044
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2045
    }
2046
    {
2047
        CPLJSonStreamingParserDump oParser;
1✔
2048
        const char sText[] = "10";
1✔
2049
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2050
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2051

2052
        oParser.Reset();
1✔
2053
        for (size_t i = 0; sText[i]; i++)
3✔
2054
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
2✔
2055
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2056
    }
2057
    {
2058
        CPLJSonStreamingParserDump oParser;
1✔
2059
        const char sText[] = "123eE-34";
1✔
2060
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2061
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2062

2063
        oParser.Reset();
1✔
2064
        for (size_t i = 0; sText[i]; i++)
9✔
2065
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
8✔
2066
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2067
    }
2068
    {
2069
        CPLJSonStreamingParserDump oParser;
1✔
2070
        const char sText[] = "\"\"";
1✔
2071
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2072
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2073

2074
        oParser.Reset();
1✔
2075
        for (size_t i = 0; sText[i]; i++)
3✔
2076
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
2✔
2077
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2078
    }
2079
    {
2080
        CPLJSonStreamingParserDump oParser;
1✔
2081
        const char sText[] = "\"\\\\a\\b\\f\\n\\r\\t\\u0020\\u0001\\\"\"";
1✔
2082
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2083
        ASSERT_EQ(oParser.GetSerialized(),
1✔
2084
                  "\"\\\\a\\b\\f\\n\\r\\t \\u0001\\\"\"");
2085

2086
        oParser.Reset();
1✔
2087
        for (size_t i = 0; sText[i]; i++)
30✔
2088
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
29✔
2089
        ASSERT_EQ(oParser.GetSerialized(),
1✔
2090
                  "\"\\\\a\\b\\f\\n\\r\\t \\u0001\\\"\"");
2091
    }
2092
    {
2093
        CPLJSonStreamingParserDump oParser;
1✔
2094
        const char sText[] =
1✔
2095
            "\"\\u0001\\u0020\\ud834\\uDD1E\\uDD1E\\uD834\\uD834\\uD834\"";
2096
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2097
        ASSERT_EQ(
1✔
2098
            oParser.GetSerialized(),
2099
            "\"\\u0001 \xf0\x9d\x84\x9e\xef\xbf\xbd\xef\xbf\xbd\xef\xbf\xbd\"");
2100
    }
2101
    {
2102
        CPLJSonStreamingParserDump oParser;
1✔
2103
        const char sText[] = "\"\\ud834\"";
1✔
2104
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2105
        ASSERT_EQ(oParser.GetSerialized(), "\"\xef\xbf\xbd\"");
1✔
2106
    }
2107
    {
2108
        CPLJSonStreamingParserDump oParser;
1✔
2109
        const char sText[] = "\"\\ud834\\t\"";
1✔
2110
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2111
        ASSERT_EQ(oParser.GetSerialized(), "\"\xef\xbf\xbd\\t\"");
1✔
2112
    }
2113
    {
2114
        CPLJSonStreamingParserDump oParser;
1✔
2115
        const char sText[] = "\"\\u00e9\"";
1✔
2116
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2117
        ASSERT_EQ(oParser.GetSerialized(), "\"\xc3\xa9\"");
1✔
2118
    }
2119
    {
2120
        CPLJSonStreamingParserDump oParser;
1✔
2121
        const char sText[] = "{}";
1✔
2122
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2123
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2124

2125
        oParser.Reset();
1✔
2126
        for (size_t i = 0; sText[i]; i++)
3✔
2127
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
2✔
2128
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2129
    }
2130
    {
2131
        CPLJSonStreamingParserDump oParser;
1✔
2132
        const char sText[] = "[]";
1✔
2133
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2134
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2135

2136
        oParser.Reset();
1✔
2137
        for (size_t i = 0; sText[i]; i++)
3✔
2138
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
2✔
2139
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2140
    }
2141
    {
2142
        CPLJSonStreamingParserDump oParser;
1✔
2143
        const char sText[] = "[[]]";
1✔
2144
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2145
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2146

2147
        oParser.Reset();
1✔
2148
        for (size_t i = 0; sText[i]; i++)
5✔
2149
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
4✔
2150
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2151
    }
2152
    {
2153
        CPLJSonStreamingParserDump oParser;
1✔
2154
        const char sText[] = "[1]";
1✔
2155
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2156
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2157

2158
        oParser.Reset();
1✔
2159
        for (size_t i = 0; sText[i]; i++)
4✔
2160
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
3✔
2161
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2162
    }
2163
    {
2164
        CPLJSonStreamingParserDump oParser;
1✔
2165
        const char sText[] = "[1,2]";
1✔
2166
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2167
        ASSERT_EQ(oParser.GetSerialized(), "[1, 2]");
1✔
2168

2169
        oParser.Reset();
1✔
2170
        for (size_t i = 0; sText[i]; i++)
6✔
2171
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
5✔
2172
        ASSERT_EQ(oParser.GetSerialized(), "[1, 2]");
1✔
2173
    }
2174
    {
2175
        CPLJSonStreamingParserDump oParser;
1✔
2176
        const char sText[] = "{\"a\":null}";
1✔
2177
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2178
        ASSERT_EQ(oParser.GetSerialized(), "{\"a\": null}");
1✔
2179

2180
        oParser.Reset();
1✔
2181
        for (size_t i = 0; sText[i]; i++)
11✔
2182
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
10✔
2183
        ASSERT_EQ(oParser.GetSerialized(), "{\"a\": null}");
1✔
2184
    }
2185
    {
2186
        CPLJSonStreamingParserDump oParser;
1✔
2187
        const char sText[] =
1✔
2188
            " { \"a\" : null ,\r\n\t\"b\": {\"c\": 1}, \"d\": [1] }";
2189
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2190
        const char sExpected[] = "{\"a\": null, \"b\": {\"c\": 1}, \"d\": [1]}";
1✔
2191
        ASSERT_EQ(oParser.GetSerialized(), sExpected);
1✔
2192

2193
        oParser.Reset();
1✔
2194
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2195
        ASSERT_EQ(oParser.GetSerialized(), sExpected);
1✔
2196

2197
        oParser.Reset();
1✔
2198
        for (size_t i = 0; sText[i]; i++)
44✔
2199
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
43✔
2200
        ASSERT_EQ(oParser.GetSerialized(), sExpected);
1✔
2201
    }
2202
    {
2203
        CPLJSonStreamingParserDump oParser;
1✔
2204
        const char sText[] = "infinity";
1✔
2205
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2206
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2207

2208
        oParser.Reset();
1✔
2209
        for (size_t i = 0; sText[i]; i++)
9✔
2210
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
8✔
2211
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2212
    }
2213
    {
2214
        CPLJSonStreamingParserDump oParser;
1✔
2215
        const char sText[] = "-infinity";
1✔
2216
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2217
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2218

2219
        oParser.Reset();
1✔
2220
        for (size_t i = 0; sText[i]; i++)
10✔
2221
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
9✔
2222
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2223
    }
2224
    {
2225
        CPLJSonStreamingParserDump oParser;
1✔
2226
        const char sText[] = "nan";
1✔
2227
        ASSERT_TRUE(oParser.Parse(sText, strlen(sText), true));
1✔
2228
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2229

2230
        oParser.Reset();
1✔
2231
        for (size_t i = 0; sText[i]; i++)
4✔
2232
            ASSERT_TRUE(oParser.Parse(sText + i, 1, sText[i + 1] == 0));
3✔
2233
        ASSERT_EQ(oParser.GetSerialized(), sText);
1✔
2234
    }
2235

2236
    // errors
2237
    {
2238
        CPLJSonStreamingParserDump oParser;
1✔
2239
        const char sText[] = "tru";
1✔
2240
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2241
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2242
    }
2243
    {
2244
        CPLJSonStreamingParserDump oParser;
1✔
2245
        const char sText[] = "tru1";
1✔
2246
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2247
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2248
    }
2249
    {
2250
        CPLJSonStreamingParserDump oParser;
1✔
2251
        const char sText[] = "truxe";
1✔
2252
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2253
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2254
    }
2255
    {
2256
        CPLJSonStreamingParserDump oParser;
1✔
2257
        const char sText[] = "truex";
1✔
2258
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2259
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2260
    }
2261
    {
2262
        CPLJSonStreamingParserDump oParser;
1✔
2263
        const char sText[] = "fals";
1✔
2264
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2265
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2266
    }
2267
    {
2268
        CPLJSonStreamingParserDump oParser;
1✔
2269
        const char sText[] = "falsxe";
1✔
2270
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2271
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2272
    }
2273
    {
2274
        CPLJSonStreamingParserDump oParser;
1✔
2275
        const char sText[] = "falsex";
1✔
2276
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2277
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2278
    }
2279
    {
2280
        CPLJSonStreamingParserDump oParser;
1✔
2281
        const char sText[] = "nul";
1✔
2282
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2283
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2284
    }
2285
    {
2286
        CPLJSonStreamingParserDump oParser;
1✔
2287
        const char sText[] = "nulxl";
1✔
2288
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2289
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2290
    }
2291
    {
2292
        CPLJSonStreamingParserDump oParser;
1✔
2293
        const char sText[] = "nullx";
1✔
2294
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2295
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2296
    }
2297
    {
2298
        CPLJSonStreamingParserDump oParser;
1✔
2299
        const char sText[] = "na";
1✔
2300
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2301
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2302
    }
2303
    {
2304
        CPLJSonStreamingParserDump oParser;
1✔
2305
        const char sText[] = "nanx";
1✔
2306
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2307
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2308
    }
2309
    {
2310
        CPLJSonStreamingParserDump oParser;
1✔
2311
        const char sText[] = "infinit";
1✔
2312
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2313
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2314
    }
2315
    {
2316
        CPLJSonStreamingParserDump oParser;
1✔
2317
        const char sText[] = "infinityx";
1✔
2318
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2319
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2320
    }
2321
    {
2322
        CPLJSonStreamingParserDump oParser;
1✔
2323
        const char sText[] = "-infinit";
1✔
2324
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2325
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2326
    }
2327
    {
2328
        CPLJSonStreamingParserDump oParser;
1✔
2329
        const char sText[] = "-infinityx";
1✔
2330
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2331
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2332
    }
2333
    {
2334
        CPLJSonStreamingParserDump oParser;
1✔
2335
        const char sText[] = "true false";
1✔
2336
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2337
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2338
    }
2339
    {
2340
        CPLJSonStreamingParserDump oParser;
1✔
2341
        const char sText[] = "x";
1✔
2342
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2343
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2344
    }
2345
    {
2346
        CPLJSonStreamingParserDump oParser;
1✔
2347
        const char sText[] = "{";
1✔
2348
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2349
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2350
    }
2351
    {
2352
        CPLJSonStreamingParserDump oParser;
1✔
2353
        const char sText[] = "}";
1✔
2354
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2355
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2356
    }
2357
    {
2358
        CPLJSonStreamingParserDump oParser;
1✔
2359
        const char sText[] = "[";
1✔
2360
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2361
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2362
    }
2363
    {
2364
        CPLJSonStreamingParserDump oParser;
1✔
2365
        const char sText[] = "[1";
1✔
2366
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2367
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2368
    }
2369
    {
2370
        CPLJSonStreamingParserDump oParser;
1✔
2371
        const char sText[] = "[,";
1✔
2372
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2373
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2374
    }
2375
    {
2376
        CPLJSonStreamingParserDump oParser;
1✔
2377
        const char sText[] = "[|";
1✔
2378
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2379
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2380
    }
2381
    {
2382
        CPLJSonStreamingParserDump oParser;
1✔
2383
        const char sText[] = "]";
1✔
2384
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2385
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2386
    }
2387
    {
2388
        CPLJSonStreamingParserDump oParser;
1✔
2389
        const char sText[] = "{ :";
1✔
2390
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2391
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2392
    }
2393
    {
2394
        CPLJSonStreamingParserDump oParser;
1✔
2395
        const char sText[] = "{ ,";
1✔
2396
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2397
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2398
    }
2399
    {
2400
        CPLJSonStreamingParserDump oParser;
1✔
2401
        const char sText[] = "{ |";
1✔
2402
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2403
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2404
    }
2405
    {
2406
        CPLJSonStreamingParserDump oParser;
1✔
2407
        const char sText[] = "{ 1";
1✔
2408
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2409
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2410
    }
2411
    {
2412
        CPLJSonStreamingParserDump oParser;
1✔
2413
        const char sText[] = "{ \"x\"";
1✔
2414
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2415
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2416
    }
2417
    {
2418
        CPLJSonStreamingParserDump oParser;
1✔
2419
        const char sText[] = "{ \"x\": ";
1✔
2420
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2421
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2422
    }
2423
    {
2424
        CPLJSonStreamingParserDump oParser;
1✔
2425
        const char sText[] = "{ \"x\": 1 2";
1✔
2426
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2427
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2428
    }
2429
    {
2430
        CPLJSonStreamingParserDump oParser;
1✔
2431
        const char sText[] = "{ \"x\", ";
1✔
2432
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2433
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2434
    }
2435
    {
2436
        CPLJSonStreamingParserDump oParser;
1✔
2437
        const char sText[] = "{ \"x\" }";
1✔
2438
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2439
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2440
    }
2441
    {
2442
        CPLJSonStreamingParserDump oParser;
1✔
2443
        const char sText[] = "{\"a\" x}";
1✔
2444
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2445
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2446
    }
2447
    {
2448
        CPLJSonStreamingParserDump oParser;
1✔
2449
        const char sText[] = "1x";
1✔
2450
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2451
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2452
    }
2453
    {
2454
        CPLJSonStreamingParserDump oParser;
1✔
2455
        const char sText[] = "\"";
1✔
2456
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2457
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2458
    }
2459
    {
2460
        CPLJSonStreamingParserDump oParser;
1✔
2461
        const char sText[] = "\"\\";
1✔
2462
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2463
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2464
    }
2465
    {
2466
        CPLJSonStreamingParserDump oParser;
1✔
2467
        const char sText[] = "\"\\x\"";
1✔
2468
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2469
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2470
    }
2471
    {
2472
        CPLJSonStreamingParserDump oParser;
1✔
2473
        const char sText[] = "\"\\u";
1✔
2474
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2475
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2476
    }
2477
    {
2478
        CPLJSonStreamingParserDump oParser;
1✔
2479
        const char sText[] = "\"\\ux";
1✔
2480
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2481
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2482
    }
2483
    {
2484
        CPLJSonStreamingParserDump oParser;
1✔
2485
        const char sText[] = "\"\\u000";
1✔
2486
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2487
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2488
    }
2489
    {
2490
        CPLJSonStreamingParserDump oParser;
1✔
2491
        const char sText[] = "\"\\uD834\\ux\"";
1✔
2492
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2493
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2494
    }
2495
    {
2496
        CPLJSonStreamingParserDump oParser;
1✔
2497
        const char sText[] = "\"\\\"";
1✔
2498
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2499
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2500
    }
2501
    {
2502
        CPLJSonStreamingParserDump oParser;
1✔
2503
        const char sText[] = "\"too long\"";
1✔
2504
        oParser.SetMaxStringSize(2);
1✔
2505
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2506
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2507
    }
2508
    {
2509
        CPLJSonStreamingParserDump oParser;
1✔
2510
        const char sText[] = "[[]]";
1✔
2511
        oParser.SetMaxDepth(1);
1✔
2512
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2513
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2514
    }
2515
    {
2516
        CPLJSonStreamingParserDump oParser;
1✔
2517
        const char sText[] = "{ \"x\": {} }";
1✔
2518
        oParser.SetMaxDepth(1);
1✔
2519
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2520
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2521
    }
2522
    {
2523
        CPLJSonStreamingParserDump oParser;
1✔
2524
        const char sText[] = "[,]";
1✔
2525
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2526
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2527
    }
2528
    {
2529
        CPLJSonStreamingParserDump oParser;
1✔
2530
        const char sText[] = "[true,]";
1✔
2531
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2532
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2533
    }
2534
    {
2535
        CPLJSonStreamingParserDump oParser;
1✔
2536
        const char sText[] = "[true,,true]";
1✔
2537
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2538
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2539
    }
2540
    {
2541
        CPLJSonStreamingParserDump oParser;
1✔
2542
        const char sText[] = "[true true]";
1✔
2543
        ASSERT_TRUE(!oParser.Parse(sText, strlen(sText), true));
1✔
2544
        ASSERT_TRUE(!oParser.GetException().empty());
1✔
2545
    }
2546
}
2547

2548
// Test cpl_mem_cache
2549
TEST_F(test_cpl, cpl_mem_cache)
4✔
2550
{
2551
    lru11::Cache<int, int> cache(2, 1);
1✔
2552
    ASSERT_EQ(cache.size(), 0U);
1✔
2553
    ASSERT_TRUE(cache.empty());
1✔
2554
    cache.clear();
1✔
2555
    int val;
2556
    ASSERT_TRUE(!cache.tryGet(0, val));
1✔
2557
    try
2558
    {
2559
        cache.get(0);
1✔
2560
        ASSERT_TRUE(false);
×
2561
    }
2562
    catch (const lru11::KeyNotFound &)
2✔
2563
    {
2564
        ASSERT_TRUE(true);
1✔
2565
    }
2566
    ASSERT_TRUE(!cache.remove(0));
1✔
2567
    ASSERT_TRUE(!cache.contains(0));
1✔
2568
    ASSERT_EQ(cache.getMaxSize(), 2U);
1✔
2569
    ASSERT_EQ(cache.getElasticity(), 1U);
1✔
2570
    ASSERT_EQ(cache.getMaxAllowedSize(), 3U);
1✔
2571
    int out;
2572
    ASSERT_TRUE(!cache.removeAndRecycleOldestEntry(out));
1✔
2573

2574
    cache.insert(0, 1);
1✔
2575
    val = 0;
1✔
2576
    ASSERT_TRUE(cache.tryGet(0, val));
1✔
2577
    int *ptr = cache.getPtr(0);
1✔
2578
    ASSERT_TRUE(ptr);
1✔
2579
    ASSERT_EQ(*ptr, 1);
1✔
2580
    ASSERT_TRUE(cache.getPtr(-1) == nullptr);
1✔
2581
    ASSERT_EQ(val, 1);
1✔
2582
    ASSERT_EQ(cache.get(0), 1);
1✔
2583
    ASSERT_EQ(cache.getCopy(0), 1);
1✔
2584
    ASSERT_EQ(cache.size(), 1U);
1✔
2585
    ASSERT_TRUE(!cache.empty());
1✔
2586
    ASSERT_TRUE(cache.contains(0));
1✔
2587
    bool visited = false;
1✔
2588
    auto lambda = [&visited](const lru11::KeyValuePair<int, int> &kv)
2✔
2589
    {
2590
        if (kv.key == 0 && kv.value == 1)
1✔
2591
            visited = true;
1✔
2592
    };
2✔
2593
    cache.cwalk(lambda);
1✔
2594
    ASSERT_TRUE(visited);
1✔
2595

2596
    out = -1;
1✔
2597
    ASSERT_TRUE(cache.removeAndRecycleOldestEntry(out));
1✔
2598
    ASSERT_EQ(out, 1);
1✔
2599

2600
    cache.insert(0, 1);
1✔
2601
    cache.insert(0, 2);
1✔
2602
    ASSERT_EQ(cache.get(0), 2);
1✔
2603
    ASSERT_EQ(cache.size(), 1U);
1✔
2604
    cache.insert(1, 3);
1✔
2605
    cache.insert(2, 4);
1✔
2606
    ASSERT_EQ(cache.size(), 3U);
1✔
2607
    cache.insert(3, 5);
1✔
2608
    ASSERT_EQ(cache.size(), 2U);
1✔
2609
    ASSERT_TRUE(cache.contains(2));
1✔
2610
    ASSERT_TRUE(cache.contains(3));
1✔
2611
    ASSERT_TRUE(!cache.contains(0));
1✔
2612
    ASSERT_TRUE(!cache.contains(1));
1✔
2613
    ASSERT_TRUE(cache.remove(2));
1✔
2614
    ASSERT_TRUE(!cache.contains(2));
1✔
2615
    ASSERT_EQ(cache.size(), 1U);
1✔
2616

2617
    {
2618
        // Check that MyObj copy constructor and copy-assignment operator
2619
        // are not needed
2620
        struct MyObj
2621
        {
2622
            int m_v;
2623

2624
            MyObj(int v) : m_v(v)
4✔
2625
            {
2626
            }
4✔
2627

2628
            MyObj(const MyObj &) = delete;
2629
            MyObj &operator=(const MyObj &) = delete;
2630
            MyObj(MyObj &&) = default;
2631
            MyObj &operator=(MyObj &&) = default;
2632
        };
2633

2634
        lru11::Cache<int, MyObj> cacheMyObj(2, 0);
1✔
2635
        ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
1✔
2636
        cacheMyObj.getPtr(0);
1✔
2637
        ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
1✔
2638
        ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
1✔
2639
        MyObj outObj(-1);
1✔
2640
        cacheMyObj.removeAndRecycleOldestEntry(outObj);
1✔
2641
    }
2642

2643
    {
2644
        // Check that MyObj copy constructor and copy-assignment operator
2645
        // are not triggered
2646
        struct MyObj
2647
        {
2648
            int m_v;
2649

2650
            MyObj(int v) : m_v(v)
4✔
2651
            {
2652
            }
4✔
2653

2654
            static void should_not_happen()
2655
            {
2656
                ASSERT_TRUE(false);
2657
            }
2658

2659
            MyObj(const MyObj &) : m_v(-1)
2660
            {
2661
                should_not_happen();
2662
            }
2663

2664
            MyObj &operator=(const MyObj &)
2665
            {
2666
                should_not_happen();
2667
                return *this;
2668
            }
2669

2670
            MyObj(MyObj &&) = default;
2671
            MyObj &operator=(MyObj &&) = default;
2672
        };
2673

2674
        lru11::Cache<int, MyObj> cacheMyObj(2, 0);
1✔
2675
        ASSERT_EQ(cacheMyObj.insert(0, MyObj(0)).m_v, 0);
1✔
2676
        cacheMyObj.getPtr(0);
1✔
2677
        ASSERT_EQ(cacheMyObj.insert(1, MyObj(1)).m_v, 1);
1✔
2678
        ASSERT_EQ(cacheMyObj.insert(2, MyObj(2)).m_v, 2);
1✔
2679
        MyObj outObj(-1);
1✔
2680
        cacheMyObj.removeAndRecycleOldestEntry(outObj);
1✔
2681
    }
2682
}
2683

2684
// Test CPLJSONDocument
2685
TEST_F(test_cpl, CPLJSONDocument)
4✔
2686
{
2687
    {
2688
        // Test Json document LoadUrl
2689
        CPLJSONDocument oDocument;
1✔
2690
        const char *options[5] = {"CONNECTTIMEOUT=15", "TIMEOUT=20",
1✔
2691
                                  "MAX_RETRY=5", "RETRY_DELAY=1", nullptr};
2692

2693
        oDocument.GetRoot().Add("foo", "bar");
1✔
2694

2695
        if (CPLHTTPEnabled())
1✔
2696
        {
2697
            CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", "YES");
1✔
2698
            VSILFILE *fpTmp = VSIFOpenL("/vsimem/test.json", "wb");
1✔
2699
            const char *pszContent = "{ \"foo\": \"bar\" }";
1✔
2700
            VSIFWriteL(pszContent, 1, strlen(pszContent), fpTmp);
1✔
2701
            VSIFCloseL(fpTmp);
1✔
2702
            ASSERT_TRUE(oDocument.LoadUrl("/vsimem/test.json",
1✔
2703
                                          const_cast<char **>(options)));
2704
            CPLSetConfigOption("CPL_CURL_ENABLE_VSIMEM", nullptr);
1✔
2705
            VSIUnlink("/vsimem/test.json");
1✔
2706

2707
            CPLJSONObject oJsonRoot = oDocument.GetRoot();
1✔
2708
            ASSERT_TRUE(oJsonRoot.IsValid());
1✔
2709

2710
            CPLString value = oJsonRoot.GetString("foo", "");
2✔
2711
            ASSERT_STRNE(value, "bar");  // not equal
1✔
2712
        }
2713
    }
2714
    {
2715
        // Test Json document LoadChunks
2716
        CPLJSONDocument oDocument;
1✔
2717

2718
        CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
2719
        ASSERT_TRUE(!oDocument.LoadChunks("/i_do/not/exist", 512));
1✔
2720
        CPLPopErrorHandler();
1✔
2721

2722
        CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
2723
        ASSERT_TRUE(!oDocument.LoadChunks("test_cpl.cpp", 512));
1✔
2724
        CPLPopErrorHandler();
1✔
2725

2726
        oDocument.GetRoot().Add("foo", "bar");
1✔
2727

2728
        ASSERT_TRUE(
1✔
2729
            oDocument.LoadChunks((data_ + SEP + "test.json").c_str(), 512));
2730

2731
        CPLJSONObject oJsonRoot = oDocument.GetRoot();
1✔
2732
        ASSERT_TRUE(oJsonRoot.IsValid());
1✔
2733
        ASSERT_EQ(oJsonRoot.GetInteger("resource/id", 10), 0);
1✔
2734

2735
        CPLJSONObject oJsonResource = oJsonRoot.GetObj("resource");
2✔
2736
        ASSERT_TRUE(oJsonResource.IsValid());
1✔
2737
        std::vector<CPLJSONObject> children = oJsonResource.GetChildren();
1✔
2738
        ASSERT_TRUE(children.size() == 11);
1✔
2739

2740
        CPLJSONArray oaScopes = oJsonRoot.GetArray("resource/scopes");
2✔
2741
        ASSERT_TRUE(oaScopes.IsValid());
1✔
2742
        ASSERT_EQ(oaScopes.Size(), 2);
1✔
2743

2744
        CPLJSONObject oHasChildren = oJsonRoot.GetObj("resource/children");
2✔
2745
        ASSERT_TRUE(oHasChildren.IsValid());
1✔
2746
        ASSERT_EQ(oHasChildren.ToBool(), true);
1✔
2747

2748
        ASSERT_EQ(oJsonResource.GetBool("children", false), true);
1✔
2749

2750
        CPLJSONObject oJsonId = oJsonRoot["resource/owner_user/id"];
2✔
2751
        ASSERT_TRUE(oJsonId.IsValid());
1✔
2752
    }
2753
    {
2754
        CPLJSONDocument oDocument;
1✔
2755
        ASSERT_TRUE(!oDocument.LoadMemory(nullptr, 0));
1✔
2756
        ASSERT_TRUE(!oDocument.LoadMemory(CPLString()));
1✔
2757
        ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
1✔
2758
        ASSERT_TRUE(oDocument.GetRoot().GetType() ==
1✔
2759
                    CPLJSONObject::Type::Boolean);
2760
        ASSERT_TRUE(oDocument.GetRoot().ToBool());
1✔
2761
        ASSERT_TRUE(oDocument.LoadMemory(std::string("false")));
1✔
2762
        ASSERT_TRUE(oDocument.GetRoot().GetType() ==
1✔
2763
                    CPLJSONObject::Type::Boolean);
2764
        ASSERT_TRUE(!oDocument.GetRoot().ToBool());
1✔
2765
    }
2766
    {
2767
        // Copy constructor
2768
        CPLJSONDocument oDocument;
1✔
2769
        ASSERT_TRUE(oDocument.LoadMemory(std::string("true")));
1✔
2770
        oDocument.GetRoot();
1✔
2771
        CPLJSONDocument oDocument2(oDocument);
1✔
2772
        CPLJSONObject oObj(oDocument.GetRoot());
1✔
2773
        ASSERT_TRUE(oObj.ToBool());
1✔
2774
        CPLJSONObject oObj2(oObj);
1✔
2775
        ASSERT_TRUE(oObj2.ToBool());
1✔
2776
        // Assignment operator
2777
        oDocument2 = oDocument;
1✔
2778
        oDocument.GetRoot();  // avoid Coverity Scan warning
1✔
2779
        auto &oDocument2Ref(oDocument2);
1✔
2780
        oDocument2 = oDocument2Ref;
1✔
2781
        oObj2 = oObj;
1✔
2782
        oObj.GetType();  // avoid Coverity Scan warning
1✔
2783
        auto &oObj2Ref(oObj2);
1✔
2784
        oObj2 = oObj2Ref;
1✔
2785
        CPLJSONObject oObj3(std::move(oObj2));
1✔
2786
        ASSERT_TRUE(oObj3.ToBool());
1✔
2787
        CPLJSONObject oObj4;
1✔
2788
        oObj4 = std::move(oObj3);
1✔
2789
        ASSERT_TRUE(oObj4.ToBool());
1✔
2790
    }
2791
    {
2792
        // Move constructor
2793
        CPLJSONDocument oDocument;
2✔
2794
        oDocument.GetRoot();
1✔
2795
        CPLJSONDocument oDocument2(std::move(oDocument));
1✔
2796
    }
2797
    {
2798
        // Move assignment
2799
        CPLJSONDocument oDocument;
2✔
2800
        oDocument.GetRoot();
1✔
2801
        CPLJSONDocument oDocument2;
2✔
2802
        oDocument2 = std::move(oDocument);
1✔
2803
    }
2804
    {
2805
        // Save
2806
        CPLJSONDocument oDocument;
1✔
2807
        CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
2808
        ASSERT_TRUE(!oDocument.Save("/i_do/not/exist"));
1✔
2809
        CPLPopErrorHandler();
1✔
2810
    }
2811
    {
2812
        CPLJSONObject oObj(nullptr);
2✔
2813
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Null);
1✔
2814
    }
2815
    {
2816
        CPLJSONObject oObj(true);
2✔
2817
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Boolean);
1✔
2818
        EXPECT_EQ(oObj.ToBool(), true);
1✔
2819
    }
2820
    {
2821
        CPLJSONObject oObj(1);
2✔
2822
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Integer);
1✔
2823
        EXPECT_EQ(oObj.ToInteger(), 1);
1✔
2824
    }
2825
    {
2826
        CPLJSONObject oObj(static_cast<int64_t>(123) * 1024 * 1024 * 1024);
2✔
2827
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Long);
1✔
2828
        EXPECT_EQ(oObj.ToLong(),
1✔
2829
                  static_cast<int64_t>(123) * 1024 * 1024 * 1024);
2830
    }
2831
    {
2832
        CPLJSONObject oObj(static_cast<uint64_t>(123) * 1024 * 1024 * 1024);
2✔
2833
        // Might be a string with older libjson versions
2834
        if (oObj.GetType() == CPLJSONObject::Type::Long)
1✔
2835
        {
2836
            EXPECT_EQ(oObj.ToLong(),
1✔
2837
                      static_cast<int64_t>(123) * 1024 * 1024 * 1024);
2838
        }
2839
    }
2840
    {
2841
        CPLJSONObject oObj(1.5);
2✔
2842
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::Double);
1✔
2843
        EXPECT_EQ(oObj.ToDouble(), 1.5);
1✔
2844
    }
2845
    {
2846
        CPLJSONObject oObj("ab");
2✔
2847
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::String);
1✔
2848
        EXPECT_STREQ(oObj.ToString().c_str(), "ab");
2✔
2849
    }
2850
    {
2851
        CPLJSONObject oObj(std::string("ab"));
3✔
2852
        EXPECT_EQ(oObj.GetType(), CPLJSONObject::Type::String);
1✔
2853
        EXPECT_STREQ(oObj.ToString().c_str(), "ab");
2✔
2854
    }
2855
    {
2856
        CPLJSONObject oObj;
1✔
2857
        oObj.Add("string", std::string("my_string"));
1✔
2858
        ASSERT_EQ(oObj.GetString("string"), std::string("my_string"));
2✔
2859
        ASSERT_EQ(oObj.GetString("inexisting_string", "default"),
2✔
2860
                  std::string("default"));
2861
        oObj.Add("const_char_star", nullptr);
1✔
2862
        oObj.Add("const_char_star", "my_const_char_star");
1✔
2863
        ASSERT_TRUE(oObj.GetObj("const_char_star").GetType() ==
1✔
2864
                    CPLJSONObject::Type::String);
2865
        oObj.Add("int", 1);
1✔
2866
        ASSERT_EQ(oObj.GetInteger("int"), 1);
1✔
2867
        ASSERT_EQ(oObj.GetInteger("inexisting_int", -987), -987);
1✔
2868
        ASSERT_TRUE(oObj.GetObj("int").GetType() ==
1✔
2869
                    CPLJSONObject::Type::Integer);
2870
        oObj.Add("int64", GINT64_MAX);
1✔
2871
        ASSERT_EQ(oObj.GetLong("int64"), GINT64_MAX);
2✔
2872
        ASSERT_EQ(oObj.GetLong("inexisting_int64", GINT64_MIN), GINT64_MIN);
2✔
2873
        ASSERT_TRUE(oObj.GetObj("int64").GetType() ==
1✔
2874
                    CPLJSONObject::Type::Long);
2875
        oObj.Add("double", 1.25);
1✔
2876
        ASSERT_EQ(oObj.GetDouble("double"), 1.25);
1✔
2877
        ASSERT_EQ(oObj.GetDouble("inexisting_double", -987.0), -987.0);
1✔
2878
        ASSERT_TRUE(oObj.GetObj("double").GetType() ==
1✔
2879
                    CPLJSONObject::Type::Double);
2880
        oObj.Add("array", CPLJSONArray());
1✔
2881
        ASSERT_TRUE(oObj.GetObj("array").GetType() ==
1✔
2882
                    CPLJSONObject::Type::Array);
2883
        oObj.Add("obj", CPLJSONObject());
1✔
2884
        ASSERT_TRUE(oObj.GetObj("obj").GetType() ==
1✔
2885
                    CPLJSONObject::Type::Object);
2886
        oObj.Add("bool", true);
1✔
2887
        ASSERT_EQ(oObj.GetBool("bool"), true);
1✔
2888
        ASSERT_EQ(oObj.GetBool("inexisting_bool", false), false);
1✔
2889
        ASSERT_TRUE(oObj.GetObj("bool").GetType() ==
1✔
2890
                    CPLJSONObject::Type::Boolean);
2891
        oObj.AddNull("null_field");
1✔
2892
        ASSERT_TRUE(oObj.GetObj("null_field").GetType() ==
1✔
2893
                    CPLJSONObject::Type::Null);
2894
        ASSERT_TRUE(oObj.GetObj("inexisting").GetType() ==
1✔
2895
                    CPLJSONObject::Type::Unknown);
2896
        oObj.Set("string", std::string("my_string"));
1✔
2897
        oObj.Set("const_char_star", nullptr);
1✔
2898
        oObj.Set("const_char_star", "my_const_char_star");
1✔
2899
        oObj.Set("int", 1);
1✔
2900
        oObj.Set("int64", GINT64_MAX);
1✔
2901
        oObj.Set("double", 1.25);
1✔
2902
        // oObj.Set("array", CPLJSONArray());
2903
        // oObj.Set("obj", CPLJSONObject());
2904
        oObj.Set("bool", true);
1✔
2905
        oObj.SetNull("null_field");
1✔
2906
        ASSERT_TRUE(CPLJSONArray().GetChildren().empty());
1✔
2907
        oObj.ToArray();
1✔
2908
    }
2909
    {
2910
        CPLJSONObject oObj;
2✔
2911
        oObj.Set("foo", "bar");
1✔
2912
        EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Spaced).c_str(),
2✔
2913
                     "{ \"foo\": \"bar\" }");
2914
        EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Pretty).c_str(),
2✔
2915
                     "{\n  \"foo\":\"bar\"\n}");
2916
        EXPECT_STREQ(oObj.Format(CPLJSONObject::PrettyFormat::Plain).c_str(),
2✔
2917
                     "{\"foo\":\"bar\"}");
2918
    }
2919
    {
2920
        CPLJSONArray oArrayConstructorString(std::string("foo"));
2✔
2921
        CPLJSONArray oArray;
1✔
2922
        oArray.Add(CPLJSONObject());
1✔
2923
        oArray.Add(std::string("str"));
1✔
2924
        oArray.Add("const_char_star");
1✔
2925
        oArray.Add(1.25);
1✔
2926
        oArray.Add(1);
1✔
2927
        oArray.Add(GINT64_MAX);
1✔
2928
        oArray.Add(true);
1✔
2929
        oArray.AddNull();
1✔
2930
        ASSERT_EQ(oArray.Size(), 8);
1✔
2931

2932
        int nCount = 0;
1✔
2933
        for (const auto &obj : oArray)
9✔
2934
        {
2935
            ASSERT_EQ(obj.GetInternalHandle(),
8✔
2936
                      oArray[nCount].GetInternalHandle());
2937
            nCount++;
8✔
2938
        }
2939
        ASSERT_EQ(nCount, 8);
1✔
2940
    }
2941
    {
2942
        CPLJSONDocument oDocument;
1✔
2943
        ASSERT_TRUE(oDocument.LoadMemory(CPLString("{ \"/foo\" : \"bar\" }")));
1✔
2944
        ASSERT_EQ(oDocument.GetRoot().GetString("/foo"), std::string("bar"));
2✔
2945
    }
2946
}
2947

2948
// Test CPLRecodeIconv() with re-allocation
2949
// (this test also passed on Windows using its native recoding API)
2950
TEST_F(test_cpl, CPLRecodeIconv)
4✔
2951
{
2952
#if defined(CPL_RECODE_ICONV) || defined(_WIN32)
2953
    int N = 32800;
1✔
2954
    char *pszIn = static_cast<char *>(CPLMalloc(N + 1));
1✔
2955
    for (int i = 0; i < N; i++)
32,801✔
2956
        pszIn[i] = '\xA1';
32,800✔
2957
    pszIn[N] = 0;
1✔
2958
    char *pszExpected = static_cast<char *>(CPLMalloc(N * 2 + 1));
1✔
2959
    for (int i = 0; i < N; i++)
32,801✔
2960
    {
2961
        pszExpected[2 * i] = '\xD0';
32,800✔
2962
        pszExpected[2 * i + 1] = '\x81';
32,800✔
2963
    }
2964
    pszExpected[N * 2] = 0;
1✔
2965
    char *pszRet = CPLRecode(pszIn, "ISO-8859-5", CPL_ENC_UTF8);
1✔
2966
    EXPECT_EQ(memcmp(pszExpected, pszRet, N * 2 + 1), 0);
1✔
2967
    CPLFree(pszIn);
1✔
2968
    CPLFree(pszRet);
1✔
2969
    CPLFree(pszExpected);
1✔
2970
#else
2971
    GTEST_SKIP() << "CPL_RECODE_ICONV missing";
2972
#endif
2973
}
1✔
2974

2975
// Test CP1252 to UTF-8
2976
TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_strict_alloc)
4✔
2977
{
2978
    CPLClearRecodeWarningFlags();
1✔
2979
    CPLErrorReset();
1✔
2980
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
2981
    // Euro character expands to 3-bytes
2982
    char *pszRet = CPLRecode("\x80", "CP1252", CPL_ENC_UTF8);
1✔
2983
    CPLPopErrorHandler();
1✔
2984
    EXPECT_STREQ(CPLGetLastErrorMsg(), "");
1✔
2985
    EXPECT_EQ(memcmp(pszRet, "\xE2\x82\xAC\x00", 4), 0);
1✔
2986
    CPLFree(pszRet);
1✔
2987
}
1✔
2988

2989
// Test CP1252 to UTF-8
2990
TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_with_ascii)
4✔
2991
{
2992
    CPLClearRecodeWarningFlags();
1✔
2993
    CPLErrorReset();
1✔
2994
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
2995
    char *pszRet = CPLRecode("x\x80y", "CP1252", CPL_ENC_UTF8);
1✔
2996
    CPLPopErrorHandler();
1✔
2997
    EXPECT_STREQ(CPLGetLastErrorMsg(), "");
1✔
2998
    EXPECT_EQ(memcmp(pszRet, "x\xE2\x82\xACy\x00", 6), 0);
1✔
2999
    CPLFree(pszRet);
1✔
3000
}
1✔
3001

3002
// Test CP1252 to UTF-8
3003
TEST_F(test_cpl, CPLRecodeStubCP1252_to_UTF8_with_warning)
4✔
3004
{
3005
    CPLClearRecodeWarningFlags();
1✔
3006
    CPLErrorReset();
1✔
3007
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3008
    // \x90 is an invalid CP1252 character. Will be skipped
3009
    char *pszRet = CPLRecode("\x90\x80", "CP1252", CPL_ENC_UTF8);
1✔
3010
    CPLPopErrorHandler();
1✔
3011
    EXPECT_STREQ(
1✔
3012
        CPLGetLastErrorMsg(),
3013
        "One or several characters couldn't be converted correctly from CP1252 "
3014
        "to UTF-8. This warning will not be emitted anymore");
3015
    EXPECT_EQ(memcmp(pszRet, "\xE2\x82\xAC\x00", 4), 0);
1✔
3016
    CPLFree(pszRet);
1✔
3017
}
1✔
3018

3019
// Test CPLHTTPParseMultipartMime()
3020
TEST_F(test_cpl, CPLHTTPParseMultipartMime)
4✔
3021
{
3022
    CPLHTTPResult *psResult;
3023

3024
    psResult =
3025
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3026
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3027
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3028
    CPLPopErrorHandler();
1✔
3029
    CPLHTTPDestroyResult(psResult);
1✔
3030

3031
    // Missing boundary value
3032
    psResult =
3033
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3034
    psResult->pszContentType = CPLStrdup("multipart/form-data; boundary=");
1✔
3035
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3036
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3037
    CPLPopErrorHandler();
1✔
3038
    CPLHTTPDestroyResult(psResult);
1✔
3039

3040
    // No content
3041
    psResult =
3042
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3043
    psResult->pszContentType =
1✔
3044
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3045
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3046
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3047
    CPLPopErrorHandler();
1✔
3048
    CPLHTTPDestroyResult(psResult);
1✔
3049

3050
    // No part
3051
    psResult =
3052
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3053
    psResult->pszContentType =
1✔
3054
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3055
    {
3056
        const char *pszText = "--myboundary  some junk\r\n";
1✔
3057
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3058
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3059
    }
3060
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3061
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3062
    CPLPopErrorHandler();
1✔
3063
    CPLHTTPDestroyResult(psResult);
1✔
3064

3065
    // Missing end boundary
3066
    psResult =
3067
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3068
    psResult->pszContentType =
1✔
3069
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3070
    {
3071
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3072
                              "\r\n"
3073
                              "Bla";
3074
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3075
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3076
    }
3077
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3078
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3079
    CPLPopErrorHandler();
1✔
3080
    CPLHTTPDestroyResult(psResult);
1✔
3081

3082
    // Truncated header
3083
    psResult =
3084
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3085
    psResult->pszContentType =
1✔
3086
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3087
    {
3088
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3089
                              "Content-Type: foo";
3090
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3091
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3092
    }
3093
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3094
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3095
    CPLPopErrorHandler();
1✔
3096
    CPLHTTPDestroyResult(psResult);
1✔
3097

3098
    // Invalid end boundary
3099
    psResult =
3100
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3101
    psResult->pszContentType =
1✔
3102
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3103
    {
3104
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3105
                              "\r\n"
3106
                              "Bla"
3107
                              "\r\n"
3108
                              "--myboundary";
3109
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3110
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3111
    }
3112
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3113
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3114
    CPLPopErrorHandler();
1✔
3115
    CPLHTTPDestroyResult(psResult);
1✔
3116

3117
    // Invalid end boundary
3118
    psResult =
3119
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3120
    psResult->pszContentType =
1✔
3121
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3122
    {
3123
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3124
                              "\r\n"
3125
                              "Bla"
3126
                              "\r\n"
3127
                              "--myboundary";
3128
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3129
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3130
    }
3131
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3132
    EXPECT_TRUE(!CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3133
    CPLPopErrorHandler();
1✔
3134
    CPLHTTPDestroyResult(psResult);
1✔
3135

3136
    // Valid single part, no header
3137
    psResult =
3138
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3139
    psResult->pszContentType =
1✔
3140
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3141
    {
3142
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3143
                              "\r\n"
3144
                              "Bla"
3145
                              "\r\n"
3146
                              "--myboundary--\r\n";
3147
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3148
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3149
    }
3150
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3151
    EXPECT_EQ(psResult->nMimePartCount, 1);
1✔
3152
    if (psResult->nMimePartCount == 1)
1✔
3153
    {
3154
        EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
1✔
3155
                  static_cast<char **>(nullptr));
3156
        EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
1✔
3157
        EXPECT_TRUE(
1✔
3158
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
3159
                    "Bla", 3) == 0);
3160
    }
3161
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3162
    CPLHTTPDestroyResult(psResult);
1✔
3163

3164
    // Valid single part, with header
3165
    psResult =
3166
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3167
    psResult->pszContentType =
1✔
3168
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3169
    {
3170
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3171
                              "Content-Type: bla\r\n"
3172
                              "\r\n"
3173
                              "Bla"
3174
                              "\r\n"
3175
                              "--myboundary--\r\n";
3176
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3177
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3178
    }
3179
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3180
    EXPECT_EQ(psResult->nMimePartCount, 1);
1✔
3181
    if (psResult->nMimePartCount == 1)
1✔
3182
    {
3183
        EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 1);
1✔
3184
        EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
1✔
3185
                     "Content-Type=bla");
3186
        EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
1✔
3187
        EXPECT_TRUE(
1✔
3188
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
3189
                    "Bla", 3) == 0);
3190
    }
3191
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3192
    CPLHTTPDestroyResult(psResult);
1✔
3193

3194
    // Valid single part, 2 headers
3195
    psResult =
3196
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3197
    psResult->pszContentType =
1✔
3198
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3199
    {
3200
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3201
                              "Content-Type: bla\r\n"
3202
                              "Content-Disposition: bar\r\n"
3203
                              "\r\n"
3204
                              "Bla"
3205
                              "\r\n"
3206
                              "--myboundary--\r\n";
3207
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3208
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3209
    }
3210
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3211
    EXPECT_EQ(psResult->nMimePartCount, 1);
1✔
3212
    if (psResult->nMimePartCount == 1)
1✔
3213
    {
3214
        EXPECT_EQ(CSLCount(psResult->pasMimePart[0].papszHeaders), 2);
1✔
3215
        EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
1✔
3216
                     "Content-Type=bla");
3217
        EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[1],
1✔
3218
                     "Content-Disposition=bar");
3219
        EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
1✔
3220
        EXPECT_TRUE(
1✔
3221
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
3222
                    "Bla", 3) == 0);
3223
    }
3224
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3225
    CPLHTTPDestroyResult(psResult);
1✔
3226

3227
    // Single part, but with header without extra terminating \r\n
3228
    // (invalid normally, but apparently necessary for some ArcGIS WCS
3229
    // implementations)
3230
    psResult =
3231
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3232
    psResult->pszContentType =
1✔
3233
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3234
    {
3235
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3236
                              "Content-Type: bla\r\n"
3237
                              "Bla"
3238
                              "\r\n"
3239
                              "--myboundary--\r\n";
3240
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3241
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3242
    }
3243
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3244
    EXPECT_EQ(psResult->nMimePartCount, 1);
1✔
3245
    if (psResult->nMimePartCount == 1)
1✔
3246
    {
3247
        EXPECT_STREQ(psResult->pasMimePart[0].papszHeaders[0],
1✔
3248
                     "Content-Type=bla");
3249
        EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
1✔
3250
        EXPECT_TRUE(
1✔
3251
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
3252
                    "Bla", 3) == 0);
3253
    }
3254
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3255
    CPLHTTPDestroyResult(psResult);
1✔
3256

3257
    // Valid 2 parts, no header
3258
    psResult =
3259
        static_cast<CPLHTTPResult *>(CPLCalloc(1, sizeof(CPLHTTPResult)));
1✔
3260
    psResult->pszContentType =
1✔
3261
        CPLStrdup("multipart/form-data; boundary=myboundary");
1✔
3262
    {
3263
        const char *pszText = "--myboundary  some junk\r\n"
1✔
3264
                              "\r\n"
3265
                              "Bla"
3266
                              "\r\n"
3267
                              "--myboundary\r\n"
3268
                              "\r\n"
3269
                              "second part"
3270
                              "\r\n"
3271
                              "--myboundary--\r\n";
3272
        psResult->pabyData = reinterpret_cast<GByte *>(CPLStrdup(pszText));
1✔
3273
        psResult->nDataLen = static_cast<int>(strlen(pszText));
1✔
3274
    }
3275
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3276
    EXPECT_EQ(psResult->nMimePartCount, 2);
1✔
3277
    if (psResult->nMimePartCount == 2)
1✔
3278
    {
3279
        EXPECT_EQ(psResult->pasMimePart[0].papszHeaders,
1✔
3280
                  static_cast<char **>(nullptr));
3281
        EXPECT_EQ(psResult->pasMimePart[0].nDataLen, 3);
1✔
3282
        EXPECT_TRUE(
1✔
3283
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[0].pabyData),
3284
                    "Bla", 3) == 0);
3285
        EXPECT_EQ(psResult->pasMimePart[1].nDataLen, 11);
1✔
3286
        EXPECT_TRUE(
1✔
3287
            strncmp(reinterpret_cast<char *>(psResult->pasMimePart[1].pabyData),
3288
                    "second part", 11) == 0);
3289
    }
3290
    EXPECT_TRUE(CPL_TO_BOOL(CPLHTTPParseMultipartMime(psResult)));
1✔
3291
    CPLHTTPDestroyResult(psResult);
1✔
3292
}
1✔
3293

3294
// Test cpl::down_cast
3295
TEST_F(test_cpl, down_cast)
4✔
3296
{
3297
    struct Base
3298
    {
3299
        virtual ~Base()
2✔
3300
        {
2✔
3301
        }
2✔
3302
    };
3303

3304
    struct Derived : public Base
3305
    {
3306
    };
3307

3308
    Base b;
×
3309
    Derived d;
×
3310
    Base *p_b_d = &d;
1✔
3311

3312
#ifdef wont_compile
3313
    struct OtherBase
3314
    {
3315
    };
3316

3317
    OtherBase ob;
3318
    ASSERT_EQ(cpl::down_cast<OtherBase *>(p_b_d), &ob);
3319
#endif
3320
#ifdef compile_with_warning
3321
    ASSERT_EQ(cpl::down_cast<Base *>(p_b_d), p_b_d);
3322
#endif
3323
    ASSERT_EQ(cpl::down_cast<Derived *>(p_b_d), &d);
1✔
3324
    ASSERT_EQ(cpl::down_cast<Derived *>(static_cast<Base *>(nullptr)),
1✔
3325
              static_cast<Derived *>(nullptr));
3326
}
3327

3328
// Test CPLPrintTime() in particular case of RFC822 formatting in C locale
3329
TEST_F(test_cpl, CPLPrintTime_RFC822)
4✔
3330
{
3331
    char szDate[64];
3332
    struct tm tm;
3333
    tm.tm_sec = 56;
1✔
3334
    tm.tm_min = 34;
1✔
3335
    tm.tm_hour = 12;
1✔
3336
    tm.tm_mday = 20;
1✔
3337
    tm.tm_mon = 6 - 1;
1✔
3338
    tm.tm_year = 2018 - 1900;
1✔
3339
    tm.tm_wday = 3;   // Wednesday
1✔
3340
    tm.tm_yday = 0;   // unused
1✔
3341
    tm.tm_isdst = 0;  // unused
1✔
3342
    int nRet = CPLPrintTime(szDate, sizeof(szDate) - 1,
1✔
3343
                            "%a, %d %b %Y %H:%M:%S GMT", &tm, "C");
3344
    szDate[nRet] = 0;
1✔
3345
    ASSERT_STREQ(szDate, "Wed, 20 Jun 2018 12:34:56 GMT");
1✔
3346
}
3347

3348
// Test CPLAutoClose
3349
TEST_F(test_cpl, CPLAutoClose)
4✔
3350
{
3351
    static int counter = 0;
3352

3353
    class AutoCloseTest
3354
    {
3355
      public:
3356
        AutoCloseTest()
2✔
3357
        {
2✔
3358
            counter += 222;
2✔
3359
        }
2✔
3360

3361
        virtual ~AutoCloseTest()
4✔
3362
        {
2✔
3363
            counter -= 22;
2✔
3364
        }
4✔
3365

3366
        static AutoCloseTest *Create()
2✔
3367
        {
3368
            return new AutoCloseTest;
2✔
3369
        }
3370

3371
        static void Destroy(AutoCloseTest *p)
2✔
3372
        {
3373
            delete p;
2✔
3374
        }
2✔
3375
    };
3376

3377
    {
3378
        AutoCloseTest *p1 = AutoCloseTest::Create();
1✔
3379
        CPL_AUTO_CLOSE_WARP(p1, AutoCloseTest::Destroy);
2✔
3380

3381
        AutoCloseTest *p2 = AutoCloseTest::Create();
1✔
3382
        CPL_AUTO_CLOSE_WARP(p2, AutoCloseTest::Destroy);
1✔
3383
    }
3384
    ASSERT_EQ(counter, 400);
1✔
3385
}
3386

3387
// Test cpl_minixml
3388
TEST_F(test_cpl, cpl_minixml)
4✔
3389
{
3390
    CPLXMLNode *psRoot = CPLCreateXMLNode(nullptr, CXT_Element, "Root");
1✔
3391
    CPLXMLNode *psElt = CPLCreateXMLElementAndValue(psRoot, "Elt", "value");
1✔
3392
    CPLAddXMLAttributeAndValue(psElt, "attr1", "val1");
1✔
3393
    CPLAddXMLAttributeAndValue(psElt, "attr2", "val2");
1✔
3394
    EXPECT_GE(CPLXMLNodeGetRAMUsageEstimate(psRoot), 0);
1✔
3395
    char *str = CPLSerializeXMLTree(psRoot);
1✔
3396
    CPLDestroyXMLNode(psRoot);
1✔
3397
    ASSERT_STREQ(
1✔
3398
        str,
3399
        "<Root>\n  <Elt attr1=\"val1\" attr2=\"val2\">value</Elt>\n</Root>\n");
3400
    CPLFree(str);
1✔
3401
}
3402

3403
// Test CPLCharUniquePtr
3404
TEST_F(test_cpl, CPLCharUniquePtr)
4✔
3405
{
3406
    CPLCharUniquePtr x;
×
3407
    ASSERT_TRUE(x.get() == nullptr);
1✔
3408
    x.reset(CPLStrdup("foo"));
1✔
3409
    ASSERT_STREQ(x.get(), "foo");
1✔
3410
}
3411

3412
// Test CPLJSonStreamingWriter
3413
TEST_F(test_cpl, CPLJSonStreamingWriter)
4✔
3414
{
3415
    {
3416
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3417
        ASSERT_EQ(x.GetString(), std::string());
2✔
3418
    }
3419
    {
3420
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3421
        x.Add(true);
1✔
3422
        ASSERT_EQ(x.GetString(), std::string("true"));
2✔
3423
    }
3424
    {
3425
        std::string res;
1✔
3426

3427
        struct MyCallback
3428
        {
3429
            static void f(const char *pszText, void *user_data)
1✔
3430
            {
3431
                *static_cast<std::string *>(user_data) += pszText;
1✔
3432
            }
1✔
3433
        };
3434

3435
        CPLJSonStreamingWriter x(&MyCallback::f, &res);
1✔
3436
        x.Add(true);
1✔
3437
        ASSERT_EQ(x.GetString(), std::string());
2✔
3438
        ASSERT_EQ(res, std::string("true"));
2✔
3439
    }
3440
    {
3441
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3442
        x.Add(false);
1✔
3443
        ASSERT_EQ(x.GetString(), std::string("false"));
2✔
3444
    }
3445
    {
3446
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3447
        x.AddNull();
1✔
3448
        ASSERT_EQ(x.GetString(), std::string("null"));
2✔
3449
    }
3450
    {
3451
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3452
        x.Add(1);
1✔
3453
        ASSERT_EQ(x.GetString(), std::string("1"));
2✔
3454
    }
3455
    {
3456
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3457
        x.Add(4200000000U);
1✔
3458
        ASSERT_EQ(x.GetString(), std::string("4200000000"));
2✔
3459
    }
3460
    {
3461
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3462
        x.Add(static_cast<std::int64_t>(-10000) * 1000000);
1✔
3463
        ASSERT_EQ(x.GetString(), std::string("-10000000000"));
2✔
3464
    }
3465
    {
3466
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3467
        x.Add(static_cast<std::uint64_t>(10000) * 1000000);
1✔
3468
        ASSERT_EQ(x.GetString(), std::string("10000000000"));
2✔
3469
    }
3470
    {
3471
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3472
        x.Add(1.5f);
1✔
3473
        ASSERT_EQ(x.GetString(), std::string("1.5"));
2✔
3474
    }
3475
    {
3476
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3477
        x.Add(std::numeric_limits<float>::quiet_NaN());
1✔
3478
        ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
2✔
3479
    }
3480
    {
3481
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3482
        x.Add(std::numeric_limits<float>::infinity());
1✔
3483
        ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
2✔
3484
    }
3485
    {
3486
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3487
        x.Add(-std::numeric_limits<float>::infinity());
1✔
3488
        ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
2✔
3489
    }
3490
    {
3491
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3492
        x.Add(1.25);
1✔
3493
        ASSERT_EQ(x.GetString(), std::string("1.25"));
2✔
3494
    }
3495
    {
3496
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3497
        x.Add(std::numeric_limits<double>::quiet_NaN());
1✔
3498
        ASSERT_EQ(x.GetString(), std::string("\"NaN\""));
2✔
3499
    }
3500
    {
3501
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3502
        x.Add(std::numeric_limits<double>::infinity());
1✔
3503
        ASSERT_EQ(x.GetString(), std::string("\"Infinity\""));
2✔
3504
    }
3505
    {
3506
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3507
        x.Add(-std::numeric_limits<double>::infinity());
1✔
3508
        ASSERT_EQ(x.GetString(), std::string("\"-Infinity\""));
2✔
3509
    }
3510
    {
3511
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3512
        x.Add(std::string("foo\\bar\"baz\b\f\n\r\t"
1✔
3513
                          "\x01"
3514
                          "boo"));
3515
        ASSERT_EQ(
2✔
3516
            x.GetString(),
3517
            std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
3518
    }
3519
    {
3520
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3521
        x.Add("foo\\bar\"baz\b\f\n\r\t"
1✔
3522
              "\x01"
3523
              "boo");
3524
        ASSERT_EQ(
2✔
3525
            x.GetString(),
3526
            std::string("\"foo\\\\bar\\\"baz\\b\\f\\n\\r\\t\\u0001boo\""));
3527
    }
3528
    {
3529
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3530
        x.SetPrettyFormatting(false);
1✔
3531
        {
3532
            auto ctxt(x.MakeObjectContext());
1✔
3533
        }
3534
        ASSERT_EQ(x.GetString(), std::string("{}"));
2✔
3535
    }
3536
    {
3537
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3538
        {
3539
            auto ctxt(x.MakeObjectContext());
1✔
3540
        }
3541
        ASSERT_EQ(x.GetString(), std::string("{}"));
2✔
3542
    }
3543
    {
3544
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3545
        x.SetPrettyFormatting(false);
1✔
3546
        {
3547
            auto ctxt(x.MakeObjectContext());
2✔
3548
            x.AddObjKey("key");
1✔
3549
            x.Add("value");
1✔
3550
        }
3551
        ASSERT_EQ(x.GetString(), std::string("{\"key\":\"value\"}"));
2✔
3552
    }
3553
    {
3554
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3555
        {
3556
            auto ctxt(x.MakeObjectContext());
2✔
3557
            x.AddObjKey("key");
1✔
3558
            x.Add("value");
1✔
3559
        }
3560
        ASSERT_EQ(x.GetString(), std::string("{\n  \"key\": \"value\"\n}"));
2✔
3561
    }
3562
    {
3563
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3564
        {
3565
            auto ctxt(x.MakeObjectContext());
2✔
3566
            x.AddObjKey("key");
1✔
3567
            x.Add("value");
1✔
3568
            x.AddObjKey("key2");
1✔
3569
            x.Add("value2");
1✔
3570
        }
3571
        ASSERT_EQ(
2✔
3572
            x.GetString(),
3573
            std::string("{\n  \"key\": \"value\",\n  \"key2\": \"value2\"\n}"));
3574
    }
3575
    {
3576
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3577
        {
3578
            auto ctxt(x.MakeArrayContext());
1✔
3579
        }
3580
        ASSERT_EQ(x.GetString(), std::string("[]"));
2✔
3581
    }
3582
    {
3583
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3584
        {
3585
            auto ctxt(x.MakeArrayContext());
2✔
3586
            x.Add(1);
1✔
3587
        }
3588
        ASSERT_EQ(x.GetString(), std::string("[\n  1\n]"));
2✔
3589
    }
3590
    {
3591
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3592
        {
3593
            auto ctxt(x.MakeArrayContext());
2✔
3594
            x.Add(1);
1✔
3595
            x.Add(2);
1✔
3596
        }
3597
        ASSERT_EQ(x.GetString(), std::string("[\n  1,\n  2\n]"));
2✔
3598
    }
3599
    {
3600
        CPLJSonStreamingWriter x(nullptr, nullptr);
1✔
3601
        {
3602
            auto ctxt(x.MakeArrayContext(true));
2✔
3603
            x.Add(1);
1✔
3604
            x.Add(2);
1✔
3605
        }
3606
        ASSERT_EQ(x.GetString(), std::string("[1, 2]"));
2✔
3607
    }
3608
}
3609

3610
// Test CPLWorkerThreadPool
3611
TEST_F(test_cpl, CPLWorkerThreadPool)
4✔
3612
{
3613
    CPLWorkerThreadPool oPool;
1✔
3614
    ASSERT_TRUE(oPool.Setup(2, nullptr, nullptr, false));
1✔
3615

3616
    const auto myJob = [](void *pData) { (*static_cast<int *>(pData))++; };
3,000✔
3617

3618
    {
3619
        std::vector<int> res(1000);
1✔
3620
        for (int i = 0; i < 1000; i++)
1,001✔
3621
        {
3622
            res[i] = i;
1,000✔
3623
            oPool.SubmitJob(myJob, &res[i]);
1,000✔
3624
        }
3625
        oPool.WaitCompletion();
1✔
3626
        for (int i = 0; i < 1000; i++)
1,001✔
3627
        {
3628
            ASSERT_EQ(res[i], i + 1);
1,000✔
3629
        }
3630
    }
3631

3632
    {
3633
        std::vector<int> res(1000);
1✔
3634
        std::vector<void *> resPtr(1000);
1✔
3635
        for (int i = 0; i < 1000; i++)
1,001✔
3636
        {
3637
            res[i] = i;
1,000✔
3638
            resPtr[i] = res.data() + i;
1,000✔
3639
        }
3640
        oPool.SubmitJobs(myJob, resPtr);
1✔
3641
        oPool.WaitEvent();
1✔
3642
        oPool.WaitCompletion();
1✔
3643
        for (int i = 0; i < 1000; i++)
1,001✔
3644
        {
3645
            ASSERT_EQ(res[i], i + 1);
1,000✔
3646
        }
3647
    }
3648

3649
    {
3650
        auto jobQueue1 = oPool.CreateJobQueue();
1✔
3651
        auto jobQueue2 = oPool.CreateJobQueue();
1✔
3652

3653
        ASSERT_EQ(jobQueue1->GetPool(), &oPool);
1✔
3654

3655
        std::vector<int> res(1000);
1✔
3656
        for (int i = 0; i < 1000; i++)
1,001✔
3657
        {
3658
            res[i] = i;
1,000✔
3659
            if (i % 2)
1,000✔
3660
                jobQueue1->SubmitJob(myJob, &res[i]);
500✔
3661
            else
3662
                jobQueue2->SubmitJob(myJob, &res[i]);
500✔
3663
        }
3664
        jobQueue1->WaitCompletion();
1✔
3665
        jobQueue2->WaitCompletion();
1✔
3666
        for (int i = 0; i < 1000; i++)
1,001✔
3667
        {
3668
            ASSERT_EQ(res[i], i + 1);
1,000✔
3669
        }
3670
    }
3671
}
3672

3673
// Test CPLHTTPFetch
3674
TEST_F(test_cpl, CPLHTTPFetch)
4✔
3675
{
3676
#ifdef HAVE_CURL
3677
    CPLStringList oOptions;
2✔
3678
    oOptions.AddNameValue("FORM_ITEM_COUNT", "5");
1✔
3679
    oOptions.AddNameValue("FORM_KEY_0", "qqq");
1✔
3680
    oOptions.AddNameValue("FORM_VALUE_0", "www");
1✔
3681
    CPLHTTPResult *pResult = CPLHTTPFetch("http://example.com", oOptions);
1✔
3682
    EXPECT_EQ(pResult->nStatus, 34);
1✔
3683
    CPLHTTPDestroyResult(pResult);
1✔
3684
    pResult = nullptr;
1✔
3685
    oOptions.Clear();
1✔
3686

3687
    oOptions.AddNameValue("FORM_FILE_PATH", "not_existed");
1✔
3688
    pResult = CPLHTTPFetch("http://example.com", oOptions);
1✔
3689
    EXPECT_EQ(pResult->nStatus, 34);
1✔
3690
    CPLHTTPDestroyResult(pResult);
1✔
3691
#else
3692
    GTEST_SKIP() << "CURL not available";
3693
#endif  // HAVE_CURL
3694
}
1✔
3695

3696
// Test CPLHTTPPushFetchCallback
3697
TEST_F(test_cpl, CPLHTTPPushFetchCallback)
4✔
3698
{
3699
    struct myCbkUserDataStruct
3700
    {
3701
        CPLString osURL{};
3702
        CSLConstList papszOptions = nullptr;
3703
        GDALProgressFunc pfnProgress = nullptr;
3704
        void *pProgressArg = nullptr;
3705
        CPLHTTPFetchWriteFunc pfnWrite = nullptr;
3706
        void *pWriteArg = nullptr;
3707
    };
3708

3709
    const auto myCbk = [](const char *pszURL, CSLConstList papszOptions,
1✔
3710
                          GDALProgressFunc pfnProgress, void *pProgressArg,
3711
                          CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
3712
                          void *pUserData)
3713
    {
3714
        myCbkUserDataStruct *pCbkUserData =
1✔
3715
            static_cast<myCbkUserDataStruct *>(pUserData);
3716
        pCbkUserData->osURL = pszURL;
1✔
3717
        pCbkUserData->papszOptions = papszOptions;
1✔
3718
        pCbkUserData->pfnProgress = pfnProgress;
1✔
3719
        pCbkUserData->pProgressArg = pProgressArg;
1✔
3720
        pCbkUserData->pfnWrite = pfnWrite;
1✔
3721
        pCbkUserData->pWriteArg = pWriteArg;
1✔
3722
        auto psResult =
3723
            static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
1✔
3724
        psResult->nStatus = 123;
1✔
3725
        return psResult;
1✔
3726
    };
3727

3728
    myCbkUserDataStruct userData;
1✔
3729
    EXPECT_TRUE(CPLHTTPPushFetchCallback(myCbk, &userData));
1✔
3730

3731
    int progressArg = 0;
1✔
3732
    const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
×
3733
    { return 0; };
×
3734
    int writeCbkArg = 00;
1✔
3735

3736
    CPLStringList aosOptions;
1✔
3737
    GDALProgressFunc pfnProgress = GDALTermProgress;
1✔
3738
    CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
1✔
3739
    CPLHTTPResult *pResult =
3740
        CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
1✔
3741
                       &progressArg, pfnWriteCbk, &writeCbkArg);
3742
    ASSERT_TRUE(pResult != nullptr);
1✔
3743
    EXPECT_EQ(pResult->nStatus, 123);
1✔
3744
    CPLHTTPDestroyResult(pResult);
1✔
3745

3746
    EXPECT_TRUE(CPLHTTPPopFetchCallback());
1✔
3747
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3748
    EXPECT_TRUE(!CPLHTTPPopFetchCallback());
1✔
3749
    CPLPopErrorHandler();
1✔
3750

3751
    EXPECT_STREQ(userData.osURL, "http://example.com");
1✔
3752
    EXPECT_EQ(userData.papszOptions, aosOptions.List());
1✔
3753
    EXPECT_EQ(userData.pfnProgress, pfnProgress);
1✔
3754
    EXPECT_EQ(userData.pProgressArg, &progressArg);
1✔
3755
    EXPECT_EQ(userData.pfnWrite, pfnWriteCbk);
1✔
3756
    EXPECT_EQ(userData.pWriteArg, &writeCbkArg);
1✔
3757
}
3758

3759
// Test CPLHTTPSetFetchCallback
3760
TEST_F(test_cpl, CPLHTTPSetFetchCallback)
4✔
3761
{
3762
    struct myCbkUserDataStruct
3763
    {
3764
        CPLString osURL{};
3765
        CSLConstList papszOptions = nullptr;
3766
        GDALProgressFunc pfnProgress = nullptr;
3767
        void *pProgressArg = nullptr;
3768
        CPLHTTPFetchWriteFunc pfnWrite = nullptr;
3769
        void *pWriteArg = nullptr;
3770
    };
3771

3772
    const auto myCbk2 = [](const char *pszURL, CSLConstList papszOptions,
1✔
3773
                           GDALProgressFunc pfnProgress, void *pProgressArg,
3774
                           CPLHTTPFetchWriteFunc pfnWrite, void *pWriteArg,
3775
                           void *pUserData)
3776
    {
3777
        myCbkUserDataStruct *pCbkUserData =
1✔
3778
            static_cast<myCbkUserDataStruct *>(pUserData);
3779
        pCbkUserData->osURL = pszURL;
1✔
3780
        pCbkUserData->papszOptions = papszOptions;
1✔
3781
        pCbkUserData->pfnProgress = pfnProgress;
1✔
3782
        pCbkUserData->pProgressArg = pProgressArg;
1✔
3783
        pCbkUserData->pfnWrite = pfnWrite;
1✔
3784
        pCbkUserData->pWriteArg = pWriteArg;
1✔
3785
        auto psResult =
3786
            static_cast<CPLHTTPResult *>(CPLCalloc(sizeof(CPLHTTPResult), 1));
1✔
3787
        psResult->nStatus = 124;
1✔
3788
        return psResult;
1✔
3789
    };
3790
    myCbkUserDataStruct userData2;
1✔
3791
    CPLHTTPSetFetchCallback(myCbk2, &userData2);
1✔
3792

3793
    int progressArg = 0;
1✔
3794
    const auto myWriteCbk = [](void *, size_t, size_t, void *) -> size_t
×
3795
    { return 0; };
×
3796
    int writeCbkArg = 00;
1✔
3797

3798
    CPLStringList aosOptions;
1✔
3799
    GDALProgressFunc pfnProgress = GDALTermProgress;
1✔
3800
    CPLHTTPFetchWriteFunc pfnWriteCbk = myWriteCbk;
1✔
3801
    CPLHTTPResult *pResult =
3802
        CPLHTTPFetchEx("http://example.com", aosOptions.List(), pfnProgress,
1✔
3803
                       &progressArg, pfnWriteCbk, &writeCbkArg);
3804
    ASSERT_TRUE(pResult != nullptr);
1✔
3805
    EXPECT_EQ(pResult->nStatus, 124);
1✔
3806
    CPLHTTPDestroyResult(pResult);
1✔
3807

3808
    CPLHTTPSetFetchCallback(nullptr, nullptr);
1✔
3809

3810
    EXPECT_STREQ(userData2.osURL, "http://example.com");
1✔
3811
    EXPECT_EQ(userData2.papszOptions, aosOptions.List());
1✔
3812
    EXPECT_EQ(userData2.pfnProgress, pfnProgress);
1✔
3813
    EXPECT_EQ(userData2.pProgressArg, &progressArg);
1✔
3814
    EXPECT_EQ(userData2.pfnWrite, pfnWriteCbk);
1✔
3815
    EXPECT_EQ(userData2.pWriteArg, &writeCbkArg);
1✔
3816
}
3817

3818
// Test CPLLoadConfigOptionsFromFile() and
3819
// CPLLoadConfigOptionsFromPredefinedFiles()
3820
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile)
4✔
3821
{
3822
    CPLLoadConfigOptionsFromFile("/i/do/not/exist", false);
1✔
3823

3824
    VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
1✔
3825
    VSIFPrintfL(fp, "# some comment\n");
1✔
3826
    VSIFPrintfL(fp, "\n");    // blank line
1✔
3827
    VSIFPrintfL(fp, "  \n");  // blank line
1✔
3828
    VSIFPrintfL(fp, "[configoptions]\n");
1✔
3829
    VSIFPrintfL(fp, "# some comment\n");
1✔
3830
    VSIFPrintfL(fp, "FOO_CONFIGOPTION=BAR\n");
1✔
3831
    VSIFCloseL(fp);
1✔
3832

3833
    // Try CPLLoadConfigOptionsFromFile()
3834
    CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
1✔
3835
    ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
1✔
3836
    CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
1✔
3837

3838
    // Try CPLLoadConfigOptionsFromPredefinedFiles() with GDAL_CONFIG_FILE set
3839
    CPLSetConfigOption("GDAL_CONFIG_FILE", "/vsimem/.gdal/gdalrc");
1✔
3840
    CPLLoadConfigOptionsFromPredefinedFiles();
1✔
3841
    ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
1✔
3842
    CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
1✔
3843

3844
    // Try CPLLoadConfigOptionsFromPredefinedFiles() with $HOME/.gdal/gdalrc
3845
    // file
3846
#ifdef _WIN32
3847
    const char *pszHOMEEnvVarName = "USERPROFILE";
3848
#else
3849
    const char *pszHOMEEnvVarName = "HOME";
1✔
3850
#endif
3851
    CPLString osOldVal(CPLGetConfigOption(pszHOMEEnvVarName, ""));
1✔
3852
    CPLSetConfigOption(pszHOMEEnvVarName, "/vsimem/");
1✔
3853
    CPLLoadConfigOptionsFromPredefinedFiles();
1✔
3854
    ASSERT_TRUE(EQUAL(CPLGetConfigOption("FOO_CONFIGOPTION", ""), "BAR"));
1✔
3855
    CPLSetConfigOption("FOO_CONFIGOPTION", nullptr);
1✔
3856
    if (!osOldVal.empty())
1✔
3857
        CPLSetConfigOption(pszHOMEEnvVarName, osOldVal.c_str());
1✔
3858
    else
3859
        CPLSetConfigOption(pszHOMEEnvVarName, nullptr);
×
3860

3861
    VSIUnlink("/vsimem/.gdal/gdalrc");
1✔
3862
}
3863

3864
// Test decompressor side of cpl_compressor.h
3865
TEST_F(test_cpl, decompressor)
4✔
3866
{
3867
    const auto compressionLambda =
3868
        [](const void * /* input_data */, size_t /* input_size */,
×
3869
           void ** /* output_data */, size_t * /* output_size */,
3870
           CSLConstList /* options */, void * /* compressor_user_data */)
3871
    { return false; };
×
3872
    int dummy = 0;
1✔
3873

3874
    CPLCompressor sComp;
3875
    sComp.nStructVersion = 1;
1✔
3876
    sComp.eType = CCT_COMPRESSOR;
1✔
3877
    sComp.pszId = "my_comp";
1✔
3878
    const char *const apszMetadata[] = {"FOO=BAR", nullptr};
1✔
3879
    sComp.papszMetadata = apszMetadata;
1✔
3880
    sComp.pfnFunc = compressionLambda;
1✔
3881
    sComp.user_data = &dummy;
1✔
3882

3883
    ASSERT_TRUE(CPLRegisterDecompressor(&sComp));
1✔
3884

3885
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3886
    ASSERT_TRUE(!CPLRegisterDecompressor(&sComp));
1✔
3887
    CPLPopErrorHandler();
1✔
3888

3889
    char **decompressors = CPLGetDecompressors();
1✔
3890
    ASSERT_TRUE(decompressors != nullptr);
1✔
3891
    EXPECT_TRUE(CSLFindString(decompressors, sComp.pszId) >= 0);
1✔
3892
    for (auto iter = decompressors; *iter; ++iter)
9✔
3893
    {
3894
        const auto pCompressor = CPLGetDecompressor(*iter);
8✔
3895
        EXPECT_TRUE(pCompressor);
8✔
3896
        if (pCompressor)
8✔
3897
        {
3898
            const char *pszOptions =
3899
                CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
8✔
3900
            if (pszOptions)
8✔
3901
            {
3902
                auto psNode = CPLParseXMLString(pszOptions);
3✔
3903
                EXPECT_TRUE(psNode);
3✔
3904
                CPLDestroyXMLNode(psNode);
3✔
3905
            }
3906
            else
3907
            {
3908
                CPLDebug("TEST", "Decompressor %s has no OPTIONS", *iter);
5✔
3909
            }
3910
        }
3911
    }
3912
    CSLDestroy(decompressors);
1✔
3913

3914
    EXPECT_TRUE(CPLGetDecompressor("invalid") == nullptr);
1✔
3915
    const auto pCompressor = CPLGetDecompressor(sComp.pszId);
1✔
3916
    ASSERT_TRUE(pCompressor);
1✔
3917
    EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
1✔
3918
    EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
1✔
3919
              CSLCount(sComp.papszMetadata));
3920
    EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
1✔
3921
    EXPECT_EQ(pCompressor->user_data, sComp.user_data);
1✔
3922

3923
    CPLDestroyCompressorRegistry();
1✔
3924
    EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
1✔
3925
}
3926

3927
// Test compressor side of cpl_compressor.h
3928
TEST_F(test_cpl, compressor)
4✔
3929
{
3930
    const auto compressionLambda =
3931
        [](const void * /* input_data */, size_t /* input_size */,
×
3932
           void ** /* output_data */, size_t * /* output_size */,
3933
           CSLConstList /* options */, void * /* compressor_user_data */)
3934
    { return false; };
×
3935
    int dummy = 0;
1✔
3936

3937
    CPLCompressor sComp;
3938
    sComp.nStructVersion = 1;
1✔
3939
    sComp.eType = CCT_COMPRESSOR;
1✔
3940
    sComp.pszId = "my_comp";
1✔
3941
    const char *const apszMetadata[] = {"FOO=BAR", nullptr};
1✔
3942
    sComp.papszMetadata = apszMetadata;
1✔
3943
    sComp.pfnFunc = compressionLambda;
1✔
3944
    sComp.user_data = &dummy;
1✔
3945

3946
    ASSERT_TRUE(CPLRegisterCompressor(&sComp));
1✔
3947

3948
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
3949
    ASSERT_TRUE(!CPLRegisterCompressor(&sComp));
1✔
3950
    CPLPopErrorHandler();
1✔
3951

3952
    char **compressors = CPLGetCompressors();
1✔
3953
    ASSERT_TRUE(compressors != nullptr);
1✔
3954
    EXPECT_TRUE(CSLFindString(compressors, sComp.pszId) >= 0);
1✔
3955
    for (auto iter = compressors; *iter; ++iter)
9✔
3956
    {
3957
        const auto pCompressor = CPLGetCompressor(*iter);
8✔
3958
        EXPECT_TRUE(pCompressor);
8✔
3959
        if (pCompressor)
8✔
3960
        {
3961
            const char *pszOptions =
3962
                CSLFetchNameValue(pCompressor->papszMetadata, "OPTIONS");
8✔
3963
            if (pszOptions)
8✔
3964
            {
3965
                auto psNode = CPLParseXMLString(pszOptions);
7✔
3966
                EXPECT_TRUE(psNode);
7✔
3967
                CPLDestroyXMLNode(psNode);
7✔
3968
            }
3969
            else
3970
            {
3971
                CPLDebug("TEST", "Compressor %s has no OPTIONS", *iter);
1✔
3972
            }
3973
        }
3974
    }
3975
    CSLDestroy(compressors);
1✔
3976

3977
    EXPECT_TRUE(CPLGetCompressor("invalid") == nullptr);
1✔
3978
    const auto pCompressor = CPLGetCompressor(sComp.pszId);
1✔
3979
    ASSERT_TRUE(pCompressor);
1✔
3980
    if (pCompressor == nullptr)
1✔
3981
        return;
×
3982
    EXPECT_STREQ(pCompressor->pszId, sComp.pszId);
1✔
3983
    EXPECT_EQ(CSLCount(pCompressor->papszMetadata),
1✔
3984
              CSLCount(sComp.papszMetadata));
3985
    EXPECT_TRUE(pCompressor->pfnFunc != nullptr);
1✔
3986
    EXPECT_EQ(pCompressor->user_data, sComp.user_data);
1✔
3987

3988
    CPLDestroyCompressorRegistry();
1✔
3989
    EXPECT_TRUE(CPLGetDecompressor(sComp.pszId) == nullptr);
1✔
3990
}
3991

3992
// Test builtin compressors/decompressor
3993
TEST_F(test_cpl, builtin_compressors)
4✔
3994
{
3995
    for (const char *id : {"blosc", "zlib", "gzip", "lzma", "zstd", "lz4"})
7✔
3996
    {
3997
        const auto pCompressor = CPLGetCompressor(id);
6✔
3998
        if (pCompressor == nullptr)
6✔
3999
        {
4000
            CPLDebug("TEST", "%s not available", id);
×
4001
            if (strcmp(id, "zlib") == 0 || strcmp(id, "gzip") == 0)
×
4002
            {
4003
                ASSERT_TRUE(false);
×
4004
            }
4005
            continue;
×
4006
        }
4007
        CPLDebug("TEST", "Testing %s", id);
6✔
4008

4009
        const char my_str[] = "my string to compress";
6✔
4010
        const char *const options[] = {"TYPESIZE=1", nullptr};
6✔
4011

4012
        // Compressor side
4013

4014
        // Just get output size
4015
        size_t out_size = 0;
6✔
4016
        ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), nullptr,
6✔
4017
                                         &out_size, options,
4018
                                         pCompressor->user_data));
4019
        ASSERT_TRUE(out_size != 0);
6✔
4020

4021
        // Let it alloc the output buffer
4022
        void *out_buffer2 = nullptr;
6✔
4023
        size_t out_size2 = 0;
6✔
4024
        ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str), &out_buffer2,
6✔
4025
                                         &out_size2, options,
4026
                                         pCompressor->user_data));
4027
        ASSERT_TRUE(out_buffer2 != nullptr);
6✔
4028
        ASSERT_TRUE(out_size2 != 0);
6✔
4029
        ASSERT_TRUE(out_size2 <= out_size);
6✔
4030

4031
        std::vector<GByte> out_buffer3(out_size);
6✔
4032

4033
        // Provide not large enough buffer size
4034
        size_t out_size3 = 1;
6✔
4035
        void *out_buffer3_ptr = &out_buffer3[0];
6✔
4036
        ASSERT_TRUE(!(pCompressor->pfnFunc(my_str, strlen(my_str),
6✔
4037
                                           &out_buffer3_ptr, &out_size3,
4038
                                           options, pCompressor->user_data)));
4039

4040
        // Provide the output buffer
4041
        out_size3 = out_buffer3.size();
6✔
4042
        out_buffer3_ptr = &out_buffer3[0];
6✔
4043
        ASSERT_TRUE(pCompressor->pfnFunc(my_str, strlen(my_str),
6✔
4044
                                         &out_buffer3_ptr, &out_size3, options,
4045
                                         pCompressor->user_data));
4046
        ASSERT_TRUE(out_buffer3_ptr != nullptr);
6✔
4047
        ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
6✔
4048
        ASSERT_TRUE(out_size3 != 0);
6✔
4049
        ASSERT_EQ(out_size3, out_size2);
6✔
4050

4051
        out_buffer3.resize(out_size3);
6✔
4052
        out_buffer3_ptr = &out_buffer3[0];
6✔
4053

4054
        ASSERT_TRUE(memcmp(out_buffer3_ptr, out_buffer2, out_size2) == 0);
6✔
4055

4056
        CPLFree(out_buffer2);
6✔
4057

4058
        const std::vector<GByte> compressedData(out_buffer3);
6✔
4059

4060
        // Decompressor side
4061
        const auto pDecompressor = CPLGetDecompressor(id);
6✔
4062
        ASSERT_TRUE(pDecompressor != nullptr);
6✔
4063

4064
        out_size = 0;
6✔
4065
        ASSERT_TRUE(pDecompressor->pfnFunc(
6✔
4066
            compressedData.data(), compressedData.size(), nullptr, &out_size,
4067
            nullptr, pDecompressor->user_data));
4068
        ASSERT_TRUE(out_size != 0);
6✔
4069
        ASSERT_TRUE(out_size >= strlen(my_str));
6✔
4070

4071
        out_buffer2 = nullptr;
6✔
4072
        out_size2 = 0;
6✔
4073
        ASSERT_TRUE(pDecompressor->pfnFunc(
6✔
4074
            compressedData.data(), compressedData.size(), &out_buffer2,
4075
            &out_size2, options, pDecompressor->user_data));
4076
        ASSERT_TRUE(out_buffer2 != nullptr);
6✔
4077
        ASSERT_TRUE(out_size2 != 0);
6✔
4078
        ASSERT_EQ(out_size2, strlen(my_str));
6✔
4079
        ASSERT_TRUE(memcmp(out_buffer2, my_str, strlen(my_str)) == 0);
6✔
4080
        CPLFree(out_buffer2);
6✔
4081

4082
        out_buffer3.clear();
6✔
4083
        out_buffer3.resize(out_size);
6✔
4084
        out_size3 = out_buffer3.size();
6✔
4085
        out_buffer3_ptr = &out_buffer3[0];
6✔
4086
        ASSERT_TRUE(pDecompressor->pfnFunc(
6✔
4087
            compressedData.data(), compressedData.size(), &out_buffer3_ptr,
4088
            &out_size3, options, pDecompressor->user_data));
4089
        ASSERT_TRUE(out_buffer3_ptr != nullptr);
6✔
4090
        ASSERT_TRUE(out_buffer3_ptr == &out_buffer3[0]);
6✔
4091
        ASSERT_EQ(out_size3, strlen(my_str));
6✔
4092
        ASSERT_TRUE(memcmp(out_buffer3.data(), my_str, strlen(my_str)) == 0);
6✔
4093
    }
4094
}
4095

4096
// Test builtin compressors/decompressor
4097
TEST_F(test_cpl, builtin_compressors_zlib_high_compression_rate)
4✔
4098
{
4099
    const auto pCompressor = CPLGetCompressor("zlib");
1✔
4100
    ASSERT_TRUE(pCompressor != nullptr);
1✔
4101

4102
    std::vector<GByte> abyInput(1024 * 1024, 0x01);
1✔
4103

4104
    // Compressor side
4105

4106
    // Let it alloc the output buffer
4107
    void *out_buffer = nullptr;
1✔
4108
    size_t out_size = 0;
1✔
4109
    ASSERT_TRUE(pCompressor->pfnFunc(abyInput.data(), abyInput.size(),
1✔
4110
                                     &out_buffer, &out_size, nullptr,
4111
                                     pCompressor->user_data));
4112
    ASSERT_TRUE(out_buffer != nullptr);
1✔
4113
    ASSERT_TRUE(out_size != 0);
1✔
4114

4115
    // Decompressor side
4116
    const auto pDecompressor = CPLGetDecompressor("zlib");
1✔
4117
    ASSERT_TRUE(pDecompressor != nullptr);
1✔
4118

4119
    void *out_buffer2 = nullptr;
1✔
4120
    size_t out_size2 = 0;
1✔
4121
    ASSERT_TRUE(pDecompressor->pfnFunc(out_buffer, out_size, &out_buffer2,
1✔
4122
                                       &out_size2, nullptr,
4123
                                       pDecompressor->user_data));
4124
    CPLFree(out_buffer);
1✔
4125

4126
    ASSERT_TRUE(out_buffer2 != nullptr);
1✔
4127
    ASSERT_TRUE(out_size2 != 0);
1✔
4128
    ASSERT_EQ(out_size2, abyInput.size());
1✔
4129
    ASSERT_TRUE(memcmp(out_buffer2, abyInput.data(), abyInput.size()) == 0);
1✔
4130
    CPLFree(out_buffer2);
1✔
4131
}
4132

4133
template <class T> struct TesterDelta
4134
{
4135
    static void test(const char *dtypeOption)
24✔
4136
    {
4137
        const auto pCompressor = CPLGetCompressor("delta");
24✔
4138
        ASSERT_TRUE(pCompressor);
24✔
4139
        if (pCompressor == nullptr)
24✔
4140
            return;
×
4141
        const auto pDecompressor = CPLGetDecompressor("delta");
24✔
4142
        ASSERT_TRUE(pDecompressor);
24✔
4143
        if (pDecompressor == nullptr)
24✔
4144
            return;
×
4145

4146
        const T tabIn[] = {static_cast<T>(-2), 3, 1};
24✔
4147
        T tabCompress[3];
4148
        T tabOut[3];
4149
        const char *const apszOptions[] = {dtypeOption, nullptr};
24✔
4150

4151
        void *outPtr = &tabCompress[0];
24✔
4152
        size_t outSize = sizeof(tabCompress);
24✔
4153
        ASSERT_TRUE(pCompressor->pfnFunc(&tabIn[0], sizeof(tabIn), &outPtr,
24✔
4154
                                         &outSize, apszOptions,
4155
                                         pCompressor->user_data));
4156
        ASSERT_EQ(outSize, sizeof(tabCompress));
24✔
4157

4158
        // ASSERT_EQ(tabCompress[0], 2);
4159
        // ASSERT_EQ(tabCompress[1], 1);
4160
        // ASSERT_EQ(tabCompress[2], -2);
4161

4162
        outPtr = &tabOut[0];
24✔
4163
        outSize = sizeof(tabOut);
24✔
4164
        ASSERT_TRUE(pDecompressor->pfnFunc(&tabCompress[0], sizeof(tabCompress),
24✔
4165
                                           &outPtr, &outSize, apszOptions,
4166
                                           pDecompressor->user_data));
4167
        ASSERT_EQ(outSize, sizeof(tabOut));
24✔
4168
        ASSERT_EQ(tabOut[0], tabIn[0]);
24✔
4169
        ASSERT_EQ(tabOut[1], tabIn[1]);
24✔
4170
        ASSERT_EQ(tabOut[2], tabIn[2]);
24✔
4171
    }
4172
};
4173

4174
// Test delta compressor/decompressor
4175
TEST_F(test_cpl, delta_compressor)
4✔
4176
{
4177
    TesterDelta<int8_t>::test("DTYPE=i1");
1✔
4178

4179
    TesterDelta<uint8_t>::test("DTYPE=u1");
1✔
4180

4181
    TesterDelta<int16_t>::test("DTYPE=i2");
1✔
4182
    TesterDelta<int16_t>::test("DTYPE=<i2");
1✔
4183
    TesterDelta<int16_t>::test("DTYPE=>i2");
1✔
4184

4185
    TesterDelta<uint16_t>::test("DTYPE=u2");
1✔
4186
    TesterDelta<uint16_t>::test("DTYPE=<u2");
1✔
4187
    TesterDelta<uint16_t>::test("DTYPE=>u2");
1✔
4188

4189
    TesterDelta<int32_t>::test("DTYPE=i4");
1✔
4190
    TesterDelta<int32_t>::test("DTYPE=<i4");
1✔
4191
    TesterDelta<int32_t>::test("DTYPE=>i4");
1✔
4192

4193
    TesterDelta<uint32_t>::test("DTYPE=u4");
1✔
4194
    TesterDelta<uint32_t>::test("DTYPE=<u4");
1✔
4195
    TesterDelta<uint32_t>::test("DTYPE=>u4");
1✔
4196

4197
    TesterDelta<int64_t>::test("DTYPE=i8");
1✔
4198
    TesterDelta<int64_t>::test("DTYPE=<i8");
1✔
4199
    TesterDelta<int64_t>::test("DTYPE=>i8");
1✔
4200

4201
    TesterDelta<uint64_t>::test("DTYPE=u8");
1✔
4202
    TesterDelta<uint64_t>::test("DTYPE=<u8");
1✔
4203
    TesterDelta<uint64_t>::test("DTYPE=>u8");
1✔
4204

4205
    TesterDelta<float>::test("DTYPE=f4");
1✔
4206
#ifdef CPL_MSB
4207
    TesterDelta<float>::test("DTYPE=>f4");
4208
#else
4209
    TesterDelta<float>::test("DTYPE=<f4");
1✔
4210
#endif
4211

4212
    TesterDelta<double>::test("DTYPE=f8");
1✔
4213
#ifdef CPL_MSB
4214
    TesterDelta<double>::test("DTYPE=>f8");
4215
#else
4216
    TesterDelta<double>::test("DTYPE=<f8");
1✔
4217
#endif
4218
}
1✔
4219

4220
// Test CPLQuadTree
4221
TEST_F(test_cpl, CPLQuadTree)
4✔
4222
{
4223
    unsigned next = 0;
1✔
4224

4225
    const auto DummyRandInit = [&next](unsigned initValue)
4✔
4226
    { next = initValue; };
5✔
4227

4228
    constexpr int MAX_RAND_VAL = 32767;
1✔
4229

4230
    // Slightly improved version of https://xkcd.com/221/, as suggested by
4231
    // "man srand"
4232
    const auto DummyRand = [&]()
16,000✔
4233
    {
4234
        next = next * 1103515245 + 12345;
16,000✔
4235
        return ((unsigned)(next / 65536) % (MAX_RAND_VAL + 1));
16,000✔
4236
    };
1✔
4237

4238
    CPLRectObj globalbounds;
4239
    globalbounds.minx = 0;
1✔
4240
    globalbounds.miny = 0;
1✔
4241
    globalbounds.maxx = 1;
1✔
4242
    globalbounds.maxy = 1;
1✔
4243

4244
    auto hTree = CPLQuadTreeCreate(&globalbounds, nullptr);
1✔
4245
    ASSERT_TRUE(hTree != nullptr);
1✔
4246

4247
    const auto GenerateRandomRect = [&](CPLRectObj &rect)
4,000✔
4248
    {
4249
        rect.minx = double(DummyRand()) / MAX_RAND_VAL;
4,000✔
4250
        rect.miny = double(DummyRand()) / MAX_RAND_VAL;
4,000✔
4251
        rect.maxx =
4,000✔
4252
            rect.minx + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.minx);
4,000✔
4253
        rect.maxy =
4,000✔
4254
            rect.miny + double(DummyRand()) / MAX_RAND_VAL * (1 - rect.miny);
4,000✔
4255
    };
4,001✔
4256

4257
    for (int j = 0; j < 2; j++)
3✔
4258
    {
4259
        DummyRandInit(j);
2✔
4260
        for (int i = 0; i < 1000; i++)
2,002✔
4261
        {
4262
            CPLRectObj rect;
4263
            GenerateRandomRect(rect);
2,000✔
4264
            void *hFeature =
2,000✔
4265
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
2,000✔
4266
            CPLQuadTreeInsertWithBounds(hTree, hFeature, &rect);
2,000✔
4267
        }
4268

4269
        {
4270
            int nFeatureCount = 0;
2✔
4271
            CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
2✔
4272
            ASSERT_EQ(nFeatureCount, 1000);
2✔
4273
        }
4274

4275
        DummyRandInit(j);
2✔
4276
        for (int i = 0; i < 1000; i++)
2,002✔
4277
        {
4278
            CPLRectObj rect;
4279
            GenerateRandomRect(rect);
2,000✔
4280
            void *hFeature =
2,000✔
4281
                reinterpret_cast<void *>(static_cast<uintptr_t>(i));
2,000✔
4282
            CPLQuadTreeRemove(hTree, hFeature, &rect);
2,000✔
4283
        }
4284

4285
        {
4286
            int nFeatureCount = 0;
2✔
4287
            CPLFree(CPLQuadTreeSearch(hTree, &globalbounds, &nFeatureCount));
2✔
4288
            ASSERT_EQ(nFeatureCount, 0);
2✔
4289
        }
4290
    }
4291

4292
    CPLQuadTreeDestroy(hTree);
1✔
4293
}
4294

4295
// Test bUnlinkAndSize on VSIGetMemFileBuffer
4296
TEST_F(test_cpl, VSIGetMemFileBuffer_unlink_and_size)
4✔
4297
{
4298
    VSILFILE *fp = VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "wb");
1✔
4299
    VSIFWriteL("test", 5, 1, fp);
1✔
4300
    GByte *pRawData =
4301
        VSIGetMemFileBuffer("/vsimem/test_unlink_and_seize.tif", nullptr, true);
1✔
4302
    ASSERT_TRUE(EQUAL(reinterpret_cast<const char *>(pRawData), "test"));
1✔
4303
    ASSERT_TRUE(VSIGetMemFileBuffer("/vsimem/test_unlink_and_seize.tif",
1✔
4304
                                    nullptr, false) == nullptr);
4305
    ASSERT_TRUE(VSIFOpenL("/vsimem/test_unlink_and_seize.tif", "r") == nullptr);
1✔
4306
    ASSERT_TRUE(VSIFReadL(pRawData, 5, 1, fp) == 0);
1✔
4307
    ASSERT_TRUE(VSIFWriteL(pRawData, 5, 1, fp) == 0);
1✔
4308
    ASSERT_TRUE(VSIFSeekL(fp, 0, SEEK_END) == 0);
1✔
4309
    CPLFree(pRawData);
1✔
4310
    VSIFCloseL(fp);
1✔
4311
}
4312

4313
// Test CPLLoadConfigOptionsFromFile() for VSI credentials
4314
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials)
4✔
4315
{
4316
    VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
1✔
4317
    VSIFPrintfL(fp, "[credentials]\n");
1✔
4318
    VSIFPrintfL(fp, "\n");
1✔
4319
    VSIFPrintfL(fp, "[.my_subsection]\n");
1✔
4320
    VSIFPrintfL(fp, "path=/vsi_test/foo/bar\n");
1✔
4321
    VSIFPrintfL(fp, "FOO=BAR\n");
1✔
4322
    VSIFPrintfL(fp, "FOO2=BAR2\n");
1✔
4323
    VSIFPrintfL(fp, "\n");
1✔
4324
    VSIFPrintfL(fp, "[.my_subsection2]\n");
1✔
4325
    VSIFPrintfL(fp, "path=/vsi_test/bar/baz\n");
1✔
4326
    VSIFPrintfL(fp, "BAR=BAZ\n");
1✔
4327
    VSIFPrintfL(fp, "[configoptions]\n");
1✔
4328
    VSIFPrintfL(fp, "configoptions_FOO=BAR\n");
1✔
4329
    VSIFCloseL(fp);
1✔
4330

4331
    CPLErrorReset();
1✔
4332
    CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
1✔
4333
    ASSERT_EQ(CPLGetLastErrorType(), CE_None);
1✔
4334

4335
    {
4336
        const char *pszVal =
4337
            VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO", nullptr);
1✔
4338
        ASSERT_TRUE(pszVal != nullptr);
1✔
4339
        ASSERT_EQ(std::string(pszVal), std::string("BAR"));
2✔
4340
    }
4341

4342
    {
4343
        const char *pszVal =
4344
            VSIGetPathSpecificOption("/vsi_test/foo/bar", "FOO2", nullptr);
1✔
4345
        ASSERT_TRUE(pszVal != nullptr);
1✔
4346
        ASSERT_EQ(std::string(pszVal), std::string("BAR2"));
2✔
4347
    }
4348

4349
    {
4350
        const char *pszVal =
4351
            VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
1✔
4352
        ASSERT_TRUE(pszVal != nullptr);
1✔
4353
        ASSERT_EQ(std::string(pszVal), std::string("BAZ"));
2✔
4354
    }
4355

4356
    {
4357
        const char *pszVal = CPLGetConfigOption("configoptions_FOO", nullptr);
1✔
4358
        ASSERT_TRUE(pszVal != nullptr);
1✔
4359
        ASSERT_EQ(std::string(pszVal), std::string("BAR"));
2✔
4360
    }
4361

4362
    VSIClearPathSpecificOptions("/vsi_test/bar/baz");
1✔
4363
    CPLSetConfigOption("configoptions_FOO", nullptr);
1✔
4364

4365
    {
4366
        const char *pszVal =
4367
            VSIGetPathSpecificOption("/vsi_test/bar/baz", "BAR", nullptr);
1✔
4368
        ASSERT_TRUE(pszVal == nullptr);
1✔
4369
    }
4370

4371
    VSIUnlink("/vsimem/credentials.txt");
1✔
4372
}
4373

4374
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
4375
TEST_F(test_cpl, CPLLoadConfigOptionsFromFile_VSI_credentials_warning)
4✔
4376
{
4377
    VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
1✔
4378
    VSIFPrintfL(fp, "[credentials]\n");
1✔
4379
    VSIFPrintfL(fp, "\n");
1✔
4380
    VSIFPrintfL(fp, "FOO=BAR\n");  // content outside of subsection
1✔
4381
    VSIFCloseL(fp);
1✔
4382

4383
    CPLErrorReset();
1✔
4384
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
4385
    CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
1✔
4386
    CPLPopErrorHandler();
1✔
4387
    ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
1✔
4388

4389
    VSIUnlink("/vsimem/credentials.txt");
1✔
4390
}
4391

4392
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
4393
TEST_F(test_cpl,
4✔
4394
       CPLLoadConfigOptionsFromFile_VSI_credentials_subsection_warning)
4395
{
4396
    VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
1✔
4397
    VSIFPrintfL(fp, "[credentials]\n");
1✔
4398
    VSIFPrintfL(fp, "[.subsection]\n");
1✔
4399
    VSIFPrintfL(fp, "FOO=BAR\n");  // first key is not 'path'
1✔
4400
    VSIFCloseL(fp);
1✔
4401

4402
    CPLErrorReset();
1✔
4403
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
4404
    CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
1✔
4405
    CPLPopErrorHandler();
1✔
4406
    ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
1✔
4407

4408
    VSIUnlink("/vsimem/credentials.txt");
1✔
4409
}
4410

4411
// Test CPLLoadConfigOptionsFromFile() for VSI credentials, warning case
4412
TEST_F(test_cpl,
4✔
4413
       CPLLoadConfigOptionsFromFile_VSI_credentials_warning_path_specific)
4414
{
4415
    VSILFILE *fp = VSIFOpenL("/vsimem/credentials.txt", "wb");
1✔
4416
    VSIFPrintfL(fp, "[credentials]\n");
1✔
4417
    VSIFPrintfL(fp, "[.subsection]\n");
1✔
4418
    VSIFPrintfL(fp, "path=/vsi_test/foo\n");
1✔
4419
    VSIFPrintfL(fp, "path=/vsi_test/bar\n");  // duplicated path
1✔
4420
    VSIFPrintfL(fp, "FOO=BAR\n");             // first key is not 'path'
1✔
4421
    VSIFPrintfL(fp, "[unrelated_section]");
1✔
4422
    VSIFPrintfL(fp, "BAR=BAZ\n");  // first key is not 'path'
1✔
4423
    VSIFCloseL(fp);
1✔
4424

4425
    CPLErrorReset();
1✔
4426
    CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
4427
    CPLLoadConfigOptionsFromFile("/vsimem/credentials.txt", false);
1✔
4428
    CPLPopErrorHandler();
1✔
4429
    ASSERT_EQ(CPLGetLastErrorType(), CE_Warning);
1✔
4430

4431
    {
4432
        const char *pszVal =
4433
            VSIGetPathSpecificOption("/vsi_test/foo", "FOO", nullptr);
1✔
4434
        ASSERT_TRUE(pszVal != nullptr);
1✔
4435
    }
4436

4437
    {
4438
        const char *pszVal =
4439
            VSIGetPathSpecificOption("/vsi_test/foo", "BAR", nullptr);
1✔
4440
        ASSERT_TRUE(pszVal == nullptr);
1✔
4441
    }
4442

4443
    VSIUnlink("/vsimem/credentials.txt");
1✔
4444
}
4445

4446
// Test CPLRecodeFromWCharIconv() with 2 bytes/char source encoding
4447
TEST_F(test_cpl, CPLRecodeFromWCharIconv_2byte_source_encoding)
4✔
4448
{
4449
#ifdef CPL_RECODE_ICONV
4450
    int N = 2048;
1✔
4451
    wchar_t *pszIn =
4452
        static_cast<wchar_t *>(CPLMalloc((N + 1) * sizeof(wchar_t)));
1✔
4453
    for (int i = 0; i < N; i++)
2,049✔
4454
        pszIn[i] = L'A';
2,048✔
4455
    pszIn[N] = L'\0';
1✔
4456
    char *pszExpected = static_cast<char *>(CPLMalloc(N + 1));
1✔
4457
    for (int i = 0; i < N; i++)
2,049✔
4458
        pszExpected[i] = 'A';
2,048✔
4459
    pszExpected[N] = '\0';
1✔
4460
    char *pszRet = CPLRecodeFromWChar(pszIn, CPL_ENC_UTF16, CPL_ENC_UTF8);
1✔
4461
    const bool bOK = memcmp(pszExpected, pszRet, N + 1) == 0;
1✔
4462
    // FIXME Some tests fail on Mac. Not sure why, but do not error out just for
4463
    // that
4464
    if (!bOK &&
1✔
4465
        (strstr(CPLGetConfigOption("TRAVIS_OS_NAME", ""), "osx") != nullptr ||
×
4466
         strstr(CPLGetConfigOption("BUILD_NAME", ""), "osx") != nullptr ||
×
4467
         getenv("DO_NOT_FAIL_ON_RECODE_ERRORS") != nullptr))
×
4468
    {
4469
        fprintf(stderr, "Recode from CPL_ENC_UTF16 to CPL_ENC_UTF8 failed\n");
×
4470
    }
4471
    else
4472
    {
4473
        EXPECT_TRUE(bOK);
1✔
4474
    }
4475
    CPLFree(pszIn);
1✔
4476
    CPLFree(pszRet);
1✔
4477
    CPLFree(pszExpected);
1✔
4478
#else
4479
    GTEST_SKIP() << "iconv support missing";
4480
#endif
4481
}
1✔
4482

4483
// VERY MINIMAL testing of VSI plugin functionality
4484
TEST_F(test_cpl, VSI_plugin_minimal_testing)
4✔
4485
{
4486
    auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
1✔
4487
    psCallbacks->open = [](void *pUserData, const char *pszFilename,
3✔
4488
                           const char *pszAccess) -> void *
4489
    {
4490
        (void)pUserData;
4491
        if (strcmp(pszFilename, "test") == 0 && strcmp(pszAccess, "rb") == 0)
2✔
4492
            return const_cast<char *>("ok");
1✔
4493
        return nullptr;
1✔
4494
    };
1✔
4495
    EXPECT_EQ(VSIInstallPluginHandler("/vsimyplugin/", psCallbacks), 0);
1✔
4496
    VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
1✔
4497
    VSILFILE *fp = VSIFOpenL("/vsimyplugin/test", "rb");
1✔
4498
    EXPECT_TRUE(fp != nullptr);
1✔
4499

4500
    // Check it doesn't crash
4501
    vsi_l_offset nOffset = 5;
1✔
4502
    size_t nSize = 10;
1✔
4503
    reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
1✔
4504

4505
    VSIFCloseL(fp);
1✔
4506
    EXPECT_TRUE(VSIFOpenL("/vsimyplugin/i_dont_exist", "rb") == nullptr);
1✔
4507

4508
    // Check that we can remove the handler
4509
    VSIRemovePluginHandler("/vsimyplugin/");
1✔
4510

4511
    EXPECT_TRUE(VSIFOpenL("/vsimyplugin/test", "rb") == nullptr);
1✔
4512
    EXPECT_TRUE(VSIFOpenL("/vsimyplugin/i_dont_exist", "rb") == nullptr);
1✔
4513

4514
    // Removing a non-existing handler is a no-op
4515
    VSIRemovePluginHandler("/vsimyplugin/");
1✔
4516
    VSIRemovePluginHandler("/vsifoobar/");
1✔
4517
}
1✔
4518

4519
TEST_F(test_cpl, VSI_plugin_advise_read)
4✔
4520
{
4521
    auto psCallbacks = VSIAllocFilesystemPluginCallbacksStruct();
1✔
4522

4523
    struct UserData
4524
    {
4525
        int nRanges = 0;
4526
        const vsi_l_offset *panOffsets = nullptr;
4527
        const size_t *panSizes = nullptr;
4528
    };
4529

4530
    UserData userData;
1✔
4531

4532
    psCallbacks->pUserData = &userData;
1✔
4533
    psCallbacks->open = [](void *pUserData, const char * /*pszFilename*/,
2✔
4534
                           const char * /*pszAccess*/) -> void *
4535
    { return pUserData; };
2✔
4536

4537
    psCallbacks->advise_read = [](void *pFile, int nRanges,
2✔
4538
                                  const vsi_l_offset *panOffsets,
4539
                                  const size_t *panSizes)
4540
    {
4541
        static_cast<UserData *>(pFile)->nRanges = nRanges;
1✔
4542
        static_cast<UserData *>(pFile)->panOffsets = panOffsets;
1✔
4543
        static_cast<UserData *>(pFile)->panSizes = panSizes;
1✔
4544
    };
2✔
4545
    EXPECT_EQ(VSIInstallPluginHandler("/VSI_plugin_advise_read/", psCallbacks),
1✔
4546
              0);
4547
    VSIFreeFilesystemPluginCallbacksStruct(psCallbacks);
1✔
4548
    VSILFILE *fp = VSIFOpenL("/VSI_plugin_advise_read/test", "rb");
1✔
4549
    EXPECT_TRUE(fp != nullptr);
1✔
4550

4551
    vsi_l_offset nOffset = 5;
1✔
4552
    size_t nSize = 10;
1✔
4553
    reinterpret_cast<VSIVirtualHandle *>(fp)->AdviseRead(1, &nOffset, &nSize);
1✔
4554
    EXPECT_EQ(userData.nRanges, 1);
1✔
4555
    EXPECT_EQ(userData.panOffsets, &nOffset);
1✔
4556
    EXPECT_EQ(userData.panSizes, &nSize);
1✔
4557

4558
    VSIFCloseL(fp);
1✔
4559
}
1✔
4560

4561
// Test CPLIsASCII()
4562
TEST_F(test_cpl, CPLIsASCII)
4✔
4563
{
4564
    ASSERT_TRUE(CPLIsASCII("foo", 3));
1✔
4565
    ASSERT_TRUE(CPLIsASCII("foo", static_cast<size_t>(-1)));
1✔
4566
    ASSERT_TRUE(!CPLIsASCII("\xFF", 1));
1✔
4567
}
4568

4569
// Test VSIIsLocal()
4570
TEST_F(test_cpl, VSIIsLocal)
4✔
4571
{
4572
    ASSERT_TRUE(VSIIsLocal("/vsimem/"));
1✔
4573
    ASSERT_TRUE(VSIIsLocal("/vsigzip//vsimem/tmp.gz"));
1✔
4574
#ifdef HAVE_CURL
4575
    ASSERT_TRUE(!VSIIsLocal("/vsicurl/http://example.com"));
1✔
4576
#endif
4577
    VSIStatBufL sStat;
4578
#ifdef _WIN32
4579
    if (VSIStatL("c:\\", &sStat) == 0)
4580
    {
4581
        ASSERT_TRUE(VSIIsLocal("c:\\i_do_not_exist"));
4582
    }
4583
#else
4584
    if (VSIStatL("/tmp", &sStat) == 0)
1✔
4585
    {
4586
        ASSERT_TRUE(VSIIsLocal("/tmp/i_do_not_exist"));
1✔
4587
    }
4588
#endif
4589
}
4590

4591
// Test VSISupportsSequentialWrite()
4592
TEST_F(test_cpl, VSISupportsSequentialWrite)
4✔
4593
{
4594
    ASSERT_TRUE(VSISupportsSequentialWrite("/vsimem/", false));
1✔
4595
#ifdef HAVE_CURL
4596
    ASSERT_TRUE(
1✔
4597
        !VSISupportsSequentialWrite("/vsicurl/http://example.com", false));
4598
    ASSERT_TRUE(VSISupportsSequentialWrite("/vsis3/test_bucket/", false));
1✔
4599
#endif
4600
    ASSERT_TRUE(VSISupportsSequentialWrite("/vsigzip//vsimem/tmp.gz", false));
1✔
4601
#ifdef HAVE_CURL
4602
    ASSERT_TRUE(!VSISupportsSequentialWrite(
1✔
4603
        "/vsigzip//vsicurl/http://example.com/tmp.gz", false));
4604
#endif
4605
    VSIStatBufL sStat;
4606
#ifdef _WIN32
4607
    if (VSIStatL("c:\\", &sStat) == 0)
4608
    {
4609
        ASSERT_TRUE(VSISupportsSequentialWrite("c:\\", false));
4610
    }
4611
#else
4612
    if (VSIStatL("/tmp", &sStat) == 0)
1✔
4613
    {
4614
        ASSERT_TRUE(VSISupportsSequentialWrite("/tmp/i_do_not_exist", false));
1✔
4615
    }
4616
#endif
4617
}
4618

4619
// Test VSISupportsRandomWrite()
4620
TEST_F(test_cpl, VSISupportsRandomWrite)
4✔
4621
{
4622
    ASSERT_TRUE(VSISupportsRandomWrite("/vsimem/", false));
1✔
4623
#ifdef HAVE_CURL
4624
    ASSERT_TRUE(!VSISupportsRandomWrite("/vsicurl/http://example.com", false));
1✔
4625
    ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", false));
1✔
4626
    ASSERT_TRUE(!VSISupportsRandomWrite("/vsis3/test_bucket/", true));
1✔
4627
    CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", "YES");
1✔
4628
    ASSERT_TRUE(VSISupportsRandomWrite("/vsis3/test_bucket/", true));
1✔
4629
    CPLSetConfigOption("CPL_VSIL_USE_TEMP_FILE_FOR_RANDOM_WRITE", nullptr);
1✔
4630
#endif
4631
    ASSERT_TRUE(!VSISupportsRandomWrite("/vsigzip//vsimem/tmp.gz", false));
1✔
4632
#ifdef HAVE_CURL
4633
    ASSERT_TRUE(!VSISupportsRandomWrite(
1✔
4634
        "/vsigzip//vsicurl/http://example.com/tmp.gz", false));
4635
#endif
4636
    VSIStatBufL sStat;
4637
#ifdef _WIN32
4638
    if (VSIStatL("c:\\", &sStat) == 0)
4639
    {
4640
        ASSERT_TRUE(VSISupportsRandomWrite("c:\\", false));
4641
    }
4642
#else
4643
    if (VSIStatL("/tmp", &sStat) == 0)
1✔
4644
    {
4645
        ASSERT_TRUE(VSISupportsRandomWrite("/tmp", false));
1✔
4646
    }
4647
#endif
4648
}
4649

4650
// Test ignore-env-vars = yes of configuration file
4651
TEST_F(test_cpl, config_file_ignore_env_vars)
4✔
4652
{
4653
    char szEnvVar[] = "SOME_ENV_VAR_FOR_TEST_CPL_61=FOO";
1✔
4654
    putenv(szEnvVar);
1✔
4655
    ASSERT_STREQ(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr),
1✔
4656
                 "FOO");
4657

4658
    VSILFILE *fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
1✔
4659
    VSIFPrintfL(fp, "[directives]\n");
1✔
4660
    VSIFPrintfL(fp, "ignore-env-vars=yes\n");
1✔
4661
    VSIFPrintfL(fp, "[configoptions]\n");
1✔
4662
    VSIFPrintfL(fp, "CONFIG_OPTION_FOR_TEST_CPL_61=BAR\n");
1✔
4663
    VSIFCloseL(fp);
1✔
4664

4665
    // Load configuration file
4666
    constexpr bool bOverrideEnvVars = false;
1✔
4667
    CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", bOverrideEnvVars);
1✔
4668

4669
    // Check that reading configuration option works
4670
    ASSERT_STREQ(CPLGetConfigOption("CONFIG_OPTION_FOR_TEST_CPL_61", ""),
1✔
4671
                 "BAR");
4672

4673
    // Check that environment variables are not read as configuration options
4674
    ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) ==
1✔
4675
                nullptr);
4676

4677
    // Reset ignore-env-vars=no
4678
    fp = VSIFOpenL("/vsimem/.gdal/gdalrc", "wb");
1✔
4679
    VSIFPrintfL(fp, "[directives]\n");
1✔
4680
    VSIFPrintfL(fp, "ignore-env-vars=no\n");
1✔
4681
    VSIFPrintfL(fp, "[configoptions]\n");
1✔
4682
    VSIFPrintfL(fp, "SOME_ENV_VAR_FOR_TEST_CPL_61=BAR\n");
1✔
4683
    VSIFCloseL(fp);
1✔
4684

4685
    // Reload configuration file
4686
    CPLLoadConfigOptionsFromFile("/vsimem/.gdal/gdalrc", false);
1✔
4687

4688
    // Check that environment variables override configuration options defined
4689
    // in the file (config file was loaded with bOverrideEnvVars = false)
4690
    ASSERT_TRUE(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", nullptr) !=
1✔
4691
                nullptr);
4692
    ASSERT_STREQ(CPLGetConfigOption("SOME_ENV_VAR_FOR_TEST_CPL_61", ""), "FOO");
1✔
4693

4694
    VSIUnlink("/vsimem/.gdal/gdalrc");
1✔
4695
}
4696

4697
// Test that explicitly defined configuration options override environment variables
4698
// with the same name
4699
TEST_F(test_cpl, test_config_overrides_environment)
4✔
4700
{
4701
    char szEnvVar[] = "TEST_CONFIG_OVERRIDES_ENVIRONMENT=123";
1✔
4702
    putenv(szEnvVar);
1✔
4703

4704
    ASSERT_STREQ(
1✔
4705
        CPLGetConfigOption("TEST_CONFIG_OVERRIDES_ENVIRONMENT", nullptr),
4706
        "123");
4707

4708
    CPLSetConfigOption("TEST_CONFIG_OVERRIDES_ENVIRONMENT", "456");
1✔
4709

4710
    ASSERT_STREQ(
1✔
4711
        CPLGetConfigOption("TEST_CONFIG_OVERRIDES_ENVIRONMENT", nullptr),
4712
        "456");
4713

4714
    CPLSetConfigOption("TEST_CONFIG_OVERRIDES_ENVIRONMENT", nullptr);
1✔
4715

4716
    ASSERT_STREQ(
1✔
4717
        CPLGetConfigOption("TEST_CONFIG_OVERRIDES_ENVIRONMENT", nullptr),
4718
        "123");
4719
}
4720

4721
// Test CPLWorkerThreadPool recursion
4722
TEST_F(test_cpl, CPLWorkerThreadPool_recursion)
4✔
4723
{
4724
    struct Context
4725
    {
4726
        CPLWorkerThreadPool oThreadPool{};
4727
        std::atomic<int> nCounter{0};
4728
        std::mutex mutex{};
4729
        std::condition_variable cv{};
4730
        bool you_can_leave = false;
4731
        int threadStarted = 0;
4732

4733
        void notifyYouCanLeave()
1✔
4734
        {
4735
            std::lock_guard<std::mutex> guard(mutex);
2✔
4736
            you_can_leave = true;
1✔
4737
            cv.notify_one();
1✔
4738
        }
1✔
4739

4740
        void waitYouCanLeave()
1✔
4741
        {
4742
            std::unique_lock<std::mutex> guard(mutex);
2✔
4743
            while (!you_can_leave)
1✔
4744
            {
UNCOV
4745
                cv.wait(guard);
×
4746
            }
4747
        }
1✔
4748
    };
4749

4750
    Context ctxt;
1✔
4751
    ctxt.oThreadPool.Setup(2, nullptr, nullptr, /* waitAllStarted = */ true);
1✔
4752

4753
    struct Data
4754
    {
4755
        Context *psCtxt;
4756
        int iJob;
4757
        GIntBig nThreadLambda = 0;
4758

4759
        Data(Context *psCtxtIn, int iJobIn) : psCtxt(psCtxtIn), iJob(iJobIn)
3✔
4760
        {
4761
        }
3✔
4762

4763
        Data(const Data &) = default;
4764
    };
4765

4766
    const auto lambda = [](void *pData)
3✔
4767
    {
4768
        auto psData = static_cast<Data *>(pData);
3✔
4769
        if (psData->iJob > 0)
3✔
4770
        {
4771
            // wait for both threads to be started
4772
            std::unique_lock<std::mutex> guard(psData->psCtxt->mutex);
4✔
4773
            psData->psCtxt->threadStarted++;
2✔
4774
            psData->psCtxt->cv.notify_one();
2✔
4775
            while (psData->psCtxt->threadStarted < 2)
3✔
4776
            {
4777
                psData->psCtxt->cv.wait(guard);
1✔
4778
            }
4779
        }
4780

4781
        psData->nThreadLambda = CPLGetPID();
3✔
4782
        // fprintf(stderr, "lambda %d: " CPL_FRMT_GIB "\n",
4783
        //         psData->iJob, psData->nThreadLambda);
4784
        const auto lambda2 = [](void *pData2)
9✔
4785
        {
4786
            const auto psData2 = static_cast<Data *>(pData2);
9✔
4787
            const int iJob = psData2->iJob;
9✔
4788
            const int nCounter = psData2->psCtxt->nCounter++;
9✔
4789
            CPL_IGNORE_RET_VAL(nCounter);
9✔
4790
            const auto nThreadLambda2 = CPLGetPID();
9✔
4791
            // fprintf(stderr, "lambda2 job=%d, counter(before)=%d, thread="
4792
            // CPL_FRMT_GIB "\n", iJob, nCounter, nThreadLambda2);
4793
            if (iJob == 100 + 0)
9✔
4794
            {
4795
                ASSERT_TRUE(nThreadLambda2 != psData2->nThreadLambda);
1✔
4796
                // make sure that job 0 run in the other thread
4797
                // takes sufficiently long that job 2 has been submitted
4798
                // before it completes
4799
                psData2->psCtxt->waitYouCanLeave();
1✔
4800
            }
4801
            else if (iJob == 100 + 1 || iJob == 100 + 2)
8✔
4802
            {
4803
                ASSERT_TRUE(nThreadLambda2 == psData2->nThreadLambda);
2✔
4804
            }
4805
        };
4806
        auto poQueue = psData->psCtxt->oThreadPool.CreateJobQueue();
6✔
4807
        Data d0(*psData);
3✔
4808
        d0.iJob = 100 + d0.iJob * 3 + 0;
3✔
4809
        Data d1(*psData);
3✔
4810
        d1.iJob = 100 + d1.iJob * 3 + 1;
3✔
4811
        Data d2(*psData);
3✔
4812
        d2.iJob = 100 + d2.iJob * 3 + 2;
3✔
4813
        poQueue->SubmitJob(lambda2, &d0);
3✔
4814
        poQueue->SubmitJob(lambda2, &d1);
3✔
4815
        poQueue->SubmitJob(lambda2, &d2);
3✔
4816
        if (psData->iJob == 0)
3✔
4817
        {
4818
            psData->psCtxt->notifyYouCanLeave();
1✔
4819
        }
4820
    };
3✔
4821
    {
4822
        auto poQueue = ctxt.oThreadPool.CreateJobQueue();
2✔
4823
        Data data0(&ctxt, 0);
1✔
4824
        poQueue->SubmitJob(lambda, &data0);
1✔
4825
    }
4826
    {
4827
        auto poQueue = ctxt.oThreadPool.CreateJobQueue();
2✔
4828
        Data data1(&ctxt, 1);
1✔
4829
        Data data2(&ctxt, 2);
1✔
4830
        poQueue->SubmitJob(lambda, &data1);
1✔
4831
        poQueue->SubmitJob(lambda, &data2);
1✔
4832
    }
4833
    ASSERT_EQ(ctxt.nCounter, 3 * 3);
1✔
4834
}
4835

4836
// Test /vsimem/ PRead() implementation
4837
TEST_F(test_cpl, vsimem_pread)
4✔
4838
{
4839
    char szContent[] = "abcd";
1✔
4840
    VSILFILE *fp = VSIFileFromMemBuffer(
1✔
4841
        "", reinterpret_cast<GByte *>(szContent), 4, FALSE);
4842
    VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
1✔
4843
    ASSERT_TRUE(poHandle->HasPRead());
1✔
4844
    {
4845
        char szBuffer[5] = {0};
1✔
4846
        ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
1✔
4847
        ASSERT_EQ(std::string(szBuffer), std::string("bc"));
2✔
4848
    }
4849
    {
4850
        char szBuffer[5] = {0};
1✔
4851
        ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
1✔
4852
        ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
2✔
4853
    }
4854
    {
4855
        char szBuffer[5] = {0};
1✔
4856
        ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
1✔
4857
        ASSERT_EQ(std::string(szBuffer), std::string());
2✔
4858
    }
4859
    VSIFCloseL(fp);
1✔
4860
}
4861

4862
// Test regular file system PRead() implementation
4863
TEST_F(test_cpl, file_system_pread)
4✔
4864
{
4865
    VSILFILE *fp = VSIFOpenL("temp_test_64.bin", "wb+");
1✔
4866
    if (fp == nullptr)
1✔
4867
        return;
×
4868
    VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(fp);
1✔
4869
    poHandle->Write("abcd", 4, 1);
1✔
4870
    if (poHandle->HasPRead())
1✔
4871
    {
4872
        poHandle->Flush();
1✔
4873
        {
4874
            char szBuffer[5] = {0};
1✔
4875
            ASSERT_EQ(poHandle->PRead(szBuffer, 2, 1), 2U);
1✔
4876
            ASSERT_EQ(std::string(szBuffer), std::string("bc"));
2✔
4877
        }
4878
        {
4879
            char szBuffer[5] = {0};
1✔
4880
            ASSERT_EQ(poHandle->PRead(szBuffer, 4, 1), 3U);
1✔
4881
            ASSERT_EQ(std::string(szBuffer), std::string("bcd"));
2✔
4882
        }
4883
        {
4884
            char szBuffer[5] = {0};
1✔
4885
            ASSERT_EQ(poHandle->PRead(szBuffer, 1, 4), 0U);
1✔
4886
            ASSERT_EQ(std::string(szBuffer), std::string());
2✔
4887
        }
4888
    }
4889
    VSIFCloseL(fp);
1✔
4890
    VSIUnlink("temp_test_64.bin");
1✔
4891
}
4892

4893
// Test CPLMask implementation
4894
TEST_F(test_cpl, CPLMask)
4✔
4895
{
4896
    constexpr std::size_t sz = 71;
1✔
4897
    auto m = CPLMaskCreate(sz, true);
1✔
4898

4899
    // Mask is set by default
4900
    for (std::size_t i = 0; i < sz; i++)
72✔
4901
    {
4902
        EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
71✔
4903
    }
4904

4905
    VSIFree(m);
1✔
4906
    m = CPLMaskCreate(sz, false);
1✔
4907
    auto m2 = CPLMaskCreate(sz, false);
1✔
4908

4909
    // Mask is unset by default
4910
    for (std::size_t i = 0; i < sz; i++)
72✔
4911
    {
4912
        EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
71✔
4913
    }
4914

4915
    // Set a few bits
4916
    CPLMaskSet(m, 10);
1✔
4917
    CPLMaskSet(m, 33);
1✔
4918
    CPLMaskSet(m, 70);
1✔
4919

4920
    // Check all bits
4921
    for (std::size_t i = 0; i < sz; i++)
72✔
4922
    {
4923
        if (i == 10 || i == 33 || i == 70)
71✔
4924
        {
4925
            EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
3✔
4926
        }
4927
        else
4928
        {
4929
            EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
68✔
4930
        }
4931
    }
4932

4933
    // Unset some bits
4934
    CPLMaskClear(m, 10);
1✔
4935
    CPLMaskClear(m, 70);
1✔
4936

4937
    // Check all bits
4938
    for (std::size_t i = 0; i < sz; i++)
72✔
4939
    {
4940
        if (i == 33)
71✔
4941
        {
4942
            EXPECT_EQ(CPLMaskGet(m, i), true) << "bit " << i;
1✔
4943
        }
4944
        else
4945
        {
4946
            EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
70✔
4947
        }
4948
    }
4949

4950
    CPLMaskSet(m2, 36);
1✔
4951
    CPLMaskMerge(m2, m, sz);
1✔
4952

4953
    // Check all bits
4954
    for (std::size_t i = 0; i < sz; i++)
72✔
4955
    {
4956
        if (i == 36 || i == 33)
71✔
4957
        {
4958
            ASSERT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
4✔
4959
        }
4960
        else
4961
        {
4962
            ASSERT_EQ(CPLMaskGet(m2, i), false) << "bit " << i;
69✔
4963
        }
4964
    }
4965

4966
    CPLMaskClearAll(m, sz);
1✔
4967
    CPLMaskSetAll(m2, sz);
1✔
4968

4969
    // Check all bits
4970
    for (std::size_t i = 0; i < sz; i++)
72✔
4971
    {
4972
        EXPECT_EQ(CPLMaskGet(m, i), false) << "bit " << i;
71✔
4973
        EXPECT_EQ(CPLMaskGet(m2, i), true) << "bit " << i;
71✔
4974
    }
4975

4976
    VSIFree(m);
1✔
4977
    VSIFree(m2);
1✔
4978
}
4979

4980
// Test cpl::ThreadSafeQueue
4981
TEST_F(test_cpl, ThreadSafeQueue)
4✔
4982
{
4983
    cpl::ThreadSafeQueue<int> queue;
1✔
4984
    ASSERT_TRUE(queue.empty());
1✔
4985
    ASSERT_EQ(queue.size(), 0U);
1✔
4986
    queue.push(1);
1✔
4987
    ASSERT_TRUE(!queue.empty());
1✔
4988
    ASSERT_EQ(queue.size(), 1U);
1✔
4989
    queue.clear();
1✔
4990
    ASSERT_TRUE(queue.empty());
1✔
4991
    ASSERT_EQ(queue.size(), 0U);
1✔
4992
    int val = 10;
1✔
4993
    queue.push(std::move(val));
1✔
4994
    ASSERT_TRUE(!queue.empty());
1✔
4995
    ASSERT_EQ(queue.size(), 1U);
1✔
4996
    ASSERT_EQ(queue.get_and_pop_front(), 10);
1✔
4997
    ASSERT_TRUE(queue.empty());
1✔
4998
}
4999

5000
TEST_F(test_cpl, CPLGetExecPath)
4✔
5001
{
5002
    std::vector<char> achBuffer(1024, 'x');
1✔
5003
    if (!CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())))
1✔
5004
    {
5005
        GTEST_SKIP() << "CPLGetExecPath() not implemented for this platform";
×
5006
        return;
5007
    }
5008

5009
    bool bFoundNulTerminatedChar = false;
1✔
5010
    for (char ch : achBuffer)
71✔
5011
    {
5012
        if (ch == '\0')
71✔
5013
        {
5014
            bFoundNulTerminatedChar = true;
1✔
5015
            break;
1✔
5016
        }
5017
    }
5018
    ASSERT_TRUE(bFoundNulTerminatedChar);
1✔
5019

5020
    // Check that the file exists
5021
    VSIStatBufL sStat;
5022
    EXPECT_EQ(VSIStatL(achBuffer.data(), &sStat), 0);
1✔
5023

5024
    const std::string osStrBefore(achBuffer.data());
2✔
5025

5026
    // Resize the buffer to just the minimum size
5027
    achBuffer.resize(strlen(achBuffer.data()) + 1);
1✔
5028
    EXPECT_TRUE(
1✔
5029
        CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())));
5030

5031
    EXPECT_STREQ(osStrBefore.c_str(), achBuffer.data());
1✔
5032

5033
    // Too small buffer
5034
    achBuffer.resize(achBuffer.size() - 1);
1✔
5035
    EXPECT_FALSE(
1✔
5036
        CPLGetExecPath(achBuffer.data(), static_cast<int>(achBuffer.size())));
5037
}
5038

5039
TEST_F(test_cpl, VSIDuplicateFileSystemHandler)
4✔
5040
{
5041
    {
5042
        CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
2✔
5043
        EXPECT_FALSE(VSIDuplicateFileSystemHandler(
1✔
5044
            "/vsi_i_dont_exist/", "/vsi_i_will_not_be_created/"));
5045
    }
5046
    {
5047
        CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
2✔
5048
        EXPECT_FALSE(
1✔
5049
            VSIDuplicateFileSystemHandler("/", "/vsi_i_will_not_be_created/"));
5050
    }
5051
    EXPECT_EQ(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
1✔
5052
              VSIFileManager::GetHandler("/"));
5053
    EXPECT_TRUE(
1✔
5054
        VSIDuplicateFileSystemHandler("/vsimem/", "/vsi_test_clone_vsimem/"));
5055
    EXPECT_NE(VSIFileManager::GetHandler("/vsi_test_clone_vsimem/"),
1✔
5056
              VSIFileManager::GetHandler("/"));
5057
    {
5058
        CPLErrorHandlerPusher oErrorHandler(CPLQuietErrorHandler);
2✔
5059
        EXPECT_FALSE(VSIDuplicateFileSystemHandler("/vsimem/",
1✔
5060
                                                   "/vsi_test_clone_vsimem/"));
5061
    }
5062
}
1✔
5063

5064
TEST_F(test_cpl, CPLAtoGIntBigEx)
4✔
5065
{
5066
    {
5067
        int bOverflow = 0;
1✔
5068
        EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775807", false, &bOverflow),
1✔
5069
                  std::numeric_limits<int64_t>::max());
5070
        EXPECT_EQ(bOverflow, FALSE);
1✔
5071
    }
5072
    {
5073
        int bOverflow = 0;
1✔
5074
        EXPECT_EQ(CPLAtoGIntBigEx("9223372036854775808", false, &bOverflow),
1✔
5075
                  std::numeric_limits<int64_t>::max());
5076
        EXPECT_EQ(bOverflow, TRUE);
1✔
5077
    }
5078
    {
5079
        int bOverflow = 0;
1✔
5080
        EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775808", false, &bOverflow),
1✔
5081
                  std::numeric_limits<int64_t>::min());
5082
        EXPECT_EQ(bOverflow, FALSE);
1✔
5083
    }
5084
    {
5085
        int bOverflow = 0;
1✔
5086
        EXPECT_EQ(CPLAtoGIntBigEx("-9223372036854775809", false, &bOverflow),
1✔
5087
                  std::numeric_limits<int64_t>::min());
5088
        EXPECT_EQ(bOverflow, TRUE);
1✔
5089
    }
5090
}
1✔
5091

5092
TEST_F(test_cpl, CPLSubscribeToSetConfigOption)
4✔
5093
{
5094
    struct Event
5095
    {
5096
        std::string osKey;
5097
        std::string osValue;
5098
        bool bThreadLocal;
5099
    };
5100

5101
    std::vector<Event> events;
2✔
5102
    const auto cbk = +[](const char *pszKey, const char *pszValue,
4✔
5103
                         bool bThreadLocal, void *pUserData)
5104
    {
5105
        std::vector<Event> *pEvents =
3✔
5106
            static_cast<std::vector<Event> *>(pUserData);
5107
        Event ev;
6✔
5108
        ev.osKey = pszKey;
3✔
5109
        ev.osValue = pszValue ? pszValue : "";
3✔
5110
        ev.bThreadLocal = bThreadLocal;
3✔
5111
        pEvents->emplace_back(ev);
3✔
5112
    };
3✔
5113

5114
    // Subscribe and unsubscribe immediately
5115
    {
5116
        int nId = CPLSubscribeToSetConfigOption(cbk, &events);
1✔
5117
        CPLSetConfigOption("CPLSubscribeToSetConfigOption", "bar");
1✔
5118
        EXPECT_EQ(events.size(), 1U);
1✔
5119
        if (!events.empty())
1✔
5120
        {
5121
            EXPECT_STREQ(events[0].osKey.c_str(),
1✔
5122
                         "CPLSubscribeToSetConfigOption");
5123
            EXPECT_STREQ(events[0].osValue.c_str(), "bar");
1✔
5124
            EXPECT_FALSE(events[0].bThreadLocal);
1✔
5125
        }
5126
        CPLUnsubscribeToSetConfigOption(nId);
1✔
5127
    }
5128
    events.clear();
1✔
5129

5130
    // Subscribe and unsubscribe in non-nested order
5131
    {
5132
        int nId1 = CPLSubscribeToSetConfigOption(cbk, &events);
1✔
5133
        int nId2 = CPLSubscribeToSetConfigOption(cbk, &events);
1✔
5134
        CPLUnsubscribeToSetConfigOption(nId1);
1✔
5135
        int nId3 = CPLSubscribeToSetConfigOption(cbk, &events);
1✔
5136

5137
        CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
1✔
5138
        EXPECT_EQ(events.size(), 2U);
1✔
5139

5140
        CPLUnsubscribeToSetConfigOption(nId2);
1✔
5141
        CPLUnsubscribeToSetConfigOption(nId3);
1✔
5142

5143
        CPLSetConfigOption("CPLSubscribeToSetConfigOption", nullptr);
1✔
5144
        EXPECT_EQ(events.size(), 2U);
1✔
5145
    }
5146
}
1✔
5147

5148
TEST_F(test_cpl, VSIGetCanonicalFilename)
4✔
5149
{
5150
    std::string osTmp = CPLGenerateTempFilename(nullptr);
2✔
5151
    if (!CPLIsFilenameRelative(osTmp.c_str()))
1✔
5152
    {
5153
        // Get the canonical filename of the base temporary file
5154
        // to be able to test afterwards just the differences on the case
5155
        // of the extension
5156
        VSILFILE *fp = VSIFOpenL(osTmp.c_str(), "wb");
×
5157
        EXPECT_TRUE(fp != nullptr);
×
5158
        if (fp)
×
5159
        {
5160
            VSIFCloseL(fp);
×
5161
            char *pszRes = VSIGetCanonicalFilename(osTmp.c_str());
×
5162
            osTmp = pszRes;
×
5163
            CPLFree(pszRes);
×
5164
            VSIUnlink(osTmp.c_str());
×
5165
        }
5166
    }
5167

5168
    std::string osLC = osTmp + ".tmp";
2✔
5169
    std::string osUC = osTmp + ".TMP";
2✔
5170
    // Create a file in lower case
5171
    VSILFILE *fp = VSIFOpenL(osLC.c_str(), "wb");
1✔
5172
    EXPECT_TRUE(fp != nullptr);
1✔
5173
    if (fp)
1✔
5174
    {
5175
        VSIFCloseL(fp);
1✔
5176
        VSIStatBufL sStat;
5177
        // And try to stat it in upper case
5178
        if (VSIStatL(osUC.c_str(), &sStat) == 0)
1✔
5179
        {
5180
            char *pszRes = VSIGetCanonicalFilename(osUC.c_str());
×
5181
            EXPECT_TRUE(pszRes);
×
5182
            if (pszRes)
×
5183
            {
5184
#if defined(_WIN32) || (defined(__MACH__) && defined(__APPLE__))
5185
                // On Windows or Mac, we should get the real canonical name,
5186
                // i.e. in lower case
5187
                EXPECT_STREQ(pszRes, osLC.c_str());
5188
#else
5189
                // On other operating systems, VSIGetCanonicalFilename()
5190
                // could not be implemented, so be laxer in the check
5191
                EXPECT_STREQ(CPLString(pszRes).tolower().c_str(),
×
5192
                             CPLString(osLC).tolower().c_str());
5193
#endif
5194
            }
5195
            CPLFree(pszRes);
×
5196
        }
5197

5198
        {
5199
            char *pszRes = VSIGetCanonicalFilename(osLC.c_str());
1✔
5200
            EXPECT_TRUE(pszRes);
1✔
5201
            if (pszRes)
1✔
5202
            {
5203
                EXPECT_STREQ(pszRes, osLC.c_str());
1✔
5204
            }
5205
            CPLFree(pszRes);
1✔
5206
        }
5207
    }
5208
    VSIUnlink(osLC.c_str());
1✔
5209
}
1✔
5210

5211
TEST_F(test_cpl, CPLStrtod)
4✔
5212
{
5213
    {
5214
        const char *pszVal = "5";
1✔
5215
        char *pszEnd = nullptr;
1✔
5216
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 5.0);
1✔
5217
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5218
    }
5219

5220
    {
5221
        const char *pszVal = "5 foo";
1✔
5222
        char *pszEnd = nullptr;
1✔
5223
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 5.0);
1✔
5224
        EXPECT_EQ(pszEnd, pszVal + 1);
1✔
5225
    }
5226

5227
    {
5228
        const char *pszVal = "foo";
1✔
5229
        char *pszEnd = nullptr;
1✔
5230
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd), 0.0);
1✔
5231
        EXPECT_EQ(pszEnd, pszVal);
1✔
5232
    }
5233

5234
    {
5235
        const char *pszVal = "-inf";
1✔
5236
        char *pszEnd = nullptr;
1✔
5237
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5238
                  -std::numeric_limits<double>::infinity());
5239
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5240
    }
5241
    {
5242
        const char *pszVal = "-Inf";
1✔
5243
        char *pszEnd = nullptr;
1✔
5244
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5245
                  -std::numeric_limits<double>::infinity());
5246
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5247
    }
5248
    {
5249
        const char *pszVal = "-INF";
1✔
5250
        char *pszEnd = nullptr;
1✔
5251
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5252
                  -std::numeric_limits<double>::infinity());
5253
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5254
    }
5255
    {
5256
        const char *pszVal = "-Infinity";
1✔
5257
        char *pszEnd = nullptr;
1✔
5258
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5259
                  -std::numeric_limits<double>::infinity());
5260
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5261
    }
5262
    {
5263
        const char *pszVal = "-1.#INF";
1✔
5264
        char *pszEnd = nullptr;
1✔
5265
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5266
                  -std::numeric_limits<double>::infinity());
5267
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5268
    }
5269

5270
    {
5271
        const char *pszVal = "inf";
1✔
5272
        char *pszEnd = nullptr;
1✔
5273
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5274
                  std::numeric_limits<double>::infinity());
5275
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5276
    }
5277
    {
5278
        const char *pszVal = "Inf";
1✔
5279
        char *pszEnd = nullptr;
1✔
5280
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5281
                  std::numeric_limits<double>::infinity());
5282
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5283
    }
5284
    {
5285
        const char *pszVal = "INF";
1✔
5286
        char *pszEnd = nullptr;
1✔
5287
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5288
                  std::numeric_limits<double>::infinity());
5289
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5290
    }
5291
    {
5292
        const char *pszVal = "Infinity";
1✔
5293
        char *pszEnd = nullptr;
1✔
5294
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5295
                  std::numeric_limits<double>::infinity());
5296
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5297
    }
5298
    {
5299
        const char *pszVal = "1.#INF";
1✔
5300
        char *pszEnd = nullptr;
1✔
5301
        EXPECT_EQ(CPLStrtod(pszVal, &pszEnd),
1✔
5302
                  std::numeric_limits<double>::infinity());
5303
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5304
    }
5305

5306
    {
5307
        const char *pszVal = "-1.#QNAN";
1✔
5308
        char *pszEnd = nullptr;
1✔
5309
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5310
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5311
    }
5312
    {
5313
        const char *pszVal = "-1.#IND";
1✔
5314
        char *pszEnd = nullptr;
1✔
5315
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5316
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5317
    }
5318
    {
5319
        const char *pszVal = "1.#QNAN";
1✔
5320
        char *pszEnd = nullptr;
1✔
5321
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5322
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5323
    }
5324
    {
5325
        const char *pszVal = "1.#SNAN";
1✔
5326
        char *pszEnd = nullptr;
1✔
5327
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5328
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5329
    }
5330
    {
5331
        const char *pszVal = "NaN";
1✔
5332
        char *pszEnd = nullptr;
1✔
5333
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5334
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5335
    }
5336
    {
5337
        const char *pszVal = "nan";
1✔
5338
        char *pszEnd = nullptr;
1✔
5339
        EXPECT_TRUE(std::isnan(CPLStrtod(pszVal, &pszEnd)));
1✔
5340
        EXPECT_EQ(pszEnd, pszVal + strlen(pszVal));
1✔
5341
    }
5342
}
1✔
5343

5344
TEST_F(test_cpl, CPLStringToComplex)
4✔
5345
{
5346
    const auto EXPECT_PARSED = [](const char *str, double real, double imag)
5✔
5347
    {
5348
        double r = -999;
5✔
5349
        double i = -999;
5✔
5350
        EXPECT_EQ(CPLStringToComplex(str, &r, &i), CE_None)
5✔
5351
            << "Unexpected error parsing " << str;
×
5352
        EXPECT_EQ(r, real);
5✔
5353
        EXPECT_EQ(i, imag);
5✔
5354
    };
5✔
5355
    const auto EXPECT_ERROR = [](const char *str)
10✔
5356
    {
5357
        CPLErrorStateBackuper state(CPLQuietErrorHandler);
20✔
5358
        double r, i;
5359
        EXPECT_EQ(CPLStringToComplex(str, &r, &i), CE_Failure)
10✔
5360
            << "Did not receive expected error parsing " << str;
×
5361
    };
10✔
5362

5363
    EXPECT_PARSED("05401", 5401, 0);
1✔
5364
    EXPECT_PARSED("-5401", -5401, 0);
1✔
5365
    EXPECT_PARSED(" 611.2-38.4i", 611.2, -38.4);
1✔
5366
    EXPECT_PARSED("611.2+38.4i ", 611.2, 38.4);
1✔
5367
    EXPECT_PARSED("-611.2+38.4i", -611.2, 38.4);
1✔
5368

5369
    EXPECT_ERROR("-611.2+38.4ii");
1✔
5370
    EXPECT_ERROR("-611.2+38.4ji");
1✔
5371
    EXPECT_ERROR("-611.2+38.4ij");
1✔
5372
    EXPECT_ERROR("f-611.2+38.4i");
1✔
5373
    EXPECT_ERROR("-611.2+f38.4i");
1✔
5374
    EXPECT_ERROR("-611.2+-38.4i");
1✔
5375
    EXPECT_ERROR("611.2+38.4");
1✔
5376
    EXPECT_ERROR("38.4i");
1✔
5377
    EXPECT_ERROR("38.4x");
1✔
5378
    EXPECT_ERROR("invalid");
1✔
5379
}
1✔
5380

5381
TEST_F(test_cpl, CPLForceToASCII)
4✔
5382
{
5383
    {
5384
        char *pszOut = CPLForceToASCII("foo", -1, '_');
1✔
5385
        EXPECT_STREQ(pszOut, "foo");
1✔
5386
        CPLFree(pszOut);
1✔
5387
    }
5388
    {
5389
        char *pszOut = CPLForceToASCII("foo", 1, '_');
1✔
5390
        EXPECT_STREQ(pszOut, "f");
1✔
5391
        CPLFree(pszOut);
1✔
5392
    }
5393
    {
5394
        char *pszOut = CPLForceToASCII("foo\xFF", -1, '_');
1✔
5395
        EXPECT_STREQ(pszOut, "foo_");
1✔
5396
        CPLFree(pszOut);
1✔
5397
    }
5398
}
1✔
5399

5400
TEST_F(test_cpl, CPLUTF8ForceToASCII)
4✔
5401
{
5402
    {
5403
        char *pszOut = CPLUTF8ForceToASCII("foo", '_');
1✔
5404
        EXPECT_STREQ(pszOut, "foo");
1✔
5405
        CPLFree(pszOut);
1✔
5406
    }
5407
    {
5408
        // Truncated UTF-8 character
5409
        char *pszOut = CPLUTF8ForceToASCII("foo\xC0", '_');
1✔
5410
        EXPECT_STREQ(pszOut, "foo");
1✔
5411
        CPLFree(pszOut);
1✔
5412
    }
5413
    {
5414
        char *pszOut = CPLUTF8ForceToASCII("foo\xc2\x80", '_');
1✔
5415
        EXPECT_STREQ(pszOut, "foo_");
1✔
5416
        CPLFree(pszOut);
1✔
5417
    }
5418
    {
5419
        char *pszOut = CPLUTF8ForceToASCII("foo\xc2\x80x", '_');
1✔
5420
        EXPECT_STREQ(pszOut, "foo_x");
1✔
5421
        CPLFree(pszOut);
1✔
5422
    }
5423
    {
5424
        std::string s;
1✔
5425
        {
5426
            VSILFILE *f =
5427
                VSIFOpenL((data_ + SEP + "utf8accents.txt").c_str(), "rb");
1✔
5428
            ASSERT_NE(f, nullptr);
1✔
5429
            VSIFSeekL(f, 0, SEEK_END);
1✔
5430
            s.resize(static_cast<size_t>(VSIFTellL(f)));
1✔
5431
            VSIFSeekL(f, 0, SEEK_SET);
1✔
5432
            VSIFReadL(&s[0], 1, s.size(), f);
1✔
5433
            VSIFCloseL(f);
1✔
5434
            while (!s.empty() && s.back() == '\n')
2✔
5435
                s.pop_back();
1✔
5436
        }
5437
        std::string sRef;
1✔
5438
        {
5439
            VSILFILE *f = VSIFOpenL(
1✔
5440
                (data_ + SEP + "utf8accents_ascii.txt").c_str(), "rb");
2✔
5441
            ASSERT_NE(f, nullptr);
1✔
5442
            VSIFSeekL(f, 0, SEEK_END);
1✔
5443
            sRef.resize(static_cast<size_t>(VSIFTellL(f)));
1✔
5444
            VSIFSeekL(f, 0, SEEK_SET);
1✔
5445
            VSIFReadL(&sRef[0], 1, sRef.size(), f);
1✔
5446
            VSIFCloseL(f);
1✔
5447
            while (!sRef.empty() && sRef.back() == '\n')
2✔
5448
                sRef.pop_back();
1✔
5449
        }
5450
        char *pszOut = CPLUTF8ForceToASCII(s.c_str(), '_');
1✔
5451
        EXPECT_STREQ(pszOut, sRef.c_str());
1✔
5452
        CPLFree(pszOut);
1✔
5453
    }
5454
}
5455

5456
#ifndef _WIN32
5457
TEST_F(test_cpl, CPLSpawn)
4✔
5458
{
5459
    VSIStatBufL sStatBuf;
5460
    if (VSIStatL("/bin/true", &sStatBuf) == 0)
1✔
5461
    {
5462
        const char *const apszArgs[] = {"/bin/true", nullptr};
1✔
5463
        EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), 0);
1✔
5464
    }
5465
    if (VSIStatL("/bin/false", &sStatBuf) == 0)
1✔
5466
    {
5467
        const char *const apszArgs[] = {"/bin/false", nullptr};
1✔
5468
        EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), 1);
1✔
5469
    }
5470

5471
    {
5472
        const char *const apszArgs[] = {"/i_do/not/exist", nullptr};
1✔
5473
        CPLPushErrorHandler(CPLQuietErrorHandler);
1✔
5474
        EXPECT_EQ(CPLSpawn(apszArgs, nullptr, nullptr, false), -1);
1✔
5475
        CPLPopErrorHandler();
1✔
5476
    }
5477
}
1✔
5478
#endif
5479

5480
static bool ENDS_WITH(const char *pszStr, const char *pszEnd)
2✔
5481
{
5482
    return strlen(pszStr) >= strlen(pszEnd) &&
4✔
5483
           strcmp(pszStr + strlen(pszStr) - strlen(pszEnd), pszEnd) == 0;
4✔
5484
}
5485

5486
TEST_F(test_cpl, VSIMemGenerateHiddenFilename)
4✔
5487
{
5488
    {
5489
        // Initial cleanup
5490
        VSIRmdirRecursive("/vsimem/");
1✔
5491
        VSIRmdirRecursive("/vsimem/.#!HIDDEN!#.");
1✔
5492

5493
        // Generate unlisted filename
5494
        const std::string osFilename1 = VSIMemGenerateHiddenFilename(nullptr);
2✔
5495
        const char *pszFilename1 = osFilename1.c_str();
1✔
5496
        EXPECT_TRUE(STARTS_WITH(pszFilename1, "/vsimem/.#!HIDDEN!#./"));
1✔
5497
        EXPECT_TRUE(ENDS_WITH(pszFilename1, "/unnamed"));
1✔
5498

5499
        {
5500
            // Check the file doesn't exist yet
5501
            VSIStatBufL sStat;
5502
            EXPECT_EQ(VSIStatL(pszFilename1, &sStat), -1);
1✔
5503
        }
5504

5505
        // Create the file with some content
5506
        GByte abyDummyData[1] = {0};
1✔
5507
        VSIFCloseL(VSIFileFromMemBuffer(pszFilename1, abyDummyData,
1✔
5508
                                        sizeof(abyDummyData), false));
5509

5510
        {
5511
            // Check the file exists now
5512
            VSIStatBufL sStat;
5513
            EXPECT_EQ(VSIStatL(pszFilename1, &sStat), 0);
1✔
5514
        }
5515

5516
        // Gets back content
5517
        EXPECT_EQ(VSIGetMemFileBuffer(pszFilename1, nullptr, false),
1✔
5518
                  abyDummyData);
5519

5520
        {
5521
            // Check the hidden file doesn't popup
5522
            const CPLStringList aosFiles(VSIReadDir("/vsimem/"));
2✔
5523
            EXPECT_EQ(aosFiles.size(), 0);
1✔
5524
        }
5525

5526
        {
5527
            // Check that we can list the below directory if we know it exists
5528
            // and there's just one subdir
5529
            const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
2✔
5530
            EXPECT_EQ(aosFiles.size(), 1);
1✔
5531
        }
5532

5533
        {
5534
            // but that it is not an explicit directory
5535
            VSIStatBufL sStat;
5536
            EXPECT_EQ(VSIStatL("/vsimem/.#!HIDDEN!#.", &sStat), -1);
1✔
5537
        }
5538

5539
        // Creates second file
5540
        const std::string osFilename2 = VSIMemGenerateHiddenFilename(nullptr);
2✔
5541
        const char *pszFilename2 = osFilename2.c_str();
1✔
5542
        EXPECT_TRUE(strcmp(pszFilename1, pszFilename2) != 0);
1✔
5543

5544
        // Create it
5545
        VSIFCloseL(VSIFileFromMemBuffer(pszFilename2, abyDummyData,
1✔
5546
                                        sizeof(abyDummyData), false));
5547

5548
        {
5549
            // Check that we can list the root hidden dir if we know it exists
5550
            const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
2✔
5551
            EXPECT_EQ(aosFiles.size(), 2);
1✔
5552
        }
5553

5554
        {
5555
            // Create an explicit subdirectory in a hidden directory
5556
            const std::string osBaseName =
5557
                VSIMemGenerateHiddenFilename(nullptr);
2✔
5558
            const std::string osSubDir =
5559
                CPLFormFilename(osBaseName.c_str(), "mysubdir", nullptr);
2✔
5560
            EXPECT_EQ(VSIMkdir(osSubDir.c_str(), 0), 0);
1✔
5561

5562
            // Check the subdirectory exists
5563
            {
5564
                VSIStatBufL sStat;
5565
                EXPECT_EQ(VSIStatL(osSubDir.c_str(), &sStat), 0);
1✔
5566
            }
5567

5568
            // but not its hidden parent
5569
            {
5570
                VSIStatBufL sStat;
5571
                EXPECT_EQ(VSIStatL(osBaseName.c_str(), &sStat), -1);
1✔
5572
            }
5573

5574
            // Create file within the subdirectory
5575
            VSIFCloseL(VSIFileFromMemBuffer(
1✔
5576
                CPLFormFilename(osSubDir.c_str(), "my.bin", nullptr),
5577
                abyDummyData, sizeof(abyDummyData), false));
5578

5579
            {
5580
                // Check that we can list the subdirectory
5581
                const CPLStringList aosFiles(VSIReadDir(osSubDir.c_str()));
2✔
5582
                EXPECT_EQ(aosFiles.size(), 1);
1✔
5583
            }
5584

5585
            {
5586
                // Check that we can list the root hidden dir if we know it exists
5587
                const CPLStringList aosFiles(
5588
                    VSIReadDir("/vsimem/.#!HIDDEN!#."));
2✔
5589
                EXPECT_EQ(aosFiles.size(), 3);
1✔
5590
            }
5591
        }
5592

5593
        // Directly create a directory with the return of VSIMemGenerateHiddenFilename()
5594
        {
5595
            const std::string osDirname = VSIMemGenerateHiddenFilename(nullptr);
2✔
5596
            EXPECT_EQ(VSIMkdir(osDirname.c_str(), 0), 0);
1✔
5597

5598
            // Check the subdirectory exists
5599
            {
5600
                VSIStatBufL sStat;
5601
                EXPECT_EQ(VSIStatL(osDirname.c_str(), &sStat), 0);
1✔
5602
            }
5603

5604
            // Create file within the subdirectory
5605
            VSIFCloseL(VSIFileFromMemBuffer(
1✔
5606
                CPLFormFilename(osDirname.c_str(), "my.bin", nullptr),
5607
                abyDummyData, sizeof(abyDummyData), false));
5608

5609
            {
5610
                // Check there's a file in this subdirectory
5611
                const CPLStringList aosFiles(VSIReadDir(osDirname.c_str()));
2✔
5612
                EXPECT_EQ(aosFiles.size(), 1);
1✔
5613
            }
5614

5615
            EXPECT_EQ(VSIRmdirRecursive(osDirname.c_str()), 0);
1✔
5616

5617
            {
5618
                // Check there's no longer any file in this subdirectory
5619
                const CPLStringList aosFiles(VSIReadDir(osDirname.c_str()));
2✔
5620
                EXPECT_EQ(aosFiles.size(), 0);
1✔
5621
            }
5622

5623
            {
5624
                // Check that it no longer exists
5625
                VSIStatBufL sStat;
5626
                EXPECT_EQ(VSIStatL(osDirname.c_str(), &sStat), -1);
1✔
5627
            }
5628
        }
5629

5630
        // Check that operations on "/vsimem/" do not interfere with hidden files
5631
        {
5632
            // Create regular file
5633
            VSIFCloseL(VSIFileFromMemBuffer("/vsimem/regular_file",
1✔
5634
                                            abyDummyData, sizeof(abyDummyData),
5635
                                            false));
5636

5637
            // Check it is visible
5638
            EXPECT_EQ(CPLStringList(VSIReadDir("/vsimem/")).size(), 1);
1✔
5639

5640
            // Clean root /vsimem/
5641
            VSIRmdirRecursive("/vsimem/");
1✔
5642

5643
            // No more user files
5644
            EXPECT_TRUE(CPLStringList(VSIReadDir("/vsimem/")).empty());
1✔
5645

5646
            // But still hidden files
5647
            EXPECT_TRUE(
1✔
5648
                !CPLStringList(VSIReadDir("/vsimem/.#!HIDDEN!#.")).empty());
5649
        }
5650

5651
        // Clean-up hidden files
5652
        EXPECT_EQ(VSIRmdirRecursive("/vsimem/.#!HIDDEN!#."), 0);
1✔
5653

5654
        {
5655
            // Check the root hidden dir is empty
5656
            const CPLStringList aosFiles(VSIReadDir("/vsimem/.#!HIDDEN!#."));
2✔
5657
            EXPECT_TRUE(aosFiles.empty());
1✔
5658
        }
5659

5660
        EXPECT_EQ(VSIRmdirRecursive("/vsimem/.#!HIDDEN!#."), 0);
1✔
5661
    }
5662

5663
    {
5664
        const std::string osFilename = VSIMemGenerateHiddenFilename("foo.bar");
2✔
5665
        const char *pszFilename = osFilename.c_str();
1✔
5666
        EXPECT_TRUE(STARTS_WITH(pszFilename, "/vsimem/.#!HIDDEN!#./"));
1✔
5667
        EXPECT_TRUE(ENDS_WITH(pszFilename, "/foo.bar"));
1✔
5668
    }
5669
}
1✔
5670

5671
TEST_F(test_cpl, VSIGlob)
4✔
5672
{
5673
    GByte abyDummyData[1] = {0};
1✔
5674
    const std::string osFilenameRadix = VSIMemGenerateHiddenFilename("");
1✔
5675
    const std::string osFilename = osFilenameRadix + "trick";
1✔
5676
    VSIFCloseL(VSIFileFromMemBuffer(osFilename.c_str(), abyDummyData,
1✔
5677
                                    sizeof(abyDummyData), false));
5678

5679
    {
5680
        CPLStringList aosRes(
5681
            VSIGlob(osFilename.c_str(), nullptr, nullptr, nullptr));
1✔
5682
        ASSERT_EQ(aosRes.size(), 1);
1✔
5683
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5684
    }
5685

5686
    {
5687
        CPLStringList aosRes(
5688
            VSIGlob(osFilename.substr(0, osFilename.size() - 1).c_str(),
1✔
5689
                    nullptr, nullptr, nullptr));
1✔
5690
        ASSERT_EQ(aosRes.size(), 0);
1✔
5691
    }
5692

5693
    {
5694
        CPLStringList aosRes(
5695
            VSIGlob(std::string(osFilenameRadix).append("?rick").c_str(),
1✔
5696
                    nullptr, nullptr, nullptr));
1✔
5697
        ASSERT_EQ(aosRes.size(), 1);
1✔
5698
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5699
    }
5700

5701
    {
5702
        CPLStringList aosRes(
5703
            VSIGlob(std::string(osFilenameRadix).append("?rack").c_str(),
1✔
5704
                    nullptr, nullptr, nullptr));
1✔
5705
        ASSERT_EQ(aosRes.size(), 0);
1✔
5706
    }
5707

5708
    {
5709
        CPLStringList aosRes(
5710
            VSIGlob(std::string(osFilenameRadix).append("*ick").c_str(),
1✔
5711
                    nullptr, nullptr, nullptr));
1✔
5712
        ASSERT_EQ(aosRes.size(), 1);
1✔
5713
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5714
    }
5715

5716
    {
5717
        CPLStringList aosRes(
5718
            VSIGlob(std::string(osFilenameRadix).append("*").c_str(), nullptr,
1✔
5719
                    nullptr, nullptr));
1✔
5720
        ASSERT_EQ(aosRes.size(), 1);
1✔
5721
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5722
    }
5723

5724
    {
5725
        CPLStringList aosRes(
5726
            VSIGlob(std::string(osFilenameRadix).append("*ack").c_str(),
1✔
5727
                    nullptr, nullptr, nullptr));
1✔
5728
        ASSERT_EQ(aosRes.size(), 0);
1✔
5729
    }
5730

5731
    {
5732
        CPLStringList aosRes(
5733
            VSIGlob(std::string(osFilenameRadix).append("*ic*").c_str(),
1✔
5734
                    nullptr, nullptr, nullptr));
1✔
5735
        ASSERT_EQ(aosRes.size(), 1);
1✔
5736
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5737
    }
5738

5739
    {
5740
        CPLStringList aosRes(
5741
            VSIGlob(std::string(osFilenameRadix).append("*ac*").c_str(),
1✔
5742
                    nullptr, nullptr, nullptr));
1✔
5743
        ASSERT_EQ(aosRes.size(), 0);
1✔
5744
    }
5745

5746
    {
5747
        CPLStringList aosRes(VSIGlob(
5748
            std::string(osFilenameRadix).append("[st][!s]ic[j-l]").c_str(),
1✔
5749
            nullptr, nullptr, nullptr));
1✔
5750
        ASSERT_EQ(aosRes.size(), 1);
1✔
5751
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5752
    }
5753

5754
    {
5755
        CPLStringList aosRes(
5756
            VSIGlob(std::string(osFilenameRadix).append("[!s]rick").c_str(),
1✔
5757
                    nullptr, nullptr, nullptr));
1✔
5758
        ASSERT_EQ(aosRes.size(), 1);
1✔
5759
        EXPECT_STREQ(aosRes[0], osFilename.c_str());
1✔
5760
    }
5761

5762
    {
5763
        CPLStringList aosRes(
5764
            VSIGlob(std::string(osFilenameRadix).append("[").c_str(), nullptr,
1✔
5765
                    nullptr, nullptr));
1✔
5766
        ASSERT_EQ(aosRes.size(), 0);
1✔
5767
    }
5768

5769
    const std::string osFilenameWithSpecialChars = osFilenameRadix + "[!-]";
1✔
5770
    VSIFCloseL(VSIFileFromMemBuffer(osFilenameWithSpecialChars.c_str(),
1✔
5771
                                    abyDummyData, sizeof(abyDummyData), false));
5772

5773
    {
5774
        CPLStringList aosRes(VSIGlob(
5775
            std::string(osFilenameRadix).append("[[][!]a-][-][]]").c_str(),
1✔
5776
            nullptr, nullptr, nullptr));
1✔
5777
        ASSERT_EQ(aosRes.size(), 1);
1✔
5778
        EXPECT_STREQ(aosRes[0], osFilenameWithSpecialChars.c_str());
1✔
5779
    }
5780

5781
    const std::string osFilename2 = osFilenameRadix + "truck/track";
1✔
5782
    VSIFCloseL(VSIFileFromMemBuffer(osFilename2.c_str(), abyDummyData,
1✔
5783
                                    sizeof(abyDummyData), false));
5784

5785
    {
5786
        CPLStringList aosRes(
5787
            VSIGlob(std::string(osFilenameRadix).append("*uc*/track").c_str(),
1✔
5788
                    nullptr, nullptr, nullptr));
1✔
5789
        ASSERT_EQ(aosRes.size(), 1);
1✔
5790
        EXPECT_STREQ(aosRes[0], osFilename2.c_str());
1✔
5791
    }
5792

5793
    {
5794
        CPLStringList aosRes(
5795
            VSIGlob(std::string(osFilenameRadix).append("*uc*/truck").c_str(),
1✔
5796
                    nullptr, nullptr, nullptr));
1✔
5797
        ASSERT_EQ(aosRes.size(), 0);
1✔
5798
    }
5799

5800
    {
5801
        CPLStringList aosRes(
5802
            VSIGlob(std::string(osFilenameRadix).append("**/track").c_str(),
1✔
5803
                    nullptr, nullptr, nullptr));
1✔
5804
        ASSERT_EQ(aosRes.size(), 1);
1✔
5805
        EXPECT_STREQ(aosRes[0], osFilename2.c_str());
1✔
5806
    }
5807

5808
    VSIUnlink(osFilename.c_str());
1✔
5809
    VSIUnlink(osFilenameWithSpecialChars.c_str());
1✔
5810
    VSIUnlink(osFilename2.c_str());
1✔
5811
    VSIUnlink(osFilenameRadix.c_str());
1✔
5812
}
5813

5814
TEST_F(test_cpl, CPLGreatestCommonDivisor)
4✔
5815
{
5816
    CPLErrorStateBackuper state(CPLQuietErrorHandler);
2✔
5817

5818
    // These tests serve to document the current behavior.
5819
    // In some cases the results are dependent on various
5820
    // hardcoded epsilons and it may be appropriate to change
5821
    // the expected results.
5822

5823
    EXPECT_EQ(CPLGreatestCommonDivisor(0.0, 1.0), 0.0);
1✔
5824
    EXPECT_EQ(
1✔
5825
        CPLGreatestCommonDivisor(std::numeric_limits<double>::quiet_NaN(), 1.0),
5826
        0.0);
5827
    EXPECT_EQ(
1✔
5828
        CPLGreatestCommonDivisor(std::numeric_limits<double>::infinity(), 1.0),
5829
        0.0);
5830
    EXPECT_EQ(
1✔
5831
        CPLGreatestCommonDivisor(-std::numeric_limits<double>::infinity(), 1.0),
5832
        0.0);
5833
    EXPECT_EQ(CPLGreatestCommonDivisor(1.0, 0.0), 0.0);
1✔
5834
    EXPECT_EQ(
1✔
5835
        CPLGreatestCommonDivisor(1.0, std::numeric_limits<double>::quiet_NaN()),
5836
        0.0);
5837
    EXPECT_EQ(
1✔
5838
        CPLGreatestCommonDivisor(1.0, std::numeric_limits<double>::infinity()),
5839
        0.0);
5840
    EXPECT_EQ(
1✔
5841
        CPLGreatestCommonDivisor(1.0, -std::numeric_limits<double>::infinity()),
5842
        0.0);
5843

5844
    EXPECT_EQ(CPLGreatestCommonDivisor(std::numeric_limits<double>::min(),
1✔
5845
                                       std::numeric_limits<double>::max()),
5846
              0.0);
5847

5848
    EXPECT_EQ(CPLGreatestCommonDivisor(-2.0, 4.0), -2.0);
1✔
5849
    EXPECT_EQ(CPLGreatestCommonDivisor(-2.0, -4.0), -2.0);
1✔
5850
    EXPECT_EQ(CPLGreatestCommonDivisor(2.0, -4.0), 2.0);
1✔
5851

5852
    EXPECT_EQ(CPLGreatestCommonDivisor(3.0, 5.0), 1.0);
1✔
5853
    EXPECT_EQ(CPLGreatestCommonDivisor(3.0 / 3600, 5.0 / 3600), 1.0 / 3600);
1✔
5854
    EXPECT_EQ(CPLGreatestCommonDivisor(5.0 / 3600, 2.5 / 3600), 2.5 / 3600);
1✔
5855
    EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 10, 1.0), 1.0 / 10);
1✔
5856
    EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 17, 1.0 / 13), 1.0 / 221);
1✔
5857
    EXPECT_EQ(CPLGreatestCommonDivisor(1.0 / 17, 1.0 / 3600), 1.0 / 61200);
1✔
5858

5859
    // GLO-90 resolutoins
5860
    EXPECT_EQ(CPLGreatestCommonDivisor(3.0 / 3600, 4.5 / 3600), 1.5 / 3600);
1✔
5861

5862
    // WorldDEM resolutions
5863
    EXPECT_EQ(CPLGreatestCommonDivisor(0.4 / 3600, 0.6 / 3600), 0.2 / 3600);
1✔
5864
    EXPECT_EQ(CPLGreatestCommonDivisor(0.6 / 3600, 0.8 / 3600), 0.2 / 3600);
1✔
5865

5866
    EXPECT_EQ(CPLGreatestCommonDivisor(M_PI, M_PI / 6), M_PI / 6);
1✔
5867
    EXPECT_EQ(CPLGreatestCommonDivisor(M_PI / 5, M_PI / 6),
1✔
5868
              0);  // Ideally we would get M_PI / 30
5869

5870
    EXPECT_EQ(CPLGreatestCommonDivisor(2.999999, 3.0), 0);
1✔
5871
    EXPECT_EQ(CPLGreatestCommonDivisor(2.9999999, 3.0), 0);
1✔
5872
    EXPECT_EQ(CPLGreatestCommonDivisor(2.99999999, 3.0), 2.99999999);
1✔
5873
}
1✔
5874

5875
TEST_F(test_cpl, CPLLevenshteinDistance)
4✔
5876
{
5877
    EXPECT_EQ(CPLLevenshteinDistance("", "", false), 0);
1✔
5878
    EXPECT_EQ(CPLLevenshteinDistance("a", "a", false), 0);
1✔
5879
    EXPECT_EQ(CPLLevenshteinDistance("a", "b", false), 1);
1✔
5880
    EXPECT_EQ(CPLLevenshteinDistance("a", "", false), 1);
1✔
5881
    EXPECT_EQ(CPLLevenshteinDistance("abc", "ac", false), 1);
1✔
5882
    EXPECT_EQ(CPLLevenshteinDistance("ac", "abc", false), 1);
1✔
5883
    EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0xy1", false), 2);
1✔
5884
    EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0xy1", true), 2);
1✔
5885
    EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0ba1", false), 2);
1✔
5886
    EXPECT_EQ(CPLLevenshteinDistance("0ab1", "0ba1", true), 1);
1✔
5887

5888
    std::string longStr(32768, 'x');
2✔
5889
    EXPECT_EQ(CPLLevenshteinDistance(longStr.c_str(), longStr.c_str(), true),
1✔
5890
              0);
5891
    EXPECT_EQ(CPLLevenshteinDistance(longStr.c_str(), "another_one", true),
1✔
5892
              std::numeric_limits<size_t>::max());
5893
}
1✔
5894

5895
TEST_F(test_cpl, CPLLockFileEx)
4✔
5896
{
5897
    const std::string osLockFilename = CPLGenerateTempFilename(".lock");
1✔
5898

5899
    ASSERT_EQ(CPLLockFileEx(nullptr, nullptr, nullptr), CLFS_API_MISUSE);
1✔
5900

5901
    ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), nullptr, nullptr),
1✔
5902
              CLFS_API_MISUSE);
5903

5904
    CPLLockFileHandle hLockFileHandle = nullptr;
1✔
5905

5906
    ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), &hLockFileHandle, nullptr),
1✔
5907
              CLFS_OK);
5908
    ASSERT_NE(hLockFileHandle, nullptr);
1✔
5909

5910
    // Check the lock file has been created
5911
    VSIStatBufL sStat;
5912
    ASSERT_EQ(VSIStatL(osLockFilename.c_str(), &sStat), 0);
1✔
5913

5914
    {
5915
        CPLStringList aosOptions;
1✔
5916
        aosOptions.SetNameValue("WAIT_TIME", "0.1");
1✔
5917
        CPLLockFileHandle hLockFileHandle2 = nullptr;
1✔
5918
        ASSERT_EQ(CPLLockFileEx(osLockFilename.c_str(), &hLockFileHandle2,
1✔
5919
                                aosOptions.List()),
5920
                  CLFS_LOCK_BUSY);
5921
    }
5922

5923
    CPLUnlockFileEx(hLockFileHandle);
1✔
5924

5925
    // Check the lock file has been deleted
5926
    ASSERT_EQ(VSIStatL(osLockFilename.c_str(), &sStat), -1);
1✔
5927

5928
    CPLUnlockFileEx(nullptr);
1✔
5929
}
5930

5931
TEST_F(test_cpl, CPLFormatReadableFileSize)
4✔
5932
{
5933
    EXPECT_STREQ(CPLFormatReadableFileSize(1.23e18).c_str(), "1.23 HB");
2✔
5934
    EXPECT_STREQ(CPLFormatReadableFileSize(1.23e15).c_str(), "1.23 PB");
2✔
5935
    EXPECT_STREQ(CPLFormatReadableFileSize(1.23e12).c_str(), "1.23 TB");
2✔
5936
    EXPECT_STREQ(CPLFormatReadableFileSize(1.23e9).c_str(), "1.23 GB");
2✔
5937
    EXPECT_STREQ(CPLFormatReadableFileSize(1.23e6).c_str(), "1.23 MB");
2✔
5938
    EXPECT_STREQ(
2✔
5939
        CPLFormatReadableFileSize(static_cast<uint64_t>(123456)).c_str(),
5940
        "123,456 bytes");
5941
}
1✔
5942

5943
TEST_F(test_cpl, CPLStrlenUTF8)
4✔
5944
{
5945
    EXPECT_EQ(CPLStrlenUTF8("a"), 1);
1✔
5946
    EXPECT_EQ(CPLStrlenUTF8("a"
1✔
5947
                            "\xC3\xA9"
5948
                            "b"),
5949
              3);
5950
}
1✔
5951

5952
TEST_F(test_cpl, CPLStrlenUTF8Ex)
4✔
5953
{
5954
    EXPECT_EQ(CPLStrlenUTF8Ex("a"), 1);
1✔
5955
    EXPECT_EQ(CPLStrlenUTF8Ex("a"
1✔
5956
                              "\xC3\xA9"
5957
                              "b"),
5958
              3);
5959
}
1✔
5960

5961
}  // namespace
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc