/* * 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> #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; #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_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