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