Home | History | Annotate | Download | only in src
      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 (at) veillard.com
      7  */
      8 
      9 #ifdef HAVE_CONFIG_H
     10 #include "libxml.h"
     11 #else
     12 #include <stdio.h>
     13 #endif
     14 
     15 #if !defined(_WIN32) || defined(__CYGWIN__)
     16 #include <unistd.h>
     17 #endif
     18 #include <string.h>
     19 #include <sys/types.h>
     20 #include <sys/stat.h>
     21 #include <fcntl.h>
     22 
     23 #include <libxml/parser.h>
     24 #include <libxml/parserInternals.h>
     25 #include <libxml/tree.h>
     26 #include <libxml/uri.h>
     27 #if defined(LIBXML_SCHEMAS_ENABLED) && defined(LIBXML_XPATH_ENABLED)
     28 #include <libxml/xmlreader.h>
     29 
     30 #include <libxml/xpath.h>
     31 #include <libxml/xpathInternals.h>
     32 
     33 #include <libxml/relaxng.h>
     34 #include <libxml/xmlschemas.h>
     35 #include <libxml/xmlschemastypes.h>
     36 
     37 #define LOGFILE "runsuite.log"
     38 static FILE *logfile = NULL;
     39 static int verbose = 0;
     40 
     41 
     42 
     43 #if defined(_WIN32) && !defined(__CYGWIN__)
     44 
     45 #define vsnprintf _vsnprintf
     46 
     47 #define snprintf _snprintf
     48 
     49 #endif
     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_tests = 0;
     91 static int nb_errors = 0;
     92 static int nb_internals = 0;
     93 static int nb_schematas = 0;
     94 static int nb_unimplemented = 0;
     95 static int nb_leaks = 0;
     96 static int extraMemoryFromResolver = 0;
     97 
     98 static int
     99 fatalError(void) {
    100     fprintf(stderr, "Exitting tests on fatal error\n");
    101     exit(1);
    102 }
    103 
    104 /*
    105  * that's needed to implement <resource>
    106  */
    107 #define MAX_ENTITIES 20
    108 static char *testEntitiesName[MAX_ENTITIES];
    109 static char *testEntitiesValue[MAX_ENTITIES];
    110 static int nb_entities = 0;
    111 static void resetEntities(void) {
    112     int i;
    113 
    114     for (i = 0;i < nb_entities;i++) {
    115         if (testEntitiesName[i] != NULL)
    116 	    xmlFree(testEntitiesName[i]);
    117         if (testEntitiesValue[i] != NULL)
    118 	    xmlFree(testEntitiesValue[i]);
    119     }
    120     nb_entities = 0;
    121 }
    122 static int addEntity(char *name, char *content) {
    123     if (nb_entities >= MAX_ENTITIES) {
    124 	fprintf(stderr, "Too many entities defined\n");
    125 	return(-1);
    126     }
    127     testEntitiesName[nb_entities] = name;
    128     testEntitiesValue[nb_entities] = content;
    129     nb_entities++;
    130     return(0);
    131 }
    132 
    133 /*
    134  * We need to trap calls to the resolver to not account memory for the catalog
    135  * which is shared to the current running test. We also don't want to have
    136  * network downloads modifying tests.
    137  */
    138 static xmlParserInputPtr
    139 testExternalEntityLoader(const char *URL, const char *ID,
    140 			 xmlParserCtxtPtr ctxt) {
    141     xmlParserInputPtr ret;
    142     int i;
    143 
    144     for (i = 0;i < nb_entities;i++) {
    145         if (!strcmp(testEntitiesName[i], URL)) {
    146 	    ret = xmlNewStringInputStream(ctxt,
    147 	                (const xmlChar *) testEntitiesValue[i]);
    148 	    if (ret != NULL) {
    149 	        ret->filename = (const char *)
    150 		                xmlStrdup((xmlChar *)testEntitiesName[i]);
    151 	    }
    152 	    return(ret);
    153 	}
    154     }
    155     if (checkTestFile(URL)) {
    156 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
    157     } else {
    158 	int memused = xmlMemUsed();
    159 	ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
    160 	extraMemoryFromResolver += xmlMemUsed() - memused;
    161     }
    162 #if 0
    163     if (ret == NULL) {
    164         fprintf(stderr, "Failed to find resource %s\n", URL);
    165     }
    166 #endif
    167 
    168     return(ret);
    169 }
    170 
    171 /*
    172  * Trapping the error messages at the generic level to grab the equivalent of
    173  * stderr messages on CLI tools.
    174  */
    175 static char testErrors[32769];
    176 static int testErrorsSize = 0;
    177 
    178 static void test_log(const char *msg, ...) {
    179     va_list args;
    180     if (logfile != NULL) {
    181         fprintf(logfile, "\n------------\n");
    182 	va_start(args, msg);
    183 	vfprintf(logfile, msg, args);
    184 	va_end(args);
    185 	fprintf(logfile, "%s", testErrors);
    186 	testErrorsSize = 0; testErrors[0] = 0;
    187     }
    188     if (verbose) {
    189 	va_start(args, msg);
    190 	vfprintf(stderr, msg, args);
    191 	va_end(args);
    192     }
    193 }
    194 
    195 static void
    196 testErrorHandler(void *ctx  ATTRIBUTE_UNUSED, const char *msg, ...) {
    197     va_list args;
    198     int res;
    199 
    200     if (testErrorsSize >= 32768)
    201         return;
    202     va_start(args, msg);
    203     res = vsnprintf(&testErrors[testErrorsSize],
    204                     32768 - testErrorsSize,
    205 		    msg, args);
    206     va_end(args);
    207     if (testErrorsSize + res >= 32768) {
    208         /* buffer is full */
    209 	testErrorsSize = 32768;
    210 	testErrors[testErrorsSize] = 0;
    211     } else {
    212         testErrorsSize += res;
    213     }
    214     testErrors[testErrorsSize] = 0;
    215 }
    216 
    217 static xmlXPathContextPtr ctxtXPath;
    218 
    219 static void
    220 initializeLibxml2(void) {
    221     xmlGetWarningsDefaultValue = 0;
    222     xmlPedanticParserDefault(0);
    223 
    224     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
    225     xmlInitParser();
    226     xmlSetExternalEntityLoader(testExternalEntityLoader);
    227     ctxtXPath = xmlXPathNewContext(NULL);
    228     /*
    229     * Deactivate the cache if created; otherwise we have to create/free it
    230     * for every test, since it will confuse the memory leak detection.
    231     * Note that normally this need not be done, since the cache is not
    232     * created until set explicitely with xmlXPathContextSetCache();
    233     * but for test purposes it is sometimes usefull to activate the
    234     * cache by default for the whole library.
    235     */
    236     if (ctxtXPath->cache != NULL)
    237 	xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
    238     /* used as default nanemspace in xstc tests */
    239     xmlXPathRegisterNs(ctxtXPath, BAD_CAST "ts", BAD_CAST "TestSuite");
    240     xmlXPathRegisterNs(ctxtXPath, BAD_CAST "xlink",
    241                        BAD_CAST "http://www.w3.org/1999/xlink");
    242     xmlSetGenericErrorFunc(NULL, testErrorHandler);
    243 #ifdef LIBXML_SCHEMAS_ENABLED
    244     xmlSchemaInitTypes();
    245     xmlRelaxNGInitTypes();
    246 #endif
    247 }
    248 
    249 static xmlNodePtr
    250 getNext(xmlNodePtr cur, const char *xpath) {
    251     xmlNodePtr ret = NULL;
    252     xmlXPathObjectPtr res;
    253     xmlXPathCompExprPtr comp;
    254 
    255     if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
    256         return(NULL);
    257     ctxtXPath->doc = cur->doc;
    258     ctxtXPath->node = cur;
    259     comp = xmlXPathCompile(BAD_CAST xpath);
    260     if (comp == NULL) {
    261         fprintf(stderr, "Failed to compile %s\n", xpath);
    262 	return(NULL);
    263     }
    264     res = xmlXPathCompiledEval(comp, ctxtXPath);
    265     xmlXPathFreeCompExpr(comp);
    266     if (res == NULL)
    267         return(NULL);
    268     if ((res->type == XPATH_NODESET) &&
    269         (res->nodesetval != NULL) &&
    270 	(res->nodesetval->nodeNr > 0) &&
    271 	(res->nodesetval->nodeTab != NULL))
    272 	ret = res->nodesetval->nodeTab[0];
    273     xmlXPathFreeObject(res);
    274     return(ret);
    275 }
    276 
    277 static xmlChar *
    278 getString(xmlNodePtr cur, const char *xpath) {
    279     xmlChar *ret = NULL;
    280     xmlXPathObjectPtr res;
    281     xmlXPathCompExprPtr comp;
    282 
    283     if ((cur == NULL)  || (cur->doc == NULL) || (xpath == NULL))
    284         return(NULL);
    285     ctxtXPath->doc = cur->doc;
    286     ctxtXPath->node = cur;
    287     comp = xmlXPathCompile(BAD_CAST xpath);
    288     if (comp == NULL) {
    289         fprintf(stderr, "Failed to compile %s\n", xpath);
    290 	return(NULL);
    291     }
    292     res = xmlXPathCompiledEval(comp, ctxtXPath);
    293     xmlXPathFreeCompExpr(comp);
    294     if (res == NULL)
    295         return(NULL);
    296     if (res->type == XPATH_STRING) {
    297         ret = res->stringval;
    298 	res->stringval = NULL;
    299     }
    300     xmlXPathFreeObject(res);
    301     return(ret);
    302 }
    303 
    304 /************************************************************************
    305  *									*
    306  *		Test test/xsdtest/xsdtestsuite.xml			*
    307  *									*
    308  ************************************************************************/
    309 
    310 static int
    311 xsdIncorectTestCase(xmlNodePtr cur) {
    312     xmlNodePtr test;
    313     xmlBufferPtr buf;
    314     xmlRelaxNGParserCtxtPtr pctxt;
    315     xmlRelaxNGPtr rng = NULL;
    316     int ret = 0, memt;
    317 
    318     cur = getNext(cur, "./incorrect[1]");
    319     if (cur == NULL) {
    320         return(0);
    321     }
    322 
    323     test = getNext(cur, "./*");
    324     if (test == NULL) {
    325         test_log("Failed to find test in correct line %ld\n",
    326 	        xmlGetLineNo(cur));
    327         return(1);
    328     }
    329 
    330     memt = xmlMemUsed();
    331     extraMemoryFromResolver = 0;
    332     /*
    333      * dump the schemas to a buffer, then reparse it and compile the schemas
    334      */
    335     buf = xmlBufferCreate();
    336     if (buf == NULL) {
    337         fprintf(stderr, "out of memory !\n");
    338 	fatalError();
    339     }
    340     xmlNodeDump(buf, test->doc, test, 0, 0);
    341     pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
    342     xmlRelaxNGSetParserErrors(pctxt,
    343          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
    344          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
    345 	 pctxt);
    346     rng = xmlRelaxNGParse(pctxt);
    347     xmlRelaxNGFreeParserCtxt(pctxt);
    348     if (rng != NULL) {
    349 	test_log("Failed to detect incorect RNG line %ld\n",
    350 		    xmlGetLineNo(test));
    351         ret = 1;
    352 	goto done;
    353     }
    354 
    355 done:
    356     if (buf != NULL)
    357 	xmlBufferFree(buf);
    358     if (rng != NULL)
    359         xmlRelaxNGFree(rng);
    360     xmlResetLastError();
    361     if ((memt < xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
    362 	test_log("Validation of tests starting line %ld leaked %d\n",
    363 		xmlGetLineNo(cur), xmlMemUsed() - memt);
    364 	nb_leaks++;
    365     }
    366     return(ret);
    367 }
    368 
    369 static void
    370 installResources(xmlNodePtr tst, const xmlChar *base) {
    371     xmlNodePtr test;
    372     xmlBufferPtr buf;
    373     xmlChar *name, *content, *res;
    374 
    375     buf = xmlBufferCreate();
    376     if (buf == NULL) {
    377         fprintf(stderr, "out of memory !\n");
    378 	fatalError();
    379     }
    380     xmlNodeDump(buf, tst->doc, tst, 0, 0);
    381 
    382     while (tst != NULL) {
    383 	test = getNext(tst, "./*");
    384 	if (test != NULL) {
    385 	    xmlBufferEmpty(buf);
    386 	    xmlNodeDump(buf, test->doc, test, 0, 0);
    387 	    name = getString(tst, "string(@name)");
    388 	    content = xmlStrdup(buf->content);
    389 	    if ((name != NULL) && (content != NULL)) {
    390 	        res = composeDir(base, name);
    391 		xmlFree(name);
    392 	        addEntity((char *) res, (char *) content);
    393 	    } else {
    394 	        if (name != NULL) xmlFree(name);
    395 	        if (content != NULL) xmlFree(content);
    396 	    }
    397 	}
    398 	tst = getNext(tst, "following-sibling::resource[1]");
    399     }
    400     if (buf != NULL)
    401 	xmlBufferFree(buf);
    402 }
    403 
    404 static void
    405 installDirs(xmlNodePtr tst, const xmlChar *base) {
    406     xmlNodePtr test;
    407     xmlChar *name, *res;
    408 
    409     name = getString(tst, "string(@name)");
    410     if (name == NULL)
    411         return;
    412     res = composeDir(base, name);
    413     xmlFree(name);
    414     if (res == NULL) {
    415 	return;
    416     }
    417     /* Now process resources and subdir recursively */
    418     test = getNext(tst, "./resource[1]");
    419     if (test != NULL) {
    420         installResources(test, res);
    421     }
    422     test = getNext(tst, "./dir[1]");
    423     while (test != NULL) {
    424         installDirs(test, res);
    425 	test = getNext(test, "following-sibling::dir[1]");
    426     }
    427     xmlFree(res);
    428 }
    429 
    430 static int
    431 xsdTestCase(xmlNodePtr tst) {
    432     xmlNodePtr test, tmp, cur;
    433     xmlBufferPtr buf;
    434     xmlDocPtr doc = NULL;
    435     xmlRelaxNGParserCtxtPtr pctxt;
    436     xmlRelaxNGValidCtxtPtr ctxt;
    437     xmlRelaxNGPtr rng = NULL;
    438     int ret = 0, mem, memt;
    439     xmlChar *dtd;
    440 
    441     resetEntities();
    442     testErrorsSize = 0; testErrors[0] = 0;
    443 
    444     tmp = getNext(tst, "./dir[1]");
    445     if (tmp != NULL) {
    446         installDirs(tmp, NULL);
    447     }
    448     tmp = getNext(tst, "./resource[1]");
    449     if (tmp != NULL) {
    450         installResources(tmp, NULL);
    451     }
    452 
    453     cur = getNext(tst, "./correct[1]");
    454     if (cur == NULL) {
    455         return(xsdIncorectTestCase(tst));
    456     }
    457 
    458     test = getNext(cur, "./*");
    459     if (test == NULL) {
    460         fprintf(stderr, "Failed to find test in correct line %ld\n",
    461 	        xmlGetLineNo(cur));
    462         return(1);
    463     }
    464 
    465     memt = xmlMemUsed();
    466     extraMemoryFromResolver = 0;
    467     /*
    468      * dump the schemas to a buffer, then reparse it and compile the schemas
    469      */
    470     buf = xmlBufferCreate();
    471     if (buf == NULL) {
    472         fprintf(stderr, "out of memory !\n");
    473 	fatalError();
    474     }
    475     xmlNodeDump(buf, test->doc, test, 0, 0);
    476     pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
    477     xmlRelaxNGSetParserErrors(pctxt,
    478          (xmlRelaxNGValidityErrorFunc) testErrorHandler,
    479          (xmlRelaxNGValidityWarningFunc) testErrorHandler,
    480 	 pctxt);
    481     rng = xmlRelaxNGParse(pctxt);
    482     xmlRelaxNGFreeParserCtxt(pctxt);
    483     if (extraMemoryFromResolver)
    484         memt = 0;
    485 
    486     if (rng == NULL) {
    487         test_log("Failed to parse RNGtest line %ld\n",
    488 	        xmlGetLineNo(test));
    489 	nb_errors++;
    490         ret = 1;
    491 	goto done;
    492     }
    493     /*
    494      * now scan all the siblings of correct to process the <valid> tests
    495      */
    496     tmp = getNext(cur, "following-sibling::valid[1]");
    497     while (tmp != NULL) {
    498 	dtd = xmlGetProp(tmp, BAD_CAST "dtd");
    499 	test = getNext(tmp, "./*");
    500 	if (test == NULL) {
    501 	    fprintf(stderr, "Failed to find test in <valid> line %ld\n",
    502 		    xmlGetLineNo(tmp));
    503 
    504 	} else {
    505 	    xmlBufferEmpty(buf);
    506 	    if (dtd != NULL)
    507 		xmlBufferAdd(buf, dtd, -1);
    508 	    xmlNodeDump(buf, test->doc, test, 0, 0);
    509 
    510 	    /*
    511 	     * We are ready to run the test
    512 	     */
    513 	    mem = xmlMemUsed();
    514 	    extraMemoryFromResolver = 0;
    515             doc = xmlReadMemory((const char *)buf->content, buf->use,
    516 	                        "test", NULL, 0);
    517 	    if (doc == NULL) {
    518 		test_log("Failed to parse valid instance line %ld\n",
    519 			xmlGetLineNo(tmp));
    520 		nb_errors++;
    521 	    } else {
    522 		nb_tests++;
    523 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
    524 		xmlRelaxNGSetValidErrors(ctxt,
    525 		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
    526 		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
    527 		     ctxt);
    528 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
    529 		xmlRelaxNGFreeValidCtxt(ctxt);
    530 		if (ret > 0) {
    531 		    test_log("Failed to validate valid instance line %ld\n",
    532 				xmlGetLineNo(tmp));
    533 		    nb_errors++;
    534 		} else if (ret < 0) {
    535 		    test_log("Internal error validating instance line %ld\n",
    536 			    xmlGetLineNo(tmp));
    537 		    nb_errors++;
    538 		}
    539 		xmlFreeDoc(doc);
    540 	    }
    541 	    xmlResetLastError();
    542 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
    543 	        test_log("Validation of instance line %ld leaked %d\n",
    544 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
    545 		xmlMemoryDump();
    546 	        nb_leaks++;
    547 	    }
    548 	}
    549 	if (dtd != NULL)
    550 	    xmlFree(dtd);
    551 	tmp = getNext(tmp, "following-sibling::valid[1]");
    552     }
    553     /*
    554      * now scan all the siblings of correct to process the <invalid> tests
    555      */
    556     tmp = getNext(cur, "following-sibling::invalid[1]");
    557     while (tmp != NULL) {
    558 	test = getNext(tmp, "./*");
    559 	if (test == NULL) {
    560 	    fprintf(stderr, "Failed to find test in <invalid> line %ld\n",
    561 		    xmlGetLineNo(tmp));
    562 
    563 	} else {
    564 	    xmlBufferEmpty(buf);
    565 	    xmlNodeDump(buf, test->doc, test, 0, 0);
    566 
    567 	    /*
    568 	     * We are ready to run the test
    569 	     */
    570 	    mem = xmlMemUsed();
    571 	    extraMemoryFromResolver = 0;
    572             doc = xmlReadMemory((const char *)buf->content, buf->use,
    573 	                        "test", NULL, 0);
    574 	    if (doc == NULL) {
    575 		test_log("Failed to parse valid instance line %ld\n",
    576 			xmlGetLineNo(tmp));
    577 		nb_errors++;
    578 	    } else {
    579 		nb_tests++;
    580 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
    581 		xmlRelaxNGSetValidErrors(ctxt,
    582 		     (xmlRelaxNGValidityErrorFunc) testErrorHandler,
    583 		     (xmlRelaxNGValidityWarningFunc) testErrorHandler,
    584 		     ctxt);
    585 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
    586 		xmlRelaxNGFreeValidCtxt(ctxt);
    587 		if (ret == 0) {
    588 		    test_log("Failed to detect invalid instance line %ld\n",
    589 				xmlGetLineNo(tmp));
    590 		    nb_errors++;
    591 		} else if (ret < 0) {
    592 		    test_log("Internal error validating instance line %ld\n",
    593 			    xmlGetLineNo(tmp));
    594 		    nb_errors++;
    595 		}
    596 		xmlFreeDoc(doc);
    597 	    }
    598 	    xmlResetLastError();
    599 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
    600 	        test_log("Validation of instance line %ld leaked %d\n",
    601 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
    602 		xmlMemoryDump();
    603 	        nb_leaks++;
    604 	    }
    605 	}
    606 	tmp = getNext(tmp, "following-sibling::invalid[1]");
    607     }
    608 
    609 done:
    610     if (buf != NULL)
    611 	xmlBufferFree(buf);
    612     if (rng != NULL)
    613         xmlRelaxNGFree(rng);
    614     xmlResetLastError();
    615     if ((memt != xmlMemUsed()) && (memt != 0)) {
    616 	test_log("Validation of tests starting line %ld leaked %d\n",
    617 		xmlGetLineNo(cur), xmlMemUsed() - memt);
    618 	nb_leaks++;
    619     }
    620     return(ret);
    621 }
    622 
    623 static int
    624 xsdTestSuite(xmlNodePtr cur) {
    625     if (verbose) {
    626 	xmlChar *doc = getString(cur, "string(documentation)");
    627 
    628 	if (doc != NULL) {
    629 	    printf("Suite %s\n", doc);
    630 	    xmlFree(doc);
    631 	}
    632     }
    633     cur = getNext(cur, "./testCase[1]");
    634     while (cur != NULL) {
    635         xsdTestCase(cur);
    636 	cur = getNext(cur, "following-sibling::testCase[1]");
    637     }
    638 
    639     return(0);
    640 }
    641 
    642 static int
    643 xsdTest(void) {
    644     xmlDocPtr doc;
    645     xmlNodePtr cur;
    646     const char *filename = "test/xsdtest/xsdtestsuite.xml";
    647     int ret = 0;
    648 
    649     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
    650     if (doc == NULL) {
    651         fprintf(stderr, "Failed to parse %s\n", filename);
    652 	return(-1);
    653     }
    654     printf("## XML Schemas datatypes test suite from James Clark\n");
    655 
    656     cur = xmlDocGetRootElement(doc);
    657     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    658         fprintf(stderr, "Unexpected format %s\n", filename);
    659 	ret = -1;
    660 	goto done;
    661     }
    662 
    663     cur = getNext(cur, "./testSuite[1]");
    664     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    665         fprintf(stderr, "Unexpected format %s\n", filename);
    666 	ret = -1;
    667 	goto done;
    668     }
    669     while (cur != NULL) {
    670         xsdTestSuite(cur);
    671 	cur = getNext(cur, "following-sibling::testSuite[1]");
    672     }
    673 
    674 done:
    675     if (doc != NULL)
    676 	xmlFreeDoc(doc);
    677     return(ret);
    678 }
    679 
    680 static int
    681 rngTestSuite(xmlNodePtr cur) {
    682     if (verbose) {
    683 	xmlChar *doc = getString(cur, "string(documentation)");
    684 
    685 	if (doc != NULL) {
    686 	    printf("Suite %s\n", doc);
    687 	    xmlFree(doc);
    688 	} else {
    689 	    doc = getString(cur, "string(section)");
    690 	    if (doc != NULL) {
    691 		printf("Section %s\n", doc);
    692 		xmlFree(doc);
    693 	    }
    694 	}
    695     }
    696     cur = getNext(cur, "./testSuite[1]");
    697     while (cur != NULL) {
    698         xsdTestSuite(cur);
    699 	cur = getNext(cur, "following-sibling::testSuite[1]");
    700     }
    701 
    702     return(0);
    703 }
    704 
    705 static int
    706 rngTest1(void) {
    707     xmlDocPtr doc;
    708     xmlNodePtr cur;
    709     const char *filename = "test/relaxng/OASIS/spectest.xml";
    710     int ret = 0;
    711 
    712     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
    713     if (doc == NULL) {
    714         fprintf(stderr, "Failed to parse %s\n", filename);
    715 	return(-1);
    716     }
    717     printf("## Relax NG test suite from James Clark\n");
    718 
    719     cur = xmlDocGetRootElement(doc);
    720     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    721         fprintf(stderr, "Unexpected format %s\n", filename);
    722 	ret = -1;
    723 	goto done;
    724     }
    725 
    726     cur = getNext(cur, "./testSuite[1]");
    727     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    728         fprintf(stderr, "Unexpected format %s\n", filename);
    729 	ret = -1;
    730 	goto done;
    731     }
    732     while (cur != NULL) {
    733         rngTestSuite(cur);
    734 	cur = getNext(cur, "following-sibling::testSuite[1]");
    735     }
    736 
    737 done:
    738     if (doc != NULL)
    739 	xmlFreeDoc(doc);
    740     return(ret);
    741 }
    742 
    743 static int
    744 rngTest2(void) {
    745     xmlDocPtr doc;
    746     xmlNodePtr cur;
    747     const char *filename = "test/relaxng/testsuite.xml";
    748     int ret = 0;
    749 
    750     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
    751     if (doc == NULL) {
    752         fprintf(stderr, "Failed to parse %s\n", filename);
    753 	return(-1);
    754     }
    755     printf("## Relax NG test suite for libxml2\n");
    756 
    757     cur = xmlDocGetRootElement(doc);
    758     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    759         fprintf(stderr, "Unexpected format %s\n", filename);
    760 	ret = -1;
    761 	goto done;
    762     }
    763 
    764     cur = getNext(cur, "./testSuite[1]");
    765     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
    766         fprintf(stderr, "Unexpected format %s\n", filename);
    767 	ret = -1;
    768 	goto done;
    769     }
    770     while (cur != NULL) {
    771         xsdTestSuite(cur);
    772 	cur = getNext(cur, "following-sibling::testSuite[1]");
    773     }
    774 
    775 done:
    776     if (doc != NULL)
    777 	xmlFreeDoc(doc);
    778     return(ret);
    779 }
    780 
    781 /************************************************************************
    782  *									*
    783  *		Schemas test suites from W3C/NIST/MS/Sun		*
    784  *									*
    785  ************************************************************************/
    786 
    787 static int
    788 xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas,
    789                  const xmlChar *spath, const char *base) {
    790     xmlChar *href = NULL;
    791     xmlChar *path = NULL;
    792     xmlChar *validity = NULL;
    793     xmlSchemaValidCtxtPtr ctxt = NULL;
    794     xmlDocPtr doc = NULL;
    795     int ret = 0, mem;
    796 
    797     xmlResetLastError();
    798     testErrorsSize = 0; testErrors[0] = 0;
    799     mem = xmlMemUsed();
    800     href = getString(cur,
    801                      "string(ts:instanceDocument/@xlink:href)");
    802     if ((href == NULL) || (href[0] == 0)) {
    803 	test_log("testGroup line %ld misses href for schemaDocument\n",
    804 		    xmlGetLineNo(cur));
    805 	ret = -1;
    806 	goto done;
    807     }
    808     path = xmlBuildURI(href, BAD_CAST base);
    809     if (path == NULL) {
    810 	fprintf(stderr,
    811 	        "Failed to build path to schemas testGroup line %ld : %s\n",
    812 		xmlGetLineNo(cur), href);
    813 	ret = -1;
    814 	goto done;
    815     }
    816     if (checkTestFile((const char *) path) <= 0) {
    817 	test_log("schemas for testGroup line %ld is missing: %s\n",
    818 		xmlGetLineNo(cur), path);
    819 	ret = -1;
    820 	goto done;
    821     }
    822     validity = getString(cur,
    823                          "string(ts:expected/@validity)");
    824     if (validity == NULL) {
    825         fprintf(stderr, "instanceDocument line %ld misses expected validity\n",
    826 	        xmlGetLineNo(cur));
    827 	ret = -1;
    828 	goto done;
    829     }
    830     nb_tests++;
    831     doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT);
    832     if (doc == NULL) {
    833         fprintf(stderr, "instance %s fails to parse\n", path);
    834 	ret = -1;
    835 	nb_errors++;
    836 	goto done;
    837     }
    838 
    839     ctxt = xmlSchemaNewValidCtxt(schemas);
    840     xmlSchemaSetValidErrors(ctxt,
    841          (xmlSchemaValidityErrorFunc) testErrorHandler,
    842          (xmlSchemaValidityWarningFunc) testErrorHandler,
    843 	 ctxt);
    844     ret = xmlSchemaValidateDoc(ctxt, doc);
    845 
    846     if (xmlStrEqual(validity, BAD_CAST "valid")) {
    847 	if (ret > 0) {
    848 	    test_log("valid instance %s failed to validate against %s\n",
    849 			path, spath);
    850 	    nb_errors++;
    851 	} else if (ret < 0) {
    852 	    test_log("valid instance %s got internal error validating %s\n",
    853 			path, spath);
    854 	    nb_internals++;
    855 	    nb_errors++;
    856 	}
    857     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
    858 	if (ret == 0) {
    859 	    test_log("Failed to detect invalid instance %s against %s\n",
    860 			path, spath);
    861 	    nb_errors++;
    862 	}
    863     } else {
    864         test_log("instanceDocument line %ld has unexpected validity value%s\n",
    865 	        xmlGetLineNo(cur), validity);
    866 	ret = -1;
    867 	goto done;
    868     }
    869 
    870 done:
    871     if (href != NULL) xmlFree(href);
    872     if (path != NULL) xmlFree(path);
    873     if (validity != NULL) xmlFree(validity);
    874     if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt);
    875     if (doc != NULL) xmlFreeDoc(doc);
    876     xmlResetLastError();
    877     if (mem != xmlMemUsed()) {
    878 	test_log("Validation of tests starting line %ld leaked %d\n",
    879 		xmlGetLineNo(cur), xmlMemUsed() - mem);
    880 	nb_leaks++;
    881     }
    882     return(ret);
    883 }
    884 
    885 static int
    886 xstcTestGroup(xmlNodePtr cur, const char *base) {
    887     xmlChar *href = NULL;
    888     xmlChar *path = NULL;
    889     xmlChar *validity = NULL;
    890     xmlSchemaPtr schemas = NULL;
    891     xmlSchemaParserCtxtPtr ctxt;
    892     xmlNodePtr instance;
    893     int ret = 0, mem;
    894 
    895     xmlResetLastError();
    896     testErrorsSize = 0; testErrors[0] = 0;
    897     mem = xmlMemUsed();
    898     href = getString(cur,
    899                      "string(ts:schemaTest/ts:schemaDocument/@xlink:href)");
    900     if ((href == NULL) || (href[0] == 0)) {
    901         test_log("testGroup line %ld misses href for schemaDocument\n",
    902 		    xmlGetLineNo(cur));
    903 	ret = -1;
    904 	goto done;
    905     }
    906     path = xmlBuildURI(href, BAD_CAST base);
    907     if (path == NULL) {
    908 	test_log("Failed to build path to schemas testGroup line %ld : %s\n",
    909 		xmlGetLineNo(cur), href);
    910 	ret = -1;
    911 	goto done;
    912     }
    913     if (checkTestFile((const char *) path) <= 0) {
    914 	test_log("schemas for testGroup line %ld is missing: %s\n",
    915 		xmlGetLineNo(cur), path);
    916 	ret = -1;
    917 	goto done;
    918     }
    919     validity = getString(cur,
    920                          "string(ts:schemaTest/ts:expected/@validity)");
    921     if (validity == NULL) {
    922         test_log("testGroup line %ld misses expected validity\n",
    923 	        xmlGetLineNo(cur));
    924 	ret = -1;
    925 	goto done;
    926     }
    927     nb_tests++;
    928     if (xmlStrEqual(validity, BAD_CAST "valid")) {
    929         nb_schematas++;
    930 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
    931 	xmlSchemaSetParserErrors(ctxt,
    932 	     (xmlSchemaValidityErrorFunc) testErrorHandler,
    933 	     (xmlSchemaValidityWarningFunc) testErrorHandler,
    934 	     ctxt);
    935 	schemas = xmlSchemaParse(ctxt);
    936 	xmlSchemaFreeParserCtxt(ctxt);
    937 	if (schemas == NULL) {
    938 	    test_log("valid schemas %s failed to parse\n",
    939 			path);
    940 	    ret = 1;
    941 	    nb_errors++;
    942 	}
    943 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
    944 	    test_log("valid schemas %s hit an unimplemented block\n",
    945 			path);
    946 	    ret = 1;
    947 	    nb_unimplemented++;
    948 	    nb_errors++;
    949 	}
    950 	instance = getNext(cur, "./ts:instanceTest[1]");
    951 	while (instance != NULL) {
    952 	    if (schemas != NULL) {
    953 		xstcTestInstance(instance, schemas, path, base);
    954 	    } else {
    955 		/*
    956 		* We'll automatically mark the instances as failed
    957 		* if the schema was broken.
    958 		*/
    959 		nb_errors++;
    960 	    }
    961 	    instance = getNext(instance,
    962 		"following-sibling::ts:instanceTest[1]");
    963 	}
    964     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
    965         nb_schematas++;
    966 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
    967 	xmlSchemaSetParserErrors(ctxt,
    968 	     (xmlSchemaValidityErrorFunc) testErrorHandler,
    969 	     (xmlSchemaValidityWarningFunc) testErrorHandler,
    970 	     ctxt);
    971 	schemas = xmlSchemaParse(ctxt);
    972 	xmlSchemaFreeParserCtxt(ctxt);
    973 	if (schemas != NULL) {
    974 	    test_log("Failed to detect error in schemas %s\n",
    975 			path);
    976 	    nb_errors++;
    977 	    ret = 1;
    978 	}
    979 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
    980 	    nb_unimplemented++;
    981 	    test_log("invalid schemas %s hit an unimplemented block\n",
    982 			path);
    983 	    ret = 1;
    984 	    nb_errors++;
    985 	}
    986     } else {
    987         test_log("testGroup line %ld misses unexpected validity value%s\n",
    988 	        xmlGetLineNo(cur), validity);
    989 	ret = -1;
    990 	goto done;
    991     }
    992 
    993 done:
    994     if (href != NULL) xmlFree(href);
    995     if (path != NULL) xmlFree(path);
    996     if (validity != NULL) xmlFree(validity);
    997     if (schemas != NULL) xmlSchemaFree(schemas);
    998     xmlResetLastError();
    999     if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
   1000 	test_log("Processing test line %ld %s leaked %d\n",
   1001 		xmlGetLineNo(cur), path, xmlMemUsed() - mem);
   1002 	nb_leaks++;
   1003     }
   1004     return(ret);
   1005 }
   1006 
   1007 static int
   1008 xstcMetadata(const char *metadata, const char *base) {
   1009     xmlDocPtr doc;
   1010     xmlNodePtr cur;
   1011     xmlChar *contributor;
   1012     xmlChar *name;
   1013     int ret = 0;
   1014 
   1015     doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT);
   1016     if (doc == NULL) {
   1017         fprintf(stderr, "Failed to parse %s\n", metadata);
   1018 	return(-1);
   1019     }
   1020 
   1021     cur = xmlDocGetRootElement(doc);
   1022     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) {
   1023         fprintf(stderr, "Unexpected format %s\n", metadata);
   1024 	return(-1);
   1025     }
   1026     contributor = xmlGetProp(cur, BAD_CAST "contributor");
   1027     if (contributor == NULL) {
   1028         contributor = xmlStrdup(BAD_CAST "Unknown");
   1029     }
   1030     name = xmlGetProp(cur, BAD_CAST "name");
   1031     if (name == NULL) {
   1032         name = xmlStrdup(BAD_CAST "Unknown");
   1033     }
   1034     printf("## %s test suite for Schemas version %s\n", contributor, name);
   1035     xmlFree(contributor);
   1036     xmlFree(name);
   1037 
   1038     cur = getNext(cur, "./ts:testGroup[1]");
   1039     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) {
   1040         fprintf(stderr, "Unexpected format %s\n", metadata);
   1041 	ret = -1;
   1042 	goto done;
   1043     }
   1044     while (cur != NULL) {
   1045         xstcTestGroup(cur, base);
   1046 	cur = getNext(cur, "following-sibling::ts:testGroup[1]");
   1047     }
   1048 
   1049 done:
   1050     xmlFreeDoc(doc);
   1051     return(ret);
   1052 }
   1053 
   1054 /************************************************************************
   1055  *									*
   1056  *		The driver for the tests				*
   1057  *									*
   1058  ************************************************************************/
   1059 
   1060 int
   1061 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
   1062     int ret = 0;
   1063     int old_errors, old_tests, old_leaks;
   1064 
   1065     logfile = fopen(LOGFILE, "w");
   1066     if (logfile == NULL) {
   1067         fprintf(stderr,
   1068 	        "Could not open the log file, running in verbose mode\n");
   1069 	verbose = 1;
   1070     }
   1071     initializeLibxml2();
   1072 
   1073     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
   1074         verbose = 1;
   1075 
   1076 
   1077     old_errors = nb_errors;
   1078     old_tests = nb_tests;
   1079     old_leaks = nb_leaks;
   1080     xsdTest();
   1081     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1082 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
   1083     else
   1084 	printf("Ran %d tests, %d errors, %d leaks\n",
   1085 	       nb_tests - old_tests,
   1086 	       nb_errors - old_errors,
   1087 	       nb_leaks - old_leaks);
   1088     old_errors = nb_errors;
   1089     old_tests = nb_tests;
   1090     old_leaks = nb_leaks;
   1091     rngTest1();
   1092     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1093 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
   1094     else
   1095 	printf("Ran %d tests, %d errors, %d leaks\n",
   1096 	       nb_tests - old_tests,
   1097 	       nb_errors - old_errors,
   1098 	       nb_leaks - old_leaks);
   1099     old_errors = nb_errors;
   1100     old_tests = nb_tests;
   1101     old_leaks = nb_leaks;
   1102     rngTest2();
   1103     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1104 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
   1105     else
   1106 	printf("Ran %d tests, %d errors, %d leaks\n",
   1107 	       nb_tests - old_tests,
   1108 	       nb_errors - old_errors,
   1109 	       nb_leaks - old_leaks);
   1110     old_errors = nb_errors;
   1111     old_tests = nb_tests;
   1112     old_leaks = nb_leaks;
   1113     nb_internals = 0;
   1114     nb_schematas = 0;
   1115     xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet",
   1116 		 "xstc/Tests/Metadata/");
   1117     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1118 	printf("Ran %d tests (%d schemata), no errors\n",
   1119 	       nb_tests - old_tests, nb_schematas);
   1120     else
   1121 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
   1122 	       nb_tests - old_tests,
   1123 	       nb_schematas,
   1124 	       nb_errors - old_errors,
   1125 	       nb_internals,
   1126 	       nb_leaks - old_leaks);
   1127     old_errors = nb_errors;
   1128     old_tests = nb_tests;
   1129     old_leaks = nb_leaks;
   1130     nb_internals = 0;
   1131     nb_schematas = 0;
   1132     xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet",
   1133 		 "xstc/Tests/");
   1134     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1135 	printf("Ran %d tests (%d schemata), no errors\n",
   1136 	       nb_tests - old_tests, nb_schematas);
   1137     else
   1138 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
   1139 	       nb_tests - old_tests,
   1140 	       nb_schematas,
   1141 	       nb_errors - old_errors,
   1142 	       nb_internals,
   1143 	       nb_leaks - old_leaks);
   1144     old_errors = nb_errors;
   1145     old_tests = nb_tests;
   1146     old_leaks = nb_leaks;
   1147     nb_internals = 0;
   1148     nb_schematas = 0;
   1149     xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet",
   1150 		 "xstc/Tests/");
   1151     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
   1152 	printf("Ran %d tests (%d schemata), no errors\n",
   1153 	       nb_tests - old_tests, nb_schematas);
   1154     else
   1155 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
   1156 	       nb_tests - old_tests,
   1157 	       nb_schematas,
   1158 	       nb_errors - old_errors,
   1159 	       nb_internals,
   1160 	       nb_leaks - old_leaks);
   1161 
   1162     if ((nb_errors == 0) && (nb_leaks == 0)) {
   1163         ret = 0;
   1164 	printf("Total %d tests, no errors\n",
   1165 	       nb_tests);
   1166     } else {
   1167         ret = 1;
   1168 	printf("Total %d tests, %d errors, %d leaks\n",
   1169 	       nb_tests, nb_errors, nb_leaks);
   1170     }
   1171     xmlXPathFreeContext(ctxtXPath);
   1172     xmlCleanupParser();
   1173     xmlMemoryDump();
   1174 
   1175     if (logfile != NULL)
   1176         fclose(logfile);
   1177     return(ret);
   1178 }
   1179 #else /* !SCHEMAS */
   1180 int
   1181 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
   1182     fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n");
   1183 }
   1184 #endif
   1185