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