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