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