1 /* 2 * testlimits.c: C program to run libxml2 regression tests checking various 3 * limits in document size. Will consume a lot of RAM and CPU cycles 4 * 5 * To compile on Unixes: 6 * cc -o testlimits `xml2-config --cflags` testlimits.c `xml2-config --libs` -lpthread 7 * 8 * See Copyright for the status of this software. 9 * 10 * daniel (at) veillard.com 11 */ 12 13 #include "libxml.h" 14 #include <stdio.h> 15 16 #if !defined(_WIN32) || defined(__CYGWIN__) 17 #include <unistd.h> 18 #endif 19 #include <string.h> 20 #include <sys/types.h> 21 #include <sys/stat.h> 22 #include <fcntl.h> 23 #include <time.h> 24 25 #include <libxml/parser.h> 26 #include <libxml/parserInternals.h> 27 #include <libxml/tree.h> 28 #include <libxml/uri.h> 29 #ifdef LIBXML_READER_ENABLED 30 #include <libxml/xmlreader.h> 31 #endif 32 33 static int verbose = 0; 34 static int tests_quiet = 0; 35 36 /************************************************************************ 37 * * 38 * time handling * 39 * * 40 ************************************************************************/ 41 42 /* maximum time for one parsing before declaring a timeout */ 43 #define MAX_TIME 2 /* seconds */ 44 45 static clock_t t0; 46 int timeout = 0; 47 48 static void reset_timout(void) { 49 timeout = 0; 50 t0 = clock(); 51 } 52 53 static int check_time(void) { 54 clock_t tnow = clock(); 55 if (((tnow - t0) / CLOCKS_PER_SEC) > MAX_TIME) { 56 timeout = 1; 57 return(0); 58 } 59 return(1); 60 } 61 62 /************************************************************************ 63 * * 64 * Huge document generator * 65 * * 66 ************************************************************************/ 67 68 #include <libxml/xmlIO.h> 69 70 /* 71 * Huge documents are built using fixed start and end chunks 72 * and filling between the two an unconventional amount of char data 73 */ 74 typedef struct hugeTest hugeTest; 75 typedef hugeTest *hugeTestPtr; 76 struct hugeTest { 77 const char *description; 78 const char *name; 79 const char *start; 80 const char *end; 81 }; 82 83 static struct hugeTest hugeTests[] = { 84 { "Huge text node", "huge:textNode", "<foo>", "</foo>" }, 85 { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" }, 86 { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" }, 87 { "Huge PI node", "huge:piNode", "<foo><?bar ", "?></foo>" }, 88 }; 89 90 static const char *current; 91 static int rlen; 92 static unsigned int currentTest = 0; 93 static int instate = 0; 94 95 /** 96 * hugeMatch: 97 * @URI: an URI to test 98 * 99 * Check for an huge: query 100 * 101 * Returns 1 if yes and 0 if another Input module should be used 102 */ 103 static int 104 hugeMatch(const char * URI) { 105 if ((URI != NULL) && (!strncmp(URI, "huge:", 5))) 106 return(1); 107 return(0); 108 } 109 110 /** 111 * hugeOpen: 112 * @URI: an URI to test 113 * 114 * Return a pointer to the huge: query handler, in this example simply 115 * the current pointer... 116 * 117 * Returns an Input context or NULL in case or error 118 */ 119 static void * 120 hugeOpen(const char * URI) { 121 if ((URI == NULL) || (strncmp(URI, "huge:", 5))) 122 return(NULL); 123 124 for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]); 125 currentTest++) 126 if (!strcmp(hugeTests[currentTest].name, URI)) 127 goto found; 128 129 return(NULL); 130 131 found: 132 rlen = strlen(hugeTests[currentTest].start); 133 current = hugeTests[currentTest].start; 134 instate = 0; 135 return((void *) current); 136 } 137 138 /** 139 * hugeClose: 140 * @context: the read context 141 * 142 * Close the huge: query handler 143 * 144 * Returns 0 or -1 in case of error 145 */ 146 static int 147 hugeClose(void * context) { 148 if (context == NULL) return(-1); 149 fprintf(stderr, "\n"); 150 return(0); 151 } 152 153 #define CHUNK 4096 154 155 char filling[CHUNK + 1]; 156 157 static void fillFilling(void) { 158 int i; 159 160 for (i = 0;i < CHUNK;i++) { 161 filling[i] = 'a'; 162 } 163 filling[CHUNK] = 0; 164 } 165 166 size_t maxlen = 64 * 1024 * 1024; 167 size_t curlen = 0; 168 size_t dotlen; 169 170 /** 171 * hugeRead: 172 * @context: the read context 173 * @buffer: where to store data 174 * @len: number of bytes to read 175 * 176 * Implement an huge: query read. 177 * 178 * Returns the number of bytes read or -1 in case of error 179 */ 180 static int 181 hugeRead(void *context, char *buffer, int len) 182 { 183 if ((context == NULL) || (buffer == NULL) || (len < 0)) 184 return (-1); 185 186 if (instate == 0) { 187 if (len >= rlen) { 188 len = rlen; 189 rlen = 0; 190 memcpy(buffer, current, len); 191 instate = 1; 192 curlen = 0; 193 dotlen = maxlen / 10; 194 } else { 195 memcpy(buffer, current, len); 196 rlen -= len; 197 current += len; 198 } 199 } else if (instate == 2) { 200 if (len >= rlen) { 201 len = rlen; 202 rlen = 0; 203 memcpy(buffer, current, len); 204 instate = 3; 205 curlen = 0; 206 } else { 207 memcpy(buffer, current, len); 208 rlen -= len; 209 current += len; 210 } 211 } else if (instate == 1) { 212 if (len > CHUNK) len = CHUNK; 213 memcpy(buffer, &filling[0], len); 214 curlen += len; 215 if (curlen >= maxlen) { 216 rlen = strlen(hugeTests[currentTest].end); 217 current = hugeTests[currentTest].end; 218 instate = 2; 219 } else { 220 if (curlen > dotlen) { 221 fprintf(stderr, "."); 222 dotlen += maxlen / 10; 223 } 224 } 225 } else 226 len = 0; 227 return (len); 228 } 229 230 /************************************************************************ 231 * * 232 * Crazy document generator * 233 * * 234 ************************************************************************/ 235 236 unsigned int crazy_indx = 0; 237 238 const char *crazy = "<?xml version='1.0' encoding='UTF-8'?>\ 239 <?tst ?>\ 240 <!-- tst -->\ 241 <!DOCTYPE foo [\ 242 <?tst ?>\ 243 <!-- tst -->\ 244 <!ELEMENT foo (#PCDATA)>\ 245 <!ELEMENT p (#PCDATA|emph)* >\ 246 ]>\ 247 <?tst ?>\ 248 <!-- tst -->\ 249 <foo bar='foo'>\ 250 <?tst ?>\ 251 <!-- tst -->\ 252 foo\ 253 <![CDATA[ ]]>\ 254 </foo>\ 255 <?tst ?>\ 256 <!-- tst -->"; 257 258 /** 259 * crazyMatch: 260 * @URI: an URI to test 261 * 262 * Check for a crazy: query 263 * 264 * Returns 1 if yes and 0 if another Input module should be used 265 */ 266 static int 267 crazyMatch(const char * URI) { 268 if ((URI != NULL) && (!strncmp(URI, "crazy:", 6))) 269 return(1); 270 return(0); 271 } 272 273 /** 274 * crazyOpen: 275 * @URI: an URI to test 276 * 277 * Return a pointer to the crazy: query handler, in this example simply 278 * the current pointer... 279 * 280 * Returns an Input context or NULL in case or error 281 */ 282 static void * 283 crazyOpen(const char * URI) { 284 if ((URI == NULL) || (strncmp(URI, "crazy:", 6))) 285 return(NULL); 286 287 if (crazy_indx > strlen(crazy)) 288 return(NULL); 289 reset_timout(); 290 rlen = crazy_indx; 291 current = &crazy[0]; 292 instate = 0; 293 return((void *) current); 294 } 295 296 /** 297 * crazyClose: 298 * @context: the read context 299 * 300 * Close the crazy: query handler 301 * 302 * Returns 0 or -1 in case of error 303 */ 304 static int 305 crazyClose(void * context) { 306 if (context == NULL) return(-1); 307 return(0); 308 } 309 310 311 /** 312 * crazyRead: 313 * @context: the read context 314 * @buffer: where to store data 315 * @len: number of bytes to read 316 * 317 * Implement an crazy: query read. 318 * 319 * Returns the number of bytes read or -1 in case of error 320 */ 321 static int 322 crazyRead(void *context, char *buffer, int len) 323 { 324 if ((context == NULL) || (buffer == NULL) || (len < 0)) 325 return (-1); 326 327 if ((check_time() <= 0) && (instate == 1)) { 328 fprintf(stderr, "\ntimeout in crazy(%d)\n", crazy_indx); 329 rlen = strlen(crazy) - crazy_indx; 330 current = &crazy[crazy_indx]; 331 instate = 2; 332 } 333 if (instate == 0) { 334 if (len >= rlen) { 335 len = rlen; 336 rlen = 0; 337 memcpy(buffer, current, len); 338 instate = 1; 339 curlen = 0; 340 } else { 341 memcpy(buffer, current, len); 342 rlen -= len; 343 current += len; 344 } 345 } else if (instate == 2) { 346 if (len >= rlen) { 347 len = rlen; 348 rlen = 0; 349 memcpy(buffer, current, len); 350 instate = 3; 351 curlen = 0; 352 } else { 353 memcpy(buffer, current, len); 354 rlen -= len; 355 current += len; 356 } 357 } else if (instate == 1) { 358 if (len > CHUNK) len = CHUNK; 359 memcpy(buffer, &filling[0], len); 360 curlen += len; 361 if (curlen >= maxlen) { 362 rlen = strlen(crazy) - crazy_indx; 363 current = &crazy[crazy_indx]; 364 instate = 2; 365 } 366 } else 367 len = 0; 368 return (len); 369 } 370 /************************************************************************ 371 * * 372 * Libxml2 specific routines * 373 * * 374 ************************************************************************/ 375 376 static int nb_tests = 0; 377 static int nb_errors = 0; 378 static int nb_leaks = 0; 379 static int extraMemoryFromResolver = 0; 380 381 /* 382 * We need to trap calls to the resolver to not account memory for the catalog 383 * which is shared to the current running test. We also don't want to have 384 * network downloads modifying tests. 385 */ 386 static xmlParserInputPtr 387 testExternalEntityLoader(const char *URL, const char *ID, 388 xmlParserCtxtPtr ctxt) { 389 xmlParserInputPtr ret; 390 int memused = xmlMemUsed(); 391 392 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); 393 extraMemoryFromResolver += xmlMemUsed() - memused; 394 395 return(ret); 396 } 397 398 /* 399 * Trapping the error messages at the generic level to grab the equivalent of 400 * stderr messages on CLI tools. 401 */ 402 static char testErrors[32769]; 403 static int testErrorsSize = 0; 404 405 static void XMLCDECL 406 channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { 407 va_list args; 408 int res; 409 410 if (testErrorsSize >= 32768) 411 return; 412 va_start(args, msg); 413 res = vsnprintf(&testErrors[testErrorsSize], 414 32768 - testErrorsSize, 415 msg, args); 416 va_end(args); 417 if (testErrorsSize + res >= 32768) { 418 /* buffer is full */ 419 testErrorsSize = 32768; 420 testErrors[testErrorsSize] = 0; 421 } else { 422 testErrorsSize += res; 423 } 424 testErrors[testErrorsSize] = 0; 425 } 426 427 /** 428 * xmlParserPrintFileContext: 429 * @input: an xmlParserInputPtr input 430 * 431 * Displays current context within the input content for error tracking 432 */ 433 434 static void 435 xmlParserPrintFileContextInternal(xmlParserInputPtr input , 436 xmlGenericErrorFunc chanl, void *data ) { 437 const xmlChar *cur, *base; 438 unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */ 439 xmlChar content[81]; /* space for 80 chars + line terminator */ 440 xmlChar *ctnt; 441 442 if (input == NULL) return; 443 cur = input->cur; 444 base = input->base; 445 /* skip backwards over any end-of-lines */ 446 while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) { 447 cur--; 448 } 449 n = 0; 450 /* search backwards for beginning-of-line (to max buff size) */ 451 while ((n++ < (sizeof(content)-1)) && (cur > base) && 452 (*(cur) != '\n') && (*(cur) != '\r')) 453 cur--; 454 if ((*(cur) == '\n') || (*(cur) == '\r')) cur++; 455 /* calculate the error position in terms of the current position */ 456 col = input->cur - cur; 457 /* search forward for end-of-line (to max buff size) */ 458 n = 0; 459 ctnt = content; 460 /* copy selected text to our buffer */ 461 while ((*cur != 0) && (*(cur) != '\n') && 462 (*(cur) != '\r') && (n < sizeof(content)-1)) { 463 *ctnt++ = *cur++; 464 n++; 465 } 466 *ctnt = 0; 467 /* print out the selected text */ 468 chanl(data ,"%s\n", content); 469 /* create blank line with problem pointer */ 470 n = 0; 471 ctnt = content; 472 /* (leave buffer space for pointer + line terminator) */ 473 while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) { 474 if (*(ctnt) != '\t') 475 *(ctnt) = ' '; 476 ctnt++; 477 } 478 *ctnt++ = '^'; 479 *ctnt = 0; 480 chanl(data ,"%s\n", content); 481 } 482 483 static void 484 testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) { 485 char *file = NULL; 486 int line = 0; 487 int code = -1; 488 int domain; 489 void *data = NULL; 490 const char *str; 491 const xmlChar *name = NULL; 492 xmlNodePtr node; 493 xmlErrorLevel level; 494 xmlParserInputPtr input = NULL; 495 xmlParserInputPtr cur = NULL; 496 xmlParserCtxtPtr ctxt = NULL; 497 498 if (err == NULL) 499 return; 500 501 file = err->file; 502 line = err->line; 503 code = err->code; 504 domain = err->domain; 505 level = err->level; 506 node = err->node; 507 if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || 508 (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || 509 (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { 510 ctxt = err->ctxt; 511 } 512 str = err->message; 513 514 if (code == XML_ERR_OK) 515 return; 516 517 if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) 518 name = node->name; 519 520 /* 521 * Maintain the compatibility with the legacy error handling 522 */ 523 if (ctxt != NULL) { 524 input = ctxt->input; 525 if ((input != NULL) && (input->filename == NULL) && 526 (ctxt->inputNr > 1)) { 527 cur = input; 528 input = ctxt->inputTab[ctxt->inputNr - 2]; 529 } 530 if (input != NULL) { 531 if (input->filename) 532 channel(data, "%s:%d: ", input->filename, input->line); 533 else if ((line != 0) && (domain == XML_FROM_PARSER)) 534 channel(data, "Entity: line %d: ", input->line); 535 } 536 } else { 537 if (file != NULL) 538 channel(data, "%s:%d: ", file, line); 539 else if ((line != 0) && (domain == XML_FROM_PARSER)) 540 channel(data, "Entity: line %d: ", line); 541 } 542 if (name != NULL) { 543 channel(data, "element %s: ", name); 544 } 545 if (code == XML_ERR_OK) 546 return; 547 switch (domain) { 548 case XML_FROM_PARSER: 549 channel(data, "parser "); 550 break; 551 case XML_FROM_NAMESPACE: 552 channel(data, "namespace "); 553 break; 554 case XML_FROM_DTD: 555 case XML_FROM_VALID: 556 channel(data, "validity "); 557 break; 558 case XML_FROM_HTML: 559 channel(data, "HTML parser "); 560 break; 561 case XML_FROM_MEMORY: 562 channel(data, "memory "); 563 break; 564 case XML_FROM_OUTPUT: 565 channel(data, "output "); 566 break; 567 case XML_FROM_IO: 568 channel(data, "I/O "); 569 break; 570 case XML_FROM_XINCLUDE: 571 channel(data, "XInclude "); 572 break; 573 case XML_FROM_XPATH: 574 channel(data, "XPath "); 575 break; 576 case XML_FROM_XPOINTER: 577 channel(data, "parser "); 578 break; 579 case XML_FROM_REGEXP: 580 channel(data, "regexp "); 581 break; 582 case XML_FROM_MODULE: 583 channel(data, "module "); 584 break; 585 case XML_FROM_SCHEMASV: 586 channel(data, "Schemas validity "); 587 break; 588 case XML_FROM_SCHEMASP: 589 channel(data, "Schemas parser "); 590 break; 591 case XML_FROM_RELAXNGP: 592 channel(data, "Relax-NG parser "); 593 break; 594 case XML_FROM_RELAXNGV: 595 channel(data, "Relax-NG validity "); 596 break; 597 case XML_FROM_CATALOG: 598 channel(data, "Catalog "); 599 break; 600 case XML_FROM_C14N: 601 channel(data, "C14N "); 602 break; 603 case XML_FROM_XSLT: 604 channel(data, "XSLT "); 605 break; 606 default: 607 break; 608 } 609 if (code == XML_ERR_OK) 610 return; 611 switch (level) { 612 case XML_ERR_NONE: 613 channel(data, ": "); 614 break; 615 case XML_ERR_WARNING: 616 channel(data, "warning : "); 617 break; 618 case XML_ERR_ERROR: 619 channel(data, "error : "); 620 break; 621 case XML_ERR_FATAL: 622 channel(data, "error : "); 623 break; 624 } 625 if (code == XML_ERR_OK) 626 return; 627 if (str != NULL) { 628 int len; 629 len = xmlStrlen((const xmlChar *)str); 630 if ((len > 0) && (str[len - 1] != '\n')) 631 channel(data, "%s\n", str); 632 else 633 channel(data, "%s", str); 634 } else { 635 channel(data, "%s\n", "out of memory error"); 636 } 637 if (code == XML_ERR_OK) 638 return; 639 640 if (ctxt != NULL) { 641 xmlParserPrintFileContextInternal(input, channel, data); 642 if (cur != NULL) { 643 if (cur->filename) 644 channel(data, "%s:%d: \n", cur->filename, cur->line); 645 else if ((line != 0) && (domain == XML_FROM_PARSER)) 646 channel(data, "Entity: line %d: \n", cur->line); 647 xmlParserPrintFileContextInternal(cur, channel, data); 648 } 649 } 650 if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) && 651 (err->int1 < 100) && 652 (err->int1 < xmlStrlen((const xmlChar *)err->str1))) { 653 xmlChar buf[150]; 654 int i; 655 656 channel(data, "%s\n", err->str1); 657 for (i=0;i < err->int1;i++) 658 buf[i] = ' '; 659 buf[i++] = '^'; 660 buf[i] = 0; 661 channel(data, "%s\n", buf); 662 } 663 } 664 665 static void 666 initializeLibxml2(void) { 667 xmlGetWarningsDefaultValue = 0; 668 xmlPedanticParserDefault(0); 669 670 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); 671 xmlInitParser(); 672 xmlSetExternalEntityLoader(testExternalEntityLoader); 673 xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); 674 /* 675 * register the new I/O handlers 676 */ 677 if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen, 678 hugeRead, hugeClose) < 0) { 679 fprintf(stderr, "failed to register Huge handlers\n"); 680 exit(1); 681 } 682 if (xmlRegisterInputCallbacks(crazyMatch, crazyOpen, 683 crazyRead, crazyClose) < 0) { 684 fprintf(stderr, "failed to register Crazy handlers\n"); 685 exit(1); 686 } 687 } 688 689 /************************************************************************ 690 * * 691 * SAX empty callbacks * 692 * * 693 ************************************************************************/ 694 695 unsigned long callbacks = 0; 696 697 /** 698 * isStandaloneCallback: 699 * @ctxt: An XML parser context 700 * 701 * Is this document tagged standalone ? 702 * 703 * Returns 1 if true 704 */ 705 static int 706 isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED) 707 { 708 callbacks++; 709 return (0); 710 } 711 712 /** 713 * hasInternalSubsetCallback: 714 * @ctxt: An XML parser context 715 * 716 * Does this document has an internal subset 717 * 718 * Returns 1 if true 719 */ 720 static int 721 hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED) 722 { 723 callbacks++; 724 return (0); 725 } 726 727 /** 728 * hasExternalSubsetCallback: 729 * @ctxt: An XML parser context 730 * 731 * Does this document has an external subset 732 * 733 * Returns 1 if true 734 */ 735 static int 736 hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED) 737 { 738 callbacks++; 739 return (0); 740 } 741 742 /** 743 * internalSubsetCallback: 744 * @ctxt: An XML parser context 745 * 746 * Does this document has an internal subset 747 */ 748 static void 749 internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED, 750 const xmlChar * name ATTRIBUTE_UNUSED, 751 const xmlChar * ExternalID ATTRIBUTE_UNUSED, 752 const xmlChar * SystemID ATTRIBUTE_UNUSED) 753 { 754 callbacks++; 755 return; 756 } 757 758 /** 759 * externalSubsetCallback: 760 * @ctxt: An XML parser context 761 * 762 * Does this document has an external subset 763 */ 764 static void 765 externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED, 766 const xmlChar * name ATTRIBUTE_UNUSED, 767 const xmlChar * ExternalID ATTRIBUTE_UNUSED, 768 const xmlChar * SystemID ATTRIBUTE_UNUSED) 769 { 770 callbacks++; 771 return; 772 } 773 774 /** 775 * resolveEntityCallback: 776 * @ctxt: An XML parser context 777 * @publicId: The public ID of the entity 778 * @systemId: The system ID of the entity 779 * 780 * Special entity resolver, better left to the parser, it has 781 * more context than the application layer. 782 * The default behaviour is to NOT resolve the entities, in that case 783 * the ENTITY_REF nodes are built in the structure (and the parameter 784 * values). 785 * 786 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. 787 */ 788 static xmlParserInputPtr 789 resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED, 790 const xmlChar * publicId ATTRIBUTE_UNUSED, 791 const xmlChar * systemId ATTRIBUTE_UNUSED) 792 { 793 callbacks++; 794 return (NULL); 795 } 796 797 /** 798 * getEntityCallback: 799 * @ctxt: An XML parser context 800 * @name: The entity name 801 * 802 * Get an entity by name 803 * 804 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. 805 */ 806 static xmlEntityPtr 807 getEntityCallback(void *ctx ATTRIBUTE_UNUSED, 808 const xmlChar * name ATTRIBUTE_UNUSED) 809 { 810 callbacks++; 811 return (NULL); 812 } 813 814 /** 815 * getParameterEntityCallback: 816 * @ctxt: An XML parser context 817 * @name: The entity name 818 * 819 * Get a parameter entity by name 820 * 821 * Returns the xmlParserInputPtr 822 */ 823 static xmlEntityPtr 824 getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED, 825 const xmlChar * name ATTRIBUTE_UNUSED) 826 { 827 callbacks++; 828 return (NULL); 829 } 830 831 832 /** 833 * entityDeclCallback: 834 * @ctxt: An XML parser context 835 * @name: the entity name 836 * @type: the entity type 837 * @publicId: The public ID of the entity 838 * @systemId: The system ID of the entity 839 * @content: the entity value (without processing). 840 * 841 * An entity definition has been parsed 842 */ 843 static void 844 entityDeclCallback(void *ctx ATTRIBUTE_UNUSED, 845 const xmlChar * name ATTRIBUTE_UNUSED, 846 int type ATTRIBUTE_UNUSED, 847 const xmlChar * publicId ATTRIBUTE_UNUSED, 848 const xmlChar * systemId ATTRIBUTE_UNUSED, 849 xmlChar * content ATTRIBUTE_UNUSED) 850 { 851 callbacks++; 852 return; 853 } 854 855 /** 856 * attributeDeclCallback: 857 * @ctxt: An XML parser context 858 * @name: the attribute name 859 * @type: the attribute type 860 * 861 * An attribute definition has been parsed 862 */ 863 static void 864 attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED, 865 const xmlChar * elem ATTRIBUTE_UNUSED, 866 const xmlChar * name ATTRIBUTE_UNUSED, 867 int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED, 868 const xmlChar * defaultValue ATTRIBUTE_UNUSED, 869 xmlEnumerationPtr tree ATTRIBUTE_UNUSED) 870 { 871 callbacks++; 872 return; 873 } 874 875 /** 876 * elementDeclCallback: 877 * @ctxt: An XML parser context 878 * @name: the element name 879 * @type: the element type 880 * @content: the element value (without processing). 881 * 882 * An element definition has been parsed 883 */ 884 static void 885 elementDeclCallback(void *ctx ATTRIBUTE_UNUSED, 886 const xmlChar * name ATTRIBUTE_UNUSED, 887 int type ATTRIBUTE_UNUSED, 888 xmlElementContentPtr content ATTRIBUTE_UNUSED) 889 { 890 callbacks++; 891 return; 892 } 893 894 /** 895 * notationDeclCallback: 896 * @ctxt: An XML parser context 897 * @name: The name of the notation 898 * @publicId: The public ID of the entity 899 * @systemId: The system ID of the entity 900 * 901 * What to do when a notation declaration has been parsed. 902 */ 903 static void 904 notationDeclCallback(void *ctx ATTRIBUTE_UNUSED, 905 const xmlChar * name ATTRIBUTE_UNUSED, 906 const xmlChar * publicId ATTRIBUTE_UNUSED, 907 const xmlChar * systemId ATTRIBUTE_UNUSED) 908 { 909 callbacks++; 910 return; 911 } 912 913 /** 914 * unparsedEntityDeclCallback: 915 * @ctxt: An XML parser context 916 * @name: The name of the entity 917 * @publicId: The public ID of the entity 918 * @systemId: The system ID of the entity 919 * @notationName: the name of the notation 920 * 921 * What to do when an unparsed entity declaration is parsed 922 */ 923 static void 924 unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED, 925 const xmlChar * name ATTRIBUTE_UNUSED, 926 const xmlChar * publicId ATTRIBUTE_UNUSED, 927 const xmlChar * systemId ATTRIBUTE_UNUSED, 928 const xmlChar * notationName ATTRIBUTE_UNUSED) 929 { 930 callbacks++; 931 return; 932 } 933 934 /** 935 * setDocumentLocatorCallback: 936 * @ctxt: An XML parser context 937 * @loc: A SAX Locator 938 * 939 * Receive the document locator at startup, actually xmlDefaultSAXLocator 940 * Everything is available on the context, so this is useless in our case. 941 */ 942 static void 943 setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED, 944 xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED) 945 { 946 callbacks++; 947 return; 948 } 949 950 /** 951 * startDocumentCallback: 952 * @ctxt: An XML parser context 953 * 954 * called when the document start being processed. 955 */ 956 static void 957 startDocumentCallback(void *ctx ATTRIBUTE_UNUSED) 958 { 959 callbacks++; 960 return; 961 } 962 963 /** 964 * endDocumentCallback: 965 * @ctxt: An XML parser context 966 * 967 * called when the document end has been detected. 968 */ 969 static void 970 endDocumentCallback(void *ctx ATTRIBUTE_UNUSED) 971 { 972 callbacks++; 973 return; 974 } 975 976 #if 0 977 /** 978 * startElementCallback: 979 * @ctxt: An XML parser context 980 * @name: The element name 981 * 982 * called when an opening tag has been processed. 983 */ 984 static void 985 startElementCallback(void *ctx ATTRIBUTE_UNUSED, 986 const xmlChar * name ATTRIBUTE_UNUSED, 987 const xmlChar ** atts ATTRIBUTE_UNUSED) 988 { 989 callbacks++; 990 return; 991 } 992 993 /** 994 * endElementCallback: 995 * @ctxt: An XML parser context 996 * @name: The element name 997 * 998 * called when the end of an element has been detected. 999 */ 1000 static void 1001 endElementCallback(void *ctx ATTRIBUTE_UNUSED, 1002 const xmlChar * name ATTRIBUTE_UNUSED) 1003 { 1004 callbacks++; 1005 return; 1006 } 1007 #endif 1008 1009 /** 1010 * charactersCallback: 1011 * @ctxt: An XML parser context 1012 * @ch: a xmlChar string 1013 * @len: the number of xmlChar 1014 * 1015 * receiving some chars from the parser. 1016 * Question: how much at a time ??? 1017 */ 1018 static void 1019 charactersCallback(void *ctx ATTRIBUTE_UNUSED, 1020 const xmlChar * ch ATTRIBUTE_UNUSED, 1021 int len ATTRIBUTE_UNUSED) 1022 { 1023 callbacks++; 1024 return; 1025 } 1026 1027 /** 1028 * referenceCallback: 1029 * @ctxt: An XML parser context 1030 * @name: The entity name 1031 * 1032 * called when an entity reference is detected. 1033 */ 1034 static void 1035 referenceCallback(void *ctx ATTRIBUTE_UNUSED, 1036 const xmlChar * name ATTRIBUTE_UNUSED) 1037 { 1038 callbacks++; 1039 return; 1040 } 1041 1042 /** 1043 * ignorableWhitespaceCallback: 1044 * @ctxt: An XML parser context 1045 * @ch: a xmlChar string 1046 * @start: the first char in the string 1047 * @len: the number of xmlChar 1048 * 1049 * receiving some ignorable whitespaces from the parser. 1050 * Question: how much at a time ??? 1051 */ 1052 static void 1053 ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED, 1054 const xmlChar * ch ATTRIBUTE_UNUSED, 1055 int len ATTRIBUTE_UNUSED) 1056 { 1057 callbacks++; 1058 return; 1059 } 1060 1061 /** 1062 * processingInstructionCallback: 1063 * @ctxt: An XML parser context 1064 * @target: the target name 1065 * @data: the PI data's 1066 * @len: the number of xmlChar 1067 * 1068 * A processing instruction has been parsed. 1069 */ 1070 static void 1071 processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED, 1072 const xmlChar * target ATTRIBUTE_UNUSED, 1073 const xmlChar * data ATTRIBUTE_UNUSED) 1074 { 1075 callbacks++; 1076 return; 1077 } 1078 1079 /** 1080 * cdataBlockCallback: 1081 * @ctx: the user data (XML parser context) 1082 * @value: The pcdata content 1083 * @len: the block length 1084 * 1085 * called when a pcdata block has been parsed 1086 */ 1087 static void 1088 cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED, 1089 const xmlChar * value ATTRIBUTE_UNUSED, 1090 int len ATTRIBUTE_UNUSED) 1091 { 1092 callbacks++; 1093 return; 1094 } 1095 1096 /** 1097 * commentCallback: 1098 * @ctxt: An XML parser context 1099 * @value: the comment content 1100 * 1101 * A comment has been parsed. 1102 */ 1103 static void 1104 commentCallback(void *ctx ATTRIBUTE_UNUSED, 1105 const xmlChar * value ATTRIBUTE_UNUSED) 1106 { 1107 callbacks++; 1108 return; 1109 } 1110 1111 /** 1112 * warningCallback: 1113 * @ctxt: An XML parser context 1114 * @msg: the message to display/transmit 1115 * @...: extra parameters for the message display 1116 * 1117 * Display and format a warning messages, gives file, line, position and 1118 * extra parameters. 1119 */ 1120 static void XMLCDECL 1121 warningCallback(void *ctx ATTRIBUTE_UNUSED, 1122 const char *msg ATTRIBUTE_UNUSED, ...) 1123 { 1124 callbacks++; 1125 return; 1126 } 1127 1128 /** 1129 * errorCallback: 1130 * @ctxt: An XML parser context 1131 * @msg: the message to display/transmit 1132 * @...: extra parameters for the message display 1133 * 1134 * Display and format a error messages, gives file, line, position and 1135 * extra parameters. 1136 */ 1137 static void XMLCDECL 1138 errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED, 1139 ...) 1140 { 1141 callbacks++; 1142 return; 1143 } 1144 1145 /** 1146 * fatalErrorCallback: 1147 * @ctxt: An XML parser context 1148 * @msg: the message to display/transmit 1149 * @...: extra parameters for the message display 1150 * 1151 * Display and format a fatalError messages, gives file, line, position and 1152 * extra parameters. 1153 */ 1154 static void XMLCDECL 1155 fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED, 1156 const char *msg ATTRIBUTE_UNUSED, ...) 1157 { 1158 return; 1159 } 1160 1161 1162 /* 1163 * SAX2 specific callbacks 1164 */ 1165 1166 /** 1167 * startElementNsCallback: 1168 * @ctxt: An XML parser context 1169 * @name: The element name 1170 * 1171 * called when an opening tag has been processed. 1172 */ 1173 static void 1174 startElementNsCallback(void *ctx ATTRIBUTE_UNUSED, 1175 const xmlChar * localname ATTRIBUTE_UNUSED, 1176 const xmlChar * prefix ATTRIBUTE_UNUSED, 1177 const xmlChar * URI ATTRIBUTE_UNUSED, 1178 int nb_namespaces ATTRIBUTE_UNUSED, 1179 const xmlChar ** namespaces ATTRIBUTE_UNUSED, 1180 int nb_attributes ATTRIBUTE_UNUSED, 1181 int nb_defaulted ATTRIBUTE_UNUSED, 1182 const xmlChar ** attributes ATTRIBUTE_UNUSED) 1183 { 1184 callbacks++; 1185 return; 1186 } 1187 1188 /** 1189 * endElementCallback: 1190 * @ctxt: An XML parser context 1191 * @name: The element name 1192 * 1193 * called when the end of an element has been detected. 1194 */ 1195 static void 1196 endElementNsCallback(void *ctx ATTRIBUTE_UNUSED, 1197 const xmlChar * localname ATTRIBUTE_UNUSED, 1198 const xmlChar * prefix ATTRIBUTE_UNUSED, 1199 const xmlChar * URI ATTRIBUTE_UNUSED) 1200 { 1201 callbacks++; 1202 return; 1203 } 1204 1205 static xmlSAXHandler callbackSAX2HandlerStruct = { 1206 internalSubsetCallback, 1207 isStandaloneCallback, 1208 hasInternalSubsetCallback, 1209 hasExternalSubsetCallback, 1210 resolveEntityCallback, 1211 getEntityCallback, 1212 entityDeclCallback, 1213 notationDeclCallback, 1214 attributeDeclCallback, 1215 elementDeclCallback, 1216 unparsedEntityDeclCallback, 1217 setDocumentLocatorCallback, 1218 startDocumentCallback, 1219 endDocumentCallback, 1220 NULL, 1221 NULL, 1222 referenceCallback, 1223 charactersCallback, 1224 ignorableWhitespaceCallback, 1225 processingInstructionCallback, 1226 commentCallback, 1227 warningCallback, 1228 errorCallback, 1229 fatalErrorCallback, 1230 getParameterEntityCallback, 1231 cdataBlockCallback, 1232 externalSubsetCallback, 1233 XML_SAX2_MAGIC, 1234 NULL, 1235 startElementNsCallback, 1236 endElementNsCallback, 1237 NULL 1238 }; 1239 1240 static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct; 1241 1242 /************************************************************************ 1243 * * 1244 * The tests front-ends * 1245 * * 1246 ************************************************************************/ 1247 1248 /** 1249 * readerTest: 1250 * @filename: the file to parse 1251 * @max_size: size of the limit to test 1252 * @options: parsing options 1253 * @fail: should a failure be reported 1254 * 1255 * Parse a memory generated file using SAX 1256 * 1257 * Returns 0 in case of success, an error code otherwise 1258 */ 1259 static int 1260 saxTest(const char *filename, size_t limit, int options, int fail) { 1261 int res = 0; 1262 xmlParserCtxtPtr ctxt; 1263 xmlDocPtr doc; 1264 xmlSAXHandlerPtr old_sax; 1265 1266 nb_tests++; 1267 1268 maxlen = limit; 1269 ctxt = xmlNewParserCtxt(); 1270 if (ctxt == NULL) { 1271 fprintf(stderr, "Failed to create parser context\n"); 1272 return(1); 1273 } 1274 old_sax = ctxt->sax; 1275 ctxt->sax = callbackSAX2Handler; 1276 ctxt->userData = NULL; 1277 doc = xmlCtxtReadFile(ctxt, filename, NULL, options); 1278 1279 if (doc != NULL) { 1280 fprintf(stderr, "SAX parsing generated a document !\n"); 1281 xmlFreeDoc(doc); 1282 res = 0; 1283 } else if (ctxt->wellFormed == 0) { 1284 if (fail) 1285 res = 0; 1286 else { 1287 fprintf(stderr, "Failed to parse '%s' %lu\n", filename, 1288 (unsigned long) limit); 1289 res = 1; 1290 } 1291 } else { 1292 if (fail) { 1293 fprintf(stderr, "Failed to get failure for '%s' %lu\n", 1294 filename, (unsigned long) limit); 1295 res = 1; 1296 } else 1297 res = 0; 1298 } 1299 ctxt->sax = old_sax; 1300 xmlFreeParserCtxt(ctxt); 1301 1302 return(res); 1303 } 1304 #ifdef LIBXML_READER_ENABLED 1305 /** 1306 * readerTest: 1307 * @filename: the file to parse 1308 * @max_size: size of the limit to test 1309 * @options: parsing options 1310 * @fail: should a failure be reported 1311 * 1312 * Parse a memory generated file using the xmlReader 1313 * 1314 * Returns 0 in case of success, an error code otherwise 1315 */ 1316 static int 1317 readerTest(const char *filename, size_t limit, int options, int fail) { 1318 xmlTextReaderPtr reader; 1319 int res = 0; 1320 int ret; 1321 1322 nb_tests++; 1323 1324 maxlen = limit; 1325 reader = xmlReaderForFile(filename , NULL, options); 1326 if (reader == NULL) { 1327 fprintf(stderr, "Failed to open '%s' test\n", filename); 1328 return(1); 1329 } 1330 ret = xmlTextReaderRead(reader); 1331 while (ret == 1) { 1332 ret = xmlTextReaderRead(reader); 1333 } 1334 if (ret != 0) { 1335 if (fail) 1336 res = 0; 1337 else { 1338 if (strncmp(filename, "crazy:", 6) == 0) 1339 fprintf(stderr, "Failed to parse '%s' %u\n", 1340 filename, crazy_indx); 1341 else 1342 fprintf(stderr, "Failed to parse '%s' %lu\n", 1343 filename, (unsigned long) limit); 1344 res = 1; 1345 } 1346 } else { 1347 if (fail) { 1348 if (strncmp(filename, "crazy:", 6) == 0) 1349 fprintf(stderr, "Failed to get failure for '%s' %u\n", 1350 filename, crazy_indx); 1351 else 1352 fprintf(stderr, "Failed to get failure for '%s' %lu\n", 1353 filename, (unsigned long) limit); 1354 res = 1; 1355 } else 1356 res = 0; 1357 } 1358 if (timeout) 1359 res = 1; 1360 xmlFreeTextReader(reader); 1361 1362 return(res); 1363 } 1364 #endif 1365 1366 /************************************************************************ 1367 * * 1368 * Tests descriptions * 1369 * * 1370 ************************************************************************/ 1371 1372 typedef int (*functest) (const char *filename, size_t limit, int options, 1373 int fail); 1374 1375 typedef struct limitDesc limitDesc; 1376 typedef limitDesc *limitDescPtr; 1377 struct limitDesc { 1378 const char *name; /* the huge generator name */ 1379 size_t limit; /* the limit to test */ 1380 int options; /* extra parser options */ 1381 int fail; /* whether the test should fail */ 1382 }; 1383 1384 static limitDesc limitDescriptions[] = { 1385 /* max length of a text node in content */ 1386 {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0}, 1387 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1}, 1388 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0}, 1389 /* max length of a text node in content */ 1390 {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0}, 1391 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1}, 1392 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0}, 1393 /* max length of a comment node */ 1394 {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0}, 1395 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1}, 1396 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0}, 1397 /* max length of a PI node */ 1398 {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0}, 1399 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1}, 1400 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0}, 1401 }; 1402 1403 typedef struct testDesc testDesc; 1404 typedef testDesc *testDescPtr; 1405 struct testDesc { 1406 const char *desc; /* descripton of the test */ 1407 functest func; /* function implementing the test */ 1408 }; 1409 1410 static 1411 testDesc testDescriptions[] = { 1412 { "Parsing of huge files with the sax parser", saxTest}, 1413 /* { "Parsing of huge files with the tree parser", treeTest}, */ 1414 #ifdef LIBXML_READER_ENABLED 1415 { "Parsing of huge files with the reader", readerTest}, 1416 #endif 1417 {NULL, NULL} 1418 }; 1419 1420 typedef struct testException testException; 1421 typedef testException *testExceptionPtr; 1422 struct testException { 1423 unsigned int test; /* the parser test number */ 1424 unsigned int limit; /* the limit test number */ 1425 int fail; /* new fail value or -1*/ 1426 size_t size; /* new limit value or 0 */ 1427 }; 1428 1429 static 1430 testException testExceptions[] = { 1431 /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */ 1432 { 0, 1, 0, 0}, 1433 }; 1434 1435 static int 1436 launchTests(testDescPtr tst, unsigned int test) { 1437 int res = 0, err = 0; 1438 unsigned int i, j; 1439 size_t limit; 1440 int fail; 1441 1442 if (tst == NULL) return(-1); 1443 1444 for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) { 1445 limit = limitDescriptions[i].limit; 1446 fail = limitDescriptions[i].fail; 1447 /* 1448 * Handle exceptions if any 1449 */ 1450 for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) { 1451 if ((testExceptions[j].test == test) && 1452 (testExceptions[j].limit == i)) { 1453 if (testExceptions[j].fail != -1) 1454 fail = testExceptions[j].fail; 1455 if (testExceptions[j].size != 0) 1456 limit = testExceptions[j].size; 1457 break; 1458 } 1459 } 1460 res = tst->func(limitDescriptions[i].name, limit, 1461 limitDescriptions[i].options, fail); 1462 if (res != 0) { 1463 nb_errors++; 1464 err++; 1465 } 1466 } 1467 return(err); 1468 } 1469 1470 1471 static int 1472 runtest(unsigned int i) { 1473 int ret = 0, res; 1474 int old_errors, old_tests, old_leaks; 1475 1476 old_errors = nb_errors; 1477 old_tests = nb_tests; 1478 old_leaks = nb_leaks; 1479 if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL)) 1480 printf("## %s\n", testDescriptions[i].desc); 1481 res = launchTests(&testDescriptions[i], i); 1482 if (res != 0) 1483 ret++; 1484 if (verbose) { 1485 if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) 1486 printf("Ran %d tests, no errors\n", nb_tests - old_tests); 1487 else 1488 printf("Ran %d tests, %d errors, %d leaks\n", 1489 nb_tests - old_tests, 1490 nb_errors - old_errors, 1491 nb_leaks - old_leaks); 1492 } 1493 return(ret); 1494 } 1495 1496 static int 1497 launchCrazySAX(unsigned int test, int fail) { 1498 int res = 0, err = 0; 1499 1500 crazy_indx = test; 1501 1502 res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail); 1503 if (res != 0) { 1504 nb_errors++; 1505 err++; 1506 } 1507 if (tests_quiet == 0) 1508 fprintf(stderr, "%c", crazy[test]); 1509 1510 return(err); 1511 } 1512 1513 #ifdef LIBXML_READER_ENABLED 1514 static int 1515 launchCrazy(unsigned int test, int fail) { 1516 int res = 0, err = 0; 1517 1518 crazy_indx = test; 1519 1520 res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail); 1521 if (res != 0) { 1522 nb_errors++; 1523 err++; 1524 } 1525 if (tests_quiet == 0) 1526 fprintf(stderr, "%c", crazy[test]); 1527 1528 return(err); 1529 } 1530 #endif 1531 1532 static int get_crazy_fail(int test) { 1533 /* 1534 * adding 1000000 of character 'a' leads to parser failure mostly 1535 * everywhere except in those special spots. Need to be updated 1536 * each time crazy is updated 1537 */ 1538 int fail = 1; 1539 if ((test == 44) || /* PI in Misc */ 1540 ((test >= 50) && (test <= 55)) || /* Comment in Misc */ 1541 (test == 79) || /* PI in DTD */ 1542 ((test >= 85) && (test <= 90)) || /* Comment in DTD */ 1543 (test == 154) || /* PI in Misc */ 1544 ((test >= 160) && (test <= 165)) || /* Comment in Misc */ 1545 ((test >= 178) && (test <= 181)) || /* attribute value */ 1546 (test == 183) || /* Text */ 1547 (test == 189) || /* PI in Content */ 1548 (test == 191) || /* Text */ 1549 ((test >= 195) && (test <= 200)) || /* Comment in Content */ 1550 ((test >= 203) && (test <= 206)) || /* Text */ 1551 (test == 215) || (test == 216) || /* in CDATA */ 1552 (test == 219) || /* Text */ 1553 (test == 231) || /* PI in Misc */ 1554 ((test >= 237) && (test <= 242))) /* Comment in Misc */ 1555 fail = 0; 1556 return(fail); 1557 } 1558 1559 static int 1560 runcrazy(void) { 1561 int ret = 0, res = 0; 1562 int old_errors, old_tests, old_leaks; 1563 unsigned int i; 1564 1565 old_errors = nb_errors; 1566 old_tests = nb_tests; 1567 old_leaks = nb_leaks; 1568 1569 #ifdef LIBXML_READER_ENABLED 1570 if (tests_quiet == 0) { 1571 printf("## Crazy tests on reader\n"); 1572 } 1573 for (i = 0;i < strlen(crazy);i++) { 1574 res += launchCrazy(i, get_crazy_fail(i)); 1575 if (res != 0) 1576 ret++; 1577 } 1578 #endif 1579 1580 if (tests_quiet == 0) { 1581 printf("\n## Crazy tests on SAX\n"); 1582 } 1583 for (i = 0;i < strlen(crazy);i++) { 1584 res += launchCrazySAX(i, get_crazy_fail(i)); 1585 if (res != 0) 1586 ret++; 1587 } 1588 if (tests_quiet == 0) 1589 fprintf(stderr, "\n"); 1590 if (verbose) { 1591 if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) 1592 printf("Ran %d tests, no errors\n", nb_tests - old_tests); 1593 else 1594 printf("Ran %d tests, %d errors, %d leaks\n", 1595 nb_tests - old_tests, 1596 nb_errors - old_errors, 1597 nb_leaks - old_leaks); 1598 } 1599 return(ret); 1600 } 1601 1602 1603 int 1604 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { 1605 int i, a, ret = 0; 1606 int subset = 0; 1607 1608 fillFilling(); 1609 initializeLibxml2(); 1610 1611 for (a = 1; a < argc;a++) { 1612 if (!strcmp(argv[a], "-v")) 1613 verbose = 1; 1614 else if (!strcmp(argv[a], "-quiet")) 1615 tests_quiet = 1; 1616 else if (!strcmp(argv[a], "-crazy")) 1617 subset = 1; 1618 } 1619 if (subset == 0) { 1620 for (i = 0; testDescriptions[i].func != NULL; i++) { 1621 ret += runtest(i); 1622 } 1623 } 1624 ret += runcrazy(); 1625 if ((nb_errors == 0) && (nb_leaks == 0)) { 1626 ret = 0; 1627 printf("Total %d tests, no errors\n", 1628 nb_tests); 1629 } else { 1630 ret = 1; 1631 printf("Total %d tests, %d errors, %d leaks\n", 1632 nb_tests, nb_errors, nb_leaks); 1633 } 1634 xmlCleanupParser(); 1635 xmlMemoryDump(); 1636 1637 return(ret); 1638 } 1639