summaryrefslogtreecommitdiffstats
path: root/libxml2/runxmlconf.c
diff options
context:
space:
mode:
authorWilliam Joye <wjoye@cfa.harvard.edu>2016-10-19 18:28:08 (GMT)
committerWilliam Joye <wjoye@cfa.harvard.edu>2016-10-19 18:28:08 (GMT)
commit7f402e480a04b974617c04142993d3919457d342 (patch)
treea39cf901d49b9767df3fe076ebf8a06c92277995 /libxml2/runxmlconf.c
parent818fd731f62993dc8aa5d07b82e1d1b2c04aae7d (diff)
parent6c75f0dc676d73c4cbcdca5a9cf05c6797a2ac0b (diff)
downloadblt-7f402e480a04b974617c04142993d3919457d342.zip
blt-7f402e480a04b974617c04142993d3919457d342.tar.gz
blt-7f402e480a04b974617c04142993d3919457d342.tar.bz2
Merge commit '6c75f0dc676d73c4cbcdca5a9cf05c6797a2ac0b' as 'libxml2'
Diffstat (limited to 'libxml2/runxmlconf.c')
-rw-r--r--libxml2/runxmlconf.c607
1 files changed, 607 insertions, 0 deletions
diff --git a/libxml2/runxmlconf.c b/libxml2/runxmlconf.c
new file mode 100644
index 0000000..cef20f4
--- /dev/null
+++ b/libxml2/runxmlconf.c
@@ -0,0 +1,607 @@
+/*
+ * runsuite.c: C program to run libxml2 againts published testsuites
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#include "libxml.h"
+#include <stdio.h>
+
+#ifdef LIBXML_XPATH_ENABLED
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#include <libxml/xmlreader.h>
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#define LOGFILE "runxmlconf.log"
+static FILE *logfile = NULL;
+static int verbose = 0;
+
+#define NB_EXPECTED_ERRORS 15
+
+
+const char *skipped_tests[] = {
+/* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */
+ "rmt-ns10-035",
+ NULL
+};
+
+/************************************************************************
+ * *
+ * File name and path utilities *
+ * *
+ ************************************************************************/
+
+static int checkTestFile(const char *filename) {
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1)
+ return(0);
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ if (!(buf.st_mode & _S_IFREG))
+ return(0);
+#else
+ if (!S_ISREG(buf.st_mode))
+ return(0);
+#endif
+
+ return(1);
+}
+
+static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
+ char buf[500];
+
+ if (dir == NULL) return(xmlStrdup(path));
+ if (path == NULL) return(NULL);
+
+ snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
+ return(xmlStrdup((const xmlChar *) buf));
+}
+
+/************************************************************************
+ * *
+ * Libxml2 specific routines *
+ * *
+ ************************************************************************/
+
+static int nb_skipped = 0;
+static int nb_tests = 0;
+static int nb_errors = 0;
+static int nb_leaks = 0;
+
+/*
+ * We need to trap calls to the resolver to not account memory for the catalog
+ * and not rely on any external resources.
+ */
+static xmlParserInputPtr
+testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
+ xmlParserCtxtPtr ctxt) {
+ xmlParserInputPtr ret;
+
+ ret = xmlNewInputFromFile(ctxt, (const char *) URL);
+
+ return(ret);
+}
+
+/*
+ * Trapping the error messages at the generic level to grab the equivalent of
+ * stderr messages on CLI tools.
+ */
+static char testErrors[32769];
+static int testErrorsSize = 0;
+static int nbError = 0;
+static int nbFatal = 0;
+
+static void test_log(const char *msg, ...) {
+ va_list args;
+ if (logfile != NULL) {
+ fprintf(logfile, "\n------------\n");
+ va_start(args, msg);
+ vfprintf(logfile, msg, args);
+ va_end(args);
+ fprintf(logfile, "%s", testErrors);
+ testErrorsSize = 0; testErrors[0] = 0;
+ }
+ if (verbose) {
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+ }
+}
+
+static void
+testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) {
+ int res;
+
+ if (testErrorsSize >= 32768)
+ return;
+ res = snprintf(&testErrors[testErrorsSize],
+ 32768 - testErrorsSize,
+ "%s:%d: %s\n", (error->file ? error->file : "entity"),
+ error->line, error->message);
+ if (error->level == XML_ERR_FATAL)
+ nbFatal++;
+ else if (error->level == XML_ERR_ERROR)
+ nbError++;
+ if (testErrorsSize + res >= 32768) {
+ /* buffer is full */
+ testErrorsSize = 32768;
+ testErrors[testErrorsSize] = 0;
+ } else {
+ testErrorsSize += res;
+ }
+ testErrors[testErrorsSize] = 0;
+}
+
+static xmlXPathContextPtr ctxtXPath;
+
+static void
+initializeLibxml2(void) {
+ xmlGetWarningsDefaultValue = 0;
+ xmlPedanticParserDefault(0);
+
+ xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
+ xmlInitParser();
+ xmlSetExternalEntityLoader(testExternalEntityLoader);
+ ctxtXPath = xmlXPathNewContext(NULL);
+ /*
+ * Deactivate the cache if created; otherwise we have to create/free it
+ * for every test, since it will confuse the memory leak detection.
+ * Note that normally this need not be done, since the cache is not
+ * created until set explicitely with xmlXPathContextSetCache();
+ * but for test purposes it is sometimes usefull to activate the
+ * cache by default for the whole library.
+ */
+ if (ctxtXPath->cache != NULL)
+ xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
+ xmlSetStructuredErrorFunc(NULL, testErrorHandler);
+}
+
+/************************************************************************
+ * *
+ * Run the xmlconf test if found *
+ * *
+ ************************************************************************/
+
+static int
+xmlconfTestInvalid(const char *id, const char *filename, int options) {
+ xmlDocPtr doc;
+ xmlParserCtxtPtr ctxt;
+ int ret = 1;
+
+ ctxt = xmlNewParserCtxt();
+ if (ctxt == NULL) {
+ test_log("test %s : %s out of memory\n",
+ id, filename);
+ return(0);
+ }
+ doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
+ if (doc == NULL) {
+ test_log("test %s : %s invalid document turned not well-formed too\n",
+ id, filename);
+ } else {
+ /* invalidity should be reported both in the context and in the document */
+ if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) {
+ test_log("test %s : %s failed to detect invalid document\n",
+ id, filename);
+ nb_errors++;
+ ret = 0;
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlFreeParserCtxt(ctxt);
+ return(ret);
+}
+
+static int
+xmlconfTestValid(const char *id, const char *filename, int options) {
+ xmlDocPtr doc;
+ xmlParserCtxtPtr ctxt;
+ int ret = 1;
+
+ ctxt = xmlNewParserCtxt();
+ if (ctxt == NULL) {
+ test_log("test %s : %s out of memory\n",
+ id, filename);
+ return(0);
+ }
+ doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
+ if (doc == NULL) {
+ test_log("test %s : %s failed to parse a valid document\n",
+ id, filename);
+ nb_errors++;
+ ret = 0;
+ } else {
+ /* validity should be reported both in the context and in the document */
+ if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
+ test_log("test %s : %s failed to validate a valid document\n",
+ id, filename);
+ nb_errors++;
+ ret = 0;
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlFreeParserCtxt(ctxt);
+ return(ret);
+}
+
+static int
+xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
+ xmlDocPtr doc;
+ int ret = 1;
+
+ /*
+ * In case of Namespace errors, libxml2 will still parse the document
+ * but log a Namesapce error.
+ */
+ doc = xmlReadFile(filename, NULL, options);
+ if (doc == NULL) {
+ test_log("test %s : %s failed to parse the XML\n",
+ id, filename);
+ nb_errors++;
+ ret = 0;
+ } else {
+ if ((xmlLastError.code == XML_ERR_OK) ||
+ (xmlLastError.domain != XML_FROM_NAMESPACE)) {
+ test_log("test %s : %s failed to detect namespace error\n",
+ id, filename);
+ nb_errors++;
+ ret = 0;
+ }
+ xmlFreeDoc(doc);
+ }
+ return(ret);
+}
+
+static int
+xmlconfTestNotWF(const char *id, const char *filename, int options) {
+ xmlDocPtr doc;
+ int ret = 1;
+
+ doc = xmlReadFile(filename, NULL, options);
+ if (doc != NULL) {
+ test_log("test %s : %s failed to detect not well formedness\n",
+ id, filename);
+ nb_errors++;
+ xmlFreeDoc(doc);
+ ret = 0;
+ }
+ return(ret);
+}
+
+static int
+xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
+ int ret = -1;
+ xmlChar *type = NULL;
+ xmlChar *filename = NULL;
+ xmlChar *uri = NULL;
+ xmlChar *base = NULL;
+ xmlChar *id = NULL;
+ xmlChar *rec = NULL;
+ xmlChar *version = NULL;
+ xmlChar *entities = NULL;
+ xmlChar *edition = NULL;
+ int options = 0;
+ int nstest = 0;
+ int mem, final;
+ int i;
+
+ testErrorsSize = 0; testErrors[0] = 0;
+ nbError = 0;
+ nbFatal = 0;
+ id = xmlGetProp(cur, BAD_CAST "ID");
+ if (id == NULL) {
+ test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
+ goto error;
+ }
+ for (i = 0;skipped_tests[i] != NULL;i++) {
+ if (!strcmp(skipped_tests[i], (char *) id)) {
+ test_log("Skipping test %s from skipped list\n", (char *) id);
+ ret = 0;
+ nb_skipped++;
+ goto error;
+ }
+ }
+ type = xmlGetProp(cur, BAD_CAST "TYPE");
+ if (type == NULL) {
+ test_log("test %s missing TYPE\n", (char *) id);
+ goto error;
+ }
+ uri = xmlGetProp(cur, BAD_CAST "URI");
+ if (uri == NULL) {
+ test_log("test %s missing URI\n", (char *) id);
+ goto error;
+ }
+ base = xmlNodeGetBase(doc, cur);
+ filename = composeDir(base, uri);
+ if (!checkTestFile((char *) filename)) {
+ test_log("test %s missing file %s \n", id,
+ (filename ? (char *)filename : "NULL"));
+ goto error;
+ }
+
+ version = xmlGetProp(cur, BAD_CAST "VERSION");
+
+ entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
+ if (!xmlStrEqual(entities, BAD_CAST "none")) {
+ options |= XML_PARSE_DTDLOAD;
+ options |= XML_PARSE_NOENT;
+ }
+ rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
+ if ((rec == NULL) ||
+ (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
+ (xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
+ (xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
+ (xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
+ if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
+ test_log("Skipping test %s for %s\n", (char *) id,
+ (char *) version);
+ ret = 0;
+ nb_skipped++;
+ goto error;
+ }
+ ret = 1;
+ } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
+ (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
+ ret = 1;
+ nstest = 1;
+ } else {
+ test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
+ ret = 0;
+ nb_skipped++;
+ goto error;
+ }
+ edition = xmlGetProp(cur, BAD_CAST "EDITION");
+ if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
+ /* test limited to all versions before 5th */
+ options |= XML_PARSE_OLD10;
+ }
+
+ /*
+ * Reset errors and check memory usage before the test
+ */
+ xmlResetLastError();
+ testErrorsSize = 0; testErrors[0] = 0;
+ mem = xmlMemUsed();
+
+ if (xmlStrEqual(type, BAD_CAST "not-wf")) {
+ if (nstest == 0)
+ xmlconfTestNotWF((char *) id, (char *) filename, options);
+ else
+ xmlconfTestNotNSWF((char *) id, (char *) filename, options);
+ } else if (xmlStrEqual(type, BAD_CAST "valid")) {
+ options |= XML_PARSE_DTDVALID;
+ xmlconfTestValid((char *) id, (char *) filename, options);
+ } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
+ options |= XML_PARSE_DTDVALID;
+ xmlconfTestInvalid((char *) id, (char *) filename, options);
+ } else if (xmlStrEqual(type, BAD_CAST "error")) {
+ test_log("Skipping error test %s \n", (char *) id);
+ ret = 0;
+ nb_skipped++;
+ goto error;
+ } else {
+ test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
+ ret = -1;
+ goto error;
+ }
+
+ /*
+ * Reset errors and check memory usage after the test
+ */
+ xmlResetLastError();
+ final = xmlMemUsed();
+ if (final > mem) {
+ test_log("test %s : %s leaked %d bytes\n",
+ id, filename, final - mem);
+ nb_leaks++;
+ xmlMemDisplayLast(logfile, final - mem);
+ }
+ nb_tests++;
+
+error:
+ if (type != NULL)
+ xmlFree(type);
+ if (entities != NULL)
+ xmlFree(entities);
+ if (edition != NULL)
+ xmlFree(edition);
+ if (version != NULL)
+ xmlFree(version);
+ if (filename != NULL)
+ xmlFree(filename);
+ if (uri != NULL)
+ xmlFree(uri);
+ if (base != NULL)
+ xmlFree(base);
+ if (id != NULL)
+ xmlFree(id);
+ if (rec != NULL)
+ xmlFree(rec);
+ return(ret);
+}
+
+static int
+xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
+ xmlChar *profile;
+ int ret = 0;
+ int tests = 0;
+ int output = 0;
+
+ if (level == 1) {
+ profile = xmlGetProp(cur, BAD_CAST "PROFILE");
+ if (profile != NULL) {
+ output = 1;
+ level++;
+ printf("Test cases: %s\n", (char *) profile);
+ xmlFree(profile);
+ }
+ }
+ cur = cur->children;
+ while (cur != NULL) {
+ /* look only at elements we ignore everything else */
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
+ ret += xmlconfTestCases(doc, cur, level);
+ } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
+ if (xmlconfTestItem(doc, cur) >= 0)
+ ret++;
+ tests++;
+ } else {
+ fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
+ }
+ }
+ cur = cur->next;
+ }
+ if (output == 1) {
+ if (tests > 0)
+ printf("Test cases: %d tests\n", tests);
+ }
+ return(ret);
+}
+
+static int
+xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
+ xmlChar *profile;
+ int ret = 0;
+
+ profile = xmlGetProp(cur, BAD_CAST "PROFILE");
+ if (profile != NULL) {
+ printf("Test suite: %s\n", (char *) profile);
+ xmlFree(profile);
+ } else
+ printf("Test suite\n");
+ cur = cur->children;
+ while (cur != NULL) {
+ /* look only at elements we ignore everything else */
+ if (cur->type == XML_ELEMENT_NODE) {
+ if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
+ ret += xmlconfTestCases(doc, cur, 1);
+ } else {
+ fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
+ }
+ }
+ cur = cur->next;
+ }
+ return(ret);
+}
+
+static void
+xmlconfInfo(void) {
+ fprintf(stderr, " you need to fetch and extract the\n");
+ fprintf(stderr, " latest XML Conformance Test Suites\n");
+ fprintf(stderr, " http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
+ fprintf(stderr, " see http://www.w3.org/XML/Test/ for informations\n");
+}
+
+static int
+xmlconfTest(void) {
+ const char *confxml = "xmlconf/xmlconf.xml";
+ xmlDocPtr doc;
+ xmlNodePtr cur;
+ int ret = 0;
+
+ if (!checkTestFile(confxml)) {
+ fprintf(stderr, "%s is missing \n", confxml);
+ xmlconfInfo();
+ return(-1);
+ }
+ doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
+ if (doc == NULL) {
+ fprintf(stderr, "%s is corrupted \n", confxml);
+ xmlconfInfo();
+ return(-1);
+ }
+
+ cur = xmlDocGetRootElement(doc);
+ if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
+ fprintf(stderr, "Unexpected format %s\n", confxml);
+ xmlconfInfo();
+ ret = -1;
+ } else {
+ ret = xmlconfTestSuite(doc, cur);
+ }
+ xmlFreeDoc(doc);
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * The driver for the tests *
+ * *
+ ************************************************************************/
+
+int
+main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
+ int ret = 0;
+ int old_errors, old_tests, old_leaks;
+
+ logfile = fopen(LOGFILE, "w");
+ if (logfile == NULL) {
+ fprintf(stderr,
+ "Could not open the log file, running in verbose mode\n");
+ verbose = 1;
+ }
+ initializeLibxml2();
+
+ if ((argc >= 2) && (!strcmp(argv[1], "-v")))
+ verbose = 1;
+
+
+ old_errors = nb_errors;
+ old_tests = nb_tests;
+ old_leaks = nb_leaks;
+ xmlconfTest();
+ if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
+ printf("Ran %d tests, no errors\n", nb_tests - old_tests);
+ else
+ printf("Ran %d tests, %d errors, %d leaks\n",
+ nb_tests - old_tests,
+ nb_errors - old_errors,
+ nb_leaks - old_leaks);
+ if ((nb_errors == 0) && (nb_leaks == 0)) {
+ ret = 0;
+ printf("Total %d tests, no errors\n",
+ nb_tests);
+ } else {
+ ret = 1;
+ printf("Total %d tests, %d errors, %d leaks\n",
+ nb_tests, nb_errors, nb_leaks);
+ printf("See %s for detailed output\n", LOGFILE);
+ if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
+ printf("%d errors were expected\n", nb_errors);
+ ret = 0;
+ }
+ }
+ xmlXPathFreeContext(ctxtXPath);
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ if (logfile != NULL)
+ fclose(logfile);
+ return(ret);
+}
+
+#else /* ! LIBXML_XPATH_ENABLED */
+#include <stdio.h>
+int
+main(int argc, char **argv) {
+ fprintf(stderr, "%s need XPath support\n", argv[0]);
+}
+#endif