File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / runxmlconf.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 23:37:58 2012 UTC (12 years, 5 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_7_8, HEAD
libxml2

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

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