Newer
Older
/*
* 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>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libxml/parser.h>
Daniel Veillard
committed
#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;
Kasimier T. Buchcik
committed
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 (!(buf.st_mode & _S_IFREG))
return(0);
#else
if (!S_ISREG(buf.st_mode))
return(0);
return(1);
}
Daniel Veillard
committed
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);
}
/*
Daniel Veillard
committed
* 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;
Daniel Veillard
committed
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.
*/
testExternalEntityLoader(const char *URL, const char *ID,
xmlParserCtxtPtr ctxt) {
xmlParserInputPtr ret;
Daniel Veillard
committed
int i;
Daniel Veillard
committed
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;
}
Daniel Veillard
committed
if (ret == NULL) {
fprintf(stderr, "Failed to find resource %s\n", URL);
}
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);
Kasimier T. Buchcik
committed
/*
* 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
Kasimier T. Buchcik
committed
* 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 *
* *
************************************************************************/
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();
}
xmlBufferSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
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);
}
Daniel Veillard
committed
static void
installResources(xmlNodePtr tst, const xmlChar *base) {
xmlNodePtr test;
xmlBufferPtr buf;
xmlChar *name, *content, *res;
Daniel Veillard
committed
buf = xmlBufferCreate();
if (buf == NULL) {
fprintf(stderr, "out of memory !\n");
fatalError();
}
xmlBufferSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
Daniel Veillard
committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
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]");
}
Daniel Veillard
committed
}
xsdTestCase(xmlNodePtr tst) {
xmlNodePtr test, tmp, cur;
xmlBufferPtr buf;
xmlDocPtr doc = NULL;
xmlRelaxNGParserCtxtPtr pctxt;
xmlRelaxNGValidCtxtPtr ctxt;
xmlRelaxNGPtr rng = NULL;
int ret = 0, mem, memt;
Daniel Veillard
committed
xmlChar *dtd;
resetEntities();
testErrorsSize = 0; testErrors[0] = 0;
Daniel Veillard
committed
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) {
}
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();
}
xmlBufferSetAllocationScheme(buf, XML_BUFFER_ALLOC_DOUBLEIT);
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) {
Daniel Veillard
committed
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);
Daniel Veillard
committed
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++;
}
}
Daniel Veillard
committed
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);
}
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);
}
xsdTest(void) {
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
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);
}
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);
}
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);
}
Daniel Veillard
committed
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);
}
rngTest2(void) {
Daniel Veillard
committed
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
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);
Daniel Veillard
committed
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,
Daniel Veillard
committed
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);
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",
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) {
Kasimier T. Buchcik
committed
if (schemas != NULL) {
xstcTestInstance(instance, schemas, path, base);
Kasimier T. Buchcik
committed
} else {
/*
* We'll automatically mark the instances as failed
* if the schema was broken.
*/
nb_errors++;
}
Kasimier T. Buchcik
committed
"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",
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++;
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;
Daniel Veillard
committed
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);