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