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