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