1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/fileconf/fileconf.cpp
3 // Purpose: wxFileConf unit test
4 // Author: Vadim Zeitlin
5 // Created: 2004-09-19
6 // Copyright: (c) 2004 Vadim Zeitlin
7 ///////////////////////////////////////////////////////////////////////////////
8
9 // ----------------------------------------------------------------------------
10 // headers
11 // ----------------------------------------------------------------------------
12
13 #include "testprec.h"
14
15
16 #if wxUSE_FILECONFIG
17
18 #ifndef WX_PRECOMP
19 #endif // WX_PRECOMP
20
21 #include "wx/fileconf.h"
22 #include "wx/sstream.h"
23 #include "wx/log.h"
24
25 static const char *testconfig =
26 "[root]\n"
27 "entry=value\n"
28 "[root/group1]\n"
29 "[root/group1/subgroup]\n"
30 "subentry=subvalue\n"
31 "subentry2=subvalue2\n"
32 "[root/group2]\n"
33 ;
34
35 // ----------------------------------------------------------------------------
36 // local functions
37 // ----------------------------------------------------------------------------
38
Dump(wxFileConfig & fc)39 static wxString Dump(wxFileConfig& fc)
40 {
41 wxStringOutputStream sos;
42 fc.Save(sos);
43 return wxTextFile::Translate(sos.GetString(), wxTextFileType_Unix);
44 }
45
46 // helper macro to test wxFileConfig contents
47 #define wxVERIFY_FILECONFIG(t, fc) CHECK(Dump(fc) == t)
48
ChangePath(wxFileConfig & fc,const char * path)49 static wxString ChangePath(wxFileConfig& fc, const char *path)
50 {
51 fc.SetPath(path);
52
53 return fc.GetPath();
54 }
55
56 // ----------------------------------------------------------------------------
57 // test class
58 // ----------------------------------------------------------------------------
59
60 TEST_CASE("wxFileConfig::Path", "[fileconfig][config]")
61 {
62 wxStringInputStream sis(testconfig);
63 wxFileConfig fc(sis);
64
65 CHECK( ChangePath(fc, "") == "" );
66 CHECK( ChangePath(fc, "/") == "" );
67 CHECK( ChangePath(fc, "root") == "/root" );
68 CHECK( ChangePath(fc, "/root") == "/root" );
69 CHECK( ChangePath(fc, "/root/group1/subgroup") == "/root/group1/subgroup" );
70 CHECK( ChangePath(fc, "/root/group2") == "/root/group2" );
71 }
72
73 TEST_CASE("wxFileConfig::AddEntries", "[fileconfig][config]")
74 {
75 wxFileConfig fc;
76
77 wxVERIFY_FILECONFIG( "", fc );
78
79 fc.Write("/Foo", "foo");
80 wxVERIFY_FILECONFIG( "Foo=foo\n", fc );
81
82 fc.Write("/Bar/Baz", "baz");
83 wxVERIFY_FILECONFIG( "Foo=foo\n[Bar]\nBaz=baz\n", fc );
84
85 fc.DeleteAll();
86 wxVERIFY_FILECONFIG( "", fc );
87
88 fc.Write("/Bar/Baz", "baz");
89 wxVERIFY_FILECONFIG( "[Bar]\nBaz=baz\n", fc );
90
91 fc.Write("/Foo", "foo");
92 wxVERIFY_FILECONFIG( "Foo=foo\n[Bar]\nBaz=baz\n", fc );
93 }
94
95 namespace
96 {
97
98 void
CheckGroupEntries(const wxFileConfig & fc,const char * path,size_t nEntries,...)99 CheckGroupEntries(const wxFileConfig& fc,
100 const char *path,
101 size_t nEntries,
102 ...)
103 {
104 wxConfigPathChanger change(&fc, wxString(path) + "/");
105
106 CHECK( fc.GetNumberOfEntries() == nEntries );
107
108 va_list ap;
109 va_start(ap, nEntries);
110
111 long cookie;
112 wxString name;
113 for ( bool cont = fc.GetFirstEntry(name, cookie);
114 cont;
115 cont = fc.GetNextEntry(name, cookie), nEntries-- )
116 {
117 CHECK( name == va_arg(ap, char *) );
118 }
119
120 CHECK( nEntries == 0 );
121
122 va_end(ap);
123 }
124
125 void
CheckGroupSubgroups(const wxFileConfig & fc,const char * path,size_t nGroups,...)126 CheckGroupSubgroups(const wxFileConfig& fc,
127 const char *path,
128 size_t nGroups,
129 ...)
130 {
131 wxConfigPathChanger change(&fc, wxString(path) + "/");
132
133 CHECK( fc.GetNumberOfGroups() == nGroups );
134
135 va_list ap;
136 va_start(ap, nGroups);
137
138 long cookie;
139 wxString name;
140 for ( bool cont = fc.GetFirstGroup(name, cookie);
141 cont;
142 cont = fc.GetNextGroup(name, cookie), nGroups-- )
143 {
144 CHECK( name == va_arg(ap, char *) );
145 }
146
147 CHECK( nGroups == 0 );
148
149 va_end(ap);
150 }
151
152 } // anonymous namespace
153
154 TEST_CASE("wxFileConfig::GetEntries", "[fileconfig][config]")
155 {
156 wxStringInputStream sis(testconfig);
157 wxFileConfig fc(sis);
158
159 CheckGroupEntries(fc, "", 0);
160 CheckGroupEntries(fc, "/root", 1, "entry");
161 CheckGroupEntries(fc, "/root/group1", 0);
162 CheckGroupEntries(fc, "/root/group1/subgroup",
163 2, "subentry", "subentry2");
164 }
165
166 TEST_CASE("wxFileConfig::GetGroups", "[fileconfig][config]")
167 {
168 wxStringInputStream sis(testconfig);
169 wxFileConfig fc(sis);
170
171 CheckGroupSubgroups(fc, "", 1, "root");
172 CheckGroupSubgroups(fc, "/root", 2, "group1", "group2");
173 CheckGroupSubgroups(fc, "/root/group1", 1, "subgroup");
174 CheckGroupSubgroups(fc, "/root/group2", 0);
175 }
176
177 TEST_CASE("wxFileConfig::HasEntry", "[fileconfig][config]")
178 {
179 wxStringInputStream sis(testconfig);
180 wxFileConfig fc(sis);
181
182 CHECK( !fc.HasEntry("root") );
183 CHECK( fc.HasEntry("root/entry") );
184 CHECK( fc.HasEntry("/root/entry") );
185 CHECK( fc.HasEntry("root/group1/subgroup/subentry") );
186 CHECK( !fc.HasEntry("") );
187 CHECK( !fc.HasEntry("root/group1") );
188 CHECK( !fc.HasEntry("subgroup/subentry") );
189 CHECK( !fc.HasEntry("/root/no_such_group/entry") );
190 CHECK( !fc.HasGroup("/root/no_such_group") );
191 }
192
193 TEST_CASE("wxFileConfig::HasGroup", "[fileconfig][config]")
194 {
195 wxStringInputStream sis(testconfig);
196 wxFileConfig fc(sis);
197
198 CHECK( fc.HasGroup("root") );
199 CHECK( fc.HasGroup("root/group1") );
200 CHECK( fc.HasGroup("root/group1/subgroup") );
201 CHECK( fc.HasGroup("root/group2") );
202 CHECK( !fc.HasGroup("") );
203 CHECK( !fc.HasGroup("root/group") );
204 CHECK( !fc.HasGroup("root//subgroup") );
205 CHECK( !fc.HasGroup("foot/subgroup") );
206 CHECK( !fc.HasGroup("foot") );
207 }
208
209 TEST_CASE("wxFileConfig::Binary", "[fileconfig][config]")
210 {
211 wxStringInputStream sis(
212 "[root]\n"
213 "binary=Zm9vCg==\n"
214 );
215 wxFileConfig fc(sis);
216
217 wxMemoryBuffer buf;
218 fc.Read("/root/binary", &buf);
219
220 CHECK( memcmp("foo\n", buf.GetData(), buf.GetDataLen()) == 0 );
221
222 buf.SetDataLen(0);
223 buf.AppendData("\0\1\2", 3);
224 fc.Write("/root/012", buf);
225 wxVERIFY_FILECONFIG(
226 "[root]\n"
227 "binary=Zm9vCg==\n"
228 "012=AAEC\n",
229 fc
230 );
231 }
232
233 TEST_CASE("wxFileConfig::Save", "[fileconfig][config]")
234 {
235 wxStringInputStream sis(testconfig);
236 wxFileConfig fc(sis);
237 wxVERIFY_FILECONFIG( testconfig, fc );
238 }
239
240 TEST_CASE("wxFileConfig::DeleteEntry", "[fileconfig][config]")
241 {
242 wxStringInputStream sis(testconfig);
243 wxFileConfig fc(sis);
244
245 CHECK( !fc.DeleteEntry("foo") );
246
247 CHECK( fc.DeleteEntry("root/group1/subgroup/subentry") );
248 wxVERIFY_FILECONFIG( "[root]\n"
249 "entry=value\n"
250 "[root/group1]\n"
251 "[root/group1/subgroup]\n"
252 "subentry2=subvalue2\n"
253 "[root/group2]\n",
254 fc );
255
256 // group should be deleted now as well as it became empty
257 wxConfigPathChanger change(&fc, "root/group1/subgroup/subentry2");
258 CHECK( fc.DeleteEntry("subentry2") );
259 wxVERIFY_FILECONFIG( "[root]\n"
260 "entry=value\n"
261 "[root/group1]\n"
262 "[root/group2]\n",
263 fc );
264 }
265
266 TEST_CASE("wxFileConfig::DeleteAndWriteEntry", "[fileconfig][config]")
267 {
268 wxStringInputStream sis(
269 "[root/group1]\n"
270 "subentry=subvalue\n"
271 "subentry2=subvalue2\n"
272 "subentry3=subvalue3\n"
273 );
274
275 wxFileConfig fc(sis);
276
277 fc.DeleteEntry("/root/group1/subentry2");
278 fc.Write("/root/group1/subentry2", "testvalue");
279 fc.DeleteEntry("/root/group2/subentry2");
280 fc.Write("/root/group2/subentry2", "testvalue2");
281 fc.DeleteEntry("/root/group1/subentry2");
282 fc.Write("/root/group1/subentry2", "testvalue");
283 fc.DeleteEntry("/root/group2/subentry2");
284 fc.Write("/root/group2/subentry2", "testvalue2");
285
286 wxVERIFY_FILECONFIG( "[root/group1]\n"
287 "subentry=subvalue\n"
288 "subentry3=subvalue3\n"
289 "subentry2=testvalue\n"
290 "[root/group2]\n"
291 "subentry2=testvalue2\n",
292 fc );
293
294 fc.DeleteEntry("/root/group2/subentry2");
295 wxVERIFY_FILECONFIG( "[root/group1]\n"
296 "subentry=subvalue\n"
297 "subentry3=subvalue3\n"
298 "subentry2=testvalue\n",
299 fc );
300
301 fc.DeleteEntry("/root/group1/subentry2");
302 fc.DeleteEntry("/root/group1/subentry");
303 fc.DeleteEntry("/root/group1/subentry3");
304 wxVERIFY_FILECONFIG( "", fc );
305 }
306
307 TEST_CASE("wxFileConfig::DeleteLastRootEntry", "[fileconfig][config]")
308 {
309 // This tests for the bug which occurred when the last entry of the root
310 // group was deleted: this corrupted internal state and resulted in a crash
311 // after trying to write the just deleted entry again.
312 wxStringInputStream sis("");
313 wxFileConfig fc(sis);
314
315 fc.Write("key", "value");
316 wxVERIFY_FILECONFIG( "key=value\n", fc );
317
318 fc.DeleteEntry("key");
319 wxVERIFY_FILECONFIG( "", fc );
320
321 fc.Write("key", "value");
322 wxVERIFY_FILECONFIG( "key=value\n", fc );
323 }
324
325 TEST_CASE("wxFileConfig::DeleteGroup", "[fileconfig][config]")
326 {
327 wxStringInputStream sis(testconfig);
328 wxFileConfig fc(sis);
329
330 CHECK( !fc.DeleteGroup("foo") );
331
332 CHECK( fc.DeleteGroup("root/group1") );
333 wxVERIFY_FILECONFIG( "[root]\n"
334 "entry=value\n"
335 "[root/group2]\n",
336 fc );
337
338 // notice trailing slash: it should be ignored
339 CHECK( fc.DeleteGroup("root/group2/") );
340 wxVERIFY_FILECONFIG( "[root]\n"
341 "entry=value\n",
342 fc );
343
344 CHECK( fc.DeleteGroup("root") );
345 CHECK( Dump(fc).empty() );
346 }
347
348 TEST_CASE("wxFileConfig::DeleteAll", "[fileconfig][config]")
349 {
350 wxStringInputStream sis(testconfig);
351 wxFileConfig fc(sis);
352
353 CHECK( fc.DeleteAll() );
354 CHECK( Dump(fc).empty() );
355 }
356
357 TEST_CASE("wxFileConfig::RenameEntry", "[fileconfig][config]")
358 {
359 wxStringInputStream sis(testconfig);
360 wxFileConfig fc(sis);
361
362 fc.SetPath("root");
363 CHECK( fc.RenameEntry("entry", "newname") );
364 wxVERIFY_FILECONFIG( "[root]\n"
365 "newname=value\n"
366 "[root/group1]\n"
367 "[root/group1/subgroup]\n"
368 "subentry=subvalue\n"
369 "subentry2=subvalue2\n"
370 "[root/group2]\n",
371 fc );
372
373 fc.SetPath("group1/subgroup");
374 CHECK( !fc.RenameEntry("entry", "newname") );
375 CHECK( !fc.RenameEntry("subentry", "subentry2") );
376
377 CHECK( fc.RenameEntry("subentry", "subentry1") );
378 wxVERIFY_FILECONFIG( "[root]\n"
379 "newname=value\n"
380 "[root/group1]\n"
381 "[root/group1/subgroup]\n"
382 "subentry2=subvalue2\n"
383 "subentry1=subvalue\n"
384 "[root/group2]\n",
385 fc );
386 }
387
388 TEST_CASE("wxFileConfig::RenameGroup", "[fileconfig][config]")
389 {
390 wxStringInputStream sis(testconfig);
391 wxFileConfig fc(sis);
392
393 CHECK( fc.RenameGroup("root", "foot") );
394 wxVERIFY_FILECONFIG( "[foot]\n"
395 "entry=value\n"
396 "[foot/group1]\n"
397 "[foot/group1/subgroup]\n"
398 "subentry=subvalue\n"
399 "subentry2=subvalue2\n"
400 "[foot/group2]\n",
401 fc );
402
403 // renaming a path doesn't work, it must be the immediate group
404 CHECK( !fc.RenameGroup("foot/group1", "group2") );
405
406
407 fc.SetPath("foot");
408
409 // renaming to a name of existing group doesn't work
410 CHECK( !fc.RenameGroup("group1", "group2") );
411
412 // try exchanging the groups names and then restore them back
413 CHECK( fc.RenameGroup("group1", "groupTmp") );
414 wxVERIFY_FILECONFIG( "[foot]\n"
415 "entry=value\n"
416 "[foot/groupTmp]\n"
417 "[foot/groupTmp/subgroup]\n"
418 "subentry=subvalue\n"
419 "subentry2=subvalue2\n"
420 "[foot/group2]\n",
421 fc );
422
423 CHECK( fc.RenameGroup("group2", "group1") );
424 wxVERIFY_FILECONFIG( "[foot]\n"
425 "entry=value\n"
426 "[foot/groupTmp]\n"
427 "[foot/groupTmp/subgroup]\n"
428 "subentry=subvalue\n"
429 "subentry2=subvalue2\n"
430 "[foot/group1]\n",
431 fc );
432
433 CHECK( fc.RenameGroup("groupTmp", "group2") );
434 wxVERIFY_FILECONFIG( "[foot]\n"
435 "entry=value\n"
436 "[foot/group2]\n"
437 "[foot/group2/subgroup]\n"
438 "subentry=subvalue\n"
439 "subentry2=subvalue2\n"
440 "[foot/group1]\n",
441 fc );
442
443 CHECK( fc.RenameGroup("group1", "groupTmp") );
444 wxVERIFY_FILECONFIG( "[foot]\n"
445 "entry=value\n"
446 "[foot/group2]\n"
447 "[foot/group2/subgroup]\n"
448 "subentry=subvalue\n"
449 "subentry2=subvalue2\n"
450 "[foot/groupTmp]\n",
451 fc );
452
453 CHECK( fc.RenameGroup("group2", "group1") );
454 wxVERIFY_FILECONFIG( "[foot]\n"
455 "entry=value\n"
456 "[foot/group1]\n"
457 "[foot/group1/subgroup]\n"
458 "subentry=subvalue\n"
459 "subentry2=subvalue2\n"
460 "[foot/groupTmp]\n",
461 fc );
462
463 CHECK( fc.RenameGroup("groupTmp", "group2") );
464 wxVERIFY_FILECONFIG( "[foot]\n"
465 "entry=value\n"
466 "[foot/group1]\n"
467 "[foot/group1/subgroup]\n"
468 "subentry=subvalue\n"
469 "subentry2=subvalue2\n"
470 "[foot/group2]\n",
471 fc );
472 }
473
474 TEST_CASE("wxFileConfig::CreateSubgroupAndEntries", "[fileconfig][config]")
475 {
476 wxFileConfig fc;
477 fc.Write("sub/sub_first", "sub_one");
478 fc.Write("first", "one");
479
480 wxVERIFY_FILECONFIG( "first=one\n"
481 "[sub]\n"
482 "sub_first=sub_one\n",
483 fc );
484 }
485
486 TEST_CASE("wxFileConfig::CreateEntriesAndSubgroup", "[fileconfig][config]")
487 {
488 wxFileConfig fc;
489 fc.Write("first", "one");
490 fc.Write("second", "two");
491 fc.Write("sub/sub_first", "sub_one");
492
493 wxVERIFY_FILECONFIG( "first=one\n"
494 "second=two\n"
495 "[sub]\n"
496 "sub_first=sub_one\n",
497 fc );
498 }
499
EmptyConfigAndWriteKey()500 static void EmptyConfigAndWriteKey()
501 {
502 wxFileConfig fc("deleteconftest");
503
504 const wxString groupPath = "/root";
505
506 if ( fc.Exists(groupPath) )
507 {
508 // using DeleteGroup exposes the problem, using DeleteAll doesn't
509 CHECK( fc.DeleteGroup(groupPath) );
510 }
511
512 // the config must be empty for the problem to arise
513 CHECK( !fc.GetNumberOfEntries(true) );
514 CHECK( !fc.GetNumberOfGroups(true) );
515
516
517 // this crashes on second call of this function
518 CHECK( fc.Write(groupPath + "/entry", "value") );
519 }
520
521 TEST_CASE("wxFileConfig::DeleteLastGroup", "[fileconfig][config]")
522 {
523 /*
524 We make 2 of the same calls, first to create a file config with a single
525 group and key...
526 */
527 ::EmptyConfigAndWriteKey();
528
529 /*
530 ... then the same but this time the key's group is deleted before the
531 key is written again. This causes a crash.
532 */
533 ::EmptyConfigAndWriteKey();
534
535
536 // clean up
537 wxLogNull noLogging;
538 (void) ::wxRemoveFile(wxFileConfig::GetLocalFileName("deleteconftest"));
539 }
540
541 TEST_CASE("wxFileConfig::DeleteAndRecreateGroup", "[fileconfig][config]")
542 {
543 static const char *confInitial =
544 "[First]\n"
545 "Value1=Foo\n"
546 "[Second]\n"
547 "Value2=Bar\n";
548
549 wxStringInputStream sis(confInitial);
550 wxFileConfig fc(sis);
551
552 fc.DeleteGroup("Second");
553 wxVERIFY_FILECONFIG( "[First]\n"
554 "Value1=Foo\n",
555 fc );
556
557 fc.Write("Second/Value2", "New");
558 wxVERIFY_FILECONFIG( "[First]\n"
559 "Value1=Foo\n"
560 "[Second]\n"
561 "Value2=New\n",
562 fc );
563 }
564
565 TEST_CASE("wxFileConfig::AddToExistingRoot", "[fileconfig][config]")
566 {
567 static const char *confInitial =
568 "[Group]\n"
569 "value1=foo\n";
570
571 wxStringInputStream sis(confInitial);
572 wxFileConfig fc(sis);
573
574 fc.Write("/value1", "bar");
575 wxVERIFY_FILECONFIG(
576 "value1=bar\n"
577 "[Group]\n"
578 "value1=foo\n",
579 fc
580 );
581 }
582
583 TEST_CASE("wxFileConfig::ReadNonExistent", "[fileconfig][config]")
584 {
585 static const char *confTest =
586 "community=censored\n"
587 "[City1]\n"
588 "URL=www.fake1.na\n"
589 "[City1/A1]\n"
590 "[City1/A1/1]\n"
591 "IP=192.168.1.66\n"
592 "URL=www.fake2.na\n"
593 ;
594
595 wxStringInputStream sis(confTest);
596 wxFileConfig fc(sis);
597
598 wxString url;
599 CHECK( !fc.Read("URL", &url) );
600 }
601
602 TEST_CASE("wxFileConfig::ReadEmpty", "[fileconfig][config]")
603 {
604 static const char *confTest = "";
605
606 wxStringInputStream sis(confTest);
607 wxFileConfig fc(sis);
608 }
609
610 TEST_CASE("wxFileConfig::ReadFloat", "[fileconfig][config]")
611 {
612 static const char *confTest =
613 "x=1.234\n"
614 "y=-9876.5432\n"
615 "z=2e+308\n"
616 ;
617
618 wxStringInputStream sis(confTest);
619 wxFileConfig fc(sis);
620
621 float f;
622 CHECK( fc.Read("x", &f) );
623 CHECK( f == 1.234f );
624
625 CHECK( fc.Read("y", &f) );
626 CHECK( f == -9876.5432f );
627 }
628
629 TEST_CASE("wxFileConfig::LongLong", "[fileconfig][config][longlong]")
630 {
631 wxFileConfig fc("", "", "", "", 0); // Don't use any files.
632
633 // See comment near val64 definition in regconf.cpp.
634 const wxLongLong_t val = wxLL(0x8000000000000008);
635 REQUIRE( fc.Write("ll", val) );
636
637 wxLongLong_t ll;
638 REQUIRE( fc.Read("ll", &ll) );
639 CHECK( ll == val );
640 }
641
642 #endif // wxUSE_FILECONFIG
643
644