Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenrunxmlconf.c
Go to the documentation of this file.
00001 /* 00002 * runsuite.c: C program to run libxml2 againts published testsuites 00003 * 00004 * See Copyright for the status of this software. 00005 * 00006 * daniel@veillard.com 00007 */ 00008 00009 #ifdef HAVE_CONFIG_H 00010 #include "libxml.h" 00011 #else 00012 #include <stdio.h> 00013 #endif 00014 00015 #ifdef LIBXML_XPATH_ENABLED 00016 00017 #if !defined(_WIN32) || defined(__CYGWIN__) 00018 #include <unistd.h> 00019 #endif 00020 #include <string.h> 00021 #include <sys/types.h> 00022 #include <sys/stat.h> 00023 #include <fcntl.h> 00024 00025 #include <libxml/parser.h> 00026 #include <libxml/parserInternals.h> 00027 #include <libxml/tree.h> 00028 #include <libxml/uri.h> 00029 #include <libxml/xmlreader.h> 00030 00031 #include <libxml/xpath.h> 00032 #include <libxml/xpathInternals.h> 00033 00034 #define LOGFILE "runxmlconf.log" 00035 static FILE *logfile = NULL; 00036 static int verbose = 0; 00037 00038 #define NB_EXPECTED_ERRORS 15 00039 00040 #if defined(_WIN32) && !defined(__CYGWIN__) 00041 00042 #define vsnprintf _vsnprintf 00043 00044 #define snprintf _snprintf 00045 00046 #endif 00047 00048 const char *skipped_tests[] = { 00049 /* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */ 00050 "rmt-ns10-035", 00051 NULL 00052 }; 00053 00054 /************************************************************************ 00055 * * 00056 * File name and path utilities * 00057 * * 00058 ************************************************************************/ 00059 00060 static int checkTestFile(const char *filename) { 00061 struct stat buf; 00062 00063 if (stat(filename, &buf) == -1) 00064 return(0); 00065 00066 #if defined(_WIN32) && !defined(__CYGWIN__) 00067 if (!(buf.st_mode & _S_IFREG)) 00068 return(0); 00069 #else 00070 if (!S_ISREG(buf.st_mode)) 00071 return(0); 00072 #endif 00073 00074 return(1); 00075 } 00076 00077 static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) { 00078 char buf[500]; 00079 00080 if (dir == NULL) return(xmlStrdup(path)); 00081 if (path == NULL) return(NULL); 00082 00083 snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path); 00084 return(xmlStrdup((const xmlChar *) buf)); 00085 } 00086 00087 /************************************************************************ 00088 * * 00089 * Libxml2 specific routines * 00090 * * 00091 ************************************************************************/ 00092 00093 static int nb_skipped = 0; 00094 static int nb_tests = 0; 00095 static int nb_errors = 0; 00096 static int nb_leaks = 0; 00097 00098 /* 00099 * We need to trap calls to the resolver to not account memory for the catalog 00100 * and not rely on any external resources. 00101 */ 00102 static xmlParserInputPtr 00103 testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED, 00104 xmlParserCtxtPtr ctxt) { 00105 xmlParserInputPtr ret; 00106 00107 ret = xmlNewInputFromFile(ctxt, (const char *) URL); 00108 00109 return(ret); 00110 } 00111 00112 /* 00113 * Trapping the error messages at the generic level to grab the equivalent of 00114 * stderr messages on CLI tools. 00115 */ 00116 static char testErrors[32769]; 00117 static int testErrorsSize = 0; 00118 static int nbError = 0; 00119 static int nbFatal = 0; 00120 00121 static void test_log(const char *msg, ...) { 00122 va_list args; 00123 if (logfile != NULL) { 00124 fprintf(logfile, "\n------------\n"); 00125 va_start(args, msg); 00126 vfprintf(logfile, msg, args); 00127 va_end(args); 00128 fprintf(logfile, "%s", testErrors); 00129 testErrorsSize = 0; testErrors[0] = 0; 00130 } 00131 if (verbose) { 00132 va_start(args, msg); 00133 vfprintf(stderr, msg, args); 00134 va_end(args); 00135 } 00136 } 00137 00138 static void 00139 testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) { 00140 int res; 00141 00142 if (testErrorsSize >= 32768) 00143 return; 00144 res = snprintf(&testErrors[testErrorsSize], 00145 32768 - testErrorsSize, 00146 "%s:%d: %s\n", (error->file ? error->file : "entity"), 00147 error->line, error->message); 00148 if (error->level == XML_ERR_FATAL) 00149 nbFatal++; 00150 else if (error->level == XML_ERR_ERROR) 00151 nbError++; 00152 if (testErrorsSize + res >= 32768) { 00153 /* buffer is full */ 00154 testErrorsSize = 32768; 00155 testErrors[testErrorsSize] = 0; 00156 } else { 00157 testErrorsSize += res; 00158 } 00159 testErrors[testErrorsSize] = 0; 00160 } 00161 00162 static xmlXPathContextPtr ctxtXPath; 00163 00164 static void 00165 initializeLibxml2(void) { 00166 xmlGetWarningsDefaultValue = 0; 00167 xmlPedanticParserDefault(0); 00168 00169 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); 00170 xmlInitParser(); 00171 xmlSetExternalEntityLoader(testExternalEntityLoader); 00172 ctxtXPath = xmlXPathNewContext(NULL); 00173 /* 00174 * Deactivate the cache if created; otherwise we have to create/free it 00175 * for every test, since it will confuse the memory leak detection. 00176 * Note that normally this need not be done, since the cache is not 00177 * created until set explicitely with xmlXPathContextSetCache(); 00178 * but for test purposes it is sometimes usefull to activate the 00179 * cache by default for the whole library. 00180 */ 00181 if (ctxtXPath->cache != NULL) 00182 xmlXPathContextSetCache(ctxtXPath, 0, -1, 0); 00183 xmlSetStructuredErrorFunc(NULL, testErrorHandler); 00184 } 00185 00186 /************************************************************************ 00187 * * 00188 * Run the xmlconf test if found * 00189 * * 00190 ************************************************************************/ 00191 00192 static int 00193 xmlconfTestInvalid(const char *id, const char *filename, int options) { 00194 xmlDocPtr doc; 00195 xmlParserCtxtPtr ctxt; 00196 int ret = 1; 00197 00198 ctxt = xmlNewParserCtxt(); 00199 if (ctxt == NULL) { 00200 test_log("test %s : %s out of memory\n", 00201 id, filename); 00202 return(0); 00203 } 00204 doc = xmlCtxtReadFile(ctxt, filename, NULL, options); 00205 if (doc == NULL) { 00206 test_log("test %s : %s invalid document turned not well-formed too\n", 00207 id, filename); 00208 } else { 00209 /* invalidity should be reported both in the context and in the document */ 00210 if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) { 00211 test_log("test %s : %s failed to detect invalid document\n", 00212 id, filename); 00213 nb_errors++; 00214 ret = 0; 00215 } 00216 xmlFreeDoc(doc); 00217 } 00218 xmlFreeParserCtxt(ctxt); 00219 return(ret); 00220 } 00221 00222 static int 00223 xmlconfTestValid(const char *id, const char *filename, int options) { 00224 xmlDocPtr doc; 00225 xmlParserCtxtPtr ctxt; 00226 int ret = 1; 00227 00228 ctxt = xmlNewParserCtxt(); 00229 if (ctxt == NULL) { 00230 test_log("test %s : %s out of memory\n", 00231 id, filename); 00232 return(0); 00233 } 00234 doc = xmlCtxtReadFile(ctxt, filename, NULL, options); 00235 if (doc == NULL) { 00236 test_log("test %s : %s failed to parse a valid document\n", 00237 id, filename); 00238 nb_errors++; 00239 ret = 0; 00240 } else { 00241 /* validity should be reported both in the context and in the document */ 00242 if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) { 00243 test_log("test %s : %s failed to validate a valid document\n", 00244 id, filename); 00245 nb_errors++; 00246 ret = 0; 00247 } 00248 xmlFreeDoc(doc); 00249 } 00250 xmlFreeParserCtxt(ctxt); 00251 return(ret); 00252 } 00253 00254 static int 00255 xmlconfTestNotNSWF(const char *id, const char *filename, int options) { 00256 xmlDocPtr doc; 00257 int ret = 1; 00258 00259 /* 00260 * In case of Namespace errors, libxml2 will still parse the document 00261 * but log a Namesapce error. 00262 */ 00263 doc = xmlReadFile(filename, NULL, options); 00264 if (doc == NULL) { 00265 test_log("test %s : %s failed to parse the XML\n", 00266 id, filename); 00267 nb_errors++; 00268 ret = 0; 00269 } else { 00270 if ((xmlLastError.code == XML_ERR_OK) || 00271 (xmlLastError.domain != XML_FROM_NAMESPACE)) { 00272 test_log("test %s : %s failed to detect namespace error\n", 00273 id, filename); 00274 nb_errors++; 00275 ret = 0; 00276 } 00277 xmlFreeDoc(doc); 00278 } 00279 return(ret); 00280 } 00281 00282 static int 00283 xmlconfTestNotWF(const char *id, const char *filename, int options) { 00284 xmlDocPtr doc; 00285 int ret = 1; 00286 00287 doc = xmlReadFile(filename, NULL, options); 00288 if (doc != NULL) { 00289 test_log("test %s : %s failed to detect not well formedness\n", 00290 id, filename); 00291 nb_errors++; 00292 xmlFreeDoc(doc); 00293 ret = 0; 00294 } 00295 return(ret); 00296 } 00297 00298 static int 00299 xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) { 00300 int ret = -1; 00301 xmlChar *type = NULL; 00302 xmlChar *filename = NULL; 00303 xmlChar *uri = NULL; 00304 xmlChar *base = NULL; 00305 xmlChar *id = NULL; 00306 xmlChar *rec = NULL; 00307 xmlChar *version = NULL; 00308 xmlChar *entities = NULL; 00309 xmlChar *edition = NULL; 00310 int options = 0; 00311 int nstest = 0; 00312 int mem, final; 00313 int i; 00314 00315 testErrorsSize = 0; testErrors[0] = 0; 00316 nbError = 0; 00317 nbFatal = 0; 00318 id = xmlGetProp(cur, BAD_CAST "ID"); 00319 if (id == NULL) { 00320 test_log("test missing ID, line %ld\n", xmlGetLineNo(cur)); 00321 goto error; 00322 } 00323 for (i = 0;skipped_tests[i] != NULL;i++) { 00324 if (!strcmp(skipped_tests[i], (char *) id)) { 00325 test_log("Skipping test %s from skipped list\n", (char *) id); 00326 ret = 0; 00327 nb_skipped++; 00328 goto error; 00329 } 00330 } 00331 type = xmlGetProp(cur, BAD_CAST "TYPE"); 00332 if (type == NULL) { 00333 test_log("test %s missing TYPE\n", (char *) id); 00334 goto error; 00335 } 00336 uri = xmlGetProp(cur, BAD_CAST "URI"); 00337 if (uri == NULL) { 00338 test_log("test %s missing URI\n", (char *) id); 00339 goto error; 00340 } 00341 base = xmlNodeGetBase(doc, cur); 00342 filename = composeDir(base, uri); 00343 if (!checkTestFile((char *) filename)) { 00344 test_log("test %s missing file %s \n", id, 00345 (filename ? (char *)filename : "NULL")); 00346 goto error; 00347 } 00348 00349 version = xmlGetProp(cur, BAD_CAST "VERSION"); 00350 00351 entities = xmlGetProp(cur, BAD_CAST "ENTITIES"); 00352 if (!xmlStrEqual(entities, BAD_CAST "none")) { 00353 options |= XML_PARSE_DTDLOAD; 00354 options |= XML_PARSE_NOENT; 00355 } 00356 rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION"); 00357 if ((rec == NULL) || 00358 (xmlStrEqual(rec, BAD_CAST "XML1.0")) || 00359 (xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) || 00360 (xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) || 00361 (xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) { 00362 if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) { 00363 test_log("Skipping test %s for %s\n", (char *) id, 00364 (char *) version); 00365 ret = 0; 00366 nb_skipped++; 00367 goto error; 00368 } 00369 ret = 1; 00370 } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) || 00371 (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) { 00372 ret = 1; 00373 nstest = 1; 00374 } else { 00375 test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec); 00376 ret = 0; 00377 nb_skipped++; 00378 goto error; 00379 } 00380 edition = xmlGetProp(cur, BAD_CAST "EDITION"); 00381 if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) { 00382 /* test limited to all versions before 5th */ 00383 options |= XML_PARSE_OLD10; 00384 } 00385 00386 /* 00387 * Reset errors and check memory usage before the test 00388 */ 00389 xmlResetLastError(); 00390 testErrorsSize = 0; testErrors[0] = 0; 00391 mem = xmlMemUsed(); 00392 00393 if (xmlStrEqual(type, BAD_CAST "not-wf")) { 00394 if (nstest == 0) 00395 xmlconfTestNotWF((char *) id, (char *) filename, options); 00396 else 00397 xmlconfTestNotNSWF((char *) id, (char *) filename, options); 00398 } else if (xmlStrEqual(type, BAD_CAST "valid")) { 00399 options |= XML_PARSE_DTDVALID; 00400 xmlconfTestValid((char *) id, (char *) filename, options); 00401 } else if (xmlStrEqual(type, BAD_CAST "invalid")) { 00402 options |= XML_PARSE_DTDVALID; 00403 xmlconfTestInvalid((char *) id, (char *) filename, options); 00404 } else if (xmlStrEqual(type, BAD_CAST "error")) { 00405 test_log("Skipping error test %s \n", (char *) id); 00406 ret = 0; 00407 nb_skipped++; 00408 goto error; 00409 } else { 00410 test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type); 00411 ret = -1; 00412 goto error; 00413 } 00414 00415 /* 00416 * Reset errors and check memory usage after the test 00417 */ 00418 xmlResetLastError(); 00419 final = xmlMemUsed(); 00420 if (final > mem) { 00421 test_log("test %s : %s leaked %d bytes\n", 00422 id, filename, final - mem); 00423 nb_leaks++; 00424 xmlMemDisplayLast(logfile, final - mem); 00425 } 00426 nb_tests++; 00427 00428 error: 00429 if (type != NULL) 00430 xmlFree(type); 00431 if (entities != NULL) 00432 xmlFree(entities); 00433 if (edition != NULL) 00434 xmlFree(edition); 00435 if (version != NULL) 00436 xmlFree(version); 00437 if (filename != NULL) 00438 xmlFree(filename); 00439 if (uri != NULL) 00440 xmlFree(uri); 00441 if (base != NULL) 00442 xmlFree(base); 00443 if (id != NULL) 00444 xmlFree(id); 00445 if (rec != NULL) 00446 xmlFree(rec); 00447 return(ret); 00448 } 00449 00450 static int 00451 xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) { 00452 xmlChar *profile; 00453 int ret = 0; 00454 int tests = 0; 00455 int output = 0; 00456 00457 if (level == 1) { 00458 profile = xmlGetProp(cur, BAD_CAST "PROFILE"); 00459 if (profile != NULL) { 00460 output = 1; 00461 level++; 00462 printf("Test cases: %s\n", (char *) profile); 00463 xmlFree(profile); 00464 } 00465 } 00466 cur = cur->children; 00467 while (cur != NULL) { 00468 /* look only at elements we ignore everything else */ 00469 if (cur->type == XML_ELEMENT_NODE) { 00470 if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) { 00471 ret += xmlconfTestCases(doc, cur, level); 00472 } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) { 00473 if (xmlconfTestItem(doc, cur) >= 0) 00474 ret++; 00475 tests++; 00476 } else { 00477 fprintf(stderr, "Unhandled element %s\n", (char *)cur->name); 00478 } 00479 } 00480 cur = cur->next; 00481 } 00482 if (output == 1) { 00483 if (tests > 0) 00484 printf("Test cases: %d tests\n", tests); 00485 } 00486 return(ret); 00487 } 00488 00489 static int 00490 xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) { 00491 xmlChar *profile; 00492 int ret = 0; 00493 00494 profile = xmlGetProp(cur, BAD_CAST "PROFILE"); 00495 if (profile != NULL) { 00496 printf("Test suite: %s\n", (char *) profile); 00497 xmlFree(profile); 00498 } else 00499 printf("Test suite\n"); 00500 cur = cur->children; 00501 while (cur != NULL) { 00502 /* look only at elements we ignore everything else */ 00503 if (cur->type == XML_ELEMENT_NODE) { 00504 if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) { 00505 ret += xmlconfTestCases(doc, cur, 1); 00506 } else { 00507 fprintf(stderr, "Unhandled element %s\n", (char *)cur->name); 00508 } 00509 } 00510 cur = cur->next; 00511 } 00512 return(ret); 00513 } 00514 00515 static void 00516 xmlconfInfo(void) { 00517 fprintf(stderr, " you need to fetch and extract the\n"); 00518 fprintf(stderr, " latest XML Conformance Test Suites\n"); 00519 fprintf(stderr, " http://www.w3.org/XML/Test/xmlts20080205.tar.gz\n"); 00520 fprintf(stderr, " see http://www.w3.org/XML/Test/ for informations\n"); 00521 } 00522 00523 static int 00524 xmlconfTest(void) { 00525 const char *confxml = "xmlconf/xmlconf.xml"; 00526 xmlDocPtr doc; 00527 xmlNodePtr cur; 00528 int ret = 0; 00529 00530 if (!checkTestFile(confxml)) { 00531 fprintf(stderr, "%s is missing \n", confxml); 00532 xmlconfInfo(); 00533 return(-1); 00534 } 00535 doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT); 00536 if (doc == NULL) { 00537 fprintf(stderr, "%s is corrupted \n", confxml); 00538 xmlconfInfo(); 00539 return(-1); 00540 } 00541 00542 cur = xmlDocGetRootElement(doc); 00543 if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) { 00544 fprintf(stderr, "Unexpected format %s\n", confxml); 00545 xmlconfInfo(); 00546 ret = -1; 00547 } else { 00548 ret = xmlconfTestSuite(doc, cur); 00549 } 00550 xmlFreeDoc(doc); 00551 return(ret); 00552 } 00553 00554 /************************************************************************ 00555 * * 00556 * The driver for the tests * 00557 * * 00558 ************************************************************************/ 00559 00560 int 00561 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { 00562 int ret = 0; 00563 int old_errors, old_tests, old_leaks; 00564 00565 logfile = fopen(LOGFILE, "w"); 00566 if (logfile == NULL) { 00567 fprintf(stderr, 00568 "Could not open the log file, running in verbose mode\n"); 00569 verbose = 1; 00570 } 00571 initializeLibxml2(); 00572 00573 if ((argc >= 2) && (!strcmp(argv[1], "-v"))) 00574 verbose = 1; 00575 00576 00577 old_errors = nb_errors; 00578 old_tests = nb_tests; 00579 old_leaks = nb_leaks; 00580 xmlconfTest(); 00581 if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) 00582 printf("Ran %d tests, no errors\n", nb_tests - old_tests); 00583 else 00584 printf("Ran %d tests, %d errors, %d leaks\n", 00585 nb_tests - old_tests, 00586 nb_errors - old_errors, 00587 nb_leaks - old_leaks); 00588 if ((nb_errors == 0) && (nb_leaks == 0)) { 00589 ret = 0; 00590 printf("Total %d tests, no errors\n", 00591 nb_tests); 00592 } else { 00593 ret = 1; 00594 printf("Total %d tests, %d errors, %d leaks\n", 00595 nb_tests, nb_errors, nb_leaks); 00596 printf("See %s for detailed output\n", LOGFILE); 00597 if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) { 00598 printf("%d errors were expected\n", nb_errors); 00599 ret = 0; 00600 } 00601 } 00602 xmlXPathFreeContext(ctxtXPath); 00603 xmlCleanupParser(); 00604 xmlMemoryDump(); 00605 00606 if (logfile != NULL) 00607 fclose(logfile); 00608 return(ret); 00609 } 00610 00611 #else /* ! LIBXML_XPATH_ENABLED */ 00612 #include <stdio.h> 00613 int 00614 main(int argc, char **argv) { 00615 fprintf(stderr, "%s need XPath support\n", argv[0]); 00616 } 00617 #endif Generated on Fri May 25 2012 04:32:58 for ReactOS by
1.7.6.1
|