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