diff options
author | Daniel Veillard <veillard@src.gnome.org> | 2008-07-29 16:12:31 +0000 |
---|---|---|
committer | Daniel Veillard <veillard@src.gnome.org> | 2008-07-29 16:12:31 +0000 |
commit | 7e5c3f481f5e8c8eb2b038cb24a9b3d77e3806ab (patch) | |
tree | c0d8bd2049f3bcc6693e1f7c42c00613cf57f38e /runxmlconf.c | |
parent | 45490aebe7a60704e8ed850c498969858ab900ee (diff) | |
download | android_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.c | 549 |
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); +} |