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