| /* |
| * runsuite.c: C program to run libxml2 against 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 explicitly with xmlXPathContextSetCache(); |
| * but for test purposes it is sometimes useful to activate the |
| * cache by default for the whole library. |
| */ |
| if (ctxtXPath->cache != NULL) |
| xmlXPathContextSetCache(ctxtXPath, 0, -1, 0); |
| /* used as default namespace 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 |
| xsdIncorrectTestCase(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, testErrorHandler, testErrorHandler, |
| pctxt); |
| rng = xmlRelaxNGParse(pctxt); |
| xmlRelaxNGFreeParserCtxt(pctxt); |
| if (rng != NULL) { |
| test_log("Failed to detect incorrect 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(xsdIncorrectTestCase(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, testErrorHandler, 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, |
| testErrorHandler, 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, |
| testErrorHandler, 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, testErrorHandler, 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, testErrorHandler, 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, testErrorHandler, 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 |