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
, limit
);
1292 fprintf(stderr
, "Failed to get failure for '%s' %lu\n",
1298 ctxt
->sax
= old_sax
;
1299 xmlFreeParserCtxt(ctxt
);
1303 #ifdef LIBXML_READER_ENABLED
1306 * @filename: the file to parse
1307 * @max_size: size of the limit to test
1308 * @options: parsing options
1309 * @fail: should a failure be reported
1311 * Parse a memory generated file using the xmlReader
1313 * Returns 0 in case of success, an error code otherwise
1316 readerTest(const char *filename
, size_t limit
, int options
, int fail
) {
1317 xmlTextReaderPtr reader
;
1324 reader
= xmlReaderForFile(filename
, NULL
, options
);
1325 if (reader
== NULL
) {
1326 fprintf(stderr
, "Failed to open '%s' test\n", filename
);
1329 ret
= xmlTextReaderRead(reader
);
1331 ret
= xmlTextReaderRead(reader
);
1337 if (strncmp(filename
, "crazy:", 6) == 0)
1338 fprintf(stderr
, "Failed to parse '%s' %u\n",
1339 filename
, crazy_indx
);
1341 fprintf(stderr
, "Failed to parse '%s' %lu\n",
1347 if (strncmp(filename
, "crazy:", 6) == 0)
1348 fprintf(stderr
, "Failed to get failure for '%s' %u\n",
1349 filename
, crazy_indx
);
1351 fprintf(stderr
, "Failed to get failure for '%s' %lu\n",
1359 xmlFreeTextReader(reader
);
1365 /************************************************************************
1367 * Tests descriptions *
1369 ************************************************************************/
1371 typedef int (*functest
) (const char *filename
, size_t limit
, int options
,
1374 typedef struct limitDesc limitDesc
;
1375 typedef limitDesc
*limitDescPtr
;
1377 const char *name
; /* the huge generator name */
1378 size_t limit
; /* the limit to test */
1379 int options
; /* extra parser options */
1380 int fail
; /* whether the test should fail */
1383 static limitDesc limitDescriptions
[] = {
1384 /* max length of a text node in content */
1385 {"huge:textNode", XML_MAX_TEXT_LENGTH
- CHUNK
, 0, 0},
1386 {"huge:textNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, 0, 1},
1387 {"huge:textNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, XML_PARSE_HUGE
, 0},
1388 /* max length of a text node in content */
1389 {"huge:attrNode", XML_MAX_TEXT_LENGTH
- CHUNK
, 0, 0},
1390 {"huge:attrNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, 0, 1},
1391 {"huge:attrNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, XML_PARSE_HUGE
, 0},
1392 /* max length of a comment node */
1393 {"huge:commentNode", XML_MAX_TEXT_LENGTH
- CHUNK
, 0, 0},
1394 {"huge:commentNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, 0, 1},
1395 {"huge:commentNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, XML_PARSE_HUGE
, 0},
1396 /* max length of a PI node */
1397 {"huge:piNode", XML_MAX_TEXT_LENGTH
- CHUNK
, 0, 0},
1398 {"huge:piNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, 0, 1},
1399 {"huge:piNode", XML_MAX_TEXT_LENGTH
+ CHUNK
, XML_PARSE_HUGE
, 0},
1402 typedef struct testDesc testDesc
;
1403 typedef testDesc
*testDescPtr
;
1405 const char *desc
; /* descripton of the test */
1406 functest func
; /* function implementing the test */
1410 testDesc testDescriptions
[] = {
1411 { "Parsing of huge files with the sax parser", saxTest
},
1412 /* { "Parsing of huge files with the tree parser", treeTest}, */
1413 #ifdef LIBXML_READER_ENABLED
1414 { "Parsing of huge files with the reader", readerTest
},
1419 typedef struct testException testException
;
1420 typedef testException
*testExceptionPtr
;
1421 struct testException
{
1422 unsigned int test
; /* the parser test number */
1423 unsigned int limit
; /* the limit test number */
1424 int fail
; /* new fail value or -1*/
1425 size_t size
; /* new limit value or 0 */
1429 testException testExceptions
[] = {
1430 /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1435 launchTests(testDescPtr tst
, unsigned int test
) {
1436 int res
= 0, err
= 0;
1441 if (tst
== NULL
) return(-1);
1443 for (i
= 0;i
< sizeof(limitDescriptions
)/sizeof(limitDescriptions
[0]);i
++) {
1444 limit
= limitDescriptions
[i
].limit
;
1445 fail
= limitDescriptions
[i
].fail
;
1447 * Handle exceptions if any
1449 for (j
= 0;j
< sizeof(testExceptions
)/sizeof(testExceptions
[0]);j
++) {
1450 if ((testExceptions
[j
].test
== test
) &&
1451 (testExceptions
[j
].limit
== i
)) {
1452 if (testExceptions
[j
].fail
!= -1)
1453 fail
= testExceptions
[j
].fail
;
1454 if (testExceptions
[j
].size
!= 0)
1455 limit
= testExceptions
[j
].size
;
1459 res
= tst
->func(limitDescriptions
[i
].name
, limit
,
1460 limitDescriptions
[i
].options
, fail
);
1471 runtest(unsigned int i
) {
1473 int old_errors
, old_tests
, old_leaks
;
1475 old_errors
= nb_errors
;
1476 old_tests
= nb_tests
;
1477 old_leaks
= nb_leaks
;
1478 if ((tests_quiet
== 0) && (testDescriptions
[i
].desc
!= NULL
))
1479 printf("## %s\n", testDescriptions
[i
].desc
);
1480 res
= launchTests(&testDescriptions
[i
], i
);
1484 if ((nb_errors
== old_errors
) && (nb_leaks
== old_leaks
))
1485 printf("Ran %d tests, no errors\n", nb_tests
- old_tests
);
1487 printf("Ran %d tests, %d errors, %d leaks\n",
1488 nb_tests
- old_tests
,
1489 nb_errors
- old_errors
,
1490 nb_leaks
- old_leaks
);
1496 launchCrazySAX(unsigned int test
, int fail
) {
1497 int res
= 0, err
= 0;
1501 res
= saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT
- CHUNK
, 0, fail
);
1506 if (tests_quiet
== 0)
1507 fprintf(stderr
, "%c", crazy
[test
]);
1512 #ifdef LIBXML_READER_ENABLED
1514 launchCrazy(unsigned int test
, int fail
) {
1515 int res
= 0, err
= 0;
1519 res
= readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT
- CHUNK
, 0, fail
);
1524 if (tests_quiet
== 0)
1525 fprintf(stderr
, "%c", crazy
[test
]);
1531 static int get_crazy_fail(int test
) {
1533 * adding 1000000 of character 'a' leads to parser failure mostly
1534 * everywhere except in those special spots. Need to be updated
1535 * each time crazy is updated
1538 if ((test
== 44) || /* PI in Misc */
1539 ((test
>= 50) && (test
<= 55)) || /* Comment in Misc */
1540 (test
== 79) || /* PI in DTD */
1541 ((test
>= 85) && (test
<= 90)) || /* Comment in DTD */
1542 (test
== 154) || /* PI in Misc */
1543 ((test
>= 160) && (test
<= 165)) || /* Comment in Misc */
1544 ((test
>= 178) && (test
<= 181)) || /* attribute value */
1545 (test
== 183) || /* Text */
1546 (test
== 189) || /* PI in Content */
1547 (test
== 191) || /* Text */
1548 ((test
>= 195) && (test
<= 200)) || /* Comment in Content */
1549 ((test
>= 203) && (test
<= 206)) || /* Text */
1550 (test
== 215) || (test
== 216) || /* in CDATA */
1551 (test
== 219) || /* Text */
1552 (test
== 231) || /* PI in Misc */
1553 ((test
>= 237) && (test
<= 242))) /* Comment in Misc */
1560 int ret
= 0, res
= 0;
1561 int old_errors
, old_tests
, old_leaks
;
1564 old_errors
= nb_errors
;
1565 old_tests
= nb_tests
;
1566 old_leaks
= nb_leaks
;
1568 #ifdef LIBXML_READER_ENABLED
1569 if (tests_quiet
== 0) {
1570 printf("## Crazy tests on reader\n");
1572 for (i
= 0;i
< strlen(crazy
);i
++) {
1573 res
+= launchCrazy(i
, get_crazy_fail(i
));
1579 if (tests_quiet
== 0) {
1580 printf("\n## Crazy tests on SAX\n");
1582 for (i
= 0;i
< strlen(crazy
);i
++) {
1583 res
+= launchCrazySAX(i
, get_crazy_fail(i
));
1587 if (tests_quiet
== 0)
1588 fprintf(stderr
, "\n");
1590 if ((nb_errors
== old_errors
) && (nb_leaks
== old_leaks
))
1591 printf("Ran %d tests, no errors\n", nb_tests
- old_tests
);
1593 printf("Ran %d tests, %d errors, %d leaks\n",
1594 nb_tests
- old_tests
,
1595 nb_errors
- old_errors
,
1596 nb_leaks
- old_leaks
);
1603 main(int argc ATTRIBUTE_UNUSED
, char **argv ATTRIBUTE_UNUSED
) {
1608 initializeLibxml2();
1610 for (a
= 1; a
< argc
;a
++) {
1611 if (!strcmp(argv
[a
], "-v"))
1613 else if (!strcmp(argv
[a
], "-quiet"))
1615 else if (!strcmp(argv
[a
], "-crazy"))
1619 for (i
= 0; testDescriptions
[i
].func
!= NULL
; i
++) {
1624 if ((nb_errors
== 0) && (nb_leaks
== 0)) {
1626 printf("Total %d tests, no errors\n",
1630 printf("Total %d tests, %d errors, %d leaks\n",
1631 nb_tests
, nb_errors
, nb_leaks
);