aboutsummaryrefslogtreecommitdiffstats
path: root/runxmlconf.c
diff options
context:
space:
mode:
authorDaniel Veillard <veillard@src.gnome.org>2008-07-29 16:12:31 +0000
committerDaniel Veillard <veillard@src.gnome.org>2008-07-29 16:12:31 +0000
commit7e5c3f481f5e8c8eb2b038cb24a9b3d77e3806ab (patch)
treec0d8bd2049f3bcc6693e1f7c42c00613cf57f38e /runxmlconf.c
parent45490aebe7a60704e8ed850c498969858ab900ee (diff)
downloadandroid_external_libxml2-7e5c3f481f5e8c8eb2b038cb24a9b3d77e3806ab.tar.gz
android_external_libxml2-7e5c3f481f5e8c8eb2b038cb24a9b3d77e3806ab.tar.bz2
android_external_libxml2-7e5c3f481f5e8c8eb2b038cb24a9b3d77e3806ab.zip
add a C program to run the W3C test suite, work in progress add a new
* runxmlconf.c Makefile.am: add a C program to run the W3C test suite, work in progress * xmllint.c: add a new option --oldxml10 to use the old parser * parser.c: fix the XML_PARSE_OLD10 processing of the new option and a bug in version parsing Daniel svn path=/trunk/; revision=3757
Diffstat (limited to 'runxmlconf.c')
-rw-r--r--runxmlconf.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/runxmlconf.c b/runxmlconf.c
new file mode 100644
index 00000000..b2bd9357
--- /dev/null
+++ b/runxmlconf.c
@@ -0,0 +1,549 @@
+/*
+ * runsuite.c: C program to run libxml2 againts published testsuites
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "libxml.h"
+#else
+#include <stdio.h>
+#endif
+
+#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 "runsuite.log"
+static FILE *logfile = NULL;
+static int verbose = 0;
+
+
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+
+#define vsnprintf _vsnprintf
+
+#define snprintf _snprintf
+
+#endif
+
+/************************************************************************
+ * *
+ * 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;
+static int extraMemoryFromResolver = 0;
+
+static int
+fatalError(void) {
+ fprintf(stderr, "Exitting tests on fatal error\n");
+ exit(1);
+}
+
+/*
+ * that's needed to implement <resource>
+ */
+#define MAX_ENTITIES 20
+static char *testEntitiesName[MAX_ENTITIES];
+static char *testEntitiesValue[MAX_ENTITIES];
+static int nb_entities = 0;
+static void resetEntities(void) {
+ int i;
+
+ for (i = 0;i < nb_entities;i++) {
+ if (testEntitiesName[i] != NULL)
+ xmlFree(testEntitiesName[i]);
+ if (testEntitiesValue[i] != NULL)
+ xmlFree(testEntitiesValue[i]);
+ }
+ nb_entities = 0;
+}
+static int addEntity(char *name, char *content) {
+ if (nb_entities >= MAX_ENTITIES) {
+ fprintf(stderr, "Too many entities defined\n");
+ return(-1);
+ }
+ testEntitiesName[nb_entities] = name;
+ testEntitiesValue[nb_entities] = content;
+ nb_entities++;
+ return(0);
+}
+
+/*
+ * We need to trap calls to the resolver to not account memory for the catalog
+ * which is shared to the current running test. We also don't want to have
+ * network downloads modifying tests.
+ */
+static xmlParserInputPtr
+testExternalEntityLoader(const char *URL, const char *ID,
+ xmlParserCtxtPtr ctxt) {
+ xmlParserInputPtr ret;
+ int i;
+
+ for (i = 0;i < nb_entities;i++) {
+ if (!strcmp(testEntitiesName[i], URL)) {
+ ret = xmlNewStringInputStream(ctxt,
+ (const xmlChar *) testEntitiesValue[i]);
+ if (ret != NULL) {
+ ret->filename = (const char *)
+ xmlStrdup((xmlChar *)testEntitiesName[i]);
+ }
+ return(ret);
+ }
+ }
+ if (checkTestFile(URL)) {
+ ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
+ } else {
+ int memused = xmlMemUsed();
+ ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
+ extraMemoryFromResolver += xmlMemUsed() - memused;
+ }
+#if 0
+ if (ret == NULL) {
+ fprintf(stderr, "Failed to find resource %s\n", URL);
+ }
+#endif
+
+ 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 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 *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
+ va_list args;
+ int res;
+
+ if (testErrorsSize >= 32768)
+ return;
+ va_start(args, msg);
+ res = vsnprintf(&testErrors[testErrorsSize],
+ 32768 - testErrorsSize,
+ msg, args);
+ va_end(args);
+ 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);
+ xmlSetGenericErrorFunc(NULL, testErrorHandler);
+}
+
+/************************************************************************
+ * *
+ * Run the xmlconf test if found *
+ * *
+ ************************************************************************/
+
+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 *entities = NULL;
+ xmlChar *edition = NULL;
+ int options = 0;
+ int nstest = 0;
+ int mem, final;
+
+ id = xmlGetProp(cur, BAD_CAST "ID");
+ if (id == NULL) {
+ test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
+ 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;
+ }
+
+ entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
+ if (!xmlStrEqual(entities, BAD_CAST "none")) {
+ options |= XML_PARSE_DTDLOAD;
+ }
+ 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"))) {
+ 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")) {
+ } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
+ } else if (xmlStrEqual(type, BAD_CAST "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++;
+ }
+ nb_tests++;
+
+error:
+ if (type != NULL)
+ xmlFree(type);
+ if (entities != NULL)
+ xmlFree(entities);
+ if (edition != NULL)
+ xmlFree(edition);
+ 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) {
+ xmlChar *profile;
+ int ret = 0;
+ int tests = 0;
+
+ profile = xmlGetProp(cur, BAD_CAST "PROFILE");
+ if (profile != NULL) {
+ 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);
+ } 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 (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);
+ } 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/xmlts20080205.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);
+ }
+ xmlXPathFreeContext(ctxtXPath);
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ if (logfile != NULL)
+ fclose(logfile);
+ return(ret);
+}