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"
35 static FILE *logfile = NULL;
36 static int verbose = 0;
37 
38 
39 /************************************************************************
40  *									*
41  *		File name and path utilities				*
42  *									*
43  ************************************************************************/
44 
checkTestFile(const char * filename)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 
composeDir(const xmlChar * dir,const xmlChar * path)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
fatalError(void)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;
resetEntities(void)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 }
addEntity(char * name,char * content)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
testExternalEntityLoader(const char * URL,const char * ID,xmlParserCtxtPtr ctxt)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 
test_log(const char * msg,...)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
testErrorHandler(void * ctx ATTRIBUTE_UNUSED,const char * msg,...)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
initializeLibxml2(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
getNext(xmlNodePtr cur,const char * xpath)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 *
getString(xmlNodePtr cur,const char * xpath)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
xsdIncorectTestCase(xmlNodePtr cur)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, testErrorHandler, testErrorHandler,
331             pctxt);
332     rng = xmlRelaxNGParse(pctxt);
333     xmlRelaxNGFreeParserCtxt(pctxt);
334     if (rng != NULL) {
335 	test_log("Failed to detect incorect RNG line %ld\n",
336 		    xmlGetLineNo(test));
337         ret = 1;
338 	goto done;
339     }
340 
341 done:
342     if (buf != NULL)
343 	xmlBufferFree(buf);
344     if (rng != NULL)
345         xmlRelaxNGFree(rng);
346     xmlResetLastError();
347     if ((memt < xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
348 	test_log("Validation of tests starting line %ld leaked %d\n",
349 		xmlGetLineNo(cur), xmlMemUsed() - memt);
350 	nb_leaks++;
351     }
352     return(ret);
353 }
354 
355 static void
installResources(xmlNodePtr tst,const xmlChar * base)356 installResources(xmlNodePtr tst, const xmlChar *base) {
357     xmlNodePtr test;
358     xmlBufferPtr buf;
359     xmlChar *name, *content, *res;
360 
361     buf = xmlBufferCreate();
362     if (buf == NULL) {
363         fprintf(stderr, "out of memory !\n");
364 	fatalError();
365     }
366     xmlNodeDump(buf, tst->doc, tst, 0, 0);
367 
368     while (tst != NULL) {
369 	test = getNext(tst, "./*");
370 	if (test != NULL) {
371 	    xmlBufferEmpty(buf);
372 	    xmlNodeDump(buf, test->doc, test, 0, 0);
373 	    name = getString(tst, "string(@name)");
374 	    content = xmlStrdup(buf->content);
375 	    if ((name != NULL) && (content != NULL)) {
376 	        res = composeDir(base, name);
377 		xmlFree(name);
378 	        addEntity((char *) res, (char *) content);
379 	    } else {
380 	        if (name != NULL) xmlFree(name);
381 	        if (content != NULL) xmlFree(content);
382 	    }
383 	}
384 	tst = getNext(tst, "following-sibling::resource[1]");
385     }
386     if (buf != NULL)
387 	xmlBufferFree(buf);
388 }
389 
390 static void
installDirs(xmlNodePtr tst,const xmlChar * base)391 installDirs(xmlNodePtr tst, const xmlChar *base) {
392     xmlNodePtr test;
393     xmlChar *name, *res;
394 
395     name = getString(tst, "string(@name)");
396     if (name == NULL)
397         return;
398     res = composeDir(base, name);
399     xmlFree(name);
400     if (res == NULL) {
401 	return;
402     }
403     /* Now process resources and subdir recursively */
404     test = getNext(tst, "./resource[1]");
405     if (test != NULL) {
406         installResources(test, res);
407     }
408     test = getNext(tst, "./dir[1]");
409     while (test != NULL) {
410         installDirs(test, res);
411 	test = getNext(test, "following-sibling::dir[1]");
412     }
413     xmlFree(res);
414 }
415 
416 static int
xsdTestCase(xmlNodePtr tst)417 xsdTestCase(xmlNodePtr tst) {
418     xmlNodePtr test, tmp, cur;
419     xmlBufferPtr buf;
420     xmlDocPtr doc = NULL;
421     xmlRelaxNGParserCtxtPtr pctxt;
422     xmlRelaxNGValidCtxtPtr ctxt;
423     xmlRelaxNGPtr rng = NULL;
424     int ret = 0, mem, memt;
425     xmlChar *dtd;
426 
427     resetEntities();
428     testErrorsSize = 0; testErrors[0] = 0;
429 
430     tmp = getNext(tst, "./dir[1]");
431     if (tmp != NULL) {
432         installDirs(tmp, NULL);
433     }
434     tmp = getNext(tst, "./resource[1]");
435     if (tmp != NULL) {
436         installResources(tmp, NULL);
437     }
438 
439     cur = getNext(tst, "./correct[1]");
440     if (cur == NULL) {
441         return(xsdIncorectTestCase(tst));
442     }
443 
444     test = getNext(cur, "./*");
445     if (test == NULL) {
446         fprintf(stderr, "Failed to find test in correct line %ld\n",
447 	        xmlGetLineNo(cur));
448         return(1);
449     }
450 
451     memt = xmlMemUsed();
452     extraMemoryFromResolver = 0;
453     /*
454      * dump the schemas to a buffer, then reparse it and compile the schemas
455      */
456     buf = xmlBufferCreate();
457     if (buf == NULL) {
458         fprintf(stderr, "out of memory !\n");
459 	fatalError();
460     }
461     xmlNodeDump(buf, test->doc, test, 0, 0);
462     pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use);
463     xmlRelaxNGSetParserErrors(pctxt, testErrorHandler, testErrorHandler,
464             pctxt);
465     rng = xmlRelaxNGParse(pctxt);
466     xmlRelaxNGFreeParserCtxt(pctxt);
467     if (extraMemoryFromResolver)
468         memt = 0;
469 
470     if (rng == NULL) {
471         test_log("Failed to parse RNGtest line %ld\n",
472 	        xmlGetLineNo(test));
473 	nb_errors++;
474         ret = 1;
475 	goto done;
476     }
477     /*
478      * now scan all the siblings of correct to process the <valid> tests
479      */
480     tmp = getNext(cur, "following-sibling::valid[1]");
481     while (tmp != NULL) {
482 	dtd = xmlGetProp(tmp, BAD_CAST "dtd");
483 	test = getNext(tmp, "./*");
484 	if (test == NULL) {
485 	    fprintf(stderr, "Failed to find test in <valid> line %ld\n",
486 		    xmlGetLineNo(tmp));
487 
488 	} else {
489 	    xmlBufferEmpty(buf);
490 	    if (dtd != NULL)
491 		xmlBufferAdd(buf, dtd, -1);
492 	    xmlNodeDump(buf, test->doc, test, 0, 0);
493 
494 	    /*
495 	     * We are ready to run the test
496 	     */
497 	    mem = xmlMemUsed();
498 	    extraMemoryFromResolver = 0;
499             doc = xmlReadMemory((const char *)buf->content, buf->use,
500 	                        "test", NULL, 0);
501 	    if (doc == NULL) {
502 		test_log("Failed to parse valid instance line %ld\n",
503 			xmlGetLineNo(tmp));
504 		nb_errors++;
505 	    } else {
506 		nb_tests++;
507 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
508 		xmlRelaxNGSetValidErrors(ctxt,
509                         testErrorHandler, testErrorHandler, ctxt);
510 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
511 		xmlRelaxNGFreeValidCtxt(ctxt);
512 		if (ret > 0) {
513 		    test_log("Failed to validate valid instance line %ld\n",
514 				xmlGetLineNo(tmp));
515 		    nb_errors++;
516 		} else if (ret < 0) {
517 		    test_log("Internal error validating instance line %ld\n",
518 			    xmlGetLineNo(tmp));
519 		    nb_errors++;
520 		}
521 		xmlFreeDoc(doc);
522 	    }
523 	    xmlResetLastError();
524 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
525 	        test_log("Validation of instance line %ld leaked %d\n",
526 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
527 		xmlMemoryDump();
528 	        nb_leaks++;
529 	    }
530 	}
531 	if (dtd != NULL)
532 	    xmlFree(dtd);
533 	tmp = getNext(tmp, "following-sibling::valid[1]");
534     }
535     /*
536      * now scan all the siblings of correct to process the <invalid> tests
537      */
538     tmp = getNext(cur, "following-sibling::invalid[1]");
539     while (tmp != NULL) {
540 	test = getNext(tmp, "./*");
541 	if (test == NULL) {
542 	    fprintf(stderr, "Failed to find test in <invalid> line %ld\n",
543 		    xmlGetLineNo(tmp));
544 
545 	} else {
546 	    xmlBufferEmpty(buf);
547 	    xmlNodeDump(buf, test->doc, test, 0, 0);
548 
549 	    /*
550 	     * We are ready to run the test
551 	     */
552 	    mem = xmlMemUsed();
553 	    extraMemoryFromResolver = 0;
554             doc = xmlReadMemory((const char *)buf->content, buf->use,
555 	                        "test", NULL, 0);
556 	    if (doc == NULL) {
557 		test_log("Failed to parse valid instance line %ld\n",
558 			xmlGetLineNo(tmp));
559 		nb_errors++;
560 	    } else {
561 		nb_tests++;
562 	        ctxt = xmlRelaxNGNewValidCtxt(rng);
563 		xmlRelaxNGSetValidErrors(ctxt,
564                         testErrorHandler, testErrorHandler, ctxt);
565 		ret = xmlRelaxNGValidateDoc(ctxt, doc);
566 		xmlRelaxNGFreeValidCtxt(ctxt);
567 		if (ret == 0) {
568 		    test_log("Failed to detect invalid instance line %ld\n",
569 				xmlGetLineNo(tmp));
570 		    nb_errors++;
571 		} else if (ret < 0) {
572 		    test_log("Internal error validating instance line %ld\n",
573 			    xmlGetLineNo(tmp));
574 		    nb_errors++;
575 		}
576 		xmlFreeDoc(doc);
577 	    }
578 	    xmlResetLastError();
579 	    if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
580 	        test_log("Validation of instance line %ld leaked %d\n",
581 		        xmlGetLineNo(tmp), xmlMemUsed() - mem);
582 		xmlMemoryDump();
583 	        nb_leaks++;
584 	    }
585 	}
586 	tmp = getNext(tmp, "following-sibling::invalid[1]");
587     }
588 
589 done:
590     if (buf != NULL)
591 	xmlBufferFree(buf);
592     if (rng != NULL)
593         xmlRelaxNGFree(rng);
594     xmlResetLastError();
595     if ((memt != xmlMemUsed()) && (memt != 0)) {
596 	test_log("Validation of tests starting line %ld leaked %d\n",
597 		xmlGetLineNo(cur), xmlMemUsed() - memt);
598 	nb_leaks++;
599     }
600     return(ret);
601 }
602 
603 static int
xsdTestSuite(xmlNodePtr cur)604 xsdTestSuite(xmlNodePtr cur) {
605     if (verbose) {
606 	xmlChar *doc = getString(cur, "string(documentation)");
607 
608 	if (doc != NULL) {
609 	    printf("Suite %s\n", doc);
610 	    xmlFree(doc);
611 	}
612     }
613     cur = getNext(cur, "./testCase[1]");
614     while (cur != NULL) {
615         xsdTestCase(cur);
616 	cur = getNext(cur, "following-sibling::testCase[1]");
617     }
618 
619     return(0);
620 }
621 
622 static int
xsdTest(void)623 xsdTest(void) {
624     xmlDocPtr doc;
625     xmlNodePtr cur;
626     const char *filename = "test/xsdtest/xsdtestsuite.xml";
627     int ret = 0;
628 
629     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
630     if (doc == NULL) {
631         fprintf(stderr, "Failed to parse %s\n", filename);
632 	return(-1);
633     }
634     printf("## XML Schemas datatypes test suite from James Clark\n");
635 
636     cur = xmlDocGetRootElement(doc);
637     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
638         fprintf(stderr, "Unexpected format %s\n", filename);
639 	ret = -1;
640 	goto done;
641     }
642 
643     cur = getNext(cur, "./testSuite[1]");
644     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
645         fprintf(stderr, "Unexpected format %s\n", filename);
646 	ret = -1;
647 	goto done;
648     }
649     while (cur != NULL) {
650         xsdTestSuite(cur);
651 	cur = getNext(cur, "following-sibling::testSuite[1]");
652     }
653 
654 done:
655     if (doc != NULL)
656 	xmlFreeDoc(doc);
657     return(ret);
658 }
659 
660 static int
rngTestSuite(xmlNodePtr cur)661 rngTestSuite(xmlNodePtr cur) {
662     if (verbose) {
663 	xmlChar *doc = getString(cur, "string(documentation)");
664 
665 	if (doc != NULL) {
666 	    printf("Suite %s\n", doc);
667 	    xmlFree(doc);
668 	} else {
669 	    doc = getString(cur, "string(section)");
670 	    if (doc != NULL) {
671 		printf("Section %s\n", doc);
672 		xmlFree(doc);
673 	    }
674 	}
675     }
676     cur = getNext(cur, "./testSuite[1]");
677     while (cur != NULL) {
678         xsdTestSuite(cur);
679 	cur = getNext(cur, "following-sibling::testSuite[1]");
680     }
681 
682     return(0);
683 }
684 
685 static int
rngTest1(void)686 rngTest1(void) {
687     xmlDocPtr doc;
688     xmlNodePtr cur;
689     const char *filename = "test/relaxng/OASIS/spectest.xml";
690     int ret = 0;
691 
692     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
693     if (doc == NULL) {
694         fprintf(stderr, "Failed to parse %s\n", filename);
695 	return(-1);
696     }
697     printf("## Relax NG test suite from James Clark\n");
698 
699     cur = xmlDocGetRootElement(doc);
700     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
701         fprintf(stderr, "Unexpected format %s\n", filename);
702 	ret = -1;
703 	goto done;
704     }
705 
706     cur = getNext(cur, "./testSuite[1]");
707     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
708         fprintf(stderr, "Unexpected format %s\n", filename);
709 	ret = -1;
710 	goto done;
711     }
712     while (cur != NULL) {
713         rngTestSuite(cur);
714 	cur = getNext(cur, "following-sibling::testSuite[1]");
715     }
716 
717 done:
718     if (doc != NULL)
719 	xmlFreeDoc(doc);
720     return(ret);
721 }
722 
723 static int
rngTest2(void)724 rngTest2(void) {
725     xmlDocPtr doc;
726     xmlNodePtr cur;
727     const char *filename = "test/relaxng/testsuite.xml";
728     int ret = 0;
729 
730     doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT);
731     if (doc == NULL) {
732         fprintf(stderr, "Failed to parse %s\n", filename);
733 	return(-1);
734     }
735     printf("## Relax NG test suite for libxml2\n");
736 
737     cur = xmlDocGetRootElement(doc);
738     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
739         fprintf(stderr, "Unexpected format %s\n", filename);
740 	ret = -1;
741 	goto done;
742     }
743 
744     cur = getNext(cur, "./testSuite[1]");
745     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) {
746         fprintf(stderr, "Unexpected format %s\n", filename);
747 	ret = -1;
748 	goto done;
749     }
750     while (cur != NULL) {
751         xsdTestSuite(cur);
752 	cur = getNext(cur, "following-sibling::testSuite[1]");
753     }
754 
755 done:
756     if (doc != NULL)
757 	xmlFreeDoc(doc);
758     return(ret);
759 }
760 
761 /************************************************************************
762  *									*
763  *		Schemas test suites from W3C/NIST/MS/Sun		*
764  *									*
765  ************************************************************************/
766 
767 static int
xstcTestInstance(xmlNodePtr cur,xmlSchemaPtr schemas,const xmlChar * spath,const char * base)768 xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas,
769                  const xmlChar *spath, const char *base) {
770     xmlChar *href = NULL;
771     xmlChar *path = NULL;
772     xmlChar *validity = NULL;
773     xmlSchemaValidCtxtPtr ctxt = NULL;
774     xmlDocPtr doc = NULL;
775     int ret = 0, mem;
776 
777     xmlResetLastError();
778     testErrorsSize = 0; testErrors[0] = 0;
779     mem = xmlMemUsed();
780     href = getString(cur,
781                      "string(ts:instanceDocument/@xlink:href)");
782     if ((href == NULL) || (href[0] == 0)) {
783 	test_log("testGroup line %ld misses href for schemaDocument\n",
784 		    xmlGetLineNo(cur));
785 	ret = -1;
786 	goto done;
787     }
788     path = xmlBuildURI(href, BAD_CAST base);
789     if (path == NULL) {
790 	fprintf(stderr,
791 	        "Failed to build path to schemas testGroup line %ld : %s\n",
792 		xmlGetLineNo(cur), href);
793 	ret = -1;
794 	goto done;
795     }
796     if (checkTestFile((const char *) path) <= 0) {
797 	test_log("schemas for testGroup line %ld is missing: %s\n",
798 		xmlGetLineNo(cur), path);
799 	ret = -1;
800 	goto done;
801     }
802     validity = getString(cur,
803                          "string(ts:expected/@validity)");
804     if (validity == NULL) {
805         fprintf(stderr, "instanceDocument line %ld misses expected validity\n",
806 	        xmlGetLineNo(cur));
807 	ret = -1;
808 	goto done;
809     }
810     nb_tests++;
811     doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT);
812     if (doc == NULL) {
813         fprintf(stderr, "instance %s fails to parse\n", path);
814 	ret = -1;
815 	nb_errors++;
816 	goto done;
817     }
818 
819     ctxt = xmlSchemaNewValidCtxt(schemas);
820     xmlSchemaSetValidErrors(ctxt, testErrorHandler, testErrorHandler, ctxt);
821     ret = xmlSchemaValidateDoc(ctxt, doc);
822 
823     if (xmlStrEqual(validity, BAD_CAST "valid")) {
824 	if (ret > 0) {
825 	    test_log("valid instance %s failed to validate against %s\n",
826 			path, spath);
827 	    nb_errors++;
828 	} else if (ret < 0) {
829 	    test_log("valid instance %s got internal error validating %s\n",
830 			path, spath);
831 	    nb_internals++;
832 	    nb_errors++;
833 	}
834     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
835 	if (ret == 0) {
836 	    test_log("Failed to detect invalid instance %s against %s\n",
837 			path, spath);
838 	    nb_errors++;
839 	}
840     } else {
841         test_log("instanceDocument line %ld has unexpected validity value%s\n",
842 	        xmlGetLineNo(cur), validity);
843 	ret = -1;
844 	goto done;
845     }
846 
847 done:
848     if (href != NULL) xmlFree(href);
849     if (path != NULL) xmlFree(path);
850     if (validity != NULL) xmlFree(validity);
851     if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt);
852     if (doc != NULL) xmlFreeDoc(doc);
853     xmlResetLastError();
854     if (mem != xmlMemUsed()) {
855 	test_log("Validation of tests starting line %ld leaked %d\n",
856 		xmlGetLineNo(cur), xmlMemUsed() - mem);
857 	nb_leaks++;
858     }
859     return(ret);
860 }
861 
862 static int
xstcTestGroup(xmlNodePtr cur,const char * base)863 xstcTestGroup(xmlNodePtr cur, const char *base) {
864     xmlChar *href = NULL;
865     xmlChar *path = NULL;
866     xmlChar *validity = NULL;
867     xmlSchemaPtr schemas = NULL;
868     xmlSchemaParserCtxtPtr ctxt;
869     xmlNodePtr instance;
870     int ret = 0, mem;
871 
872     xmlResetLastError();
873     testErrorsSize = 0; testErrors[0] = 0;
874     mem = xmlMemUsed();
875     href = getString(cur,
876                      "string(ts:schemaTest/ts:schemaDocument/@xlink:href)");
877     if ((href == NULL) || (href[0] == 0)) {
878         test_log("testGroup line %ld misses href for schemaDocument\n",
879 		    xmlGetLineNo(cur));
880 	ret = -1;
881 	goto done;
882     }
883     path = xmlBuildURI(href, BAD_CAST base);
884     if (path == NULL) {
885 	test_log("Failed to build path to schemas testGroup line %ld : %s\n",
886 		xmlGetLineNo(cur), href);
887 	ret = -1;
888 	goto done;
889     }
890     if (checkTestFile((const char *) path) <= 0) {
891 	test_log("schemas for testGroup line %ld is missing: %s\n",
892 		xmlGetLineNo(cur), path);
893 	ret = -1;
894 	goto done;
895     }
896     validity = getString(cur,
897                          "string(ts:schemaTest/ts:expected/@validity)");
898     if (validity == NULL) {
899         test_log("testGroup line %ld misses expected validity\n",
900 	        xmlGetLineNo(cur));
901 	ret = -1;
902 	goto done;
903     }
904     nb_tests++;
905     if (xmlStrEqual(validity, BAD_CAST "valid")) {
906         nb_schematas++;
907 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
908 	xmlSchemaSetParserErrors(ctxt, testErrorHandler, testErrorHandler,
909                 ctxt);
910 	schemas = xmlSchemaParse(ctxt);
911 	xmlSchemaFreeParserCtxt(ctxt);
912 	if (schemas == NULL) {
913 	    test_log("valid schemas %s failed to parse\n",
914 			path);
915 	    ret = 1;
916 	    nb_errors++;
917 	}
918 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
919 	    test_log("valid schemas %s hit an unimplemented block\n",
920 			path);
921 	    ret = 1;
922 	    nb_unimplemented++;
923 	    nb_errors++;
924 	}
925 	instance = getNext(cur, "./ts:instanceTest[1]");
926 	while (instance != NULL) {
927 	    if (schemas != NULL) {
928 		xstcTestInstance(instance, schemas, path, base);
929 	    } else {
930 		/*
931 		* We'll automatically mark the instances as failed
932 		* if the schema was broken.
933 		*/
934 		nb_errors++;
935 	    }
936 	    instance = getNext(instance,
937 		"following-sibling::ts:instanceTest[1]");
938 	}
939     } else if (xmlStrEqual(validity, BAD_CAST "invalid")) {
940         nb_schematas++;
941 	ctxt = xmlSchemaNewParserCtxt((const char *) path);
942 	xmlSchemaSetParserErrors(ctxt, testErrorHandler, testErrorHandler,
943                 ctxt);
944 	schemas = xmlSchemaParse(ctxt);
945 	xmlSchemaFreeParserCtxt(ctxt);
946 	if (schemas != NULL) {
947 	    test_log("Failed to detect error in schemas %s\n",
948 			path);
949 	    nb_errors++;
950 	    ret = 1;
951 	}
952 	if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) {
953 	    nb_unimplemented++;
954 	    test_log("invalid schemas %s hit an unimplemented block\n",
955 			path);
956 	    ret = 1;
957 	    nb_errors++;
958 	}
959     } else {
960         test_log("testGroup line %ld misses unexpected validity value%s\n",
961 	        xmlGetLineNo(cur), validity);
962 	ret = -1;
963 	goto done;
964     }
965 
966 done:
967     if (href != NULL) xmlFree(href);
968     if (path != NULL) xmlFree(path);
969     if (validity != NULL) xmlFree(validity);
970     if (schemas != NULL) xmlSchemaFree(schemas);
971     xmlResetLastError();
972     if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
973 	test_log("Processing test line %ld %s leaked %d\n",
974 		xmlGetLineNo(cur), path, xmlMemUsed() - mem);
975 	nb_leaks++;
976     }
977     return(ret);
978 }
979 
980 static int
xstcMetadata(const char * metadata,const char * base)981 xstcMetadata(const char *metadata, const char *base) {
982     xmlDocPtr doc;
983     xmlNodePtr cur;
984     xmlChar *contributor;
985     xmlChar *name;
986     int ret = 0;
987 
988     doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT);
989     if (doc == NULL) {
990         fprintf(stderr, "Failed to parse %s\n", metadata);
991 	return(-1);
992     }
993 
994     cur = xmlDocGetRootElement(doc);
995     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) {
996         fprintf(stderr, "Unexpected format %s\n", metadata);
997 	return(-1);
998     }
999     contributor = xmlGetProp(cur, BAD_CAST "contributor");
1000     if (contributor == NULL) {
1001         contributor = xmlStrdup(BAD_CAST "Unknown");
1002     }
1003     name = xmlGetProp(cur, BAD_CAST "name");
1004     if (name == NULL) {
1005         name = xmlStrdup(BAD_CAST "Unknown");
1006     }
1007     printf("## %s test suite for Schemas version %s\n", contributor, name);
1008     xmlFree(contributor);
1009     xmlFree(name);
1010 
1011     cur = getNext(cur, "./ts:testGroup[1]");
1012     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) {
1013         fprintf(stderr, "Unexpected format %s\n", metadata);
1014 	ret = -1;
1015 	goto done;
1016     }
1017     while (cur != NULL) {
1018         xstcTestGroup(cur, base);
1019 	cur = getNext(cur, "following-sibling::ts:testGroup[1]");
1020     }
1021 
1022 done:
1023     xmlFreeDoc(doc);
1024     return(ret);
1025 }
1026 
1027 /************************************************************************
1028  *									*
1029  *		The driver for the tests				*
1030  *									*
1031  ************************************************************************/
1032 
1033 int
main(int argc ATTRIBUTE_UNUSED,char ** argv ATTRIBUTE_UNUSED)1034 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1035     int ret = 0;
1036     int old_errors, old_tests, old_leaks;
1037 
1038     logfile = fopen(LOGFILE, "w");
1039     if (logfile == NULL) {
1040         fprintf(stderr,
1041 	        "Could not open the log file, running in verbose mode\n");
1042 	verbose = 1;
1043     }
1044     initializeLibxml2();
1045 
1046     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
1047         verbose = 1;
1048 
1049 
1050     old_errors = nb_errors;
1051     old_tests = nb_tests;
1052     old_leaks = nb_leaks;
1053     xsdTest();
1054     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1055 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1056     else
1057 	printf("Ran %d tests, %d errors, %d leaks\n",
1058 	       nb_tests - old_tests,
1059 	       nb_errors - old_errors,
1060 	       nb_leaks - old_leaks);
1061     old_errors = nb_errors;
1062     old_tests = nb_tests;
1063     old_leaks = nb_leaks;
1064     rngTest1();
1065     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1066 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1067     else
1068 	printf("Ran %d tests, %d errors, %d leaks\n",
1069 	       nb_tests - old_tests,
1070 	       nb_errors - old_errors,
1071 	       nb_leaks - old_leaks);
1072     old_errors = nb_errors;
1073     old_tests = nb_tests;
1074     old_leaks = nb_leaks;
1075     rngTest2();
1076     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1077 	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1078     else
1079 	printf("Ran %d tests, %d errors, %d leaks\n",
1080 	       nb_tests - old_tests,
1081 	       nb_errors - old_errors,
1082 	       nb_leaks - old_leaks);
1083     old_errors = nb_errors;
1084     old_tests = nb_tests;
1085     old_leaks = nb_leaks;
1086     nb_internals = 0;
1087     nb_schematas = 0;
1088     xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet",
1089 		 "xstc/Tests/Metadata/");
1090     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1091 	printf("Ran %d tests (%d schemata), no errors\n",
1092 	       nb_tests - old_tests, nb_schematas);
1093     else
1094 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
1095 	       nb_tests - old_tests,
1096 	       nb_schematas,
1097 	       nb_errors - old_errors,
1098 	       nb_internals,
1099 	       nb_leaks - old_leaks);
1100     old_errors = nb_errors;
1101     old_tests = nb_tests;
1102     old_leaks = nb_leaks;
1103     nb_internals = 0;
1104     nb_schematas = 0;
1105     xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet",
1106 		 "xstc/Tests/");
1107     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1108 	printf("Ran %d tests (%d schemata), no errors\n",
1109 	       nb_tests - old_tests, nb_schematas);
1110     else
1111 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
1112 	       nb_tests - old_tests,
1113 	       nb_schematas,
1114 	       nb_errors - old_errors,
1115 	       nb_internals,
1116 	       nb_leaks - old_leaks);
1117     old_errors = nb_errors;
1118     old_tests = nb_tests;
1119     old_leaks = nb_leaks;
1120     nb_internals = 0;
1121     nb_schematas = 0;
1122     xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet",
1123 		 "xstc/Tests/");
1124     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1125 	printf("Ran %d tests (%d schemata), no errors\n",
1126 	       nb_tests - old_tests, nb_schematas);
1127     else
1128 	printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n",
1129 	       nb_tests - old_tests,
1130 	       nb_schematas,
1131 	       nb_errors - old_errors,
1132 	       nb_internals,
1133 	       nb_leaks - old_leaks);
1134 
1135     if ((nb_errors == 0) && (nb_leaks == 0)) {
1136         ret = 0;
1137 	printf("Total %d tests, no errors\n",
1138 	       nb_tests);
1139     } else {
1140         ret = 1;
1141 	printf("Total %d tests, %d errors, %d leaks\n",
1142 	       nb_tests, nb_errors, nb_leaks);
1143     }
1144     xmlXPathFreeContext(ctxtXPath);
1145     xmlCleanupParser();
1146     xmlMemoryDump();
1147 
1148     if (logfile != NULL)
1149         fclose(logfile);
1150     return(ret);
1151 }
1152 #else /* !SCHEMAS */
1153 int
main(int argc ATTRIBUTE_UNUSED,char ** argv ATTRIBUTE_UNUSED)1154 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1155     fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n");
1156 }
1157 #endif
1158