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