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