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