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