1/*
2 * runsuite.c: C program to run libxml2 againts published testsuites
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#ifdef HAVE_CONFIG_H
10#include "libxml.h"
11#else
12#include <stdio.h>
13#endif
14
15#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"
38static FILE *logfile = NULL;
39static 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
57static 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
74static 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
90static int nb_tests = 0;
91static int nb_errors = 0;
92static int nb_internals = 0;
93static int nb_schematas = 0;
94static int nb_unimplemented = 0;
95static int nb_leaks = 0;
96static int extraMemoryFromResolver = 0;
97
98static int
99fatalError(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
108static char *testEntitiesName[MAX_ENTITIES];
109static char *testEntitiesValue[MAX_ENTITIES];
110static int nb_entities = 0;
111static 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}
122static 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 */
138static xmlParserInputPtr
139testExternalEntityLoader(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 */
175static char testErrors[32769];
176static int testErrorsSize = 0;
177
178static 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
195static void
196testErrorHandler(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
217static xmlXPathContextPtr ctxtXPath;
218
219static void
220initializeLibxml2(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
249static xmlNodePtr
250getNext(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
277static xmlChar *
278getString(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
310static int
311xsdIncorectTestCase(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
355done:
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
369static void
370installResources(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
404static void
405installDirs(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
430static int
431xsdTestCase(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
609done:
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
623static int
624xsdTestSuite(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
642static int
643xsdTest(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
674done:
675    if (doc != NULL)
676	xmlFreeDoc(doc);
677    return(ret);
678}
679
680static int
681rngTestSuite(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
705static int
706rngTest1(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
737done:
738    if (doc != NULL)
739	xmlFreeDoc(doc);
740    return(ret);
741}
742
743static int
744rngTest2(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
775done:
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
787static int
788xstcTestInstance(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
870done:
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
885static int
886xstcTestGroup(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
993done:
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
1007static int
1008xstcMetadata(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
1049done:
1050    xmlFreeDoc(doc);
1051    return(ret);
1052}
1053
1054/************************************************************************
1055 *									*
1056 *		The driver for the tests				*
1057 *									*
1058 ************************************************************************/
1059
1060int
1061main(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 */
1180int
1181main(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