first version of testlimits new test
Used to check behaviour on various parsing limits
diff --git a/Makefile.am b/Makefile.am
index 487a37e..d9abcdf 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -11,7 +11,7 @@
noinst_PROGRAMS=testSchemas testRelax testSAX testHTML testXPath testURI \
testThreads testC14N testAutomata testRegexp \
testReader testapi testModule runtest runsuite testchar \
- testdict runxmlconf testrecurse
+ testdict runxmlconf testrecurse testlimits
bin_PROGRAMS = xmllint xmlcatalog
@@ -72,6 +72,11 @@
testrecurse_DEPENDENCIES = $(DEPS)
testrecurse_LDADD= @BASE_THREAD_LIBS@ @RDL_LIBS@ $(LDADDS)
+testlimits_SOURCES=testlimits.c
+testlimits_LDFLAGS =
+testlimits_DEPENDENCIES = $(DEPS)
+testlimits_LDADD= @BASE_THREAD_LIBS@ @RDL_LIBS@ $(LDADDS)
+
testchar_SOURCES=testchar.c
testchar_LDFLAGS =
testchar_DEPENDENCIES = $(DEPS)
diff --git a/testlimits.c b/testlimits.c
new file mode 100644
index 0000000..5335117
--- /dev/null
+++ b/testlimits.c
@@ -0,0 +1,1343 @@
+/*
+ * testlimits.c: C program to run libxml2 regression tests checking various
+ * limits in document size. Will consume a lot of RAM and CPU cycles
+ *
+ * To compile on Unixes:
+ * cc -o testlimits `xml2-config --cflags` testlimits.c `xml2-config --libs` -lpthread
+ *
+ * See Copyright for the status of this software.
+ *
+ * daniel@veillard.com
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "libxml.h"
+#else
+#include <stdio.h>
+#endif
+
+#if !defined(_WIN32) || defined(__CYGWIN__)
+#include <unistd.h>
+#endif
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <libxml/parser.h>
+#include <libxml/parserInternals.h>
+#include <libxml/tree.h>
+#include <libxml/uri.h>
+#ifdef LIBXML_READER_ENABLED
+#include <libxml/xmlreader.h>
+#endif
+
+static int verbose = 0;
+static int tests_quiet = 0;
+
+/************************************************************************
+ * *
+ * Huge document generator *
+ * *
+ ************************************************************************/
+
+#include <libxml/xmlIO.h>
+
+/*
+ * Huge documents are built using fixed start and end chunks
+ * and filling between the two an unconventional amount of char data
+ */
+typedef struct hugeTest hugeTest;
+typedef hugeTest *hugeTestPtr;
+struct hugeTest {
+ const char *description;
+ const char *name;
+ const char *start;
+ const char *end;
+};
+
+static struct hugeTest hugeTests[] = {
+ { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
+ { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
+ { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
+};
+
+static const char *current;
+static int rlen;
+static unsigned int currentTest = 0;
+static int instate = 0;
+
+/**
+ * hugeMatch:
+ * @URI: an URI to test
+ *
+ * Check for an huge: query
+ *
+ * Returns 1 if yes and 0 if another Input module should be used
+ */
+static int
+hugeMatch(const char * URI) {
+ if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
+ return(1);
+ return(0);
+}
+
+/**
+ * hugeOpen:
+ * @URI: an URI to test
+ *
+ * Return a pointer to the huge: query handler, in this example simply
+ * the current pointer...
+ *
+ * Returns an Input context or NULL in case or error
+ */
+static void *
+hugeOpen(const char * URI) {
+ if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
+ return(NULL);
+
+ for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
+ currentTest++)
+ if (!strcmp(hugeTests[currentTest].name, URI))
+ goto found;
+
+ return(NULL);
+
+found:
+ rlen = strlen(hugeTests[currentTest].start);
+ current = hugeTests[currentTest].start;
+ instate = 0;
+ return((void *) current);
+}
+
+/**
+ * hugeClose:
+ * @context: the read context
+ *
+ * Close the huge: query handler
+ *
+ * Returns 0 or -1 in case of error
+ */
+static int
+hugeClose(void * context) {
+ if (context == NULL) return(-1);
+ return(0);
+}
+
+#define CHUNK 4096
+
+char filling[CHUNK + 1];
+
+static void fillFilling(void) {
+ int i;
+
+ for (i = 0;i < CHUNK;i++) {
+ filling[i] = 'a';
+ }
+ filling[CHUNK] = 0;
+}
+
+size_t maxlen = 64 * 1024 * 1024;
+size_t curlen = 0;
+size_t dotlen;
+
+/**
+ * hugeRead:
+ * @context: the read context
+ * @buffer: where to store data
+ * @len: number of bytes to read
+ *
+ * Implement an huge: query read.
+ *
+ * Returns the number of bytes read or -1 in case of error
+ */
+static int
+hugeRead(void *context, char *buffer, int len)
+{
+ if ((context == NULL) || (buffer == NULL) || (len < 0))
+ return (-1);
+
+ if (instate == 0) {
+ if (len >= rlen) {
+ len = rlen;
+ rlen = 0;
+ memcpy(buffer, current, len);
+ instate = 1;
+ curlen = 0;
+ dotlen = maxlen / 10;
+ } else {
+ memcpy(buffer, current, len);
+ rlen -= len;
+ current += len;
+ }
+ } else if (instate == 2) {
+ if (len >= rlen) {
+ len = rlen;
+ rlen = 0;
+ memcpy(buffer, current, len);
+ instate = 3;
+ curlen = 0;
+ } else {
+ memcpy(buffer, current, len);
+ rlen -= len;
+ current += len;
+ }
+ } else if (instate == 1) {
+ if (len > CHUNK) len = CHUNK;
+ memcpy(buffer, &filling[0], len);
+ curlen += len;
+ if (curlen >= maxlen) {
+ rlen = strlen(hugeTests[currentTest].end);
+ current = hugeTests[currentTest].end;
+ instate = 2;
+ } else {
+ if (curlen > dotlen) {
+ fprintf(stderr, ".");
+ dotlen += maxlen / 10;
+ }
+ }
+ } else
+ len = 0;
+ return (len);
+}
+
+/************************************************************************
+ * *
+ * Libxml2 specific routines *
+ * *
+ ************************************************************************/
+
+static int nb_tests = 0;
+static int nb_errors = 0;
+static int nb_leaks = 0;
+static int extraMemoryFromResolver = 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 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 XMLCDECL
+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);
+
+ xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
+ xmlInitParser();
+ xmlSetExternalEntityLoader(testExternalEntityLoader);
+ xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
+ /*
+ * register the new I/O handlers
+ */
+ if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
+ hugeRead, hugeClose) < 0) {
+ fprintf(stderr, "failed to register Huge handlers\n");
+ exit(1);
+ }
+}
+
+/************************************************************************
+ * *
+ * SAX empty callbacks *
+ * *
+ ************************************************************************/
+
+unsigned long callbacks = 0;
+
+/**
+ * isStandaloneCallback:
+ * @ctxt: An XML parser context
+ *
+ * Is this document tagged standalone ?
+ *
+ * Returns 1 if true
+ */
+static int
+isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (0);
+}
+
+/**
+ * hasInternalSubsetCallback:
+ * @ctxt: An XML parser context
+ *
+ * Does this document has an internal subset
+ *
+ * Returns 1 if true
+ */
+static int
+hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (0);
+}
+
+/**
+ * hasExternalSubsetCallback:
+ * @ctxt: An XML parser context
+ *
+ * Does this document has an external subset
+ *
+ * Returns 1 if true
+ */
+static int
+hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (0);
+}
+
+/**
+ * internalSubsetCallback:
+ * @ctxt: An XML parser context
+ *
+ * Does this document has an internal subset
+ */
+static void
+internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ const xmlChar * ExternalID ATTRIBUTE_UNUSED,
+ const xmlChar * SystemID ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * externalSubsetCallback:
+ * @ctxt: An XML parser context
+ *
+ * Does this document has an external subset
+ */
+static void
+externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ const xmlChar * ExternalID ATTRIBUTE_UNUSED,
+ const xmlChar * SystemID ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * resolveEntityCallback:
+ * @ctxt: An XML parser context
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ *
+ * Special entity resolver, better left to the parser, it has
+ * more context than the application layer.
+ * The default behaviour is to NOT resolve the entities, in that case
+ * the ENTITY_REF nodes are built in the structure (and the parameter
+ * values).
+ *
+ * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
+ */
+static xmlParserInputPtr
+resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * publicId ATTRIBUTE_UNUSED,
+ const xmlChar * systemId ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (NULL);
+}
+
+/**
+ * getEntityCallback:
+ * @ctxt: An XML parser context
+ * @name: The entity name
+ *
+ * Get an entity by name
+ *
+ * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
+ */
+static xmlEntityPtr
+getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (NULL);
+}
+
+/**
+ * getParameterEntityCallback:
+ * @ctxt: An XML parser context
+ * @name: The entity name
+ *
+ * Get a parameter entity by name
+ *
+ * Returns the xmlParserInputPtr
+ */
+static xmlEntityPtr
+getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return (NULL);
+}
+
+
+/**
+ * entityDeclCallback:
+ * @ctxt: An XML parser context
+ * @name: the entity name
+ * @type: the entity type
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ * @content: the entity value (without processing).
+ *
+ * An entity definition has been parsed
+ */
+static void
+entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED,
+ const xmlChar * publicId ATTRIBUTE_UNUSED,
+ const xmlChar * systemId ATTRIBUTE_UNUSED,
+ xmlChar * content ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * attributeDeclCallback:
+ * @ctxt: An XML parser context
+ * @name: the attribute name
+ * @type: the attribute type
+ *
+ * An attribute definition has been parsed
+ */
+static void
+attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * elem ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
+ const xmlChar * defaultValue ATTRIBUTE_UNUSED,
+ xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * elementDeclCallback:
+ * @ctxt: An XML parser context
+ * @name: the element name
+ * @type: the element type
+ * @content: the element value (without processing).
+ *
+ * An element definition has been parsed
+ */
+static void
+elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ int type ATTRIBUTE_UNUSED,
+ xmlElementContentPtr content ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * notationDeclCallback:
+ * @ctxt: An XML parser context
+ * @name: The name of the notation
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ *
+ * What to do when a notation declaration has been parsed.
+ */
+static void
+notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ const xmlChar * publicId ATTRIBUTE_UNUSED,
+ const xmlChar * systemId ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * unparsedEntityDeclCallback:
+ * @ctxt: An XML parser context
+ * @name: The name of the entity
+ * @publicId: The public ID of the entity
+ * @systemId: The system ID of the entity
+ * @notationName: the name of the notation
+ *
+ * What to do when an unparsed entity declaration is parsed
+ */
+static void
+unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ const xmlChar * publicId ATTRIBUTE_UNUSED,
+ const xmlChar * systemId ATTRIBUTE_UNUSED,
+ const xmlChar * notationName ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * setDocumentLocatorCallback:
+ * @ctxt: An XML parser context
+ * @loc: A SAX Locator
+ *
+ * Receive the document locator at startup, actually xmlDefaultSAXLocator
+ * Everything is available on the context, so this is useless in our case.
+ */
+static void
+setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
+ xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * startDocumentCallback:
+ * @ctxt: An XML parser context
+ *
+ * called when the document start being processed.
+ */
+static void
+startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * endDocumentCallback:
+ * @ctxt: An XML parser context
+ *
+ * called when the document end has been detected.
+ */
+static void
+endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+#if 0
+/**
+ * startElementCallback:
+ * @ctxt: An XML parser context
+ * @name: The element name
+ *
+ * called when an opening tag has been processed.
+ */
+static void
+startElementCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED,
+ const xmlChar ** atts ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * endElementCallback:
+ * @ctxt: An XML parser context
+ * @name: The element name
+ *
+ * called when the end of an element has been detected.
+ */
+static void
+endElementCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+#endif
+
+/**
+ * charactersCallback:
+ * @ctxt: An XML parser context
+ * @ch: a xmlChar string
+ * @len: the number of xmlChar
+ *
+ * receiving some chars from the parser.
+ * Question: how much at a time ???
+ */
+static void
+charactersCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * ch ATTRIBUTE_UNUSED,
+ int len ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * referenceCallback:
+ * @ctxt: An XML parser context
+ * @name: The entity name
+ *
+ * called when an entity reference is detected.
+ */
+static void
+referenceCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * name ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * ignorableWhitespaceCallback:
+ * @ctxt: An XML parser context
+ * @ch: a xmlChar string
+ * @start: the first char in the string
+ * @len: the number of xmlChar
+ *
+ * receiving some ignorable whitespaces from the parser.
+ * Question: how much at a time ???
+ */
+static void
+ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * ch ATTRIBUTE_UNUSED,
+ int len ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * processingInstructionCallback:
+ * @ctxt: An XML parser context
+ * @target: the target name
+ * @data: the PI data's
+ * @len: the number of xmlChar
+ *
+ * A processing instruction has been parsed.
+ */
+static void
+processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * target ATTRIBUTE_UNUSED,
+ const xmlChar * data ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * cdataBlockCallback:
+ * @ctx: the user data (XML parser context)
+ * @value: The pcdata content
+ * @len: the block length
+ *
+ * called when a pcdata block has been parsed
+ */
+static void
+cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * value ATTRIBUTE_UNUSED,
+ int len ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * commentCallback:
+ * @ctxt: An XML parser context
+ * @value: the comment content
+ *
+ * A comment has been parsed.
+ */
+static void
+commentCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * value ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * warningCallback:
+ * @ctxt: An XML parser context
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Display and format a warning messages, gives file, line, position and
+ * extra parameters.
+ */
+static void XMLCDECL
+warningCallback(void *ctx ATTRIBUTE_UNUSED,
+ const char *msg ATTRIBUTE_UNUSED, ...)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * errorCallback:
+ * @ctxt: An XML parser context
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Display and format a error messages, gives file, line, position and
+ * extra parameters.
+ */
+static void XMLCDECL
+errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
+ ...)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * fatalErrorCallback:
+ * @ctxt: An XML parser context
+ * @msg: the message to display/transmit
+ * @...: extra parameters for the message display
+ *
+ * Display and format a fatalError messages, gives file, line, position and
+ * extra parameters.
+ */
+static void XMLCDECL
+fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
+ const char *msg ATTRIBUTE_UNUSED, ...)
+{
+ return;
+}
+
+
+/*
+ * SAX2 specific callbacks
+ */
+
+/**
+ * startElementNsCallback:
+ * @ctxt: An XML parser context
+ * @name: The element name
+ *
+ * called when an opening tag has been processed.
+ */
+static void
+startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * localname ATTRIBUTE_UNUSED,
+ const xmlChar * prefix ATTRIBUTE_UNUSED,
+ const xmlChar * URI ATTRIBUTE_UNUSED,
+ int nb_namespaces ATTRIBUTE_UNUSED,
+ const xmlChar ** namespaces ATTRIBUTE_UNUSED,
+ int nb_attributes ATTRIBUTE_UNUSED,
+ int nb_defaulted ATTRIBUTE_UNUSED,
+ const xmlChar ** attributes ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+/**
+ * endElementCallback:
+ * @ctxt: An XML parser context
+ * @name: The element name
+ *
+ * called when the end of an element has been detected.
+ */
+static void
+endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
+ const xmlChar * localname ATTRIBUTE_UNUSED,
+ const xmlChar * prefix ATTRIBUTE_UNUSED,
+ const xmlChar * URI ATTRIBUTE_UNUSED)
+{
+ callbacks++;
+ return;
+}
+
+static xmlSAXHandler callbackSAX2HandlerStruct = {
+ internalSubsetCallback,
+ isStandaloneCallback,
+ hasInternalSubsetCallback,
+ hasExternalSubsetCallback,
+ resolveEntityCallback,
+ getEntityCallback,
+ entityDeclCallback,
+ notationDeclCallback,
+ attributeDeclCallback,
+ elementDeclCallback,
+ unparsedEntityDeclCallback,
+ setDocumentLocatorCallback,
+ startDocumentCallback,
+ endDocumentCallback,
+ NULL,
+ NULL,
+ referenceCallback,
+ charactersCallback,
+ ignorableWhitespaceCallback,
+ processingInstructionCallback,
+ commentCallback,
+ warningCallback,
+ errorCallback,
+ fatalErrorCallback,
+ getParameterEntityCallback,
+ cdataBlockCallback,
+ externalSubsetCallback,
+ XML_SAX2_MAGIC,
+ NULL,
+ startElementNsCallback,
+ endElementNsCallback,
+ NULL
+};
+
+static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
+
+/************************************************************************
+ * *
+ * The tests front-ends *
+ * *
+ ************************************************************************/
+
+/**
+ * readerTest:
+ * @filename: the file to parse
+ * @max_size: size of the limit to test
+ * @options: parsing options
+ * @fail: should a failure be reported
+ *
+ * Parse a memory generated file using the xmlReader
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+saxTest(const char *filename, size_t limit, int options, int fail) {
+ int res = 0;
+ xmlParserCtxtPtr ctxt;
+ xmlDocPtr doc;
+ xmlSAXHandlerPtr old_sax;
+
+ nb_tests++;
+
+ maxlen = limit;
+ ctxt = xmlNewParserCtxt();
+ if (ctxt == NULL) {
+ fprintf(stderr, "Failed to create parser context\n");
+ return(1);
+ }
+ old_sax = ctxt->sax;
+ ctxt->sax = callbackSAX2Handler;
+ ctxt->userData = NULL;
+ doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
+ fprintf(stderr, "\n");
+
+ if (doc != NULL) {
+ fprintf(stderr, "SAX parsing generated a document !\n");
+ xmlFreeDoc(doc);
+ res = 0;
+ } else if (ctxt->wellFormed == 0) {
+ if (fail)
+ res = 0;
+ else {
+ fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
+ res = 1;
+ }
+ } else {
+ if (fail) {
+ fprintf(stderr, "Failed to get failure for '%s' %lu\n",
+ filename, limit);
+ res = 1;
+ } else
+ res = 0;
+ }
+ ctxt->sax = old_sax;
+ xmlFreeParserCtxt(ctxt);
+
+ return(res);
+}
+#ifdef LIBXML_READER_ENABLED
+/**
+ * readerTest:
+ * @filename: the file to parse
+ * @max_size: size of the limit to test
+ * @options: parsing options
+ * @fail: should a failure be reported
+ *
+ * Parse a memory generated file using the xmlReader
+ *
+ * Returns 0 in case of success, an error code otherwise
+ */
+static int
+readerTest(const char *filename, size_t limit, int options, int fail) {
+ xmlTextReaderPtr reader;
+ int res = 0;
+ int ret;
+
+ nb_tests++;
+
+ maxlen = limit;
+ reader = xmlReaderForFile(filename , NULL, options);
+ if (reader == NULL) {
+ fprintf(stderr, "Failed to open '%s' test\n", filename);
+ return(1);
+ }
+ ret = xmlTextReaderRead(reader);
+ while (ret == 1) {
+ ret = xmlTextReaderRead(reader);
+ }
+ fprintf(stderr, "\n");
+ if (ret != 0) {
+ if (fail)
+ res = 0;
+ else {
+ fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
+ res = 1;
+ }
+ } else {
+ if (fail) {
+ fprintf(stderr, "Failed to get failure for '%s' %lu\n",
+ filename, limit);
+ res = 1;
+ } else
+ res = 0;
+ }
+ xmlFreeTextReader(reader);
+
+ return(res);
+}
+#endif
+
+/************************************************************************
+ * *
+ * Tests descriptions *
+ * *
+ ************************************************************************/
+
+typedef int (*functest) (const char *filename, size_t limit, int options,
+ int fail);
+
+typedef struct limitDesc limitDesc;
+typedef limitDesc *limitDescPtr;
+struct limitDesc {
+ const char *name; /* the huge generator name */
+ size_t limit; /* the limit to test */
+ int options; /* extra parser options */
+ int fail; /* whether the test should fail */
+};
+
+static limitDesc limitDescriptions[] = {
+ /* max lenght of a text node in content */
+ {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
+ {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
+ {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
+ /* max lenght of a text node in content */
+ {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
+ {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
+ {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
+ /* max lenght of a comment node */
+ {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
+ {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
+ {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
+};
+
+typedef struct testDesc testDesc;
+typedef testDesc *testDescPtr;
+struct testDesc {
+ const char *desc; /* descripton of the test */
+ functest func; /* function implementing the test */
+};
+
+static
+testDesc testDescriptions[] = {
+ { "Parsing of huge files with the sax parser", saxTest},
+/* { "Parsing of huge files with the tree parser", treeTest}, */
+#ifdef LIBXML_READER_ENABLED
+ { "Parsing of huge files with the reader", readerTest},
+#endif
+ {NULL, NULL}
+};
+
+typedef struct testException testException;
+typedef testException *testExceptionPtr;
+struct testException {
+ unsigned int test; /* the parser test number */
+ unsigned int limit; /* the limit test number */
+ int fail; /* new fail value or -1*/
+ size_t size; /* new limit value or 0 */
+};
+
+static
+testException testExceptions[] = {
+ /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
+ { 0, 1, 0, 0},
+};
+
+static int
+launchTests(testDescPtr tst, unsigned int test) {
+ int res = 0, err = 0;
+ unsigned int i, j;
+ size_t limit;
+ int fail;
+
+ if (tst == NULL) return(-1);
+
+ for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
+ limit = limitDescriptions[i].limit;
+ fail = limitDescriptions[i].fail;
+ /*
+ * Handle exceptions if any
+ */
+ for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
+ if ((testExceptions[j].test == test) &&
+ (testExceptions[j].limit == i)) {
+ if (testExceptions[j].fail != -1)
+ fail = testExceptions[j].fail;
+ if (testExceptions[j].size != 0)
+ limit = testExceptions[j].size;
+ break;
+ }
+ }
+ res = tst->func(limitDescriptions[i].name, limit,
+ limitDescriptions[i].options, fail);
+ if (res != 0) {
+ nb_errors++;
+ err++;
+ }
+ }
+ return(err);
+}
+
+
+static int
+runtest(unsigned int i) {
+ int ret = 0, res;
+ int old_errors, old_tests, old_leaks;
+
+ old_errors = nb_errors;
+ old_tests = nb_tests;
+ old_leaks = nb_leaks;
+ if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
+ printf("## %s\n", testDescriptions[i].desc);
+ res = launchTests(&testDescriptions[i], 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);
+ }
+ return(ret);
+}
+
+int
+main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
+ int i, a, ret = 0;
+ int subset = 0;
+
+ fillFilling();
+ initializeLibxml2();
+
+ for (a = 1; a < argc;a++) {
+ if (!strcmp(argv[a], "-v"))
+ verbose = 1;
+ else if (!strcmp(argv[a], "-quiet"))
+ tests_quiet = 1;
+ }
+ if (subset == 0) {
+ for (i = 0; testDescriptions[i].func != NULL; i++) {
+ ret += runtest(i);
+ }
+ }
+ 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);
+}