File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / testrecurse.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Mon Jul 22 01:22:21 2013 UTC (10 years, 11 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_8_0p0, v2_8_0, HEAD
2.8.0

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>