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