fixed baselibs
[platform/upstream/libxml2.git] / runxmlconf.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 #ifdef LIBXML_XPATH_ENABLED
16
17 #if !defined(_WIN32) || defined(__CYGWIN__)
18 #include <unistd.h>
19 #endif
20 #include <string.h>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24
25 #include <libxml/parser.h>
26 #include <libxml/parserInternals.h>
27 #include <libxml/tree.h>
28 #include <libxml/uri.h>
29 #include <libxml/xmlreader.h>
30
31 #include <libxml/xpath.h>
32 #include <libxml/xpathInternals.h>
33
34 #define LOGFILE "runxmlconf.log"
35 static FILE *logfile = NULL;
36 static int verbose = 0;
37
38 #define NB_EXPECTED_ERRORS 15
39
40 #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MINGW32__)
41 #define vsnprintf _vsnprintf
42 #define snprintf _snprintf
43 #endif
44
45 const char *skipped_tests[] = {
46 /* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */
47     "rmt-ns10-035",
48     NULL
49 };
50
51 /************************************************************************
52  *                                                                      *
53  *              File name and path utilities                            *
54  *                                                                      *
55  ************************************************************************/
56
57 static int checkTestFile(const char *filename) {
58     struct stat buf;
59
60     if (stat(filename, &buf) == -1)
61         return(0);
62
63 #if defined(_WIN32) && !defined(__CYGWIN__)
64     if (!(buf.st_mode & _S_IFREG))
65         return(0);
66 #else
67     if (!S_ISREG(buf.st_mode))
68         return(0);
69 #endif
70
71     return(1);
72 }
73
74 static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
75     char buf[500];
76
77     if (dir == NULL) return(xmlStrdup(path));
78     if (path == NULL) return(NULL);
79
80     snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
81     return(xmlStrdup((const xmlChar *) buf));
82 }
83
84 /************************************************************************
85  *                                                                      *
86  *              Libxml2 specific routines                               *
87  *                                                                      *
88  ************************************************************************/
89
90 static int nb_skipped = 0;
91 static int nb_tests = 0;
92 static int nb_errors = 0;
93 static int nb_leaks = 0;
94
95 /*
96  * We need to trap calls to the resolver to not account memory for the catalog
97  * and not rely on any external resources.
98  */
99 static xmlParserInputPtr
100 testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
101                          xmlParserCtxtPtr ctxt) {
102     xmlParserInputPtr ret;
103
104     ret = xmlNewInputFromFile(ctxt, (const char *) URL);
105
106     return(ret);
107 }
108
109 /*
110  * Trapping the error messages at the generic level to grab the equivalent of
111  * stderr messages on CLI tools.
112  */
113 static char testErrors[32769];
114 static int testErrorsSize = 0;
115 static int nbError = 0;
116 static int nbFatal = 0;
117
118 static void test_log(const char *msg, ...) {
119     va_list args;
120     if (logfile != NULL) {
121         fprintf(logfile, "\n------------\n");
122         va_start(args, msg);
123         vfprintf(logfile, msg, args);
124         va_end(args);
125         fprintf(logfile, "%s", testErrors);
126         testErrorsSize = 0; testErrors[0] = 0;
127     }
128     if (verbose) {
129         va_start(args, msg);
130         vfprintf(stderr, msg, args);
131         va_end(args);
132     }
133 }
134
135 static void
136 testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) {
137     int res;
138
139     if (testErrorsSize >= 32768)
140         return;
141     res = snprintf(&testErrors[testErrorsSize],
142                     32768 - testErrorsSize,
143                    "%s:%d: %s\n", (error->file ? error->file : "entity"),
144                    error->line, error->message);
145     if (error->level == XML_ERR_FATAL)
146         nbFatal++;
147     else if (error->level == XML_ERR_ERROR)
148         nbError++;
149     if (testErrorsSize + res >= 32768) {
150         /* buffer is full */
151         testErrorsSize = 32768;
152         testErrors[testErrorsSize] = 0;
153     } else {
154         testErrorsSize += res;
155     }
156     testErrors[testErrorsSize] = 0;
157 }
158
159 static xmlXPathContextPtr ctxtXPath;
160
161 static void
162 initializeLibxml2(void) {
163     xmlGetWarningsDefaultValue = 0;
164     xmlPedanticParserDefault(0);
165
166     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
167     xmlInitParser();
168     xmlSetExternalEntityLoader(testExternalEntityLoader);
169     ctxtXPath = xmlXPathNewContext(NULL);
170     /*
171     * Deactivate the cache if created; otherwise we have to create/free it
172     * for every test, since it will confuse the memory leak detection.
173     * Note that normally this need not be done, since the cache is not
174     * created until set explicitely with xmlXPathContextSetCache();
175     * but for test purposes it is sometimes usefull to activate the
176     * cache by default for the whole library.
177     */
178     if (ctxtXPath->cache != NULL)
179         xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
180     xmlSetStructuredErrorFunc(NULL, testErrorHandler);
181 }
182
183 /************************************************************************
184  *                                                                      *
185  *              Run the xmlconf test if found                           *
186  *                                                                      *
187  ************************************************************************/
188
189 static int
190 xmlconfTestInvalid(const char *id, const char *filename, int options) {
191     xmlDocPtr doc;
192     xmlParserCtxtPtr ctxt;
193     int ret = 1;
194
195     ctxt = xmlNewParserCtxt();
196     if (ctxt == NULL) {
197         test_log("test %s : %s out of memory\n",
198                  id, filename);
199         return(0);
200     }
201     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
202     if (doc == NULL) {
203         test_log("test %s : %s invalid document turned not well-formed too\n",
204                  id, filename);
205     } else {
206     /* invalidity should be reported both in the context and in the document */
207         if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) {
208             test_log("test %s : %s failed to detect invalid document\n",
209                      id, filename);
210             nb_errors++;
211             ret = 0;
212         }
213         xmlFreeDoc(doc);
214     }
215     xmlFreeParserCtxt(ctxt);
216     return(ret);
217 }
218
219 static int
220 xmlconfTestValid(const char *id, const char *filename, int options) {
221     xmlDocPtr doc;
222     xmlParserCtxtPtr ctxt;
223     int ret = 1;
224
225     ctxt = xmlNewParserCtxt();
226     if (ctxt == NULL) {
227         test_log("test %s : %s out of memory\n",
228                  id, filename);
229         return(0);
230     }
231     doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
232     if (doc == NULL) {
233         test_log("test %s : %s failed to parse a valid document\n",
234                  id, filename);
235         nb_errors++;
236         ret = 0;
237     } else {
238     /* validity should be reported both in the context and in the document */
239         if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
240             test_log("test %s : %s failed to validate a valid document\n",
241                      id, filename);
242             nb_errors++;
243             ret = 0;
244         }
245         xmlFreeDoc(doc);
246     }
247     xmlFreeParserCtxt(ctxt);
248     return(ret);
249 }
250
251 static int
252 xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
253     xmlDocPtr doc;
254     int ret = 1;
255
256     /*
257      * In case of Namespace errors, libxml2 will still parse the document
258      * but log a Namesapce error.
259      */
260     doc = xmlReadFile(filename, NULL, options);
261     if (doc == NULL) {
262         test_log("test %s : %s failed to parse the XML\n",
263                  id, filename);
264         nb_errors++;
265         ret = 0;
266     } else {
267         if ((xmlLastError.code == XML_ERR_OK) ||
268             (xmlLastError.domain != XML_FROM_NAMESPACE)) {
269             test_log("test %s : %s failed to detect namespace error\n",
270                      id, filename);
271             nb_errors++;
272             ret = 0;
273         }
274         xmlFreeDoc(doc);
275     }
276     return(ret);
277 }
278
279 static int
280 xmlconfTestNotWF(const char *id, const char *filename, int options) {
281     xmlDocPtr doc;
282     int ret = 1;
283
284     doc = xmlReadFile(filename, NULL, options);
285     if (doc != NULL) {
286         test_log("test %s : %s failed to detect not well formedness\n",
287                  id, filename);
288         nb_errors++;
289         xmlFreeDoc(doc);
290         ret = 0;
291     }
292     return(ret);
293 }
294
295 static int
296 xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
297     int ret = -1;
298     xmlChar *type = NULL;
299     xmlChar *filename = NULL;
300     xmlChar *uri = NULL;
301     xmlChar *base = NULL;
302     xmlChar *id = NULL;
303     xmlChar *rec = NULL;
304     xmlChar *version = NULL;
305     xmlChar *entities = NULL;
306     xmlChar *edition = NULL;
307     int options = 0;
308     int nstest = 0;
309     int mem, final;
310     int i;
311
312     testErrorsSize = 0; testErrors[0] = 0;
313     nbError = 0;
314     nbFatal = 0;
315     id = xmlGetProp(cur, BAD_CAST "ID");
316     if (id == NULL) {
317         test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
318         goto error;
319     }
320     for (i = 0;skipped_tests[i] != NULL;i++) {
321         if (!strcmp(skipped_tests[i], (char *) id)) {
322             test_log("Skipping test %s from skipped list\n", (char *) id);
323             ret = 0;
324             nb_skipped++;
325             goto error;
326         }
327     }
328     type = xmlGetProp(cur, BAD_CAST "TYPE");
329     if (type == NULL) {
330         test_log("test %s missing TYPE\n", (char *) id);
331         goto error;
332     }
333     uri = xmlGetProp(cur, BAD_CAST "URI");
334     if (uri == NULL) {
335         test_log("test %s missing URI\n", (char *) id);
336         goto error;
337     }
338     base = xmlNodeGetBase(doc, cur);
339     filename = composeDir(base, uri);
340     if (!checkTestFile((char *) filename)) {
341         test_log("test %s missing file %s \n", id,
342                  (filename ? (char *)filename : "NULL"));
343         goto error;
344     }
345
346     version = xmlGetProp(cur, BAD_CAST "VERSION");
347
348     entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
349     if (!xmlStrEqual(entities, BAD_CAST "none")) {
350         options |= XML_PARSE_DTDLOAD;
351         options |= XML_PARSE_NOENT;
352     }
353     rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
354     if ((rec == NULL) ||
355         (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
356         (xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
357         (xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
358         (xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
359         if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
360             test_log("Skipping test %s for %s\n", (char *) id,
361                      (char *) version);
362             ret = 0;
363             nb_skipped++;
364             goto error;
365         }
366         ret = 1;
367     } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
368                (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
369         ret = 1;
370         nstest = 1;
371     } else {
372         test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
373         ret = 0;
374         nb_skipped++;
375         goto error;
376     }
377     edition = xmlGetProp(cur, BAD_CAST "EDITION");
378     if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
379         /* test limited to all versions before 5th */
380         options |= XML_PARSE_OLD10;
381     }
382
383     /*
384      * Reset errors and check memory usage before the test
385      */
386     xmlResetLastError();
387     testErrorsSize = 0; testErrors[0] = 0;
388     mem = xmlMemUsed();
389
390     if (xmlStrEqual(type, BAD_CAST "not-wf")) {
391         if (nstest == 0)
392             xmlconfTestNotWF((char *) id, (char *) filename, options);
393         else 
394             xmlconfTestNotNSWF((char *) id, (char *) filename, options);
395     } else if (xmlStrEqual(type, BAD_CAST "valid")) {
396         options |= XML_PARSE_DTDVALID;
397         xmlconfTestValid((char *) id, (char *) filename, options);
398     } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
399         options |= XML_PARSE_DTDVALID;
400         xmlconfTestInvalid((char *) id, (char *) filename, options);
401     } else if (xmlStrEqual(type, BAD_CAST "error")) {
402         test_log("Skipping error test %s \n", (char *) id);
403         ret = 0;
404         nb_skipped++;
405         goto error;
406     } else {
407         test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
408         ret = -1;
409         goto error;
410     }
411
412     /*
413      * Reset errors and check memory usage after the test
414      */
415     xmlResetLastError();
416     final = xmlMemUsed();
417     if (final > mem) {
418         test_log("test %s : %s leaked %d bytes\n",
419                  id, filename, final - mem);
420         nb_leaks++;
421         xmlMemDisplayLast(logfile, final - mem);
422     }
423     nb_tests++;
424
425 error:
426     if (type != NULL)
427         xmlFree(type);
428     if (entities != NULL)
429         xmlFree(entities);
430     if (edition != NULL)
431         xmlFree(edition);
432     if (version != NULL)
433         xmlFree(version);
434     if (filename != NULL)
435         xmlFree(filename);
436     if (uri != NULL)
437         xmlFree(uri);
438     if (base != NULL)
439         xmlFree(base);
440     if (id != NULL)
441         xmlFree(id);
442     if (rec != NULL)
443         xmlFree(rec);
444     return(ret);
445 }
446
447 static int
448 xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
449     xmlChar *profile;
450     int ret = 0;
451     int tests = 0;
452     int output = 0;
453
454     if (level == 1) {
455         profile = xmlGetProp(cur, BAD_CAST "PROFILE");
456         if (profile != NULL) {
457             output = 1;
458             level++;
459             printf("Test cases: %s\n", (char *) profile);
460             xmlFree(profile);
461         }
462     }
463     cur = cur->children;
464     while (cur != NULL) {
465         /* look only at elements we ignore everything else */
466         if (cur->type == XML_ELEMENT_NODE) {
467             if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
468                 ret += xmlconfTestCases(doc, cur, level);
469             } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
470                 if (xmlconfTestItem(doc, cur) >= 0)
471                     ret++;
472                 tests++;
473             } else {
474                 fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
475             }
476         }
477         cur = cur->next;
478     }
479     if (output == 1) {
480         if (tests > 0)
481             printf("Test cases: %d tests\n", tests);
482     }
483     return(ret);
484 }
485
486 static int
487 xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
488     xmlChar *profile;
489     int ret = 0;
490
491     profile = xmlGetProp(cur, BAD_CAST "PROFILE");
492     if (profile != NULL) {
493         printf("Test suite: %s\n", (char *) profile);
494         xmlFree(profile);
495     } else
496         printf("Test suite\n");
497     cur = cur->children;
498     while (cur != NULL) {
499         /* look only at elements we ignore everything else */
500         if (cur->type == XML_ELEMENT_NODE) {
501             if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
502                 ret += xmlconfTestCases(doc, cur, 1);
503             } else {
504                 fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
505             }
506         }
507         cur = cur->next;
508     }
509     return(ret);
510 }
511
512 static void
513 xmlconfInfo(void) {
514     fprintf(stderr, "  you need to fetch and extract the\n");
515     fprintf(stderr, "  latest XML Conformance Test Suites\n");
516     fprintf(stderr, "  http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
517     fprintf(stderr, "  see http://www.w3.org/XML/Test/ for informations\n");
518 }
519
520 static int
521 xmlconfTest(void) {
522     const char *confxml = "xmlconf/xmlconf.xml";
523     xmlDocPtr doc;
524     xmlNodePtr cur;
525     int ret = 0;
526
527     if (!checkTestFile(confxml)) {
528         fprintf(stderr, "%s is missing \n", confxml);
529         xmlconfInfo();
530         return(-1);
531     }
532     doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
533     if (doc == NULL) {
534         fprintf(stderr, "%s is corrupted \n", confxml);
535         xmlconfInfo();
536         return(-1);
537     }
538
539     cur = xmlDocGetRootElement(doc);
540     if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
541         fprintf(stderr, "Unexpected format %s\n", confxml);
542         xmlconfInfo();
543         ret = -1;
544     } else {
545         ret = xmlconfTestSuite(doc, cur);
546     }
547     xmlFreeDoc(doc);
548     return(ret);
549 }
550
551 /************************************************************************
552  *                                                                      *
553  *              The driver for the tests                                *
554  *                                                                      *
555  ************************************************************************/
556
557 int
558 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
559     int ret = 0;
560     int old_errors, old_tests, old_leaks;
561
562     logfile = fopen(LOGFILE, "w");
563     if (logfile == NULL) {
564         fprintf(stderr,
565                 "Could not open the log file, running in verbose mode\n");
566         verbose = 1;
567     }
568     initializeLibxml2();
569
570     if ((argc >= 2) && (!strcmp(argv[1], "-v")))
571         verbose = 1;
572
573
574     old_errors = nb_errors;
575     old_tests = nb_tests;
576     old_leaks = nb_leaks;
577     xmlconfTest();
578     if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
579         printf("Ran %d tests, no errors\n", nb_tests - old_tests);
580     else
581         printf("Ran %d tests, %d errors, %d leaks\n",
582                nb_tests - old_tests,
583                nb_errors - old_errors,
584                nb_leaks - old_leaks);
585     if ((nb_errors == 0) && (nb_leaks == 0)) {
586         ret = 0;
587         printf("Total %d tests, no errors\n",
588                nb_tests);
589     } else {
590         ret = 1;
591         printf("Total %d tests, %d errors, %d leaks\n",
592                nb_tests, nb_errors, nb_leaks);
593         printf("See %s for detailed output\n", LOGFILE);
594         if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
595             printf("%d errors were expected\n", nb_errors);
596             ret = 0;
597         }
598     }
599     xmlXPathFreeContext(ctxtXPath);
600     xmlCleanupParser();
601     xmlMemoryDump();
602
603     if (logfile != NULL)
604         fclose(logfile);
605     return(ret);
606 }
607
608 #else /* ! LIBXML_XPATH_ENABLED */
609 #include <stdio.h>
610 int
611 main(int argc, char **argv) {
612     fprintf(stderr, "%s need XPath support\n", argv[0]);
613 }
614 #endif