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