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.
16 #if !defined(_WIN32) || defined(__CYGWIN__)
20 #include <sys/types.h>
25 #include <libxml/parser.h>
26 #include <libxml/parserInternals.h>
27 #include <libxml/tree.h>
28 #include <libxml/uri.h>
29 #ifdef LIBXML_READER_ENABLED
30 #include <libxml/xmlreader.h>
33 static int verbose = 0;
34 static int tests_quiet = 0;
36 /************************************************************************
40 ************************************************************************/
42 /* maximum time for one parsing before declaring a timeout */
43 #define MAX_TIME 2 /* seconds */
48 static void reset_timout(void) {
53 static int check_time(void) {
54 clock_t tnow = clock();
55 if (((tnow - t0) / CLOCKS_PER_SEC) > MAX_TIME) {
62 /************************************************************************
64 * Huge document generator *
66 ************************************************************************/
68 #include <libxml/xmlIO.h>
71 * Huge documents are built using fixed start and end chunks
72 * and filling between the two an unconventional amount of char data
74 typedef struct hugeTest hugeTest;
75 typedef hugeTest *hugeTestPtr;
77 const char *description;
83 static struct hugeTest hugeTests[] = {
84 { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
85 { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
86 { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
87 { "Huge PI node", "huge:piNode", "<foo><?bar ", "?></foo>" },
90 static const char *current;
92 static unsigned int currentTest = 0;
93 static int instate = 0;
97 * @URI: an URI to test
99 * Check for an huge: query
101 * Returns 1 if yes and 0 if another Input module should be used
104 hugeMatch(const char * URI) {
105 if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
112 * @URI: an URI to test
114 * Return a pointer to the huge: query handler, in this example simply
115 * the current pointer...
117 * Returns an Input context or NULL in case or error
120 hugeOpen(const char * URI) {
121 if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
124 for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
126 if (!strcmp(hugeTests[currentTest].name, URI))
132 rlen = strlen(hugeTests[currentTest].start);
133 current = hugeTests[currentTest].start;
135 return((void *) current);
140 * @context: the read context
142 * Close the huge: query handler
144 * Returns 0 or -1 in case of error
147 hugeClose(void * context) {
148 if (context == NULL) return(-1);
149 fprintf(stderr, "\n");
155 char filling[CHUNK + 1];
157 static void fillFilling(void) {
160 for (i = 0;i < CHUNK;i++) {
166 size_t maxlen = 64 * 1024 * 1024;
172 * @context: the read context
173 * @buffer: where to store data
174 * @len: number of bytes to read
176 * Implement an huge: query read.
178 * Returns the number of bytes read or -1 in case of error
181 hugeRead(void *context, char *buffer, int len)
183 if ((context == NULL) || (buffer == NULL) || (len < 0))
190 memcpy(buffer, current, len);
193 dotlen = maxlen / 10;
195 memcpy(buffer, current, len);
199 } else if (instate == 2) {
203 memcpy(buffer, current, len);
207 memcpy(buffer, current, len);
211 } else if (instate == 1) {
212 if (len > CHUNK) len = CHUNK;
213 memcpy(buffer, &filling[0], len);
215 if (curlen >= maxlen) {
216 rlen = strlen(hugeTests[currentTest].end);
217 current = hugeTests[currentTest].end;
220 if (curlen > dotlen) {
221 fprintf(stderr, ".");
222 dotlen += maxlen / 10;
230 /************************************************************************
232 * Crazy document generator *
234 ************************************************************************/
236 unsigned int crazy_indx = 0;
238 const char *crazy = "<?xml version='1.0' encoding='UTF-8'?>\
244 <!ELEMENT foo (#PCDATA)>\
245 <!ELEMENT p (#PCDATA|emph)* >\
260 * @URI: an URI to test
262 * Check for a crazy: query
264 * Returns 1 if yes and 0 if another Input module should be used
267 crazyMatch(const char * URI) {
268 if ((URI != NULL) && (!strncmp(URI, "crazy:", 6)))
275 * @URI: an URI to test
277 * Return a pointer to the crazy: query handler, in this example simply
278 * the current pointer...
280 * Returns an Input context or NULL in case or error
283 crazyOpen(const char * URI) {
284 if ((URI == NULL) || (strncmp(URI, "crazy:", 6)))
287 if (crazy_indx > strlen(crazy))
293 return((void *) current);
298 * @context: the read context
300 * Close the crazy: query handler
302 * Returns 0 or -1 in case of error
305 crazyClose(void * context) {
306 if (context == NULL) return(-1);
313 * @context: the read context
314 * @buffer: where to store data
315 * @len: number of bytes to read
317 * Implement an crazy: query read.
319 * Returns the number of bytes read or -1 in case of error
322 crazyRead(void *context, char *buffer, int len)
324 if ((context == NULL) || (buffer == NULL) || (len < 0))
327 if ((check_time() <= 0) && (instate == 1)) {
328 fprintf(stderr, "\ntimeout in crazy(%d)\n", crazy_indx);
329 rlen = strlen(crazy) - crazy_indx;
330 current = &crazy[crazy_indx];
337 memcpy(buffer, current, len);
341 memcpy(buffer, current, len);
345 } else if (instate == 2) {
349 memcpy(buffer, current, len);
353 memcpy(buffer, current, len);
357 } else if (instate == 1) {
358 if (len > CHUNK) len = CHUNK;
359 memcpy(buffer, &filling[0], len);
361 if (curlen >= maxlen) {
362 rlen = strlen(crazy) - crazy_indx;
363 current = &crazy[crazy_indx];
370 /************************************************************************
372 * Libxml2 specific routines *
374 ************************************************************************/
376 static int nb_tests = 0;
377 static int nb_errors = 0;
378 static int nb_leaks = 0;
379 static int extraMemoryFromResolver = 0;
382 * We need to trap calls to the resolver to not account memory for the catalog
383 * which is shared to the current running test. We also don't want to have
384 * network downloads modifying tests.
386 static xmlParserInputPtr
387 testExternalEntityLoader(const char *URL, const char *ID,
388 xmlParserCtxtPtr ctxt) {
389 xmlParserInputPtr ret;
390 int memused = xmlMemUsed();
392 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
393 extraMemoryFromResolver += xmlMemUsed() - memused;
399 * Trapping the error messages at the generic level to grab the equivalent of
400 * stderr messages on CLI tools.
402 static char testErrors[32769];
403 static int testErrorsSize = 0;
406 channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
410 if (testErrorsSize >= 32768)
413 res = vsnprintf(&testErrors[testErrorsSize],
414 32768 - testErrorsSize,
417 if (testErrorsSize + res >= 32768) {
419 testErrorsSize = 32768;
420 testErrors[testErrorsSize] = 0;
422 testErrorsSize += res;
424 testErrors[testErrorsSize] = 0;
428 * xmlParserPrintFileContext:
429 * @input: an xmlParserInputPtr input
431 * Displays current context within the input content for error tracking
435 xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
436 xmlGenericErrorFunc chanl, void *data ) {
437 const xmlChar *cur, *base;
438 unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */
439 xmlChar content[81]; /* space for 80 chars + line terminator */
442 if (input == NULL) return;
445 /* skip backwards over any end-of-lines */
446 while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
450 /* search backwards for beginning-of-line (to max buff size) */
451 while ((n++ < (sizeof(content)-1)) && (cur > base) &&
452 (*(cur) != '\n') && (*(cur) != '\r'))
454 if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
455 /* calculate the error position in terms of the current position */
456 col = input->cur - cur;
457 /* search forward for end-of-line (to max buff size) */
460 /* copy selected text to our buffer */
461 while ((*cur != 0) && (*(cur) != '\n') &&
462 (*(cur) != '\r') && (n < sizeof(content)-1)) {
467 /* print out the selected text */
468 chanl(data ,"%s\n", content);
469 /* create blank line with problem pointer */
472 /* (leave buffer space for pointer + line terminator) */
473 while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
480 chanl(data ,"%s\n", content);
484 testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) {
491 const xmlChar *name = NULL;
494 xmlParserInputPtr input = NULL;
495 xmlParserInputPtr cur = NULL;
496 xmlParserCtxtPtr ctxt = NULL;
504 domain = err->domain;
507 if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
508 (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
509 (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
514 if (code == XML_ERR_OK)
517 if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
521 * Maintain the compatibility with the legacy error handling
525 if ((input != NULL) && (input->filename == NULL) &&
526 (ctxt->inputNr > 1)) {
528 input = ctxt->inputTab[ctxt->inputNr - 2];
532 channel(data, "%s:%d: ", input->filename, input->line);
533 else if ((line != 0) && (domain == XML_FROM_PARSER))
534 channel(data, "Entity: line %d: ", input->line);
538 channel(data, "%s:%d: ", file, line);
539 else if ((line != 0) && (domain == XML_FROM_PARSER))
540 channel(data, "Entity: line %d: ", line);
543 channel(data, "element %s: ", name);
545 if (code == XML_ERR_OK)
548 case XML_FROM_PARSER:
549 channel(data, "parser ");
551 case XML_FROM_NAMESPACE:
552 channel(data, "namespace ");
556 channel(data, "validity ");
559 channel(data, "HTML parser ");
561 case XML_FROM_MEMORY:
562 channel(data, "memory ");
564 case XML_FROM_OUTPUT:
565 channel(data, "output ");
568 channel(data, "I/O ");
570 case XML_FROM_XINCLUDE:
571 channel(data, "XInclude ");
574 channel(data, "XPath ");
576 case XML_FROM_XPOINTER:
577 channel(data, "parser ");
579 case XML_FROM_REGEXP:
580 channel(data, "regexp ");
582 case XML_FROM_MODULE:
583 channel(data, "module ");
585 case XML_FROM_SCHEMASV:
586 channel(data, "Schemas validity ");
588 case XML_FROM_SCHEMASP:
589 channel(data, "Schemas parser ");
591 case XML_FROM_RELAXNGP:
592 channel(data, "Relax-NG parser ");
594 case XML_FROM_RELAXNGV:
595 channel(data, "Relax-NG validity ");
597 case XML_FROM_CATALOG:
598 channel(data, "Catalog ");
601 channel(data, "C14N ");
604 channel(data, "XSLT ");
609 if (code == XML_ERR_OK)
615 case XML_ERR_WARNING:
616 channel(data, "warning : ");
619 channel(data, "error : ");
622 channel(data, "error : ");
625 if (code == XML_ERR_OK)
629 len = xmlStrlen((const xmlChar *)str);
630 if ((len > 0) && (str[len - 1] != '\n'))
631 channel(data, "%s\n", str);
633 channel(data, "%s", str);
635 channel(data, "%s\n", "out of memory error");
637 if (code == XML_ERR_OK)
641 xmlParserPrintFileContextInternal(input, channel, data);
644 channel(data, "%s:%d: \n", cur->filename, cur->line);
645 else if ((line != 0) && (domain == XML_FROM_PARSER))
646 channel(data, "Entity: line %d: \n", cur->line);
647 xmlParserPrintFileContextInternal(cur, channel, data);
650 if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
652 (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
656 channel(data, "%s\n", err->str1);
657 for (i=0;i < err->int1;i++)
661 channel(data, "%s\n", buf);
666 initializeLibxml2(void) {
667 xmlGetWarningsDefaultValue = 0;
668 xmlPedanticParserDefault(0);
670 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
672 xmlSetExternalEntityLoader(testExternalEntityLoader);
673 xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
675 * register the new I/O handlers
677 if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
678 hugeRead, hugeClose) < 0) {
679 fprintf(stderr, "failed to register Huge handlers\n");
682 if (xmlRegisterInputCallbacks(crazyMatch, crazyOpen,
683 crazyRead, crazyClose) < 0) {
684 fprintf(stderr, "failed to register Crazy handlers\n");
689 /************************************************************************
691 * SAX empty callbacks *
693 ************************************************************************/
695 unsigned long callbacks = 0;
698 * isStandaloneCallback:
699 * @ctxt: An XML parser context
701 * Is this document tagged standalone ?
706 isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
713 * hasInternalSubsetCallback:
714 * @ctxt: An XML parser context
716 * Does this document has an internal subset
721 hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
728 * hasExternalSubsetCallback:
729 * @ctxt: An XML parser context
731 * Does this document has an external subset
736 hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
743 * internalSubsetCallback:
744 * @ctxt: An XML parser context
746 * Does this document has an internal subset
749 internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
750 const xmlChar * name ATTRIBUTE_UNUSED,
751 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
752 const xmlChar * SystemID ATTRIBUTE_UNUSED)
759 * externalSubsetCallback:
760 * @ctxt: An XML parser context
762 * Does this document has an external subset
765 externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
766 const xmlChar * name ATTRIBUTE_UNUSED,
767 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
768 const xmlChar * SystemID ATTRIBUTE_UNUSED)
775 * resolveEntityCallback:
776 * @ctxt: An XML parser context
777 * @publicId: The public ID of the entity
778 * @systemId: The system ID of the entity
780 * Special entity resolver, better left to the parser, it has
781 * more context than the application layer.
782 * The default behaviour is to NOT resolve the entities, in that case
783 * the ENTITY_REF nodes are built in the structure (and the parameter
786 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
788 static xmlParserInputPtr
789 resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
790 const xmlChar * publicId ATTRIBUTE_UNUSED,
791 const xmlChar * systemId ATTRIBUTE_UNUSED)
799 * @ctxt: An XML parser context
800 * @name: The entity name
802 * Get an entity by name
804 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
807 getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
808 const xmlChar * name ATTRIBUTE_UNUSED)
815 * getParameterEntityCallback:
816 * @ctxt: An XML parser context
817 * @name: The entity name
819 * Get a parameter entity by name
821 * Returns the xmlParserInputPtr
824 getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
825 const xmlChar * name ATTRIBUTE_UNUSED)
833 * entityDeclCallback:
834 * @ctxt: An XML parser context
835 * @name: the entity name
836 * @type: the entity type
837 * @publicId: The public ID of the entity
838 * @systemId: The system ID of the entity
839 * @content: the entity value (without processing).
841 * An entity definition has been parsed
844 entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
845 const xmlChar * name ATTRIBUTE_UNUSED,
846 int type ATTRIBUTE_UNUSED,
847 const xmlChar * publicId ATTRIBUTE_UNUSED,
848 const xmlChar * systemId ATTRIBUTE_UNUSED,
849 xmlChar * content ATTRIBUTE_UNUSED)
856 * attributeDeclCallback:
857 * @ctxt: An XML parser context
858 * @name: the attribute name
859 * @type: the attribute type
861 * An attribute definition has been parsed
864 attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
865 const xmlChar * elem ATTRIBUTE_UNUSED,
866 const xmlChar * name ATTRIBUTE_UNUSED,
867 int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
868 const xmlChar * defaultValue ATTRIBUTE_UNUSED,
869 xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
876 * elementDeclCallback:
877 * @ctxt: An XML parser context
878 * @name: the element name
879 * @type: the element type
880 * @content: the element value (without processing).
882 * An element definition has been parsed
885 elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
886 const xmlChar * name ATTRIBUTE_UNUSED,
887 int type ATTRIBUTE_UNUSED,
888 xmlElementContentPtr content ATTRIBUTE_UNUSED)
895 * notationDeclCallback:
896 * @ctxt: An XML parser context
897 * @name: The name of the notation
898 * @publicId: The public ID of the entity
899 * @systemId: The system ID of the entity
901 * What to do when a notation declaration has been parsed.
904 notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
905 const xmlChar * name ATTRIBUTE_UNUSED,
906 const xmlChar * publicId ATTRIBUTE_UNUSED,
907 const xmlChar * systemId ATTRIBUTE_UNUSED)
914 * unparsedEntityDeclCallback:
915 * @ctxt: An XML parser context
916 * @name: The name of the entity
917 * @publicId: The public ID of the entity
918 * @systemId: The system ID of the entity
919 * @notationName: the name of the notation
921 * What to do when an unparsed entity declaration is parsed
924 unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
925 const xmlChar * name ATTRIBUTE_UNUSED,
926 const xmlChar * publicId ATTRIBUTE_UNUSED,
927 const xmlChar * systemId ATTRIBUTE_UNUSED,
928 const xmlChar * notationName ATTRIBUTE_UNUSED)
935 * setDocumentLocatorCallback:
936 * @ctxt: An XML parser context
937 * @loc: A SAX Locator
939 * Receive the document locator at startup, actually xmlDefaultSAXLocator
940 * Everything is available on the context, so this is useless in our case.
943 setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
944 xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
951 * startDocumentCallback:
952 * @ctxt: An XML parser context
954 * called when the document start being processed.
957 startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
964 * endDocumentCallback:
965 * @ctxt: An XML parser context
967 * called when the document end has been detected.
970 endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
978 * startElementCallback:
979 * @ctxt: An XML parser context
980 * @name: The element name
982 * called when an opening tag has been processed.
985 startElementCallback(void *ctx ATTRIBUTE_UNUSED,
986 const xmlChar * name ATTRIBUTE_UNUSED,
987 const xmlChar ** atts ATTRIBUTE_UNUSED)
994 * endElementCallback:
995 * @ctxt: An XML parser context
996 * @name: The element name
998 * called when the end of an element has been detected.
1001 endElementCallback(void *ctx ATTRIBUTE_UNUSED,
1002 const xmlChar * name ATTRIBUTE_UNUSED)
1010 * charactersCallback:
1011 * @ctxt: An XML parser context
1012 * @ch: a xmlChar string
1013 * @len: the number of xmlChar
1015 * receiving some chars from the parser.
1016 * Question: how much at a time ???
1019 charactersCallback(void *ctx ATTRIBUTE_UNUSED,
1020 const xmlChar * ch ATTRIBUTE_UNUSED,
1021 int len ATTRIBUTE_UNUSED)
1028 * referenceCallback:
1029 * @ctxt: An XML parser context
1030 * @name: The entity name
1032 * called when an entity reference is detected.
1035 referenceCallback(void *ctx ATTRIBUTE_UNUSED,
1036 const xmlChar * name ATTRIBUTE_UNUSED)
1043 * ignorableWhitespaceCallback:
1044 * @ctxt: An XML parser context
1045 * @ch: a xmlChar string
1046 * @start: the first char in the string
1047 * @len: the number of xmlChar
1049 * receiving some ignorable whitespaces from the parser.
1050 * Question: how much at a time ???
1053 ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
1054 const xmlChar * ch ATTRIBUTE_UNUSED,
1055 int len ATTRIBUTE_UNUSED)
1062 * processingInstructionCallback:
1063 * @ctxt: An XML parser context
1064 * @target: the target name
1065 * @data: the PI data's
1066 * @len: the number of xmlChar
1068 * A processing instruction has been parsed.
1071 processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
1072 const xmlChar * target ATTRIBUTE_UNUSED,
1073 const xmlChar * data ATTRIBUTE_UNUSED)
1080 * cdataBlockCallback:
1081 * @ctx: the user data (XML parser context)
1082 * @value: The pcdata content
1083 * @len: the block length
1085 * called when a pcdata block has been parsed
1088 cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
1089 const xmlChar * value ATTRIBUTE_UNUSED,
1090 int len ATTRIBUTE_UNUSED)
1098 * @ctxt: An XML parser context
1099 * @value: the comment content
1101 * A comment has been parsed.
1104 commentCallback(void *ctx ATTRIBUTE_UNUSED,
1105 const xmlChar * value ATTRIBUTE_UNUSED)
1113 * @ctxt: An XML parser context
1114 * @msg: the message to display/transmit
1115 * @...: extra parameters for the message display
1117 * Display and format a warning messages, gives file, line, position and
1120 static void XMLCDECL
1121 warningCallback(void *ctx ATTRIBUTE_UNUSED,
1122 const char *msg ATTRIBUTE_UNUSED, ...)
1130 * @ctxt: An XML parser context
1131 * @msg: the message to display/transmit
1132 * @...: extra parameters for the message display
1134 * Display and format a error messages, gives file, line, position and
1137 static void XMLCDECL
1138 errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
1146 * fatalErrorCallback:
1147 * @ctxt: An XML parser context
1148 * @msg: the message to display/transmit
1149 * @...: extra parameters for the message display
1151 * Display and format a fatalError messages, gives file, line, position and
1154 static void XMLCDECL
1155 fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
1156 const char *msg ATTRIBUTE_UNUSED, ...)
1163 * SAX2 specific callbacks
1167 * startElementNsCallback:
1168 * @ctxt: An XML parser context
1169 * @name: The element name
1171 * called when an opening tag has been processed.
1174 startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1175 const xmlChar * localname ATTRIBUTE_UNUSED,
1176 const xmlChar * prefix ATTRIBUTE_UNUSED,
1177 const xmlChar * URI ATTRIBUTE_UNUSED,
1178 int nb_namespaces ATTRIBUTE_UNUSED,
1179 const xmlChar ** namespaces ATTRIBUTE_UNUSED,
1180 int nb_attributes ATTRIBUTE_UNUSED,
1181 int nb_defaulted ATTRIBUTE_UNUSED,
1182 const xmlChar ** attributes ATTRIBUTE_UNUSED)
1189 * endElementCallback:
1190 * @ctxt: An XML parser context
1191 * @name: The element name
1193 * called when the end of an element has been detected.
1196 endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1197 const xmlChar * localname ATTRIBUTE_UNUSED,
1198 const xmlChar * prefix ATTRIBUTE_UNUSED,
1199 const xmlChar * URI ATTRIBUTE_UNUSED)
1205 static xmlSAXHandler callbackSAX2HandlerStruct = {
1206 internalSubsetCallback,
1207 isStandaloneCallback,
1208 hasInternalSubsetCallback,
1209 hasExternalSubsetCallback,
1210 resolveEntityCallback,
1213 notationDeclCallback,
1214 attributeDeclCallback,
1215 elementDeclCallback,
1216 unparsedEntityDeclCallback,
1217 setDocumentLocatorCallback,
1218 startDocumentCallback,
1219 endDocumentCallback,
1224 ignorableWhitespaceCallback,
1225 processingInstructionCallback,
1230 getParameterEntityCallback,
1232 externalSubsetCallback,
1235 startElementNsCallback,
1236 endElementNsCallback,
1240 static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
1242 /************************************************************************
1244 * The tests front-ends *
1246 ************************************************************************/
1250 * @filename: the file to parse
1251 * @max_size: size of the limit to test
1252 * @options: parsing options
1253 * @fail: should a failure be reported
1255 * Parse a memory generated file using SAX
1257 * Returns 0 in case of success, an error code otherwise
1260 saxTest(const char *filename, size_t limit, int options, int fail) {
1262 xmlParserCtxtPtr ctxt;
1264 xmlSAXHandlerPtr old_sax;
1269 ctxt = xmlNewParserCtxt();
1271 fprintf(stderr, "Failed to create parser context\n");
1274 old_sax = ctxt->sax;
1275 ctxt->sax = callbackSAX2Handler;
1276 ctxt->userData = NULL;
1277 doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
1280 fprintf(stderr, "SAX parsing generated a document !\n");
1283 } else if (ctxt->wellFormed == 0) {
1287 fprintf(stderr, "Failed to parse '%s' %lu\n", filename,
1288 (unsigned long) limit);
1293 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1294 filename, (unsigned long) limit);
1299 ctxt->sax = old_sax;
1300 xmlFreeParserCtxt(ctxt);
1304 #ifdef LIBXML_READER_ENABLED
1307 * @filename: the file to parse
1308 * @max_size: size of the limit to test
1309 * @options: parsing options
1310 * @fail: should a failure be reported
1312 * Parse a memory generated file using the xmlReader
1314 * Returns 0 in case of success, an error code otherwise
1317 readerTest(const char *filename, size_t limit, int options, int fail) {
1318 xmlTextReaderPtr reader;
1325 reader = xmlReaderForFile(filename , NULL, options);
1326 if (reader == NULL) {
1327 fprintf(stderr, "Failed to open '%s' test\n", filename);
1330 ret = xmlTextReaderRead(reader);
1332 ret = xmlTextReaderRead(reader);
1338 if (strncmp(filename, "crazy:", 6) == 0)
1339 fprintf(stderr, "Failed to parse '%s' %u\n",
1340 filename, crazy_indx);
1342 fprintf(stderr, "Failed to parse '%s' %lu\n",
1343 filename, (unsigned long) limit);
1348 if (strncmp(filename, "crazy:", 6) == 0)
1349 fprintf(stderr, "Failed to get failure for '%s' %u\n",
1350 filename, crazy_indx);
1352 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1353 filename, (unsigned long) limit);
1360 xmlFreeTextReader(reader);
1366 /************************************************************************
1368 * Tests descriptions *
1370 ************************************************************************/
1372 typedef int (*functest) (const char *filename, size_t limit, int options,
1375 typedef struct limitDesc limitDesc;
1376 typedef limitDesc *limitDescPtr;
1378 const char *name; /* the huge generator name */
1379 size_t limit; /* the limit to test */
1380 int options; /* extra parser options */
1381 int fail; /* whether the test should fail */
1384 static limitDesc limitDescriptions[] = {
1385 /* max length of a text node in content */
1386 {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1387 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1388 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1389 /* max length of a text node in content */
1390 {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1391 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1392 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1393 /* max length of a comment node */
1394 {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1395 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1396 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1397 /* max length of a PI node */
1398 {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1399 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1400 {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1403 typedef struct testDesc testDesc;
1404 typedef testDesc *testDescPtr;
1406 const char *desc; /* descripton of the test */
1407 functest func; /* function implementing the test */
1411 testDesc testDescriptions[] = {
1412 { "Parsing of huge files with the sax parser", saxTest},
1413 /* { "Parsing of huge files with the tree parser", treeTest}, */
1414 #ifdef LIBXML_READER_ENABLED
1415 { "Parsing of huge files with the reader", readerTest},
1420 typedef struct testException testException;
1421 typedef testException *testExceptionPtr;
1422 struct testException {
1423 unsigned int test; /* the parser test number */
1424 unsigned int limit; /* the limit test number */
1425 int fail; /* new fail value or -1*/
1426 size_t size; /* new limit value or 0 */
1430 testException testExceptions[] = {
1431 /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1436 launchTests(testDescPtr tst, unsigned int test) {
1437 int res = 0, err = 0;
1442 if (tst == NULL) return(-1);
1444 for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1445 limit = limitDescriptions[i].limit;
1446 fail = limitDescriptions[i].fail;
1448 * Handle exceptions if any
1450 for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1451 if ((testExceptions[j].test == test) &&
1452 (testExceptions[j].limit == i)) {
1453 if (testExceptions[j].fail != -1)
1454 fail = testExceptions[j].fail;
1455 if (testExceptions[j].size != 0)
1456 limit = testExceptions[j].size;
1460 res = tst->func(limitDescriptions[i].name, limit,
1461 limitDescriptions[i].options, fail);
1472 runtest(unsigned int i) {
1474 int old_errors, old_tests, old_leaks;
1476 old_errors = nb_errors;
1477 old_tests = nb_tests;
1478 old_leaks = nb_leaks;
1479 if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1480 printf("## %s\n", testDescriptions[i].desc);
1481 res = launchTests(&testDescriptions[i], i);
1485 if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1486 printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1488 printf("Ran %d tests, %d errors, %d leaks\n",
1489 nb_tests - old_tests,
1490 nb_errors - old_errors,
1491 nb_leaks - old_leaks);
1497 launchCrazySAX(unsigned int test, int fail) {
1498 int res = 0, err = 0;
1502 res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1507 if (tests_quiet == 0)
1508 fprintf(stderr, "%c", crazy[test]);
1513 #ifdef LIBXML_READER_ENABLED
1515 launchCrazy(unsigned int test, int fail) {
1516 int res = 0, err = 0;
1520 res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1525 if (tests_quiet == 0)
1526 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;
1569 #ifdef LIBXML_READER_ENABLED
1570 if (tests_quiet == 0) {
1571 printf("## Crazy tests on reader\n");
1573 for (i = 0;i < strlen(crazy);i++) {
1574 res += launchCrazy(i, get_crazy_fail(i));
1580 if (tests_quiet == 0) {
1581 printf("\n## Crazy tests on SAX\n");
1583 for (i = 0;i < strlen(crazy);i++) {
1584 res += launchCrazySAX(i, get_crazy_fail(i));
1588 if (tests_quiet == 0)
1589 fprintf(stderr, "\n");
1591 if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1592 printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1594 printf("Ran %d tests, %d errors, %d leaks\n",
1595 nb_tests - old_tests,
1596 nb_errors - old_errors,
1597 nb_leaks - old_leaks);
1604 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1609 initializeLibxml2();
1611 for (a = 1; a < argc;a++) {
1612 if (!strcmp(argv[a], "-v"))
1614 else if (!strcmp(argv[a], "-quiet"))
1616 else if (!strcmp(argv[a], "-crazy"))
1620 for (i = 0; testDescriptions[i].func != NULL; i++) {
1625 if ((nb_errors == 0) && (nb_leaks == 0)) {
1627 printf("Total %d tests, no errors\n",
1631 printf("Total %d tests, %d errors, %d leaks\n",
1632 nb_tests, nb_errors, nb_leaks);