blob: 978b2e0b1c04e33f8828d5070dd93be876e0f0ba [file] [log] [blame]
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001/*
2 * runtest.c: C program to run libxml2 regression tests without
3 * requiring make or Python, and reducing platform dependancies
4 * to a strict minimum.
5 *
6 * See Copyright for the status of this software.
7 *
8 * daniel@veillard.com
9 */
10
11#include <unistd.h>
12#include <string.h>
13#include <stdio.h>
14#include <glob.h>
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <fcntl.h>
18#include <unistd.h>
19
20#include <libxml/parser.h>
21#include <libxml/tree.h>
Daniel Veillardc111c152005-06-27 08:22:10 +000022#ifdef LIBXML_READER_ENABLED
23#include <libxml/xmlreader.h>
24#endif
Daniel Veillard1b75c3b2005-06-26 21:49:08 +000025
Daniel Veillardfc319af2005-06-27 12:44:55 +000026#ifdef LIBXML_HTML_ENABLED
27#include <libxml/HTMLparser.h>
28#include <libxml/HTMLtree.h>
29
30/*
31 * pseudo flag for the unification of HTML and XML tests
32 */
33#define XML_PARSE_HTML 1 << 24
34#endif
35
Daniel Veillardfd110d22005-06-27 00:02:02 +000036typedef int (*functest) (const char *filename, const char *result,
Daniel Veillardc111c152005-06-27 08:22:10 +000037 const char *error, int options);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +000038
39typedef struct testDesc testDesc;
40typedef testDesc *testDescPtr;
41struct testDesc {
42 const char *desc; /* descripton of the test */
43 functest func; /* function implementing the test */
44 const char *in; /* glob to path for input files */
45 const char *out; /* output directory */
46 const char *suffix;/* suffix for output files */
Daniel Veillardfd110d22005-06-27 00:02:02 +000047 const char *err; /* suffix for error output files */
Daniel Veillardc111c152005-06-27 08:22:10 +000048 int options; /* parser options for the test */
Daniel Veillard1b75c3b2005-06-26 21:49:08 +000049};
50
51static int checkTestFile(const char *filename);
52/************************************************************************
53 * *
54 * Libxml2 specific routines *
55 * *
56 ************************************************************************/
57
58static long libxmlMemoryAllocatedBase = 0;
59static int extraMemoryFromResolver = 0;
60
61static int
62fatalError(void) {
63 fprintf(stderr, "Exitting tests on fatal error\n");
64 exit(1);
65}
66
67/*
68 * We need to trap calls to the resolver to not account memory for the catalog
69 * which is shared to the current running test. We also don't want to have
70 * network downloads modifying tests.
71 */
72static xmlParserInputPtr
73testExternalEntityLoader(const char *URL, const char *ID,
74 xmlParserCtxtPtr ctxt) {
75 xmlParserInputPtr ret;
76
77 if (checkTestFile(URL)) {
78 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
79 } else {
80 int memused = xmlMemUsed();
81 ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
82 extraMemoryFromResolver += xmlMemUsed() - memused;
83 }
84
85 return(ret);
86}
87
Daniel Veillardfd110d22005-06-27 00:02:02 +000088/*
89 * Trapping the error messages at the generic level to grab the equivalent of
90 * stderr messages on CLI tools.
91 */
92static char testErrors[32769];
93static int testErrorsSize = 0;
94
95static void
Daniel Veillardc111c152005-06-27 08:22:10 +000096testErrorHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) {
Daniel Veillardfd110d22005-06-27 00:02:02 +000097 va_list args;
98 int res;
99
100 if (testErrorsSize >= 32768)
101 return;
102 va_start(args, msg);
103 res = vsnprintf(&testErrors[testErrorsSize],
104 32768 - testErrorsSize,
105 msg, args);
106 va_end(args);
107 if (testErrorsSize + res >= 32768) {
108 /* buffer is full */
109 testErrorsSize = 32768;
110 testErrors[testErrorsSize] = 0;
111 } else {
112 testErrorsSize += res;
113 }
114 testErrors[testErrorsSize] = 0;
115}
116
Daniel Veillard1b75c3b2005-06-26 21:49:08 +0000117static void
118initializeLibxml2(void) {
119 xmlGetWarningsDefaultValue = 0;
120 xmlPedanticParserDefault(0);
121
122 xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
123 xmlInitParser();
124 xmlSetExternalEntityLoader(testExternalEntityLoader);
Daniel Veillardfd110d22005-06-27 00:02:02 +0000125 xmlSetGenericErrorFunc(NULL, testErrorHandler);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +0000126 libxmlMemoryAllocatedBase = xmlMemUsed();
127}
128
129
130/************************************************************************
131 * *
132 * File name and path utilities *
133 * *
134 ************************************************************************/
135
136static const char *baseFilename(const char *filename) {
137 const char *cur;
138 if (filename == NULL)
139 return(NULL);
140 cur = &filename[strlen(filename)];
141 while ((cur > filename) && (*cur != '/'))
142 cur--;
143 if (*cur == '/')
144 return(cur + 1);
145 return(cur);
146}
147
148static char *resultFilename(const char *filename, const char *out,
149 const char *suffix) {
150 const char *base;
151 char res[500];
152
153/*************
154 if ((filename[0] == 't') && (filename[1] == 'e') &&
155 (filename[2] == 's') && (filename[3] == 't') &&
156 (filename[4] == '/'))
157 filename = &filename[5];
158 *************/
159
160 base = baseFilename(filename);
161 if (suffix == NULL)
162 suffix = ".tmp";
163 if (out == NULL)
164 out = "";
165 snprintf(res, 499, "%s%s%s", out, base, suffix);
166 res[499] = 0;
167 return(strdup(res));
168}
169
170static int checkTestFile(const char *filename) {
171 struct stat buf;
172
173 if (stat(filename, &buf) == -1)
174 return(0);
175
176 if (!S_ISREG(buf.st_mode))
177 return(0);
178
179 return(1);
180}
181
182static int compareFiles(const char *r1, const char *r2) {
183 int res1, res2;
184 int fd1, fd2;
185 char bytes1[4096];
186 char bytes2[4096];
187
188 fd1 = open(r1, O_RDONLY);
189 if (fd1 < 0)
190 return(-1);
191 fd2 = open(r2, O_RDONLY);
192 if (fd2 < 0) {
193 close(fd1);
194 return(-1);
195 }
196 while (1) {
197 res1 = read(fd1, bytes1, 4096);
198 res2 = read(fd2, bytes2, 4096);
199 if (res1 != res2) {
200 close(fd1);
201 close(fd2);
202 return(1);
203 }
204 if (res1 == 0)
205 break;
206 if (memcmp(bytes1, bytes2, res1) != 0) {
207 close(fd1);
208 close(fd2);
209 return(1);
210 }
211 }
212 close(fd1);
213 close(fd2);
214 return(0);
215}
216
Daniel Veillardfd110d22005-06-27 00:02:02 +0000217static int compareFileMem(const char *filename, const char *mem, int size) {
218 int res;
219 int fd;
220 char bytes[4096];
221 int idx = 0;
222 struct stat info;
223
224 if (stat(filename, &info) < 0)
225 return(-1);
226 if (info.st_size != size)
227 return(-1);
228 fd = open(filename, O_RDONLY);
229 if (fd < 0)
230 return(-1);
231 while (idx < size) {
232 res = read(fd, bytes, 4096);
233 if (res <= 0)
234 break;
235 if (res + idx > size)
236 break;
237 if (memcmp(bytes, &mem[idx], res) != 0) {
238 close(fd);
239 return(1);
240 }
241 idx += res;
242 }
243 close(fd);
244 return(idx != size);
245}
246
Daniel Veillard1b75c3b2005-06-26 21:49:08 +0000247static int loadMem(const char *filename, const char **mem, int *size) {
248 int fd, res;
249 struct stat info;
250 char *base;
251 int siz = 0;
252 if (stat(filename, &info) < 0)
253 return(-1);
254 base = malloc(info.st_size + 1);
255 if (base == NULL)
256 return(-1);
257 if ((fd = open(filename, O_RDONLY)) < 0) {
258 free(base);
259 return(-1);
260 }
261 while ((res = read(fd, &base[siz], info.st_size - siz)) > 0) {
262 siz += res;
263 }
264 close(fd);
265 if (siz != info.st_size) {
266 free(base);
267 return(-1);
268 }
269 base[siz] = 0;
270 *mem = base;
271 *size = siz;
272 return(0);
273}
274
275static int unloadMem(const char *mem) {
276 free((char *)mem);
277 return(0);
278}
279
280/************************************************************************
281 * *
282 * Tests implementations *
283 * *
284 ************************************************************************/
285
Daniel Veillard4a5a9642005-06-27 10:40:55 +0000286/************************************************************************
287 * *
288 * Parse to SAX based tests *
289 * *
290 ************************************************************************/
291
292FILE *SAXdebug = NULL;
293
294/*
295 * empty SAX block
296 */
297xmlSAXHandler emptySAXHandlerStruct = {
298 NULL, /* internalSubset */
299 NULL, /* isStandalone */
300 NULL, /* hasInternalSubset */
301 NULL, /* hasExternalSubset */
302 NULL, /* resolveEntity */
303 NULL, /* getEntity */
304 NULL, /* entityDecl */
305 NULL, /* notationDecl */
306 NULL, /* attributeDecl */
307 NULL, /* elementDecl */
308 NULL, /* unparsedEntityDecl */
309 NULL, /* setDocumentLocator */
310 NULL, /* startDocument */
311 NULL, /* endDocument */
312 NULL, /* startElement */
313 NULL, /* endElement */
314 NULL, /* reference */
315 NULL, /* characters */
316 NULL, /* ignorableWhitespace */
317 NULL, /* processingInstruction */
318 NULL, /* comment */
319 NULL, /* xmlParserWarning */
320 NULL, /* xmlParserError */
321 NULL, /* xmlParserError */
322 NULL, /* getParameterEntity */
323 NULL, /* cdataBlock; */
324 NULL, /* externalSubset; */
325 1,
326 NULL,
327 NULL, /* startElementNs */
328 NULL, /* endElementNs */
329 NULL /* xmlStructuredErrorFunc */
330};
331
332static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct;
333int callbacks = 0;
334int quiet = 0;
335
336/**
337 * isStandaloneDebug:
338 * @ctxt: An XML parser context
339 *
340 * Is this document tagged standalone ?
341 *
342 * Returns 1 if true
343 */
344static int
345isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED)
346{
347 callbacks++;
348 if (quiet)
349 return(0);
350 fprintf(SAXdebug, "SAX.isStandalone()\n");
351 return(0);
352}
353
354/**
355 * hasInternalSubsetDebug:
356 * @ctxt: An XML parser context
357 *
358 * Does this document has an internal subset
359 *
360 * Returns 1 if true
361 */
362static int
363hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
364{
365 callbacks++;
366 if (quiet)
367 return(0);
368 fprintf(SAXdebug, "SAX.hasInternalSubset()\n");
369 return(0);
370}
371
372/**
373 * hasExternalSubsetDebug:
374 * @ctxt: An XML parser context
375 *
376 * Does this document has an external subset
377 *
378 * Returns 1 if true
379 */
380static int
381hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED)
382{
383 callbacks++;
384 if (quiet)
385 return(0);
386 fprintf(SAXdebug, "SAX.hasExternalSubset()\n");
387 return(0);
388}
389
390/**
391 * internalSubsetDebug:
392 * @ctxt: An XML parser context
393 *
394 * Does this document has an internal subset
395 */
396static void
397internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
398 const xmlChar *ExternalID, const xmlChar *SystemID)
399{
400 callbacks++;
401 if (quiet)
402 return;
403 fprintf(SAXdebug, "SAX.internalSubset(%s,", name);
404 if (ExternalID == NULL)
405 fprintf(SAXdebug, " ,");
406 else
407 fprintf(SAXdebug, " %s,", ExternalID);
408 if (SystemID == NULL)
409 fprintf(SAXdebug, " )\n");
410 else
411 fprintf(SAXdebug, " %s)\n", SystemID);
412}
413
414/**
415 * externalSubsetDebug:
416 * @ctxt: An XML parser context
417 *
418 * Does this document has an external subset
419 */
420static void
421externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
422 const xmlChar *ExternalID, const xmlChar *SystemID)
423{
424 callbacks++;
425 if (quiet)
426 return;
427 fprintf(SAXdebug, "SAX.externalSubset(%s,", name);
428 if (ExternalID == NULL)
429 fprintf(SAXdebug, " ,");
430 else
431 fprintf(SAXdebug, " %s,", ExternalID);
432 if (SystemID == NULL)
433 fprintf(SAXdebug, " )\n");
434 else
435 fprintf(SAXdebug, " %s)\n", SystemID);
436}
437
438/**
439 * resolveEntityDebug:
440 * @ctxt: An XML parser context
441 * @publicId: The public ID of the entity
442 * @systemId: The system ID of the entity
443 *
444 * Special entity resolver, better left to the parser, it has
445 * more context than the application layer.
446 * The default behaviour is to NOT resolve the entities, in that case
447 * the ENTITY_REF nodes are built in the structure (and the parameter
448 * values).
449 *
450 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
451 */
452static xmlParserInputPtr
453resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId)
454{
455 callbacks++;
456 if (quiet)
457 return(NULL);
458 /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */
459
460
461 fprintf(SAXdebug, "SAX.resolveEntity(");
462 if (publicId != NULL)
463 fprintf(SAXdebug, "%s", (char *)publicId);
464 else
465 fprintf(SAXdebug, " ");
466 if (systemId != NULL)
467 fprintf(SAXdebug, ", %s)\n", (char *)systemId);
468 else
469 fprintf(SAXdebug, ", )\n");
470/*********
471 if (systemId != NULL) {
472 return(xmlNewInputFromFile(ctxt, (char *) systemId));
473 }
474 *********/
475 return(NULL);
476}
477
478/**
479 * getEntityDebug:
480 * @ctxt: An XML parser context
481 * @name: The entity name
482 *
483 * Get an entity by name
484 *
485 * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
486 */
487static xmlEntityPtr
488getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
489{
490 callbacks++;
491 if (quiet)
492 return(NULL);
493 fprintf(SAXdebug, "SAX.getEntity(%s)\n", name);
494 return(NULL);
495}
496
497/**
498 * getParameterEntityDebug:
499 * @ctxt: An XML parser context
500 * @name: The entity name
501 *
502 * Get a parameter entity by name
503 *
504 * Returns the xmlParserInputPtr
505 */
506static xmlEntityPtr
507getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
508{
509 callbacks++;
510 if (quiet)
511 return(NULL);
512 fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name);
513 return(NULL);
514}
515
516
517/**
518 * entityDeclDebug:
519 * @ctxt: An XML parser context
520 * @name: the entity name
521 * @type: the entity type
522 * @publicId: The public ID of the entity
523 * @systemId: The system ID of the entity
524 * @content: the entity value (without processing).
525 *
526 * An entity definition has been parsed
527 */
528static void
529entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
530 const xmlChar *publicId, const xmlChar *systemId, xmlChar *content)
531{
532const xmlChar *nullstr = BAD_CAST "(null)";
533 /* not all libraries handle printing null pointers nicely */
534 if (publicId == NULL)
535 publicId = nullstr;
536 if (systemId == NULL)
537 systemId = nullstr;
538 if (content == NULL)
539 content = (xmlChar *)nullstr;
540 callbacks++;
541 if (quiet)
542 return;
543 fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n",
544 name, type, publicId, systemId, content);
545}
546
547/**
548 * attributeDeclDebug:
549 * @ctxt: An XML parser context
550 * @name: the attribute name
551 * @type: the attribute type
552 *
553 * An attribute definition has been parsed
554 */
555static void
556attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem,
557 const xmlChar * name, int type, int def,
558 const xmlChar * defaultValue, xmlEnumerationPtr tree)
559{
560 callbacks++;
561 if (quiet)
562 return;
563 if (defaultValue == NULL)
564 fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n",
565 elem, name, type, def);
566 else
567 fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n",
568 elem, name, type, def, defaultValue);
569 xmlFreeEnumeration(tree);
570}
571
572/**
573 * elementDeclDebug:
574 * @ctxt: An XML parser context
575 * @name: the element name
576 * @type: the element type
577 * @content: the element value (without processing).
578 *
579 * An element definition has been parsed
580 */
581static void
582elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type,
583 xmlElementContentPtr content ATTRIBUTE_UNUSED)
584{
585 callbacks++;
586 if (quiet)
587 return;
588 fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n",
589 name, type);
590}
591
592/**
593 * notationDeclDebug:
594 * @ctxt: An XML parser context
595 * @name: The name of the notation
596 * @publicId: The public ID of the entity
597 * @systemId: The system ID of the entity
598 *
599 * What to do when a notation declaration has been parsed.
600 */
601static void
602notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
603 const xmlChar *publicId, const xmlChar *systemId)
604{
605 callbacks++;
606 if (quiet)
607 return;
608 fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n",
609 (char *) name, (char *) publicId, (char *) systemId);
610}
611
612/**
613 * unparsedEntityDeclDebug:
614 * @ctxt: An XML parser context
615 * @name: The name of the entity
616 * @publicId: The public ID of the entity
617 * @systemId: The system ID of the entity
618 * @notationName: the name of the notation
619 *
620 * What to do when an unparsed entity declaration is parsed
621 */
622static void
623unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name,
624 const xmlChar *publicId, const xmlChar *systemId,
625 const xmlChar *notationName)
626{
627const xmlChar *nullstr = BAD_CAST "(null)";
628
629 if (publicId == NULL)
630 publicId = nullstr;
631 if (systemId == NULL)
632 systemId = nullstr;
633 if (notationName == NULL)
634 notationName = nullstr;
635 callbacks++;
636 if (quiet)
637 return;
638 fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n",
639 (char *) name, (char *) publicId, (char *) systemId,
640 (char *) notationName);
641}
642
643/**
644 * setDocumentLocatorDebug:
645 * @ctxt: An XML parser context
646 * @loc: A SAX Locator
647 *
648 * Receive the document locator at startup, actually xmlDefaultSAXLocator
649 * Everything is available on the context, so this is useless in our case.
650 */
651static void
652setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
653{
654 callbacks++;
655 if (quiet)
656 return;
657 fprintf(SAXdebug, "SAX.setDocumentLocator()\n");
658}
659
660/**
661 * startDocumentDebug:
662 * @ctxt: An XML parser context
663 *
664 * called when the document start being processed.
665 */
666static void
667startDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
668{
669 callbacks++;
670 if (quiet)
671 return;
672 fprintf(SAXdebug, "SAX.startDocument()\n");
673}
674
675/**
676 * endDocumentDebug:
677 * @ctxt: An XML parser context
678 *
679 * called when the document end has been detected.
680 */
681static void
682endDocumentDebug(void *ctx ATTRIBUTE_UNUSED)
683{
684 callbacks++;
685 if (quiet)
686 return;
687 fprintf(SAXdebug, "SAX.endDocument()\n");
688}
689
690/**
691 * startElementDebug:
692 * @ctxt: An XML parser context
693 * @name: The element name
694 *
695 * called when an opening tag has been processed.
696 */
697static void
698startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
699{
700 int i;
701
702 callbacks++;
703 if (quiet)
704 return;
705 fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
706 if (atts != NULL) {
707 for (i = 0;(atts[i] != NULL);i++) {
708 fprintf(SAXdebug, ", %s='", atts[i++]);
709 if (atts[i] != NULL)
710 fprintf(SAXdebug, "%s'", atts[i]);
711 }
712 }
713 fprintf(SAXdebug, ")\n");
714}
715
716/**
717 * endElementDebug:
718 * @ctxt: An XML parser context
719 * @name: The element name
720 *
721 * called when the end of an element has been detected.
722 */
723static void
724endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
725{
726 callbacks++;
727 if (quiet)
728 return;
729 fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name);
730}
731
732/**
733 * charactersDebug:
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 */
741static void
742charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
743{
744 char output[40];
745 int i;
746
747 callbacks++;
748 if (quiet)
749 return;
750 for (i = 0;(i<len) && (i < 30);i++)
751 output[i] = ch[i];
752 output[i] = 0;
753
754 fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
755}
756
757/**
758 * referenceDebug:
759 * @ctxt: An XML parser context
760 * @name: The entity name
761 *
762 * called when an entity reference is detected.
763 */
764static void
765referenceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name)
766{
767 callbacks++;
768 if (quiet)
769 return;
770 fprintf(SAXdebug, "SAX.reference(%s)\n", name);
771}
772
773/**
774 * ignorableWhitespaceDebug:
775 * @ctxt: An XML parser context
776 * @ch: a xmlChar string
777 * @start: the first char in the string
778 * @len: the number of xmlChar
779 *
780 * receiving some ignorable whitespaces from the parser.
781 * Question: how much at a time ???
782 */
783static void
784ignorableWhitespaceDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
785{
786 char output[40];
787 int i;
788
789 callbacks++;
790 if (quiet)
791 return;
792 for (i = 0;(i<len) && (i < 30);i++)
793 output[i] = ch[i];
794 output[i] = 0;
795 fprintf(SAXdebug, "SAX.ignorableWhitespace(%s, %d)\n", output, len);
796}
797
798/**
799 * processingInstructionDebug:
800 * @ctxt: An XML parser context
801 * @target: the target name
802 * @data: the PI data's
803 * @len: the number of xmlChar
804 *
805 * A processing instruction has been parsed.
806 */
807static void
808processingInstructionDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *target,
809 const xmlChar *data)
810{
811 callbacks++;
812 if (quiet)
813 return;
814 if (data != NULL)
815 fprintf(SAXdebug, "SAX.processingInstruction(%s, %s)\n",
816 (char *) target, (char *) data);
817 else
818 fprintf(SAXdebug, "SAX.processingInstruction(%s, NULL)\n",
819 (char *) target);
820}
821
822/**
823 * cdataBlockDebug:
824 * @ctx: the user data (XML parser context)
825 * @value: The pcdata content
826 * @len: the block length
827 *
828 * called when a pcdata block has been parsed
829 */
830static void
831cdataBlockDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value, int len)
832{
833 callbacks++;
834 if (quiet)
835 return;
836 fprintf(SAXdebug, "SAX.pcdata(%.20s, %d)\n",
837 (char *) value, len);
838}
839
840/**
841 * commentDebug:
842 * @ctxt: An XML parser context
843 * @value: the comment content
844 *
845 * A comment has been parsed.
846 */
847static void
848commentDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *value)
849{
850 callbacks++;
851 if (quiet)
852 return;
853 fprintf(SAXdebug, "SAX.comment(%s)\n", value);
854}
855
856/**
857 * warningDebug:
858 * @ctxt: An XML parser context
859 * @msg: the message to display/transmit
860 * @...: extra parameters for the message display
861 *
862 * Display and format a warning messages, gives file, line, position and
863 * extra parameters.
864 */
865static void
866warningDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
867{
868 va_list args;
869
870 callbacks++;
871 if (quiet)
872 return;
873 va_start(args, msg);
874 fprintf(SAXdebug, "SAX.warning: ");
875 vfprintf(SAXdebug, msg, args);
876 va_end(args);
877}
878
879/**
880 * errorDebug:
881 * @ctxt: An XML parser context
882 * @msg: the message to display/transmit
883 * @...: extra parameters for the message display
884 *
885 * Display and format a error messages, gives file, line, position and
886 * extra parameters.
887 */
888static void
889errorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
890{
891 va_list args;
892
893 callbacks++;
894 if (quiet)
895 return;
896 va_start(args, msg);
897 fprintf(SAXdebug, "SAX.error: ");
898 vfprintf(SAXdebug, msg, args);
899 va_end(args);
900}
901
902/**
903 * fatalErrorDebug:
904 * @ctxt: An XML parser context
905 * @msg: the message to display/transmit
906 * @...: extra parameters for the message display
907 *
908 * Display and format a fatalError messages, gives file, line, position and
909 * extra parameters.
910 */
911static void
912fatalErrorDebug(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...)
913{
914 va_list args;
915
916 callbacks++;
917 if (quiet)
918 return;
919 va_start(args, msg);
920 fprintf(SAXdebug, "SAX.fatalError: ");
921 vfprintf(SAXdebug, msg, args);
922 va_end(args);
923}
924
925xmlSAXHandler debugSAXHandlerStruct = {
926 internalSubsetDebug,
927 isStandaloneDebug,
928 hasInternalSubsetDebug,
929 hasExternalSubsetDebug,
930 resolveEntityDebug,
931 getEntityDebug,
932 entityDeclDebug,
933 notationDeclDebug,
934 attributeDeclDebug,
935 elementDeclDebug,
936 unparsedEntityDeclDebug,
937 setDocumentLocatorDebug,
938 startDocumentDebug,
939 endDocumentDebug,
940 startElementDebug,
941 endElementDebug,
942 referenceDebug,
943 charactersDebug,
944 ignorableWhitespaceDebug,
945 processingInstructionDebug,
946 commentDebug,
947 warningDebug,
948 errorDebug,
949 fatalErrorDebug,
950 getParameterEntityDebug,
951 cdataBlockDebug,
952 externalSubsetDebug,
953 1,
954 NULL,
955 NULL,
956 NULL,
957 NULL
958};
959
960xmlSAXHandlerPtr debugSAXHandler = &debugSAXHandlerStruct;
961
962/*
963 * SAX2 specific callbacks
964 */
965/**
966 * startElementNsDebug:
967 * @ctxt: An XML parser context
968 * @name: The element name
969 *
970 * called when an opening tag has been processed.
971 */
972static void
973startElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
974 const xmlChar *localname,
975 const xmlChar *prefix,
976 const xmlChar *URI,
977 int nb_namespaces,
978 const xmlChar **namespaces,
979 int nb_attributes,
980 int nb_defaulted,
981 const xmlChar **attributes)
982{
983 int i;
984
985 callbacks++;
986 if (quiet)
987 return;
988 fprintf(SAXdebug, "SAX.startElementNs(%s", (char *) localname);
989 if (prefix == NULL)
990 fprintf(SAXdebug, ", NULL");
991 else
992 fprintf(SAXdebug, ", %s", (char *) prefix);
993 if (URI == NULL)
994 fprintf(SAXdebug, ", NULL");
995 else
996 fprintf(SAXdebug, ", '%s'", (char *) URI);
997 fprintf(SAXdebug, ", %d", nb_namespaces);
998
999 if (namespaces != NULL) {
1000 for (i = 0;i < nb_namespaces * 2;i++) {
1001 fprintf(SAXdebug, ", xmlns");
1002 if (namespaces[i] != NULL)
1003 fprintf(SAXdebug, ":%s", namespaces[i]);
1004 i++;
1005 fprintf(SAXdebug, "='%s'", namespaces[i]);
1006 }
1007 }
1008 fprintf(SAXdebug, ", %d, %d", nb_attributes, nb_defaulted);
1009 if (attributes != NULL) {
1010 for (i = 0;i < nb_attributes * 5;i += 5) {
1011 if (attributes[i + 1] != NULL)
1012 fprintf(SAXdebug, ", %s:%s='", attributes[i + 1], attributes[i]);
1013 else
1014 fprintf(SAXdebug, ", %s='", attributes[i]);
1015 fprintf(SAXdebug, "%.4s...', %d", attributes[i + 3],
1016 (int)(attributes[i + 4] - attributes[i + 3]));
1017 }
1018 }
1019 fprintf(SAXdebug, ")\n");
1020}
1021
1022/**
1023 * endElementDebug:
1024 * @ctxt: An XML parser context
1025 * @name: The element name
1026 *
1027 * called when the end of an element has been detected.
1028 */
1029static void
1030endElementNsDebug(void *ctx ATTRIBUTE_UNUSED,
1031 const xmlChar *localname,
1032 const xmlChar *prefix,
1033 const xmlChar *URI)
1034{
1035 callbacks++;
1036 if (quiet)
1037 return;
1038 fprintf(SAXdebug, "SAX.endElementNs(%s", (char *) localname);
1039 if (prefix == NULL)
1040 fprintf(SAXdebug, ", NULL");
1041 else
1042 fprintf(SAXdebug, ", %s", (char *) prefix);
1043 if (URI == NULL)
1044 fprintf(SAXdebug, ", NULL)\n");
1045 else
1046 fprintf(SAXdebug, ", '%s')\n", (char *) URI);
1047}
1048
1049xmlSAXHandler debugSAX2HandlerStruct = {
1050 internalSubsetDebug,
1051 isStandaloneDebug,
1052 hasInternalSubsetDebug,
1053 hasExternalSubsetDebug,
1054 resolveEntityDebug,
1055 getEntityDebug,
1056 entityDeclDebug,
1057 notationDeclDebug,
1058 attributeDeclDebug,
1059 elementDeclDebug,
1060 unparsedEntityDeclDebug,
1061 setDocumentLocatorDebug,
1062 startDocumentDebug,
1063 endDocumentDebug,
1064 NULL,
1065 NULL,
1066 referenceDebug,
1067 charactersDebug,
1068 ignorableWhitespaceDebug,
1069 processingInstructionDebug,
1070 commentDebug,
1071 warningDebug,
1072 errorDebug,
1073 fatalErrorDebug,
1074 getParameterEntityDebug,
1075 cdataBlockDebug,
1076 externalSubsetDebug,
1077 XML_SAX2_MAGIC,
1078 NULL,
1079 startElementNsDebug,
1080 endElementNsDebug,
1081 NULL
1082};
1083
1084xmlSAXHandlerPtr debugSAX2Handler = &debugSAX2HandlerStruct;
1085
1086/**
Daniel Veillardfc319af2005-06-27 12:44:55 +00001087 * htmlstartElementDebug:
1088 * @ctxt: An XML parser context
1089 * @name: The element name
1090 *
1091 * called when an opening tag has been processed.
1092 */
1093static void
1094htmlstartElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts)
1095{
1096 int i;
1097
1098 fprintf(SAXdebug, "SAX.startElement(%s", (char *) name);
1099 if (atts != NULL) {
1100 for (i = 0;(atts[i] != NULL);i++) {
1101 fprintf(SAXdebug, ", %s", atts[i++]);
1102 if (atts[i] != NULL) {
1103 unsigned char output[40];
1104 const unsigned char *att = atts[i];
1105 int outlen, attlen;
1106 fprintf(SAXdebug, "='");
1107 while ((attlen = strlen((char*)att)) > 0) {
1108 outlen = sizeof output - 1;
1109 htmlEncodeEntities(output, &outlen, att, &attlen, '\'');
1110 output[outlen] = 0;
1111 fprintf(SAXdebug, "%s", (char *) output);
1112 att += attlen;
1113 }
1114 fprintf(SAXdebug, "'");
1115 }
1116 }
1117 }
1118 fprintf(SAXdebug, ")\n");
1119}
1120
1121/**
1122 * htmlcharactersDebug:
1123 * @ctxt: An XML parser context
1124 * @ch: a xmlChar string
1125 * @len: the number of xmlChar
1126 *
1127 * receiving some chars from the parser.
1128 * Question: how much at a time ???
1129 */
1130static void
1131htmlcharactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1132{
1133 unsigned char output[40];
1134 int inlen = len, outlen = 30;
1135
1136 htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1137 output[outlen] = 0;
1138
1139 fprintf(SAXdebug, "SAX.characters(%s, %d)\n", output, len);
1140}
1141
1142/**
1143 * htmlcdataDebug:
1144 * @ctxt: An XML parser context
1145 * @ch: a xmlChar string
1146 * @len: the number of xmlChar
1147 *
1148 * receiving some cdata chars from the parser.
1149 * Question: how much at a time ???
1150 */
1151static void
1152htmlcdataDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len)
1153{
1154 unsigned char output[40];
1155 int inlen = len, outlen = 30;
1156
1157 htmlEncodeEntities(output, &outlen, ch, &inlen, 0);
1158 output[outlen] = 0;
1159
1160 fprintf(SAXdebug, "SAX.cdata(%s, %d)\n", output, len);
1161}
1162
1163xmlSAXHandler debugHTMLSAXHandlerStruct = {
1164 internalSubsetDebug,
1165 isStandaloneDebug,
1166 hasInternalSubsetDebug,
1167 hasExternalSubsetDebug,
1168 resolveEntityDebug,
1169 getEntityDebug,
1170 entityDeclDebug,
1171 notationDeclDebug,
1172 attributeDeclDebug,
1173 elementDeclDebug,
1174 unparsedEntityDeclDebug,
1175 setDocumentLocatorDebug,
1176 startDocumentDebug,
1177 endDocumentDebug,
1178 htmlstartElementDebug,
1179 endElementDebug,
1180 referenceDebug,
1181 htmlcharactersDebug,
1182 ignorableWhitespaceDebug,
1183 processingInstructionDebug,
1184 commentDebug,
1185 warningDebug,
1186 errorDebug,
1187 fatalErrorDebug,
1188 getParameterEntityDebug,
1189 htmlcdataDebug,
1190 externalSubsetDebug,
1191 1,
1192 NULL,
1193 NULL,
1194 NULL,
1195 NULL
1196};
1197
1198xmlSAXHandlerPtr debugHTMLSAXHandler = &debugHTMLSAXHandlerStruct;
1199/**
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001200 * saxParseTest:
1201 * @filename: the file to parse
1202 * @result: the file with expected result
1203 * @err: the file with error messages
1204 *
1205 * Parse a file using the SAX API and check for errors.
1206 *
1207 * Returns 0 in case of success, an error code otherwise
1208 */
1209static int
Daniel Veillardfc319af2005-06-27 12:44:55 +00001210saxParseTest(const char *filename, const char *result,
1211 const char *err ATTRIBUTE_UNUSED,
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001212 int options) {
1213 int ret;
1214 char *temp;
1215
1216 temp = resultFilename(filename, "", ".res");
1217 if (temp == NULL) {
1218 fprintf(stderr, "out of memory\n");
1219 fatalError();
1220 }
1221 SAXdebug = fopen(temp, "w");
1222 if (SAXdebug == NULL) {
1223 fprintf(stderr, "Failed to write to %s\n", temp);
1224 free(temp);
1225 return(-1);
1226 }
1227
Daniel Veillardfc319af2005-06-27 12:44:55 +00001228#ifdef LIBXML_HTML_ENABLED
1229 if (options & XML_PARSE_HTML) {
1230 htmlSAXParseFile(filename, NULL, emptySAXHandler, NULL);
1231 ret = 0;
1232 } else
1233#endif
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001234 ret = xmlSAXUserParseFile(emptySAXHandler, NULL, filename);
1235 if (ret == XML_WAR_UNDECLARED_ENTITY) {
1236 fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1237 ret = 0;
1238 }
1239 if (ret != 0) {
1240 fprintf(stderr, "Failed to parse %s\n", filename);
1241 return(1);
1242 }
Daniel Veillardfc319af2005-06-27 12:44:55 +00001243#ifdef LIBXML_HTML_ENABLED
1244 if (options & XML_PARSE_HTML) {
1245 htmlSAXParseFile(filename, NULL, debugHTMLSAXHandler, NULL);
1246 ret = 0;
1247 } else
1248#endif
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001249 if (options & XML_PARSE_SAX1) {
1250 ret = xmlSAXUserParseFile(debugSAXHandler, NULL, filename);
1251 } else {
1252 ret = xmlSAXUserParseFile(debugSAX2Handler, NULL, filename);
1253 }
1254 if (ret == XML_WAR_UNDECLARED_ENTITY) {
1255 fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret);
1256 ret = 0;
1257 }
1258 fclose(SAXdebug);
1259 if (compareFiles(temp, result)) {
1260 ret = 1;
1261 }
1262 unlink(temp);
1263 free(temp);
1264
1265 return(ret);
1266}
1267
1268/************************************************************************
1269 * *
1270 * Parse to tree based tests *
1271 * *
1272 ************************************************************************/
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001273/**
1274 * oldParseTest:
1275 * @filename: the file to parse
1276 * @result: the file with expected result
Daniel Veillardfd110d22005-06-27 00:02:02 +00001277 * @err: the file with error messages: unused
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001278 *
1279 * Parse a file using the old xmlParseFile API, then serialize back
1280 * reparse the result and serialize again, then check for deviation
1281 * in serialization.
1282 *
1283 * Returns 0 in case of success, an error code otherwise
1284 */
1285static int
Daniel Veillardc111c152005-06-27 08:22:10 +00001286oldParseTest(const char *filename, const char *result,
1287 const char *err ATTRIBUTE_UNUSED,
1288 int options ATTRIBUTE_UNUSED) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001289 xmlDocPtr doc;
1290 char *temp;
1291 int res = 0;
1292
1293 /*
1294 * base of the test, parse with the old API
1295 */
1296 doc = xmlParseFile(filename);
1297 if (doc == NULL)
1298 return(1);
1299 temp = resultFilename(filename, "", ".res");
1300 if (temp == NULL) {
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001301 fprintf(stderr, "out of memory\n");
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001302 fatalError();
1303 }
1304 xmlSaveFile(temp, doc);
1305 if (compareFiles(temp, result)) {
1306 res = 1;
1307 }
1308 xmlFreeDoc(doc);
1309
1310 /*
1311 * Parse the saved result to make sure the round trip is okay
1312 */
1313 doc = xmlParseFile(temp);
1314 if (doc == NULL)
1315 return(1);
1316 xmlSaveFile(temp, doc);
1317 if (compareFiles(temp, result)) {
1318 res = 1;
1319 }
1320 xmlFreeDoc(doc);
1321
1322 unlink(temp);
1323 free(temp);
1324 return(res);
1325}
1326
Daniel Veillardfc319af2005-06-27 12:44:55 +00001327#ifdef LIBXML_PUSH_ENABLED
1328/**
1329 * pushParseTest:
1330 * @filename: the file to parse
1331 * @result: the file with expected result
1332 * @err: the file with error messages: unused
1333 *
1334 * Parse a file using the Push API, then serialize back
1335 * to check for content.
1336 *
1337 * Returns 0 in case of success, an error code otherwise
1338 */
1339static int
1340pushParseTest(const char *filename, const char *result,
1341 const char *err ATTRIBUTE_UNUSED,
1342 int options) {
1343 xmlParserCtxtPtr ctxt;
1344 xmlDocPtr doc;
1345 const char *base;
1346 int size, res;
1347 int cur = 0;
1348
1349 /*
1350 * load the document in memory and work from there.
1351 */
1352 if (loadMem(filename, &base, &size) != 0) {
1353 fprintf(stderr, "Failed to load %s\n", filename);
1354 return(-1);
1355 }
1356
1357#ifdef LIBXML_HTML_ENABLED
1358 if (options & XML_PARSE_HTML)
1359 ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename,
1360 XML_CHAR_ENCODING_NONE);
1361 else
1362#endif
1363 ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, 4, filename);
1364 xmlCtxtUseOptions(ctxt, options);
1365 cur += 4;
1366 while (cur < size) {
1367 if (cur + 1024 >= size) {
1368#ifdef LIBXML_HTML_ENABLED
1369 if (options & XML_PARSE_HTML)
1370 htmlParseChunk(ctxt, base + cur, size - cur, 1);
1371 else
1372#endif
1373 xmlParseChunk(ctxt, base + cur, size - cur, 1);
1374 break;
1375 } else {
1376#ifdef LIBXML_HTML_ENABLED
1377 if (options & XML_PARSE_HTML)
1378 htmlParseChunk(ctxt, base + cur, 1024, 0);
1379 else
1380#endif
1381 xmlParseChunk(ctxt, base + cur, 1024, 0);
1382 cur += 1024;
1383 }
1384 }
1385 doc = ctxt->myDoc;
1386#ifdef LIBXML_HTML_ENABLED
1387 if (options & XML_PARSE_HTML)
1388 res = 1;
1389 else
1390#endif
1391 res = ctxt->wellFormed;
1392 xmlFreeParserCtxt(ctxt);
1393 free((char *)base);
1394 if (!res) {
1395 xmlFreeDoc(doc);
1396 fprintf(stderr, "Failed to parse %s\n", filename);
1397 return(-1);
1398 }
1399#ifdef LIBXML_HTML_ENABLED
1400 if (options & XML_PARSE_HTML)
1401 htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1402 else
1403#endif
1404 xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1405 xmlFreeDoc(doc);
1406 res = compareFileMem(result, base, size);
1407 if ((base == NULL) || (res != 0)) {
1408 if (base != NULL)
1409 xmlFree((char *)base);
1410 fprintf(stderr, "Result for %s failed\n", filename);
1411 return(-1);
1412 }
1413 xmlFree((char *)base);
1414 if (err != NULL) {
1415 res = compareFileMem(err, testErrors, testErrorsSize);
1416 if (res != 0) {
1417 fprintf(stderr, "Error for %s failed\n", filename);
1418 return(-1);
1419 }
1420 }
1421 return(0);
1422}
1423#endif
1424
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001425/**
1426 * memParseTest:
1427 * @filename: the file to parse
1428 * @result: the file with expected result
Daniel Veillardfd110d22005-06-27 00:02:02 +00001429 * @err: the file with error messages: unused
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001430 *
1431 * Parse a file using the old xmlReadMemory API, then serialize back
1432 * reparse the result and serialize again, then check for deviation
1433 * in serialization.
1434 *
1435 * Returns 0 in case of success, an error code otherwise
1436 */
1437static int
Daniel Veillardc111c152005-06-27 08:22:10 +00001438memParseTest(const char *filename, const char *result,
1439 const char *err ATTRIBUTE_UNUSED,
1440 int options ATTRIBUTE_UNUSED) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001441 xmlDocPtr doc;
1442 const char *base;
Daniel Veillardfd110d22005-06-27 00:02:02 +00001443 int size, res;
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001444
1445 /*
1446 * load and parse the memory
1447 */
1448 if (loadMem(filename, &base, &size) != 0) {
1449 fprintf(stderr, "Failed to load %s\n", filename);
1450 return(-1);
1451 }
1452
1453 doc = xmlReadMemory(base, size, filename, NULL, 0);
1454 unloadMem(base);
1455 if (doc == NULL) {
1456 return(1);
1457 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001458 xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001459 xmlFreeDoc(doc);
Daniel Veillardfd110d22005-06-27 00:02:02 +00001460 res = compareFileMem(result, base, size);
1461 if ((base == NULL) || (res != 0)) {
1462 if (base != NULL)
1463 xmlFree((char *)base);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001464 fprintf(stderr, "Result for %s failed\n", filename);
1465 return(-1);
1466 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001467 xmlFree((char *)base);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001468 return(0);
1469}
1470
1471/**
1472 * noentParseTest:
1473 * @filename: the file to parse
1474 * @result: the file with expected result
Daniel Veillardfd110d22005-06-27 00:02:02 +00001475 * @err: the file with error messages: unused
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001476 *
1477 * Parse a file with entity resolution, then serialize back
1478 * reparse the result and serialize again, then check for deviation
1479 * in serialization.
1480 *
1481 * Returns 0 in case of success, an error code otherwise
1482 */
1483static int
Daniel Veillardc111c152005-06-27 08:22:10 +00001484noentParseTest(const char *filename, const char *result,
1485 const char *err ATTRIBUTE_UNUSED,
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001486 int options) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001487 xmlDocPtr doc;
1488 char *temp;
1489 int res = 0;
1490
1491 /*
1492 * base of the test, parse with the old API
1493 */
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001494 doc = xmlReadFile(filename, NULL, options);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001495 if (doc == NULL)
1496 return(1);
1497 temp = resultFilename(filename, "", ".res");
1498 if (temp == NULL) {
1499 fprintf(stderr, "Out of memory\n");
1500 fatalError();
1501 }
1502 xmlSaveFile(temp, doc);
1503 if (compareFiles(temp, result)) {
1504 res = 1;
1505 }
1506 xmlFreeDoc(doc);
1507
1508 /*
1509 * Parse the saved result to make sure the round trip is okay
1510 */
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001511 doc = xmlReadFile(filename, NULL, options);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001512 if (doc == NULL)
1513 return(1);
1514 xmlSaveFile(temp, doc);
1515 if (compareFiles(temp, result)) {
1516 res = 1;
1517 }
1518 xmlFreeDoc(doc);
1519
1520 unlink(temp);
1521 free(temp);
1522 return(res);
1523}
1524
Daniel Veillardfd110d22005-06-27 00:02:02 +00001525/**
Daniel Veillardc111c152005-06-27 08:22:10 +00001526 * errParseTest:
Daniel Veillardfd110d22005-06-27 00:02:02 +00001527 * @filename: the file to parse
1528 * @result: the file with expected result
1529 * @err: the file with error messages
1530 *
1531 * Parse a file using the xmlReadFile API and check for errors.
1532 *
1533 * Returns 0 in case of success, an error code otherwise
1534 */
1535static int
Daniel Veillardc111c152005-06-27 08:22:10 +00001536errParseTest(const char *filename, const char *result, const char *err,
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001537 int options) {
Daniel Veillardfd110d22005-06-27 00:02:02 +00001538 xmlDocPtr doc;
1539 const char *base;
Daniel Veillarddbee0f12005-06-27 13:42:57 +00001540 int size, res = 0;
Daniel Veillardfd110d22005-06-27 00:02:02 +00001541
Daniel Veillardfc319af2005-06-27 12:44:55 +00001542#ifdef LIBXML_HTML_ENABLED
1543 if (options & XML_PARSE_HTML) {
1544 doc = htmlReadFile(filename, NULL, options);
1545 } else
1546#endif
1547 {
1548 xmlGetWarningsDefaultValue = 1;
1549 doc = xmlReadFile(filename, NULL, options);
1550 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001551 xmlGetWarningsDefaultValue = 0;
Daniel Veillarddbee0f12005-06-27 13:42:57 +00001552 if (result) {
1553 if (doc == NULL) {
1554 base = "";
1555 size = 0;
1556 } else {
1557#ifdef LIBXML_HTML_ENABLED
1558 if (options & XML_PARSE_HTML) {
1559 htmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1560 } else
1561#endif
1562 xmlDocDumpMemory(doc, (xmlChar **) &base, &size);
1563 }
1564 res = compareFileMem(result, base, size);
1565 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001566 if (doc != NULL) {
1567 if (base != NULL)
1568 xmlFree((char *)base);
1569 xmlFreeDoc(doc);
1570 }
1571 if (res != 0) {
1572 fprintf(stderr, "Result for %s failed\n", filename);
1573 return(-1);
1574 }
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001575 if (err != NULL) {
1576 res = compareFileMem(err, testErrors, testErrorsSize);
1577 if (res != 0) {
1578 fprintf(stderr, "Error for %s failed\n", filename);
1579 return(-1);
1580 }
Daniel Veillarddbee0f12005-06-27 13:42:57 +00001581 } else if (options & XML_PARSE_DTDVALID) {
1582 if (testErrorsSize != 0)
1583 fprintf(stderr, "Validation for %s failed\n", filename);
Daniel Veillardfd110d22005-06-27 00:02:02 +00001584 }
1585
1586 return(0);
1587}
1588
Daniel Veillardc111c152005-06-27 08:22:10 +00001589#ifdef LIBXML_READER_ENABLED
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001590/************************************************************************
1591 * *
1592 * Reader based tests *
1593 * *
1594 ************************************************************************/
1595
Daniel Veillardc111c152005-06-27 08:22:10 +00001596static void processNode(FILE *out, xmlTextReaderPtr reader) {
1597 const xmlChar *name, *value;
1598 int type, empty;
1599
1600 type = xmlTextReaderNodeType(reader);
1601 empty = xmlTextReaderIsEmptyElement(reader);
1602
1603 name = xmlTextReaderConstName(reader);
1604 if (name == NULL)
1605 name = BAD_CAST "--";
1606
1607 value = xmlTextReaderConstValue(reader);
1608
1609
1610 fprintf(out, "%d %d %s %d %d",
1611 xmlTextReaderDepth(reader),
1612 type,
1613 name,
1614 empty,
1615 xmlTextReaderHasValue(reader));
1616 if (value == NULL)
1617 fprintf(out, "\n");
1618 else {
1619 fprintf(out, " %s\n", value);
1620 }
1621#if 0
1622#ifdef LIBXML_PATTERN_ENABLED
1623 if (patternc) {
1624 xmlChar *path = NULL;
1625 int match = -1;
1626
1627 if (type == XML_READER_TYPE_ELEMENT) {
1628 /* do the check only on element start */
1629 match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader));
1630
1631 if (match) {
1632 path = xmlGetNodePath(xmlTextReaderCurrentNode(reader));
1633 fprintf(out, "Node %s matches pattern %s\n", path, pattern);
1634 }
1635 }
1636 if (patstream != NULL) {
1637 int ret;
1638
1639 if (type == XML_READER_TYPE_ELEMENT) {
1640 ret = xmlStreamPush(patstream,
1641 xmlTextReaderConstLocalName(reader),
1642 xmlTextReaderConstNamespaceUri(reader));
1643 if (ret < 0) {
1644 fprintf(stderr, "xmlStreamPush() failure\n");
1645 xmlFreeStreamCtxt(patstream);
1646 patstream = NULL;
1647 } else if (ret != match) {
1648 if (path == NULL) {
1649 path = xmlGetNodePath(
1650 xmlTextReaderCurrentNode(reader));
1651 }
1652 fprintf(stderr,
1653 "xmlPatternMatch and xmlStreamPush disagree\n");
1654 fprintf(stderr,
1655 " pattern %s node %s\n",
1656 pattern, path);
1657 }
1658
1659
1660 }
1661 if ((type == XML_READER_TYPE_END_ELEMENT) ||
1662 ((type == XML_READER_TYPE_ELEMENT) && (empty))) {
1663 ret = xmlStreamPop(patstream);
1664 if (ret < 0) {
1665 fprintf(stderr, "xmlStreamPop() failure\n");
1666 xmlFreeStreamCtxt(patstream);
1667 patstream = NULL;
1668 }
1669 }
1670 }
1671 if (path != NULL)
1672 xmlFree(path);
1673 }
1674#endif
1675#endif
1676}
Daniel Veillardc111c152005-06-27 08:22:10 +00001677static int
Daniel Veillardc950d702005-06-27 09:15:06 +00001678streamProcessTest(const char *filename, const char *result, const char *err,
1679 xmlTextReaderPtr reader) {
Daniel Veillardc111c152005-06-27 08:22:10 +00001680 int ret;
1681 char *temp = NULL;
1682 FILE *t = NULL;
1683
Daniel Veillardc950d702005-06-27 09:15:06 +00001684 if (reader == NULL)
1685 return(-1);
1686
Daniel Veillardc111c152005-06-27 08:22:10 +00001687 if (result != NULL) {
1688 temp = resultFilename(filename, "", ".res");
1689 if (temp == NULL) {
1690 fprintf(stderr, "Out of memory\n");
1691 fatalError();
1692 }
1693 t = fopen(temp, "w");
1694 if (t == NULL) {
1695 fprintf(stderr, "Can't open temp file %s\n", temp);
1696 free(temp);
1697 return(-1);
1698 }
1699 }
1700 xmlGetWarningsDefaultValue = 1;
Daniel Veillardc111c152005-06-27 08:22:10 +00001701 ret = xmlTextReaderRead(reader);
1702 while (ret == 1) {
1703 if (t != NULL)
1704 processNode(t, reader);
1705 ret = xmlTextReaderRead(reader);
1706 }
1707 if (ret != 0) {
1708 testErrorHandler(NULL, "%s : failed to parse\n", filename);
1709 }
Daniel Veillardc111c152005-06-27 08:22:10 +00001710 xmlGetWarningsDefaultValue = 0;
1711 if (t != NULL) {
1712 fclose(t);
1713 ret = compareFiles(temp, result);
1714 unlink(temp);
1715 free(temp);
1716 if (ret) {
1717 fprintf(stderr, "Result for %s failed\n", filename);
1718 return(-1);
1719 }
1720 }
1721 if (err != NULL) {
1722 ret = compareFileMem(err, testErrors, testErrorsSize);
1723 if (ret != 0) {
1724 fprintf(stderr, "Error for %s failed\n", filename);
1725 return(-1);
1726 }
1727 }
1728
1729 return(0);
1730}
Daniel Veillardc950d702005-06-27 09:15:06 +00001731
1732/**
1733 * streamParseTest:
1734 * @filename: the file to parse
1735 * @result: the file with expected result
1736 * @err: the file with error messages
1737 *
1738 * Parse a file using the reader API and check for errors.
1739 *
1740 * Returns 0 in case of success, an error code otherwise
1741 */
1742static int
1743streamParseTest(const char *filename, const char *result, const char *err,
1744 int options) {
1745 xmlTextReaderPtr reader;
1746 int ret;
1747
1748 reader = xmlReaderForFile(filename, NULL, options);
1749 ret = streamProcessTest(filename, result, err, reader);
1750 xmlFreeTextReader(reader);
1751 return(ret);
1752}
1753
1754/**
1755 * walkerParseTest:
1756 * @filename: the file to parse
1757 * @result: the file with expected result
1758 * @err: the file with error messages
1759 *
1760 * Parse a file using the walker, i.e. a reader built from a atree.
1761 *
1762 * Returns 0 in case of success, an error code otherwise
1763 */
1764static int
1765walkerParseTest(const char *filename, const char *result, const char *err,
1766 int options) {
1767 xmlDocPtr doc;
1768 xmlTextReaderPtr reader;
1769 int ret;
1770
1771 doc = xmlReadFile(filename, NULL, options);
1772 if (doc == NULL) {
1773 fprintf(stderr, "Failed to parse %s\n", filename);
1774 return(-1);
1775 }
1776 reader = xmlReaderWalker(doc);
1777 ret = streamProcessTest(filename, result, err, reader);
1778 xmlFreeTextReader(reader);
1779 xmlFreeDoc(doc);
1780 return(ret);
1781}
1782
1783/**
1784 * streamMemParseTest:
1785 * @filename: the file to parse
1786 * @result: the file with expected result
1787 * @err: the file with error messages
1788 *
1789 * Parse a file using the reader API from memory and check for errors.
1790 *
1791 * Returns 0 in case of success, an error code otherwise
1792 */
1793static int
1794streamMemParseTest(const char *filename, const char *result, const char *err,
1795 int options) {
1796 xmlTextReaderPtr reader;
1797 int ret;
1798 const char *base;
1799 int size;
1800
1801 /*
1802 * load and parse the memory
1803 */
1804 if (loadMem(filename, &base, &size) != 0) {
1805 fprintf(stderr, "Failed to load %s\n", filename);
1806 return(-1);
1807 }
1808 reader = xmlReaderForMemory(base, size, filename, NULL, options);
1809 ret = streamProcessTest(filename, result, err, reader);
1810 free((char *)base);
1811 xmlFreeTextReader(reader);
1812 return(ret);
1813}
Daniel Veillardc111c152005-06-27 08:22:10 +00001814#endif
1815
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001816/************************************************************************
1817 * *
1818 * Tests Descriptions *
1819 * *
1820 ************************************************************************/
1821
1822static
1823testDesc testDescriptions[] = {
Daniel Veillardfd110d22005-06-27 00:02:02 +00001824 { "XML regression tests" ,
Daniel Veillardc111c152005-06-27 08:22:10 +00001825 oldParseTest, "./test/*", "result/", "", NULL,
1826 0 },
Daniel Veillardfd110d22005-06-27 00:02:02 +00001827 { "XML regression tests on memory" ,
Daniel Veillardc111c152005-06-27 08:22:10 +00001828 memParseTest, "./test/*", "result/", "", NULL,
1829 0 },
Daniel Veillardfd110d22005-06-27 00:02:02 +00001830 { "XML entity subst regression tests" ,
Daniel Veillardc111c152005-06-27 08:22:10 +00001831 noentParseTest, "./test/*", "result/noent/", "", NULL,
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001832 XML_PARSE_NOENT },
Daniel Veillardfd110d22005-06-27 00:02:02 +00001833 { "XML Namespaces regression tests",
Daniel Veillardc111c152005-06-27 08:22:10 +00001834 errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err",
1835 0 },
Daniel Veillardfd110d22005-06-27 00:02:02 +00001836 { "Error cases regression tests",
Daniel Veillardc111c152005-06-27 08:22:10 +00001837 errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err",
1838 0 },
1839#ifdef LIBXML_READER_ENABLED
1840 { "Error cases stream regression tests",
1841 streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str",
1842 0 },
1843 { "Reader regression tests",
1844 streamParseTest, "./test/*", "result/", ".rdr", NULL,
1845 0 },
1846 { "Reader entities substitution regression tests",
1847 streamParseTest, "./test/*", "result/", ".rde", NULL,
1848 XML_PARSE_NOENT },
Daniel Veillardc950d702005-06-27 09:15:06 +00001849 { "Reader on memory regression tests",
1850 streamMemParseTest, "./test/*", "result/", ".rdr", NULL,
1851 0 },
1852 { "Walker regression tests",
1853 walkerParseTest, "./test/*", "result/", ".rdr", NULL,
1854 0 },
Daniel Veillardc111c152005-06-27 08:22:10 +00001855#endif
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001856 { "SAX1 callbacks regression tests" ,
1857 saxParseTest, "./test/*", "result/", ".sax", NULL,
1858 XML_PARSE_SAX1 },
1859 { "SAX2 callbacks regression tests" ,
1860 saxParseTest, "./test/*", "result/", ".sax2", NULL,
1861 0 },
Daniel Veillardfc319af2005-06-27 12:44:55 +00001862#ifdef LIBXML_PUSH_ENABLED
1863 { "XML push regression tests" ,
1864 pushParseTest, "./test/*", "result/", "", NULL,
1865 0 },
1866#endif
1867#ifdef LIBXML_HTML_ENABLED
1868 { "HTML regression tests" ,
1869 errParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
1870 XML_PARSE_HTML },
1871#ifdef LIBXML_PUSH_ENABLED
1872 { "Push HTML regression tests" ,
1873 pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err",
1874 XML_PARSE_HTML },
1875#endif
1876 { "HTML SAX regression tests" ,
1877 saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL,
1878 XML_PARSE_HTML },
1879#endif
Daniel Veillarddbee0f12005-06-27 13:42:57 +00001880#ifdef LIBXML_VALID_ENABLED
1881 { "Valid documents regression tests" ,
1882 errParseTest, "./test/VCM/*", NULL, NULL, NULL,
1883 XML_PARSE_DTDVALID },
1884 { "Validity checking regression tests" ,
1885 errParseTest, "./test/VC/*", "result/VC/", NULL, "",
1886 XML_PARSE_DTDVALID },
1887 { "General documents valid regression tests" ,
1888 errParseTest, "./test/valid/*", "result/valid/", "", ".err",
1889 XML_PARSE_DTDVALID },
1890#endif
Daniel Veillardc111c152005-06-27 08:22:10 +00001891 {NULL, NULL, NULL, NULL, NULL, NULL, 0}
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001892};
1893
1894/************************************************************************
1895 * *
Daniel Veillard4a5a9642005-06-27 10:40:55 +00001896 * The main code driving the tests *
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001897 * *
1898 ************************************************************************/
1899
1900static int
1901launchTests(testDescPtr tst) {
1902 int res = 0, err = 0;
1903 size_t i;
1904 char *result;
Daniel Veillardfd110d22005-06-27 00:02:02 +00001905 char *error;
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001906 int mem, leak;
1907
1908 if (tst == NULL) return(-1);
1909 if (tst->in != NULL) {
1910 glob_t globbuf;
1911
1912 globbuf.gl_offs = 0;
1913 glob(tst->in, GLOB_DOOFFS, NULL, &globbuf);
1914 for (i = 0;i < globbuf.gl_pathc;i++) {
1915 if (!checkTestFile(globbuf.gl_pathv[i]))
1916 continue;
Daniel Veillardfd110d22005-06-27 00:02:02 +00001917 if (tst->suffix != NULL) {
1918 result = resultFilename(globbuf.gl_pathv[i], tst->out,
1919 tst->suffix);
1920 if (result == NULL) {
1921 fprintf(stderr, "Out of memory !\n");
1922 fatalError();
1923 }
1924 } else {
1925 result = NULL;
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001926 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001927 if (tst->err != NULL) {
1928 error = resultFilename(globbuf.gl_pathv[i], tst->out,
1929 tst->err);
1930 if (error == NULL) {
1931 fprintf(stderr, "Out of memory !\n");
1932 fatalError();
1933 }
1934 } else {
1935 error = NULL;
1936 }
1937 if ((result) &&(!checkTestFile(result))) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001938 fprintf(stderr, "Missing result file %s\n", result);
Daniel Veillardfd110d22005-06-27 00:02:02 +00001939 } else if ((error) &&(!checkTestFile(error))) {
1940 fprintf(stderr, "Missing error file %s\n", error);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001941 } else {
1942 mem = xmlMemUsed();
1943 extraMemoryFromResolver = 0;
Daniel Veillardfd110d22005-06-27 00:02:02 +00001944 testErrorsSize = 0;
1945 testErrors[0] = 0;
Daniel Veillardc111c152005-06-27 08:22:10 +00001946 res = tst->func(globbuf.gl_pathv[i], result, error,
1947 tst->options);
Daniel Veillardfc319af2005-06-27 12:44:55 +00001948 xmlResetLastError();
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001949 if (res != 0) {
1950 fprintf(stderr, "File %s generated an error\n",
1951 globbuf.gl_pathv[i]);
1952 err++;
1953 }
1954 else if (xmlMemUsed() != mem) {
Daniel Veillardfd110d22005-06-27 00:02:02 +00001955 if ((xmlMemUsed() != mem) &&
1956 (extraMemoryFromResolver == 0)) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001957 fprintf(stderr, "File %s leaked %d bytes\n",
1958 globbuf.gl_pathv[i], xmlMemUsed() - mem);
1959 leak++;
1960 err++;
1961 }
1962 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001963 testErrorsSize = 0;
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001964 }
Daniel Veillardfd110d22005-06-27 00:02:02 +00001965 if (result)
1966 free(result);
1967 if (error)
1968 free(error);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001969 }
1970 } else {
Daniel Veillardfd110d22005-06-27 00:02:02 +00001971 testErrorsSize = 0;
1972 testErrors[0] = 0;
1973 extraMemoryFromResolver = 0;
Daniel Veillardc111c152005-06-27 08:22:10 +00001974 res = tst->func(NULL, NULL, NULL, tst->options);
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001975 if (res != 0)
1976 err++;
1977 }
1978 return(err);
1979}
1980
1981int
Daniel Veillardc111c152005-06-27 08:22:10 +00001982main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
Daniel Veillard1b75c3b2005-06-26 21:49:08 +00001983 int i = 0, res, ret = 0;
1984
1985 initializeLibxml2();
1986
1987 for (i = 0; testDescriptions[i].func != NULL; i++) {
1988 if (testDescriptions[i].desc != NULL)
1989 printf("## %s\n", testDescriptions[i].desc);
1990 res = launchTests(&testDescriptions[i]);
1991 if (res != 0)
1992 ret++;
1993 }
1994 xmlCleanupParser();
1995 xmlMemoryDump();
1996
1997 return(ret);
1998}