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