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