complete, checking on other platforms is needed updated fix a bug raised
* runtest.c: complete, checking on other platforms is needed
* README: updated
* debugXML.c: fix a bug raised by bill on IRC
* relaxng.c: fix a leak in weird circumstances
* runsuite.c Makefile.am: standalone test tool agaisnt
the regression suites, work in progress
Daniel
diff --git a/ChangeLog b/ChangeLog
index 173fb50..01039a6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+Thu Jun 30 15:01:52 CEST 2005 Daniel Veillard <daniel@veillard.com>
+
+ * runtest.c: complete, checking on other platforms is needed
+ * README: updated
+ * debugXML.c: fix a bug raised by bill on IRC
+ * relaxng.c: fix a leak in weird circumstances
+ * runsuite.c Makefile.am: standalone test tool agaisnt
+ the regression suites, work in progress
+
Tue Jun 28 08:30:26 CEST 2005 Daniel Veillard <daniel@veillard.com>
* runtest.c: adding URI tests
diff --git a/Makefile.am b/Makefile.am
index f0ef80c..5b5fc78 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -8,7 +8,7 @@
noinst_PROGRAMS=testSchemas testRelax testSAX testHTML testXPath testURI \
testThreads testC14N testAutomata testRegexp \
- testReader testapi testModule runtest
+ testReader testapi testModule runtest runsuite
bin_PROGRAMS = xmllint xmlcatalog
@@ -52,7 +52,12 @@
runtest_SOURCES=runtest.c
runtest_LDFLAGS =
runtest_DEPENDENCIES = $(DEPS)
-runtest_LDADD= @RDL_LIBS@ $(LDADDS)
+runtest_LDADD= @BASE_THREAD_LIBS@ @RDL_LIBS@ $(LDADDS)
+
+runsuite_SOURCES=runsuite.c
+runsuite_LDFLAGS =
+runsuite_DEPENDENCIES = $(DEPS)
+runsuite_LDADD= @RDL_LIBS@ $(LDADDS)
xmllint_SOURCES=xmllint.c
xmllint_LDFLAGS =
diff --git a/README b/README
index 279b375..7726225 100644
--- a/README
+++ b/README
@@ -6,6 +6,21 @@
This code is released under the MIT Licence see the Copyright file.
+To build on an Unixised setup:
+ ./configure ; make ; make install
+To build on Windows:
+ see instructions on win32/Readme.txt
+
+To assert build quality:
+ on an Unixised setup:
+ run make tests
+ otherwise:
+ There is 2 standalone tools runtest.c and testapi.c, which should
+ compile as part of the build or as any application would.
+ Launch them from this directory to get results, runtest checks
+ the proper functionning of libxml2 main APIs while testapi does
+ a full coverage check. Report failures to the list.
+
To report bugs, follow the instructions at:
http://xmlsoft.org/bugs.html
diff --git a/debugXML.c b/debugXML.c
index 20190ad..2335c4c 100644
--- a/debugXML.c
+++ b/debugXML.c
@@ -344,6 +344,7 @@
if ((node->type != XML_ELEMENT_NODE) &&
(node->type != XML_ATTRIBUTE_NODE) &&
+ (node->type != XML_ELEMENT_DECL) &&
(node->type != XML_ATTRIBUTE_DECL) &&
(node->type != XML_DTD_NODE) &&
(node->type != XML_HTML_DOCUMENT_NODE) &&
diff --git a/relaxng.c b/relaxng.c
index 41c899b..b1d4a77 100644
--- a/relaxng.c
+++ b/relaxng.c
@@ -6908,6 +6908,8 @@
xmlRngPErr(ctxt, cur, XML_RNGP_MISSING_HREF,
"xmlRelaxNGParse: externalRef has no href attribute\n",
NULL, NULL);
+ if (ns != NULL)
+ xmlFree(ns);
delete = cur;
goto skip_children;
}
@@ -6916,6 +6918,8 @@
xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
"Incorrect URI for externalRef %s\n",
href, NULL);
+ if (ns != NULL)
+ xmlFree(ns);
if (href != NULL)
xmlFree(href);
delete = cur;
@@ -6925,6 +6929,8 @@
xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
"Fragment forbidden in URI for externalRef %s\n",
href, NULL);
+ if (ns != NULL)
+ xmlFree(ns);
xmlFreeURI(uri);
if (href != NULL)
xmlFree(href);
@@ -6938,6 +6944,8 @@
xmlRngPErr(ctxt, cur, XML_RNGP_HREF_ERROR,
"Failed to compute URL for externalRef %s\n",
href, NULL);
+ if (ns != NULL)
+ xmlFree(ns);
if (href != NULL)
xmlFree(href);
if (base != NULL)
@@ -6954,6 +6962,8 @@
xmlRngPErr(ctxt, cur, XML_RNGP_EXTERNAL_REF_FAILURE,
"Failed to load externalRef %s\n", URL,
NULL);
+ if (ns != NULL)
+ xmlFree(ns);
xmlFree(URL);
delete = cur;
goto skip_children;
diff --git a/runsuite.c b/runsuite.c
new file mode 100644
index 0000000..fefbafd
--- /dev/null
+++ b/runsuite.c
@@ -0,0 +1,610 @@
+/*
+ * runsuite.c: C program to run libxml2 againts external published testsuites
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <glob.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#include <libxml/xmlreader.h>
+
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libxml/relaxng.h>
+#include <libxml/xmlschemas.h>
+#include <libxml/xmlschemastypes.h>
+
+/************************************************************************
+ * *
+ * File name and path utilities *
+ * *
+ ************************************************************************/
+
+static int checkTestFile(const char *filename) {
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1)
+ return(0);
+
+ if (!S_ISREG(buf.st_mode))
+ return(0);
+
+ return(1);
+}
+
+/************************************************************************
+ * *
+ * Libxml2 specific routines *
+ * *
+ ************************************************************************/
+
+static int nb_tests = 0;
+static int nb_errors = 0;
+static int nb_leaks = 0;
+static long libxmlMemoryAllocatedBase = 0;
+static int extraMemoryFromResolver = 0;
+
+static int
+fatalError(void) {
+ fprintf(stderr, "Exitting tests on fatal error\n");
+ exit(1);
+}
+
+/*
+ * 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;
+
+ if (checkTestFile(URL)) {
+ ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
+ } else {
+ int memused = xmlMemUsed();
+ ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
+ extraMemoryFromResolver += xmlMemUsed() - memused;
+ }
+
+ 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
+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 void
+initializeLibxml2(void) {
+ xmlGetWarningsDefaultValue = 0;
+ xmlPedanticParserDefault(0);
+
+ xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
+ xmlInitParser();
+ xmlSetExternalEntityLoader(testExternalEntityLoader);
+#ifdef LIBXML_SCHEMAS_ENABLED
+ xmlSchemaInitTypes();
+ xmlRelaxNGInitTypes();
+#endif
+ libxmlMemoryAllocatedBase = xmlMemUsed();
+}
+
+static xmlNodePtr
+getNext(xmlNodePtr cur, const char *xpath) {
+ xmlNodePtr ret = NULL;
+ xmlXPathObjectPtr res;
+ xmlXPathContextPtr ctxt;
+ xmlXPathCompExprPtr comp;
+
+ if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL))
+ return(NULL);
+ ctxt = xmlXPathNewContext(cur->doc);
+ ctxt->node = cur;
+ comp = xmlXPathCompile(BAD_CAST xpath);
+ if (comp == NULL) {
+ fprintf(stderr, "Failed to compile %s\n", xpath);
+ xmlXPathFreeContext(ctxt);
+ return(NULL);
+ }
+ res = xmlXPathCompiledEval(comp, ctxt);
+ xmlXPathFreeCompExpr(comp);
+ xmlXPathFreeContext(ctxt);
+ 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;
+ xmlXPathContextPtr ctxt;
+ xmlXPathCompExprPtr comp;
+
+ if ((cur == NULL) || (cur->doc == NULL) || (xpath == NULL))
+ return(NULL);
+ ctxt = xmlXPathNewContext(cur->doc);
+ ctxt->node = cur;
+ comp = xmlXPathCompile(BAD_CAST xpath);
+ if (comp == NULL) {
+ fprintf(stderr, "Failed to compile %s\n", xpath);
+ return(NULL);
+ }
+ res = xmlXPathCompiledEval(comp, ctxt);
+ xmlXPathFreeCompExpr(comp);
+ xmlXPathFreeContext(ctxt);
+ 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(int verbose, 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) {
+ 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 (rng != NULL) {
+ fprintf(stderr, "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)) {
+ fprintf(stderr, "Validation of tests starting line %ld leaked %d\n",
+ xmlGetLineNo(cur), xmlMemUsed() - memt);
+ nb_leaks++;
+ }
+ return(ret);
+}
+
+static int
+xsdTestCase(int verbose, xmlNodePtr tst) {
+ xmlNodePtr test, tmp, cur;
+ xmlBufferPtr buf;
+ xmlDocPtr doc = NULL;
+ xmlRelaxNGParserCtxtPtr pctxt;
+ xmlRelaxNGValidCtxtPtr ctxt;
+ xmlRelaxNGPtr rng = NULL;
+ int ret = 0, mem, memt;
+
+ cur = getNext(tst, "./correct[1]");
+ if (cur == NULL) {
+ return(xsdIncorectTestCase(verbose, 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) {
+ fprintf(stderr, "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) {
+ test = getNext(tmp, "./*");
+ if (test == NULL) {
+ fprintf(stderr, "Failed to find test in <valid> 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) {
+ fprintf(stderr,
+ "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) {
+ fprintf(stderr,
+ "Failed to validate valid instance line %ld\n",
+ xmlGetLineNo(tmp));
+ nb_errors++;
+ } else if (ret < 0) {
+ fprintf(stderr,
+ "Internal error validating instance line %ld\n",
+ xmlGetLineNo(tmp));
+ nb_errors++;
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlResetLastError();
+ if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
+ fprintf(stderr, "Validation of instance line %ld leaked %d\n",
+ xmlGetLineNo(tmp), xmlMemUsed() - mem);
+ xmlMemoryDump();
+ nb_leaks++;
+ }
+ }
+ 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) {
+ fprintf(stderr,
+ "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) {
+ fprintf(stderr,
+ "Failed to detect invalid instance line %ld\n",
+ xmlGetLineNo(tmp));
+ nb_errors++;
+ } else if (ret < 0) {
+ fprintf(stderr,
+ "Internal error validating instance line %ld\n",
+ xmlGetLineNo(tmp));
+ nb_errors++;
+ }
+ xmlFreeDoc(doc);
+ }
+ xmlResetLastError();
+ if ((mem != xmlMemUsed()) && (extraMemoryFromResolver == 0)) {
+ fprintf(stderr, "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)) {
+ fprintf(stderr, "Validation of tests starting line %ld leaked %d\n",
+ xmlGetLineNo(cur), xmlMemUsed() - memt);
+ nb_leaks++;
+ }
+ return(ret);
+}
+
+static int
+xsdTestSuite(int verbose, 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(verbose, cur);
+ cur = getNext(cur, "following-sibling::testCase[1]");
+ }
+
+ return(0);
+}
+
+static int
+xsdTest(int verbose) {
+ 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(verbose, cur);
+ cur = getNext(cur, "following-sibling::testSuite[1]");
+ }
+
+done:
+ if (doc != NULL)
+ xmlFreeDoc(doc);
+ return(ret);
+}
+
+static int
+rngTestSuite(int verbose, 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(verbose, cur);
+ cur = getNext(cur, "following-sibling::testSuite[1]");
+ }
+
+ return(0);
+}
+
+static int
+rngTest1(int verbose) {
+ 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 1 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(verbose, cur);
+ cur = getNext(cur, "following-sibling::testSuite[1]");
+ }
+
+done:
+ if (doc != NULL)
+ xmlFreeDoc(doc);
+ return(ret);
+}
+
+/************************************************************************
+ * *
+ * Libxml2 specific routines *
+ * *
+ ************************************************************************/
+
+int
+main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
+ int res, ret = 0;
+ int verbose = 0;
+ int old_errors, old_tests, old_leaks;
+
+ initializeLibxml2();
+
+ if ((argc >= 2) && (!strcmp(argv[1], "-v")))
+ verbose = 1;
+
+
+ res = xsdTest(verbose);
+ 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;
+ res = rngTest1(verbose);
+ 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;
+
+ 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);
+ }
+ xmlCleanupParser();
+ xmlMemoryDump();
+
+ return(ret);
+}
diff --git a/runtest.c b/runtest.c
index 4f7aa5a..36f3d1a 100644
--- a/runtest.c
+++ b/runtest.c
@@ -36,6 +36,20 @@
#endif
#endif
+#ifdef LIBXML_SCHEMAS_ENABLED
+#include <libxml/relaxng.h>
+#include <libxml/xmlschemas.h>
+#include <libxml/xmlschemastypes.h>
+#endif
+
+#ifdef LIBXML_PATTERN_ENABLED
+#include <libxml/pattern.h>
+#endif
+
+#ifdef LIBXML_C14N_ENABLED
+#include <libxml/c14n.h>
+#endif
+
#ifdef LIBXML_HTML_ENABLED
#include <libxml/HTMLparser.h>
#include <libxml/HTMLtree.h>
@@ -46,6 +60,17 @@
#define XML_PARSE_HTML 1 << 24
#endif
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
+#include <libxml/globals.h>
+#include <libxml/threads.h>
+#include <libxml/parser.h>
+#include <libxml/catalog.h>
+#include <string.h>
+#if !defined(_MSC_VER)
+#include <unistd.h>
+#endif
+#endif
+
typedef int (*functest) (const char *filename, const char *result,
const char *error, int options);
@@ -68,6 +93,9 @@
* *
************************************************************************/
+static int nb_tests = 0;
+static int nb_errors = 0;
+static int nb_leaks = 0;
static long libxmlMemoryAllocatedBase = 0;
static int extraMemoryFromResolver = 0;
@@ -128,6 +156,266 @@
}
static void
+channel(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;
+}
+
+/**
+ * xmlParserPrintFileContext:
+ * @input: an xmlParserInputPtr input
+ *
+ * Displays current context within the input content for error tracking
+ */
+
+static void
+xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
+ xmlGenericErrorFunc chanl, void *data ) {
+ const xmlChar *cur, *base;
+ unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */
+ xmlChar content[81]; /* space for 80 chars + line terminator */
+ xmlChar *ctnt;
+
+ if (input == NULL) return;
+ cur = input->cur;
+ base = input->base;
+ /* skip backwards over any end-of-lines */
+ while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
+ cur--;
+ }
+ n = 0;
+ /* search backwards for beginning-of-line (to max buff size) */
+ while ((n++ < (sizeof(content)-1)) && (cur > base) &&
+ (*(cur) != '\n') && (*(cur) != '\r'))
+ cur--;
+ if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
+ /* calculate the error position in terms of the current position */
+ col = input->cur - cur;
+ /* search forward for end-of-line (to max buff size) */
+ n = 0;
+ ctnt = content;
+ /* copy selected text to our buffer */
+ while ((*cur != 0) && (*(cur) != '\n') &&
+ (*(cur) != '\r') && (n < sizeof(content)-1)) {
+ *ctnt++ = *cur++;
+ n++;
+ }
+ *ctnt = 0;
+ /* print out the selected text */
+ chanl(data ,"%s\n", content);
+ /* create blank line with problem pointer */
+ n = 0;
+ ctnt = content;
+ /* (leave buffer space for pointer + line terminator) */
+ while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
+ if (*(ctnt) != '\t')
+ *(ctnt) = ' ';
+ ctnt++;
+ }
+ *ctnt++ = '^';
+ *ctnt = 0;
+ chanl(data ,"%s\n", content);
+}
+
+static void
+testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) {
+ char *file = NULL;
+ int line = 0;
+ int code = -1;
+ int domain;
+ void *data = NULL;
+ const char *str;
+ const xmlChar *name = NULL;
+ xmlNodePtr node;
+ xmlErrorLevel level;
+ xmlParserInputPtr input = NULL;
+ xmlParserInputPtr cur = NULL;
+ xmlParserCtxtPtr ctxt = NULL;
+
+ if (err == NULL)
+ return;
+
+ file = err->file;
+ line = err->line;
+ code = err->code;
+ domain = err->domain;
+ level = err->level;
+ node = err->node;
+ if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
+ (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
+ (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
+ ctxt = err->ctxt;
+ }
+ str = err->message;
+
+ if (code == XML_ERR_OK)
+ return;
+
+ if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
+ name = node->name;
+
+ /*
+ * Maintain the compatibility with the legacy error handling
+ */
+ if (ctxt != NULL) {
+ input = ctxt->input;
+ if ((input != NULL) && (input->filename == NULL) &&
+ (ctxt->inputNr > 1)) {
+ cur = input;
+ input = ctxt->inputTab[ctxt->inputNr - 2];
+ }
+ if (input != NULL) {
+ if (input->filename)
+ channel(data, "%s:%d: ", input->filename, input->line);
+ else if ((line != 0) && (domain == XML_FROM_PARSER))
+ channel(data, "Entity: line %d: ", input->line);
+ }
+ } else {
+ if (file != NULL)
+ channel(data, "%s:%d: ", file, line);
+ else if ((line != 0) && (domain == XML_FROM_PARSER))
+ channel(data, "Entity: line %d: ", line);
+ }
+ if (name != NULL) {
+ channel(data, "element %s: ", name);
+ }
+ if (code == XML_ERR_OK)
+ return;
+ switch (domain) {
+ case XML_FROM_PARSER:
+ channel(data, "parser ");
+ break;
+ case XML_FROM_NAMESPACE:
+ channel(data, "namespace ");
+ break;
+ case XML_FROM_DTD:
+ case XML_FROM_VALID:
+ channel(data, "validity ");
+ break;
+ case XML_FROM_HTML:
+ channel(data, "HTML parser ");
+ break;
+ case XML_FROM_MEMORY:
+ channel(data, "memory ");
+ break;
+ case XML_FROM_OUTPUT:
+ channel(data, "output ");
+ break;
+ case XML_FROM_IO:
+ channel(data, "I/O ");
+ break;
+ case XML_FROM_XINCLUDE:
+ channel(data, "XInclude ");
+ break;
+ case XML_FROM_XPATH:
+ channel(data, "XPath ");
+ break;
+ case XML_FROM_XPOINTER:
+ channel(data, "parser ");
+ break;
+ case XML_FROM_REGEXP:
+ channel(data, "regexp ");
+ break;
+ case XML_FROM_MODULE:
+ channel(data, "module ");
+ break;
+ case XML_FROM_SCHEMASV:
+ channel(data, "Schemas validity ");
+ break;
+ case XML_FROM_SCHEMASP:
+ channel(data, "Schemas parser ");
+ break;
+ case XML_FROM_RELAXNGP:
+ channel(data, "Relax-NG parser ");
+ break;
+ case XML_FROM_RELAXNGV:
+ channel(data, "Relax-NG validity ");
+ break;
+ case XML_FROM_CATALOG:
+ channel(data, "Catalog ");
+ break;
+ case XML_FROM_C14N:
+ channel(data, "C14N ");
+ break;
+ case XML_FROM_XSLT:
+ channel(data, "XSLT ");
+ break;
+ default:
+ break;
+ }
+ if (code == XML_ERR_OK)
+ return;
+ switch (level) {
+ case XML_ERR_NONE:
+ channel(data, ": ");
+ break;
+ case XML_ERR_WARNING:
+ channel(data, "warning : ");
+ break;
+ case XML_ERR_ERROR:
+ channel(data, "error : ");
+ break;
+ case XML_ERR_FATAL:
+ channel(data, "error : ");
+ break;
+ }
+ if (code == XML_ERR_OK)
+ return;
+ if (str != NULL) {
+ int len;
+ len = xmlStrlen((const xmlChar *)str);
+ if ((len > 0) && (str[len - 1] != '\n'))
+ channel(data, "%s\n", str);
+ else
+ channel(data, "%s", str);
+ } else {
+ channel(data, "%s\n", "out of memory error");
+ }
+ if (code == XML_ERR_OK)
+ return;
+
+ if (ctxt != NULL) {
+ xmlParserPrintFileContextInternal(input, channel, data);
+ if (cur != NULL) {
+ if (cur->filename)
+ channel(data, "%s:%d: \n", cur->filename, cur->line);
+ else if ((line != 0) && (domain == XML_FROM_PARSER))
+ channel(data, "Entity: line %d: \n", cur->line);
+ xmlParserPrintFileContextInternal(cur, channel, data);
+ }
+ }
+ if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
+ (err->int1 < 100) &&
+ (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
+ xmlChar buf[150];
+ int i;
+
+ channel(data, "%s\n", err->str1);
+ for (i=0;i < err->int1;i++)
+ buf[i] = ' ';
+ buf[i++] = '^';
+ buf[i] = 0;
+ channel(data, "%s\n", buf);
+ }
+}
+
+static void
initializeLibxml2(void) {
xmlGetWarningsDefaultValue = 0;
xmlPedanticParserDefault(0);
@@ -135,7 +423,11 @@
xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
xmlInitParser();
xmlSetExternalEntityLoader(testExternalEntityLoader);
- xmlSetGenericErrorFunc(NULL, testErrorHandler);
+ xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
+#ifdef LIBXML_SCHEMAS_ENABLED
+ xmlSchemaInitTypes();
+ xmlRelaxNGInitTypes();
+#endif
libxmlMemoryAllocatedBase = xmlMemUsed();
}
@@ -1226,6 +1518,7 @@
int ret;
char *temp;
+ nb_tests++;
temp = resultFilename(filename, "", ".res");
if (temp == NULL) {
fprintf(stderr, "out of memory\n");
@@ -1238,6 +1531,10 @@
return(-1);
}
+ /* for SAX we really want the callbacks though the context handlers */
+ xmlSetStructuredErrorFunc(NULL, NULL);
+ xmlSetGenericErrorFunc(NULL, testErrorHandler);
+
#ifdef LIBXML_HTML_ENABLED
if (options & XML_PARSE_HTML) {
htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL);
@@ -1270,11 +1567,16 @@
}
fclose(SAXdebug);
if (compareFiles(temp, result)) {
+ fprintf(stderr, "Got a difference for %s\n", filename);
ret = 1;
- }
+ } else
unlink(temp);
free(temp);
+ /* switch back to structured error handling */
+ xmlSetGenericErrorFunc(NULL, NULL);
+ xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
+
return(ret);
}
@@ -1303,6 +1605,7 @@
char *temp;
int res = 0;
+ nb_tests++;
/*
* base of the test, parse with the old API
*/
@@ -1359,6 +1662,7 @@
int size, res;
int cur = 0;
+ nb_tests++;
/*
* load the document in memory and work from there.
*/
@@ -1455,6 +1759,7 @@
const char *base;
int size, res;
+ nb_tests++;
/*
* load and parse the memory
*/
@@ -1501,6 +1806,7 @@
char *temp;
int res = 0;
+ nb_tests++;
/*
* base of the test, parse with the old API
*/
@@ -1552,6 +1858,7 @@
const char *base = NULL;
int size, res = 0;
+ nb_tests++;
#ifdef LIBXML_HTML_ENABLED
if (options & XML_PARSE_HTML) {
doc = htmlReadFile(filename, NULL, options);
@@ -1637,65 +1944,10 @@
else {
fprintf(out, " %s\n", value);
}
-#if 0
-#ifdef LIBXML_PATTERN_ENABLED
- if (patternc) {
- xmlChar *path = NULL;
- int match = -1;
-
- if (type == XML_READER_TYPE_ELEMENT) {
- /* do the check only on element start */
- match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
-
- if (match) {
- path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
- fprintf(out, "Node %s matches pattern %s\n", path, pattern);
- }
- }
- if (patstream != NULL) {
- int ret;
-
- if (type == XML_READER_TYPE_ELEMENT) {
- ret = xmlStreamPush(patstream,
- xmlTextReaderConstLocalName(reader),
- xmlTextReaderConstNamespaceUri(reader));
- if (ret < 0) {
- fprintf(stderr, "xmlStreamPush() failure\n");
- xmlFreeStreamCtxt(patstream);
- patstream = NULL;
- } else if (ret != match) {
- if (path == NULL) {
- path = xmlGetNodePath(
- xmlTextReaderCurrentNode(reader));
- }
- fprintf(stderr,
- "xmlPatternMatch and xmlStreamPush disagree\n");
- fprintf(stderr,
- " pattern %s node %s\n",
- pattern, path);
- }
-
-
- }
- if ((type == XML_READER_TYPE_END_ELEMENT) ||
- ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
- ret = xmlStreamPop(patstream);
- if (ret < 0) {
- fprintf(stderr, "xmlStreamPop() failure\n");
- xmlFreeStreamCtxt(patstream);
- patstream = NULL;
- }
- }
- }
- if (path != NULL)
- xmlFree(path);
- }
-#endif
-#endif
}
static int
streamProcessTest(const char *filename, const char *result, const char *err,
- xmlTextReaderPtr reader) {
+ xmlTextReaderPtr reader, const char *rng) {
int ret;
char *temp = NULL;
FILE *t = NULL;
@@ -1703,6 +1955,7 @@
if (reader == NULL)
return(-1);
+ nb_tests++;
if (result != NULL) {
temp = resultFilename(filename, "", ".res");
if (temp == NULL) {
@@ -1716,16 +1969,34 @@
return(-1);
}
}
+ if (rng != NULL) {
+ ret = xmlTextReaderRelaxNGValidate(reader, rng);
+ if (ret < 0) {
+ testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n",
+ rng);
+ fclose(t);
+ unlink(temp);
+ free(temp);
+ return(0);
+ }
+ }
xmlGetWarningsDefaultValue = 1;
ret = xmlTextReaderRead(reader);
while (ret == 1) {
- if (t != NULL)
+ if ((t != NULL) && (rng == NULL))
processNode(t, reader);
ret = xmlTextReaderRead(reader);
}
if (ret != 0) {
testErrorHandler(NULL, "%s : failed to parse\n", filename);
}
+ if (rng != NULL) {
+ if (xmlTextReaderIsValid(reader) != 1) {
+ testErrorHandler(NULL, "%s fails to validate\n", filename);
+ } else {
+ testErrorHandler(NULL, "%s validates\n", filename);
+ }
+ }
xmlGetWarningsDefaultValue = 0;
if (t != NULL) {
fclose(t);
@@ -1741,6 +2012,7 @@
ret = compareFileMem(err, testErrors, testErrorsSize);
if (ret != 0) {
fprintf(stderr, "Error for %s failed\n", filename);
+ printf("%s", testErrors);
return(-1);
}
}
@@ -1765,7 +2037,7 @@
int ret;
reader = xmlReaderForFile(filename, NULL, options);
- ret = streamProcessTest(filename, result, err, reader);
+ ret = streamProcessTest(filename, result, err, reader, NULL);
xmlFreeTextReader(reader);
return(ret);
}
@@ -1793,7 +2065,7 @@
return(-1);
}
reader = xmlReaderWalker(doc);
- ret = streamProcessTest(filename, result, err, reader);
+ ret = streamProcessTest(filename, result, err, reader, NULL);
xmlFreeTextReader(reader);
xmlFreeDoc(doc);
return(ret);
@@ -1825,7 +2097,7 @@
return(-1);
}
reader = xmlReaderForMemory(base, size, filename, NULL, options);
- ret = streamProcessTest(filename, result, err, reader);
+ ret = streamProcessTest(filename, result, err, reader, NULL);
free((char *)base);
xmlFreeTextReader(reader);
return(ret);
@@ -1847,6 +2119,7 @@
xmlXPathObjectPtr res;
xmlXPathContextPtr ctxt;
+ nb_tests++;
#if defined(LIBXML_XPTR_ENABLED)
if (xptr) {
ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL);
@@ -2217,6 +2490,7 @@
i--;
str[i] = 0;
}
+ nb_tests++;
handleURI(str, base, o);
}
@@ -2281,6 +2555,1226 @@
"http://foo.com/path/to/index.html?orig#help"));
}
+#ifdef LIBXML_SCHEMAS_ENABLED
+/************************************************************************
+ * *
+ * Schemas tests *
+ * *
+ ************************************************************************/
+static int
+schemasOneTest(const char *sch,
+ const char *filename,
+ const char *result,
+ const char *err,
+ int options,
+ xmlSchemaPtr schemas) {
+ xmlDocPtr doc;
+ xmlSchemaValidCtxtPtr ctxt;
+ int ret = 0;
+ char *temp;
+ FILE *schemasOutput;
+
+ doc = xmlReadFile(filename, NULL, options);
+ if (doc == NULL) {
+ fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
+ return(-1);
+ }
+
+ temp = resultFilename(result, "", ".res");
+ if (temp == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ fatalError();
+ }
+ schemasOutput = fopen(temp, "w");
+ if (schemasOutput == NULL) {
+ fprintf(stderr, "failed to open output file %s\n", temp);
+ xmlFreeDoc(doc);
+ free(temp);
+ return(-1);
+ }
+
+ ctxt = xmlSchemaNewValidCtxt(schemas);
+ xmlSchemaSetValidErrors(ctxt,
+ (xmlSchemaValidityErrorFunc) testErrorHandler,
+ (xmlSchemaValidityWarningFunc) testErrorHandler,
+ ctxt);
+ ret = xmlSchemaValidateDoc(ctxt, doc);
+ if (ret == 0) {
+ fprintf(schemasOutput, "%s validates\n", filename);
+ } else if (ret > 0) {
+ fprintf(schemasOutput, "%s fails to validate\n", filename);
+ } else {
+ fprintf(schemasOutput, "%s validation generated an internal error\n",
+ filename);
+ }
+ fclose(schemasOutput);
+ if (result) {
+ if (compareFiles(temp, result)) {
+ fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
+ ret = 1;
+ }
+ }
+ unlink(temp);
+ free(temp);
+
+ if (err != NULL) {
+ if (compareFileMem(err, testErrors, testErrorsSize)) {
+ fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
+ ret = 1;
+ }
+ }
+
+
+ xmlSchemaFreeValidCtxt(ctxt);
+ xmlFreeDoc(doc);
+ return(ret);
+}
+/**
+ * schemasTest:
+ * @filename: the schemas file
+ * @result: the file with expected result
+ * @err: the file with error messages
+ *
+ * Parse a file containing URI, compose them against a fixed base and
+ * check for errors
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+schemasTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *errr ATTRIBUTE_UNUSED,
+ int options) {
+ const char *base = baseFilename(filename);
+ const char *base2;
+ const char *instance;
+ xmlSchemaParserCtxtPtr ctxt;
+ xmlSchemaPtr schemas;
+ int res = 0, len, ret;
+ char pattern[500];
+ char prefix[500];
+ char result[500];
+ char err[500];
+ glob_t globbuf;
+ size_t i;
+ char count = 0;
+
+ /* first compile the schemas if possible */
+ ctxt = xmlSchemaNewParserCtxt(filename);
+ xmlSchemaSetParserErrors(ctxt,
+ (xmlSchemaValidityErrorFunc) testErrorHandler,
+ (xmlSchemaValidityWarningFunc) testErrorHandler,
+ ctxt);
+ schemas = xmlSchemaParse(ctxt);
+ xmlSchemaFreeParserCtxt(ctxt);
+
+ /*
+ * most of the mess is about the output filenames generated by the Makefile
+ */
+ len = strlen(base);
+ if ((len > 499) || (len < 5)) {
+ xmlSchemaFree(schemas);
+ return(-1);
+ }
+ len -= 4; /* remove trailing .xsd */
+ if (base[len - 2] == '_') {
+ len -= 2; /* remove subtest number */
+ }
+ if (base[len - 2] == '_') {
+ len -= 2; /* remove subtest number */
+ }
+ memcpy(prefix, base, len);
+ prefix[len] = 0;
+
+ snprintf(pattern, 499, "./test/schemas/%s_?.xml", prefix);
+ pattern[499] = 0;
+
+ if (base[len] == '_') {
+ len += 2;
+ memcpy(prefix, base, len);
+ prefix[len] = 0;
+ }
+
+ globbuf.gl_offs = 0;
+ glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
+ for (i = 0;i < globbuf.gl_pathc;i++) {
+ testErrorsSize = 0;
+ testErrors[0] = 0;
+ instance = globbuf.gl_pathv[i];
+ base2 = baseFilename(instance);
+ len = strlen(base2);
+ if ((len > 6) && (base2[len - 6] == '_')) {
+ count = base2[len - 5];
+ snprintf(result, 499, "result/schemas/%s_%c",
+ prefix, count);
+ result[499] = 0;
+ snprintf(err, 499, "result/schemas/%s_%c.err",
+ prefix, count);
+ err[499] = 0;
+ } else {
+ fprintf(stderr, "don't know how to process %s\n", instance);
+ continue;
+ }
+ if (schemas == NULL) {
+ } else {
+ nb_tests++;
+ ret = schemasOneTest(filename, instance, result, err,
+ options, schemas);
+ if (res != 0)
+ ret = res;
+ }
+ }
+ globfree(&globbuf);
+ xmlSchemaFree(schemas);
+
+ return(res);
+}
+
+/************************************************************************
+ * *
+ * Schemas tests *
+ * *
+ ************************************************************************/
+static int
+rngOneTest(const char *sch,
+ const char *filename,
+ const char *result,
+ const char *err,
+ int options,
+ xmlRelaxNGPtr schemas) {
+ xmlDocPtr doc;
+ xmlRelaxNGValidCtxtPtr ctxt;
+ int ret = 0;
+ char *temp;
+ FILE *schemasOutput;
+
+ doc = xmlReadFile(filename, NULL, options);
+ if (doc == NULL) {
+ fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch);
+ return(-1);
+ }
+
+ temp = resultFilename(result, "", ".res");
+ if (temp == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ fatalError();
+ }
+ schemasOutput = fopen(temp, "w");
+ if (schemasOutput == NULL) {
+ fprintf(stderr, "failed to open output file %s\n", temp);
+ xmlFreeDoc(doc);
+ free(temp);
+ return(-1);
+ }
+
+ ctxt = xmlRelaxNGNewValidCtxt(schemas);
+ xmlRelaxNGSetValidErrors(ctxt,
+ (xmlRelaxNGValidityErrorFunc) testErrorHandler,
+ (xmlRelaxNGValidityWarningFunc) testErrorHandler,
+ ctxt);
+ ret = xmlRelaxNGValidateDoc(ctxt, doc);
+ if (ret == 0) {
+ testErrorHandler(NULL, "%s validates\n", filename);
+ } else if (ret > 0) {
+ testErrorHandler(NULL, "%s fails to validate\n", filename);
+ } else {
+ testErrorHandler(NULL, "%s validation generated an internal error\n",
+ filename);
+ }
+ fclose(schemasOutput);
+ if (result) {
+ if (compareFiles(temp, result)) {
+ fprintf(stderr, "Result for %s on %s failed\n", filename, sch);
+ ret = 1;
+ }
+ }
+ unlink(temp);
+ free(temp);
+
+ if (err != NULL) {
+ if (compareFileMem(err, testErrors, testErrorsSize)) {
+ fprintf(stderr, "Error for %s on %s failed\n", filename, sch);
+ ret = 1;
+ printf("%s", testErrors);
+ }
+ }
+
+
+ xmlRelaxNGFreeValidCtxt(ctxt);
+ xmlFreeDoc(doc);
+ return(ret);
+}
+/**
+ * rngTest:
+ * @filename: the schemas file
+ * @result: the file with expected result
+ * @err: the file with error messages
+ *
+ * Parse an RNG schemas and then apply it to the related .xml
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+rngTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *errr ATTRIBUTE_UNUSED,
+ int options) {
+ const char *base = baseFilename(filename);
+ const char *base2;
+ const char *instance;
+ xmlRelaxNGParserCtxtPtr ctxt;
+ xmlRelaxNGPtr schemas;
+ int res = 0, len, ret;
+ char pattern[500];
+ char prefix[500];
+ char result[500];
+ char err[500];
+ glob_t globbuf;
+ size_t i;
+ char count = 0;
+
+ /* first compile the schemas if possible */
+ ctxt = xmlRelaxNGNewParserCtxt(filename);
+ xmlRelaxNGSetParserErrors(ctxt,
+ (xmlRelaxNGValidityErrorFunc) testErrorHandler,
+ (xmlRelaxNGValidityWarningFunc) testErrorHandler,
+ ctxt);
+ schemas = xmlRelaxNGParse(ctxt);
+ xmlRelaxNGFreeParserCtxt(ctxt);
+
+ /*
+ * most of the mess is about the output filenames generated by the Makefile
+ */
+ len = strlen(base);
+ if ((len > 499) || (len < 5)) {
+ xmlRelaxNGFree(schemas);
+ return(-1);
+ }
+ len -= 4; /* remove trailing .rng */
+ memcpy(prefix, base, len);
+ prefix[len] = 0;
+
+ snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
+ pattern[499] = 0;
+
+ globbuf.gl_offs = 0;
+ glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
+ for (i = 0;i < globbuf.gl_pathc;i++) {
+ testErrorsSize = 0;
+ testErrors[0] = 0;
+ instance = globbuf.gl_pathv[i];
+ base2 = baseFilename(instance);
+ len = strlen(base2);
+ if ((len > 6) && (base2[len - 6] == '_')) {
+ count = base2[len - 5];
+ snprintf(result, 499, "result/relaxng/%s_%c",
+ prefix, count);
+ result[499] = 0;
+ snprintf(err, 499, "result/relaxng/%s_%c.err",
+ prefix, count);
+ err[499] = 0;
+ } else {
+ fprintf(stderr, "don't know how to process %s\n", instance);
+ continue;
+ }
+ if (schemas == NULL) {
+ } else {
+ nb_tests++;
+ ret = rngOneTest(filename, instance, result, err,
+ options, schemas);
+ if (res != 0)
+ ret = res;
+ }
+ }
+ globfree(&globbuf);
+ xmlRelaxNGFree(schemas);
+
+ return(res);
+}
+
+/**
+ * rngStreamTest:
+ * @filename: the schemas file
+ * @result: the file with expected result
+ * @err: the file with error messages
+ *
+ * Parse a set of files with streaming, applying an RNG schemas
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+rngStreamTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *errr ATTRIBUTE_UNUSED,
+ int options) {
+ const char *base = baseFilename(filename);
+ const char *base2;
+ const char *instance;
+ int res = 0, len, ret;
+ char pattern[500];
+ char prefix[500];
+ char result[500];
+ char err[500];
+ glob_t globbuf;
+ size_t i;
+ char count = 0;
+ xmlTextReaderPtr reader;
+ int disable_err = 0;
+
+ /*
+ * most of the mess is about the output filenames generated by the Makefile
+ */
+ len = strlen(base);
+ if ((len > 499) || (len < 5)) {
+ fprintf(stderr, "len(base) == %d !\n", len);
+ return(-1);
+ }
+ len -= 4; /* remove trailing .rng */
+ memcpy(prefix, base, len);
+ prefix[len] = 0;
+
+ /*
+ * strictly unifying the error messages is nearly impossible this
+ * hack is also done in the Makefile
+ */
+ if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) ||
+ (!strcmp(prefix, "tutor3_2")))
+ disable_err = 1;
+
+ snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix);
+ pattern[499] = 0;
+
+ globbuf.gl_offs = 0;
+ glob(pattern, GLOB_DOOFFS, NULL, &globbuf);
+ for (i = 0;i < globbuf.gl_pathc;i++) {
+ testErrorsSize = 0;
+ testErrors[0] = 0;
+ instance = globbuf.gl_pathv[i];
+ base2 = baseFilename(instance);
+ len = strlen(base2);
+ if ((len > 6) && (base2[len - 6] == '_')) {
+ count = base2[len - 5];
+ snprintf(result, 499, "result/relaxng/%s_%c",
+ prefix, count);
+ result[499] = 0;
+ snprintf(err, 499, "result/relaxng/%s_%c.err",
+ prefix, count);
+ err[499] = 0;
+ } else {
+ fprintf(stderr, "don't know how to process %s\n", instance);
+ continue;
+ }
+ reader = xmlReaderForFile(instance, NULL, options);
+ if (reader == NULL) {
+ fprintf(stderr, "Failed to build reder for %s\n", instance);
+ }
+ if (disable_err == 1)
+ ret = streamProcessTest(instance, result, NULL, reader, filename);
+ else
+ ret = streamProcessTest(instance, result, err, reader, filename);
+ xmlFreeTextReader(reader);
+ if (ret != 0) {
+ fprintf(stderr, "instance %s failed\n", instance);
+ res = ret;
+ }
+ }
+ globfree(&globbuf);
+
+ return(res);
+}
+
+
+#endif
+
+#ifdef LIBXML_PATTERN_ENABLED
+/************************************************************************
+ * *
+ * Patterns tests *
+ * *
+ ************************************************************************/
+static void patternNode(FILE *out, xmlTextReaderPtr reader,
+ const char *pattern, xmlPatternPtr patternc,
+ xmlStreamCtxtPtr patstream) {
+ xmlChar *path = NULL;
+ int match = -1;
+ int type, empty;
+
+ type = xmlTextReaderNodeType(reader);
+ empty = xmlTextReaderIsEmptyElement(reader);
+
+ if (type == XML_READER_TYPE_ELEMENT) {
+ /* do the check only on element start */
+ match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
+
+ if (match) {
+ path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
+ fprintf(out, "Node %s matches pattern %s\n", path, pattern);
+ }
+ }
+ if (patstream != NULL) {
+ int ret;
+
+ if (type == XML_READER_TYPE_ELEMENT) {
+ ret = xmlStreamPush(patstream,
+ xmlTextReaderConstLocalName(reader),
+ xmlTextReaderConstNamespaceUri(reader));
+ if (ret < 0) {
+ fprintf(out, "xmlStreamPush() failure\n");
+ xmlFreeStreamCtxt(patstream);
+ patstream = NULL;
+ } else if (ret != match) {
+ if (path == NULL) {
+ path = xmlGetNodePath(
+ xmlTextReaderCurrentNode(reader));
+ }
+ fprintf(out,
+ "xmlPatternMatch and xmlStreamPush disagree\n");
+ fprintf(out,
+ " pattern %s node %s\n",
+ pattern, path);
+ }
+
+
+ }
+ if ((type == XML_READER_TYPE_END_ELEMENT) ||
+ ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
+ ret = xmlStreamPop(patstream);
+ if (ret < 0) {
+ fprintf(out, "xmlStreamPop() failure\n");
+ xmlFreeStreamCtxt(patstream);
+ patstream = NULL;
+ }
+ }
+ }
+ if (path != NULL)
+ xmlFree(path);
+}
+
+/**
+ * patternTest:
+ * @filename: the schemas file
+ * @result: the file with expected result
+ * @err: the file with error messages
+ *
+ * Parse a set of files with streaming, applying an RNG schemas
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+patternTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *err ATTRIBUTE_UNUSED,
+ int options) {
+ xmlPatternPtr patternc = NULL;
+ xmlStreamCtxtPtr patstream = NULL;
+ FILE *o, *f;
+ char str[1024];
+ char xml[500];
+ char result[500];
+ int len, i;
+ int ret = 0, res;
+ char *temp;
+ xmlTextReaderPtr reader;
+ xmlDocPtr doc;
+
+ len = strlen(filename);
+ len -= 4;
+ memcpy(xml, filename, len);
+ xml[len] = 0;
+ snprintf(result, 499, "result/pattern/%s", baseFilename(xml));
+ result[499] = 0;
+ memcpy(xml + len, ".xml", 5);
+
+ if (!checkTestFile(xml)) {
+ fprintf(stderr, "Missing xml file %s\n", xml);
+ return(-1);
+ }
+ if (!checkTestFile(result)) {
+ fprintf(stderr, "Missing result file %s\n", result);
+ return(-1);
+ }
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ fprintf(stderr, "Failed to open %s\n", filename);
+ return(-1);
+ }
+ temp = resultFilename(filename, "", ".res");
+ if (temp == NULL) {
+ fprintf(stderr, "Out of memory\n");
+ fatalError();
+ }
+ o = fopen(temp, "w");
+ if (o == NULL) {
+ fprintf(stderr, "failed to open output file %s\n", temp);
+ fclose(f);
+ free(temp);
+ return(-1);
+ }
+ while (1) {
+ /*
+ * read one line in string buffer.
+ */
+ if (fgets (&str[0], sizeof (str) - 1, f) == NULL)
+ break;
+
+ /*
+ * remove the ending spaces
+ */
+ i = strlen(str);
+ while ((i > 0) &&
+ ((str[i - 1] == '\n') || (str[i - 1] == '\r') ||
+ (str[i - 1] == ' ') || (str[i - 1] == '\t'))) {
+ i--;
+ str[i] = 0;
+ }
+ doc = xmlReadFile(xml, NULL, options);
+ if (doc == NULL) {
+ fprintf(stderr, "Failed to parse %s\n", xml);
+ ret = 1;
+ } else {
+ xmlNodePtr root;
+ const xmlChar *namespaces[22];
+ int j;
+ xmlNsPtr ns;
+
+ root = xmlDocGetRootElement(doc);
+ for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) {
+ namespaces[j++] = ns->href;
+ namespaces[j++] = ns->prefix;
+ }
+ namespaces[j++] = NULL;
+ namespaces[j++] = NULL;
+
+ patternc = xmlPatterncompile((const xmlChar *) str, doc->dict,
+ 0, &namespaces[0]);
+ if (patternc == NULL) {
+ testErrorHandler(NULL,
+ "Pattern %s failed to compile\n", str);
+ xmlFreeDoc(doc);
+ ret = 1;
+ continue;
+ }
+ patstream = xmlPatternGetStreamCtxt(patternc);
+ if (patstream != NULL) {
+ ret = xmlStreamPush(patstream, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "xmlStreamPush() failure\n");
+ xmlFreeStreamCtxt(patstream);
+ patstream = NULL;
+ }
+ }
+ nb_tests++;
+
+ reader = xmlReaderWalker(doc);
+ res = xmlTextReaderRead(reader);
+ while (res == 1) {
+ patternNode(o, reader, str, patternc, patstream);
+ res = xmlTextReaderRead(reader);
+ }
+ if (res != 0) {
+ fprintf(o, "%s : failed to parse\n", filename);
+ }
+ xmlFreeTextReader(reader);
+ xmlFreeDoc(doc);
+ xmlFreeStreamCtxt(patstream);
+ patstream = NULL;
+ xmlFreePattern(patternc);
+
+ }
+ }
+
+ fclose(f);
+ fclose(o);
+
+ ret = compareFiles(temp, result);
+ if (ret) {
+ fprintf(stderr, "Result for %s failed\n", filename);
+ ret = 1;
+ }
+ unlink(temp);
+ free(temp);
+ return(ret);
+}
+#endif
+#ifdef LIBXML_C14N_ENABLED
+/************************************************************************
+ * *
+ * Canonicalization tests *
+ * *
+ ************************************************************************/
+static xmlXPathObjectPtr
+load_xpath_expr (xmlDocPtr parent_doc, const char* filename) {
+ xmlXPathObjectPtr xpath;
+ xmlDocPtr doc;
+ xmlChar *expr;
+ xmlXPathContextPtr ctx;
+ xmlNodePtr node;
+ xmlNsPtr ns;
+
+ /*
+ * load XPath expr as a file
+ */
+ xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ xmlSubstituteEntitiesDefault(1);
+
+ doc = xmlParseFile(filename);
+ if (doc == NULL) {
+ fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename);
+ return(NULL);
+ }
+
+ /*
+ * Check the document is of the right kind
+ */
+ if(xmlDocGetRootElement(doc) == NULL) {
+ fprintf(stderr,"Error: empty document for file \"%s\"\n", filename);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ node = doc->children;
+ while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) {
+ node = node->next;
+ }
+
+ if(node == NULL) {
+ fprintf(stderr,"Error: XPath element expected in the file \"%s\"\n", filename);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ expr = xmlNodeGetContent(node);
+ if(expr == NULL) {
+ fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ ctx = xmlXPathNewContext(parent_doc);
+ if(ctx == NULL) {
+ fprintf(stderr,"Error: unable to create new context\n");
+ xmlFree(expr);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ /*
+ * Register namespaces
+ */
+ ns = node->nsDef;
+ while(ns != NULL) {
+ if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) {
+ fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href);
+ xmlFree(expr);
+ xmlXPathFreeContext(ctx);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+ ns = ns->next;
+ }
+
+ /*
+ * Evaluate xpath
+ */
+ xpath = xmlXPathEvalExpression(expr, ctx);
+ if(xpath == NULL) {
+ fprintf(stderr,"Error: unable to evaluate xpath expression\n");
+ xmlFree(expr);
+ xmlXPathFreeContext(ctx);
+ xmlFreeDoc(doc);
+ return(NULL);
+ }
+
+ /* print_xpath_nodes(xpath->nodesetval); */
+
+ xmlFree(expr);
+ xmlXPathFreeContext(ctx);
+ xmlFreeDoc(doc);
+ return(xpath);
+}
+
+/*
+ * Macro used to grow the current buffer.
+ */
+#define xxx_growBufferReentrant() { \
+ buffer_size *= 2; \
+ buffer = (xmlChar **) \
+ xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \
+ if (buffer == NULL) { \
+ perror("realloc failed"); \
+ return(NULL); \
+ } \
+}
+
+static xmlChar **
+parse_list(xmlChar *str) {
+ xmlChar **buffer;
+ xmlChar **out = NULL;
+ int buffer_size = 0;
+ int len;
+
+ if(str == NULL) {
+ return(NULL);
+ }
+
+ len = xmlStrlen(str);
+ if((str[0] == '\'') && (str[len - 1] == '\'')) {
+ str[len - 1] = '\0';
+ str++;
+ len -= 2;
+ }
+ /*
+ * allocate an translation buffer.
+ */
+ buffer_size = 1000;
+ buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*));
+ if (buffer == NULL) {
+ perror("malloc failed");
+ return(NULL);
+ }
+ out = buffer;
+
+ while(*str != '\0') {
+ if (out - buffer > buffer_size - 10) {
+ int indx = out - buffer;
+
+ xxx_growBufferReentrant();
+ out = &buffer[indx];
+ }
+ (*out++) = str;
+ while(*str != ',' && *str != '\0') ++str;
+ if(*str == ',') *(str++) = '\0';
+ }
+ (*out) = NULL;
+ return buffer;
+}
+
+static int
+c14nRunTest(const char* xml_filename, int with_comments, int exclusive,
+ const char* xpath_filename, const char *ns_filename,
+ const char* result_file) {
+ xmlDocPtr doc;
+ xmlXPathObjectPtr xpath = NULL;
+ xmlChar *result = NULL;
+ int ret;
+ xmlChar **inclusive_namespaces = NULL;
+ const char *nslist = NULL;
+ int nssize;
+
+
+ /*
+ * build an XML tree from a the file; we need to add default
+ * attributes and resolve all character and entities references
+ */
+ xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;
+ xmlSubstituteEntitiesDefault(1);
+
+ doc = xmlParseFile(xml_filename);
+ if (doc == NULL) {
+ fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename);
+ return(-1);
+ }
+
+ /*
+ * Check the document is of the right kind
+ */
+ if(xmlDocGetRootElement(doc) == NULL) {
+ fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename);
+ xmlFreeDoc(doc);
+ return(-1);
+ }
+
+ /*
+ * load xpath file if specified
+ */
+ if(xpath_filename) {
+ xpath = load_xpath_expr(doc, xpath_filename);
+ if(xpath == NULL) {
+ fprintf(stderr,"Error: unable to evaluate xpath expression\n");
+ xmlFreeDoc(doc);
+ return(-1);
+ }
+ }
+
+ if (ns_filename != NULL) {
+ if (loadMem(ns_filename, &nslist, &nssize)) {
+ fprintf(stderr,"Error: unable to evaluate xpath expression\n");
+ if(xpath != NULL) xmlXPathFreeObject(xpath);
+ xmlFreeDoc(doc);
+ return(-1);
+ }
+ inclusive_namespaces = parse_list((xmlChar *) nslist);
+ }
+
+ /*
+ * Canonical form
+ */
+ /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */
+ ret = xmlC14NDocDumpMemory(doc,
+ (xpath) ? xpath->nodesetval : NULL,
+ exclusive, inclusive_namespaces,
+ with_comments, &result);
+ if (ret >= 0) {
+ if(result != NULL) {
+ if (compareFileMem(result_file, (const char *) result, ret)) {
+ fprintf(stderr, "Result mismatch for %s\n", xml_filename);
+ ret = -1;
+ }
+ }
+ } else {
+ fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret);
+ ret = -1;
+ }
+
+ /*
+ * Cleanup
+ */
+ if (result != NULL) xmlFree(result);
+ if(xpath != NULL) xmlXPathFreeObject(xpath);
+ if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces);
+ if (nslist != NULL) free((char *) nslist);
+ xmlFreeDoc(doc);
+
+ return(ret);
+}
+
+static int
+c14nCommonTest(const char *filename, int with_comments, int exclusive,
+ const char *subdir) {
+ char buf[500];
+ char prefix[500];
+ const char *base;
+ int len;
+ char *result = NULL;
+ char *xpath = NULL;
+ char *ns = NULL;
+ int ret = 0;
+
+ base = baseFilename(filename);
+ len = strlen(base);
+ len -= 4;
+ memcpy(prefix, base, len);
+ prefix[len] = 0;
+
+ snprintf(buf, 499, "result/c14n/%s/%s", subdir,prefix);
+ if (!checkTestFile(buf)) {
+ fprintf(stderr, "Missing result file %s", buf);
+ return(-1);
+ }
+ result = strdup(buf);
+ snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir,prefix);
+ if (checkTestFile(buf)) {
+ xpath = strdup(buf);
+ }
+ snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir,prefix);
+ if (checkTestFile(buf)) {
+ ns = strdup(buf);
+ }
+
+ nb_tests++;
+ if (c14nRunTest(filename, with_comments, exclusive,
+ xpath, ns, result) < 0)
+ ret = 1;
+
+ if (result != NULL) free(result);
+ if (xpath != NULL) free(xpath);
+ if (ns != NULL) free(ns);
+ return(ret);
+}
+
+static int
+c14nWithCommentTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *err ATTRIBUTE_UNUSED,
+ int options ATTRIBUTE_UNUSED) {
+ return(c14nCommonTest(filename, 1, 0, "with-comments"));
+}
+static int
+c14nWithoutCommentTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *err ATTRIBUTE_UNUSED,
+ int options ATTRIBUTE_UNUSED) {
+ return(c14nCommonTest(filename, 0, 0, "without-comments"));
+}
+static int
+c14nExcWithoutCommentTest(const char *filename,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *err ATTRIBUTE_UNUSED,
+ int options ATTRIBUTE_UNUSED) {
+ return(c14nCommonTest(filename, 0, 1, "exc-without-comments"));
+}
+#endif
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
+/************************************************************************
+ * *
+ * Catalog and threads test *
+ * *
+ ************************************************************************/
+
+/*
+ * mostly a cut and paste from testThreads.c
+ */
+#define MAX_ARGC 20
+
+static const char *catalog = "test/threads/complex.xml";
+static const char *testfiles[] = {
+ "test/threads/abc.xml",
+ "test/threads/acb.xml",
+ "test/threads/bac.xml",
+ "test/threads/bca.xml",
+ "test/threads/cab.xml",
+ "test/threads/cba.xml",
+ "test/threads/invalid.xml",
+};
+
+const char *Okay = "OK";
+const char *Failed = "Failed";
+
+#ifndef xmlDoValidityCheckingDefaultValue
+#error xmlDoValidityCheckingDefaultValue is not a macro
+#endif
+#ifndef xmlGenericErrorContext
+#error xmlGenericErrorContext is not a macro
+#endif
+
+static void *
+thread_specific_data(void *private_data)
+{
+ xmlDocPtr myDoc;
+ const char *filename = (const char *) private_data;
+ int okay = 1;
+
+ if (!strcmp(filename, "test/threads/invalid.xml")) {
+ xmlDoValidityCheckingDefaultValue = 0;
+ xmlGenericErrorContext = stdout;
+ } else {
+ xmlDoValidityCheckingDefaultValue = 1;
+ xmlGenericErrorContext = stderr;
+ }
+ myDoc = xmlParseFile(filename);
+ if (myDoc) {
+ xmlFreeDoc(myDoc);
+ } else {
+ printf("parse failed\n");
+ okay = 0;
+ }
+ if (!strcmp(filename, "test/threads/invalid.xml")) {
+ if (xmlDoValidityCheckingDefaultValue != 0) {
+ printf("ValidityCheckingDefaultValue override failed\n");
+ okay = 0;
+ }
+ if (xmlGenericErrorContext != stdout) {
+ printf("xmlGenericErrorContext override failed\n");
+ okay = 0;
+ }
+ } else {
+ if (xmlDoValidityCheckingDefaultValue != 1) {
+ printf("ValidityCheckingDefaultValue override failed\n");
+ okay = 0;
+ }
+ if (xmlGenericErrorContext != stderr) {
+ printf("xmlGenericErrorContext override failed\n");
+ okay = 0;
+ }
+ }
+ if (okay == 0)
+ return ((void *) Failed);
+ return ((void *) Okay);
+}
+
+#if defined(linux) || defined(solaris)
+
+#include <pthread.h>
+
+static pthread_t tid[MAX_ARGC];
+
+static int
+testThread(void)
+{
+ unsigned int i, repeat;
+ unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
+ void *results[MAX_ARGC];
+ int ret;
+ int res = 0;
+
+ xmlInitParser();
+
+ for (repeat = 0; repeat < 500; repeat++) {
+ xmlLoadCatalog(catalog);
+ nb_tests++;
+
+ nb_tests++;
+ for (i = 0; i < num_threads; i++) {
+ results[i] = NULL;
+ tid[i] = (pthread_t) - 1;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ ret = pthread_create(&tid[i], 0, thread_specific_data,
+ (void *) testfiles[i]);
+ if (ret != 0) {
+ fprintf(stderr, "pthread_create failed\n");
+ return (1);
+ }
+ }
+ for (i = 0; i < num_threads; i++) {
+ ret = pthread_join(tid[i], &results[i]);
+ if (ret != 0) {
+ fprintf(stderr, "pthread_join failed\n");
+ return (1);
+ }
+ }
+
+ xmlCatalogCleanup();
+ for (i = 0; i < num_threads; i++)
+ if (results[i] != (void *) Okay) {
+ fprintf(stderr, "Thread %d handling %s failed\n",
+ i, testfiles[i]);
+ res = 1;
+ }
+ }
+ return (res);
+}
+
+#elif defined WIN32
+#include <windows.h>
+#include <string.h>
+
+static HANDLE tid[MAX_ARGC];
+
+static DWORD WINAPI
+win32_thread_specific_data(void *private_data)
+{
+ return((DWORD) thread_specific_data());
+}
+
+static int
+testThread(void)
+{
+ unsigned int i, repeat;
+ unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
+ DWORD results[MAX_ARGC];
+ BOOL ret;
+ int res = 0;
+
+ xmlInitParser();
+ for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) {
+ xmlLoadCatalog(catalog);
+
+ for (i = 0; i < num_threads; i++) {
+ results[i] = 0;
+ tid[i] = (HANDLE) - 1;
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ DWORD useless;
+
+ tid[i] = CreateThread(NULL, 0,
+ win32_thread_specific_data, testfiles[i], 0,
+ &useless);
+ if (tid[i] == NULL) {
+ fprintf(stderr, "CreateThread failed\n");
+ return(1);
+ }
+ }
+
+ if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) ==
+ WAIT_FAILED) {
+ fprintf(stderr, "WaitForMultipleObjects failed\n");
+ return(1);
+ }
+
+ for (i = 0; i < num_threads; i++) {
+ ret = GetExitCodeThread(tid[i], &results[i]);
+ if (ret == 0) {
+ fprintf(stderr, "GetExitCodeThread failed\n");
+ return(1);
+ }
+ CloseHandle(tid[i]);
+ }
+
+ xmlCatalogCleanup();
+ for (i = 0; i < num_threads; i++) {
+ if (results[i] != (DWORD) Okay) {
+ fprintf(stderr, "Thread %d handling %s failed\n",
+ i, testfiles[i]);
+ res = 1;
+ }
+ }
+ }
+
+ return (res);
+}
+
+#elif defined __BEOS__
+#include <OS.h>
+
+static thread_id tid[MAX_ARGC];
+
+static int
+testThread(void)
+{
+ unsigned int i, repeat;
+ unsigned int num_threads = sizeof(testfiles) / sizeof(testfiles[0]);
+ void *results[MAX_ARGC];
+ status_t ret;
+ int res = 0;
+
+ xmlInitParser();
+ for (repeat = 0; repeat < 500; repeat++) {
+ xmlLoadCatalog(catalog);
+ for (i = 0; i < num_threads; i++) {
+ results[i] = NULL;
+ tid[i] = (thread_id) - 1;
+ }
+ for (i = 0; i < num_threads; i++) {
+ tid[i] =
+ spawn_thread(thread_specific_data, "xmlTestThread",
+ B_NORMAL_PRIORITY, (void *) testfiles[i]);
+ if (tid[i] < B_OK) {
+ fprintf(stderr, "beos_thread_create failed\n");
+ return (1);
+ }
+ printf("beos_thread_create %d -> %d\n", i, tid[i]);
+ }
+ for (i = 0; i < num_threads; i++) {
+ ret = wait_for_thread(tid[i], &results[i]);
+ printf("beos_thread_wait %d -> %d\n", i, ret);
+ if (ret != B_OK) {
+ fprintf(stderr, "beos_thread_wait failed\n");
+ return (1);
+ }
+ }
+
+ xmlCatalogCleanup();
+ ret = B_OK;
+ for (i = 0; i < num_threads; i++)
+ if (results[i] != (void *) Okay) {
+ printf("Thread %d handling %s failed\n", i, testfiles[i]);
+ ret = B_ERROR;
+ }
+ }
+ if (ret != B_OK)
+ return(1);
+ return (0);
+}
+#else
+static int
+testThread(void)
+{
+ fprintf(stderr,
+ "Specific platform thread support not detected\n");
+ return (-1);
+}
+#endif
+static int
+threadsTest(const char *filename ATTRIBUTE_UNUSED,
+ const char *resul ATTRIBUTE_UNUSED,
+ const char *err ATTRIBUTE_UNUSED,
+ int options ATTRIBUTE_UNUSED) {
+ return(testThread());
+}
+#endif
/************************************************************************
* *
* Tests Descriptions *
@@ -2396,6 +3890,38 @@
{ "URI base composition tests" ,
uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL,
0 },
+#ifdef LIBXML_SCHEMAS_ENABLED
+ { "Schemas regression tests" ,
+ schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL,
+ 0 },
+ { "Relax-NG regression tests" ,
+ rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
+ XML_PARSE_DTDATTR | XML_PARSE_NOENT },
+ { "Relax-NG streaming regression tests" ,
+ rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL,
+ XML_PARSE_DTDATTR | XML_PARSE_NOENT },
+#endif
+#ifdef LIBXML_PATTERN_ENABLED
+ { "Pattern regression tests" ,
+ patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL,
+ 0 },
+#endif
+#ifdef LIBXML_C14N_ENABLED
+ { "C14N with comments regression tests" ,
+ c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL,
+ 0 },
+ { "C14N without comments regression tests" ,
+ c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL,
+ 0 },
+ { "C14N exclusive without comments regression tests" ,
+ c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL,
+ 0 },
+#endif
+#if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED)
+ { "Catalog and Threads regression tests" ,
+ threadsTest, NULL, NULL, NULL, NULL,
+ 0 },
+#endif
{NULL, NULL, NULL, NULL, NULL, NULL, 0}
};
@@ -2411,7 +3937,7 @@
size_t i;
char *result;
char *error;
- int mem, leak;
+ int mem;
if (tst == NULL) return(-1);
if (tst->in != NULL) {
@@ -2457,6 +3983,7 @@
if (res != 0) {
fprintf(stderr, "File %s generated an error\n",
globbuf.gl_pathv[i]);
+ nb_errors++;
err++;
}
else if (xmlMemUsed() != mem) {
@@ -2464,7 +3991,7 @@
(extraMemoryFromResolver == 0)) {
fprintf(stderr, "File %s leaked %d bytes\n",
globbuf.gl_pathv[i], xmlMemUsed() - mem);
- leak++;
+ nb_leaks++;
err++;
}
}
@@ -2481,8 +4008,10 @@
testErrors[0] = 0;
extraMemoryFromResolver = 0;
res = tst->func(NULL, NULL, NULL, tst->options);
- if (res != 0)
+ if (res != 0) {
+ nb_errors++;
err++;
+ }
}
return(err);
}
@@ -2490,15 +4019,40 @@
int
main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
int i = 0, res, ret = 0;
+ int verbose = 0;
+ int old_errors, old_tests, old_leaks;
initializeLibxml2();
+ if ((argc >= 2) && (!strcmp(argv[1], "-v")))
+ verbose = 1;
for (i = 0; testDescriptions[i].func != NULL; i++) {
+ old_errors = nb_errors;
+ old_tests = nb_tests;
+ old_leaks = nb_leaks;
if (testDescriptions[i].desc != NULL)
printf("## %s\n", testDescriptions[i].desc);
res = launchTests(&testDescriptions[i]);
if (res != 0)
ret++;
+ if (verbose) {
+ 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);
}
xmlCleanupParser();
xmlMemoryDump();