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