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