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