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