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