Annotation of embedaddon/libxml2/runxmlconf.c, revision 1.1
1.1 ! misho 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
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>