(root)/
libxml2-2.12.3/
runxmlconf.c
       1  /*
       2   * runxmlconf.c: C program to run XML W3C conformance testsuites
       3   *
       4   * See Copyright for the status of this software.
       5   *
       6   * daniel@veillard.com
       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  
      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  
      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
      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  
     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
     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
     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      xmlSetStructuredErrorFunc(NULL, testErrorHandler);
     167  }
     168  
     169  /************************************************************************
     170   *									*
     171   *		Run the xmlconf test if found				*
     172   *									*
     173   ************************************************************************/
     174  
     175  static int
     176  xmlconfTestInvalid(const char *id, const char *filename, int options) {
     177      xmlDocPtr doc;
     178      xmlParserCtxtPtr ctxt;
     179      int ret = 1;
     180  
     181      ctxt = xmlNewParserCtxt();
     182      if (ctxt == NULL) {
     183          test_log("test %s : %s out of memory\n",
     184  	         id, filename);
     185          return(0);
     186      }
     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
     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      doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
     218      if (doc == NULL) {
     219          test_log("test %s : %s failed to parse a valid document\n",
     220  	         id, filename);
     221          nb_errors++;
     222  	ret = 0;
     223      } else {
     224      /* validity should be reported both in the context and in the document */
     225          if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
     226  	    test_log("test %s : %s failed to validate a valid document\n",
     227  		     id, filename);
     228  	    nb_errors++;
     229  	    ret = 0;
     230  	}
     231  	xmlFreeDoc(doc);
     232      }
     233      xmlFreeParserCtxt(ctxt);
     234      return(ret);
     235  }
     236  
     237  static int
     238  xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
     239      xmlDocPtr doc;
     240      int ret = 1;
     241  
     242      /*
     243       * In case of Namespace errors, libxml2 will still parse the document
     244       * but log a Namespace error.
     245       */
     246      doc = xmlReadFile(filename, NULL, options);
     247      if (doc == NULL) {
     248          test_log("test %s : %s failed to parse the XML\n",
     249  	         id, filename);
     250          nb_errors++;
     251  	ret = 0;
     252      } else {
     253          const xmlError *error = xmlGetLastError();
     254  
     255  	if ((error->code == XML_ERR_OK) ||
     256  	    (error->domain != XML_FROM_NAMESPACE)) {
     257  	    test_log("test %s : %s failed to detect namespace error\n",
     258  		     id, filename);
     259  	    nb_errors++;
     260  	    ret = 0;
     261  	}
     262  	xmlFreeDoc(doc);
     263      }
     264      return(ret);
     265  }
     266  
     267  static int
     268  xmlconfTestNotWF(const char *id, const char *filename, int options) {
     269      xmlDocPtr doc;
     270      int ret = 1;
     271  
     272      doc = xmlReadFile(filename, NULL, options);
     273      if (doc != NULL) {
     274          test_log("test %s : %s failed to detect not well formedness\n",
     275  	         id, filename);
     276          nb_errors++;
     277  	xmlFreeDoc(doc);
     278  	ret = 0;
     279      }
     280      return(ret);
     281  }
     282  
     283  static int
     284  xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
     285      int ret = -1;
     286      xmlChar *type = NULL;
     287      xmlChar *filename = NULL;
     288      xmlChar *uri = NULL;
     289      xmlChar *base = NULL;
     290      xmlChar *id = NULL;
     291      xmlChar *rec = NULL;
     292      xmlChar *version = NULL;
     293      xmlChar *entities = NULL;
     294      xmlChar *edition = NULL;
     295      int options = 0;
     296      int nstest = 0;
     297      int mem, final;
     298      int i;
     299  
     300      testErrorsSize = 0; testErrors[0] = 0;
     301      nbError = 0;
     302      nbFatal = 0;
     303      id = xmlGetProp(cur, BAD_CAST "ID");
     304      if (id == NULL) {
     305          test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
     306  	goto error;
     307      }
     308      for (i = 0;skipped_tests[i] != NULL;i++) {
     309          if (!strcmp(skipped_tests[i], (char *) id)) {
     310  	    test_log("Skipping test %s from skipped list\n", (char *) id);
     311  	    ret = 0;
     312  	    nb_skipped++;
     313  	    goto error;
     314  	}
     315      }
     316      type = xmlGetProp(cur, BAD_CAST "TYPE");
     317      if (type == NULL) {
     318          test_log("test %s missing TYPE\n", (char *) id);
     319  	goto error;
     320      }
     321      uri = xmlGetProp(cur, BAD_CAST "URI");
     322      if (uri == NULL) {
     323          test_log("test %s missing URI\n", (char *) id);
     324  	goto error;
     325      }
     326      base = xmlNodeGetBase(doc, cur);
     327      filename = composeDir(base, uri);
     328      if (!checkTestFile((char *) filename)) {
     329          test_log("test %s missing file %s \n", id,
     330  	         (filename ? (char *)filename : "NULL"));
     331  	goto error;
     332      }
     333  
     334      version = xmlGetProp(cur, BAD_CAST "VERSION");
     335  
     336      entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
     337      if (!xmlStrEqual(entities, BAD_CAST "none")) {
     338          options |= XML_PARSE_DTDLOAD;
     339          options |= XML_PARSE_NOENT;
     340      }
     341      rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
     342      if ((rec == NULL) ||
     343          (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
     344  	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
     345  	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
     346  	(xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
     347  	if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
     348  	    test_log("Skipping test %s for %s\n", (char *) id,
     349  	             (char *) version);
     350  	    ret = 0;
     351  	    nb_skipped++;
     352  	    goto error;
     353  	}
     354  	ret = 1;
     355      } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
     356  	       (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
     357  	ret = 1;
     358  	nstest = 1;
     359      } else {
     360          test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
     361  	ret = 0;
     362  	nb_skipped++;
     363  	goto error;
     364      }
     365      edition = xmlGetProp(cur, BAD_CAST "EDITION");
     366      if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
     367          /* test limited to all versions before 5th */
     368  	options |= XML_PARSE_OLD10;
     369      }
     370  
     371      /*
     372       * Reset errors and check memory usage before the test
     373       */
     374      xmlResetLastError();
     375      testErrorsSize = 0; testErrors[0] = 0;
     376      mem = xmlMemUsed();
     377  
     378      if (xmlStrEqual(type, BAD_CAST "not-wf")) {
     379          if (nstest == 0)
     380  	    xmlconfTestNotWF((char *) id, (char *) filename, options);
     381          else
     382  	    xmlconfTestNotNSWF((char *) id, (char *) filename, options);
     383      } else if (xmlStrEqual(type, BAD_CAST "valid")) {
     384          options |= XML_PARSE_DTDVALID;
     385  	xmlconfTestValid((char *) id, (char *) filename, options);
     386      } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
     387          options |= XML_PARSE_DTDVALID;
     388  	xmlconfTestInvalid((char *) id, (char *) filename, options);
     389      } else if (xmlStrEqual(type, BAD_CAST "error")) {
     390          test_log("Skipping error test %s \n", (char *) id);
     391  	ret = 0;
     392  	nb_skipped++;
     393  	goto error;
     394      } else {
     395          test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
     396  	ret = -1;
     397  	goto error;
     398      }
     399  
     400      /*
     401       * Reset errors and check memory usage after the test
     402       */
     403      xmlResetLastError();
     404      final = xmlMemUsed();
     405      if (final > mem) {
     406          test_log("test %s : %s leaked %d bytes\n",
     407  	         id, filename, final - mem);
     408          nb_leaks++;
     409  	xmlMemDisplayLast(logfile, final - mem);
     410      }
     411      nb_tests++;
     412  
     413  error:
     414      if (type != NULL)
     415          xmlFree(type);
     416      if (entities != NULL)
     417          xmlFree(entities);
     418      if (edition != NULL)
     419          xmlFree(edition);
     420      if (version != NULL)
     421          xmlFree(version);
     422      if (filename != NULL)
     423          xmlFree(filename);
     424      if (uri != NULL)
     425          xmlFree(uri);
     426      if (base != NULL)
     427          xmlFree(base);
     428      if (id != NULL)
     429          xmlFree(id);
     430      if (rec != NULL)
     431          xmlFree(rec);
     432      return(ret);
     433  }
     434  
     435  static int
     436  xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
     437      xmlChar *profile;
     438      int ret = 0;
     439      int tests = 0;
     440      int output = 0;
     441  
     442      if (level == 1) {
     443  	profile = xmlGetProp(cur, BAD_CAST "PROFILE");
     444  	if (profile != NULL) {
     445  	    output = 1;
     446  	    level++;
     447  	    printf("Test cases: %s\n", (char *) profile);
     448  	    xmlFree(profile);
     449  	}
     450      }
     451      cur = cur->children;
     452      while (cur != NULL) {
     453          /* look only at elements we ignore everything else */
     454          if (cur->type == XML_ELEMENT_NODE) {
     455  	    if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
     456  	        ret += xmlconfTestCases(doc, cur, level);
     457  	    } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
     458  	        if (xmlconfTestItem(doc, cur) >= 0)
     459  		    ret++;
     460  		tests++;
     461  	    } else {
     462  	        fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
     463  	    }
     464  	}
     465          cur = cur->next;
     466      }
     467      if (output == 1) {
     468  	if (tests > 0)
     469  	    printf("Test cases: %d tests\n", tests);
     470      }
     471      return(ret);
     472  }
     473  
     474  static int
     475  xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
     476      xmlChar *profile;
     477      int ret = 0;
     478  
     479      profile = xmlGetProp(cur, BAD_CAST "PROFILE");
     480      if (profile != NULL) {
     481          printf("Test suite: %s\n", (char *) profile);
     482  	xmlFree(profile);
     483      } else
     484          printf("Test suite\n");
     485      cur = cur->children;
     486      while (cur != NULL) {
     487          /* look only at elements we ignore everything else */
     488          if (cur->type == XML_ELEMENT_NODE) {
     489  	    if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
     490  	        ret += xmlconfTestCases(doc, cur, 1);
     491  	    } else {
     492  	        fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
     493  	    }
     494  	}
     495          cur = cur->next;
     496      }
     497      return(ret);
     498  }
     499  
     500  static void
     501  xmlconfInfo(void) {
     502      fprintf(stderr, "  you need to fetch and extract the\n");
     503      fprintf(stderr, "  latest XML Conformance Test Suites\n");
     504      fprintf(stderr, "  http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
     505      fprintf(stderr, "  see http://www.w3.org/XML/Test/ for information\n");
     506  }
     507  
     508  static int
     509  xmlconfTest(void) {
     510      const char *confxml = "xmlconf/xmlconf.xml";
     511      xmlDocPtr doc;
     512      xmlNodePtr cur;
     513      int ret = 0;
     514  
     515      if (!checkTestFile(confxml)) {
     516          fprintf(stderr, "%s is missing \n", confxml);
     517  	xmlconfInfo();
     518  	return(-1);
     519      }
     520      doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
     521      if (doc == NULL) {
     522          fprintf(stderr, "%s is corrupted \n", confxml);
     523  	xmlconfInfo();
     524  	return(-1);
     525      }
     526  
     527      cur = xmlDocGetRootElement(doc);
     528      if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
     529          fprintf(stderr, "Unexpected format %s\n", confxml);
     530  	xmlconfInfo();
     531  	ret = -1;
     532      } else {
     533          ret = xmlconfTestSuite(doc, cur);
     534      }
     535      xmlFreeDoc(doc);
     536      return(ret);
     537  }
     538  
     539  /************************************************************************
     540   *									*
     541   *		The driver for the tests				*
     542   *									*
     543   ************************************************************************/
     544  
     545  int
     546  main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
     547      int ret = 0;
     548      int old_errors, old_tests, old_leaks;
     549  
     550      logfile = fopen(LOGFILE, "w");
     551      if (logfile == NULL) {
     552          fprintf(stderr,
     553  	        "Could not open the log file, running in verbose mode\n");
     554  	verbose = 1;
     555      }
     556      initializeLibxml2();
     557  
     558      if ((argc >= 2) && (!strcmp(argv[1], "-v")))
     559          verbose = 1;
     560  
     561  
     562      old_errors = nb_errors;
     563      old_tests = nb_tests;
     564      old_leaks = nb_leaks;
     565      xmlconfTest();
     566      if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
     567  	printf("Ran %d tests, no errors\n", nb_tests - old_tests);
     568      else
     569  	printf("Ran %d tests, %d errors, %d leaks\n",
     570  	       nb_tests - old_tests,
     571  	       nb_errors - old_errors,
     572  	       nb_leaks - old_leaks);
     573      if ((nb_errors == 0) && (nb_leaks == 0)) {
     574          ret = 0;
     575  	printf("Total %d tests, no errors\n",
     576  	       nb_tests);
     577      } else {
     578  	ret = 1;
     579  	printf("Total %d tests, %d errors, %d leaks\n",
     580  	       nb_tests, nb_errors, nb_leaks);
     581  	printf("See %s for detailed output\n", LOGFILE);
     582  	if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
     583  	    printf("%d errors were expected\n", nb_errors);
     584  	    ret = 0;
     585  	}
     586      }
     587      xmlXPathFreeContext(ctxtXPath);
     588      xmlCleanupParser();
     589  
     590      if (logfile != NULL)
     591          fclose(logfile);
     592      return(ret);
     593  }
     594  
     595  #else /* ! LIBXML_XPATH_ENABLED */
     596  int
     597  main(int argc ATTRIBUTE_UNUSED, char **argv) {
     598      fprintf(stderr, "%s need XPath and validation support\n", argv[0]);
     599      return(0);
     600  }
     601  #endif