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: fileconftest.cpp 44126 2007-01-07 16:36:54Z VZ $
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 
Dump(wxFileConfig & fc)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:
FileConfigTestCase()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( Save );
71         CPPUNIT_TEST( DeleteEntry );
72         CPPUNIT_TEST( DeleteGroup );
73         CPPUNIT_TEST( DeleteAll );
74         CPPUNIT_TEST( RenameEntry );
75         CPPUNIT_TEST( RenameGroup );
76         CPPUNIT_TEST( CreateEntriesAndSubgroup );
77         CPPUNIT_TEST( CreateSubgroupAndEntries );
78         CPPUNIT_TEST( DeleteLastGroup );
79     CPPUNIT_TEST_SUITE_END();
80 
81     void Path();
82     void AddEntries();
83     void GetEntries();
84     void GetGroups();
85     void HasEntry();
86     void HasGroup();
87     void Save();
88     void DeleteEntry();
89     void DeleteGroup();
90     void DeleteAll();
91     void RenameEntry();
92     void RenameGroup();
93     void CreateEntriesAndSubgroup();
94     void CreateSubgroupAndEntries();
95     void DeleteLastGroup();
96 
ChangePath(wxFileConfig & fc,const wxChar * path)97     static wxString ChangePath(wxFileConfig& fc, const wxChar *path)
98     {
99         fc.SetPath(path);
100 
101         return fc.GetPath();
102     }
103 
104     void CheckGroupEntries(const wxFileConfig& fc,
105                            const wxChar *path,
106                            size_t nEntries,
107                            ...);
108     void CheckGroupSubgroups(const wxFileConfig& fc,
109                              const wxChar *path,
110                              size_t nGroups,
111                              ...);
112 
113     DECLARE_NO_COPY_CLASS(FileConfigTestCase)
114 };
115 
116 // register in the unnamed registry so that these tests are run by default
117 CPPUNIT_TEST_SUITE_REGISTRATION( FileConfigTestCase );
118 
119 // also include in it's own registry so that these tests can be run alone
120 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( FileConfigTestCase, "FileConfigTestCase" );
121 
Path()122 void FileConfigTestCase::Path()
123 {
124     wxStringInputStream sis(testconfig);
125     wxFileConfig fc(sis);
126 
127     CPPUNIT_ASSERT( ChangePath(fc, _T("")) == _T("") );
128     CPPUNIT_ASSERT( ChangePath(fc, _T("/")) == _T("") );
129     CPPUNIT_ASSERT( ChangePath(fc, _T("root")) == _T("/root") );
130     CPPUNIT_ASSERT( ChangePath(fc, _T("/root")) == _T("/root") );
131     CPPUNIT_ASSERT( ChangePath(fc, _T("/root/group1/subgroup")) == _T("/root/group1/subgroup") );
132     CPPUNIT_ASSERT( ChangePath(fc, _T("/root/group2")) == _T("/root/group2") );
133 }
134 
AddEntries()135 void FileConfigTestCase::AddEntries()
136 {
137     wxFileConfig fc;
138 
139     wxVERIFY_FILECONFIG( _T(""), fc  );
140 
141     fc.Write(_T("/Foo"), _T("foo"));
142     wxVERIFY_FILECONFIG( _T("Foo=foo\n"), fc  );
143 
144     fc.Write(_T("/Bar/Baz"), _T("baz"));
145     wxVERIFY_FILECONFIG( _T("Foo=foo\n[Bar]\nBaz=baz\n"), fc  );
146 
147     fc.DeleteAll();
148     wxVERIFY_FILECONFIG( _T(""), fc  );
149 
150     fc.Write(_T("/Bar/Baz"), _T("baz"));
151     wxVERIFY_FILECONFIG( _T("[Bar]\nBaz=baz\n"), fc  );
152 
153     fc.Write(_T("/Foo"), _T("foo"));
154     wxVERIFY_FILECONFIG( _T("Foo=foo\n[Bar]\nBaz=baz\n"), fc  );
155 }
156 
157 void
CheckGroupEntries(const wxFileConfig & fc,const wxChar * path,size_t nEntries,...)158 FileConfigTestCase::CheckGroupEntries(const wxFileConfig& fc,
159                                       const wxChar *path,
160                                       size_t nEntries,
161                                       ...)
162 {
163     wxConfigPathChanger change(&fc, wxString(path) + _T("/"));
164 
165     CPPUNIT_ASSERT( fc.GetNumberOfEntries() == nEntries );
166 
167     va_list ap;
168     va_start(ap, nEntries);
169 
170     long cookie;
171     wxString name;
172     for ( bool cont = fc.GetFirstEntry(name, cookie);
173           cont;
174           cont = fc.GetNextEntry(name, cookie), nEntries-- )
175     {
176         CPPUNIT_ASSERT( name == va_arg(ap, wxChar *) );
177     }
178 
179     CPPUNIT_ASSERT( nEntries == 0 );
180 
181     va_end(ap);
182 }
183 
184 void
CheckGroupSubgroups(const wxFileConfig & fc,const wxChar * path,size_t nGroups,...)185 FileConfigTestCase::CheckGroupSubgroups(const wxFileConfig& fc,
186                                         const wxChar *path,
187                                         size_t nGroups,
188                                         ...)
189 {
190     wxConfigPathChanger change(&fc, wxString(path) + _T("/"));
191 
192     CPPUNIT_ASSERT( fc.GetNumberOfGroups() == nGroups );
193 
194     va_list ap;
195     va_start(ap, nGroups);
196 
197     long cookie;
198     wxString name;
199     for ( bool cont = fc.GetFirstGroup(name, cookie);
200           cont;
201           cont = fc.GetNextGroup(name, cookie), nGroups-- )
202     {
203         CPPUNIT_ASSERT( name == va_arg(ap, wxChar *) );
204     }
205 
206     CPPUNIT_ASSERT( nGroups == 0 );
207 
208     va_end(ap);
209 }
210 
GetEntries()211 void FileConfigTestCase::GetEntries()
212 {
213     wxStringInputStream sis(testconfig);
214     wxFileConfig fc(sis);
215 
216     CheckGroupEntries(fc, _T(""), 0);
217     CheckGroupEntries(fc, _T("/root"), 1, _T("entry"));
218     CheckGroupEntries(fc, _T("/root/group1"), 0);
219     CheckGroupEntries(fc, _T("/root/group1/subgroup"),
220                         2, _T("subentry"), _T("subentry2"));
221 }
222 
GetGroups()223 void FileConfigTestCase::GetGroups()
224 {
225     wxStringInputStream sis(testconfig);
226     wxFileConfig fc(sis);
227 
228     CheckGroupSubgroups(fc, _T(""), 1, _T("root"));
229     CheckGroupSubgroups(fc, _T("/root"), 2, _T("group1"), _T("group2"));
230     CheckGroupSubgroups(fc, _T("/root/group1"), 1, _T("subgroup"));
231     CheckGroupSubgroups(fc, _T("/root/group2"), 0);
232 }
233 
HasEntry()234 void FileConfigTestCase::HasEntry()
235 {
236     wxStringInputStream sis(testconfig);
237     wxFileConfig fc(sis);
238 
239     CPPUNIT_ASSERT( !fc.HasEntry(_T("root")) );
240     CPPUNIT_ASSERT( fc.HasEntry(_T("root/entry")) );
241     CPPUNIT_ASSERT( fc.HasEntry(_T("/root/entry")) );
242     CPPUNIT_ASSERT( fc.HasEntry(_T("root/group1/subgroup/subentry")) );
243     CPPUNIT_ASSERT( !fc.HasEntry(_T("")) );
244     CPPUNIT_ASSERT( !fc.HasEntry(_T("root/group1")) );
245     CPPUNIT_ASSERT( !fc.HasEntry(_T("subgroup/subentry")) );
246     CPPUNIT_ASSERT( !fc.HasEntry(_T("/root/no_such_group/entry")) );
247     CPPUNIT_ASSERT( !fc.HasGroup(_T("/root/no_such_group")) );
248 }
249 
HasGroup()250 void FileConfigTestCase::HasGroup()
251 {
252     wxStringInputStream sis(testconfig);
253     wxFileConfig fc(sis);
254 
255     CPPUNIT_ASSERT( fc.HasGroup(_T("root")) );
256     CPPUNIT_ASSERT( fc.HasGroup(_T("root/group1")) );
257     CPPUNIT_ASSERT( fc.HasGroup(_T("root/group1/subgroup")) );
258     CPPUNIT_ASSERT( fc.HasGroup(_T("root/group2")) );
259     CPPUNIT_ASSERT( !fc.HasGroup(_T("")) );
260     CPPUNIT_ASSERT( !fc.HasGroup(_T("root/group")) );
261     CPPUNIT_ASSERT( !fc.HasGroup(_T("root//subgroup")) );
262     CPPUNIT_ASSERT( !fc.HasGroup(_T("foot/subgroup")) );
263     CPPUNIT_ASSERT( !fc.HasGroup(_T("foot")) );
264 }
265 
Save()266 void FileConfigTestCase::Save()
267 {
268     wxStringInputStream sis(testconfig);
269     wxFileConfig fc(sis);
270     wxVERIFY_FILECONFIG( testconfig, fc );
271 }
272 
DeleteEntry()273 void FileConfigTestCase::DeleteEntry()
274 {
275     wxStringInputStream sis(testconfig);
276     wxFileConfig fc(sis);
277 
278     CPPUNIT_ASSERT( !fc.DeleteEntry(_T("foo")) );
279 
280     CPPUNIT_ASSERT( fc.DeleteEntry(_T("root/group1/subgroup/subentry")) );
281     wxVERIFY_FILECONFIG( _T("[root]\n")
282                          _T("entry=value\n")
283                          _T("[root/group1]\n")
284                          _T("[root/group1/subgroup]\n")
285                          _T("subentry2=subvalue2\n")
286                          _T("[root/group2]\n"),
287                          fc );
288 
289     // group should be deleted now as well as it became empty
290     wxConfigPathChanger change(&fc, _T("root/group1/subgroup/subentry2"));
291     CPPUNIT_ASSERT( fc.DeleteEntry(_T("subentry2")) );
292     wxVERIFY_FILECONFIG( _T("[root]\n")
293                          _T("entry=value\n")
294                          _T("[root/group1]\n")
295                          _T("[root/group2]\n"),
296                          fc );
297 }
298 
DeleteGroup()299 void FileConfigTestCase::DeleteGroup()
300 {
301     wxStringInputStream sis(testconfig);
302     wxFileConfig fc(sis);
303 
304     CPPUNIT_ASSERT( !fc.DeleteGroup(_T("foo")) );
305 
306     CPPUNIT_ASSERT( fc.DeleteGroup(_T("root/group1")) );
307     wxVERIFY_FILECONFIG( _T("[root]\n")
308                          _T("entry=value\n")
309                          _T("[root/group2]\n"),
310                          fc );
311 
312     // notice trailing slash: it should be ignored
313     CPPUNIT_ASSERT( fc.DeleteGroup(_T("root/group2/")) );
314     wxVERIFY_FILECONFIG( _T("[root]\n")
315                          _T("entry=value\n"),
316                          fc );
317 
318     CPPUNIT_ASSERT( fc.DeleteGroup(_T("root")) );
319     CPPUNIT_ASSERT( Dump(fc).empty() );
320 }
321 
DeleteAll()322 void FileConfigTestCase::DeleteAll()
323 {
324     wxStringInputStream sis(testconfig);
325     wxFileConfig fc(sis);
326 
327     CPPUNIT_ASSERT( fc.DeleteAll() );
328     CPPUNIT_ASSERT( Dump(fc).empty() );
329 }
330 
RenameEntry()331 void FileConfigTestCase::RenameEntry()
332 {
333     wxStringInputStream sis(testconfig);
334     wxFileConfig fc(sis);
335 
336     fc.SetPath(_T("root"));
337     CPPUNIT_ASSERT( fc.RenameEntry(_T("entry"), _T("newname")) );
338     wxVERIFY_FILECONFIG( _T("[root]\n")
339                          _T("newname=value\n")
340                          _T("[root/group1]\n")
341                          _T("[root/group1/subgroup]\n")
342                          _T("subentry=subvalue\n")
343                          _T("subentry2=subvalue2\n")
344                          _T("[root/group2]\n"),
345                          fc );
346 
347     fc.SetPath(_T("group1/subgroup"));
348     CPPUNIT_ASSERT( !fc.RenameEntry(_T("entry"), _T("newname")) );
349     CPPUNIT_ASSERT( !fc.RenameEntry(_T("subentry"), _T("subentry2")) );
350 
351     CPPUNIT_ASSERT( fc.RenameEntry(_T("subentry"), _T("subentry1")) );
352     wxVERIFY_FILECONFIG( _T("[root]\n")
353                          _T("newname=value\n")
354                          _T("[root/group1]\n")
355                          _T("[root/group1/subgroup]\n")
356                          _T("subentry2=subvalue2\n")
357                          _T("subentry1=subvalue\n")
358                          _T("[root/group2]\n"),
359                          fc );
360 }
361 
RenameGroup()362 void FileConfigTestCase::RenameGroup()
363 {
364     wxStringInputStream sis(testconfig);
365     wxFileConfig fc(sis);
366 
367     CPPUNIT_ASSERT( fc.RenameGroup(_T("root"), _T("foot")) );
368     wxVERIFY_FILECONFIG( _T("[foot]\n")
369                          _T("entry=value\n")
370                          _T("[foot/group1]\n")
371                          _T("[foot/group1/subgroup]\n")
372                          _T("subentry=subvalue\n")
373                          _T("subentry2=subvalue2\n")
374                          _T("[foot/group2]\n"),
375                          fc );
376 
377     // renaming a path doesn't work, it must be the immediate group
378     CPPUNIT_ASSERT( !fc.RenameGroup(_T("foot/group1"), _T("group2")) );
379 
380 
381     fc.SetPath(_T("foot"));
382 
383     // renaming to a name of existing group doesn't work
384     CPPUNIT_ASSERT( !fc.RenameGroup(_T("group1"), _T("group2")) );
385 
386     // try exchanging the groups names and then restore them back
387     CPPUNIT_ASSERT( fc.RenameGroup(_T("group1"), _T("groupTmp")) );
388     wxVERIFY_FILECONFIG( _T("[foot]\n")
389                          _T("entry=value\n")
390                          _T("[foot/groupTmp]\n")
391                          _T("[foot/groupTmp/subgroup]\n")
392                          _T("subentry=subvalue\n")
393                          _T("subentry2=subvalue2\n")
394                          _T("[foot/group2]\n"),
395                          fc );
396 
397     CPPUNIT_ASSERT( fc.RenameGroup(_T("group2"), _T("group1")) );
398     wxVERIFY_FILECONFIG( _T("[foot]\n")
399                          _T("entry=value\n")
400                          _T("[foot/groupTmp]\n")
401                          _T("[foot/groupTmp/subgroup]\n")
402                          _T("subentry=subvalue\n")
403                          _T("subentry2=subvalue2\n")
404                          _T("[foot/group1]\n"),
405                          fc );
406 
407     CPPUNIT_ASSERT( fc.RenameGroup(_T("groupTmp"), _T("group2")) );
408     wxVERIFY_FILECONFIG( _T("[foot]\n")
409                          _T("entry=value\n")
410                          _T("[foot/group2]\n")
411                          _T("[foot/group2/subgroup]\n")
412                          _T("subentry=subvalue\n")
413                          _T("subentry2=subvalue2\n")
414                          _T("[foot/group1]\n"),
415                          fc );
416 
417     CPPUNIT_ASSERT( fc.RenameGroup(_T("group1"), _T("groupTmp")) );
418     wxVERIFY_FILECONFIG( _T("[foot]\n")
419                          _T("entry=value\n")
420                          _T("[foot/group2]\n")
421                          _T("[foot/group2/subgroup]\n")
422                          _T("subentry=subvalue\n")
423                          _T("subentry2=subvalue2\n")
424                          _T("[foot/groupTmp]\n"),
425                          fc );
426 
427     CPPUNIT_ASSERT( fc.RenameGroup(_T("group2"), _T("group1")) );
428     wxVERIFY_FILECONFIG( _T("[foot]\n")
429                          _T("entry=value\n")
430                          _T("[foot/group1]\n")
431                          _T("[foot/group1/subgroup]\n")
432                          _T("subentry=subvalue\n")
433                          _T("subentry2=subvalue2\n")
434                          _T("[foot/groupTmp]\n"),
435                          fc );
436 
437     CPPUNIT_ASSERT( fc.RenameGroup(_T("groupTmp"), _T("group2")) );
438     wxVERIFY_FILECONFIG( _T("[foot]\n")
439                          _T("entry=value\n")
440                          _T("[foot/group1]\n")
441                          _T("[foot/group1/subgroup]\n")
442                          _T("subentry=subvalue\n")
443                          _T("subentry2=subvalue2\n")
444                          _T("[foot/group2]\n"),
445                          fc );
446 }
447 
CreateSubgroupAndEntries()448 void FileConfigTestCase::CreateSubgroupAndEntries()
449 {
450     wxFileConfig fc;
451     fc.Write(_T("sub/sub_first"), _T("sub_one"));
452     fc.Write(_T("first"), _T("one"));
453 
454     wxVERIFY_FILECONFIG( _T("first=one\n")
455                          _T("[sub]\n")
456                          _T("sub_first=sub_one\n"),
457                          fc );
458 }
459 
CreateEntriesAndSubgroup()460 void FileConfigTestCase::CreateEntriesAndSubgroup()
461 {
462     wxFileConfig fc;
463     fc.Write(_T("first"), _T("one"));
464     fc.Write(_T("second"), _T("two"));
465     fc.Write(_T("sub/sub_first"), _T("sub_one"));
466 
467     wxVERIFY_FILECONFIG( _T("first=one\n")
468                          _T("second=two\n")
469                          _T("[sub]\n")
470                          _T("sub_first=sub_one\n"),
471                          fc );
472 }
473 
EmptyConfigAndWriteKey()474 static void EmptyConfigAndWriteKey()
475 {
476     wxFileConfig fc(_T("deleteconftest"));
477 
478     const wxString groupPath = _T("/root");
479 
480     if ( fc.Exists(groupPath) )
481     {
482         // using DeleteGroup exposes the problem, using DeleteAll doesn't
483         CPPUNIT_ASSERT( fc.DeleteGroup(groupPath) );
484     }
485 
486     // the config must be empty for the problem to arise
487     CPPUNIT_ASSERT( !fc.GetNumberOfEntries(true) );
488     CPPUNIT_ASSERT( !fc.GetNumberOfGroups(true) );
489 
490 
491     // this crashes on second call of this function
492     CPPUNIT_ASSERT( fc.Write(groupPath + _T("/entry"), _T("value")) );
493 }
494 
DeleteLastGroup()495 void FileConfigTestCase::DeleteLastGroup()
496 {
497     /*
498     We make 2 of the same calls, first to create a file config with a single
499     group and key...
500     */
501     ::EmptyConfigAndWriteKey();
502 
503     /*
504     ... then the same but this time the key's group is deleted before the
505     key is written again. This causes a crash.
506     */
507     ::EmptyConfigAndWriteKey();
508 
509 
510     // clean up
511     wxLogNull noLogging;
512     (void) ::wxRemoveFile(wxFileConfig::GetLocalFileName(_T("deleteconftest")));
513 }
514 
515 #endif // wxUSE_FILECONFIG
516 
517