File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / libxml2 / runxmlconf.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 19:53:31 2014 UTC (9 years, 11 months ago) by misho
Branches: libxml2, MAIN
CVS tags: v2_9_1p0, v2_9_1, HEAD
libxml2 2.9.1

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

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