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