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