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