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