diff options
Diffstat (limited to 'libxml2/runsuite.c')
-rw-r--r-- | libxml2/runsuite.c | 1172 |
1 files changed, 1172 insertions, 0 deletions
diff --git a/libxml2/runsuite.c b/libxml2/runsuite.c new file mode 100644 index 0000000..aaab13e --- /dev/null +++ b/libxml2/runsuite.c @@ -0,0 +1,1172 @@ +/* + * 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> + +#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> +#if defined(LIBXML_SCHEMAS_ENABLED) && defined(LIBXML_XPATH_ENABLED) +#include <libxml/xmlreader.h> + +#include <libxml/xpath.h> +#include <libxml/xpathInternals.h> + +#include <libxml/relaxng.h> +#include <libxml/xmlschemas.h> +#include <libxml/xmlschemastypes.h> + +#define LOGFILE "runsuite.log" +static FILE *logfile = NULL; +static int verbose = 0; + + +/************************************************************************ + * * + * 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_tests = 0; +static int nb_errors = 0; +static int nb_internals = 0; +static int nb_schematas = 0; +static int nb_unimplemented = 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); + /* used as default nanemspace in xstc tests */ + xmlXPathRegisterNs(ctxtXPath, BAD_CAST "ts", BAD_CAST "TestSuite"); + xmlXPathRegisterNs(ctxtXPath, BAD_CAST "xlink", + BAD_CAST "http://www.w3.org/1999/xlink"); + xmlSetGenericErrorFunc(NULL, testErrorHandler); +#ifdef LIBXML_SCHEMAS_ENABLED + xmlSchemaInitTypes(); + xmlRelaxNGInitTypes(); +#endif +} + +static xmlNodePtr +getNext(xmlNodePtr cur, const char *xpath) { + xmlNodePtr ret = NULL; + xmlXPathObjectPtr res; + xmlXPathCompExprPtr comp; + + if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL)) + return(NULL); + ctxtXPath->doc = cur->doc; + ctxtXPath->node = cur; + comp = xmlXPathCompile(BAD_CAST xpath); + if (comp == NULL) { + fprintf(stderr, "Failed to compile %s\n", xpath); + return(NULL); + } + res = xmlXPathCompiledEval(comp, ctxtXPath); + xmlXPathFreeCompExpr(comp); + if (res == NULL) + return(NULL); + if ((res->type == XPATH_NODESET) && + (res->nodesetval != NULL) && + (res->nodesetval->nodeNr > 0) && + (res->nodesetval->nodeTab != NULL)) + ret = res->nodesetval->nodeTab[0]; + xmlXPathFreeObject(res); + return(ret); +} + +static xmlChar * +getString(xmlNodePtr cur, const char *xpath) { + xmlChar *ret = NULL; + xmlXPathObjectPtr res; + xmlXPathCompExprPtr comp; + + if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL)) + return(NULL); + ctxtXPath->doc = cur->doc; + ctxtXPath->node = cur; + comp = xmlXPathCompile(BAD_CAST xpath); + if (comp == NULL) { + fprintf(stderr, "Failed to compile %s\n", xpath); + return(NULL); + } + res = xmlXPathCompiledEval(comp, ctxtXPath); + xmlXPathFreeCompExpr(comp); + if (res == NULL) + return(NULL); + if (res->type == XPATH_STRING) { + ret = res->stringval; + res->stringval = NULL; + } + xmlXPathFreeObject(res); + return(ret); +} + +/************************************************************************ + * * + * Test test/xsdtest/xsdtestsuite.xml * + * * + ************************************************************************/ + +static int +xsdIncorectTestCase(xmlNodePtr cur) { + xmlNodePtr test; + xmlBufferPtr buf; + xmlRelaxNGParserCtxtPtr pctxt; + xmlRelaxNGPtr rng = NULL; + int ret = 0, memt; + + cur = getNext(cur, "./incorrect[1]"); + if (cur == NULL) { + return(0); + } + + test = getNext(cur, "./*"); + if (test == NULL) { + test_log("Failed to find test in correct line %ld\n", + xmlGetLineNo(cur)); + return(1); + } + + memt = xmlMemUsed(); + extraMemoryFromResolver = 0; + /* + * dump the schemas to a buffer, then reparse it and compile the schemas + */ + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, test->doc, test, 0, 0); + pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use); + xmlRelaxNGSetParserErrors(pctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + pctxt); + rng = xmlRelaxNGParse(pctxt); + xmlRelaxNGFreeParserCtxt(pctxt); + if (rng != NULL) { + test_log("Failed to detect incorect RNG line %ld\n", + xmlGetLineNo(test)); + ret = 1; + goto done; + } + +done: + if (buf != NULL) + xmlBufferFree(buf); + if (rng != NULL) + xmlRelaxNGFree(rng); + xmlResetLastError(); + if ((memt < xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - memt); + nb_leaks++; + } + return(ret); +} + +static void +installResources(xmlNodePtr tst, const xmlChar *base) { + xmlNodePtr test; + xmlBufferPtr buf; + xmlChar *name, *content, *res; + + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, tst->doc, tst, 0, 0); + + while (tst != NULL) { + test = getNext(tst, "./*"); + if (test != NULL) { + xmlBufferEmpty(buf); + xmlNodeDump(buf, test->doc, test, 0, 0); + name = getString(tst, "string(@name)"); + content = xmlStrdup(buf->content); + if ((name != NULL) && (content != NULL)) { + res = composeDir(base, name); + xmlFree(name); + addEntity((char *) res, (char *) content); + } else { + if (name != NULL) xmlFree(name); + if (content != NULL) xmlFree(content); + } + } + tst = getNext(tst, "following-sibling::resource[1]"); + } + if (buf != NULL) + xmlBufferFree(buf); +} + +static void +installDirs(xmlNodePtr tst, const xmlChar *base) { + xmlNodePtr test; + xmlChar *name, *res; + + name = getString(tst, "string(@name)"); + if (name == NULL) + return; + res = composeDir(base, name); + xmlFree(name); + if (res == NULL) { + return; + } + /* Now process resources and subdir recursively */ + test = getNext(tst, "./resource[1]"); + if (test != NULL) { + installResources(test, res); + } + test = getNext(tst, "./dir[1]"); + while (test != NULL) { + installDirs(test, res); + test = getNext(test, "following-sibling::dir[1]"); + } + xmlFree(res); +} + +static int +xsdTestCase(xmlNodePtr tst) { + xmlNodePtr test, tmp, cur; + xmlBufferPtr buf; + xmlDocPtr doc = NULL; + xmlRelaxNGParserCtxtPtr pctxt; + xmlRelaxNGValidCtxtPtr ctxt; + xmlRelaxNGPtr rng = NULL; + int ret = 0, mem, memt; + xmlChar *dtd; + + resetEntities(); + testErrorsSize = 0; testErrors[0] = 0; + + tmp = getNext(tst, "./dir[1]"); + if (tmp != NULL) { + installDirs(tmp, NULL); + } + tmp = getNext(tst, "./resource[1]"); + if (tmp != NULL) { + installResources(tmp, NULL); + } + + cur = getNext(tst, "./correct[1]"); + if (cur == NULL) { + return(xsdIncorectTestCase(tst)); + } + + test = getNext(cur, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in correct line %ld\n", + xmlGetLineNo(cur)); + return(1); + } + + memt = xmlMemUsed(); + extraMemoryFromResolver = 0; + /* + * dump the schemas to a buffer, then reparse it and compile the schemas + */ + buf = xmlBufferCreate(); + if (buf == NULL) { + fprintf(stderr, "out of memory !\n"); + fatalError(); + } + xmlNodeDump(buf, test->doc, test, 0, 0); + pctxt = xmlRelaxNGNewMemParserCtxt((const char *)buf->content, buf->use); + xmlRelaxNGSetParserErrors(pctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + pctxt); + rng = xmlRelaxNGParse(pctxt); + xmlRelaxNGFreeParserCtxt(pctxt); + if (extraMemoryFromResolver) + memt = 0; + + if (rng == NULL) { + test_log("Failed to parse RNGtest line %ld\n", + xmlGetLineNo(test)); + nb_errors++; + ret = 1; + goto done; + } + /* + * now scan all the siblings of correct to process the <valid> tests + */ + tmp = getNext(cur, "following-sibling::valid[1]"); + while (tmp != NULL) { + dtd = xmlGetProp(tmp, BAD_CAST "dtd"); + test = getNext(tmp, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in <valid> line %ld\n", + xmlGetLineNo(tmp)); + + } else { + xmlBufferEmpty(buf); + if (dtd != NULL) + xmlBufferAdd(buf, dtd, -1); + xmlNodeDump(buf, test->doc, test, 0, 0); + + /* + * We are ready to run the test + */ + mem = xmlMemUsed(); + extraMemoryFromResolver = 0; + doc = xmlReadMemory((const char *)buf->content, buf->use, + "test", NULL, 0); + if (doc == NULL) { + test_log("Failed to parse valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else { + nb_tests++; + ctxt = xmlRelaxNGNewValidCtxt(rng); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + xmlRelaxNGFreeValidCtxt(ctxt); + if (ret > 0) { + test_log("Failed to validate valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else if (ret < 0) { + test_log("Internal error validating instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } + xmlFreeDoc(doc); + } + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of instance line %ld leaked %d\n", + xmlGetLineNo(tmp), xmlMemUsed() - mem); + xmlMemoryDump(); + nb_leaks++; + } + } + if (dtd != NULL) + xmlFree(dtd); + tmp = getNext(tmp, "following-sibling::valid[1]"); + } + /* + * now scan all the siblings of correct to process the <invalid> tests + */ + tmp = getNext(cur, "following-sibling::invalid[1]"); + while (tmp != NULL) { + test = getNext(tmp, "./*"); + if (test == NULL) { + fprintf(stderr, "Failed to find test in <invalid> line %ld\n", + xmlGetLineNo(tmp)); + + } else { + xmlBufferEmpty(buf); + xmlNodeDump(buf, test->doc, test, 0, 0); + + /* + * We are ready to run the test + */ + mem = xmlMemUsed(); + extraMemoryFromResolver = 0; + doc = xmlReadMemory((const char *)buf->content, buf->use, + "test", NULL, 0); + if (doc == NULL) { + test_log("Failed to parse valid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else { + nb_tests++; + ctxt = xmlRelaxNGNewValidCtxt(rng); + xmlRelaxNGSetValidErrors(ctxt, + (xmlRelaxNGValidityErrorFunc) testErrorHandler, + (xmlRelaxNGValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlRelaxNGValidateDoc(ctxt, doc); + xmlRelaxNGFreeValidCtxt(ctxt); + if (ret == 0) { + test_log("Failed to detect invalid instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } else if (ret < 0) { + test_log("Internal error validating instance line %ld\n", + xmlGetLineNo(tmp)); + nb_errors++; + } + xmlFreeDoc(doc); + } + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Validation of instance line %ld leaked %d\n", + xmlGetLineNo(tmp), xmlMemUsed() - mem); + xmlMemoryDump(); + nb_leaks++; + } + } + tmp = getNext(tmp, "following-sibling::invalid[1]"); + } + +done: + if (buf != NULL) + xmlBufferFree(buf); + if (rng != NULL) + xmlRelaxNGFree(rng); + xmlResetLastError(); + if ((memt != xmlMemUsed()) && (memt != 0)) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - memt); + nb_leaks++; + } + return(ret); +} + +static int +xsdTestSuite(xmlNodePtr cur) { + if (verbose) { + xmlChar *doc = getString(cur, "string(documentation)"); + + if (doc != NULL) { + printf("Suite %s\n", doc); + xmlFree(doc); + } + } + cur = getNext(cur, "./testCase[1]"); + while (cur != NULL) { + xsdTestCase(cur); + cur = getNext(cur, "following-sibling::testCase[1]"); + } + + return(0); +} + +static int +xsdTest(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/xsdtest/xsdtestsuite.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## XML Schemas datatypes test suite from James Clark\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +static int +rngTestSuite(xmlNodePtr cur) { + if (verbose) { + xmlChar *doc = getString(cur, "string(documentation)"); + + if (doc != NULL) { + printf("Suite %s\n", doc); + xmlFree(doc); + } else { + doc = getString(cur, "string(section)"); + if (doc != NULL) { + printf("Section %s\n", doc); + xmlFree(doc); + } + } + } + cur = getNext(cur, "./testSuite[1]"); + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + + return(0); +} + +static int +rngTest1(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/relaxng/OASIS/spectest.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## Relax NG test suite from James Clark\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + rngTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +static int +rngTest2(void) { + xmlDocPtr doc; + xmlNodePtr cur; + const char *filename = "test/relaxng/testsuite.xml"; + int ret = 0; + + doc = xmlReadFile(filename, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", filename); + return(-1); + } + printf("## Relax NG test suite for libxml2\n"); + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + + cur = getNext(cur, "./testSuite[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSuite"))) { + fprintf(stderr, "Unexpected format %s\n", filename); + ret = -1; + goto done; + } + while (cur != NULL) { + xsdTestSuite(cur); + cur = getNext(cur, "following-sibling::testSuite[1]"); + } + +done: + if (doc != NULL) + xmlFreeDoc(doc); + return(ret); +} + +/************************************************************************ + * * + * Schemas test suites from W3C/NIST/MS/Sun * + * * + ************************************************************************/ + +static int +xstcTestInstance(xmlNodePtr cur, xmlSchemaPtr schemas, + const xmlChar *spath, const char *base) { + xmlChar *href = NULL; + xmlChar *path = NULL; + xmlChar *validity = NULL; + xmlSchemaValidCtxtPtr ctxt = NULL; + xmlDocPtr doc = NULL; + int ret = 0, mem; + + xmlResetLastError(); + testErrorsSize = 0; testErrors[0] = 0; + mem = xmlMemUsed(); + href = getString(cur, + "string(ts:instanceDocument/@xlink:href)"); + if ((href == NULL) || (href[0] == 0)) { + test_log("testGroup line %ld misses href for schemaDocument\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + path = xmlBuildURI(href, BAD_CAST base); + if (path == NULL) { + fprintf(stderr, + "Failed to build path to schemas testGroup line %ld : %s\n", + xmlGetLineNo(cur), href); + ret = -1; + goto done; + } + if (checkTestFile((const char *) path) <= 0) { + test_log("schemas for testGroup line %ld is missing: %s\n", + xmlGetLineNo(cur), path); + ret = -1; + goto done; + } + validity = getString(cur, + "string(ts:expected/@validity)"); + if (validity == NULL) { + fprintf(stderr, "instanceDocument line %ld misses expected validity\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + nb_tests++; + doc = xmlReadFile((const char *) path, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "instance %s fails to parse\n", path); + ret = -1; + nb_errors++; + goto done; + } + + ctxt = xmlSchemaNewValidCtxt(schemas); + xmlSchemaSetValidErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + ret = xmlSchemaValidateDoc(ctxt, doc); + + if (xmlStrEqual(validity, BAD_CAST "valid")) { + if (ret > 0) { + test_log("valid instance %s failed to validate against %s\n", + path, spath); + nb_errors++; + } else if (ret < 0) { + test_log("valid instance %s got internal error validating %s\n", + path, spath); + nb_internals++; + nb_errors++; + } + } else if (xmlStrEqual(validity, BAD_CAST "invalid")) { + if (ret == 0) { + test_log("Failed to detect invalid instance %s against %s\n", + path, spath); + nb_errors++; + } + } else { + test_log("instanceDocument line %ld has unexpected validity value%s\n", + xmlGetLineNo(cur), validity); + ret = -1; + goto done; + } + +done: + if (href != NULL) xmlFree(href); + if (path != NULL) xmlFree(path); + if (validity != NULL) xmlFree(validity); + if (ctxt != NULL) xmlSchemaFreeValidCtxt(ctxt); + if (doc != NULL) xmlFreeDoc(doc); + xmlResetLastError(); + if (mem != xmlMemUsed()) { + test_log("Validation of tests starting line %ld leaked %d\n", + xmlGetLineNo(cur), xmlMemUsed() - mem); + nb_leaks++; + } + return(ret); +} + +static int +xstcTestGroup(xmlNodePtr cur, const char *base) { + xmlChar *href = NULL; + xmlChar *path = NULL; + xmlChar *validity = NULL; + xmlSchemaPtr schemas = NULL; + xmlSchemaParserCtxtPtr ctxt; + xmlNodePtr instance; + int ret = 0, mem; + + xmlResetLastError(); + testErrorsSize = 0; testErrors[0] = 0; + mem = xmlMemUsed(); + href = getString(cur, + "string(ts:schemaTest/ts:schemaDocument/@xlink:href)"); + if ((href == NULL) || (href[0] == 0)) { + test_log("testGroup line %ld misses href for schemaDocument\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + path = xmlBuildURI(href, BAD_CAST base); + if (path == NULL) { + test_log("Failed to build path to schemas testGroup line %ld : %s\n", + xmlGetLineNo(cur), href); + ret = -1; + goto done; + } + if (checkTestFile((const char *) path) <= 0) { + test_log("schemas for testGroup line %ld is missing: %s\n", + xmlGetLineNo(cur), path); + ret = -1; + goto done; + } + validity = getString(cur, + "string(ts:schemaTest/ts:expected/@validity)"); + if (validity == NULL) { + test_log("testGroup line %ld misses expected validity\n", + xmlGetLineNo(cur)); + ret = -1; + goto done; + } + nb_tests++; + if (xmlStrEqual(validity, BAD_CAST "valid")) { + nb_schematas++; + ctxt = xmlSchemaNewParserCtxt((const char *) path); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlSchemaParse(ctxt); + xmlSchemaFreeParserCtxt(ctxt); + if (schemas == NULL) { + test_log("valid schemas %s failed to parse\n", + path); + ret = 1; + nb_errors++; + } + if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) { + test_log("valid schemas %s hit an unimplemented block\n", + path); + ret = 1; + nb_unimplemented++; + nb_errors++; + } + instance = getNext(cur, "./ts:instanceTest[1]"); + while (instance != NULL) { + if (schemas != NULL) { + xstcTestInstance(instance, schemas, path, base); + } else { + /* + * We'll automatically mark the instances as failed + * if the schema was broken. + */ + nb_errors++; + } + instance = getNext(instance, + "following-sibling::ts:instanceTest[1]"); + } + } else if (xmlStrEqual(validity, BAD_CAST "invalid")) { + nb_schematas++; + ctxt = xmlSchemaNewParserCtxt((const char *) path); + xmlSchemaSetParserErrors(ctxt, + (xmlSchemaValidityErrorFunc) testErrorHandler, + (xmlSchemaValidityWarningFunc) testErrorHandler, + ctxt); + schemas = xmlSchemaParse(ctxt); + xmlSchemaFreeParserCtxt(ctxt); + if (schemas != NULL) { + test_log("Failed to detect error in schemas %s\n", + path); + nb_errors++; + ret = 1; + } + if ((ret == 0) && (strstr(testErrors, "nimplemented") != NULL)) { + nb_unimplemented++; + test_log("invalid schemas %s hit an unimplemented block\n", + path); + ret = 1; + nb_errors++; + } + } else { + test_log("testGroup line %ld misses unexpected validity value%s\n", + xmlGetLineNo(cur), validity); + ret = -1; + goto done; + } + +done: + if (href != NULL) xmlFree(href); + if (path != NULL) xmlFree(path); + if (validity != NULL) xmlFree(validity); + if (schemas != NULL) xmlSchemaFree(schemas); + xmlResetLastError(); + if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) { + test_log("Processing test line %ld %s leaked %d\n", + xmlGetLineNo(cur), path, xmlMemUsed() - mem); + nb_leaks++; + } + return(ret); +} + +static int +xstcMetadata(const char *metadata, const char *base) { + xmlDocPtr doc; + xmlNodePtr cur; + xmlChar *contributor; + xmlChar *name; + int ret = 0; + + doc = xmlReadFile(metadata, NULL, XML_PARSE_NOENT); + if (doc == NULL) { + fprintf(stderr, "Failed to parse %s\n", metadata); + return(-1); + } + + cur = xmlDocGetRootElement(doc); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testSet"))) { + fprintf(stderr, "Unexpected format %s\n", metadata); + return(-1); + } + contributor = xmlGetProp(cur, BAD_CAST "contributor"); + if (contributor == NULL) { + contributor = xmlStrdup(BAD_CAST "Unknown"); + } + name = xmlGetProp(cur, BAD_CAST "name"); + if (name == NULL) { + name = xmlStrdup(BAD_CAST "Unknown"); + } + printf("## %s test suite for Schemas version %s\n", contributor, name); + xmlFree(contributor); + xmlFree(name); + + cur = getNext(cur, "./ts:testGroup[1]"); + if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "testGroup"))) { + fprintf(stderr, "Unexpected format %s\n", metadata); + ret = -1; + goto done; + } + while (cur != NULL) { + xstcTestGroup(cur, base); + cur = getNext(cur, "following-sibling::ts:testGroup[1]"); + } + +done: + 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; + xsdTest(); + 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); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + rngTest1(); + 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); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + rngTest2(); + 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); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/NISTXMLSchemaDatatypes.testSet", + "xstc/Tests/Metadata/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/SunXMLSchema1-0-20020116.testSet", + "xstc/Tests/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + nb_leaks - old_leaks); + old_errors = nb_errors; + old_tests = nb_tests; + old_leaks = nb_leaks; + nb_internals = 0; + nb_schematas = 0; + xstcMetadata("xstc/Tests/Metadata/MSXMLSchema1-0-20020116.testSet", + "xstc/Tests/"); + if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) + printf("Ran %d tests (%d schemata), no errors\n", + nb_tests - old_tests, nb_schematas); + else + printf("Ran %d tests (%d schemata), %d errors (%d internals), %d leaks\n", + nb_tests - old_tests, + nb_schematas, + nb_errors - old_errors, + nb_internals, + 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); +} +#else /* !SCHEMAS */ +int +main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { + fprintf(stderr, "runsuite requires support for schemas and xpath in libxml2\n"); +} +#endif |