1/* 2 * testrecurse.c: C program to run libxml2 regression tests checking entities 3 * recursions 4 * 5 * To compile on Unixes: 6 * cc -o testrecurse `xml2-config --cflags` testrecurse.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 27#include <libxml/parser.h> 28#include <libxml/tree.h> 29#include <libxml/uri.h> 30#ifdef LIBXML_READER_ENABLED 31#include <libxml/xmlreader.h> 32#endif 33 34/* 35 * O_BINARY is just for Windows compatibility - if it isn't defined 36 * on this system, avoid any compilation error 37 */ 38#ifdef O_BINARY 39#define RD_FLAGS O_RDONLY | O_BINARY 40#else 41#define RD_FLAGS O_RDONLY 42#endif 43 44typedef int (*functest) (const char *filename, const char *result, 45 const char *error, int options); 46 47typedef struct testDesc testDesc; 48typedef testDesc *testDescPtr; 49struct testDesc { 50 const char *desc; /* descripton of the test */ 51 functest func; /* function implementing the test */ 52 const char *in; /* glob to path for input files */ 53 const char *out; /* output directory */ 54 const char *suffix;/* suffix for output files */ 55 const char *err; /* suffix for error output files */ 56 int options; /* parser options for the test */ 57}; 58 59static int checkTestFile(const char *filename); 60 61 62#if defined(_WIN32) && !defined(__CYGWIN__) 63 64#include <windows.h> 65#include <io.h> 66 67typedef struct 68{ 69 size_t gl_pathc; /* Count of paths matched so far */ 70 char **gl_pathv; /* List of matched pathnames. */ 71 size_t gl_offs; /* Slots to reserve in 'gl_pathv'. */ 72} glob_t; 73 74#define GLOB_DOOFFS 0 75static int glob(const char *pattern, int flags, 76 int errfunc(const char *epath, int eerrno), 77 glob_t *pglob) { 78 glob_t *ret; 79 WIN32_FIND_DATA FindFileData; 80 HANDLE hFind; 81 unsigned int nb_paths = 0; 82 char directory[500]; 83 int len; 84 85 if ((pattern == NULL) || (pglob == NULL)) return(-1); 86 87 strncpy(directory, pattern, 499); 88 for (len = strlen(directory);len >= 0;len--) { 89 if (directory[len] == '/') { 90 len++; 91 directory[len] = 0; 92 break; 93 } 94 } 95 if (len <= 0) 96 len = 0; 97 98 99 ret = pglob; 100 memset(ret, 0, sizeof(glob_t)); 101 102 hFind = FindFirstFileA(pattern, &FindFileData); 103 if (hFind == INVALID_HANDLE_VALUE) 104 return(0); 105 nb_paths = 20; 106 ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *)); 107 if (ret->gl_pathv == NULL) { 108 FindClose(hFind); 109 return(-1); 110 } 111 strncpy(directory + len, FindFileData.cFileName, 499 - len); 112 ret->gl_pathv[ret->gl_pathc] = strdup(directory); 113 if (ret->gl_pathv[ret->gl_pathc] == NULL) 114 goto done; 115 ret->gl_pathc++; 116 while(FindNextFileA(hFind, &FindFileData)) { 117 if (FindFileData.cFileName[0] == '.') 118 continue; 119 if (ret->gl_pathc + 2 > nb_paths) { 120 char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *)); 121 if (tmp == NULL) 122 break; 123 ret->gl_pathv = tmp; 124 nb_paths *= 2; 125 } 126 strncpy(directory + len, FindFileData.cFileName, 499 - len); 127 ret->gl_pathv[ret->gl_pathc] = strdup(directory); 128 if (ret->gl_pathv[ret->gl_pathc] == NULL) 129 break; 130 ret->gl_pathc++; 131 } 132 ret->gl_pathv[ret->gl_pathc] = NULL; 133 134done: 135 FindClose(hFind); 136 return(0); 137} 138 139 140 141static void globfree(glob_t *pglob) { 142 unsigned int i; 143 if (pglob == NULL) 144 return; 145 146 for (i = 0;i < pglob->gl_pathc;i++) { 147 if (pglob->gl_pathv[i] != NULL) 148 free(pglob->gl_pathv[i]); 149 } 150} 151#define vsnprintf _vsnprintf 152#define snprintf _snprintf 153#else 154#include <glob.h> 155#endif 156 157/************************************************************************ 158 * * 159 * Huge document generator * 160 * * 161 ************************************************************************/ 162 163#include <libxml/xmlIO.h> 164 165 166static const char *start = "<!DOCTYPE foo [\ 167<!ENTITY f 'some internal data'> \ 168<!ENTITY e '&f;&f;'> \ 169<!ENTITY d '&e;&e;'> \ 170]> \ 171<foo>"; 172 173static const char *segment = " <bar>&e; &f; &d;</bar>\n"; 174static const char *finish = "</foo>"; 175 176static int curseg = 0; 177static const char *current; 178static int rlen; 179 180/** 181 * hugeMatch: 182 * @URI: an URI to test 183 * 184 * Check for an huge: query 185 * 186 * Returns 1 if yes and 0 if another Input module should be used 187 */ 188static int 189hugeMatch(const char * URI) { 190 if ((URI != NULL) && (!strncmp(URI, "huge:", 4))) 191 return(1); 192 return(0); 193} 194 195/** 196 * hugeOpen: 197 * @URI: an URI to test 198 * 199 * Return a pointer to the huge: query handler, in this example simply 200 * the current pointer... 201 * 202 * Returns an Input context or NULL in case or error 203 */ 204static void * 205hugeOpen(const char * URI) { 206 if ((URI == NULL) || (strncmp(URI, "huge:", 4))) 207 return(NULL); 208 rlen = strlen(start); 209 current = start; 210 return((void *) current); 211} 212 213/** 214 * hugeClose: 215 * @context: the read context 216 * 217 * Close the huge: query handler 218 * 219 * Returns 0 or -1 in case of error 220 */ 221static int 222hugeClose(void * context) { 223 if (context == NULL) return(-1); 224 return(0); 225} 226 227#define MAX_NODES 1000000 228 229/** 230 * hugeRead: 231 * @context: the read context 232 * @buffer: where to store data 233 * @len: number of bytes to read 234 * 235 * Implement an huge: query read. 236 * 237 * Returns the number of bytes read or -1 in case of error 238 */ 239static int 240hugeRead(void *context, char *buffer, int len) 241{ 242 if ((context == NULL) || (buffer == NULL) || (len < 0)) 243 return (-1); 244 245 if (len >= rlen) { 246 if (curseg >= MAX_NODES + 1) { 247 rlen = 0; 248 return(0); 249 } 250 len = rlen; 251 rlen = 0; 252 memcpy(buffer, current, len); 253 curseg ++; 254 if (curseg == MAX_NODES) { 255 fprintf(stderr, "\n"); 256 rlen = strlen(finish); 257 current = finish; 258 } else { 259 if (curseg % (MAX_NODES / 10) == 0) 260 fprintf(stderr, "."); 261 rlen = strlen(segment); 262 current = segment; 263 } 264 } else { 265 memcpy(buffer, current, len); 266 rlen -= len; 267 current += len; 268 } 269 return (len); 270} 271 272/************************************************************************ 273 * * 274 * Libxml2 specific routines * 275 * * 276 ************************************************************************/ 277 278static int nb_tests = 0; 279static int nb_errors = 0; 280static int nb_leaks = 0; 281static int extraMemoryFromResolver = 0; 282 283static int 284fatalError(void) { 285 fprintf(stderr, "Exitting tests on fatal error\n"); 286 exit(1); 287} 288 289/* 290 * We need to trap calls to the resolver to not account memory for the catalog 291 * which is shared to the current running test. We also don't want to have 292 * network downloads modifying tests. 293 */ 294static xmlParserInputPtr 295testExternalEntityLoader(const char *URL, const char *ID, 296 xmlParserCtxtPtr ctxt) { 297 xmlParserInputPtr ret; 298 299 if (checkTestFile(URL)) { 300 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); 301 } else { 302 int memused = xmlMemUsed(); 303 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); 304 extraMemoryFromResolver += xmlMemUsed() - memused; 305 } 306 307 return(ret); 308} 309 310/* 311 * Trapping the error messages at the generic level to grab the equivalent of 312 * stderr messages on CLI tools. 313 */ 314static char testErrors[32769]; 315static int testErrorsSize = 0; 316 317static void XMLCDECL 318channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { 319 va_list args; 320 int res; 321 322 if (testErrorsSize >= 32768) 323 return; 324 va_start(args, msg); 325 res = vsnprintf(&testErrors[testErrorsSize], 326 32768 - testErrorsSize, 327 msg, args); 328 va_end(args); 329 if (testErrorsSize + res >= 32768) { 330 /* buffer is full */ 331 testErrorsSize = 32768; 332 testErrors[testErrorsSize] = 0; 333 } else { 334 testErrorsSize += res; 335 } 336 testErrors[testErrorsSize] = 0; 337} 338 339/** 340 * xmlParserPrintFileContext: 341 * @input: an xmlParserInputPtr input 342 * 343 * Displays current context within the input content for error tracking 344 */ 345 346static void 347xmlParserPrintFileContextInternal(xmlParserInputPtr input , 348 xmlGenericErrorFunc chanl, void *data ) { 349 const xmlChar *cur, *base; 350 unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */ 351 xmlChar content[81]; /* space for 80 chars + line terminator */ 352 xmlChar *ctnt; 353 354 if (input == NULL) return; 355 cur = input->cur; 356 base = input->base; 357 /* skip backwards over any end-of-lines */ 358 while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) { 359 cur--; 360 } 361 n = 0; 362 /* search backwards for beginning-of-line (to max buff size) */ 363 while ((n++ < (sizeof(content)-1)) && (cur > base) && 364 (*(cur) != '\n') && (*(cur) != '\r')) 365 cur--; 366 if ((*(cur) == '\n') || (*(cur) == '\r')) cur++; 367 /* calculate the error position in terms of the current position */ 368 col = input->cur - cur; 369 /* search forward for end-of-line (to max buff size) */ 370 n = 0; 371 ctnt = content; 372 /* copy selected text to our buffer */ 373 while ((*cur != 0) && (*(cur) != '\n') && 374 (*(cur) != '\r') && (n < sizeof(content)-1)) { 375 *ctnt++ = *cur++; 376 n++; 377 } 378 *ctnt = 0; 379 /* print out the selected text */ 380 chanl(data ,"%s\n", content); 381 /* create blank line with problem pointer */ 382 n = 0; 383 ctnt = content; 384 /* (leave buffer space for pointer + line terminator) */ 385 while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) { 386 if (*(ctnt) != '\t') 387 *(ctnt) = ' '; 388 ctnt++; 389 } 390 *ctnt++ = '^'; 391 *ctnt = 0; 392 chanl(data ,"%s\n", content); 393} 394 395static void 396testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) { 397 char *file = NULL; 398 int line = 0; 399 int code = -1; 400 int domain; 401 void *data = NULL; 402 const char *str; 403 const xmlChar *name = NULL; 404 xmlNodePtr node; 405 xmlErrorLevel level; 406 xmlParserInputPtr input = NULL; 407 xmlParserInputPtr cur = NULL; 408 xmlParserCtxtPtr ctxt = NULL; 409 410 if (err == NULL) 411 return; 412 413 file = err->file; 414 line = err->line; 415 code = err->code; 416 domain = err->domain; 417 level = err->level; 418 node = err->node; 419 if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || 420 (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || 421 (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { 422 ctxt = err->ctxt; 423 } 424 str = err->message; 425 426 if (code == XML_ERR_OK) 427 return; 428 429 if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) 430 name = node->name; 431 432 /* 433 * Maintain the compatibility with the legacy error handling 434 */ 435 if (ctxt != NULL) { 436 input = ctxt->input; 437 if ((input != NULL) && (input->filename == NULL) && 438 (ctxt->inputNr > 1)) { 439 cur = input; 440 input = ctxt->inputTab[ctxt->inputNr - 2]; 441 } 442 if (input != NULL) { 443 if (input->filename) 444 channel(data, "%s:%d: ", input->filename, input->line); 445 else if ((line != 0) && (domain == XML_FROM_PARSER)) 446 channel(data, "Entity: line %d: ", input->line); 447 } 448 } else { 449 if (file != NULL) 450 channel(data, "%s:%d: ", file, line); 451 else if ((line != 0) && (domain == XML_FROM_PARSER)) 452 channel(data, "Entity: line %d: ", line); 453 } 454 if (name != NULL) { 455 channel(data, "element %s: ", name); 456 } 457 if (code == XML_ERR_OK) 458 return; 459 switch (domain) { 460 case XML_FROM_PARSER: 461 channel(data, "parser "); 462 break; 463 case XML_FROM_NAMESPACE: 464 channel(data, "namespace "); 465 break; 466 case XML_FROM_DTD: 467 case XML_FROM_VALID: 468 channel(data, "validity "); 469 break; 470 case XML_FROM_HTML: 471 channel(data, "HTML parser "); 472 break; 473 case XML_FROM_MEMORY: 474 channel(data, "memory "); 475 break; 476 case XML_FROM_OUTPUT: 477 channel(data, "output "); 478 break; 479 case XML_FROM_IO: 480 channel(data, "I/O "); 481 break; 482 case XML_FROM_XINCLUDE: 483 channel(data, "XInclude "); 484 break; 485 case XML_FROM_XPATH: 486 channel(data, "XPath "); 487 break; 488 case XML_FROM_XPOINTER: 489 channel(data, "parser "); 490 break; 491 case XML_FROM_REGEXP: 492 channel(data, "regexp "); 493 break; 494 case XML_FROM_MODULE: 495 channel(data, "module "); 496 break; 497 case XML_FROM_SCHEMASV: 498 channel(data, "Schemas validity "); 499 break; 500 case XML_FROM_SCHEMASP: 501 channel(data, "Schemas parser "); 502 break; 503 case XML_FROM_RELAXNGP: 504 channel(data, "Relax-NG parser "); 505 break; 506 case XML_FROM_RELAXNGV: 507 channel(data, "Relax-NG validity "); 508 break; 509 case XML_FROM_CATALOG: 510 channel(data, "Catalog "); 511 break; 512 case XML_FROM_C14N: 513 channel(data, "C14N "); 514 break; 515 case XML_FROM_XSLT: 516 channel(data, "XSLT "); 517 break; 518 default: 519 break; 520 } 521 if (code == XML_ERR_OK) 522 return; 523 switch (level) { 524 case XML_ERR_NONE: 525 channel(data, ": "); 526 break; 527 case XML_ERR_WARNING: 528 channel(data, "warning : "); 529 break; 530 case XML_ERR_ERROR: 531 channel(data, "error : "); 532 break; 533 case XML_ERR_FATAL: 534 channel(data, "error : "); 535 break; 536 } 537 if (code == XML_ERR_OK) 538 return; 539 if (str != NULL) { 540 int len; 541 len = xmlStrlen((const xmlChar *)str); 542 if ((len > 0) && (str[len - 1] != '\n')) 543 channel(data, "%s\n", str); 544 else 545 channel(data, "%s", str); 546 } else { 547 channel(data, "%s\n", "out of memory error"); 548 } 549 if (code == XML_ERR_OK) 550 return; 551 552 if (ctxt != NULL) { 553 xmlParserPrintFileContextInternal(input, channel, data); 554 if (cur != NULL) { 555 if (cur->filename) 556 channel(data, "%s:%d: \n", cur->filename, cur->line); 557 else if ((line != 0) && (domain == XML_FROM_PARSER)) 558 channel(data, "Entity: line %d: \n", cur->line); 559 xmlParserPrintFileContextInternal(cur, channel, data); 560 } 561 } 562 if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) && 563 (err->int1 < 100) && 564 (err->int1 < xmlStrlen((const xmlChar *)err->str1))) { 565 xmlChar buf[150]; 566 int i; 567 568 channel(data, "%s\n", err->str1); 569 for (i=0;i < err->int1;i++) 570 buf[i] = ' '; 571 buf[i++] = '^'; 572 buf[i] = 0; 573 channel(data, "%s\n", buf); 574 } 575} 576 577static void 578initializeLibxml2(void) { 579 xmlGetWarningsDefaultValue = 0; 580 xmlPedanticParserDefault(0); 581 582 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); 583 xmlInitParser(); 584 xmlSetExternalEntityLoader(testExternalEntityLoader); 585 xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); 586 /* 587 * register the new I/O handlers 588 */ 589 if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen, 590 hugeRead, hugeClose) < 0) { 591 fprintf(stderr, "failed to register Huge handler\n"); 592 exit(1); 593 } 594} 595 596/************************************************************************ 597 * * 598 * File name and path utilities * 599 * * 600 ************************************************************************/ 601 602static const char *baseFilename(const char *filename) { 603 const char *cur; 604 if (filename == NULL) 605 return(NULL); 606 cur = &filename[strlen(filename)]; 607 while ((cur > filename) && (*cur != '/')) 608 cur--; 609 if (*cur == '/') 610 return(cur + 1); 611 return(cur); 612} 613 614static char *resultFilename(const char *filename, const char *out, 615 const char *suffix) { 616 const char *base; 617 char res[500]; 618 char suffixbuff[500]; 619 620/************* 621 if ((filename[0] == 't') && (filename[1] == 'e') && 622 (filename[2] == 's') && (filename[3] == 't') && 623 (filename[4] == '/')) 624 filename = &filename[5]; 625 *************/ 626 627 base = baseFilename(filename); 628 if (suffix == NULL) 629 suffix = ".tmp"; 630 if (out == NULL) 631 out = ""; 632 633 strncpy(suffixbuff,suffix,499); 634#ifdef VMS 635 if(strstr(base,".") && suffixbuff[0]=='.') 636 suffixbuff[0]='_'; 637#endif 638 639 snprintf(res, 499, "%s%s%s", out, base, suffixbuff); 640 res[499] = 0; 641 return(strdup(res)); 642} 643 644static int checkTestFile(const char *filename) { 645 struct stat buf; 646 647 if (stat(filename, &buf) == -1) 648 return(0); 649 650#if defined(_WIN32) && !defined(__CYGWIN__) 651 if (!(buf.st_mode & _S_IFREG)) 652 return(0); 653#else 654 if (!S_ISREG(buf.st_mode)) 655 return(0); 656#endif 657 658 return(1); 659} 660 661 662 663/************************************************************************ 664 * * 665 * Test to detect or not recursive entities * 666 * * 667 ************************************************************************/ 668/** 669 * recursiveDetectTest: 670 * @filename: the file to parse 671 * @result: the file with expected result 672 * @err: the file with error messages: unused 673 * 674 * Parse a file loading DTD and replacing entities check it fails for 675 * lol cases 676 * 677 * Returns 0 in case of success, an error code otherwise 678 */ 679static int 680recursiveDetectTest(const char *filename, 681 const char *result ATTRIBUTE_UNUSED, 682 const char *err ATTRIBUTE_UNUSED, 683 int options ATTRIBUTE_UNUSED) { 684 xmlDocPtr doc; 685 xmlParserCtxtPtr ctxt; 686 int res = 0; 687 int mem; 688 689 nb_tests++; 690 691 ctxt = xmlNewParserCtxt(); 692 mem = xmlMemUsed(); 693 /* 694 * base of the test, parse with the old API 695 */ 696 doc = xmlCtxtReadFile(ctxt, filename, NULL, 697 XML_PARSE_NOENT | XML_PARSE_DTDLOAD); 698 if ((doc != NULL) || (ctxt->lastError.code != XML_ERR_ENTITY_LOOP)) { 699 fprintf(stderr, "Failed to detect recursion in %s\n", filename); 700 xmlFreeParserCtxt(ctxt); 701 xmlFreeDoc(doc); 702 return(1); 703 } 704 xmlFreeParserCtxt(ctxt); 705 706 return(res); 707} 708 709/** 710 * notRecursiveDetectTest: 711 * @filename: the file to parse 712 * @result: the file with expected result 713 * @err: the file with error messages: unused 714 * 715 * Parse a file loading DTD and replacing entities check it works for 716 * good cases 717 * 718 * Returns 0 in case of success, an error code otherwise 719 */ 720static int 721notRecursiveDetectTest(const char *filename, 722 const char *result ATTRIBUTE_UNUSED, 723 const char *err ATTRIBUTE_UNUSED, 724 int options ATTRIBUTE_UNUSED) { 725 xmlDocPtr doc; 726 xmlParserCtxtPtr ctxt; 727 int res = 0; 728 int mem; 729 730 nb_tests++; 731 732 ctxt = xmlNewParserCtxt(); 733 mem = xmlMemUsed(); 734 /* 735 * base of the test, parse with the old API 736 */ 737 doc = xmlCtxtReadFile(ctxt, filename, NULL, 738 XML_PARSE_NOENT | XML_PARSE_DTDLOAD); 739 if (doc == NULL) { 740 fprintf(stderr, "Failed to parse correct file %s\n", filename); 741 xmlFreeParserCtxt(ctxt); 742 return(1); 743 } 744 xmlFreeDoc(doc); 745 xmlFreeParserCtxt(ctxt); 746 747 return(res); 748} 749 750#ifdef LIBXML_READER_ENABLED 751/** 752 * notRecursiveHugeTest: 753 * @filename: the file to parse 754 * @result: the file with expected result 755 * @err: the file with error messages: unused 756 * 757 * Parse a memory generated file 758 * good cases 759 * 760 * Returns 0 in case of success, an error code otherwise 761 */ 762static int 763notRecursiveHugeTest(const char *filename ATTRIBUTE_UNUSED, 764 const char *result ATTRIBUTE_UNUSED, 765 const char *err ATTRIBUTE_UNUSED, 766 int options ATTRIBUTE_UNUSED) { 767 xmlTextReaderPtr reader; 768 int res = 0; 769 int ret; 770 771 nb_tests++; 772 773 reader = xmlReaderForFile("huge:test" , NULL, 774 XML_PARSE_NOENT | XML_PARSE_DTDLOAD); 775 if (reader == NULL) { 776 fprintf(stderr, "Failed to open huge:test\n"); 777 return(1); 778 } 779 ret = xmlTextReaderRead(reader); 780 while (ret == 1) { 781 ret = xmlTextReaderRead(reader); 782 } 783 if (ret != 0) { 784 fprintf(stderr, "Failed to parser huge:test with entities\n"); 785 res = 1; 786 } 787 xmlFreeTextReader(reader); 788 789 return(res); 790} 791#endif 792 793/************************************************************************ 794 * * 795 * Tests Descriptions * 796 * * 797 ************************************************************************/ 798 799static 800testDesc testDescriptions[] = { 801 { "Parsing recursive test cases" , 802 recursiveDetectTest, "./test/recurse/lol*.xml", NULL, NULL, NULL, 803 0 }, 804 { "Parsing non-recursive test cases" , 805 notRecursiveDetectTest, "./test/recurse/good*.xml", NULL, NULL, NULL, 806 0 }, 807#ifdef LIBXML_READER_ENABLED 808 { "Parsing non-recursive huge case" , 809 notRecursiveHugeTest, NULL, NULL, NULL, NULL, 810 0 }, 811#endif 812 {NULL, NULL, NULL, NULL, NULL, NULL, 0} 813}; 814 815/************************************************************************ 816 * * 817 * The main code driving the tests * 818 * * 819 ************************************************************************/ 820 821static int 822launchTests(testDescPtr tst) { 823 int res = 0, err = 0; 824 size_t i; 825 char *result; 826 char *error; 827 int mem; 828 829 if (tst == NULL) return(-1); 830 if (tst->in != NULL) { 831 glob_t globbuf; 832 833 globbuf.gl_offs = 0; 834 glob(tst->in, GLOB_DOOFFS, NULL, &globbuf); 835 for (i = 0;i < globbuf.gl_pathc;i++) { 836 if (!checkTestFile(globbuf.gl_pathv[i])) 837 continue; 838 if (tst->suffix != NULL) { 839 result = resultFilename(globbuf.gl_pathv[i], tst->out, 840 tst->suffix); 841 if (result == NULL) { 842 fprintf(stderr, "Out of memory !\n"); 843 fatalError(); 844 } 845 } else { 846 result = NULL; 847 } 848 if (tst->err != NULL) { 849 error = resultFilename(globbuf.gl_pathv[i], tst->out, 850 tst->err); 851 if (error == NULL) { 852 fprintf(stderr, "Out of memory !\n"); 853 fatalError(); 854 } 855 } else { 856 error = NULL; 857 } 858 if ((result) &&(!checkTestFile(result))) { 859 fprintf(stderr, "Missing result file %s\n", result); 860 } else if ((error) &&(!checkTestFile(error))) { 861 fprintf(stderr, "Missing error file %s\n", error); 862 } else { 863 mem = xmlMemUsed(); 864 extraMemoryFromResolver = 0; 865 testErrorsSize = 0; 866 testErrors[0] = 0; 867 res = tst->func(globbuf.gl_pathv[i], result, error, 868 tst->options | XML_PARSE_COMPACT); 869 xmlResetLastError(); 870 if (res != 0) { 871 fprintf(stderr, "File %s generated an error\n", 872 globbuf.gl_pathv[i]); 873 nb_errors++; 874 err++; 875 } 876 else if (xmlMemUsed() != mem) { 877 if ((xmlMemUsed() != mem) && 878 (extraMemoryFromResolver == 0)) { 879 fprintf(stderr, "File %s leaked %d bytes\n", 880 globbuf.gl_pathv[i], xmlMemUsed() - mem); 881 nb_leaks++; 882 err++; 883 } 884 } 885 testErrorsSize = 0; 886 } 887 if (result) 888 free(result); 889 if (error) 890 free(error); 891 } 892 globfree(&globbuf); 893 } else { 894 testErrorsSize = 0; 895 testErrors[0] = 0; 896 extraMemoryFromResolver = 0; 897 res = tst->func(NULL, NULL, NULL, tst->options); 898 if (res != 0) { 899 nb_errors++; 900 err++; 901 } 902 } 903 return(err); 904} 905 906static int verbose = 0; 907static int tests_quiet = 0; 908 909static int 910runtest(int i) { 911 int ret = 0, res; 912 int old_errors, old_tests, old_leaks; 913 914 old_errors = nb_errors; 915 old_tests = nb_tests; 916 old_leaks = nb_leaks; 917 if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL)) 918 printf("## %s\n", testDescriptions[i].desc); 919 res = launchTests(&testDescriptions[i]); 920 if (res != 0) 921 ret++; 922 if (verbose) { 923 if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) 924 printf("Ran %d tests, no errors\n", nb_tests - old_tests); 925 else 926 printf("Ran %d tests, %d errors, %d leaks\n", 927 nb_tests - old_tests, 928 nb_errors - old_errors, 929 nb_leaks - old_leaks); 930 } 931 return(ret); 932} 933 934int 935main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { 936 int i, a, ret = 0; 937 int subset = 0; 938 939 initializeLibxml2(); 940 941 for (a = 1; a < argc;a++) { 942 if (!strcmp(argv[a], "-v")) 943 verbose = 1; 944 else if (!strcmp(argv[a], "-quiet")) 945 tests_quiet = 1; 946 else { 947 for (i = 0; testDescriptions[i].func != NULL; i++) { 948 if (strstr(testDescriptions[i].desc, argv[a])) { 949 ret += runtest(i); 950 subset++; 951 } 952 } 953 } 954 } 955 if (subset == 0) { 956 for (i = 0; testDescriptions[i].func != NULL; i++) { 957 ret += runtest(i); 958 } 959 } 960 if ((nb_errors == 0) && (nb_leaks == 0)) { 961 ret = 0; 962 printf("Total %d tests, no errors\n", 963 nb_tests); 964 } else { 965 ret = 1; 966 printf("Total %d tests, %d errors, %d leaks\n", 967 nb_tests, nb_errors, nb_leaks); 968 } 969 xmlCleanupParser(); 970 xmlMemoryDump(); 971 972 return(ret); 973} 974