1/*
2 * runsuite.c: C program to run libxml2 againts published testsuites
3 *
4 * See Copyright for the status of this software.
5 *
6 * daniel@veillard.com
7 */
8
9#include "libxml.h"
10#include <stdio.h>
11
12#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"
35static FILE *logfile = NULL;
36static int verbose = 0;
37
38
39/************************************************************************
40 *									*
41 *		File name and path utilities				*
42 *									*
43 ************************************************************************/
44
45static 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
62static 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
78static int nb_tests = 0;
79static int nb_errors = 0;
80static int nb_internals = 0;
81static int nb_schematas = 0;
82static int nb_unimplemented = 0;
83static int nb_leaks = 0;
84static int extraMemoryFromResolver = 0;
85
86static int
87fatalError(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
96static char *testEntitiesName[MAX_ENTITIES];
97static char *testEntitiesValue[MAX_ENTITIES];
98static int nb_entities = 0;
99static 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}
110static 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 */
126static xmlParserInputPtr
127testExternalEntityLoader(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 */
163static char testErrors[32769];
164static int testErrorsSize = 0;
165
166static 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
183static void
184testErrorHandler(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
205static xmlXPathContextPtr ctxtXPath;
206
207static void
208initializeLibxml2(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
237static xmlNodePtr
238getNext(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
265static xmlChar *
266getString(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
298static int
299xsdIncorectTestCase(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
343done:
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
357static void
358installResources(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
392static void
393installDirs(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
418static int
419xsdTestCase(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
597done:
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
611static int
612xsdTestSuite(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
630static int
631xsdTest(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
662done:
663    if (doc != NULL)
664	xmlFreeDoc(doc);
665    return(ret);
666}
667
668static int
669rngTestSuite(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
693static int
694rngTest1(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
725done:
726    if (doc != NULL)
727	xmlFreeDoc(doc);
728    return(ret);
729}
730
731static int
732rngTest2(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
763done:
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
775static int
776xstcTestInstance(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
858done:
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
873static int
874xstcTestGroup(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
981done:
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
995static int
996xstcMetadata(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
1037done:
1038    xmlFreeDoc(doc);
1039    return(ret);
1040}
1041
1042/************************************************************************
1043 *									*
1044 *		The driver for the tests				*
1045 *									*
1046 ************************************************************************/
1047
1048int
1049main(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 */
1168int
1169main(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