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

    1: /*
    2:  * runsuite.c: C program to run libxml2 againts published testsuites
    3:  *
    4:  * See Copyright for the status of this software.
    5:  *
    6:  * daniel@veillard.com
    7:  */
    8: 
    9: #ifdef HAVE_CONFIG_H
   10: #include "libxml.h"
   11: #else
   12: #include <stdio.h>
   13: #endif
   14: 
   15: #ifdef LIBXML_XPATH_ENABLED
   16: 
   17: #if !defined(_WIN32) || defined(__CYGWIN__)
   18: #include <unistd.h>
   19: #endif
   20: #include <string.h>
   21: #include <sys/types.h>
   22: #include <sys/stat.h>
   23: #include <fcntl.h>
   24: 
   25: #include <libxml/parser.h>
   26: #include <libxml/parserInternals.h>
   27: #include <libxml/tree.h>
   28: #include <libxml/uri.h>
   29: #include <libxml/xmlreader.h>
   30: 
   31: #include <libxml/xpath.h>
   32: #include <libxml/xpathInternals.h>
   33: 
   34: #define LOGFILE "runxmlconf.log"
   35: static FILE *logfile = NULL;
   36: static int verbose = 0;
   37: 
   38: #define NB_EXPECTED_ERRORS 15
   39: 
   40: #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
   41: #define vsnprintf _vsnprintf
   42: #define snprintf _snprintf
   43: #endif
   44: 
   45: const char *skipped_tests[] = {
   46: /* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */
   47:     "rmt-ns10-035",
   48:     NULL
   49: };
   50: 
   51: /************************************************************************
   52:  *									*
   53:  *		File name and path utilities				*
   54:  *									*
   55:  ************************************************************************/
   56: 
   57: static int checkTestFile(const char *filename) {
   58:     struct stat buf;
   59: 
   60:     if (stat(filename, &buf) == -1)
   61:         return(0);
   62: 
   63: #if defined(_WIN32) && !defined(__CYGWIN__)
   64:     if (!(buf.st_mode & _S_IFREG))
   65:         return(0);
   66: #else
   67:     if (!S_ISREG(buf.st_mode))
   68:         return(0);
   69: #endif
   70: 
   71:     return(1);
   72: }
   73: 
   74: static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
   75:     char buf[500];
   76: 
   77:     if (dir == NULL) return(xmlStrdup(path));
   78:     if (path == NULL) return(NULL);
   79: 
   80:     snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
   81:     return(xmlStrdup((const xmlChar *) buf));
   82: }
   83: 
   84: /************************************************************************
   85:  *									*
   86:  *		Libxml2 specific routines				*
   87:  *									*
   88:  ************************************************************************/
   89: 
   90: static int nb_skipped = 0;
   91: static int nb_tests = 0;
   92: static int nb_errors = 0;
   93: static int nb_leaks = 0;
   94: 
   95: /*
   96:  * We need to trap calls to the resolver to not account memory for the catalog
   97:  * and not rely on any external resources.
   98:  */
   99: static xmlParserInputPtr
  100: testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
  101: 			 xmlParserCtxtPtr ctxt) {
  102:     xmlParserInputPtr ret;
  103: 
  104:     ret = xmlNewInputFromFile(ctxt, (const char *) URL);
  105: 
  106:     return(ret);
  107: }
  108: 
  109: /*
  110:  * Trapping the error messages at the generic level to grab the equivalent of
  111:  * stderr messages on CLI tools.
  112:  */
  113: static char testErrors[32769];
  114: static int testErrorsSize = 0;
  115: static int nbError = 0;
  116: static int nbFatal = 0;
  117: 
  118: static void test_log(const char *msg, ...) {
  119:     va_list args;
  120:     if (logfile != NULL) {
  121:         fprintf(logfile, "\n------------\n");
  122: 	va_start(args, msg);
  123: 	vfprintf(logfile, msg, args);
  124: 	va_end(args);
  125: 	fprintf(logfile, "%s", testErrors);
  126: 	testErrorsSize = 0; testErrors[0] = 0;
  127:     }
  128:     if (verbose) {
  129: 	va_start(args, msg);
  130: 	vfprintf(stderr, msg, args);
  131: 	va_end(args);
  132:     }
  133: }
  134: 
  135: static void
  136: testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) {
  137:     int res;
  138: 
  139:     if (testErrorsSize >= 32768)
  140:         return;
  141:     res = snprintf(&testErrors[testErrorsSize],
  142:                     32768 - testErrorsSize,
  143: 		   "%s:%d: %s\n", (error->file ? error->file : "entity"),
  144: 		   error->line, error->message);
  145:     if (error->level == XML_ERR_FATAL)
  146:         nbFatal++;
  147:     else if (error->level == XML_ERR_ERROR)
  148:         nbError++;
  149:     if (testErrorsSize + res >= 32768) {
  150:         /* buffer is full */
  151: 	testErrorsSize = 32768;
  152: 	testErrors[testErrorsSize] = 0;
  153:     } else {
  154:         testErrorsSize += res;
  155:     }
  156:     testErrors[testErrorsSize] = 0;
  157: }
  158: 
  159: static xmlXPathContextPtr ctxtXPath;
  160: 
  161: static void
  162: initializeLibxml2(void) {
  163:     xmlGetWarningsDefaultValue = 0;
  164:     xmlPedanticParserDefault(0);
  165: 
  166:     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
  167:     xmlInitParser();
  168:     xmlSetExternalEntityLoader(testExternalEntityLoader);
  169:     ctxtXPath = xmlXPathNewContext(NULL);
  170:     /*
  171:     * Deactivate the cache if created; otherwise we have to create/free it
  172:     * for every test, since it will confuse the memory leak detection.
  173:     * Note that normally this need not be done, since the cache is not
  174:     * created until set explicitely with xmlXPathContextSetCache();
  175:     * but for test purposes it is sometimes usefull to activate the
  176:     * cache by default for the whole library.
  177:     */
  178:     if (ctxtXPath->cache != NULL)
  179: 	xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
  180:     xmlSetStructuredErrorFunc(NULL, testErrorHandler);
  181: }
  182: 
  183: /************************************************************************
  184:  *									*
  185:  *		Run the xmlconf test if found				*
  186:  *									*
  187:  ************************************************************************/
  188: 
  189: static int
  190: xmlconfTestInvalid(const char *id, const char *filename, int options) {
  191:     xmlDocPtr doc;
  192:     xmlParserCtxtPtr ctxt;
  193:     int ret = 1;
  194: 
  195:     ctxt = xmlNewParserCtxt();
  196:     if (ctxt == NULL) {
  197:         test_log("test %s : %s out of memory\n",
  198: 	         id, filename);
  199:         return(0);
  200:     }
  201:     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
  202:     if (doc == NULL) {
  203:         test_log("test %s : %s invalid document turned not well-formed too\n",
  204: 	         id, filename);
  205:     } else {
  206:     /* invalidity should be reported both in the context and in the document */
  207:         if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) {
  208: 	    test_log("test %s : %s failed to detect invalid document\n",
  209: 		     id, filename);
  210: 	    nb_errors++;
  211: 	    ret = 0;
  212: 	}
  213: 	xmlFreeDoc(doc);
  214:     }
  215:     xmlFreeParserCtxt(ctxt);
  216:     return(ret);
  217: }
  218: 
  219: static int
  220: xmlconfTestValid(const char *id, const char *filename, int options) {
  221:     xmlDocPtr doc;
  222:     xmlParserCtxtPtr ctxt;
  223:     int ret = 1;
  224: 
  225:     ctxt = xmlNewParserCtxt();
  226:     if (ctxt == NULL) {
  227:         test_log("test %s : %s out of memory\n",
  228: 	         id, filename);
  229:         return(0);
  230:     }
  231:     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
  232:     if (doc == NULL) {
  233:         test_log("test %s : %s failed to parse a valid document\n",
  234: 	         id, filename);
  235:         nb_errors++;
  236: 	ret = 0;
  237:     } else {
  238:     /* validity should be reported both in the context and in the document */
  239:         if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
  240: 	    test_log("test %s : %s failed to validate a valid document\n",
  241: 		     id, filename);
  242: 	    nb_errors++;
  243: 	    ret = 0;
  244: 	}
  245: 	xmlFreeDoc(doc);
  246:     }
  247:     xmlFreeParserCtxt(ctxt);
  248:     return(ret);
  249: }
  250: 
  251: static int
  252: xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
  253:     xmlDocPtr doc;
  254:     int ret = 1;
  255: 
  256:     /*
  257:      * In case of Namespace errors, libxml2 will still parse the document
  258:      * but log a Namesapce error.
  259:      */
  260:     doc = xmlReadFile(filename, NULL, options);
  261:     if (doc == NULL) {
  262:         test_log("test %s : %s failed to parse the XML\n",
  263: 	         id, filename);
  264:         nb_errors++;
  265: 	ret = 0;
  266:     } else {
  267: 	if ((xmlLastError.code == XML_ERR_OK) ||
  268: 	    (xmlLastError.domain != XML_FROM_NAMESPACE)) {
  269: 	    test_log("test %s : %s failed to detect namespace error\n",
  270: 		     id, filename);
  271: 	    nb_errors++;
  272: 	    ret = 0;
  273: 	}
  274: 	xmlFreeDoc(doc);
  275:     }
  276:     return(ret);
  277: }
  278: 
  279: static int
  280: xmlconfTestNotWF(const char *id, const char *filename, int options) {
  281:     xmlDocPtr doc;
  282:     int ret = 1;
  283: 
  284:     doc = xmlReadFile(filename, NULL, options);
  285:     if (doc != NULL) {
  286:         test_log("test %s : %s failed to detect not well formedness\n",
  287: 	         id, filename);
  288:         nb_errors++;
  289: 	xmlFreeDoc(doc);
  290: 	ret = 0;
  291:     }
  292:     return(ret);
  293: }
  294: 
  295: static int
  296: xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
  297:     int ret = -1;
  298:     xmlChar *type = NULL;
  299:     xmlChar *filename = NULL;
  300:     xmlChar *uri = NULL;
  301:     xmlChar *base = NULL;
  302:     xmlChar *id = NULL;
  303:     xmlChar *rec = NULL;
  304:     xmlChar *version = NULL;
  305:     xmlChar *entities = NULL;
  306:     xmlChar *edition = NULL;
  307:     int options = 0;
  308:     int nstest = 0;
  309:     int mem, final;
  310:     int i;
  311: 
  312:     testErrorsSize = 0; testErrors[0] = 0;
  313:     nbError = 0;
  314:     nbFatal = 0;
  315:     id = xmlGetProp(cur, BAD_CAST "ID");
  316:     if (id == NULL) {
  317:         test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
  318: 	goto error;
  319:     }
  320:     for (i = 0;skipped_tests[i] != NULL;i++) {
  321:         if (!strcmp(skipped_tests[i], (char *) id)) {
  322: 	    test_log("Skipping test %s from skipped list\n", (char *) id);
  323: 	    ret = 0;
  324: 	    nb_skipped++;
  325: 	    goto error;
  326: 	}
  327:     }
  328:     type = xmlGetProp(cur, BAD_CAST "TYPE");
  329:     if (type == NULL) {
  330:         test_log("test %s missing TYPE\n", (char *) id);
  331: 	goto error;
  332:     }
  333:     uri = xmlGetProp(cur, BAD_CAST "URI");
  334:     if (uri == NULL) {
  335:         test_log("test %s missing URI\n", (char *) id);
  336: 	goto error;
  337:     }
  338:     base = xmlNodeGetBase(doc, cur);
  339:     filename = composeDir(base, uri);
  340:     if (!checkTestFile((char *) filename)) {
  341:         test_log("test %s missing file %s \n", id,
  342: 	         (filename ? (char *)filename : "NULL"));
  343: 	goto error;
  344:     }
  345: 
  346:     version = xmlGetProp(cur, BAD_CAST "VERSION");
  347: 
  348:     entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
  349:     if (!xmlStrEqual(entities, BAD_CAST "none")) {
  350:         options |= XML_PARSE_DTDLOAD;
  351:         options |= XML_PARSE_NOENT;
  352:     }
  353:     rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
  354:     if ((rec == NULL) ||
  355:         (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
  356: 	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
  357: 	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
  358: 	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
  359: 	if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
  360: 	    test_log("Skipping test %s for %s\n", (char *) id,
  361: 	             (char *) version);
  362: 	    ret = 0;
  363: 	    nb_skipped++;
  364: 	    goto error;
  365: 	}
  366: 	ret = 1;
  367:     } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
  368: 	       (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
  369: 	ret = 1;
  370: 	nstest = 1;
  371:     } else {
  372:         test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
  373: 	ret = 0;
  374: 	nb_skipped++;
  375: 	goto error;
  376:     }
  377:     edition = xmlGetProp(cur, BAD_CAST "EDITION");
  378:     if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
  379:         /* test limited to all versions before 5th */
  380: 	options |= XML_PARSE_OLD10;
  381:     }
  382: 
  383:     /*
  384:      * Reset errors and check memory usage before the test
  385:      */
  386:     xmlResetLastError();
  387:     testErrorsSize = 0; testErrors[0] = 0;
  388:     mem = xmlMemUsed();
  389: 
  390:     if (xmlStrEqual(type, BAD_CAST "not-wf")) {
  391:         if (nstest == 0)
  392: 	    xmlconfTestNotWF((char *) id, (char *) filename, options);
  393:         else 
  394: 	    xmlconfTestNotNSWF((char *) id, (char *) filename, options);
  395:     } else if (xmlStrEqual(type, BAD_CAST "valid")) {
  396:         options |= XML_PARSE_DTDVALID;
  397: 	xmlconfTestValid((char *) id, (char *) filename, options);
  398:     } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
  399:         options |= XML_PARSE_DTDVALID;
  400: 	xmlconfTestInvalid((char *) id, (char *) filename, options);
  401:     } else if (xmlStrEqual(type, BAD_CAST "error")) {
  402:         test_log("Skipping error test %s \n", (char *) id);
  403: 	ret = 0;
  404: 	nb_skipped++;
  405: 	goto error;
  406:     } else {
  407:         test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
  408: 	ret = -1;
  409: 	goto error;
  410:     }
  411: 
  412:     /*
  413:      * Reset errors and check memory usage after the test
  414:      */
  415:     xmlResetLastError();
  416:     final = xmlMemUsed();
  417:     if (final > mem) {
  418:         test_log("test %s : %s leaked %d bytes\n",
  419: 	         id, filename, final - mem);
  420:         nb_leaks++;
  421: 	xmlMemDisplayLast(logfile, final - mem);
  422:     }
  423:     nb_tests++;
  424: 
  425: error:
  426:     if (type != NULL)
  427:         xmlFree(type);
  428:     if (entities != NULL)
  429:         xmlFree(entities);
  430:     if (edition != NULL)
  431:         xmlFree(edition);
  432:     if (version != NULL)
  433:         xmlFree(version);
  434:     if (filename != NULL)
  435:         xmlFree(filename);
  436:     if (uri != NULL)
  437:         xmlFree(uri);
  438:     if (base != NULL)
  439:         xmlFree(base);
  440:     if (id != NULL)
  441:         xmlFree(id);
  442:     if (rec != NULL)
  443:         xmlFree(rec);
  444:     return(ret);
  445: }
  446: 
  447: static int
  448: xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
  449:     xmlChar *profile;
  450:     int ret = 0;
  451:     int tests = 0;
  452:     int output = 0;
  453: 
  454:     if (level == 1) {
  455: 	profile = xmlGetProp(cur, BAD_CAST "PROFILE");
  456: 	if (profile != NULL) {
  457: 	    output = 1;
  458: 	    level++;
  459: 	    printf("Test cases: %s\n", (char *) profile);
  460: 	    xmlFree(profile);
  461: 	}
  462:     }
  463:     cur = cur->children;
  464:     while (cur != NULL) {
  465:         /* look only at elements we ignore everything else */
  466:         if (cur->type == XML_ELEMENT_NODE) {
  467: 	    if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
  468: 	        ret += xmlconfTestCases(doc, cur, level);
  469: 	    } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
  470: 	        if (xmlconfTestItem(doc, cur) >= 0)
  471: 		    ret++;
  472: 		tests++;
  473: 	    } else {
  474: 	        fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
  475: 	    }
  476: 	}
  477:         cur = cur->next;
  478:     }
  479:     if (output == 1) {
  480: 	if (tests > 0)
  481: 	    printf("Test cases: %d tests\n", tests);
  482:     }
  483:     return(ret);
  484: }
  485: 
  486: static int
  487: xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
  488:     xmlChar *profile;
  489:     int ret = 0;
  490: 
  491:     profile = xmlGetProp(cur, BAD_CAST "PROFILE");
  492:     if (profile != NULL) {
  493:         printf("Test suite: %s\n", (char *) profile);
  494: 	xmlFree(profile);
  495:     } else
  496:         printf("Test suite\n");
  497:     cur = cur->children;
  498:     while (cur != NULL) {
  499:         /* look only at elements we ignore everything else */
  500:         if (cur->type == XML_ELEMENT_NODE) {
  501: 	    if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
  502: 	        ret += xmlconfTestCases(doc, cur, 1);
  503: 	    } else {
  504: 	        fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
  505: 	    }
  506: 	}
  507:         cur = cur->next;
  508:     }
  509:     return(ret);
  510: }
  511: 
  512: static void
  513: xmlconfInfo(void) {
  514:     fprintf(stderr, "  you need to fetch and extract the\n");
  515:     fprintf(stderr, "  latest XML Conformance Test Suites\n");
  516:     fprintf(stderr, "  http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
  517:     fprintf(stderr, "  see http://www.w3.org/XML/Test/ for informations\n");
  518: }
  519: 
  520: static int
  521: xmlconfTest(void) {
  522:     const char *confxml = "xmlconf/xmlconf.xml";
  523:     xmlDocPtr doc;
  524:     xmlNodePtr cur;
  525:     int ret = 0;
  526: 
  527:     if (!checkTestFile(confxml)) {
  528:         fprintf(stderr, "%s is missing \n", confxml);
  529: 	xmlconfInfo();
  530: 	return(-1);
  531:     }
  532:     doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
  533:     if (doc == NULL) {
  534:         fprintf(stderr, "%s is corrupted \n", confxml);
  535: 	xmlconfInfo();
  536: 	return(-1);
  537:     }
  538: 
  539:     cur = xmlDocGetRootElement(doc);
  540:     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
  541:         fprintf(stderr, "Unexpected format %s\n", confxml);
  542: 	xmlconfInfo();
  543: 	ret = -1;
  544:     } else {
  545:         ret = xmlconfTestSuite(doc, cur);
  546:     }
  547:     xmlFreeDoc(doc);
  548:     return(ret);
  549: }
  550: 
  551: /************************************************************************
  552:  *									*
  553:  *		The driver for the tests				*
  554:  *									*
  555:  ************************************************************************/
  556: 
  557: int
  558: main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
  559:     int ret = 0;
  560:     int old_errors, old_tests, old_leaks;
  561: 
  562:     logfile = fopen(LOGFILE, "w");
  563:     if (logfile == NULL) {
  564:         fprintf(stderr,
  565: 	        "Could not open the log file, running in verbose mode\n");
  566: 	verbose = 1;
  567:     }
  568:     initializeLibxml2();
  569: 
  570:     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
  571:         verbose = 1;
  572: 
  573: 
  574:     old_errors = nb_errors;
  575:     old_tests = nb_tests;
  576:     old_leaks = nb_leaks;
  577:     xmlconfTest();
  578:     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  579: 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
  580:     else
  581: 	printf("Ran %d tests, %d errors, %d leaks\n",
  582: 	       nb_tests - old_tests,
  583: 	       nb_errors - old_errors,
  584: 	       nb_leaks - old_leaks);
  585:     if ((nb_errors == 0) && (nb_leaks == 0)) {
  586:         ret = 0;
  587: 	printf("Total %d tests, no errors\n",
  588: 	       nb_tests);
  589:     } else {
  590: 	ret = 1;
  591: 	printf("Total %d tests, %d errors, %d leaks\n",
  592: 	       nb_tests, nb_errors, nb_leaks);
  593: 	printf("See %s for detailed output\n", LOGFILE);
  594: 	if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
  595: 	    printf("%d errors were expected\n", nb_errors);
  596: 	    ret = 0;
  597: 	}
  598:     }
  599:     xmlXPathFreeContext(ctxtXPath);
  600:     xmlCleanupParser();
  601:     xmlMemoryDump();
  602: 
  603:     if (logfile != NULL)
  604:         fclose(logfile);
  605:     return(ret);
  606: }
  607: 
  608: #else /* ! LIBXML_XPATH_ENABLED */
  609: #include <stdio.h>
  610: int
  611: main(int argc, char **argv) {
  612:     fprintf(stderr, "%s need XPath support\n", argv[0]);
  613: }
  614: #endif

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