blob: 5335117d163ebc37200464884f08851ce2f2e548 [file] [log] [blame]
Daniel Veillard0de1f312012-07-18 17:43:34 +08001/*
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
4 *
5 * To compile on Unixes:
6 * cc -o testlimits `xml2-config --cflags` testlimits.c `xml2-config --libs` -lpthread
7 *
8 * See Copyright for the status of this software.
9 *
10 * daniel@veillard.com
11 */
12
13#ifdef HAVE_CONFIG_H
14#include "libxml.h"
15#else
16#include <stdio.h>
17#endif
18
19#if !defined(_WIN32) || defined(__CYGWIN__)
20#include <unistd.h>
21#endif
22#include <string.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <fcntl.h>
26
27#include <libxml/parser.h>
28#include <libxml/parserInternals.h>
29#include <libxml/tree.h>
30#include <libxml/uri.h>
31#ifdef LIBXML_READER_ENABLED
32#include <libxml/xmlreader.h>
33#endif
34
35static int verbose = 0;
36static int tests_quiet = 0;
37
38/************************************************************************
39 * *
40 * Huge document generator *
41 * *
42 ************************************************************************/
43
44#include <libxml/xmlIO.h>
45
46/*
47 * Huge documents are built using fixed start and end chunks
48 * and filling between the two an unconventional amount of char data
49 */
50typedef struct hugeTest hugeTest;
51typedef hugeTest *hugeTestPtr;
52struct hugeTest {
53 const char *description;
54 const char *name;
55 const char *start;
56 const char *end;
57};
58
59static struct hugeTest hugeTests[] = {
60 { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
61 { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
62 { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
63};
64
65static const char *current;
66static int rlen;
67static unsigned int currentTest = 0;
68static int instate = 0;
69
70/**
71 * hugeMatch:
72 * @URI: an URI to test
73 *
74 * Check for an huge: query
75 *
76 * Returns 1 if yes and 0 if another Input module should be used
77 */
78static int
79hugeMatch(const char * URI) {
80 if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
81 return(1);
82 return(0);
83}
84
85/**
86 * hugeOpen:
87 * @URI: an URI to test
88 *
89 * Return a pointer to the huge: query handler, in this example simply
90 * the current pointer...
91 *
92 * Returns an Input context or NULL in case or error
93 */
94static void *
95hugeOpen(const char * URI) {
96 if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
97 return(NULL);
98
99 for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
100 currentTest++)
101 if (!strcmp(hugeTests[currentTest].name, URI))
102 goto found;
103
104 return(NULL);
105
106found:
107 rlen = strlen(hugeTests[currentTest].start);
108 current = hugeTests[currentTest].start;
109 instate = 0;
110 return((void *) current);
111}
112
113/**
114 * hugeClose:
115 * @context: the read context
116 *
117 * Close the huge: query handler
118 *
119 * Returns 0 or -1 in case of error
120 */
121static int
122hugeClose(void * context) {
123 if (context == NULL) return(-1);
124 return(0);
125}
126
127#define CHUNK 4096
128
129char filling[CHUNK + 1];
130
131static void fillFilling(void) {
132 int i;
133
134 for (i = 0;i < CHUNK;i++) {
135 filling[i] = 'a';
136 }
137 filling[CHUNK] = 0;
138}
139
140size_t maxlen = 64 * 1024 * 1024;
141size_t curlen = 0;
142size_t dotlen;
143
144/**
145 * hugeRead:
146 * @context: the read context
147 * @buffer: where to store data
148 * @len: number of bytes to read
149 *
150 * Implement an huge: query read.
151 *
152 * Returns the number of bytes read or -1 in case of error
153 */
154static int
155hugeRead(void *context, char *buffer, int len)
156{
157 if ((context == NULL) || (buffer == NULL) || (len < 0))
158 return (-1);
159
160 if (instate == 0) {
161 if (len >= rlen) {
162 len = rlen;
163 rlen = 0;
164 memcpy(buffer, current, len);
165 instate = 1;
166 curlen = 0;
167 dotlen = maxlen / 10;
168 } else {
169 memcpy(buffer, current, len);
170 rlen -= len;
171 current += len;
172 }
173 } else if (instate == 2) {
174 if (len >= rlen) {
175 len = rlen;
176 rlen = 0;
177 memcpy(buffer, current, len);
178 instate = 3;
179 curlen = 0;
180 } else {
181 memcpy(buffer, current, len);
182 rlen -= len;
183 current += len;
184 }
185 } else if (instate == 1) {
186 if (len > CHUNK) len = CHUNK;
187 memcpy(buffer, &filling[0], len);
188 curlen += len;
189 if (curlen >= maxlen) {
190 rlen = strlen(hugeTests[currentTest].end);
191 current = hugeTests[currentTest].end;
192 instate = 2;
193 } else {
194 if (curlen > dotlen) {
195 fprintf(stderr, ".");
196 dotlen += maxlen / 10;
197 }
198 }
199 } else
200 len = 0;
201 return (len);
202}
203
204/************************************************************************
205 * *
206 * Libxml2 specific routines *
207 * *
208 ************************************************************************/
209
210static int nb_tests = 0;
211static int nb_errors = 0;
212static int nb_leaks = 0;
213static int extraMemoryFromResolver = 0;
214
215/*
216 * We need to trap calls to the resolver to not account memory for the catalog
217 * which is shared to the current running test. We also don't want to have
218 * network downloads modifying tests.
219 */
220static xmlParserInputPtr
221testExternalEntityLoader(const char *URL, const char *ID,
222 xmlParserCtxtPtr ctxt) {
223 xmlParserInputPtr ret;
224 int memused = xmlMemUsed();
225
226 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
227 extraMemoryFromResolver += xmlMemUsed() - memused;
228
229 return(ret);
230}
231
232/*
233 * Trapping the error messages at the generic level to grab the equivalent of
234 * stderr messages on CLI tools.
235 */
236static char testErrors[32769];
237static int testErrorsSize = 0;
238
239static void XMLCDECL
240channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
241 va_list args;
242 int res;
243
244 if (testErrorsSize >= 32768)
245 return;
246 va_start(args, msg);
247 res = vsnprintf(&testErrors[testErrorsSize],
248 32768 - testErrorsSize,
249 msg, args);
250 va_end(args);
251 if (testErrorsSize + res >= 32768) {
252 /* buffer is full */
253 testErrorsSize = 32768;
254 testErrors[testErrorsSize] = 0;
255 } else {
256 testErrorsSize += res;
257 }
258 testErrors[testErrorsSize] = 0;
259}
260
261/**
262 * xmlParserPrintFileContext:
263 * @input: an xmlParserInputPtr input
264 *
265 * Displays current context within the input content for error tracking
266 */
267
268static void
269xmlParserPrintFileContextInternal(xmlParserInputPtr input ,
270 xmlGenericErrorFunc chanl, void *data ) {
271 const xmlChar *cur, *base;
272 unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */
273 xmlChar content[81]; /* space for 80 chars + line terminator */
274 xmlChar *ctnt;
275
276 if (input == NULL) return;
277 cur = input->cur;
278 base = input->base;
279 /* skip backwards over any end-of-lines */
280 while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) {
281 cur--;
282 }
283 n = 0;
284 /* search backwards for beginning-of-line (to max buff size) */
285 while ((n++ < (sizeof(content)-1)) && (cur > base) &&
286 (*(cur) != '\n') && (*(cur) != '\r'))
287 cur--;
288 if ((*(cur) == '\n') || (*(cur) == '\r')) cur++;
289 /* calculate the error position in terms of the current position */
290 col = input->cur - cur;
291 /* search forward for end-of-line (to max buff size) */
292 n = 0;
293 ctnt = content;
294 /* copy selected text to our buffer */
295 while ((*cur != 0) && (*(cur) != '\n') &&
296 (*(cur) != '\r') && (n < sizeof(content)-1)) {
297 *ctnt++ = *cur++;
298 n++;
299 }
300 *ctnt = 0;
301 /* print out the selected text */
302 chanl(data ,"%s\n", content);
303 /* create blank line with problem pointer */
304 n = 0;
305 ctnt = content;
306 /* (leave buffer space for pointer + line terminator) */
307 while ((n<col) && (n++ < sizeof(content)-2) && (*ctnt != 0)) {
308 if (*(ctnt) != '\t')
309 *(ctnt) = ' ';
310 ctnt++;
311 }
312 *ctnt++ = '^';
313 *ctnt = 0;
314 chanl(data ,"%s\n", content);
315}
316
317static void
318testStructuredErrorHandler(void *ctx ATTRIBUTE_UNUSED, xmlErrorPtr err) {
319 char *file = NULL;
320 int line = 0;
321 int code = -1;
322 int domain;
323 void *data = NULL;
324 const char *str;
325 const xmlChar *name = NULL;
326 xmlNodePtr node;
327 xmlErrorLevel level;
328 xmlParserInputPtr input = NULL;
329 xmlParserInputPtr cur = NULL;
330 xmlParserCtxtPtr ctxt = NULL;
331
332 if (err == NULL)
333 return;
334
335 file = err->file;
336 line = err->line;
337 code = err->code;
338 domain = err->domain;
339 level = err->level;
340 node = err->node;
341 if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) ||
342 (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) ||
343 (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) {
344 ctxt = err->ctxt;
345 }
346 str = err->message;
347
348 if (code == XML_ERR_OK)
349 return;
350
351 if ((node != NULL) && (node->type == XML_ELEMENT_NODE))
352 name = node->name;
353
354 /*
355 * Maintain the compatibility with the legacy error handling
356 */
357 if (ctxt != NULL) {
358 input = ctxt->input;
359 if ((input != NULL) && (input->filename == NULL) &&
360 (ctxt->inputNr > 1)) {
361 cur = input;
362 input = ctxt->inputTab[ctxt->inputNr - 2];
363 }
364 if (input != NULL) {
365 if (input->filename)
366 channel(data, "%s:%d: ", input->filename, input->line);
367 else if ((line != 0) && (domain == XML_FROM_PARSER))
368 channel(data, "Entity: line %d: ", input->line);
369 }
370 } else {
371 if (file != NULL)
372 channel(data, "%s:%d: ", file, line);
373 else if ((line != 0) && (domain == XML_FROM_PARSER))
374 channel(data, "Entity: line %d: ", line);
375 }
376 if (name != NULL) {
377 channel(data, "element %s: ", name);
378 }
379 if (code == XML_ERR_OK)
380 return;
381 switch (domain) {
382 case XML_FROM_PARSER:
383 channel(data, "parser ");
384 break;
385 case XML_FROM_NAMESPACE:
386 channel(data, "namespace ");
387 break;
388 case XML_FROM_DTD:
389 case XML_FROM_VALID:
390 channel(data, "validity ");
391 break;
392 case XML_FROM_HTML:
393 channel(data, "HTML parser ");
394 break;
395 case XML_FROM_MEMORY:
396 channel(data, "memory ");
397 break;
398 case XML_FROM_OUTPUT:
399 channel(data, "output ");
400 break;
401 case XML_FROM_IO:
402 channel(data, "I/O ");
403 break;
404 case XML_FROM_XINCLUDE:
405 channel(data, "XInclude ");
406 break;
407 case XML_FROM_XPATH:
408 channel(data, "XPath ");
409 break;
410 case XML_FROM_XPOINTER:
411 channel(data, "parser ");
412 break;
413 case XML_FROM_REGEXP:
414 channel(data, "regexp ");
415 break;
416 case XML_FROM_MODULE:
417 channel(data, "module ");
418 break;
419 case XML_FROM_SCHEMASV:
420 channel(data, "Schemas validity ");
421 break;
422 case XML_FROM_SCHEMASP:
423 channel(data, "Schemas parser ");
424 break;
425 case XML_FROM_RELAXNGP:
426 channel(data, "Relax-NG parser ");
427 break;
428 case XML_FROM_RELAXNGV:
429 channel(data, "Relax-NG validity ");
430 break;
431 case XML_FROM_CATALOG:
432 channel(data, "Catalog ");
433 break;
434 case XML_FROM_C14N:
435 channel(data, "C14N ");
436 break;
437 case XML_FROM_XSLT:
438 channel(data, "XSLT ");
439 break;
440 default:
441 break;
442 }
443 if (code == XML_ERR_OK)
444 return;
445 switch (level) {
446 case XML_ERR_NONE:
447 channel(data, ": ");
448 break;
449 case XML_ERR_WARNING:
450 channel(data, "warning : ");
451 break;
452 case XML_ERR_ERROR:
453 channel(data, "error : ");
454 break;
455 case XML_ERR_FATAL:
456 channel(data, "error : ");
457 break;
458 }
459 if (code == XML_ERR_OK)
460 return;
461 if (str != NULL) {
462 int len;
463 len = xmlStrlen((const xmlChar *)str);
464 if ((len > 0) && (str[len - 1] != '\n'))
465 channel(data, "%s\n", str);
466 else
467 channel(data, "%s", str);
468 } else {
469 channel(data, "%s\n", "out of memory error");
470 }
471 if (code == XML_ERR_OK)
472 return;
473
474 if (ctxt != NULL) {
475 xmlParserPrintFileContextInternal(input, channel, data);
476 if (cur != NULL) {
477 if (cur->filename)
478 channel(data, "%s:%d: \n", cur->filename, cur->line);
479 else if ((line != 0) && (domain == XML_FROM_PARSER))
480 channel(data, "Entity: line %d: \n", cur->line);
481 xmlParserPrintFileContextInternal(cur, channel, data);
482 }
483 }
484 if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) &&
485 (err->int1 < 100) &&
486 (err->int1 < xmlStrlen((const xmlChar *)err->str1))) {
487 xmlChar buf[150];
488 int i;
489
490 channel(data, "%s\n", err->str1);
491 for (i=0;i < err->int1;i++)
492 buf[i] = ' ';
493 buf[i++] = '^';
494 buf[i] = 0;
495 channel(data, "%s\n", buf);
496 }
497}
498
499static void
500initializeLibxml2(void) {
501 xmlGetWarningsDefaultValue = 0;
502 xmlPedanticParserDefault(0);
503
504 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
505 xmlInitParser();
506 xmlSetExternalEntityLoader(testExternalEntityLoader);
507 xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler);
508 /*
509 * register the new I/O handlers
510 */
511 if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
512 hugeRead, hugeClose) < 0) {
513 fprintf(stderr, "failed to register Huge handlers\n");
514 exit(1);
515 }
516}
517
518/************************************************************************
519 * *
520 * SAX empty callbacks *
521 * *
522 ************************************************************************/
523
524unsigned long callbacks = 0;
525
526/**
527 * isStandaloneCallback:
528 * @ctxt: An XML parser context
529 *
530 * Is this document tagged standalone ?
531 *
532 * Returns 1 if true
533 */
534static int
535isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
536{
537 callbacks++;
538 return (0);
539}
540
541/**
542 * hasInternalSubsetCallback:
543 * @ctxt: An XML parser context
544 *
545 * Does this document has an internal subset
546 *
547 * Returns 1 if true
548 */
549static int
550hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
551{
552 callbacks++;
553 return (0);
554}
555
556/**
557 * hasExternalSubsetCallback:
558 * @ctxt: An XML parser context
559 *
560 * Does this document has an external subset
561 *
562 * Returns 1 if true
563 */
564static int
565hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
566{
567 callbacks++;
568 return (0);
569}
570
571/**
572 * internalSubsetCallback:
573 * @ctxt: An XML parser context
574 *
575 * Does this document has an internal subset
576 */
577static void
578internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
579 const xmlChar * name ATTRIBUTE_UNUSED,
580 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
581 const xmlChar * SystemID ATTRIBUTE_UNUSED)
582{
583 callbacks++;
584 return;
585}
586
587/**
588 * externalSubsetCallback:
589 * @ctxt: An XML parser context
590 *
591 * Does this document has an external subset
592 */
593static void
594externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
595 const xmlChar * name ATTRIBUTE_UNUSED,
596 const xmlChar * ExternalID ATTRIBUTE_UNUSED,
597 const xmlChar * SystemID ATTRIBUTE_UNUSED)
598{
599 callbacks++;
600 return;
601}
602
603/**
604 * resolveEntityCallback:
605 * @ctxt: An XML parser context
606 * @publicId: The public ID of the entity
607 * @systemId: The system ID of the entity
608 *
609 * Special entity resolver, better left to the parser, it has
610 * more context than the application layer.
611 * The default behaviour is to NOT resolve the entities, in that case
612 * the ENTITY_REF nodes are built in the structure (and the parameter
613 * values).
614 *
615 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
616 */
617static xmlParserInputPtr
618resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
619 const xmlChar * publicId ATTRIBUTE_UNUSED,
620 const xmlChar * systemId ATTRIBUTE_UNUSED)
621{
622 callbacks++;
623 return (NULL);
624}
625
626/**
627 * getEntityCallback:
628 * @ctxt: An XML parser context
629 * @name: The entity name
630 *
631 * Get an entity by name
632 *
633 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
634 */
635static xmlEntityPtr
636getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
637 const xmlChar * name ATTRIBUTE_UNUSED)
638{
639 callbacks++;
640 return (NULL);
641}
642
643/**
644 * getParameterEntityCallback:
645 * @ctxt: An XML parser context
646 * @name: The entity name
647 *
648 * Get a parameter entity by name
649 *
650 * Returns the xmlParserInputPtr
651 */
652static xmlEntityPtr
653getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
654 const xmlChar * name ATTRIBUTE_UNUSED)
655{
656 callbacks++;
657 return (NULL);
658}
659
660
661/**
662 * entityDeclCallback:
663 * @ctxt: An XML parser context
664 * @name: the entity name
665 * @type: the entity type
666 * @publicId: The public ID of the entity
667 * @systemId: The system ID of the entity
668 * @content: the entity value (without processing).
669 *
670 * An entity definition has been parsed
671 */
672static void
673entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
674 const xmlChar * name ATTRIBUTE_UNUSED,
675 int type ATTRIBUTE_UNUSED,
676 const xmlChar * publicId ATTRIBUTE_UNUSED,
677 const xmlChar * systemId ATTRIBUTE_UNUSED,
678 xmlChar * content ATTRIBUTE_UNUSED)
679{
680 callbacks++;
681 return;
682}
683
684/**
685 * attributeDeclCallback:
686 * @ctxt: An XML parser context
687 * @name: the attribute name
688 * @type: the attribute type
689 *
690 * An attribute definition has been parsed
691 */
692static void
693attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
694 const xmlChar * elem ATTRIBUTE_UNUSED,
695 const xmlChar * name ATTRIBUTE_UNUSED,
696 int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
697 const xmlChar * defaultValue ATTRIBUTE_UNUSED,
698 xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
699{
700 callbacks++;
701 return;
702}
703
704/**
705 * elementDeclCallback:
706 * @ctxt: An XML parser context
707 * @name: the element name
708 * @type: the element type
709 * @content: the element value (without processing).
710 *
711 * An element definition has been parsed
712 */
713static void
714elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
715 const xmlChar * name ATTRIBUTE_UNUSED,
716 int type ATTRIBUTE_UNUSED,
717 xmlElementContentPtr content ATTRIBUTE_UNUSED)
718{
719 callbacks++;
720 return;
721}
722
723/**
724 * notationDeclCallback:
725 * @ctxt: An XML parser context
726 * @name: The name of the notation
727 * @publicId: The public ID of the entity
728 * @systemId: The system ID of the entity
729 *
730 * What to do when a notation declaration has been parsed.
731 */
732static void
733notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
734 const xmlChar * name ATTRIBUTE_UNUSED,
735 const xmlChar * publicId ATTRIBUTE_UNUSED,
736 const xmlChar * systemId ATTRIBUTE_UNUSED)
737{
738 callbacks++;
739 return;
740}
741
742/**
743 * unparsedEntityDeclCallback:
744 * @ctxt: An XML parser context
745 * @name: The name of the entity
746 * @publicId: The public ID of the entity
747 * @systemId: The system ID of the entity
748 * @notationName: the name of the notation
749 *
750 * What to do when an unparsed entity declaration is parsed
751 */
752static void
753unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
754 const xmlChar * name ATTRIBUTE_UNUSED,
755 const xmlChar * publicId ATTRIBUTE_UNUSED,
756 const xmlChar * systemId ATTRIBUTE_UNUSED,
757 const xmlChar * notationName ATTRIBUTE_UNUSED)
758{
759 callbacks++;
760 return;
761}
762
763/**
764 * setDocumentLocatorCallback:
765 * @ctxt: An XML parser context
766 * @loc: A SAX Locator
767 *
768 * Receive the document locator at startup, actually xmlDefaultSAXLocator
769 * Everything is available on the context, so this is useless in our case.
770 */
771static void
772setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
773 xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
774{
775 callbacks++;
776 return;
777}
778
779/**
780 * startDocumentCallback:
781 * @ctxt: An XML parser context
782 *
783 * called when the document start being processed.
784 */
785static void
786startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
787{
788 callbacks++;
789 return;
790}
791
792/**
793 * endDocumentCallback:
794 * @ctxt: An XML parser context
795 *
796 * called when the document end has been detected.
797 */
798static void
799endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
800{
801 callbacks++;
802 return;
803}
804
805#if 0
806/**
807 * startElementCallback:
808 * @ctxt: An XML parser context
809 * @name: The element name
810 *
811 * called when an opening tag has been processed.
812 */
813static void
814startElementCallback(void *ctx ATTRIBUTE_UNUSED,
815 const xmlChar * name ATTRIBUTE_UNUSED,
816 const xmlChar ** atts ATTRIBUTE_UNUSED)
817{
818 callbacks++;
819 return;
820}
821
822/**
823 * endElementCallback:
824 * @ctxt: An XML parser context
825 * @name: The element name
826 *
827 * called when the end of an element has been detected.
828 */
829static void
830endElementCallback(void *ctx ATTRIBUTE_UNUSED,
831 const xmlChar * name ATTRIBUTE_UNUSED)
832{
833 callbacks++;
834 return;
835}
836#endif
837
838/**
839 * charactersCallback:
840 * @ctxt: An XML parser context
841 * @ch: a xmlChar string
842 * @len: the number of xmlChar
843 *
844 * receiving some chars from the parser.
845 * Question: how much at a time ???
846 */
847static void
848charactersCallback(void *ctx ATTRIBUTE_UNUSED,
849 const xmlChar * ch ATTRIBUTE_UNUSED,
850 int len ATTRIBUTE_UNUSED)
851{
852 callbacks++;
853 return;
854}
855
856/**
857 * referenceCallback:
858 * @ctxt: An XML parser context
859 * @name: The entity name
860 *
861 * called when an entity reference is detected.
862 */
863static void
864referenceCallback(void *ctx ATTRIBUTE_UNUSED,
865 const xmlChar * name ATTRIBUTE_UNUSED)
866{
867 callbacks++;
868 return;
869}
870
871/**
872 * ignorableWhitespaceCallback:
873 * @ctxt: An XML parser context
874 * @ch: a xmlChar string
875 * @start: the first char in the string
876 * @len: the number of xmlChar
877 *
878 * receiving some ignorable whitespaces from the parser.
879 * Question: how much at a time ???
880 */
881static void
882ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
883 const xmlChar * ch ATTRIBUTE_UNUSED,
884 int len ATTRIBUTE_UNUSED)
885{
886 callbacks++;
887 return;
888}
889
890/**
891 * processingInstructionCallback:
892 * @ctxt: An XML parser context
893 * @target: the target name
894 * @data: the PI data's
895 * @len: the number of xmlChar
896 *
897 * A processing instruction has been parsed.
898 */
899static void
900processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
901 const xmlChar * target ATTRIBUTE_UNUSED,
902 const xmlChar * data ATTRIBUTE_UNUSED)
903{
904 callbacks++;
905 return;
906}
907
908/**
909 * cdataBlockCallback:
910 * @ctx: the user data (XML parser context)
911 * @value: The pcdata content
912 * @len: the block length
913 *
914 * called when a pcdata block has been parsed
915 */
916static void
917cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
918 const xmlChar * value ATTRIBUTE_UNUSED,
919 int len ATTRIBUTE_UNUSED)
920{
921 callbacks++;
922 return;
923}
924
925/**
926 * commentCallback:
927 * @ctxt: An XML parser context
928 * @value: the comment content
929 *
930 * A comment has been parsed.
931 */
932static void
933commentCallback(void *ctx ATTRIBUTE_UNUSED,
934 const xmlChar * value ATTRIBUTE_UNUSED)
935{
936 callbacks++;
937 return;
938}
939
940/**
941 * warningCallback:
942 * @ctxt: An XML parser context
943 * @msg: the message to display/transmit
944 * @...: extra parameters for the message display
945 *
946 * Display and format a warning messages, gives file, line, position and
947 * extra parameters.
948 */
949static void XMLCDECL
950warningCallback(void *ctx ATTRIBUTE_UNUSED,
951 const char *msg ATTRIBUTE_UNUSED, ...)
952{
953 callbacks++;
954 return;
955}
956
957/**
958 * errorCallback:
959 * @ctxt: An XML parser context
960 * @msg: the message to display/transmit
961 * @...: extra parameters for the message display
962 *
963 * Display and format a error messages, gives file, line, position and
964 * extra parameters.
965 */
966static void XMLCDECL
967errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
968 ...)
969{
970 callbacks++;
971 return;
972}
973
974/**
975 * fatalErrorCallback:
976 * @ctxt: An XML parser context
977 * @msg: the message to display/transmit
978 * @...: extra parameters for the message display
979 *
980 * Display and format a fatalError messages, gives file, line, position and
981 * extra parameters.
982 */
983static void XMLCDECL
984fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
985 const char *msg ATTRIBUTE_UNUSED, ...)
986{
987 return;
988}
989
990
991/*
992 * SAX2 specific callbacks
993 */
994
995/**
996 * startElementNsCallback:
997 * @ctxt: An XML parser context
998 * @name: The element name
999 *
1000 * called when an opening tag has been processed.
1001 */
1002static void
1003startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1004 const xmlChar * localname ATTRIBUTE_UNUSED,
1005 const xmlChar * prefix ATTRIBUTE_UNUSED,
1006 const xmlChar * URI ATTRIBUTE_UNUSED,
1007 int nb_namespaces ATTRIBUTE_UNUSED,
1008 const xmlChar ** namespaces ATTRIBUTE_UNUSED,
1009 int nb_attributes ATTRIBUTE_UNUSED,
1010 int nb_defaulted ATTRIBUTE_UNUSED,
1011 const xmlChar ** attributes ATTRIBUTE_UNUSED)
1012{
1013 callbacks++;
1014 return;
1015}
1016
1017/**
1018 * endElementCallback:
1019 * @ctxt: An XML parser context
1020 * @name: The element name
1021 *
1022 * called when the end of an element has been detected.
1023 */
1024static void
1025endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
1026 const xmlChar * localname ATTRIBUTE_UNUSED,
1027 const xmlChar * prefix ATTRIBUTE_UNUSED,
1028 const xmlChar * URI ATTRIBUTE_UNUSED)
1029{
1030 callbacks++;
1031 return;
1032}
1033
1034static xmlSAXHandler callbackSAX2HandlerStruct = {
1035 internalSubsetCallback,
1036 isStandaloneCallback,
1037 hasInternalSubsetCallback,
1038 hasExternalSubsetCallback,
1039 resolveEntityCallback,
1040 getEntityCallback,
1041 entityDeclCallback,
1042 notationDeclCallback,
1043 attributeDeclCallback,
1044 elementDeclCallback,
1045 unparsedEntityDeclCallback,
1046 setDocumentLocatorCallback,
1047 startDocumentCallback,
1048 endDocumentCallback,
1049 NULL,
1050 NULL,
1051 referenceCallback,
1052 charactersCallback,
1053 ignorableWhitespaceCallback,
1054 processingInstructionCallback,
1055 commentCallback,
1056 warningCallback,
1057 errorCallback,
1058 fatalErrorCallback,
1059 getParameterEntityCallback,
1060 cdataBlockCallback,
1061 externalSubsetCallback,
1062 XML_SAX2_MAGIC,
1063 NULL,
1064 startElementNsCallback,
1065 endElementNsCallback,
1066 NULL
1067};
1068
1069static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
1070
1071/************************************************************************
1072 * *
1073 * The tests front-ends *
1074 * *
1075 ************************************************************************/
1076
1077/**
1078 * readerTest:
1079 * @filename: the file to parse
1080 * @max_size: size of the limit to test
1081 * @options: parsing options
1082 * @fail: should a failure be reported
1083 *
1084 * Parse a memory generated file using the xmlReader
1085 *
1086 * Returns 0 in case of success, an error code otherwise
1087 */
1088static int
1089saxTest(const char *filename, size_t limit, int options, int fail) {
1090 int res = 0;
1091 xmlParserCtxtPtr ctxt;
1092 xmlDocPtr doc;
1093 xmlSAXHandlerPtr old_sax;
1094
1095 nb_tests++;
1096
1097 maxlen = limit;
1098 ctxt = xmlNewParserCtxt();
1099 if (ctxt == NULL) {
1100 fprintf(stderr, "Failed to create parser context\n");
1101 return(1);
1102 }
1103 old_sax = ctxt->sax;
1104 ctxt->sax = callbackSAX2Handler;
1105 ctxt->userData = NULL;
1106 doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
1107 fprintf(stderr, "\n");
1108
1109 if (doc != NULL) {
1110 fprintf(stderr, "SAX parsing generated a document !\n");
1111 xmlFreeDoc(doc);
1112 res = 0;
1113 } else if (ctxt->wellFormed == 0) {
1114 if (fail)
1115 res = 0;
1116 else {
1117 fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
1118 res = 1;
1119 }
1120 } else {
1121 if (fail) {
1122 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1123 filename, limit);
1124 res = 1;
1125 } else
1126 res = 0;
1127 }
1128 ctxt->sax = old_sax;
1129 xmlFreeParserCtxt(ctxt);
1130
1131 return(res);
1132}
1133#ifdef LIBXML_READER_ENABLED
1134/**
1135 * readerTest:
1136 * @filename: the file to parse
1137 * @max_size: size of the limit to test
1138 * @options: parsing options
1139 * @fail: should a failure be reported
1140 *
1141 * Parse a memory generated file using the xmlReader
1142 *
1143 * Returns 0 in case of success, an error code otherwise
1144 */
1145static int
1146readerTest(const char *filename, size_t limit, int options, int fail) {
1147 xmlTextReaderPtr reader;
1148 int res = 0;
1149 int ret;
1150
1151 nb_tests++;
1152
1153 maxlen = limit;
1154 reader = xmlReaderForFile(filename , NULL, options);
1155 if (reader == NULL) {
1156 fprintf(stderr, "Failed to open '%s' test\n", filename);
1157 return(1);
1158 }
1159 ret = xmlTextReaderRead(reader);
1160 while (ret == 1) {
1161 ret = xmlTextReaderRead(reader);
1162 }
1163 fprintf(stderr, "\n");
1164 if (ret != 0) {
1165 if (fail)
1166 res = 0;
1167 else {
1168 fprintf(stderr, "Failed to parse '%s' %lu\n", filename, limit);
1169 res = 1;
1170 }
1171 } else {
1172 if (fail) {
1173 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1174 filename, limit);
1175 res = 1;
1176 } else
1177 res = 0;
1178 }
1179 xmlFreeTextReader(reader);
1180
1181 return(res);
1182}
1183#endif
1184
1185/************************************************************************
1186 * *
1187 * Tests descriptions *
1188 * *
1189 ************************************************************************/
1190
1191typedef int (*functest) (const char *filename, size_t limit, int options,
1192 int fail);
1193
1194typedef struct limitDesc limitDesc;
1195typedef limitDesc *limitDescPtr;
1196struct limitDesc {
1197 const char *name; /* the huge generator name */
1198 size_t limit; /* the limit to test */
1199 int options; /* extra parser options */
1200 int fail; /* whether the test should fail */
1201};
1202
1203static limitDesc limitDescriptions[] = {
1204 /* max lenght of a text node in content */
1205 {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1206 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1207 {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1208 /* max lenght of a text node in content */
1209 {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1210 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1211 {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1212 /* max lenght of a comment node */
1213 {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1214 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1215 {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1216};
1217
1218typedef struct testDesc testDesc;
1219typedef testDesc *testDescPtr;
1220struct testDesc {
1221 const char *desc; /* descripton of the test */
1222 functest func; /* function implementing the test */
1223};
1224
1225static
1226testDesc testDescriptions[] = {
1227 { "Parsing of huge files with the sax parser", saxTest},
1228/* { "Parsing of huge files with the tree parser", treeTest}, */
1229#ifdef LIBXML_READER_ENABLED
1230 { "Parsing of huge files with the reader", readerTest},
1231#endif
1232 {NULL, NULL}
1233};
1234
1235typedef struct testException testException;
1236typedef testException *testExceptionPtr;
1237struct testException {
1238 unsigned int test; /* the parser test number */
1239 unsigned int limit; /* the limit test number */
1240 int fail; /* new fail value or -1*/
1241 size_t size; /* new limit value or 0 */
1242};
1243
1244static
1245testException testExceptions[] = {
1246 /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1247 { 0, 1, 0, 0},
1248};
1249
1250static int
1251launchTests(testDescPtr tst, unsigned int test) {
1252 int res = 0, err = 0;
1253 unsigned int i, j;
1254 size_t limit;
1255 int fail;
1256
1257 if (tst == NULL) return(-1);
1258
1259 for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1260 limit = limitDescriptions[i].limit;
1261 fail = limitDescriptions[i].fail;
1262 /*
1263 * Handle exceptions if any
1264 */
1265 for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1266 if ((testExceptions[j].test == test) &&
1267 (testExceptions[j].limit == i)) {
1268 if (testExceptions[j].fail != -1)
1269 fail = testExceptions[j].fail;
1270 if (testExceptions[j].size != 0)
1271 limit = testExceptions[j].size;
1272 break;
1273 }
1274 }
1275 res = tst->func(limitDescriptions[i].name, limit,
1276 limitDescriptions[i].options, fail);
1277 if (res != 0) {
1278 nb_errors++;
1279 err++;
1280 }
1281 }
1282 return(err);
1283}
1284
1285
1286static int
1287runtest(unsigned int i) {
1288 int ret = 0, res;
1289 int old_errors, old_tests, old_leaks;
1290
1291 old_errors = nb_errors;
1292 old_tests = nb_tests;
1293 old_leaks = nb_leaks;
1294 if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1295 printf("## %s\n", testDescriptions[i].desc);
1296 res = launchTests(&testDescriptions[i], i);
1297 if (res != 0)
1298 ret++;
1299 if (verbose) {
1300 if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1301 printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1302 else
1303 printf("Ran %d tests, %d errors, %d leaks\n",
1304 nb_tests - old_tests,
1305 nb_errors - old_errors,
1306 nb_leaks - old_leaks);
1307 }
1308 return(ret);
1309}
1310
1311int
1312main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1313 int i, a, ret = 0;
1314 int subset = 0;
1315
1316 fillFilling();
1317 initializeLibxml2();
1318
1319 for (a = 1; a < argc;a++) {
1320 if (!strcmp(argv[a], "-v"))
1321 verbose = 1;
1322 else if (!strcmp(argv[a], "-quiet"))
1323 tests_quiet = 1;
1324 }
1325 if (subset == 0) {
1326 for (i = 0; testDescriptions[i].func != NULL; i++) {
1327 ret += runtest(i);
1328 }
1329 }
1330 if ((nb_errors == 0) && (nb_leaks == 0)) {
1331 ret = 0;
1332 printf("Total %d tests, no errors\n",
1333 nb_tests);
1334 } else {
1335 ret = 1;
1336 printf("Total %d tests, %d errors, %d leaks\n",
1337 nb_tests, nb_errors, nb_leaks);
1338 }
1339 xmlCleanupParser();
1340 xmlMemoryDump();
1341
1342 return(ret);
1343}