Fix wxXmlDocument::SetRoot() broken by recent changes.
[wxWidgets.git] / tests / xml / xmltest.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // Name: tests/xml/xmltest.cpp
3 // Purpose: XML classes unit test
4 // Author: Vaclav Slavik
5 // Created: 2008-03-29
6 // RCS-ID: $Id$
7 // Copyright: (c) 2008 Vaclav Slavik
8 ///////////////////////////////////////////////////////////////////////////////
9
10 // ----------------------------------------------------------------------------
11 // headers
12 // ----------------------------------------------------------------------------
13
14 #include "testprec.h"
15
16 #ifdef __BORLANDC__
17 #pragma hdrstop
18 #endif
19
20 #ifndef WX_PRECOMP
21 #include "wx/wx.h"
22 #endif // WX_PRECOMP
23
24 #include "wx/xml/xml.h"
25 #include "wx/scopedptr.h"
26 #include "wx/sstream.h"
27
28 #include <stdarg.h>
29
30 // ----------------------------------------------------------------------------
31 // helpers for testing XML tree
32 // ----------------------------------------------------------------------------
33
34 namespace
35 {
36
37 void CheckXml(const wxXmlNode *n, ...)
38 {
39 va_list args;
40 va_start(args, n);
41
42 wxXmlNode *child = n->GetChildren();
43
44 for (;;)
45 {
46 const char *childName = va_arg(args, char*);
47 if ( childName == NULL )
48 break;
49
50 CPPUNIT_ASSERT( child );
51 CPPUNIT_ASSERT_EQUAL( childName, child->GetName() );
52 CPPUNIT_ASSERT( child->GetChildren() == NULL );
53 CPPUNIT_ASSERT( child->GetParent() == n );
54
55 child = child->GetNext();
56 }
57
58 va_end(args);
59
60 CPPUNIT_ASSERT( child == NULL ); // no more children
61 }
62
63 } // anon namespace
64
65 // ----------------------------------------------------------------------------
66 // test class
67 // ----------------------------------------------------------------------------
68
69 class XmlTestCase : public CppUnit::TestCase
70 {
71 public:
72 XmlTestCase() {}
73
74 private:
75 CPPUNIT_TEST_SUITE( XmlTestCase );
76 CPPUNIT_TEST( InsertChild );
77 CPPUNIT_TEST( InsertChildAfter );
78 CPPUNIT_TEST( LoadSave );
79 CPPUNIT_TEST( CDATA );
80 CPPUNIT_TEST( PI );
81 CPPUNIT_TEST( Escaping );
82 CPPUNIT_TEST( DetachRoot );
83 CPPUNIT_TEST( AppendToProlog );
84 CPPUNIT_TEST( SetRoot );
85 CPPUNIT_TEST_SUITE_END();
86
87 void InsertChild();
88 void InsertChildAfter();
89 void LoadSave();
90 void CDATA();
91 void PI();
92 void Escaping();
93 void DetachRoot();
94 void AppendToProlog();
95 void SetRoot();
96
97 DECLARE_NO_COPY_CLASS(XmlTestCase)
98 };
99
100 // register in the unnamed registry so that these tests are run by default
101 CPPUNIT_TEST_SUITE_REGISTRATION( XmlTestCase );
102
103 // also include in it's own registry so that these tests can be run alone
104 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( XmlTestCase, "XmlTestCase" );
105
106 void XmlTestCase::InsertChild()
107 {
108 wxScopedPtr<wxXmlNode> root(new wxXmlNode(wxXML_ELEMENT_NODE, "root"));
109 root->AddChild(new wxXmlNode(wxXML_ELEMENT_NODE, "1"));
110 wxXmlNode *two = new wxXmlNode(wxXML_ELEMENT_NODE, "2");
111 root->AddChild(two);
112 root->AddChild(new wxXmlNode(wxXML_ELEMENT_NODE, "3"));
113 CheckXml(root.get(), "1", "2", "3", NULL);
114
115 // check inserting in front:
116 root->InsertChild(new wxXmlNode(wxXML_ELEMENT_NODE, "A"), NULL);
117 CheckXml(root.get(), "A", "1", "2", "3", NULL);
118 root->InsertChild(new wxXmlNode(wxXML_ELEMENT_NODE, "B"), root->GetChildren());
119 CheckXml(root.get(), "B", "A", "1", "2", "3", NULL);
120
121 // and in the middle:
122 root->InsertChild(new wxXmlNode(wxXML_ELEMENT_NODE, "C"), two);
123 CheckXml(root.get(), "B", "A", "1", "C", "2", "3", NULL);
124 }
125
126 void XmlTestCase::InsertChildAfter()
127 {
128 wxScopedPtr<wxXmlNode> root(new wxXmlNode(wxXML_ELEMENT_NODE, "root"));
129
130 root->InsertChildAfter(new wxXmlNode(wxXML_ELEMENT_NODE, "1"), NULL);
131 CheckXml(root.get(), "1", NULL);
132
133 wxXmlNode *two = new wxXmlNode(wxXML_ELEMENT_NODE, "2");
134 root->AddChild(two);
135 wxXmlNode *three = new wxXmlNode(wxXML_ELEMENT_NODE, "3");
136 root->AddChild(three);
137 CheckXml(root.get(), "1", "2", "3", NULL);
138
139 // check inserting in the middle:
140 root->InsertChildAfter(new wxXmlNode(wxXML_ELEMENT_NODE, "A"), root->GetChildren());
141 CheckXml(root.get(), "1", "A", "2", "3", NULL);
142 root->InsertChildAfter(new wxXmlNode(wxXML_ELEMENT_NODE, "B"), two);
143 CheckXml(root.get(), "1", "A", "2", "B", "3", NULL);
144
145 // and at the end:
146 root->InsertChildAfter(new wxXmlNode(wxXML_ELEMENT_NODE, "C"), three);
147 CheckXml(root.get(), "1", "A", "2", "B", "3", "C", NULL);
148 }
149
150 void XmlTestCase::LoadSave()
151 {
152 // NB: this is not real XRC but rather some XRC-like XML fragment which
153 // exercises different XML constructs to check that they're saved back
154 // correctly
155 //
156 // Also note that there should be no blank lines here as they disappear
157 // after saving.
158 const char *xmlText =
159 "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>\n"
160 "<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
161 " <!-- Test comment -->\n"
162 " <object class=\"wxDialog\" name=\"my_dialog\">\n"
163 " <children>\n"
164 " <grandchild id=\"1\"/>\n"
165 " </children>\n"
166 " <subobject/>\n"
167 " </object>\n"
168 "</resource>\n"
169 ;
170
171 wxStringInputStream sis(xmlText);
172
173 wxXmlDocument doc;
174 CPPUNIT_ASSERT( doc.Load(sis) );
175
176 wxStringOutputStream sos;
177 CPPUNIT_ASSERT( doc.Save(sos) );
178
179 CPPUNIT_ASSERT_EQUAL( xmlText, sos.GetString() );
180
181
182 #if wxUSE_UNICODE
183 const char *utf8xmlText =
184 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
185 "<word>\n"
186 " <lang name=\"fr\">\xc3\xa9t\xc3\xa9</lang>\n"
187 " <lang name=\"ru\">\xd0\xbb\xd0\xb5\xd1\x82\xd0\xbe</lang>\n"
188 "</word>\n"
189 ;
190
191 wxStringInputStream sis8(wxString::FromUTF8(utf8xmlText));
192 CPPUNIT_ASSERT( doc.Load(sis8) );
193
194 // this contents can't be represented in Latin-1 as it contains Cyrillic
195 // letters
196 doc.SetFileEncoding("ISO-8859-1");
197 CPPUNIT_ASSERT( !doc.Save(sos) );
198
199 // but it should work in UTF-8
200 wxStringOutputStream sos8;
201 doc.SetFileEncoding("UTF-8");
202 CPPUNIT_ASSERT( doc.Save(sos8) );
203 CPPUNIT_ASSERT_EQUAL( wxString(utf8xmlText),
204 wxString(sos8.GetString().ToUTF8()) );
205 #endif // wxUSE_UNICODE
206
207 const char *xmlTextProlog =
208 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
209 "<!-- Prolog comment -->\n"
210 "<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
211 "<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
212 " <!-- Test comment -->\n"
213 " <object class=\"wxDialog\" name=\"my_dialog\">\n"
214 " <children>\n"
215 " <grandchild id=\"1\"/>\n"
216 " </children>\n"
217 " <subobject/>\n"
218 " </object>\n"
219 "</resource>\n"
220 "<!-- Trailing comment -->\n"
221 ;
222
223 wxStringInputStream sisp(xmlTextProlog);
224 CPPUNIT_ASSERT( doc.Load(sisp, "UTF-8") );
225
226 wxStringOutputStream sosp;
227 CPPUNIT_ASSERT( doc.Save(sosp) );
228
229 CPPUNIT_ASSERT_EQUAL( xmlTextProlog, sosp.GetString() );
230 }
231
232 void XmlTestCase::CDATA()
233 {
234 const char *xmlText =
235 "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
236 "<name>\n"
237 " <![CDATA[Giovanni Mittone]]>\n"
238 "</name>\n"
239 ;
240
241 wxStringInputStream sis(xmlText);
242 wxXmlDocument doc;
243 CPPUNIT_ASSERT( doc.Load(sis) );
244
245 wxXmlNode *n = doc.GetRoot();
246 CPPUNIT_ASSERT( n );
247
248 n = n->GetChildren();
249 CPPUNIT_ASSERT( n );
250
251 // check that both leading (" ") and trailing white space is not part of
252 // the node contents when CDATA is used and wxXMLDOC_KEEP_WHITESPACE_NODES
253 // is not
254 CPPUNIT_ASSERT_EQUAL( "Giovanni Mittone", n->GetContent() );
255 }
256
257 void XmlTestCase::PI()
258 {
259 const char *xmlText =
260 "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
261 "<root>\n"
262 " <?robot index=\"no\" follow=\"no\"?>\n"
263 "</root>\n"
264 ;
265
266 wxStringInputStream sis(xmlText);
267 wxXmlDocument doc;
268 CPPUNIT_ASSERT( doc.Load(sis) );
269
270 wxXmlNode *n = doc.GetRoot();
271 CPPUNIT_ASSERT( n );
272
273 n = n->GetChildren();
274 CPPUNIT_ASSERT( n );
275
276 CPPUNIT_ASSERT_EQUAL( "index=\"no\" follow=\"no\"", n->GetContent() );
277 }
278
279 void XmlTestCase::Escaping()
280 {
281 // Verify that attribute values are escaped correctly, see
282 // http://trac.wxwidgets.org/ticket/12275
283
284 const char *xmlText =
285 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
286 "<root text=\"hello&#xD;&#xA;this is a new line\">\n"
287 " <x/>\n"
288 "</root>\n"
289 ;
290
291 wxStringInputStream sis(xmlText);
292
293 wxXmlDocument doc;
294 CPPUNIT_ASSERT( doc.Load(sis) );
295
296 wxStringOutputStream sos;
297 CPPUNIT_ASSERT( doc.Save(sos) );
298
299 CPPUNIT_ASSERT_EQUAL( xmlText, sos.GetString() );
300 }
301
302 void XmlTestCase::DetachRoot()
303 {
304 const char *xmlTextProlog =
305 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
306 "<!-- Prolog comment -->\n"
307 "<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
308 "<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
309 " <!-- Test comment -->\n"
310 " <object class=\"wxDialog\" name=\"my_dialog\">\n"
311 " <children>\n"
312 " <grandchild id=\"1\"/>\n"
313 " </children>\n"
314 " <subobject/>\n"
315 " </object>\n"
316 "</resource>\n"
317 "<!-- Trailing comment -->\n"
318 ;
319 const char *xmlTextHtm =
320 "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
321 "<html>\n"
322 " <head>\n"
323 " <title>Testing wxXml</title>\n"
324 " </head>\n"
325 " <body>\n"
326 " <p>Some body text</p>\n"
327 " </body>\n"
328 "</html>\n"
329 ;
330 wxXmlDocument doc;
331
332 wxStringInputStream sish(xmlTextHtm);
333 CPPUNIT_ASSERT( doc.Load(sish) );
334
335 wxXmlNode *root = doc.DetachRoot();
336
337 wxStringInputStream sisp(xmlTextProlog);
338 CPPUNIT_ASSERT( doc.Load(sisp) );
339
340 doc.SetRoot(root);
341
342 wxStringOutputStream sos;
343 CPPUNIT_ASSERT( doc.Save(sos) );
344
345 const char *xmlTextResult1 =
346 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
347 "<!-- Prolog comment -->\n"
348 "<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
349 "<html>\n"
350 " <head>\n"
351 " <title>Testing wxXml</title>\n"
352 " </head>\n"
353 " <body>\n"
354 " <p>Some body text</p>\n"
355 " </body>\n"
356 "</html>\n"
357 "<!-- Trailing comment -->\n"
358 ;
359 CPPUNIT_ASSERT_EQUAL( xmlTextResult1, sos.GetString() );
360
361 wxStringInputStream sisp2(xmlTextProlog);
362 CPPUNIT_ASSERT( doc.Load(sisp2) );
363
364 root = doc.DetachRoot();
365
366 wxStringInputStream sish2(xmlTextHtm);
367 CPPUNIT_ASSERT( doc.Load(sish2) );
368
369 doc.SetRoot(root);
370
371 wxStringOutputStream sos2;
372 CPPUNIT_ASSERT( doc.Save(sos2) );
373
374 const char *xmlTextResult2 =
375 "<?xml version=\"1.0\" encoding=\"windows-1252\"?>\n"
376 "<resource xmlns=\"http://www.wxwidgets.org/wxxrc\" version=\"2.3.0.1\">\n"
377 " <!-- Test comment -->\n"
378 " <object class=\"wxDialog\" name=\"my_dialog\">\n"
379 " <children>\n"
380 " <grandchild id=\"1\"/>\n"
381 " </children>\n"
382 " <subobject/>\n"
383 " </object>\n"
384 "</resource>\n"
385 ;
386 CPPUNIT_ASSERT_EQUAL( xmlTextResult2, sos2.GetString() );
387 }
388
389 void XmlTestCase::AppendToProlog()
390 {
391 const char *xmlText =
392 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
393 "<root>\n"
394 " <p>Some text</p>\n"
395 "</root>\n"
396 ;
397 wxXmlDocument rootdoc;
398 wxStringInputStream sis(xmlText);
399 CPPUNIT_ASSERT( rootdoc.Load(sis) );
400 wxXmlNode *root = rootdoc.DetachRoot();
401
402 wxXmlNode *comment1 = new wxXmlNode(wxXML_COMMENT_NODE, "comment",
403 " 1st prolog entry ");
404 wxXmlNode *pi = new wxXmlNode(wxXML_PI_NODE, "xml-stylesheet",
405 "href=\"style.css\" type=\"text/css\"");
406 wxXmlNode *comment2 = new wxXmlNode(wxXML_COMMENT_NODE, "comment",
407 " 3rd prolog entry ");
408
409 wxXmlDocument doc;
410 doc.AppendToProlog( comment1 );
411 doc.AppendToProlog( pi );
412 doc.SetRoot( root );
413 doc.AppendToProlog( comment2 );
414
415 wxStringOutputStream sos;
416 CPPUNIT_ASSERT( doc.Save(sos) );
417
418 const char *xmlTextResult =
419 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
420 "<!-- 1st prolog entry -->\n"
421 "<?xml-stylesheet href=\"style.css\" type=\"text/css\"?>\n"
422 "<!-- 3rd prolog entry -->\n"
423 "<root>\n"
424 " <p>Some text</p>\n"
425 "</root>\n"
426 ;
427 CPPUNIT_ASSERT_EQUAL( xmlTextResult, sos.GetString() );
428 }
429
430 void XmlTestCase::SetRoot()
431 {
432 wxXmlDocument doc;
433 CPPUNIT_ASSERT( !doc.IsOk() );
434 wxXmlNode *root = new wxXmlNode(wxXML_ELEMENT_NODE, "root");
435
436 // Test for the problem of http://trac.wxwidgets.org/ticket/13135
437 doc.SetRoot( root );
438 wxXmlNode *docNode = doc.GetDocumentNode();
439 CPPUNIT_ASSERT( docNode && root == docNode->GetChildren() );
440 CPPUNIT_ASSERT( doc.IsOk() );
441
442 // Other tests.
443 CPPUNIT_ASSERT( docNode == root->GetParent() );
444 doc.SetRoot(NULL); // Removes from doc but dosn't free mem, doc node left.
445 CPPUNIT_ASSERT( !doc.IsOk() );
446
447 wxXmlNode *comment = new wxXmlNode(wxXML_COMMENT_NODE, "comment", "Prolog Comment");
448 wxXmlNode *pi = new wxXmlNode(wxXML_PI_NODE, "target", "PI instructions");
449 doc.AppendToProlog(comment);
450 doc.SetRoot( root );
451 doc.AppendToProlog(pi);
452 CPPUNIT_ASSERT( doc.IsOk() );
453 wxXmlNode *node = docNode->GetChildren();
454 CPPUNIT_ASSERT( node );
455 CPPUNIT_ASSERT( node->GetType() == wxXML_COMMENT_NODE );
456 CPPUNIT_ASSERT( node->GetParent() == docNode );
457 node = node->GetNext();
458 CPPUNIT_ASSERT( node );
459 CPPUNIT_ASSERT( node->GetType() == wxXML_PI_NODE );
460 CPPUNIT_ASSERT( node->GetParent() == docNode );
461 node = node->GetNext();
462 CPPUNIT_ASSERT( node );
463 CPPUNIT_ASSERT( node->GetType() == wxXML_ELEMENT_NODE );
464 CPPUNIT_ASSERT( node->GetParent() == docNode );
465 node = node->GetNext();
466 CPPUNIT_ASSERT( !node );
467 doc.SetRoot(NULL);
468 CPPUNIT_ASSERT( !doc.IsOk() );
469 doc.SetRoot(root);
470 CPPUNIT_ASSERT( doc.IsOk() );
471 }