1 #if defined( _MSC_VER )
2 #if !defined( _CRT_SECURE_NO_WARNINGS )
3 #define _CRT_SECURE_NO_WARNINGS // This test file is not intended to be secure.
4 #endif
5 #endif
6
7 #include "tinyxml2.h"
8 #include <cerrno>
9 #include <cstdlib>
10 #include <cstring>
11 #include <ctime>
12
13 #if defined( _MSC_VER ) || defined (WIN32)
14 #include <crtdbg.h>
15 #define WIN32_LEAN_AND_MEAN
16 #include <windows.h>
17 _CrtMemState startMemState;
18 _CrtMemState endMemState;
19 #else
20 #include <sys/stat.h>
21 #include <sys/types.h>
22 #endif
23
24 using namespace tinyxml2;
25 using namespace std;
26 int gPass = 0;
27 int gFail = 0;
28
29
XMLTest(const char * testString,const char * expected,const char * found,bool echo=true,bool extraNL=false)30 bool XMLTest (const char* testString, const char* expected, const char* found, bool echo=true, bool extraNL=false )
31 {
32 bool pass;
33 if ( !expected && !found )
34 pass = true;
35 else if ( !expected || !found )
36 pass = false;
37 else
38 pass = !strcmp( expected, found );
39 if ( pass )
40 printf ("[pass]");
41 else
42 printf ("[fail]");
43
44 if ( !echo ) {
45 printf (" %s\n", testString);
46 }
47 else {
48 if ( extraNL ) {
49 printf( " %s\n", testString );
50 printf( "%s\n", expected );
51 printf( "%s\n", found );
52 }
53 else {
54 printf (" %s [%s][%s]\n", testString, expected, found);
55 }
56 }
57
58 if ( pass )
59 ++gPass;
60 else
61 ++gFail;
62 return pass;
63 }
64
XMLTest(const char * testString,XMLError expected,XMLError found,bool echo=true,bool extraNL=false)65 bool XMLTest(const char* testString, XMLError expected, XMLError found, bool echo = true, bool extraNL = false)
66 {
67 return XMLTest(testString, XMLDocument::ErrorIDToName(expected), XMLDocument::ErrorIDToName(found), echo, extraNL);
68 }
69
XMLTest(const char * testString,bool expected,bool found,bool echo=true,bool extraNL=false)70 bool XMLTest(const char* testString, bool expected, bool found, bool echo = true, bool extraNL = false)
71 {
72 return XMLTest(testString, expected ? "true" : "false", found ? "true" : "false", echo, extraNL);
73 }
74
XMLTest(const char * testString,T expected,T found,bool echo=true)75 template< class T > bool XMLTest( const char* testString, T expected, T found, bool echo=true )
76 {
77 bool pass = ( expected == found );
78 if ( pass )
79 printf ("[pass]");
80 else
81 printf ("[fail]");
82
83 if ( !echo )
84 printf (" %s\n", testString);
85 else
86 printf (" %s [%d][%d]\n", testString, static_cast<int>(expected), static_cast<int>(found) );
87
88 if ( pass )
89 ++gPass;
90 else
91 ++gFail;
92 return pass;
93 }
94
95
NullLineEndings(char * p)96 void NullLineEndings( char* p )
97 {
98 while( p && *p ) {
99 if ( *p == '\n' || *p == '\r' ) {
100 *p = 0;
101 return;
102 }
103 ++p;
104 }
105 }
106
107
example_1()108 int example_1()
109 {
110 XMLDocument doc;
111 doc.LoadFile( "resources/dream.xml" );
112
113 return doc.ErrorID();
114 }
115 /** @page Example-1 Load an XML File
116 * @dontinclude ./xmltest.cpp
117 * Basic XML file loading.
118 * The basic syntax to load an XML file from
119 * disk and check for an error. (ErrorID()
120 * will return 0 for no error.)
121 * @skip example_1()
122 * @until }
123 */
124
125
example_2()126 int example_2()
127 {
128 static const char* xml = "<element/>";
129 XMLDocument doc;
130 doc.Parse( xml );
131
132 return doc.ErrorID();
133 }
134 /** @page Example-2 Parse an XML from char buffer
135 * @dontinclude ./xmltest.cpp
136 * Basic XML string parsing.
137 * The basic syntax to parse an XML for
138 * a char* and check for an error. (ErrorID()
139 * will return 0 for no error.)
140 * @skip example_2()
141 * @until }
142 */
143
144
example_3()145 int example_3()
146 {
147 static const char* xml =
148 "<?xml version=\"1.0\"?>"
149 "<!DOCTYPE PLAY SYSTEM \"play.dtd\">"
150 "<PLAY>"
151 "<TITLE>A Midsummer Night's Dream</TITLE>"
152 "</PLAY>";
153
154 XMLDocument doc;
155 doc.Parse( xml );
156
157 XMLElement* titleElement = doc.FirstChildElement( "PLAY" )->FirstChildElement( "TITLE" );
158 const char* title = titleElement->GetText();
159 printf( "Name of play (1): %s\n", title );
160
161 XMLText* textNode = titleElement->FirstChild()->ToText();
162 title = textNode->Value();
163 printf( "Name of play (2): %s\n", title );
164
165 return doc.ErrorID();
166 }
167 /** @page Example-3 Get information out of XML
168 @dontinclude ./xmltest.cpp
169 In this example, we navigate a simple XML
170 file, and read some interesting text. Note
171 that this example doesn't use error
172 checking; working code should check for null
173 pointers when walking an XML tree, or use
174 XMLHandle.
175
176 (The XML is an excerpt from "dream.xml").
177
178 @skip example_3()
179 @until </PLAY>";
180
181 The structure of the XML file is:
182
183 <ul>
184 <li>(declaration)</li>
185 <li>(dtd stuff)</li>
186 <li>Element "PLAY"</li>
187 <ul>
188 <li>Element "TITLE"</li>
189 <ul>
190 <li>Text "A Midsummer Night's Dream"</li>
191 </ul>
192 </ul>
193 </ul>
194
195 For this example, we want to print out the
196 title of the play. The text of the title (what
197 we want) is child of the "TITLE" element which
198 is a child of the "PLAY" element.
199
200 We want to skip the declaration and dtd, so the
201 method FirstChildElement() is a good choice. The
202 FirstChildElement() of the Document is the "PLAY"
203 Element, the FirstChildElement() of the "PLAY" Element
204 is the "TITLE" Element.
205
206 @until ( "TITLE" );
207
208 We can then use the convenience function GetText()
209 to get the title of the play.
210
211 @until title );
212
213 Text is just another Node in the XML DOM. And in
214 fact you should be a little cautious with it, as
215 text nodes can contain elements.
216
217 @verbatim
218 Consider: A Midsummer Night's <b>Dream</b>
219 @endverbatim
220
221 It is more correct to actually query the Text Node
222 if in doubt:
223
224 @until title );
225
226 Noting that here we use FirstChild() since we are
227 looking for XMLText, not an element, and ToText()
228 is a cast from a Node to a XMLText.
229 */
230
231
example_4()232 bool example_4()
233 {
234 static const char* xml =
235 "<information>"
236 " <attributeApproach v='2' />"
237 " <textApproach>"
238 " <v>2</v>"
239 " </textApproach>"
240 "</information>";
241
242 XMLDocument doc;
243 doc.Parse( xml );
244
245 int v0 = 0;
246 int v1 = 0;
247
248 XMLElement* attributeApproachElement = doc.FirstChildElement()->FirstChildElement( "attributeApproach" );
249 attributeApproachElement->QueryIntAttribute( "v", &v0 );
250
251 XMLElement* textApproachElement = doc.FirstChildElement()->FirstChildElement( "textApproach" );
252 textApproachElement->FirstChildElement( "v" )->QueryIntText( &v1 );
253
254 printf( "Both values are the same: %d and %d\n", v0, v1 );
255
256 return !doc.Error() && ( v0 == v1 );
257 }
258 /** @page Example-4 Read attributes and text information.
259 @dontinclude ./xmltest.cpp
260
261 There are fundamentally 2 ways of writing a key-value
262 pair into an XML file. (Something that's always annoyed
263 me about XML.) Either by using attributes, or by writing
264 the key name into an element and the value into
265 the text node wrapped by the element. Both approaches
266 are illustrated in this example, which shows two ways
267 to encode the value "2" into the key "v":
268
269 @skip example_4()
270 @until "</information>";
271
272 TinyXML-2 has accessors for both approaches.
273
274 When using an attribute, you navigate to the XMLElement
275 with that attribute and use the QueryIntAttribute()
276 group of methods. (Also QueryFloatAttribute(), etc.)
277
278 @skip XMLElement* attributeApproachElement
279 @until &v0 );
280
281 When using the text approach, you need to navigate
282 down one more step to the XMLElement that contains
283 the text. Note the extra FirstChildElement( "v" )
284 in the code below. The value of the text can then
285 be safely queried with the QueryIntText() group
286 of methods. (Also QueryFloatText(), etc.)
287
288 @skip XMLElement* textApproachElement
289 @until &v1 );
290 */
291
292
main(int argc,const char ** argv)293 int main( int argc, const char ** argv )
294 {
295 #if defined( _MSC_VER ) && defined( DEBUG )
296 _CrtMemCheckpoint( &startMemState );
297 // Enable MS Visual C++ debug heap memory leaks dump on exit
298 _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
299 {
300 int leaksOnStart = _CrtDumpMemoryLeaks();
301 XMLTest( "No leaks on start?", FALSE, leaksOnStart );
302 }
303 #endif
304
305 {
306 TIXMLASSERT( true );
307 }
308
309 if ( argc > 1 ) {
310 XMLDocument* doc = new XMLDocument();
311 clock_t startTime = clock();
312 doc->LoadFile( argv[1] );
313 clock_t loadTime = clock();
314 int errorID = doc->ErrorID();
315 delete doc; doc = 0;
316 clock_t deleteTime = clock();
317
318 printf( "Test file '%s' loaded. ErrorID=%d\n", argv[1], errorID );
319 if ( !errorID ) {
320 printf( "Load time=%u\n", (unsigned)(loadTime - startTime) );
321 printf( "Delete time=%u\n", (unsigned)(deleteTime - loadTime) );
322 printf( "Total time=%u\n", (unsigned)(deleteTime - startTime) );
323 }
324 exit(0);
325 }
326
327 FILE* fp = fopen( "resources/dream.xml", "r" );
328 if ( !fp ) {
329 printf( "Error opening test file 'dream.xml'.\n"
330 "Is your working directory the same as where \n"
331 "the xmltest.cpp and dream.xml file are?\n\n"
332 #if defined( _MSC_VER )
333 "In windows Visual Studio you may need to set\n"
334 "Properties->Debugging->Working Directory to '..'\n"
335 #endif
336 );
337 exit( 1 );
338 }
339 fclose( fp );
340
341 XMLTest( "Example-1", 0, example_1() );
342 XMLTest( "Example-2", 0, example_2() );
343 XMLTest( "Example-3", 0, example_3() );
344 XMLTest( "Example-4", true, example_4() );
345
346 /* ------ Example 2: Lookup information. ---- */
347
348 {
349 static const char* test[] = { "<element />",
350 "<element></element>",
351 "<element><subelement/></element>",
352 "<element><subelement></subelement></element>",
353 "<element><subelement><subsub/></subelement></element>",
354 "<!--comment beside elements--><element><subelement></subelement></element>",
355 "<!--comment beside elements, this time with spaces--> \n <element> <subelement> \n </subelement> </element>",
356 "<element attrib1='foo' attrib2=\"bar\" ></element>",
357 "<element attrib1='foo' attrib2=\"bar\" ><subelement attrib3='yeehaa' /></element>",
358 "<element>Text inside element.</element>",
359 "<element><b></b></element>",
360 "<element>Text inside and <b>bolded</b> in the element.</element>",
361 "<outer><element>Text inside and <b>bolded</b> in the element.</element></outer>",
362 "<element>This & That.</element>",
363 "<element attrib='This<That' />",
364 0
365 };
366 for( int i=0; test[i]; ++i ) {
367 XMLDocument doc;
368 doc.Parse( test[i] );
369 XMLTest( "Element test", false, doc.Error() );
370 doc.Print();
371 printf( "----------------------------------------------\n" );
372 }
373 }
374 #if 1
375 {
376 static const char* test = "<!--hello world\n"
377 " line 2\r"
378 " line 3\r\n"
379 " line 4\n\r"
380 " line 5\r-->";
381
382 XMLDocument doc;
383 doc.Parse( test );
384 XMLTest( "Hello world declaration", false, doc.Error() );
385 doc.Print();
386 }
387
388 {
389 // This test is pre-test for the next one
390 // (where Element1 is inserted "after itself".
391 // This code didn't use to crash.
392 XMLDocument doc;
393 XMLElement* element1 = doc.NewElement("Element1");
394 XMLElement* element2 = doc.NewElement("Element2");
395 doc.InsertEndChild(element1);
396 doc.InsertEndChild(element2);
397 doc.InsertAfterChild(element2, element2);
398 doc.InsertAfterChild(element2, element2);
399 }
400
401 {
402 XMLDocument doc;
403 XMLElement* element1 = doc.NewElement("Element1");
404 XMLElement* element2 = doc.NewElement("Element2");
405 doc.InsertEndChild(element1);
406 doc.InsertEndChild(element2);
407
408 // This insertion "after itself"
409 // used to cause invalid memory access and crash
410 doc.InsertAfterChild(element1, element1);
411 doc.InsertAfterChild(element1, element1);
412 doc.InsertAfterChild(element2, element2);
413 doc.InsertAfterChild(element2, element2);
414 }
415
416 {
417 static const char* test = "<element>Text before.</element>";
418 XMLDocument doc;
419 doc.Parse( test );
420 XMLTest( "Element text before", false, doc.Error() );
421 XMLElement* root = doc.FirstChildElement();
422 XMLElement* newElement = doc.NewElement( "Subelement" );
423 root->InsertEndChild( newElement );
424 doc.Print();
425 }
426 {
427 XMLDocument* doc = new XMLDocument();
428 static const char* test = "<element><sub/></element>";
429 doc->Parse( test );
430 XMLTest( "Element with sub element", false, doc->Error() );
431 delete doc;
432 }
433 {
434 // Test: Programmatic DOM nodes insertion return values
435 XMLDocument doc;
436
437 XMLNode* first = doc.NewElement( "firstElement" );
438 XMLTest( "New element", true, first != 0 );
439 XMLNode* firstAfterInsertion = doc.InsertFirstChild( first );
440 XMLTest( "New element inserted first", true, firstAfterInsertion == first );
441
442 XMLNode* last = doc.NewElement( "lastElement" );
443 XMLTest( "New element", true, last != 0 );
444 XMLNode* lastAfterInsertion = doc.InsertEndChild( last );
445 XMLTest( "New element inserted last", true, lastAfterInsertion == last );
446
447 XMLNode* middle = doc.NewElement( "middleElement" );
448 XMLTest( "New element", true, middle != 0 );
449 XMLNode* middleAfterInsertion = doc.InsertAfterChild( first, middle );
450 XMLTest( "New element inserted middle", true, middleAfterInsertion == middle );
451 }
452 {
453 // Test: Programmatic DOM
454 // Build:
455 // <element>
456 // <!--comment-->
457 // <sub attrib="1" />
458 // <sub attrib="2" />
459 // <sub attrib="3" >& Text!</sub>
460 // <element>
461
462 XMLDocument* doc = new XMLDocument();
463 XMLNode* element = doc->InsertEndChild( doc->NewElement( "element" ) );
464
465 XMLElement* sub[3] = { doc->NewElement( "sub" ), doc->NewElement( "sub" ), doc->NewElement( "sub" ) };
466 for( int i=0; i<3; ++i ) {
467 sub[i]->SetAttribute( "attrib", i );
468 }
469 element->InsertEndChild( sub[2] );
470
471 const int dummyInitialValue = 1000;
472 int dummyValue = dummyInitialValue;
473
474 XMLNode* comment = element->InsertFirstChild( doc->NewComment( "comment" ) );
475 comment->SetUserData(&dummyValue);
476 element->InsertAfterChild( comment, sub[0] );
477 element->InsertAfterChild( sub[0], sub[1] );
478 sub[2]->InsertFirstChild( doc->NewText( "& Text!" ));
479 doc->Print();
480 XMLTest( "Programmatic DOM", "comment", doc->FirstChildElement( "element" )->FirstChild()->Value() );
481 XMLTest( "Programmatic DOM", "0", doc->FirstChildElement( "element" )->FirstChildElement()->Attribute( "attrib" ) );
482 XMLTest( "Programmatic DOM", 2, doc->FirstChildElement()->LastChildElement( "sub" )->IntAttribute( "attrib" ) );
483 XMLTest( "Programmatic DOM", "& Text!",
484 doc->FirstChildElement()->LastChildElement( "sub" )->FirstChild()->ToText()->Value() );
485 XMLTest("User data - pointer", true, &dummyValue == comment->GetUserData(), false);
486 XMLTest("User data - value behind pointer", dummyInitialValue, dummyValue, false);
487
488 // And now deletion:
489 element->DeleteChild( sub[2] );
490 doc->DeleteNode( comment );
491
492 element->FirstChildElement()->SetAttribute( "attrib", true );
493 element->LastChildElement()->DeleteAttribute( "attrib" );
494
495 XMLTest( "Programmatic DOM", true, doc->FirstChildElement()->FirstChildElement()->BoolAttribute( "attrib" ) );
496 const int defaultIntValue = 10;
497 const int replacementIntValue = 20;
498 int value1 = defaultIntValue;
499 int value2 = doc->FirstChildElement()->LastChildElement()->IntAttribute( "attrib", replacementIntValue );
500 XMLError result = doc->FirstChildElement()->LastChildElement()->QueryIntAttribute( "attrib", &value1 );
501 XMLTest( "Programmatic DOM", XML_NO_ATTRIBUTE, result );
502 XMLTest( "Programmatic DOM", defaultIntValue, value1 );
503 XMLTest( "Programmatic DOM", replacementIntValue, value2 );
504
505 doc->Print();
506
507 {
508 XMLPrinter streamer;
509 doc->Print( &streamer );
510 printf( "%s", streamer.CStr() );
511 }
512 {
513 XMLPrinter streamer( 0, true );
514 doc->Print( &streamer );
515 XMLTest( "Compact mode", "<element><sub attrib=\"true\"/><sub/></element>", streamer.CStr(), false );
516 }
517 doc->SaveFile( "./resources/out/pretty.xml" );
518 XMLTest( "Save pretty.xml", false, doc->Error() );
519 doc->SaveFile( "./resources/out/compact.xml", true );
520 XMLTest( "Save compact.xml", false, doc->Error() );
521 delete doc;
522 }
523 {
524 // Test: Dream
525 // XML1 : 1,187,569 bytes in 31,209 allocations
526 // XML2 : 469,073 bytes in 323 allocations
527 //int newStart = gNew;
528 XMLDocument doc;
529 doc.LoadFile( "resources/dream.xml" );
530 XMLTest( "Load dream.xml", false, doc.Error() );
531
532 doc.SaveFile( "resources/out/dreamout.xml" );
533 XMLTest( "Save dreamout.xml", false, doc.Error() );
534 doc.PrintError();
535
536 XMLTest( "Dream", "xml version=\"1.0\"",
537 doc.FirstChild()->ToDeclaration()->Value() );
538 XMLTest( "Dream", true, doc.FirstChild()->NextSibling()->ToUnknown() ? true : false );
539 XMLTest( "Dream", "DOCTYPE PLAY SYSTEM \"play.dtd\"",
540 doc.FirstChild()->NextSibling()->ToUnknown()->Value() );
541 XMLTest( "Dream", "And Robin shall restore amends.",
542 doc.LastChild()->LastChild()->LastChild()->LastChild()->LastChildElement()->GetText() );
543 XMLTest( "Dream", "And Robin shall restore amends.",
544 doc.LastChild()->LastChild()->LastChild()->LastChild()->LastChildElement()->GetText() );
545
546 XMLDocument doc2;
547 doc2.LoadFile( "resources/out/dreamout.xml" );
548 XMLTest( "Load dreamout.xml", false, doc2.Error() );
549 XMLTest( "Dream-out", "xml version=\"1.0\"",
550 doc2.FirstChild()->ToDeclaration()->Value() );
551 XMLTest( "Dream-out", true, doc2.FirstChild()->NextSibling()->ToUnknown() ? true : false );
552 XMLTest( "Dream-out", "DOCTYPE PLAY SYSTEM \"play.dtd\"",
553 doc2.FirstChild()->NextSibling()->ToUnknown()->Value() );
554 XMLTest( "Dream-out", "And Robin shall restore amends.",
555 doc2.LastChild()->LastChild()->LastChild()->LastChild()->LastChildElement()->GetText() );
556
557 //gNewTotal = gNew - newStart;
558 }
559
560
561 {
562 const char* error = "<?xml version=\"1.0\" standalone=\"no\" ?>\n"
563 "<passages count=\"006\" formatversion=\"20020620\">\n"
564 " <wrong error>\n"
565 "</passages>";
566
567 XMLDocument doc;
568 doc.Parse( error );
569 XMLTest( "Bad XML", XML_ERROR_PARSING_ATTRIBUTE, doc.ErrorID() );
570 }
571
572 {
573 const char* str = "<doc attr0='1' attr1='2.0' attr2='foo' />";
574
575 XMLDocument doc;
576 doc.Parse( str );
577 XMLTest( "Top level attributes", false, doc.Error() );
578
579 XMLElement* ele = doc.FirstChildElement();
580
581 int iVal;
582 XMLError result;
583 double dVal;
584
585 result = ele->QueryDoubleAttribute( "attr0", &dVal );
586 XMLTest( "Query attribute: int as double", XML_SUCCESS, result);
587 XMLTest( "Query attribute: int as double", 1, (int)dVal );
588 XMLTest( "Query attribute: int as double", 1, (int)ele->DoubleAttribute("attr0"));
589
590 result = ele->QueryDoubleAttribute( "attr1", &dVal );
591 XMLTest( "Query attribute: double as double", XML_SUCCESS, result);
592 XMLTest( "Query attribute: double as double", 2.0, dVal );
593 XMLTest( "Query attribute: double as double", 2.0, ele->DoubleAttribute("attr1") );
594
595 result = ele->QueryIntAttribute( "attr1", &iVal );
596 XMLTest( "Query attribute: double as int", XML_SUCCESS, result);
597 XMLTest( "Query attribute: double as int", 2, iVal );
598
599 result = ele->QueryIntAttribute( "attr2", &iVal );
600 XMLTest( "Query attribute: not a number", XML_WRONG_ATTRIBUTE_TYPE, result );
601 XMLTest( "Query attribute: not a number", 4.0, ele->DoubleAttribute("attr2", 4.0) );
602
603 result = ele->QueryIntAttribute( "bar", &iVal );
604 XMLTest( "Query attribute: does not exist", XML_NO_ATTRIBUTE, result );
605 XMLTest( "Query attribute: does not exist", true, ele->BoolAttribute("bar", true) );
606 }
607
608 {
609 const char* str = "<doc/>";
610
611 XMLDocument doc;
612 doc.Parse( str );
613 XMLTest( "Empty top element", false, doc.Error() );
614
615 XMLElement* ele = doc.FirstChildElement();
616
617 int iVal, iVal2;
618 double dVal, dVal2;
619
620 ele->SetAttribute( "str", "strValue" );
621 ele->SetAttribute( "int", 1 );
622 ele->SetAttribute( "double", -1.0 );
623
624 const char* cStr = ele->Attribute( "str" );
625 {
626 XMLError queryResult = ele->QueryIntAttribute( "int", &iVal );
627 XMLTest( "Query int attribute", XML_SUCCESS, queryResult);
628 }
629 {
630 XMLError queryResult = ele->QueryDoubleAttribute( "double", &dVal );
631 XMLTest( "Query double attribute", XML_SUCCESS, queryResult);
632 }
633
634 {
635 int queryResult = ele->QueryAttribute( "int", &iVal2 );
636 XMLTest( "Query int attribute generic", (int)XML_SUCCESS, queryResult);
637 }
638 {
639 int queryResult = ele->QueryAttribute( "double", &dVal2 );
640 XMLTest( "Query double attribute generic", (int)XML_SUCCESS, queryResult);
641 }
642
643 XMLTest( "Attribute match test", "strValue", ele->Attribute( "str", "strValue" ) );
644 XMLTest( "Attribute round trip. c-string.", "strValue", cStr );
645 XMLTest( "Attribute round trip. int.", 1, iVal );
646 XMLTest( "Attribute round trip. double.", -1, (int)dVal );
647 XMLTest( "Alternate query", true, iVal == iVal2 );
648 XMLTest( "Alternate query", true, dVal == dVal2 );
649 XMLTest( "Alternate query", true, iVal == ele->IntAttribute("int") );
650 XMLTest( "Alternate query", true, dVal == ele->DoubleAttribute("double") );
651 }
652
653 {
654 XMLDocument doc;
655 doc.LoadFile( "resources/utf8test.xml" );
656 XMLTest( "Load utf8test.xml", false, doc.Error() );
657
658 // Get the attribute "value" from the "Russian" element and check it.
659 XMLElement* element = doc.FirstChildElement( "document" )->FirstChildElement( "Russian" );
660 const unsigned char correctValue[] = { 0xd1U, 0x86U, 0xd0U, 0xb5U, 0xd0U, 0xbdU, 0xd0U, 0xbdU,
661 0xd0U, 0xbeU, 0xd1U, 0x81U, 0xd1U, 0x82U, 0xd1U, 0x8cU, 0 };
662
663 XMLTest( "UTF-8: Russian value.", (const char*)correctValue, element->Attribute( "value" ) );
664
665 const unsigned char russianElementName[] = { 0xd0U, 0xa0U, 0xd1U, 0x83U,
666 0xd1U, 0x81U, 0xd1U, 0x81U,
667 0xd0U, 0xbaU, 0xd0U, 0xb8U,
668 0xd0U, 0xb9U, 0 };
669 const char russianText[] = "<\xD0\xB8\xD0\xBC\xD0\xB5\xD0\xB5\xD1\x82>";
670
671 XMLText* text = doc.FirstChildElement( "document" )->FirstChildElement( (const char*) russianElementName )->FirstChild()->ToText();
672 XMLTest( "UTF-8: Browsing russian element name.",
673 russianText,
674 text->Value() );
675
676 // Now try for a round trip.
677 doc.SaveFile( "resources/out/utf8testout.xml" );
678 XMLTest( "UTF-8: Save testout.xml", false, doc.Error() );
679
680 // Check the round trip.
681 bool roundTripOkay = false;
682
683 FILE* saved = fopen( "resources/out/utf8testout.xml", "r" );
684 XMLTest( "UTF-8: Open utf8testout.xml", true, saved != 0 );
685
686 FILE* verify = fopen( "resources/utf8testverify.xml", "r" );
687 XMLTest( "UTF-8: Open utf8testverify.xml", true, verify != 0 );
688
689 if ( saved && verify )
690 {
691 roundTripOkay = true;
692 char verifyBuf[256];
693 while ( fgets( verifyBuf, 256, verify ) )
694 {
695 char savedBuf[256];
696 fgets( savedBuf, 256, saved );
697 NullLineEndings( verifyBuf );
698 NullLineEndings( savedBuf );
699
700 if ( strcmp( verifyBuf, savedBuf ) )
701 {
702 printf( "verify:%s<\n", verifyBuf );
703 printf( "saved :%s<\n", savedBuf );
704 roundTripOkay = false;
705 break;
706 }
707 }
708 }
709 if ( saved )
710 fclose( saved );
711 if ( verify )
712 fclose( verify );
713 XMLTest( "UTF-8: Verified multi-language round trip.", true, roundTripOkay );
714 }
715
716 // --------GetText()-----------
717 {
718 const char* str = "<foo>This is text</foo>";
719 XMLDocument doc;
720 doc.Parse( str );
721 XMLTest( "Double whitespace", false, doc.Error() );
722 const XMLElement* element = doc.RootElement();
723
724 XMLTest( "GetText() normal use.", "This is text", element->GetText() );
725
726 str = "<foo><b>This is text</b></foo>";
727 doc.Parse( str );
728 XMLTest( "Bold text simulation", false, doc.Error() );
729 element = doc.RootElement();
730
731 XMLTest( "GetText() contained element.", element->GetText() == 0, true );
732 }
733
734
735 // --------SetText()-----------
736 {
737 const char* str = "<foo></foo>";
738 XMLDocument doc;
739 doc.Parse( str );
740 XMLTest( "Empty closed element", false, doc.Error() );
741 XMLElement* element = doc.RootElement();
742
743 element->SetText("darkness.");
744 XMLTest( "SetText() normal use (open/close).", "darkness.", element->GetText() );
745
746 element->SetText("blue flame.");
747 XMLTest( "SetText() replace.", "blue flame.", element->GetText() );
748
749 str = "<foo/>";
750 doc.Parse( str );
751 XMLTest( "Empty self-closed element", false, doc.Error() );
752 element = doc.RootElement();
753
754 element->SetText("The driver");
755 XMLTest( "SetText() normal use. (self-closing)", "The driver", element->GetText() );
756
757 element->SetText("<b>horses</b>");
758 XMLTest( "SetText() replace with tag-like text.", "<b>horses</b>", element->GetText() );
759 //doc.Print();
760
761 str = "<foo><bar>Text in nested element</bar></foo>";
762 doc.Parse( str );
763 XMLTest( "Text in nested element", false, doc.Error() );
764 element = doc.RootElement();
765
766 element->SetText("wolves");
767 XMLTest( "SetText() prefix to nested non-text children.", "wolves", element->GetText() );
768
769 str = "<foo/>";
770 doc.Parse( str );
771 XMLTest( "Empty self-closed element round 2", false, doc.Error() );
772 element = doc.RootElement();
773
774 element->SetText( "str" );
775 XMLTest( "SetText types", "str", element->GetText() );
776
777 element->SetText( 1 );
778 XMLTest( "SetText types", "1", element->GetText() );
779
780 element->SetText( 1U );
781 XMLTest( "SetText types", "1", element->GetText() );
782
783 element->SetText( true );
784 XMLTest( "SetText types", "true", element->GetText() );
785
786 element->SetText( 1.5f );
787 XMLTest( "SetText types", "1.5", element->GetText() );
788
789 element->SetText( 1.5 );
790 XMLTest( "SetText types", "1.5", element->GetText() );
791 }
792
793 // ---------- Attributes ---------
794 {
795 static const int64_t BIG = -123456789012345678;
796 XMLDocument doc;
797 XMLElement* element = doc.NewElement("element");
798 doc.InsertFirstChild(element);
799
800 {
801 element->SetAttribute("attrib", int(-100));
802 {
803 int v = 0;
804 XMLError queryResult = element->QueryIntAttribute("attrib", &v);
805 XMLTest("Attribute: int", XML_SUCCESS, queryResult, true);
806 XMLTest("Attribute: int", -100, v, true);
807 }
808 {
809 int v = 0;
810 int queryResult = element->QueryAttribute("attrib", &v);
811 XMLTest("Attribute: int", (int)XML_SUCCESS, queryResult, true);
812 XMLTest("Attribute: int", -100, v, true);
813 }
814 XMLTest("Attribute: int", -100, element->IntAttribute("attrib"), true);
815 }
816 {
817 element->SetAttribute("attrib", unsigned(100));
818 {
819 unsigned v = 0;
820 XMLError queryResult = element->QueryUnsignedAttribute("attrib", &v);
821 XMLTest("Attribute: unsigned", XML_SUCCESS, queryResult, true);
822 XMLTest("Attribute: unsigned", unsigned(100), v, true);
823 }
824 {
825 unsigned v = 0;
826 int queryResult = element->QueryAttribute("attrib", &v);
827 XMLTest("Attribute: unsigned", (int)XML_SUCCESS, queryResult, true);
828 XMLTest("Attribute: unsigned", unsigned(100), v, true);
829 }
830 XMLTest("Attribute: unsigned", unsigned(100), element->UnsignedAttribute("attrib"), true);
831 }
832 {
833 element->SetAttribute("attrib", BIG);
834 {
835 int64_t v = 0;
836 XMLError queryResult = element->QueryInt64Attribute("attrib", &v);
837 XMLTest("Attribute: int64_t", XML_SUCCESS, queryResult, true);
838 XMLTest("Attribute: int64_t", BIG, v, true);
839 }
840 {
841 int64_t v = 0;
842 int queryResult = element->QueryAttribute("attrib", &v);
843 XMLTest("Attribute: int64_t", (int)XML_SUCCESS, queryResult, true);
844 XMLTest("Attribute: int64_t", BIG, v, true);
845 }
846 XMLTest("Attribute: int64_t", BIG, element->Int64Attribute("attrib"), true);
847 }
848 {
849 element->SetAttribute("attrib", true);
850 {
851 bool v = false;
852 XMLError queryResult = element->QueryBoolAttribute("attrib", &v);
853 XMLTest("Attribute: bool", XML_SUCCESS, queryResult, true);
854 XMLTest("Attribute: bool", true, v, true);
855 }
856 {
857 bool v = false;
858 int queryResult = element->QueryAttribute("attrib", &v);
859 XMLTest("Attribute: bool", (int)XML_SUCCESS, queryResult, true);
860 XMLTest("Attribute: bool", true, v, true);
861 }
862 XMLTest("Attribute: bool", true, element->BoolAttribute("attrib"), true);
863 }
864 {
865 element->SetAttribute("attrib", true);
866 const char* result = element->Attribute("attrib");
867 XMLTest("Bool true is 'true'", "true", result);
868
869 XMLUtil::SetBoolSerialization("1", "0");
870 element->SetAttribute("attrib", true);
871 result = element->Attribute("attrib");
872 XMLTest("Bool true is '1'", "1", result);
873
874 XMLUtil::SetBoolSerialization(0, 0);
875 }
876 {
877 element->SetAttribute("attrib", 100.0);
878 {
879 double v = 0;
880 XMLError queryResult = element->QueryDoubleAttribute("attrib", &v);
881 XMLTest("Attribute: double", XML_SUCCESS, queryResult, true);
882 XMLTest("Attribute: double", 100.0, v, true);
883 }
884 {
885 double v = 0;
886 int queryResult = element->QueryAttribute("attrib", &v);
887 XMLTest("Attribute: bool", (int)XML_SUCCESS, queryResult, true);
888 XMLTest("Attribute: double", 100.0, v, true);
889 }
890 XMLTest("Attribute: double", 100.0, element->DoubleAttribute("attrib"), true);
891 }
892 {
893 element->SetAttribute("attrib", 100.0f);
894 {
895 float v = 0;
896 XMLError queryResult = element->QueryFloatAttribute("attrib", &v);
897 XMLTest("Attribute: float", XML_SUCCESS, queryResult, true);
898 XMLTest("Attribute: float", 100.0f, v, true);
899 }
900 {
901 float v = 0;
902 int queryResult = element->QueryAttribute("attrib", &v);
903 XMLTest("Attribute: float", (int)XML_SUCCESS, queryResult, true);
904 XMLTest("Attribute: float", 100.0f, v, true);
905 }
906 XMLTest("Attribute: float", 100.0f, element->FloatAttribute("attrib"), true);
907 }
908 {
909 element->SetText(BIG);
910 int64_t v = 0;
911 XMLError queryResult = element->QueryInt64Text(&v);
912 XMLTest("Element: int64_t", XML_SUCCESS, queryResult, true);
913 XMLTest("Element: int64_t", BIG, v, true);
914 }
915 }
916
917 // ---------- XMLPrinter stream mode ------
918 {
919 {
920 FILE* printerfp = fopen("resources/out/printer.xml", "w");
921 XMLTest("Open printer.xml", true, printerfp != 0);
922 XMLPrinter printer(printerfp);
923 printer.OpenElement("foo");
924 printer.PushAttribute("attrib-text", "text");
925 printer.PushAttribute("attrib-int", int(1));
926 printer.PushAttribute("attrib-unsigned", unsigned(2));
927 printer.PushAttribute("attrib-int64", int64_t(3));
928 printer.PushAttribute("attrib-bool", true);
929 printer.PushAttribute("attrib-double", 4.0);
930 printer.CloseElement();
931 fclose(printerfp);
932 }
933 {
934 XMLDocument doc;
935 doc.LoadFile("resources/out/printer.xml");
936 XMLTest("XMLPrinter Stream mode: load", XML_SUCCESS, doc.ErrorID(), true);
937
938 const XMLDocument& cdoc = doc;
939
940 const XMLAttribute* attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-text");
941 XMLTest("attrib-text", "text", attrib->Value(), true);
942 attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-int");
943 XMLTest("attrib-int", int(1), attrib->IntValue(), true);
944 attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-unsigned");
945 XMLTest("attrib-unsigned", unsigned(2), attrib->UnsignedValue(), true);
946 attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-int64");
947 XMLTest("attrib-int64", int64_t(3), attrib->Int64Value(), true);
948 attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-bool");
949 XMLTest("attrib-bool", true, attrib->BoolValue(), true);
950 attrib = cdoc.FirstChildElement("foo")->FindAttribute("attrib-double");
951 XMLTest("attrib-double", 4.0, attrib->DoubleValue(), true);
952 }
953
954 }
955
956
957 // ---------- CDATA ---------------
958 {
959 const char* str = "<xmlElement>"
960 "<![CDATA["
961 "I am > the rules!\n"
962 "...since I make symbolic puns"
963 "]]>"
964 "</xmlElement>";
965 XMLDocument doc;
966 doc.Parse( str );
967 XMLTest( "CDATA symbolic puns round 1", false, doc.Error() );
968 doc.Print();
969
970 XMLTest( "CDATA parse.", "I am > the rules!\n...since I make symbolic puns",
971 doc.FirstChildElement()->FirstChild()->Value(),
972 false );
973 }
974
975 // ----------- CDATA -------------
976 {
977 const char* str = "<xmlElement>"
978 "<![CDATA["
979 "<b>I am > the rules!</b>\n"
980 "...since I make symbolic puns"
981 "]]>"
982 "</xmlElement>";
983 XMLDocument doc;
984 doc.Parse( str );
985 XMLTest( "CDATA symbolic puns round 2", false, doc.Error() );
986 doc.Print();
987
988 XMLTest( "CDATA parse. [ tixml1:1480107 ]",
989 "<b>I am > the rules!</b>\n...since I make symbolic puns",
990 doc.FirstChildElement()->FirstChild()->Value(),
991 false );
992 }
993
994 // InsertAfterChild causes crash.
995 {
996 // InsertBeforeChild and InsertAfterChild causes crash.
997 XMLDocument doc;
998 XMLElement* parent = doc.NewElement( "Parent" );
999 doc.InsertFirstChild( parent );
1000
1001 XMLElement* childText0 = doc.NewElement( "childText0" );
1002 XMLElement* childText1 = doc.NewElement( "childText1" );
1003
1004 XMLNode* childNode0 = parent->InsertEndChild( childText0 );
1005 XMLTest( "InsertEndChild() return", true, childNode0 == childText0 );
1006 XMLNode* childNode1 = parent->InsertAfterChild( childNode0, childText1 );
1007 XMLTest( "InsertAfterChild() return", true, childNode1 == childText1 );
1008
1009 XMLTest( "Test InsertAfterChild on empty node. ", true, ( childNode1 == parent->LastChild() ) );
1010 }
1011
1012 {
1013 // Entities not being written correctly.
1014 // From Lynn Allen
1015
1016 const char* passages =
1017 "<?xml version=\"1.0\" standalone=\"no\" ?>"
1018 "<passages count=\"006\" formatversion=\"20020620\">"
1019 "<psg context=\"Line 5 has "quotation marks" and 'apostrophe marks'."
1020 " It also has <, >, and &, as well as a fake copyright ©.\"> </psg>"
1021 "</passages>";
1022
1023 XMLDocument doc;
1024 doc.Parse( passages );
1025 XMLTest( "Entity transformation parse round 1", false, doc.Error() );
1026 XMLElement* psg = doc.RootElement()->FirstChildElement();
1027 const char* context = psg->Attribute( "context" );
1028 const char* expected = "Line 5 has \"quotation marks\" and 'apostrophe marks'. It also has <, >, and &, as well as a fake copyright \xC2\xA9.";
1029
1030 XMLTest( "Entity transformation: read. ", expected, context, true );
1031
1032 const char* textFilePath = "resources/out/textfile.txt";
1033 FILE* textfile = fopen( textFilePath, "w" );
1034 XMLTest( "Entity transformation: open text file for writing", true, textfile != 0, true );
1035 if ( textfile )
1036 {
1037 XMLPrinter streamer( textfile );
1038 bool acceptResult = psg->Accept( &streamer );
1039 fclose( textfile );
1040 XMLTest( "Entity transformation: Accept", true, acceptResult );
1041 }
1042
1043 textfile = fopen( textFilePath, "r" );
1044 XMLTest( "Entity transformation: open text file for reading", true, textfile != 0, true );
1045 if ( textfile )
1046 {
1047 char buf[ 1024 ];
1048 fgets( buf, 1024, textfile );
1049 XMLTest( "Entity transformation: write. ",
1050 "<psg context=\"Line 5 has "quotation marks" and 'apostrophe marks'."
1051 " It also has <, >, and &, as well as a fake copyright \xC2\xA9.\"/>\n",
1052 buf, false );
1053 fclose( textfile );
1054 }
1055 }
1056
1057 {
1058 // Suppress entities.
1059 const char* passages =
1060 "<?xml version=\"1.0\" standalone=\"no\" ?>"
1061 "<passages count=\"006\" formatversion=\"20020620\">"
1062 "<psg context=\"Line 5 has "quotation marks" and 'apostrophe marks'.\">Crazy &ttk;</psg>"
1063 "</passages>";
1064
1065 XMLDocument doc( false );
1066 doc.Parse( passages );
1067 XMLTest( "Entity transformation parse round 2", false, doc.Error() );
1068
1069 XMLTest( "No entity parsing.",
1070 "Line 5 has "quotation marks" and 'apostrophe marks'.",
1071 doc.FirstChildElement()->FirstChildElement()->Attribute( "context" ) );
1072 XMLTest( "No entity parsing.", "Crazy &ttk;",
1073 doc.FirstChildElement()->FirstChildElement()->FirstChild()->Value() );
1074 doc.Print();
1075 }
1076
1077 {
1078 const char* test = "<?xml version='1.0'?><a.elem xmi.version='2.0'/>";
1079
1080 XMLDocument doc;
1081 doc.Parse( test );
1082 XMLTest( "dot in names", false, doc.Error() );
1083 XMLTest( "dot in names", "a.elem", doc.FirstChildElement()->Name() );
1084 XMLTest( "dot in names", "2.0", doc.FirstChildElement()->Attribute( "xmi.version" ) );
1085 }
1086
1087 {
1088 const char* test = "<element><Name>1.1 Start easy ignore fin thickness
</Name></element>";
1089
1090 XMLDocument doc;
1091 doc.Parse( test );
1092 XMLTest( "fin thickness", false, doc.Error() );
1093
1094 XMLText* text = doc.FirstChildElement()->FirstChildElement()->FirstChild()->ToText();
1095 XMLTest( "Entity with one digit.",
1096 "1.1 Start easy ignore fin thickness\n", text->Value(),
1097 false );
1098 }
1099
1100 {
1101 // DOCTYPE not preserved (950171)
1102 //
1103 const char* doctype =
1104 "<?xml version=\"1.0\" ?>"
1105 "<!DOCTYPE PLAY SYSTEM 'play.dtd'>"
1106 "<!ELEMENT title (#PCDATA)>"
1107 "<!ELEMENT books (title,authors)>"
1108 "<element />";
1109
1110 XMLDocument doc;
1111 doc.Parse( doctype );
1112 XMLTest( "PLAY SYSTEM parse", false, doc.Error() );
1113 doc.SaveFile( "resources/out/test7.xml" );
1114 XMLTest( "PLAY SYSTEM save", false, doc.Error() );
1115 doc.DeleteChild( doc.RootElement() );
1116 doc.LoadFile( "resources/out/test7.xml" );
1117 XMLTest( "PLAY SYSTEM load", false, doc.Error() );
1118 doc.Print();
1119
1120 const XMLUnknown* decl = doc.FirstChild()->NextSibling()->ToUnknown();
1121 XMLTest( "Correct value of unknown.", "DOCTYPE PLAY SYSTEM 'play.dtd'", decl->Value() );
1122
1123 }
1124
1125 {
1126 // Comments do not stream out correctly.
1127 const char* doctype =
1128 "<!-- Somewhat<evil> -->";
1129 XMLDocument doc;
1130 doc.Parse( doctype );
1131 XMLTest( "Comment somewhat evil", false, doc.Error() );
1132
1133 XMLComment* comment = doc.FirstChild()->ToComment();
1134
1135 XMLTest( "Comment formatting.", " Somewhat<evil> ", comment->Value() );
1136 }
1137 {
1138 // Double attributes
1139 const char* doctype = "<element attr='red' attr='blue' />";
1140
1141 XMLDocument doc;
1142 doc.Parse( doctype );
1143
1144 XMLTest( "Parsing repeated attributes.", XML_ERROR_PARSING_ATTRIBUTE, doc.ErrorID() ); // is an error to tinyxml (didn't use to be, but caused issues)
1145 doc.PrintError();
1146 }
1147
1148 {
1149 // Embedded null in stream.
1150 const char* doctype = "<element att\0r='red' attr='blue' />";
1151
1152 XMLDocument doc;
1153 doc.Parse( doctype );
1154 XMLTest( "Embedded null throws error.", true, doc.Error() );
1155 }
1156
1157 {
1158 // Empty documents should return TIXML_XML_ERROR_PARSING_EMPTY, bug 1070717
1159 const char* str = "";
1160 XMLDocument doc;
1161 doc.Parse( str );
1162 XMLTest( "Empty document error", XML_ERROR_EMPTY_DOCUMENT, doc.ErrorID() );
1163 }
1164
1165 {
1166 // Documents with all whitespaces should return TIXML_XML_ERROR_PARSING_EMPTY, bug 1070717
1167 const char* str = " ";
1168 XMLDocument doc;
1169 doc.Parse( str );
1170 XMLTest( "All whitespaces document error", XML_ERROR_EMPTY_DOCUMENT, doc.ErrorID() );
1171 }
1172
1173 {
1174 // Low entities
1175 XMLDocument doc;
1176 doc.Parse( "<test></test>" );
1177 XMLTest( "Hex values", false, doc.Error() );
1178 const char result[] = { 0x0e, 0 };
1179 XMLTest( "Low entities.", result, doc.FirstChildElement()->GetText() );
1180 doc.Print();
1181 }
1182
1183 {
1184 // Attribute values with trailing quotes not handled correctly
1185 XMLDocument doc;
1186 doc.Parse( "<foo attribute=bar\" />" );
1187 XMLTest( "Throw error with bad end quotes.", true, doc.Error() );
1188 }
1189
1190 {
1191 // [ 1663758 ] Failure to report error on bad XML
1192 XMLDocument xml;
1193 xml.Parse("<x>");
1194 XMLTest("Missing end tag at end of input", true, xml.Error());
1195 xml.Parse("<x> ");
1196 XMLTest("Missing end tag with trailing whitespace", true, xml.Error());
1197 xml.Parse("<x></y>");
1198 XMLTest("Mismatched tags", XML_ERROR_MISMATCHED_ELEMENT, xml.ErrorID() );
1199 }
1200
1201
1202 {
1203 // [ 1475201 ] TinyXML parses entities in comments
1204 XMLDocument xml;
1205 xml.Parse("<!-- declarations for <head> & <body> -->"
1206 "<!-- far & away -->" );
1207 XMLTest( "Declarations for head and body", false, xml.Error() );
1208
1209 XMLNode* e0 = xml.FirstChild();
1210 XMLNode* e1 = e0->NextSibling();
1211 XMLComment* c0 = e0->ToComment();
1212 XMLComment* c1 = e1->ToComment();
1213
1214 XMLTest( "Comments ignore entities.", " declarations for <head> & <body> ", c0->Value(), true );
1215 XMLTest( "Comments ignore entities.", " far & away ", c1->Value(), true );
1216 }
1217
1218 {
1219 XMLDocument xml;
1220 xml.Parse( "<Parent>"
1221 "<child1 att=''/>"
1222 "<!-- With this comment, child2 will not be parsed! -->"
1223 "<child2 att=''/>"
1224 "</Parent>" );
1225 XMLTest( "Comments iteration", false, xml.Error() );
1226 xml.Print();
1227
1228 int count = 0;
1229
1230 for( XMLNode* ele = xml.FirstChildElement( "Parent" )->FirstChild();
1231 ele;
1232 ele = ele->NextSibling() )
1233 {
1234 ++count;
1235 }
1236
1237 XMLTest( "Comments iterate correctly.", 3, count );
1238 }
1239
1240 {
1241 // trying to repro ]1874301]. If it doesn't go into an infinite loop, all is well.
1242 unsigned char buf[] = "<?xml version=\"1.0\" encoding=\"utf-8\"?><feed><![CDATA[Test XMLblablablalblbl";
1243 buf[60] = 239;
1244 buf[61] = 0;
1245
1246 XMLDocument doc;
1247 doc.Parse( (const char*)buf);
1248 XMLTest( "Broken CDATA", true, doc.Error() );
1249 }
1250
1251
1252 {
1253 // bug 1827248 Error while parsing a little bit malformed file
1254 // Actually not malformed - should work.
1255 XMLDocument xml;
1256 xml.Parse( "<attributelist> </attributelist >" );
1257 XMLTest( "Handle end tag whitespace", false, xml.Error() );
1258 }
1259
1260 {
1261 // This one must not result in an infinite loop
1262 XMLDocument xml;
1263 xml.Parse( "<infinite>loop" );
1264 XMLTest( "No closing element", true, xml.Error() );
1265 XMLTest( "Infinite loop test.", true, true );
1266 }
1267 #endif
1268 {
1269 const char* pub = "<?xml version='1.0'?> <element><sub/></element> <!--comment--> <!DOCTYPE>";
1270 XMLDocument doc;
1271 doc.Parse( pub );
1272 XMLTest( "Trailing DOCTYPE", false, doc.Error() );
1273
1274 XMLDocument clone;
1275 for( const XMLNode* node=doc.FirstChild(); node; node=node->NextSibling() ) {
1276 XMLNode* copy = node->ShallowClone( &clone );
1277 clone.InsertEndChild( copy );
1278 }
1279
1280 clone.Print();
1281
1282 int count=0;
1283 const XMLNode* a=clone.FirstChild();
1284 const XMLNode* b=doc.FirstChild();
1285 for( ; a && b; a=a->NextSibling(), b=b->NextSibling() ) {
1286 ++count;
1287 XMLTest( "Clone and Equal", true, a->ShallowEqual( b ));
1288 }
1289 XMLTest( "Clone and Equal", 4, count );
1290 }
1291
1292 {
1293 // Deep Cloning of root element.
1294 XMLDocument doc2;
1295 XMLPrinter printer1;
1296 {
1297 // Make sure doc1 is deleted before we test doc2
1298 const char* xml =
1299 "<root>"
1300 " <child1 foo='bar'/>"
1301 " <!-- comment thing -->"
1302 " <child2 val='1'>Text</child2>"
1303 "</root>";
1304 XMLDocument doc;
1305 doc.Parse(xml);
1306 XMLTest( "Parse before deep cloning root element", false, doc.Error() );
1307
1308 doc.Print(&printer1);
1309 XMLNode* root = doc.RootElement()->DeepClone(&doc2);
1310 doc2.InsertFirstChild(root);
1311 }
1312 XMLPrinter printer2;
1313 doc2.Print(&printer2);
1314
1315 XMLTest("Deep clone of element.", printer1.CStr(), printer2.CStr(), true);
1316 }
1317
1318 {
1319 // Deep Cloning of sub element.
1320 XMLDocument doc2;
1321 XMLPrinter printer1;
1322 {
1323 // Make sure doc1 is deleted before we test doc2
1324 const char* xml =
1325 "<?xml version ='1.0'?>"
1326 "<root>"
1327 " <child1 foo='bar'/>"
1328 " <!-- comment thing -->"
1329 " <child2 val='1'>Text</child2>"
1330 "</root>";
1331 XMLDocument doc;
1332 doc.Parse(xml);
1333 XMLTest( "Parse before deep cloning sub element", false, doc.Error() );
1334
1335 const XMLElement* subElement = doc.FirstChildElement("root")->FirstChildElement("child2");
1336 bool acceptResult = subElement->Accept(&printer1);
1337 XMLTest( "Accept before deep cloning", true, acceptResult );
1338
1339 XMLNode* clonedSubElement = subElement->DeepClone(&doc2);
1340 doc2.InsertFirstChild(clonedSubElement);
1341 }
1342 XMLPrinter printer2;
1343 doc2.Print(&printer2);
1344
1345 XMLTest("Deep clone of sub-element.", printer1.CStr(), printer2.CStr(), true);
1346 }
1347
1348 {
1349 // Deep cloning of document.
1350 XMLDocument doc2;
1351 XMLPrinter printer1;
1352 {
1353 // Make sure doc1 is deleted before we test doc2
1354 const char* xml =
1355 "<?xml version ='1.0'?>"
1356 "<!-- Top level comment. -->"
1357 "<root>"
1358 " <child1 foo='bar'/>"
1359 " <!-- comment thing -->"
1360 " <child2 val='1'>Text</child2>"
1361 "</root>";
1362 XMLDocument doc;
1363 doc.Parse(xml);
1364 XMLTest( "Parse before deep cloning document", false, doc.Error() );
1365 doc.Print(&printer1);
1366
1367 doc.DeepCopy(&doc2);
1368 }
1369 XMLPrinter printer2;
1370 doc2.Print(&printer2);
1371
1372 XMLTest("DeepCopy of document.", printer1.CStr(), printer2.CStr(), true);
1373 }
1374
1375
1376 {
1377 // This shouldn't crash.
1378 XMLDocument doc;
1379 if(XML_SUCCESS != doc.LoadFile( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ))
1380 {
1381 doc.PrintError();
1382 }
1383 XMLTest( "Error in snprinf handling.", true, doc.Error() );
1384 }
1385
1386 {
1387 // Attribute ordering.
1388 static const char* xml = "<element attrib1=\"1\" attrib2=\"2\" attrib3=\"3\" />";
1389 XMLDocument doc;
1390 doc.Parse( xml );
1391 XMLTest( "Parse for attribute ordering", false, doc.Error() );
1392 XMLElement* ele = doc.FirstChildElement();
1393
1394 const XMLAttribute* a = ele->FirstAttribute();
1395 XMLTest( "Attribute order", "1", a->Value() );
1396 a = a->Next();
1397 XMLTest( "Attribute order", "2", a->Value() );
1398 a = a->Next();
1399 XMLTest( "Attribute order", "3", a->Value() );
1400 XMLTest( "Attribute order", "attrib3", a->Name() );
1401
1402 ele->DeleteAttribute( "attrib2" );
1403 a = ele->FirstAttribute();
1404 XMLTest( "Attribute order", "1", a->Value() );
1405 a = a->Next();
1406 XMLTest( "Attribute order", "3", a->Value() );
1407
1408 ele->DeleteAttribute( "attrib1" );
1409 ele->DeleteAttribute( "attrib3" );
1410 XMLTest( "Attribute order (empty)", true, ele->FirstAttribute() == 0 );
1411 }
1412
1413 {
1414 // Make sure an attribute with a space in it succeeds.
1415 static const char* xml0 = "<element attribute1= \"Test Attribute\"/>";
1416 static const char* xml1 = "<element attribute1 =\"Test Attribute\"/>";
1417 static const char* xml2 = "<element attribute1 = \"Test Attribute\"/>";
1418 XMLDocument doc0;
1419 doc0.Parse( xml0 );
1420 XMLTest( "Parse attribute with space 1", false, doc0.Error() );
1421 XMLDocument doc1;
1422 doc1.Parse( xml1 );
1423 XMLTest( "Parse attribute with space 2", false, doc1.Error() );
1424 XMLDocument doc2;
1425 doc2.Parse( xml2 );
1426 XMLTest( "Parse attribute with space 3", false, doc2.Error() );
1427
1428 XMLElement* ele = 0;
1429 ele = doc0.FirstChildElement();
1430 XMLTest( "Attribute with space #1", "Test Attribute", ele->Attribute( "attribute1" ) );
1431 ele = doc1.FirstChildElement();
1432 XMLTest( "Attribute with space #2", "Test Attribute", ele->Attribute( "attribute1" ) );
1433 ele = doc2.FirstChildElement();
1434 XMLTest( "Attribute with space #3", "Test Attribute", ele->Attribute( "attribute1" ) );
1435 }
1436
1437 {
1438 // Make sure we don't go into an infinite loop.
1439 static const char* xml = "<doc><element attribute='attribute'/><element attribute='attribute'/></doc>";
1440 XMLDocument doc;
1441 doc.Parse( xml );
1442 XMLTest( "Parse two elements with attribute", false, doc.Error() );
1443 XMLElement* ele0 = doc.FirstChildElement()->FirstChildElement();
1444 XMLElement* ele1 = ele0->NextSiblingElement();
1445 bool equal = ele0->ShallowEqual( ele1 );
1446
1447 XMLTest( "Infinite loop in shallow equal.", true, equal );
1448 }
1449
1450 // -------- Handles ------------
1451 {
1452 static const char* xml = "<element attrib='bar'><sub>Text</sub></element>";
1453 XMLDocument doc;
1454 doc.Parse( xml );
1455 XMLTest( "Parse element with attribute and nested element round 1", false, doc.Error() );
1456
1457 XMLElement* ele = XMLHandle( doc ).FirstChildElement( "element" ).FirstChild().ToElement();
1458 XMLTest( "Handle, success, mutable", "sub", ele->Value() );
1459
1460 XMLHandle docH( doc );
1461 ele = docH.FirstChildElement( "none" ).FirstChildElement( "element" ).ToElement();
1462 XMLTest( "Handle, dne, mutable", true, ele == 0 );
1463 }
1464
1465 {
1466 static const char* xml = "<element attrib='bar'><sub>Text</sub></element>";
1467 XMLDocument doc;
1468 doc.Parse( xml );
1469 XMLTest( "Parse element with attribute and nested element round 2", false, doc.Error() );
1470 XMLConstHandle docH( doc );
1471
1472 const XMLElement* ele = docH.FirstChildElement( "element" ).FirstChild().ToElement();
1473 XMLTest( "Handle, success, const", "sub", ele->Value() );
1474
1475 ele = docH.FirstChildElement( "none" ).FirstChildElement( "element" ).ToElement();
1476 XMLTest( "Handle, dne, const", true, ele == 0 );
1477 }
1478 {
1479 // Default Declaration & BOM
1480 XMLDocument doc;
1481 doc.InsertEndChild( doc.NewDeclaration() );
1482 doc.SetBOM( true );
1483
1484 XMLPrinter printer;
1485 doc.Print( &printer );
1486
1487 static const char* result = "\xef\xbb\xbf<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
1488 XMLTest( "BOM and default declaration", result, printer.CStr(), false );
1489 XMLTest( "CStrSize", 42, printer.CStrSize(), false );
1490 }
1491 {
1492 const char* xml = "<ipxml ws='1'><info bla=' /></ipxml>";
1493 XMLDocument doc;
1494 doc.Parse( xml );
1495 XMLTest( "Ill formed XML", true, doc.Error() );
1496 }
1497
1498 // QueryXYZText
1499 {
1500 const char* xml = "<point> <x>1.2</x> <y>1</y> <z>38</z> <valid>true</valid> </point>";
1501 XMLDocument doc;
1502 doc.Parse( xml );
1503 XMLTest( "Parse points", false, doc.Error() );
1504
1505 const XMLElement* pointElement = doc.RootElement();
1506
1507 {
1508 int intValue = 0;
1509 XMLError queryResult = pointElement->FirstChildElement( "y" )->QueryIntText( &intValue );
1510 XMLTest( "QueryIntText result", XML_SUCCESS, queryResult, false );
1511 XMLTest( "QueryIntText", 1, intValue, false );
1512 }
1513
1514 {
1515 unsigned unsignedValue = 0;
1516 XMLError queryResult = pointElement->FirstChildElement( "y" )->QueryUnsignedText( &unsignedValue );
1517 XMLTest( "QueryUnsignedText result", XML_SUCCESS, queryResult, false );
1518 XMLTest( "QueryUnsignedText", (unsigned)1, unsignedValue, false );
1519 }
1520
1521 {
1522 float floatValue = 0;
1523 XMLError queryResult = pointElement->FirstChildElement( "x" )->QueryFloatText( &floatValue );
1524 XMLTest( "QueryFloatText result", XML_SUCCESS, queryResult, false );
1525 XMLTest( "QueryFloatText", 1.2f, floatValue, false );
1526 }
1527
1528 {
1529 double doubleValue = 0;
1530 XMLError queryResult = pointElement->FirstChildElement( "x" )->QueryDoubleText( &doubleValue );
1531 XMLTest( "QueryDoubleText result", XML_SUCCESS, queryResult, false );
1532 XMLTest( "QueryDoubleText", 1.2, doubleValue, false );
1533 }
1534
1535 {
1536 bool boolValue = false;
1537 XMLError queryResult = pointElement->FirstChildElement( "valid" )->QueryBoolText( &boolValue );
1538 XMLTest( "QueryBoolText result", XML_SUCCESS, queryResult, false );
1539 XMLTest( "QueryBoolText", true, boolValue, false );
1540 }
1541 }
1542
1543 {
1544 const char* xml = "<element><_sub/><:sub/><sub:sub/><sub-sub/></element>";
1545 XMLDocument doc;
1546 doc.Parse( xml );
1547 XMLTest( "Non-alpha element lead letter parses.", false, doc.Error() );
1548 }
1549
1550 {
1551 const char* xml = "<element _attr1=\"foo\" :attr2=\"bar\"></element>";
1552 XMLDocument doc;
1553 doc.Parse( xml );
1554 XMLTest("Non-alpha attribute lead character parses.", false, doc.Error());
1555 }
1556
1557 {
1558 const char* xml = "<3lement></3lement>";
1559 XMLDocument doc;
1560 doc.Parse( xml );
1561 XMLTest("Element names with lead digit fail to parse.", true, doc.Error());
1562 }
1563
1564 {
1565 const char* xml = "<element/>WOA THIS ISN'T GOING TO PARSE";
1566 XMLDocument doc;
1567 doc.Parse( xml, 10 );
1568 XMLTest( "Set length of incoming data", false, doc.Error() );
1569 }
1570
1571 {
1572 XMLDocument doc;
1573 XMLTest( "Document is initially empty", true, doc.NoChildren() );
1574 doc.Clear();
1575 XMLTest( "Empty is empty after Clear()", true, doc.NoChildren() );
1576 doc.LoadFile( "resources/dream.xml" );
1577 XMLTest( "Load dream.xml", false, doc.Error() );
1578 XMLTest( "Document has something to Clear()", false, doc.NoChildren() );
1579 doc.Clear();
1580 XMLTest( "Document Clear()'s", true, doc.NoChildren() );
1581 }
1582
1583 {
1584 XMLDocument doc;
1585 XMLTest( "No error initially", false, doc.Error() );
1586 XMLError error = doc.Parse( "This is not XML" );
1587 XMLTest( "Error after invalid XML", true, doc.Error() );
1588 XMLTest( "Error after invalid XML", error, doc.ErrorID() );
1589 doc.Clear();
1590 XMLTest( "No error after Clear()", false, doc.Error() );
1591 }
1592
1593 // ----------- Whitespace ------------
1594 {
1595 const char* xml = "<element>"
1596 "<a> This \nis ' text ' </a>"
1597 "<b> This is ' text ' \n</b>"
1598 "<c>This is ' \n\n text '</c>"
1599 "</element>";
1600 XMLDocument doc( true, COLLAPSE_WHITESPACE );
1601 doc.Parse( xml );
1602 XMLTest( "Parse with whitespace collapsing and &apos", false, doc.Error() );
1603
1604 const XMLElement* element = doc.FirstChildElement();
1605 for( const XMLElement* parent = element->FirstChildElement();
1606 parent;
1607 parent = parent->NextSiblingElement() )
1608 {
1609 XMLTest( "Whitespace collapse", "This is ' text '", parent->GetText() );
1610 }
1611 }
1612
1613 #if 0
1614 {
1615 // Passes if assert doesn't fire.
1616 XMLDocument xmlDoc;
1617
1618 xmlDoc.NewDeclaration();
1619 xmlDoc.NewComment("Configuration file");
1620
1621 XMLElement *root = xmlDoc.NewElement("settings");
1622 root->SetAttribute("version", 2);
1623 }
1624 #endif
1625
1626 {
1627 const char* xml = "<element> </element>";
1628 XMLDocument doc( true, COLLAPSE_WHITESPACE );
1629 doc.Parse( xml );
1630 XMLTest( "Parse with all whitespaces", false, doc.Error() );
1631 XMLTest( "Whitespace all space", true, 0 == doc.FirstChildElement()->FirstChild() );
1632 }
1633
1634 {
1635 // An assert should not fire.
1636 const char* xml = "<element/>";
1637 XMLDocument doc;
1638 doc.Parse( xml );
1639 XMLTest( "Parse with self-closed element", false, doc.Error() );
1640 XMLElement* ele = doc.NewElement( "unused" ); // This will get cleaned up with the 'doc' going out of scope.
1641 XMLTest( "Tracking unused elements", true, ele != 0, false );
1642 }
1643
1644
1645 {
1646 const char* xml = "<parent><child>abc</child></parent>";
1647 XMLDocument doc;
1648 doc.Parse( xml );
1649 XMLTest( "Parse for printing of sub-element", false, doc.Error() );
1650 XMLElement* ele = doc.FirstChildElement( "parent")->FirstChildElement( "child");
1651
1652 XMLPrinter printer;
1653 bool acceptResult = ele->Accept( &printer );
1654 XMLTest( "Accept of sub-element", true, acceptResult );
1655 XMLTest( "Printing of sub-element", "<child>abc</child>\n", printer.CStr(), false );
1656 }
1657
1658
1659 {
1660 XMLDocument doc;
1661 XMLError error = doc.LoadFile( "resources/empty.xml" );
1662 XMLTest( "Loading an empty file", XML_ERROR_EMPTY_DOCUMENT, error );
1663 XMLTest( "Loading an empty file and ErrorName as string", "XML_ERROR_EMPTY_DOCUMENT", doc.ErrorName() );
1664 doc.PrintError();
1665 }
1666
1667 {
1668 // BOM preservation
1669 static const char* xml_bom_preservation = "\xef\xbb\xbf<element/>\n";
1670 {
1671 XMLDocument doc;
1672 XMLTest( "BOM preservation (parse)", XML_SUCCESS, doc.Parse( xml_bom_preservation ), false );
1673 XMLPrinter printer;
1674 doc.Print( &printer );
1675
1676 XMLTest( "BOM preservation (compare)", xml_bom_preservation, printer.CStr(), false, true );
1677 doc.SaveFile( "resources/bomtest.xml" );
1678 XMLTest( "Save bomtest.xml", false, doc.Error() );
1679 }
1680 {
1681 XMLDocument doc;
1682 doc.LoadFile( "resources/bomtest.xml" );
1683 XMLTest( "Load bomtest.xml", false, doc.Error() );
1684 XMLTest( "BOM preservation (load)", true, doc.HasBOM(), false );
1685
1686 XMLPrinter printer;
1687 doc.Print( &printer );
1688 XMLTest( "BOM preservation (compare)", xml_bom_preservation, printer.CStr(), false, true );
1689 }
1690 }
1691
1692 {
1693 // Insertion with Removal
1694 const char* xml = "<?xml version=\"1.0\" ?>"
1695 "<root>"
1696 "<one>"
1697 "<subtree>"
1698 "<elem>element 1</elem>text<!-- comment -->"
1699 "</subtree>"
1700 "</one>"
1701 "<two/>"
1702 "</root>";
1703 const char* xmlInsideTwo = "<?xml version=\"1.0\" ?>"
1704 "<root>"
1705 "<one/>"
1706 "<two>"
1707 "<subtree>"
1708 "<elem>element 1</elem>text<!-- comment -->"
1709 "</subtree>"
1710 "</two>"
1711 "</root>";
1712 const char* xmlAfterOne = "<?xml version=\"1.0\" ?>"
1713 "<root>"
1714 "<one/>"
1715 "<subtree>"
1716 "<elem>element 1</elem>text<!-- comment -->"
1717 "</subtree>"
1718 "<two/>"
1719 "</root>";
1720 const char* xmlAfterTwo = "<?xml version=\"1.0\" ?>"
1721 "<root>"
1722 "<one/>"
1723 "<two/>"
1724 "<subtree>"
1725 "<elem>element 1</elem>text<!-- comment -->"
1726 "</subtree>"
1727 "</root>";
1728
1729 XMLDocument doc;
1730 doc.Parse(xml);
1731 XMLTest( "Insertion with removal parse round 1", false, doc.Error() );
1732 XMLElement* subtree = doc.RootElement()->FirstChildElement("one")->FirstChildElement("subtree");
1733 XMLElement* two = doc.RootElement()->FirstChildElement("two");
1734 two->InsertFirstChild(subtree);
1735 XMLPrinter printer1(0, true);
1736 bool acceptResult = doc.Accept(&printer1);
1737 XMLTest("Move node from within <one> to <two> - Accept()", true, acceptResult);
1738 XMLTest("Move node from within <one> to <two>", xmlInsideTwo, printer1.CStr());
1739
1740 doc.Parse(xml);
1741 XMLTest( "Insertion with removal parse round 2", false, doc.Error() );
1742 subtree = doc.RootElement()->FirstChildElement("one")->FirstChildElement("subtree");
1743 two = doc.RootElement()->FirstChildElement("two");
1744 doc.RootElement()->InsertAfterChild(two, subtree);
1745 XMLPrinter printer2(0, true);
1746 acceptResult = doc.Accept(&printer2);
1747 XMLTest("Move node from within <one> after <two> - Accept()", true, acceptResult);
1748 XMLTest("Move node from within <one> after <two>", xmlAfterTwo, printer2.CStr(), false);
1749
1750 doc.Parse(xml);
1751 XMLTest( "Insertion with removal parse round 3", false, doc.Error() );
1752 XMLNode* one = doc.RootElement()->FirstChildElement("one");
1753 subtree = one->FirstChildElement("subtree");
1754 doc.RootElement()->InsertAfterChild(one, subtree);
1755 XMLPrinter printer3(0, true);
1756 acceptResult = doc.Accept(&printer3);
1757 XMLTest("Move node from within <one> after <one> - Accept()", true, acceptResult);
1758 XMLTest("Move node from within <one> after <one>", xmlAfterOne, printer3.CStr(), false);
1759
1760 doc.Parse(xml);
1761 XMLTest( "Insertion with removal parse round 4", false, doc.Error() );
1762 subtree = doc.RootElement()->FirstChildElement("one")->FirstChildElement("subtree");
1763 two = doc.RootElement()->FirstChildElement("two");
1764 doc.RootElement()->InsertEndChild(subtree);
1765 XMLPrinter printer4(0, true);
1766 acceptResult = doc.Accept(&printer4);
1767 XMLTest("Move node from within <one> after <two> - Accept()", true, acceptResult);
1768 XMLTest("Move node from within <one> after <two>", xmlAfterTwo, printer4.CStr(), false);
1769 }
1770
1771 {
1772 const char* xml = "<svg width = \"128\" height = \"128\">"
1773 " <text> </text>"
1774 "</svg>";
1775 XMLDocument doc;
1776 doc.Parse(xml);
1777 XMLTest( "Parse svg with text", false, doc.Error() );
1778 doc.Print();
1779 }
1780
1781 {
1782 // Test that it doesn't crash.
1783 const char* xml = "<?xml version=\"1.0\"?><root><sample><field0><1</field0><field1>2</field1></sample></root>";
1784 XMLDocument doc;
1785 doc.Parse(xml);
1786 XMLTest( "Parse root-sample-field0", true, doc.Error() );
1787 doc.PrintError();
1788 }
1789
1790 #if 1
1791 // the question being explored is what kind of print to use:
1792 // https://github.com/leethomason/tinyxml2/issues/63
1793 {
1794 //const char* xml = "<element attrA='123456789.123456789' attrB='1.001e9' attrC='1.0e-10' attrD='1001000000.000000' attrE='0.1234567890123456789'/>";
1795 const char* xml = "<element/>";
1796 XMLDocument doc;
1797 doc.Parse( xml );
1798 XMLTest( "Parse self-closed empty element", false, doc.Error() );
1799 doc.FirstChildElement()->SetAttribute( "attrA-f64", 123456789.123456789 );
1800 doc.FirstChildElement()->SetAttribute( "attrB-f64", 1.001e9 );
1801 doc.FirstChildElement()->SetAttribute( "attrC-f64", 1.0e9 );
1802 doc.FirstChildElement()->SetAttribute( "attrC-f64", 1.0e20 );
1803 doc.FirstChildElement()->SetAttribute( "attrD-f64", 1.0e-10 );
1804 doc.FirstChildElement()->SetAttribute( "attrD-f64", 0.123456789 );
1805
1806 doc.FirstChildElement()->SetAttribute( "attrA-f32", 123456789.123456789f );
1807 doc.FirstChildElement()->SetAttribute( "attrB-f32", 1.001e9f );
1808 doc.FirstChildElement()->SetAttribute( "attrC-f32", 1.0e9f );
1809 doc.FirstChildElement()->SetAttribute( "attrC-f32", 1.0e20f );
1810 doc.FirstChildElement()->SetAttribute( "attrD-f32", 1.0e-10f );
1811 doc.FirstChildElement()->SetAttribute( "attrD-f32", 0.123456789f );
1812
1813 doc.Print();
1814
1815 /* The result of this test is platform, compiler, and library version dependent. :("
1816 XMLPrinter printer;
1817 doc.Print( &printer );
1818 XMLTest( "Float and double formatting.",
1819 "<element attrA-f64=\"123456789.12345679\" attrB-f64=\"1001000000\" attrC-f64=\"1e+20\" attrD-f64=\"0.123456789\" attrA-f32=\"1.2345679e+08\" attrB-f32=\"1.001e+09\" attrC-f32=\"1e+20\" attrD-f32=\"0.12345679\"/>\n",
1820 printer.CStr(),
1821 true );
1822 */
1823 }
1824 #endif
1825
1826 {
1827 // Issue #184
1828 // If it doesn't assert, it passes. Caused by objects
1829 // getting created during parsing which are then
1830 // inaccessible in the memory pools.
1831 const char* xmlText = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><test>";
1832 {
1833 XMLDocument doc;
1834 doc.Parse(xmlText);
1835 XMLTest( "Parse hex no closing tag round 1", true, doc.Error() );
1836 }
1837 {
1838 XMLDocument doc;
1839 doc.Parse(xmlText);
1840 XMLTest( "Parse hex no closing tag round 2", true, doc.Error() );
1841 doc.Clear();
1842 }
1843 }
1844
1845 {
1846 // If this doesn't assert in DEBUG, all is well.
1847 tinyxml2::XMLDocument doc;
1848 tinyxml2::XMLElement *pRoot = doc.NewElement("Root");
1849 doc.DeleteNode(pRoot);
1850 }
1851
1852 {
1853 XMLDocument doc;
1854 XMLElement* root = doc.NewElement( "Root" );
1855 XMLTest( "Node document before insertion", true, &doc == root->GetDocument() );
1856 doc.InsertEndChild( root );
1857 XMLTest( "Node document after insertion", true, &doc == root->GetDocument() );
1858 }
1859
1860 {
1861 // If this doesn't assert in DEBUG, all is well.
1862 XMLDocument doc;
1863 XMLElement* unlinkedRoot = doc.NewElement( "Root" );
1864 XMLElement* linkedRoot = doc.NewElement( "Root" );
1865 doc.InsertFirstChild( linkedRoot );
1866 unlinkedRoot->GetDocument()->DeleteNode( linkedRoot );
1867 unlinkedRoot->GetDocument()->DeleteNode( unlinkedRoot );
1868 }
1869
1870 {
1871 // Should not assert in DEBUG
1872 XMLPrinter printer;
1873 }
1874
1875 {
1876 // Issue 291. Should not crash
1877 const char* xml = "�</a>";
1878 XMLDocument doc;
1879 doc.Parse( xml );
1880 XMLTest( "Parse hex with closing tag", false, doc.Error() );
1881
1882 XMLPrinter printer;
1883 doc.Print( &printer );
1884 }
1885 {
1886 // Issue 299. Can print elements that are not linked in.
1887 // Will crash if issue not fixed.
1888 XMLDocument doc;
1889 XMLElement* newElement = doc.NewElement( "printme" );
1890 XMLPrinter printer;
1891 bool acceptResult = newElement->Accept( &printer );
1892 XMLTest( "printme - Accept()", true, acceptResult );
1893 // Delete the node to avoid possible memory leak report in debug output
1894 doc.DeleteNode( newElement );
1895 }
1896 {
1897 // Issue 302. Clear errors from LoadFile/SaveFile
1898 XMLDocument doc;
1899 XMLTest( "Issue 302. Should be no error initially", "XML_SUCCESS", doc.ErrorName() );
1900 doc.SaveFile( "./no/such/path/pretty.xml" );
1901 XMLTest( "Issue 302. Fail to save", "XML_ERROR_FILE_COULD_NOT_BE_OPENED", doc.ErrorName() );
1902 doc.SaveFile( "./resources/out/compact.xml", true );
1903 XMLTest( "Issue 302. Subsequent success in saving", "XML_SUCCESS", doc.ErrorName() );
1904 }
1905
1906 {
1907 // If a document fails to load then subsequent
1908 // successful loads should clear the error
1909 XMLDocument doc;
1910 XMLTest( "Should be no error initially", false, doc.Error() );
1911 doc.LoadFile( "resources/no-such-file.xml" );
1912 XMLTest( "No such file - should fail", true, doc.Error() );
1913
1914 doc.LoadFile( "resources/dream.xml" );
1915 XMLTest( "Error should be cleared", false, doc.Error() );
1916 }
1917
1918 {
1919 // Check that declarations are allowed only at beginning of document
1920 const char* xml0 = "<?xml version=\"1.0\" ?>"
1921 " <!-- xml version=\"1.1\" -->"
1922 "<first />";
1923 const char* xml1 = "<?xml version=\"1.0\" ?>"
1924 "<?xml-stylesheet type=\"text/xsl\" href=\"Anything.xsl\"?>"
1925 "<first />";
1926 const char* xml2 = "<first />"
1927 "<?xml version=\"1.0\" ?>";
1928 const char* xml3 = "<first></first>"
1929 "<?xml version=\"1.0\" ?>";
1930
1931 const char* xml4 = "<first><?xml version=\"1.0\" ?></first>";
1932
1933 XMLDocument doc;
1934 doc.Parse(xml0);
1935 XMLTest("Test that the code changes do not affect normal parsing", false, doc.Error() );
1936 doc.Parse(xml1);
1937 XMLTest("Test that the second declaration is allowed", false, doc.Error() );
1938 doc.Parse(xml2);
1939 XMLTest("Test that declaration after a child is not allowed", XML_ERROR_PARSING_DECLARATION, doc.ErrorID() );
1940 doc.Parse(xml3);
1941 XMLTest("Test that declaration after a child is not allowed", XML_ERROR_PARSING_DECLARATION, doc.ErrorID() );
1942 doc.Parse(xml4);
1943 XMLTest("Test that declaration inside a child is not allowed", XML_ERROR_PARSING_DECLARATION, doc.ErrorID() );
1944 }
1945
1946 {
1947 // No matter - before or after successfully parsing a text -
1948 // calling XMLDocument::Value() used to cause an assert in debug.
1949 // Null must be retured.
1950 const char* validXml = "<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
1951 "<first />"
1952 "<second />";
1953 XMLDocument* doc = new XMLDocument();
1954 XMLTest( "XMLDocument::Value() returns null?", NULL, doc->Value() );
1955 doc->Parse( validXml );
1956 XMLTest( "Parse to test XMLDocument::Value()", false, doc->Error());
1957 XMLTest( "XMLDocument::Value() returns null?", NULL, doc->Value() );
1958 delete doc;
1959 }
1960
1961 {
1962 XMLDocument doc;
1963 for( int i = 0; i < XML_ERROR_COUNT; i++ ) {
1964 const XMLError error = static_cast<XMLError>(i);
1965 const char* name = XMLDocument::ErrorIDToName(error);
1966 XMLTest( "ErrorName() after ClearError()", true, name != 0 );
1967 XMLTest( "ErrorName() after ClearError()", true, strlen(name) > 0 );
1968 }
1969 }
1970
1971 {
1972 // Evil memory leaks.
1973 // If an XMLElement (etc) is allocated via NewElement() (etc.)
1974 // and NOT added to the XMLDocument, what happens?
1975 //
1976 // Previously (buggy):
1977 // The memory would be free'd when the XMLDocument is
1978 // destructed. But the XMLElement destructor wasn't called, so
1979 // memory allocated for the XMLElement text would not be free'd.
1980 // In practice this meant strings allocated for the XMLElement
1981 // text would be leaked. An edge case, but annoying.
1982 // Now:
1983 // The XMLElement destructor is called. But the unlinked nodes
1984 // have to be tracked using a list. This has a minor performance
1985 // impact that can become significant if you have a lot of
1986 // unlinked nodes. (But why would you do that?)
1987 // The only way to see this bug was in a Visual C++ runtime debug heap
1988 // leak tracker. This is compiled in by default on Windows Debug and
1989 // enabled with _CRTDBG_LEAK_CHECK_DF parameter passed to _CrtSetDbgFlag().
1990 {
1991 XMLDocument doc;
1992 doc.NewElement("LEAK 1");
1993 }
1994 {
1995 XMLDocument doc;
1996 XMLElement* ele = doc.NewElement("LEAK 2");
1997 doc.DeleteNode(ele);
1998 }
1999 }
2000
2001 {
2002 // Crashing reported via email.
2003 const char* xml =
2004 "<playlist id='playlist1'>"
2005 "<property name='track_name'>voice</property>"
2006 "<property name='audio_track'>1</property>"
2007 "<entry out = '604' producer = '4_playlist1' in = '0' />"
2008 "<blank length = '1' />"
2009 "<entry out = '1625' producer = '3_playlist' in = '0' />"
2010 "<blank length = '2' />"
2011 "<entry out = '946' producer = '2_playlist1' in = '0' />"
2012 "<blank length = '1' />"
2013 "<entry out = '128' producer = '1_playlist1' in = '0' />"
2014 "</playlist>";
2015
2016 // It's not a good idea to delete elements as you walk the
2017 // list. I'm not sure this technically should work; but it's
2018 // an interesting test case.
2019 XMLDocument doc;
2020 XMLError err = doc.Parse(xml);
2021 XMLTest("Crash bug parsing", XML_SUCCESS, err );
2022
2023 XMLElement* playlist = doc.FirstChildElement("playlist");
2024 XMLTest("Crash bug parsing", true, playlist != 0);
2025
2026 {
2027 const char* elementName = "entry";
2028 XMLElement* entry = playlist->FirstChildElement(elementName);
2029 XMLTest("Crash bug parsing", true, entry != 0);
2030 while (entry) {
2031 XMLElement* todelete = entry;
2032 entry = entry->NextSiblingElement(elementName);
2033 playlist->DeleteChild(todelete);
2034 }
2035 entry = playlist->FirstChildElement(elementName);
2036 XMLTest("Crash bug parsing", true, entry == 0);
2037 }
2038 {
2039 const char* elementName = "blank";
2040 XMLElement* blank = playlist->FirstChildElement(elementName);
2041 XMLTest("Crash bug parsing", true, blank != 0);
2042 while (blank) {
2043 XMLElement* todelete = blank;
2044 blank = blank->NextSiblingElement(elementName);
2045 playlist->DeleteChild(todelete);
2046 }
2047 XMLTest("Crash bug parsing", true, blank == 0);
2048 }
2049
2050 tinyxml2::XMLPrinter printer;
2051 const bool acceptResult = playlist->Accept(&printer);
2052 XMLTest("Crash bug parsing - Accept()", true, acceptResult);
2053 printf("%s\n", printer.CStr());
2054
2055 // No test; it only need to not crash.
2056 // Still, wrap it up with a sanity check
2057 int nProperty = 0;
2058 for (const XMLElement* p = playlist->FirstChildElement("property"); p; p = p->NextSiblingElement("property")) {
2059 nProperty++;
2060 }
2061 XMLTest("Crash bug parsing", 2, nProperty);
2062 }
2063
2064 // ----------- Line Number Tracking --------------
2065 {
2066 struct TestUtil: XMLVisitor
2067 {
2068 TestUtil() : str() {}
2069
2070 void TestParseError(const char *testString, const char *docStr, XMLError expected_error, int expectedLine)
2071 {
2072 XMLDocument doc;
2073 const XMLError parseError = doc.Parse(docStr);
2074
2075 XMLTest(testString, parseError, doc.ErrorID());
2076 XMLTest(testString, true, doc.Error());
2077 XMLTest(testString, expected_error, parseError);
2078 XMLTest(testString, expectedLine, doc.ErrorLineNum());
2079 };
2080
2081 void TestStringLines(const char *testString, const char *docStr, const char *expectedLines)
2082 {
2083 XMLDocument doc;
2084 doc.Parse(docStr);
2085 XMLTest(testString, false, doc.Error());
2086 TestDocLines(testString, doc, expectedLines);
2087 }
2088
2089 void TestFileLines(const char *testString, const char *file_name, const char *expectedLines)
2090 {
2091 XMLDocument doc;
2092 doc.LoadFile(file_name);
2093 XMLTest(testString, false, doc.Error());
2094 TestDocLines(testString, doc, expectedLines);
2095 }
2096
2097 private:
2098 DynArray<char, 10> str;
2099
2100 void Push(char type, int lineNum)
2101 {
2102 str.Push(type);
2103 str.Push(char('0' + (lineNum / 10)));
2104 str.Push(char('0' + (lineNum % 10)));
2105 }
2106
2107 bool VisitEnter(const XMLDocument& doc)
2108 {
2109 Push('D', doc.GetLineNum());
2110 return true;
2111 }
2112 bool VisitEnter(const XMLElement& element, const XMLAttribute* firstAttribute)
2113 {
2114 Push('E', element.GetLineNum());
2115 for (const XMLAttribute *attr = firstAttribute; attr != 0; attr = attr->Next())
2116 Push('A', attr->GetLineNum());
2117 return true;
2118 }
2119 bool Visit(const XMLDeclaration& declaration)
2120 {
2121 Push('L', declaration.GetLineNum());
2122 return true;
2123 }
2124 bool Visit(const XMLText& text)
2125 {
2126 Push('T', text.GetLineNum());
2127 return true;
2128 }
2129 bool Visit(const XMLComment& comment)
2130 {
2131 Push('C', comment.GetLineNum());
2132 return true;
2133 }
2134 bool Visit(const XMLUnknown& unknown)
2135 {
2136 Push('U', unknown.GetLineNum());
2137 return true;
2138 }
2139
2140 void TestDocLines(const char *testString, XMLDocument &doc, const char *expectedLines)
2141 {
2142 str.Clear();
2143 const bool acceptResult = doc.Accept(this);
2144 XMLTest(testString, true, acceptResult);
2145 str.Push(0);
2146 XMLTest(testString, expectedLines, str.Mem());
2147 }
2148 } tester;
2149
2150 tester.TestParseError("ErrorLine-Parsing", "\n<root>\n foo \n<unclosed/>", XML_ERROR_PARSING, 2);
2151 tester.TestParseError("ErrorLine-Declaration", "<root>\n<?xml version=\"1.0\"?>", XML_ERROR_PARSING_DECLARATION, 2);
2152 tester.TestParseError("ErrorLine-Mismatch", "\n<root>\n</mismatch>", XML_ERROR_MISMATCHED_ELEMENT, 2);
2153 tester.TestParseError("ErrorLine-CData", "\n<root><![CDATA[ \n foo bar \n", XML_ERROR_PARSING_CDATA, 2);
2154 tester.TestParseError("ErrorLine-Text", "\n<root>\n foo bar \n", XML_ERROR_PARSING_TEXT, 3);
2155 tester.TestParseError("ErrorLine-Comment", "\n<root>\n<!-- >\n", XML_ERROR_PARSING_COMMENT, 3);
2156 tester.TestParseError("ErrorLine-Declaration", "\n<root>\n<? >\n", XML_ERROR_PARSING_DECLARATION, 3);
2157 tester.TestParseError("ErrorLine-Unknown", "\n<root>\n<! \n", XML_ERROR_PARSING_UNKNOWN, 3);
2158 tester.TestParseError("ErrorLine-Element", "\n<root>\n<unclosed \n", XML_ERROR_PARSING_ELEMENT, 3);
2159 tester.TestParseError("ErrorLine-Attribute", "\n<root>\n<unclosed \n att\n", XML_ERROR_PARSING_ATTRIBUTE, 4);
2160 tester.TestParseError("ErrorLine-ElementClose", "\n<root>\n<unclosed \n/unexpected", XML_ERROR_PARSING_ELEMENT, 3);
2161
2162 tester.TestStringLines(
2163 "LineNumbers-String",
2164
2165 "<?xml version=\"1.0\"?>\n" // 1 Doc, DecL
2166 "<root a='b' \n" // 2 Element Attribute
2167 "c='d'> d <blah/> \n" // 3 Attribute Text Element
2168 "newline in text \n" // 4 Text
2169 "and second <zxcv/><![CDATA[\n" // 5 Element Text
2170 " cdata test ]]><!-- comment -->\n" // 6 Comment
2171 "<! unknown></root>", // 7 Unknown
2172
2173 "D01L01E02A02A03T03E03T04E05T05C06U07");
2174
2175 tester.TestStringLines(
2176 "LineNumbers-CRLF",
2177
2178 "\r\n" // 1 Doc (arguably should be line 2)
2179 "<?xml version=\"1.0\"?>\n" // 2 DecL
2180 "<root>\r\n" // 3 Element
2181 "\n" // 4
2182 "text contining new line \n" // 5 Text
2183 " and also containing crlf \r\n" // 6
2184 "<sub><![CDATA[\n" // 7 Element Text
2185 "cdata containing new line \n" // 8
2186 " and also containing cflr\r\n" // 9
2187 "]]></sub><sub2/></root>", // 10 Element
2188
2189 "D01L02E03T05E07T07E10");
2190
2191 tester.TestFileLines(
2192 "LineNumbers-File",
2193 "resources/utf8test.xml",
2194 "D01L01E02E03A03A03T03E04A04A04T04E05A05A05T05E06A06A06T06E07A07A07T07E08A08A08T08E09T09E10T10");
2195 }
2196
2197 {
2198 const char* xml = "<Hello>Text</Error>";
2199 XMLDocument doc;
2200 doc.Parse(xml);
2201 XMLTest("Test mismatched elements.", true, doc.Error());
2202 XMLTest("Test mismatched elements.", XML_ERROR_MISMATCHED_ELEMENT, doc.ErrorID());
2203 // For now just make sure calls work & doesn't crash.
2204 // May solidify the error output in the future.
2205 printf("%s\n", doc.ErrorStr());
2206 doc.PrintError();
2207 }
2208
2209 // ----------- Performance tracking --------------
2210 {
2211 #if defined( _MSC_VER )
2212 __int64 start, end, freq;
2213 QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
2214 #endif
2215
2216 FILE* perfFP = fopen("resources/dream.xml", "r");
2217 XMLTest("Open dream.xml", true, perfFP != 0);
2218 fseek(perfFP, 0, SEEK_END);
2219 long size = ftell(perfFP);
2220 fseek(perfFP, 0, SEEK_SET);
2221
2222 char* mem = new char[size + 1];
2223 memset(mem, 0xfe, size);
2224 size_t bytesRead = fread(mem, 1, size, perfFP);
2225 XMLTest("Read dream.xml", true, uint32_t(size) >= uint32_t(bytesRead));
2226 fclose(perfFP);
2227 mem[size] = 0;
2228
2229 #if defined( _MSC_VER )
2230 QueryPerformanceCounter((LARGE_INTEGER*)&start);
2231 #else
2232 clock_t cstart = clock();
2233 #endif
2234 bool parseDreamXmlFailed = false;
2235 static const int COUNT = 10;
2236 for (int i = 0; i < COUNT; ++i) {
2237 XMLDocument doc;
2238 doc.Parse(mem);
2239 parseDreamXmlFailed = parseDreamXmlFailed || doc.Error();
2240 }
2241 #if defined( _MSC_VER )
2242 QueryPerformanceCounter((LARGE_INTEGER*)&end);
2243 #else
2244 clock_t cend = clock();
2245 #endif
2246 XMLTest( "Parse dream.xml", false, parseDreamXmlFailed );
2247
2248 delete[] mem;
2249
2250 static const char* note =
2251 #ifdef DEBUG
2252 "DEBUG";
2253 #else
2254 "Release";
2255 #endif
2256
2257 #if defined( _MSC_VER )
2258 const double duration = 1000.0 * (double)(end - start) / ((double)freq * (double)COUNT);
2259 #else
2260 const double duration = (double)(cend - cstart) / (double)COUNT;
2261 #endif
2262 printf("\nParsing dream.xml (%s): %.3f milli-seconds\n", note, duration);
2263 }
2264
2265 #if defined( _MSC_VER ) && defined( DEBUG )
2266 {
2267 _CrtMemCheckpoint( &endMemState );
2268
2269 _CrtMemState diffMemState;
2270 _CrtMemDifference( &diffMemState, &startMemState, &endMemState );
2271 _CrtMemDumpStatistics( &diffMemState );
2272
2273 {
2274 int leaksBeforeExit = _CrtDumpMemoryLeaks();
2275 XMLTest( "No leaks before exit?", FALSE, leaksBeforeExit );
2276 }
2277 }
2278 #endif
2279
2280 printf ("\nPass %d, Fail %d\n", gPass, gFail);
2281
2282 return gFail;
2283 }
2284