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