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