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