2 * testlimits.c: C program to run libxml2 regression tests checking various
3 * limits in document size. Will consume a lot of RAM and CPU cycles
5 * To compile on Unixes:
6 * cc -o testlimits `xml2-config --cflags` testlimits.c `xml2-config --libs` -lpthread
8 * See Copyright for the status of this software.
19 #if !defined(_WIN32) || defined(__CYGWIN__)
23 #include <sys/types.h>
28 #include <libxml/parser.h>
29 #include <libxml/parserInternals.h>
30 #include <libxml/tree.h>
31 #include <libxml/uri.h>
32 #ifdef LIBXML_READER_ENABLED
33 #include <libxml/xmlreader.h>
36 static int verbose = 0;
37 static int tests_quiet = 0;
39 /************************************************************************
43 ************************************************************************/
45 /* maximum time for one parsing before declaring a timeout */
46 #define MAX_TIME 2 /* seconds */
51 static void reset_timout(void) {
56 static int check_time(void) {
57 clock_t tnow = clock();
58 if (((tnow - t0) / CLOCKS_PER_SEC) > MAX_TIME) {
65 /************************************************************************
67 * Huge document generator *
69 ************************************************************************/
71 #include <libxml/xmlIO.h>
74 * Huge documents are built using fixed start and end chunks
75 * and filling between the two an unconventional amount of char data
77 typedef struct hugeTest hugeTest;
78 typedef hugeTest *hugeTestPtr;
80 const char *description;
86 static struct hugeTest hugeTests[] = {
87 { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
88 { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
89 { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
90 { "Huge PI node", "huge:piNode", "<foo><?bar ", "?></foo>" },
93 static const char *current;
95 static unsigned int currentTest = 0;
96 static int instate = 0;
100 * @URI: an URI to test
102 * Check for an huge: query
104 * Returns 1 if yes and 0 if another Input module should be used
107 hugeMatch(const char * URI) {
108 if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
115 * @URI: an URI to test
117 * Return a pointer to the huge: query handler, in this example simply
118 * the current pointer...
120 * Returns an Input context or NULL in case or error
123 hugeOpen(const char * URI) {
124 if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
127 for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
129 if (!strcmp(hugeTests[currentTest].name, URI))
135 rlen = strlen(hugeTests[currentTest].start);
136 current = hugeTests[currentTest].start;
138 return((void *) current);
143 * @context: the read context
145 * Close the huge: query handler
147 * Returns 0 or -1 in case of error
150 hugeClose(void * context) {
151 if (context == NULL) return(-1);
152 fprintf(stderr, "\n");
158 char filling[CHUNK + 1];
160 static void fillFilling(void) {
163 for (i = 0;i < CHUNK;i++) {
169 size_t maxlen = 64 * 1024 * 1024;
175 * @context: the read context
176 * @buffer: where to store data
177 * @len: number of bytes to read
179 * Implement an huge: query read.
181 * Returns the number of bytes read or -1 in case of error
184 hugeRead(void *context, char *buffer, int len)
186 if ((context == NULL) || (buffer == NULL) || (len < 0))
193 memcpy(buffer, current, len);
196 dotlen = maxlen / 10;
198 memcpy(buffer, current, len);
202 } else if (instate == 2) {
206 memcpy(buffer, current, len);
210 memcpy(buffer, current, len);
214 } else if (instate == 1) {
215 if (len > CHUNK) len = CHUNK;
216 memcpy(buffer, &filling[0], len);
218 if (curlen >= maxlen) {
219 rlen = strlen(hugeTests[currentTest].end);
220 current = hugeTests[currentTest].end;
223 if (curlen > dotlen) {
224 fprintf(stderr, ".");
225 dotlen += maxlen / 10;
233 /************************************************************************
235 * Crazy document generator *
237 ************************************************************************/
239 unsigned int crazy_indx = 0;
241 const char *crazy = "<?xml version='1.0' encoding='UTF-8'?>\
247 <!ELEMENT foo (#PCDATA)>\
248 <!ELEMENT p (#PCDATA|emph)* >\
263 * @URI: an URI to test
265 * Check for a crazy: query
267 * Returns 1 if yes and 0 if another Input module should be used
270 crazyMatch(const char * URI) {
271 if ((URI != NULL) && (!strncmp(URI, "crazy:", 6)))
278 * @URI: an URI to test
280 * Return a pointer to the crazy: query handler, in this example simply
281 * the current pointer...
283 * Returns an Input context or NULL in case or error
286 crazyOpen(const char * URI) {
287 if ((URI == NULL) || (strncmp(URI, "crazy:", 6)))
290 if (crazy_indx > strlen(crazy))
296 return((void *) current);
301 * @context: the read context
303 * Close the crazy: query handler
305 * Returns 0 or -1 in case of error
308 crazyClose(void * context) {
309 if (context == NULL) return(-1);
316 * @context: the read context
317 * @buffer: where to store data
318 * @len: number of bytes to read
320 * Implement an crazy: query read.
322 * Returns the number of bytes read or -1 in case of error
325 crazyRead(void *context, char *buffer, int len)
327 if ((context == NULL) || (buffer == NULL) || (len < 0))
330 if ((check_time() <= 0) && (instate == 1)) {
331 fprintf(stderr, "\ntimeout in crazy(%d)\n", crazy_indx);
332 rlen = strlen(crazy) - crazy_indx;
333 current = &crazy[crazy_indx];
340 memcpy(buffer, current, len);
344 memcpy(buffer, current, len);
348 } else if (instate == 2) {
352 memcpy(buffer, current, len);
356 memcpy(buffer, current, len);
360 } else if (instate == 1) {
361 if (len > CHUNK) len = CHUNK;
362 memcpy(buffer, &filling[0], len);
364 if (curlen >= maxlen) {
365 rlen = strlen(crazy) - crazy_indx;
366 current = &crazy[crazy_indx];
373 /************************************************************************
375 * Libxml2 specific routines *
377 ************************************************************************/
379 static int nb_tests = 0;
380 static int nb_errors = 0;
381 static int nb_leaks = 0;
382 static int extraMemoryFromResolver = 0;
385 * We need to trap calls to the resolver to not account memory for the catalog
386 * which is shared to the current running test. We also don't want to have
387 * network downloads modifying tests.
389 static xmlParserInputPtr
390 testExternalEntityLoader(const char *URL, const char *ID,
391 xmlParserCtxtPtr ctxt) {
392 xmlParserInputPtr ret;
393 int memused = xmlMemUsed();
395 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
396 extraMemoryFromResolver += xmlMemUsed() - memused;
402 * Trapping the error messages at the generic level to grab the equivalent of
403 * stderr messages on CLI tools.
405 static char testErrors[32769];
406 static int testErrorsSize = 0;
409 channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
413 if (testErrorsSize >= 32768)
416 res = vsnprintf(&testErrors[testErrorsSize],
417 32768 - testErrorsSize,
420 if (testErrorsSize + res >= 32768) {
422 testErrorsSize = 32768;
423 testErrors[testErrorsSize] = 0;
425 testErrorsSize += res;
427 testErrors[testErrorsSize] = 0;
431 * xmlParserPrintFileContext:
432 * @input: an xmlParserInputPtr input
434 * Displays current context within the input content for error tracking
438 xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
439 xmlGenericErrorFunc chanl, void *data ) {
440 const xmlChar *cur, *base;
441 unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */
442 xmlChar content[81]; /* space for 80 chars + line terminator */
445 if (input == NULL) return;
448 /* skip backwards over any end-of-lines */
449 while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
453 /* search backwards for beginning-of-line (to max buff size) */
454 while ((n++ < (sizeof(content)-1)) && (cur > base) &&
455 (*(cur) != '\n') && (*(cur) != '\r'))
457 if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
458 /* calculate the error position in terms of the current position */
459 col = input->cur - cur;
460 /* search forward for end-of-line (to max buff size) */
463 /* copy selected text to our buffer */
464 while ((*cur != 0) && (*(cur) != '\n') &&
465 (*(cur) != '\r') && (n < sizeof(content)-1)) {
470 /* print out the selected text */
471 chanl(data ,"%s\n", content);
472 /* create blank line with problem pointer */
475 /* (leave buffer space for pointer + line terminator) */
476 while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
483 chanl(data ,"%s\n", content);
487 testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) {
494 const xmlChar *name = NULL;
497 xmlParserInputPtr input = NULL;
498 xmlParserInputPtr cur = NULL;
499 xmlParserCtxtPtr ctxt = NULL;
507 domain = err->domain;
510 if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
511 (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
512 (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
517 if (code == XML_ERR_OK)
520 if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
524 * Maintain the compatibility with the legacy error handling
528 if ((input != NULL) && (input->filename == NULL) &&
529 (ctxt->inputNr > 1)) {
531 input = ctxt->inputTab[ctxt->inputNr - 2];
535 channel(data, "%s:%d: ", input->filename, input->line);
536 else if ((line != 0) && (domain == XML_FROM_PARSER))
537 channel(data, "Entity: line %d: ", input->line);
541 channel(data, "%s:%d: ", file, line);
542 else if ((line != 0) && (domain == XML_FROM_PARSER))
543 channel(data, "Entity: line %d: ", line);
546 channel(data, "element %s: ", name);
548 if (code == XML_ERR_OK)
551 case XML_FROM_PARSER:
552 channel(data, "parser ");
554 case XML_FROM_NAMESPACE:
555 channel(data, "namespace ");
559 channel(data, "validity ");
562 channel(data, "HTML parser ");
564 case XML_FROM_MEMORY:
565 channel(data, "memory ");
567 case XML_FROM_OUTPUT:
568 channel(data, "output ");
571 channel(data, "I/O ");
573 case XML_FROM_XINCLUDE:
574 channel(data, "XInclude ");
577 channel(data, "XPath ");
579 case XML_FROM_XPOINTER:
580 channel(data, "parser ");
582 case XML_FROM_REGEXP:
583 channel(data, "regexp ");
585 case XML_FROM_MODULE:
586 channel(data, "module ");
588 case XML_FROM_SCHEMASV:
589 channel(data, "Schemas validity ");
591 case XML_FROM_SCHEMASP:
592 channel(data, "Schemas parser ");
594 case XML_FROM_RELAXNGP:
595 channel(data, "Relax-NG parser ");
597 case XML_FROM_RELAXNGV:
598 channel(data, "Relax-NG validity ");
600 case XML_FROM_CATALOG:
601 channel(data, "Catalog ");
604 channel(data, "C14N ");
607 channel(data, "XSLT ");
612 if (code == XML_ERR_OK)
618 case XML_ERR_WARNING:
619 channel(data, "warning : ");
622 channel(data, "error : ");
625 channel(data, "error : ");
628 if (code == XML_ERR_OK)
632 len = xmlStrlen((const xmlChar *)str);
633 if ((len > 0) && (str[len - 1] != '\n'))
634 channel(data, "%s\n", str);
636 channel(data, "%s", str);
638 channel(data, "%s\n", "out of memory error");
640 if (code == XML_ERR_OK)
644 xmlParserPrintFileContextInternal(input, channel, data);
647 channel(data, "%s:%d: \n", cur->filename, cur->line);
648 else if ((line != 0) && (domain == XML_FROM_PARSER))
649 channel(data, "Entity: line %d: \n", cur->line);
650 xmlParserPrintFileContextInternal(cur, channel, data);
653 if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
655 (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
659 channel(data, "%s\n", err->str1);
660 for (i=0;i < err->int1;i++)
664 channel(data, "%s\n", buf);
669 initializeLibxml2(void) {
670 xmlGetWarningsDefaultValue = 0;
671 xmlPedanticParserDefault(0);
673 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
675 xmlSetExternalEntityLoader(testExternalEntityLoader);
676 xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
678 * register the new I/O handlers
680 if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
681 hugeRead, hugeClose) < 0) {
682 fprintf(stderr, "failed to register Huge handlers\n");
685 if (xmlRegisterInputCallbacks(crazyMatch, crazyOpen,
686 crazyRead, crazyClose) < 0) {
687 fprintf(stderr, "failed to register Crazy handlers\n");
692 /************************************************************************
694 * SAX empty callbacks *
696 ************************************************************************/
698 unsigned long callbacks = 0;
701 * isStandaloneCallback:
702 * @ctxt: An XML parser context
704 * Is this document tagged standalone ?
709 isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
716 * hasInternalSubsetCallback:
717 * @ctxt: An XML parser context
719 * Does this document has an internal subset
724 hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
731 * hasExternalSubsetCallback:
732 * @ctxt: An XML parser context
734 * Does this document has an external subset
739 hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
746 * internalSubsetCallback:
747 * @ctxt: An XML parser context
749 * Does this document has an internal subset
752 internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
753 const xmlChar * name ATTRIBUTE_UNUSED,
754 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
755 const xmlChar * SystemID ATTRIBUTE_UNUSED)
762 * externalSubsetCallback:
763 * @ctxt: An XML parser context
765 * Does this document has an external subset
768 externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
769 const xmlChar * name ATTRIBUTE_UNUSED,
770 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
771 const xmlChar * SystemID ATTRIBUTE_UNUSED)
778 * resolveEntityCallback:
779 * @ctxt: An XML parser context
780 * @publicId: The public ID of the entity
781 * @systemId: The system ID of the entity
783 * Special entity resolver, better left to the parser, it has
784 * more context than the application layer.
785 * The default behaviour is to NOT resolve the entities, in that case
786 * the ENTITY_REF nodes are built in the structure (and the parameter
789 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
791 static xmlParserInputPtr
792 resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
793 const xmlChar * publicId ATTRIBUTE_UNUSED,
794 const xmlChar * systemId ATTRIBUTE_UNUSED)
802 * @ctxt: An XML parser context
803 * @name: The entity name
805 * Get an entity by name
807 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
810 getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
811 const xmlChar * name ATTRIBUTE_UNUSED)
818 * getParameterEntityCallback:
819 * @ctxt: An XML parser context
820 * @name: The entity name
822 * Get a parameter entity by name
824 * Returns the xmlParserInputPtr
827 getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
828 const xmlChar * name ATTRIBUTE_UNUSED)
836 * entityDeclCallback:
837 * @ctxt: An XML parser context
838 * @name: the entity name
839 * @type: the entity type
840 * @publicId: The public ID of the entity
841 * @systemId: The system ID of the entity
842 * @content: the entity value (without processing).
844 * An entity definition has been parsed
847 entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
848 const xmlChar * name ATTRIBUTE_UNUSED,
849 int type ATTRIBUTE_UNUSED,
850 const xmlChar * publicId ATTRIBUTE_UNUSED,
851 const xmlChar * systemId ATTRIBUTE_UNUSED,
852 xmlChar * content ATTRIBUTE_UNUSED)
859 * attributeDeclCallback:
860 * @ctxt: An XML parser context
861 * @name: the attribute name
862 * @type: the attribute type
864 * An attribute definition has been parsed
867 attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
868 const xmlChar * elem ATTRIBUTE_UNUSED,
869 const xmlChar * name ATTRIBUTE_UNUSED,
870 int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
871 const xmlChar * defaultValue ATTRIBUTE_UNUSED,
872 xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
879 * elementDeclCallback:
880 * @ctxt: An XML parser context
881 * @name: the element name
882 * @type: the element type
883 * @content: the element value (without processing).
885 * An element definition has been parsed
888 elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
889 const xmlChar * name ATTRIBUTE_UNUSED,
890 int type ATTRIBUTE_UNUSED,
891 xmlElementContentPtr content ATTRIBUTE_UNUSED)
898 * notationDeclCallback:
899 * @ctxt: An XML parser context
900 * @name: The name of the notation
901 * @publicId: The public ID of the entity
902 * @systemId: The system ID of the entity
904 * What to do when a notation declaration has been parsed.
907 notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
908 const xmlChar * name ATTRIBUTE_UNUSED,
909 const xmlChar * publicId ATTRIBUTE_UNUSED,
910 const xmlChar * systemId ATTRIBUTE_UNUSED)
917 * unparsedEntityDeclCallback:
918 * @ctxt: An XML parser context
919 * @name: The name of the entity
920 * @publicId: The public ID of the entity
921 * @systemId: The system ID of the entity
922 * @notationName: the name of the notation
924 * What to do when an unparsed entity declaration is parsed
927 unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
928 const xmlChar * name ATTRIBUTE_UNUSED,
929 const xmlChar * publicId ATTRIBUTE_UNUSED,
930 const xmlChar * systemId ATTRIBUTE_UNUSED,
931 const xmlChar * notationName ATTRIBUTE_UNUSED)
938 * setDocumentLocatorCallback:
939 * @ctxt: An XML parser context
940 * @loc: A SAX Locator
942 * Receive the document locator at startup, actually xmlDefaultSAXLocator
943 * Everything is available on the context, so this is useless in our case.
946 setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
947 xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
954 * startDocumentCallback:
955 * @ctxt: An XML parser context
957 * called when the document start being processed.
960 startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
967 * endDocumentCallback:
968 * @ctxt: An XML parser context
970 * called when the document end has been detected.
973 endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
981 * startElementCallback:
982 * @ctxt: An XML parser context
983 * @name: The element name
985 * called when an opening tag has been processed.
988 startElementCallback(void *ctx ATTRIBUTE_UNUSED,
989 const xmlChar * name ATTRIBUTE_UNUSED,
990 const xmlChar ** atts ATTRIBUTE_UNUSED)
997 * endElementCallback:
998 * @ctxt: An XML parser context
999 * @name: The element name
1001 * called when the end of an element has been detected.
1004 endElementCallback(void *ctx ATTRIBUTE_UNUSED,
1005 const xmlChar * name ATTRIBUTE_UNUSED)
1013 * charactersCallback:
1014 * @ctxt: An XML parser context
1015 * @ch: a xmlChar string
1016 * @len: the number of xmlChar
1018 * receiving some chars from the parser.
1019 * Question: how much at a time ???
1022 charactersCallback(void *ctx ATTRIBUTE_UNUSED,
1023 const xmlChar * ch ATTRIBUTE_UNUSED,
1024 int len ATTRIBUTE_UNUSED)
1031 * referenceCallback:
1032 * @ctxt: An XML parser context
1033 * @name: The entity name
1035 * called when an entity reference is detected.
1038 referenceCallback(void *ctx ATTRIBUTE_UNUSED,
1039 const xmlChar * name ATTRIBUTE_UNUSED)
1046 * ignorableWhitespaceCallback:
1047 * @ctxt: An XML parser context
1048 * @ch: a xmlChar string
1049 * @start: the first char in the string
1050 * @len: the number of xmlChar
1052 * receiving some ignorable whitespaces from the parser.
1053 * Question: how much at a time ???
1056 ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
1057 const xmlChar * ch ATTRIBUTE_UNUSED,
1058 int len ATTRIBUTE_UNUSED)
1065 * processingInstructionCallback:
1066 * @ctxt: An XML parser context
1067 * @target: the target name
1068 * @data: the PI data's
1069 * @len: the number of xmlChar
1071 * A processing instruction has been parsed.
1074 processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
1075 const xmlChar * target ATTRIBUTE_UNUSED,
1076 const xmlChar * data ATTRIBUTE_UNUSED)
1083 * cdataBlockCallback:
1084 * @ctx: the user data (XML parser context)
1085 * @value: The pcdata content
1086 * @len: the block length
1088 * called when a pcdata block has been parsed
1091 cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
1092 const xmlChar * value ATTRIBUTE_UNUSED,
1093 int len ATTRIBUTE_UNUSED)
1101 * @ctxt: An XML parser context
1102 * @value: the comment content
1104 * A comment has been parsed.
1107 commentCallback(void *ctx ATTRIBUTE_UNUSED,
1108 const xmlChar * value ATTRIBUTE_UNUSED)
1116 * @ctxt: An XML parser context
1117 * @msg: the message to display/transmit
1118 * @...: extra parameters for the message display
1120 * Display and format a warning messages, gives file, line, position and
1123 static void XMLCDECL
1124 warningCallback(void *ctx ATTRIBUTE_UNUSED,
1125 const char *msg ATTRIBUTE_UNUSED, ...)
1133 * @ctxt: An XML parser context
1134 * @msg: the message to display/transmit
1135 * @...: extra parameters for the message display
1137 * Display and format a error messages, gives file, line, position and
1140 static void XMLCDECL
1141 errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
1149 * fatalErrorCallback:
1150 * @ctxt: An XML parser context
1151 * @msg: the message to display/transmit
1152 * @...: extra parameters for the message display
1154 * Display and format a fatalError messages, gives file, line, position and
1157 static void XMLCDECL
1158 fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
1159 const char *msg ATTRIBUTE_UNUSED, ...)
1166 * SAX2 specific callbacks
1170 * startElementNsCallback:
1171 * @ctxt: An XML parser context
1172 * @name: The element name
1174 * called when an opening tag has been processed.
1177 startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1178 const xmlChar * localname ATTRIBUTE_UNUSED,
1179 const xmlChar * prefix ATTRIBUTE_UNUSED,
1180 const xmlChar * URI ATTRIBUTE_UNUSED,
1181 int nb_namespaces ATTRIBUTE_UNUSED,
1182 const xmlChar ** namespaces ATTRIBUTE_UNUSED,
1183 int nb_attributes ATTRIBUTE_UNUSED,
1184 int nb_defaulted ATTRIBUTE_UNUSED,
1185 const xmlChar ** attributes ATTRIBUTE_UNUSED)
1192 * endElementCallback:
1193 * @ctxt: An XML parser context
1194 * @name: The element name
1196 * called when the end of an element has been detected.
1199 endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1200 const xmlChar * localname ATTRIBUTE_UNUSED,
1201 const xmlChar * prefix ATTRIBUTE_UNUSED,
1202 const xmlChar * URI ATTRIBUTE_UNUSED)
1208 static xmlSAXHandler callbackSAX2HandlerStruct = {
1209 internalSubsetCallback,
1210 isStandaloneCallback,
1211 hasInternalSubsetCallback,
1212 hasExternalSubsetCallback,
1213 resolveEntityCallback,
1216 notationDeclCallback,
1217 attributeDeclCallback,
1218 elementDeclCallback,
1219 unparsedEntityDeclCallback,
1220 setDocumentLocatorCallback,
1221 startDocumentCallback,
1222 endDocumentCallback,
1227 ignorableWhitespaceCallback,
1228 processingInstructionCallback,
1233 getParameterEntityCallback,
1235 externalSubsetCallback,
1238 startElementNsCallback,
1239 endElementNsCallback,
1243 static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
1245 /************************************************************************
1247 * The tests front-ends *
1249 ************************************************************************/
1253 * @filename: the file to parse
1254 * @max_size: size of the limit to test
1255 * @options: parsing options
1256 * @fail: should a failure be reported
1258 * Parse a memory generated file using SAX
1260 * Returns 0 in case of success, an error code otherwise
1263 saxTest(const char *filename, size_t limit, int options, int fail) {
1265 xmlParserCtxtPtr ctxt;
1267 xmlSAXHandlerPtr old_sax;
1272 ctxt = xmlNewParserCtxt();
1274 fprintf(stderr, "Failed to create parser context\n");
1277 old_sax = ctxt->sax;
1278 ctxt->sax = callbackSAX2Handler;
1279 ctxt->userData = NULL;
1280 doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
1283 fprintf(stderr, "SAX parsing generated a document !\n");
1286 } else if (ctxt->wellFormed == 0) {
1290 fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
1295 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1301 ctxt->sax = old_sax;
1302 xmlFreeParserCtxt(ctxt);
1306 #ifdef LIBXML_READER_ENABLED
1309 * @filename: the file to parse
1310 * @max_size: size of the limit to test
1311 * @options: parsing options
1312 * @fail: should a failure be reported
1314 * Parse a memory generated file using the xmlReader
1316 * Returns 0 in case of success, an error code otherwise
1319 readerTest(const char *filename, size_t limit, int options, int fail) {
1320 xmlTextReaderPtr reader;
1327 reader = xmlReaderForFile(filename , NULL, options);
1328 if (reader == NULL) {
1329 fprintf(stderr, "Failed to open '%s' test\n", filename);
1332 ret = xmlTextReaderRead(reader);
1334 ret = xmlTextReaderRead(reader);
1340 if (strncmp(filename, "crazy:", 6) == 0)
1341 fprintf(stderr, "Failed to parse '%s' %u\n",
1342 filename, crazy_indx);
1344 fprintf(stderr, "Failed to parse '%s' %lu\n",
1350 if (strncmp(filename, "crazy:", 6) == 0)
1351 fprintf(stderr, "Failed to get failure for '%s' %u\n",
1352 filename, crazy_indx);
1354 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1362 xmlFreeTextReader(reader);
1368 /************************************************************************
1370 * Tests descriptions *
1372 ************************************************************************/
1374 typedef int (*functest) (const char *filename, size_t limit, int options,
1377 typedef struct limitDesc limitDesc;
1378 typedef limitDesc *limitDescPtr;
1380 const char *name; /* the huge generator name */
1381 size_t limit; /* the limit to test */
1382 int options; /* extra parser options */
1383 int fail; /* whether the test should fail */
1386 static limitDesc limitDescriptions[] = {
1387 /* max length of a text node in content */
1388 {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1389 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1390 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1391 /* max length of a text node in content */
1392 {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1393 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1394 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1395 /* max length of a comment node */
1396 {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1397 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1398 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1399 /* max length of a PI node */
1400 {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1401 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1402 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1405 typedef struct testDesc testDesc;
1406 typedef testDesc *testDescPtr;
1408 const char *desc; /* descripton of the test */
1409 functest func; /* function implementing the test */
1413 testDesc testDescriptions[] = {
1414 { "Parsing of huge files with the sax parser", saxTest},
1415 /* { "Parsing of huge files with the tree parser", treeTest}, */
1416 #ifdef LIBXML_READER_ENABLED
1417 { "Parsing of huge files with the reader", readerTest},
1422 typedef struct testException testException;
1423 typedef testException *testExceptionPtr;
1424 struct testException {
1425 unsigned int test; /* the parser test number */
1426 unsigned int limit; /* the limit test number */
1427 int fail; /* new fail value or -1*/
1428 size_t size; /* new limit value or 0 */
1432 testException testExceptions[] = {
1433 /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1438 launchTests(testDescPtr tst, unsigned int test) {
1439 int res = 0, err = 0;
1444 if (tst == NULL) return(-1);
1446 for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1447 limit = limitDescriptions[i].limit;
1448 fail = limitDescriptions[i].fail;
1450 * Handle exceptions if any
1452 for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1453 if ((testExceptions[j].test == test) &&
1454 (testExceptions[j].limit == i)) {
1455 if (testExceptions[j].fail != -1)
1456 fail = testExceptions[j].fail;
1457 if (testExceptions[j].size != 0)
1458 limit = testExceptions[j].size;
1462 res = tst->func(limitDescriptions[i].name, limit,
1463 limitDescriptions[i].options, fail);
1474 runtest(unsigned int i) {
1476 int old_errors, old_tests, old_leaks;
1478 old_errors = nb_errors;
1479 old_tests = nb_tests;
1480 old_leaks = nb_leaks;
1481 if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1482 printf("## %s\n", testDescriptions[i].desc);
1483 res = launchTests(&testDescriptions[i], i);
1487 if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1488 printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1490 printf("Ran %d tests, %d errors, %d leaks\n",
1491 nb_tests - old_tests,
1492 nb_errors - old_errors,
1493 nb_leaks - old_leaks);
1499 launchCrazySAX(unsigned int test, int fail) {
1500 int res = 0, err = 0;
1504 res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1509 if (tests_quiet == 0)
1510 fprintf(stderr, "%c", crazy[test]);
1516 launchCrazy(unsigned int test, int fail) {
1517 int res = 0, err = 0;
1521 res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1526 if (tests_quiet == 0)
1527 fprintf(stderr, "%c", crazy[test]);
1532 static int get_crazy_fail(int test) {
1534 * adding 1000000 of character 'a' leads to parser failure mostly
1535 * everywhere except in those special spots. Need to be updated
1536 * each time crazy is updated
1539 if ((test == 44) || /* PI in Misc */
1540 ((test >= 50) && (test <= 55)) || /* Comment in Misc */
1541 (test == 79) || /* PI in DTD */
1542 ((test >= 85) && (test <= 90)) || /* Comment in DTD */
1543 (test == 154) || /* PI in Misc */
1544 ((test >= 160) && (test <= 165)) || /* Comment in Misc */
1545 ((test >= 178) && (test <= 181)) || /* attribute value */
1546 (test == 183) || /* Text */
1547 (test == 189) || /* PI in Content */
1548 (test == 191) || /* Text */
1549 ((test >= 195) && (test <= 200)) || /* Comment in Content */
1550 ((test >= 203) && (test <= 206)) || /* Text */
1551 (test == 215) || (test == 216) || /* in CDATA */
1552 (test == 219) || /* Text */
1553 (test == 231) || /* PI in Misc */
1554 ((test >= 237) && (test <= 242))) /* Comment in Misc */
1561 int ret = 0, res = 0;
1562 int old_errors, old_tests, old_leaks;
1565 old_errors = nb_errors;
1566 old_tests = nb_tests;
1567 old_leaks = nb_leaks;
1568 if (tests_quiet == 0) {
1569 printf("## Crazy tests on reader\n");
1571 for (i = 0;i < strlen(crazy);i++) {
1572 res += launchCrazy(i, get_crazy_fail(i));
1576 if (tests_quiet == 0) {
1577 printf("\n## Crazy tests on SAX\n");
1579 for (i = 0;i < strlen(crazy);i++) {
1580 res += launchCrazySAX(i, get_crazy_fail(i));
1584 if (tests_quiet == 0)
1585 fprintf(stderr, "\n");
1587 if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1588 printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1590 printf("Ran %d tests, %d errors, %d leaks\n",
1591 nb_tests - old_tests,
1592 nb_errors - old_errors,
1593 nb_leaks - old_leaks);
1600 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1605 initializeLibxml2();
1607 for (a = 1; a < argc;a++) {
1608 if (!strcmp(argv[a], "-v"))
1610 else if (!strcmp(argv[a], "-quiet"))
1612 else if (!strcmp(argv[a], "-crazy"))
1616 for (i = 0; testDescriptions[i].func != NULL; i++) {
1621 if ((nb_errors == 0) && (nb_leaks == 0)) {
1623 printf("Total %d tests, no errors\n",
1627 printf("Total %d tests, %d errors, %d leaks\n",
1628 nb_tests, nb_errors, nb_leaks);