/* * runtest.c: C program to run libxml2 regression tests without * requiring make or Python, and reducing platform dependencies * to a strict minimum. * * To compile on Unixes: * cc -o runtest `xml2-config --cflags` runtest.c `xml2-config --libs` -lpthread * * See Copyright for the status of this software. * * daniel@veillard.com */ #include "config.h" #include #ifdef HAVE_UNISTD_H #include #elif defined (_WIN32) #include #endif #include #include #include #include #include #include #include #include #include #ifdef LIBXML_OUTPUT_ENABLED #ifdef LIBXML_READER_ENABLED #include #endif #ifdef LIBXML_XINCLUDE_ENABLED #include #endif #ifdef LIBXML_XPATH_ENABLED #include #include #ifdef LIBXML_XPTR_ENABLED #include #endif #endif #ifdef LIBXML_SCHEMAS_ENABLED #include #include #include #endif #ifdef LIBXML_PATTERN_ENABLED #include #endif #ifdef LIBXML_C14N_ENABLED #include #endif #ifdef LIBXML_HTML_ENABLED #include #include #endif #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) #include #include #include #endif /* * pseudo flag for the unification of HTML and XML tests */ #define XML_PARSE_HTML 1 << 24 /* * O_BINARY is just for Windows compatibility - if it isn't defined * on this system, avoid any compilation error */ #ifdef O_BINARY #define RD_FLAGS O_RDONLY | O_BINARY #define WR_FLAGS O_WRONLY | O_CREAT | O_TRUNC | O_BINARY #else #define RD_FLAGS O_RDONLY #define WR_FLAGS O_WRONLY | O_CREAT | O_TRUNC #endif typedef int (*functest) (const char *filename, const char *result, const char *error, int options); typedef struct testDesc testDesc; typedef testDesc *testDescPtr; struct testDesc { const char *desc; /* description of the test */ functest func; /* function implementing the test */ const char *in; /* glob to path for input files */ const char *out; /* output directory */ const char *suffix;/* suffix for output files */ const char *err; /* suffix for error output files */ int options; /* parser options for the test */ }; static int update_results = 0; static char* temp_directory = NULL; static int checkTestFile(const char *filename); #if defined(_WIN32) #include typedef struct { size_t gl_pathc; /* Count of paths matched so far */ char **gl_pathv; /* List of matched pathnames. */ size_t gl_offs; /* Slots to reserve in 'gl_pathv'. */ } glob_t; #define GLOB_DOOFFS 0 static int glob(const char *pattern, ATTRIBUTE_UNUSED int flags, ATTRIBUTE_UNUSED int errfunc(const char *epath, int eerrno), glob_t *pglob) { glob_t *ret; WIN32_FIND_DATA FindFileData; HANDLE hFind; unsigned int nb_paths = 0; char directory[500]; int len; if ((pattern == NULL) || (pglob == NULL)) return(-1); strncpy(directory, pattern, 499); for (len = strlen(directory);len >= 0;len--) { if (directory[len] == '/') { len++; directory[len] = 0; break; } } if (len <= 0) len = 0; ret = pglob; memset(ret, 0, sizeof(glob_t)); hFind = FindFirstFileA(pattern, &FindFileData); if (hFind == INVALID_HANDLE_VALUE) return(0); nb_paths = 20; ret->gl_pathv = (char **) malloc(nb_paths * sizeof(char *)); if (ret->gl_pathv == NULL) { FindClose(hFind); return(-1); } strncpy(directory + len, FindFileData.cFileName, 499 - len); ret->gl_pathv[ret->gl_pathc] = strdup(directory); if (ret->gl_pathv[ret->gl_pathc] == NULL) goto done; ret->gl_pathc++; while(FindNextFileA(hFind, &FindFileData)) { if (FindFileData.cFileName[0] == '.') continue; if (ret->gl_pathc + 2 > nb_paths) { char **tmp = realloc(ret->gl_pathv, nb_paths * 2 * sizeof(char *)); if (tmp == NULL) break; ret->gl_pathv = tmp; nb_paths *= 2; } strncpy(directory + len, FindFileData.cFileName, 499 - len); ret->gl_pathv[ret->gl_pathc] = strdup(directory); if (ret->gl_pathv[ret->gl_pathc] == NULL) break; ret->gl_pathc++; } ret->gl_pathv[ret->gl_pathc] = NULL; done: FindClose(hFind); return(0); } static void globfree(glob_t *pglob) { unsigned int i; if (pglob == NULL) return; for (i = 0;i < pglob->gl_pathc;i++) { if (pglob->gl_pathv[i] != NULL) free(pglob->gl_pathv[i]); } } #else #include #endif /************************************************************************ * * * Libxml2 specific routines * * * ************************************************************************/ static int nb_tests = 0; static int nb_errors = 0; static int nb_leaks = 0; static int extraMemoryFromResolver = 0; static int fatalError(void) { fprintf(stderr, "Exitting tests on fatal error\n"); exit(1); } /* * We need to trap calls to the resolver to not account memory for the catalog * which is shared to the current running test. We also don't want to have * network downloads modifying tests. */ static xmlParserInputPtr testExternalEntityLoader(const char *URL, const char *ID, xmlParserCtxtPtr ctxt) { xmlParserInputPtr ret; if (checkTestFile(URL)) { ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); } else { int memused = xmlMemUsed(); ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt); extraMemoryFromResolver += xmlMemUsed() - memused; } return(ret); } /* * Trapping the error messages at the generic level to grab the equivalent of * stderr messages on CLI tools. */ static char testErrors[32769]; static int testErrorsSize = 0; static void testErrorHandler(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { va_list args; int res; if (testErrorsSize >= 32768) return; va_start(args, msg); res = vsnprintf(&testErrors[testErrorsSize], 32768 - testErrorsSize, msg, args); va_end(args); if (testErrorsSize + res >= 32768) { /* buffer is full */ testErrorsSize = 32768; testErrors[testErrorsSize] = 0; } else { testErrorsSize += res; } testErrors[testErrorsSize] = 0; } static void channel(void *ctx ATTRIBUTE_UNUSED, const char *msg, ...) { va_list args; int res; if (testErrorsSize >= 32768) return; va_start(args, msg); res = vsnprintf(&testErrors[testErrorsSize], 32768 - testErrorsSize, msg, args); va_end(args); if (testErrorsSize + res >= 32768) { /* buffer is full */ testErrorsSize = 32768; testErrors[testErrorsSize] = 0; } else { testErrorsSize += res; } testErrors[testErrorsSize] = 0; } /** * xmlParserPrintFileContextInternal: * @input: an xmlParserInputPtr input * * Displays current context within the input content for error tracking */ static void xmlParserPrintFileContextInternal(xmlParserInputPtr input , xmlGenericErrorFunc chanl, void *data ) { const xmlChar *cur, *base, *start; unsigned int n, col; /* GCC warns if signed, because compared with sizeof() */ xmlChar content[81]; /* space for 80 chars + line terminator */ xmlChar *ctnt; if ((input == NULL) || (input->cur == NULL)) return; cur = input->cur; base = input->base; /* skip backwards over any end-of-lines */ while ((cur > base) && ((*(cur) == '\n') || (*(cur) == '\r'))) { cur--; } n = 0; /* search backwards for beginning-of-line (to max buff size) */ while ((n++ < (sizeof(content)-1)) && (cur > base) && (*(cur) != '\n') && (*(cur) != '\r')) cur--; if ((*(cur) == '\n') || (*(cur) == '\r')) { cur++; } else { /* skip over continuation bytes */ while ((cur < input->cur) && ((*cur & 0xC0) == 0x80)) cur++; } /* calculate the error position in terms of the current position */ col = input->cur - cur; /* search forward for end-of-line (to max buff size) */ n = 0; start = cur; /* copy selected text to our buffer */ while ((*cur != 0) && (*(cur) != '\n') && (*(cur) != '\r')) { int len = input->end - cur; int c = xmlGetUTF8Char(cur, &len); if ((c < 0) || (n + len > sizeof(content)-1)) break; cur += len; n += len; } memcpy(content, start, n); content[n] = 0; /* print out the selected text */ chanl(data ,"%s\n", content); /* create blank line with problem pointer */ n = 0; ctnt = content; /* (leave buffer space for pointer + line terminator) */ while ((nfile; line = err->line; code = err->code; domain = err->domain; level = err->level; node = err->node; if ((domain == XML_FROM_PARSER) || (domain == XML_FROM_HTML) || (domain == XML_FROM_DTD) || (domain == XML_FROM_NAMESPACE) || (domain == XML_FROM_IO) || (domain == XML_FROM_VALID)) { ctxt = err->ctxt; } str = err->message; if (code == XML_ERR_OK) return; if ((node != NULL) && (node->type == XML_ELEMENT_NODE)) name = node->name; /* * Maintain the compatibility with the legacy error handling */ if (ctxt != NULL) { input = ctxt->input; if ((input != NULL) && (input->filename == NULL) && (ctxt->inputNr > 1)) { cur = input; input = ctxt->inputTab[ctxt->inputNr - 2]; } if (input != NULL) { if (input->filename) channel(data, "%s:%d: ", input->filename, input->line); else if ((line != 0) && (domain == XML_FROM_PARSER)) channel(data, "Entity: line %d: ", input->line); } } else { if (file != NULL) channel(data, "%s:%d: ", file, line); else if ((line != 0) && (domain == XML_FROM_PARSER)) channel(data, "Entity: line %d: ", line); } /* * Skip element name when testing schemas to make memory and streaming * output match. */ if ((domain != XML_FROM_SCHEMASV) && (name != NULL)) { channel(data, "element %s: ", name); } if (code == XML_ERR_OK) return; switch (domain) { case XML_FROM_PARSER: channel(data, "parser "); break; case XML_FROM_NAMESPACE: channel(data, "namespace "); break; case XML_FROM_DTD: case XML_FROM_VALID: channel(data, "validity "); break; case XML_FROM_HTML: channel(data, "HTML parser "); break; case XML_FROM_MEMORY: channel(data, "memory "); break; case XML_FROM_OUTPUT: channel(data, "output "); break; case XML_FROM_IO: channel(data, "I/O "); break; case XML_FROM_XINCLUDE: channel(data, "XInclude "); break; case XML_FROM_XPATH: channel(data, "XPath "); break; case XML_FROM_XPOINTER: channel(data, "parser "); break; case XML_FROM_REGEXP: channel(data, "regexp "); break; case XML_FROM_MODULE: channel(data, "module "); break; case XML_FROM_SCHEMASV: channel(data, "Schemas validity "); break; case XML_FROM_SCHEMASP: channel(data, "Schemas parser "); break; case XML_FROM_RELAXNGP: channel(data, "Relax-NG parser "); break; case XML_FROM_RELAXNGV: channel(data, "Relax-NG validity "); break; case XML_FROM_CATALOG: channel(data, "Catalog "); break; case XML_FROM_C14N: channel(data, "C14N "); break; case XML_FROM_XSLT: channel(data, "XSLT "); break; default: break; } if (code == XML_ERR_OK) return; switch (level) { case XML_ERR_NONE: channel(data, ": "); break; case XML_ERR_WARNING: channel(data, "warning : "); break; case XML_ERR_ERROR: channel(data, "error : "); break; case XML_ERR_FATAL: channel(data, "error : "); break; } if (code == XML_ERR_OK) return; if (str != NULL) { int len; len = xmlStrlen((const xmlChar *)str); if ((len > 0) && (str[len - 1] != '\n')) channel(data, "%s\n", str); else channel(data, "%s", str); } else { channel(data, "%s\n", "out of memory error"); } if (code == XML_ERR_OK) return; if (ctxt != NULL) { xmlParserPrintFileContextInternal(input, channel, data); if (cur != NULL) { if (cur->filename) channel(data, "%s:%d: \n", cur->filename, cur->line); else if ((line != 0) && (domain == XML_FROM_PARSER)) channel(data, "Entity: line %d: \n", cur->line); xmlParserPrintFileContextInternal(cur, channel, data); } } if ((domain == XML_FROM_XPATH) && (err->str1 != NULL) && (err->int1 < 100) && (err->int1 < xmlStrlen((const xmlChar *)err->str1))) { xmlChar buf[150]; int i; channel(data, "%s\n", err->str1); for (i=0;i < err->int1;i++) buf[i] = ' '; buf[i++] = '^'; buf[i] = 0; channel(data, "%s\n", buf); } } static void initializeLibxml2(void) { /* * This verifies that xmlInitParser doesn't allocate memory with * xmlMalloc */ xmlFree = NULL; xmlMalloc = NULL; xmlRealloc = NULL; xmlMemStrdup = NULL; xmlInitParser(); xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup); xmlSetExternalEntityLoader(testExternalEntityLoader); xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); #ifdef LIBXML_SCHEMAS_ENABLED xmlSchemaInitTypes(); xmlRelaxNGInitTypes(); #endif } /************************************************************************ * * * File name and path utilities * * * ************************************************************************/ static const char *baseFilename(const char *filename) { const char *cur; if (filename == NULL) return(NULL); cur = &filename[strlen(filename)]; while ((cur > filename) && (*cur != '/')) cur--; if (*cur == '/') return(cur + 1); return(cur); } static char *resultFilename(const char *filename, const char *out, const char *suffix) { const char *base; char res[500]; char suffixbuff[500]; /************* if ((filename[0] == 't') && (filename[1] == 'e') && (filename[2] == 's') && (filename[3] == 't') && (filename[4] == '/')) filename = &filename[5]; *************/ base = baseFilename(filename); if (suffix == NULL) suffix = ".tmp"; if (out == NULL) out = ""; strncpy(suffixbuff,suffix,499); #ifdef VMS if(strstr(base,".") && suffixbuff[0]=='.') suffixbuff[0]='_'; #endif if (snprintf(res, 499, "%s%s%s", out, base, suffixbuff) >= 499) res[499] = 0; return(strdup(res)); } static int checkTestFile(const char *filename) { struct stat buf; if (stat(filename, &buf) == -1) return(0); #if defined(_WIN32) if (!(buf.st_mode & _S_IFREG)) return(0); #else if (!S_ISREG(buf.st_mode)) return(0); #endif return(1); } static int compareFiles(const char *r1 /* temp */, const char *r2 /* result */) { int res1, res2, total; int fd1, fd2; char bytes1[4096]; char bytes2[4096]; if (update_results) { fd1 = open(r1, RD_FLAGS); if (fd1 < 0) return(-1); fd2 = open(r2, WR_FLAGS, 0644); if (fd2 < 0) { close(fd1); return(-1); } total = 0; do { res1 = read(fd1, bytes1, 4096); if (res1 <= 0) break; total += res1; res2 = write(fd2, bytes1, res1); if (res2 <= 0 || res2 != res1) break; } while (1); close(fd2); close(fd1); if (total == 0) unlink(r2); return(res1 != 0); } fd1 = open(r1, RD_FLAGS); if (fd1 < 0) return(-1); fd2 = open(r2, RD_FLAGS); while (1) { res1 = read(fd1, bytes1, 4096); res2 = fd2 >= 0 ? read(fd2, bytes2, 4096) : 0; if ((res1 != res2) || (res1 < 0)) { close(fd1); if (fd2 >= 0) close(fd2); return(1); } if (res1 == 0) break; if (memcmp(bytes1, bytes2, res1) != 0) { close(fd1); if (fd2 >= 0) close(fd2); return(1); } } close(fd1); if (fd2 >= 0) close(fd2); return(0); } static int compareFileMem(const char *filename, const char *mem, int size) { int res; int fd; char bytes[4096]; int idx = 0; struct stat info; if (update_results) { if (size == 0) { unlink(filename); return(0); } fd = open(filename, WR_FLAGS, 0644); if (fd < 0) { fprintf(stderr, "failed to open %s for writing", filename); return(-1); } res = write(fd, mem, size); close(fd); return(res != size); } if (stat(filename, &info) < 0) { if (size == 0) return(0); fprintf(stderr, "failed to stat %s\n", filename); return(-1); } if (info.st_size != size) { fprintf(stderr, "file %s is %ld bytes, result is %d bytes\n", filename, (long) info.st_size, size); return(-1); } fd = open(filename, RD_FLAGS); if (fd < 0) { fprintf(stderr, "failed to open %s for reading", filename); return(-1); } while (idx < size) { res = read(fd, bytes, 4096); if (res <= 0) break; if (res + idx > size) break; if (memcmp(bytes, &mem[idx], res) != 0) { int ix; for (ix=0; ix 0) { siz += res; } close(fd); #if !defined(_WIN32) if (siz != info.st_size) { free(base); return(-1); } #endif base[siz] = 0; *mem = base; *size = siz; return(0); } static int unloadMem(const char *mem) { free((char *)mem); return(0); } /************************************************************************ * * * Tests implementations * * * ************************************************************************/ /************************************************************************ * * * Parse to SAX based tests * * * ************************************************************************/ static FILE *SAXdebug = NULL; /* * empty SAX block */ static xmlSAXHandler emptySAXHandlerStruct = { NULL, /* internalSubset */ NULL, /* isStandalone */ NULL, /* hasInternalSubset */ NULL, /* hasExternalSubset */ NULL, /* resolveEntity */ NULL, /* getEntity */ NULL, /* entityDecl */ NULL, /* notationDecl */ NULL, /* attributeDecl */ NULL, /* elementDecl */ NULL, /* unparsedEntityDecl */ NULL, /* setDocumentLocator */ NULL, /* startDocument */ NULL, /* endDocument */ NULL, /* startElement */ NULL, /* endElement */ NULL, /* reference */ NULL, /* characters */ NULL, /* ignorableWhitespace */ NULL, /* processingInstruction */ NULL, /* comment */ NULL, /* xmlParserWarning */ NULL, /* xmlParserError */ NULL, /* xmlParserError */ NULL, /* getParameterEntity */ NULL, /* cdataBlock; */ NULL, /* externalSubset; */ 1, NULL, NULL, /* startElementNs */ NULL, /* endElementNs */ NULL /* xmlStructuredErrorFunc */ }; static xmlSAXHandlerPtr emptySAXHandler = &emptySAXHandlerStruct; static int callbacks = 0; static int quiet = 0; /** * isStandaloneDebug: * @ctxt: An XML parser context * * Is this document tagged standalone ? * * Returns 1 if true */ static int isStandaloneDebug(void *ctx ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return(0); fprintf(SAXdebug, "SAX.isStandalone()\n"); return(0); } /** * hasInternalSubsetDebug: * @ctxt: An XML parser context * * Does this document has an internal subset * * Returns 1 if true */ static int hasInternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return(0); fprintf(SAXdebug, "SAX.hasInternalSubset()\n"); return(0); } /** * hasExternalSubsetDebug: * @ctxt: An XML parser context * * Does this document has an external subset * * Returns 1 if true */ static int hasExternalSubsetDebug(void *ctx ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return(0); fprintf(SAXdebug, "SAX.hasExternalSubset()\n"); return(0); } /** * internalSubsetDebug: * @ctxt: An XML parser context * * Does this document has an internal subset */ static void internalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) { callbacks++; if (quiet) return; if (name == NULL) name = BAD_CAST "(null)"; fprintf(SAXdebug, "SAX.internalSubset(%s,", name); if (ExternalID == NULL) fprintf(SAXdebug, " ,"); else fprintf(SAXdebug, " %s,", ExternalID); if (SystemID == NULL) fprintf(SAXdebug, " )\n"); else fprintf(SAXdebug, " %s)\n", SystemID); } /** * externalSubsetDebug: * @ctxt: An XML parser context * * Does this document has an external subset */ static void externalSubsetDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar *ExternalID, const xmlChar *SystemID) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.externalSubset(%s,", name); if (ExternalID == NULL) fprintf(SAXdebug, " ,"); else fprintf(SAXdebug, " %s,", ExternalID); if (SystemID == NULL) fprintf(SAXdebug, " )\n"); else fprintf(SAXdebug, " %s)\n", SystemID); } /** * resolveEntityDebug: * @ctxt: An XML parser context * @publicId: The public ID of the entity * @systemId: The system ID of the entity * * Special entity resolver, better left to the parser, it has * more context than the application layer. * The default behaviour is to NOT resolve the entities, in that case * the ENTITY_REF nodes are built in the structure (and the parameter * values). * * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. */ static xmlParserInputPtr resolveEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *publicId, const xmlChar *systemId) { callbacks++; if (quiet) return(NULL); /* xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) ctx; */ fprintf(SAXdebug, "SAX.resolveEntity("); if (publicId != NULL) fprintf(SAXdebug, "%s", (char *)publicId); else fprintf(SAXdebug, " "); if (systemId != NULL) fprintf(SAXdebug, ", %s)\n", (char *)systemId); else fprintf(SAXdebug, ", )\n"); /********* if (systemId != NULL) { return(xmlNewInputFromFile(ctxt, (char *) systemId)); } *********/ return(NULL); } /** * getEntityDebug: * @ctxt: An XML parser context * @name: The entity name * * Get an entity by name * * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour. */ static xmlEntityPtr getEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) { callbacks++; if (quiet) return(NULL); fprintf(SAXdebug, "SAX.getEntity(%s)\n", name); return(NULL); } /** * getParameterEntityDebug: * @ctxt: An XML parser context * @name: The entity name * * Get a parameter entity by name * * Returns the xmlParserInputPtr */ static xmlEntityPtr getParameterEntityDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) { callbacks++; if (quiet) return(NULL); fprintf(SAXdebug, "SAX.getParameterEntity(%s)\n", name); return(NULL); } /** * entityDeclDebug: * @ctxt: An XML parser context * @name: the entity name * @type: the entity type * @publicId: The public ID of the entity * @systemId: The system ID of the entity * @content: the entity value (without processing). * * An entity definition has been parsed */ static void entityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, const xmlChar *publicId, const xmlChar *systemId, xmlChar *content) { const xmlChar *nullstr = BAD_CAST "(null)"; /* not all libraries handle printing null pointers nicely */ if (publicId == NULL) publicId = nullstr; if (systemId == NULL) systemId = nullstr; if (content == NULL) content = (xmlChar *)nullstr; callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.entityDecl(%s, %d, %s, %s, %s)\n", name, type, publicId, systemId, content); } /** * attributeDeclDebug: * @ctxt: An XML parser context * @name: the attribute name * @type: the attribute type * * An attribute definition has been parsed */ static void attributeDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar * elem, const xmlChar * name, int type, int def, const xmlChar * defaultValue, xmlEnumerationPtr tree) { callbacks++; if (quiet) return; if (defaultValue == NULL) fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, NULL, ...)\n", elem, name, type, def); else fprintf(SAXdebug, "SAX.attributeDecl(%s, %s, %d, %d, %s, ...)\n", elem, name, type, def, defaultValue); xmlFreeEnumeration(tree); } /** * elementDeclDebug: * @ctxt: An XML parser context * @name: the element name * @type: the element type * @content: the element value (without processing). * * An element definition has been parsed */ static void elementDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, int type, xmlElementContentPtr content ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.elementDecl(%s, %d, ...)\n", name, type); } /** * notationDeclDebug: * @ctxt: An XML parser context * @name: The name of the notation * @publicId: The public ID of the entity * @systemId: The system ID of the entity * * What to do when a notation declaration has been parsed. */ static void notationDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.notationDecl(%s, %s, %s)\n", (char *) name, (char *) publicId, (char *) systemId); } /** * unparsedEntityDeclDebug: * @ctxt: An XML parser context * @name: The name of the entity * @publicId: The public ID of the entity * @systemId: The system ID of the entity * @notationName: the name of the notation * * What to do when an unparsed entity declaration is parsed */ static void unparsedEntityDeclDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar *publicId, const xmlChar *systemId, const xmlChar *notationName) { const xmlChar *nullstr = BAD_CAST "(null)"; if (publicId == NULL) publicId = nullstr; if (systemId == NULL) systemId = nullstr; if (notationName == NULL) notationName = nullstr; callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.unparsedEntityDecl(%s, %s, %s, %s)\n", (char *) name, (char *) publicId, (char *) systemId, (char *) notationName); } /** * setDocumentLocatorDebug: * @ctxt: An XML parser context * @loc: A SAX Locator * * Receive the document locator at startup, actually xmlDefaultSAXLocator * Everything is available on the context, so this is useless in our case. */ static void setDocumentLocatorDebug(void *ctx ATTRIBUTE_UNUSED, xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.setDocumentLocator()\n"); } /** * startDocumentDebug: * @ctxt: An XML parser context * * called when the document start being processed. */ static void startDocumentDebug(void *ctx ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.startDocument()\n"); } /** * endDocumentDebug: * @ctxt: An XML parser context * * called when the document end has been detected. */ static void endDocumentDebug(void *ctx ATTRIBUTE_UNUSED) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.endDocument()\n"); } /** * startElementDebug: * @ctxt: An XML parser context * @name: The element name * * called when an opening tag has been processed. */ static void startElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name, const xmlChar **atts) { int i; callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.startElement(%s", (char *) name); if (atts != NULL) { for (i = 0;(atts[i] != NULL);i++) { fprintf(SAXdebug, ", %s='", atts[i++]); if (atts[i] != NULL) fprintf(SAXdebug, "%s'", atts[i]); } } fprintf(SAXdebug, ")\n"); } /** * endElementDebug: * @ctxt: An XML parser context * @name: The element name * * called when the end of an element has been detected. */ static void endElementDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *name) { callbacks++; if (quiet) return; fprintf(SAXdebug, "SAX.endElement(%s)\n", (char *) name); } /** * charactersDebug: * @ctxt: An XML parser context * @ch: a xmlChar string * @len: the number of xmlChar * * receiving some chars from the parser. * Question: how much at a time ??? */ static void charactersDebug(void *ctx ATTRIBUTE_UNUSED, const xmlChar *ch, int len) { char output[40]; int i; callbacks++; if (quiet) return; for (i = 0;(isax, emptySAXHandler, sizeof(xmlSAXHandler)); xmlCtxtUseOptions(ctxt, options); xmlParseDocument(ctxt); ret = ctxt->wellFormed ? 0 : ctxt->errNo; xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); } if (ret == XML_WAR_UNDECLARED_ENTITY) { fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret); ret = 0; } if (ret != 0) { fprintf(stderr, "Failed to parse %s\n", filename); ret = 1; goto done; } #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { htmlParserCtxtPtr ctxt; ctxt = htmlNewSAXParserCtxt(debugHTMLSAXHandler, NULL); htmlCtxtReadFile(ctxt, filename, NULL, options); htmlFreeParserCtxt(ctxt); ret = 0; } else #endif { xmlParserCtxtPtr ctxt = xmlCreateFileParserCtxt(filename); if (options & XML_PARSE_SAX1) { memcpy(ctxt->sax, debugSAXHandler, sizeof(xmlSAXHandler)); options -= XML_PARSE_SAX1; } else { memcpy(ctxt->sax, debugSAX2Handler, sizeof(xmlSAXHandler)); } xmlCtxtUseOptions(ctxt, options); xmlParseDocument(ctxt); ret = ctxt->wellFormed ? 0 : ctxt->errNo; xmlFreeDoc(ctxt->myDoc); xmlFreeParserCtxt(ctxt); } if (ret == XML_WAR_UNDECLARED_ENTITY) { fprintf(SAXdebug, "xmlSAXUserParseFile returned error %d\n", ret); ret = 0; } fclose(SAXdebug); if (compareFiles(temp, result)) { fprintf(stderr, "Got a difference for %s\n", filename); ret = 1; } done: if (temp != NULL) { unlink(temp); free(temp); } /* switch back to structured error handling */ xmlSetGenericErrorFunc(NULL, NULL); xmlSetStructuredErrorFunc(NULL, testStructuredErrorHandler); return(ret); } /************************************************************************ * * * Parse to tree based tests * * * ************************************************************************/ /** * oldParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages: unused * * Parse a file using the old xmlParseFile API, then serialize back * reparse the result and serialize again, then check for deviation * in serialization. * * Returns 0 in case of success, an error code otherwise */ static int oldParseTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { xmlDocPtr doc; char *temp; int res = 0; nb_tests++; /* * base of the test, parse with the old API */ #ifdef LIBXML_SAX1_ENABLED doc = xmlParseFile(filename); #else doc = xmlReadFile(filename, NULL, 0); #endif if (doc == NULL) return(1); temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "out of memory\n"); fatalError(); } xmlSaveFile(temp, doc); if (compareFiles(temp, result)) { res = 1; } xmlFreeDoc(doc); /* * Parse the saved result to make sure the round trip is okay */ #ifdef LIBXML_SAX1_ENABLED doc = xmlParseFile(temp); #else doc = xmlReadFile(temp, NULL, 0); #endif if (doc == NULL) return(1); xmlSaveFile(temp, doc); if (compareFiles(temp, result)) { res = 1; } xmlFreeDoc(doc); if (temp != NULL) { unlink(temp); free(temp); } return(res); } #ifdef LIBXML_PUSH_ENABLED /** * pushParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages: unused * * Parse a file using the Push API, then serialize back * to check for content. * * Returns 0 in case of success, an error code otherwise */ static int pushParseTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options) { xmlParserCtxtPtr ctxt; xmlDocPtr doc; const char *base; int size, res; int cur = 0; int chunkSize = 4; nb_tests++; /* * load the document in memory and work from there. */ if (loadMem(filename, &base, &size) != 0) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } if (chunkSize > size) chunkSize = size; #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) ctxt = htmlCreatePushParserCtxt(NULL, NULL, base + cur, chunkSize, filename, XML_CHAR_ENCODING_NONE); else #endif ctxt = xmlCreatePushParserCtxt(NULL, NULL, base + cur, chunkSize, filename); xmlCtxtUseOptions(ctxt, options); cur += chunkSize; chunkSize = 1024; do { if (cur + chunkSize >= size) { #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) htmlParseChunk(ctxt, base + cur, size - cur, 1); else #endif xmlParseChunk(ctxt, base + cur, size - cur, 1); break; } else { #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) htmlParseChunk(ctxt, base + cur, chunkSize, 0); else #endif xmlParseChunk(ctxt, base + cur, chunkSize, 0); cur += chunkSize; } } while (cur < size); doc = ctxt->myDoc; #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) res = 1; else #endif res = ctxt->wellFormed; xmlFreeParserCtxt(ctxt); free((char *)base); if (!res) { xmlFreeDoc(doc); fprintf(stderr, "Failed to parse %s\n", filename); return(-1); } #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) htmlDocDumpMemory(doc, (xmlChar **) &base, &size); else #endif xmlDocDumpMemory(doc, (xmlChar **) &base, &size); xmlFreeDoc(doc); res = compareFileMem(result, base, size); if ((base == NULL) || (res != 0)) { if (base != NULL) xmlFree((char *)base); fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } xmlFree((char *)base); if (err != NULL) { res = compareFileMem(err, testErrors, testErrorsSize); if (res != 0) { fprintf(stderr, "Error for %s failed\n", filename); return(-1); } } return(0); } static int pushBoundaryCount; static int pushBoundaryRefCount; static int pushBoundaryCharsCount; static int pushBoundaryCDataCount; static void internalSubsetBnd(void *ctx, const xmlChar *name, const xmlChar *externalID, const xmlChar *systemID) { pushBoundaryCount++; xmlSAX2InternalSubset(ctx, name, externalID, systemID); } static void referenceBnd(void *ctx, const xmlChar *name) { pushBoundaryRefCount++; xmlSAX2Reference(ctx, name); } static void charactersBnd(void *ctx, const xmlChar *ch, int len) { pushBoundaryCount++; pushBoundaryCharsCount++; xmlSAX2Characters(ctx, ch, len); } static void cdataBlockBnd(void *ctx, const xmlChar *ch, int len) { pushBoundaryCount++; pushBoundaryCDataCount++; xmlSAX2CDataBlock(ctx, ch, len); } static void processingInstructionBnd(void *ctx, const xmlChar *target, const xmlChar *data) { pushBoundaryCount++; xmlSAX2ProcessingInstruction(ctx, target, data); } static void commentBnd(void *ctx, const xmlChar *value) { xmlParserCtxtPtr ctxt = ctx; if (ctxt->inSubset == 0) pushBoundaryCount++; xmlSAX2Comment(ctx, value); } static void startElementBnd(void *ctx, const xmlChar *xname, const xmlChar **atts) { const char *name = (const char *)xname; /* Some elements might be created automatically. */ if ((strcmp(name, "html") != 0) && (strcmp(name, "body") != 0) && (strcmp(name, "head") != 0) && (strcmp(name, "p") != 0)) { pushBoundaryCount++; } xmlSAX2StartElement(ctx, xname, atts); } static void endElementBnd(void *ctx, const xmlChar *name) { /*pushBoundaryCount++;*/ xmlSAX2EndElement(ctx, name); } static void startElementNsBnd(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI, int nb_namespaces, const xmlChar **namespaces, int nb_attributes, int nb_defaulted, const xmlChar **attributes) { pushBoundaryCount++; xmlSAX2StartElementNs(ctx, localname, prefix, URI, nb_namespaces, namespaces, nb_attributes, nb_defaulted, attributes); } static void endElementNsBnd(void *ctx, const xmlChar *localname, const xmlChar *prefix, const xmlChar *URI) { /*pushBoundaryCount++;*/ xmlSAX2EndElementNs(ctx, localname, prefix, URI); } /** * pushBoundaryTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages: unused * * Test whether the push parser detects boundaries between syntactical * elements correctly. * * Returns 0 in case of success, an error code otherwise */ static int pushBoundaryTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options) { xmlParserCtxtPtr ctxt; xmlDocPtr doc; xmlSAXHandler bndSAX; const char *base; int size, res, numCallbacks; int cur = 0; unsigned long avail, oldConsumed, consumed; /* * If the parser made progress, check that exactly one construct was * processed and that the input buffer is (almost) empty. * Since we use a chunk size of 1, this tests whether content is * processed as early as possible. */ nb_tests++; memset(&bndSAX, 0, sizeof(bndSAX)); #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { xmlSAX2InitHtmlDefaultSAXHandler(&bndSAX); bndSAX.startElement = startElementBnd; bndSAX.endElement = endElementBnd; } else #endif { xmlSAXVersion(&bndSAX, 2); bndSAX.startElementNs = startElementNsBnd; bndSAX.endElementNs = endElementNsBnd; } bndSAX.internalSubset = internalSubsetBnd; bndSAX.reference = referenceBnd; bndSAX.characters = charactersBnd; bndSAX.cdataBlock = cdataBlockBnd; bndSAX.processingInstruction = processingInstructionBnd; bndSAX.comment = commentBnd; /* * load the document in memory and work from there. */ if (loadMem(filename, &base, &size) != 0) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) ctxt = htmlCreatePushParserCtxt(&bndSAX, NULL, base, 1, filename, XML_CHAR_ENCODING_NONE); else #endif ctxt = xmlCreatePushParserCtxt(&bndSAX, NULL, base, 1, filename); xmlCtxtUseOptions(ctxt, options); cur = 1; consumed = 0; numCallbacks = 0; avail = 0; while ((cur < size) && (numCallbacks <= 1) && (avail <= 0)) { int terminate = (cur + 1 >= size); int isText = 0; if (ctxt->instate == XML_PARSER_CONTENT) { int firstChar = (ctxt->input->end > ctxt->input->cur) ? *ctxt->input->cur : base[cur]; if ((firstChar != '<') && ((options & XML_PARSE_HTML) || (firstChar != '&'))) isText = 1; } oldConsumed = ctxt->input->consumed + (unsigned long) (ctxt->input->cur - ctxt->input->base); pushBoundaryCount = 0; pushBoundaryRefCount = 0; pushBoundaryCharsCount = 0; pushBoundaryCDataCount = 0; #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) htmlParseChunk(ctxt, base + cur, 1, terminate); else #endif xmlParseChunk(ctxt, base + cur, 1, terminate); cur += 1; /* * Callback check: Check that only a single construct was parsed. */ if (pushBoundaryRefCount > 0) { numCallbacks = 1; } else { numCallbacks = pushBoundaryCount; if (pushBoundaryCharsCount > 1) { if (options & XML_PARSE_HTML) { /* * The HTML parser can generate a mix of chars and * references. */ numCallbacks -= pushBoundaryCharsCount - 1; } else { /* * Allow two chars callbacks. This can happen when * multi-byte chars are split across buffer boundaries. */ numCallbacks -= 1; } } if (options & XML_PARSE_HTML) { /* * Allow multiple cdata callbacks in HTML mode. */ if (pushBoundaryCDataCount > 1) numCallbacks -= pushBoundaryCDataCount - 1; } } /* * Buffer check: If input was consumed, check that the input * buffer is (almost) empty. */ consumed = ctxt->input->consumed + (unsigned long) (ctxt->input->cur - ctxt->input->base); if ((ctxt->instate != XML_PARSER_DTD) && (consumed >= 4) && (consumed != oldConsumed)) { size_t max = 0; avail = ctxt->input->end - ctxt->input->cur; if ((options & XML_PARSE_HTML) && (ctxt->instate == XML_PARSER_END_TAG)) { /* Something related to script parsing. */ max = 3; } else if (isText) { int c = *ctxt->input->cur; /* 3 bytes for partial UTF-8 */ max = ((c == '<') || (c == '&')) ? 1 : 3; } else if (ctxt->instate == XML_PARSER_CDATA_SECTION) { /* 2 bytes for terminator, 3 bytes for UTF-8 */ max = 5; } if (avail <= max) avail = 0; } } doc = ctxt->myDoc; #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) res = 1; else #endif res = ctxt->wellFormed; xmlFreeParserCtxt(ctxt); free((char *)base); if (numCallbacks > 1) { xmlFreeDoc(doc); fprintf(stderr, "Failed push boundary callback test (%d@%lu-%lu): %s\n", numCallbacks, oldConsumed, consumed, filename); return(-1); } if (avail > 0) { xmlFreeDoc(doc); fprintf(stderr, "Failed push boundary buffer test (%lu@%lu): %s\n", avail, consumed, filename); return(-1); } if (!res) { xmlFreeDoc(doc); fprintf(stderr, "Failed to parse %s\n", filename); return(-1); } #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) htmlDocDumpMemory(doc, (xmlChar **) &base, &size); else #endif xmlDocDumpMemory(doc, (xmlChar **) &base, &size); xmlFreeDoc(doc); res = compareFileMem(result, base, size); if ((base == NULL) || (res != 0)) { if (base != NULL) xmlFree((char *)base); fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } xmlFree((char *)base); if (err != NULL) { res = compareFileMem(err, testErrors, testErrorsSize); if (res != 0) { fprintf(stderr, "Error for %s failed\n", filename); return(-1); } } return(0); } #endif /** * memParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages: unused * * Parse a file using the old xmlReadMemory API, then serialize back * reparse the result and serialize again, then check for deviation * in serialization. * * Returns 0 in case of success, an error code otherwise */ static int memParseTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { xmlDocPtr doc; const char *base; int size, res; nb_tests++; /* * load and parse the memory */ if (loadMem(filename, &base, &size) != 0) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } doc = xmlReadMemory(base, size, filename, NULL, 0); unloadMem(base); if (doc == NULL) { return(1); } xmlDocDumpMemory(doc, (xmlChar **) &base, &size); xmlFreeDoc(doc); res = compareFileMem(result, base, size); if ((base == NULL) || (res != 0)) { if (base != NULL) xmlFree((char *)base); fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } xmlFree((char *)base); return(0); } /** * noentParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages: unused * * Parse a file with entity resolution, then serialize back * reparse the result and serialize again, then check for deviation * in serialization. * * Returns 0 in case of success, an error code otherwise */ static int noentParseTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options) { xmlDocPtr doc; char *temp; int res = 0; nb_tests++; /* * base of the test, parse with the old API */ doc = xmlReadFile(filename, NULL, options); if (doc == NULL) return(1); temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } xmlSaveFile(temp, doc); if (compareFiles(temp, result)) { res = 1; } xmlFreeDoc(doc); /* * Parse the saved result to make sure the round trip is okay */ doc = xmlReadFile(filename, NULL, options); if (doc == NULL) return(1); xmlSaveFile(temp, doc); if (compareFiles(temp, result)) { res = 1; } xmlFreeDoc(doc); if (temp != NULL) { unlink(temp); free(temp); } return(res); } /** * errParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file using the xmlReadFile API and check for errors. * * Returns 0 in case of success, an error code otherwise */ static int errParseTest(const char *filename, const char *result, const char *err, int options) { xmlDocPtr doc; const char *base = NULL; int size, res = 0; nb_tests++; #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { doc = htmlReadFile(filename, NULL, options); } else #endif #ifdef LIBXML_XINCLUDE_ENABLED if (options & XML_PARSE_XINCLUDE) { doc = xmlReadFile(filename, NULL, options); if (xmlXIncludeProcessFlags(doc, options) < 0) { xmlFreeDoc(doc); doc = NULL; } } else #endif { doc = xmlReadFile(filename, NULL, options); } if (result) { if (doc == NULL) { base = ""; size = 0; } else { #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { htmlDocDumpMemory(doc, (xmlChar **) &base, &size); } else #endif xmlDocDumpMemory(doc, (xmlChar **) &base, &size); } res = compareFileMem(result, base, size); } if (doc != NULL) { if (base != NULL) xmlFree((char *)base); xmlFreeDoc(doc); } if (res != 0) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } if (err != NULL) { res = compareFileMem(err, testErrors, testErrorsSize); if (res != 0) { fprintf(stderr, "Error for %s failed\n", filename); return(-1); } } else if (options & XML_PARSE_DTDVALID) { if (testErrorsSize != 0) fprintf(stderr, "Validation for %s failed\n", filename); } return(0); } #if defined(LIBXML_VALID_ENABLED) || defined(LIBXML_HTML_ENABLED) /** * fdParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file using the xmlReadFd API and check for errors. * * Returns 0 in case of success, an error code otherwise */ static int fdParseTest(const char *filename, const char *result, const char *err, int options) { xmlDocPtr doc; const char *base = NULL; int size, res = 0, fd; nb_tests++; fd = open(filename, RD_FLAGS); #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { doc = htmlReadFd(fd, filename, NULL, options); } else #endif { doc = xmlReadFd(fd, filename, NULL, options); } close(fd); if (result) { if (doc == NULL) { base = ""; size = 0; } else { #ifdef LIBXML_HTML_ENABLED if (options & XML_PARSE_HTML) { htmlDocDumpMemory(doc, (xmlChar **) &base, &size); } else #endif xmlDocDumpMemory(doc, (xmlChar **) &base, &size); } res = compareFileMem(result, base, size); } if (doc != NULL) { if (base != NULL) xmlFree((char *)base); xmlFreeDoc(doc); } if (res != 0) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } if (err != NULL) { res = compareFileMem(err, testErrors, testErrorsSize); if (res != 0) { fprintf(stderr, "Error for %s failed\n", filename); return(-1); } } else if (options & XML_PARSE_DTDVALID) { if (testErrorsSize != 0) fprintf(stderr, "Validation for %s failed\n", filename); } return(0); } #endif #ifdef LIBXML_READER_ENABLED /************************************************************************ * * * Reader based tests * * * ************************************************************************/ static void processNode(FILE *out, xmlTextReaderPtr reader) { const xmlChar *name, *value; int type, empty; type = xmlTextReaderNodeType(reader); empty = xmlTextReaderIsEmptyElement(reader); name = xmlTextReaderConstName(reader); if (name == NULL) name = BAD_CAST "--"; value = xmlTextReaderConstValue(reader); fprintf(out, "%d %d %s %d %d", xmlTextReaderDepth(reader), type, name, empty, xmlTextReaderHasValue(reader)); if (value == NULL) fprintf(out, "\n"); else { fprintf(out, " %s\n", value); } } static int streamProcessTest(const char *filename, const char *result, const char *err, xmlTextReaderPtr reader, const char *rng, int options ATTRIBUTE_UNUSED) { int ret; char *temp = NULL; FILE *t = NULL; if (reader == NULL) return(-1); nb_tests++; if (result != NULL) { temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } t = fopen(temp, "wb"); if (t == NULL) { fprintf(stderr, "Can't open temp file %s\n", temp); free(temp); return(-1); } } #ifdef LIBXML_SCHEMAS_ENABLED if (rng != NULL) { ret = xmlTextReaderRelaxNGValidate(reader, rng); if (ret < 0) { testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", rng); fclose(t); if (temp != NULL) { unlink(temp); free(temp); } return(0); } } #endif ret = xmlTextReaderRead(reader); while (ret == 1) { if ((t != NULL) && (rng == NULL)) processNode(t, reader); ret = xmlTextReaderRead(reader); } if (ret != 0) { testErrorHandler(NULL, "%s : failed to parse\n", filename); } if (rng != NULL) { if (xmlTextReaderIsValid(reader) != 1) { testErrorHandler(NULL, "%s fails to validate\n", filename); } else { testErrorHandler(NULL, "%s validates\n", filename); } } if (t != NULL) { fclose(t); ret = compareFiles(temp, result); if (temp != NULL) { unlink(temp); free(temp); } if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); return(-1); } } if (err != NULL) { ret = compareFileMem(err, testErrors, testErrorsSize); if (ret != 0) { fprintf(stderr, "Error for %s failed\n", filename); printf("%s", testErrors); return(-1); } } return(0); } /** * streamParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file using the reader API and check for errors. * * Returns 0 in case of success, an error code otherwise */ static int streamParseTest(const char *filename, const char *result, const char *err, int options) { xmlTextReaderPtr reader; int ret; reader = xmlReaderForFile(filename, NULL, options); ret = streamProcessTest(filename, result, err, reader, NULL, options); xmlFreeTextReader(reader); return(ret); } /** * walkerParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file using the walker, i.e. a reader built from a atree. * * Returns 0 in case of success, an error code otherwise */ static int walkerParseTest(const char *filename, const char *result, const char *err, int options) { xmlDocPtr doc; xmlTextReaderPtr reader; int ret; doc = xmlReadFile(filename, NULL, options); if (doc == NULL) { fprintf(stderr, "Failed to parse %s\n", filename); return(-1); } reader = xmlReaderWalker(doc); ret = streamProcessTest(filename, result, err, reader, NULL, options); xmlFreeTextReader(reader); xmlFreeDoc(doc); return(ret); } /** * streamMemParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file using the reader API from memory and check for errors. * * Returns 0 in case of success, an error code otherwise */ static int streamMemParseTest(const char *filename, const char *result, const char *err, int options) { xmlTextReaderPtr reader; int ret; const char *base; int size; /* * load and parse the memory */ if (loadMem(filename, &base, &size) != 0) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } reader = xmlReaderForMemory(base, size, filename, NULL, options); ret = streamProcessTest(filename, result, err, reader, NULL, options); free((char *)base); xmlFreeTextReader(reader); return(ret); } #endif #ifdef LIBXML_XPATH_ENABLED #ifdef LIBXML_DEBUG_ENABLED /************************************************************************ * * * XPath and XPointer based tests * * * ************************************************************************/ static FILE *xpathOutput; static xmlDocPtr xpathDocument; static void ignoreGenericError(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED, ...) { } static void testXPath(const char *str, int xptr, int expr) { xmlXPathObjectPtr res; xmlXPathContextPtr ctxt; /* Don't print generic errors to stderr. */ xmlSetGenericErrorFunc(NULL, ignoreGenericError); nb_tests++; #if defined(LIBXML_XPTR_ENABLED) if (xptr) { ctxt = xmlXPtrNewContext(xpathDocument, NULL, NULL); res = xmlXPtrEval(BAD_CAST str, ctxt); } else { #endif ctxt = xmlXPathNewContext(xpathDocument); ctxt->node = xmlDocGetRootElement(xpathDocument); if (expr) res = xmlXPathEvalExpression(BAD_CAST str, ctxt); else { /* res = xmlXPathEval(BAD_CAST str, ctxt); */ xmlXPathCompExprPtr comp; comp = xmlXPathCompile(BAD_CAST str); if (comp != NULL) { res = xmlXPathCompiledEval(comp, ctxt); xmlXPathFreeCompExpr(comp); } else res = NULL; } #if defined(LIBXML_XPTR_ENABLED) } #endif xmlXPathDebugDumpObject(xpathOutput, res, 0); xmlXPathFreeObject(res); xmlXPathFreeContext(ctxt); /* Reset generic error handler. */ xmlSetGenericErrorFunc(NULL, NULL); } /** * xpathExprTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing XPath standalone expressions and evaluate them * * Returns 0 in case of success, an error code otherwise */ static int xpathCommonTest(const char *filename, const char *result, int xptr, int expr) { FILE *input; char expression[5000]; int len, ret = 0; char *temp; temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } xpathOutput = fopen(temp, "wb"); if (xpathOutput == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); free(temp); return(-1); } input = fopen(filename, "rb"); if (input == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot open %s for reading\n", filename); free(temp); return(-1); } while (fgets(expression, 4500, input) != NULL) { len = strlen(expression); len--; while ((len >= 0) && ((expression[len] == '\n') || (expression[len] == '\t') || (expression[len] == '\r') || (expression[len] == ' '))) len--; expression[len + 1] = 0; if (len >= 0) { fprintf(xpathOutput, "\n========================\nExpression: %s\n", expression) ; testXPath(expression, xptr, expr); } } fclose(input); fclose(xpathOutput); if (result != NULL) { ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); } } if (temp != NULL) { unlink(temp); free(temp); } return(ret); } /** * xpathExprTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing XPath standalone expressions and evaluate them * * Returns 0 in case of success, an error code otherwise */ static int xpathExprTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(xpathCommonTest(filename, result, 0, 1)); } /** * xpathDocTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing XPath expressions and evaluate them against * a set of corresponding documents. * * Returns 0 in case of success, an error code otherwise */ static int xpathDocTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options) { char pattern[500]; char result[500]; glob_t globbuf; size_t i; int ret = 0, res; xpathDocument = xmlReadFile(filename, NULL, options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); if (xpathDocument == NULL) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } res = snprintf(pattern, 499, "./test/XPath/tests/%s*", baseFilename(filename)); if (res >= 499) pattern[499] = 0; globbuf.gl_offs = 0; glob(pattern, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { res = snprintf(result, 499, "result/XPath/tests/%s", baseFilename(globbuf.gl_pathv[i])); if (res >= 499) result[499] = 0; res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 0, 0); if (res != 0) ret = res; } globfree(&globbuf); xmlFreeDoc(xpathDocument); return(ret); } #ifdef LIBXML_XPTR_ENABLED /** * xptrDocTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing XPath expressions and evaluate them against * a set of corresponding documents. * * Returns 0 in case of success, an error code otherwise */ static int xptrDocTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options) { char pattern[500]; char result[500]; glob_t globbuf; size_t i; int ret = 0, res; const char *subdir = options == -1 ? "xptr-xp1" : "xptr"; xpathDocument = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT); if (xpathDocument == NULL) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } res = snprintf(pattern, 499, "./test/XPath/%s/%s*", subdir, baseFilename(filename)); if (res >= 499) pattern[499] = 0; globbuf.gl_offs = 0; glob(pattern, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { res = snprintf(result, 499, "result/XPath/%s/%s", subdir, baseFilename(globbuf.gl_pathv[i])); if (res >= 499) result[499] = 0; res = xpathCommonTest(globbuf.gl_pathv[i], &result[0], 1, 0); if (res != 0) ret = res; } globfree(&globbuf); xmlFreeDoc(xpathDocument); return(ret); } #endif /* LIBXML_XPTR_ENABLED */ #ifdef LIBXML_VALID_ENABLED /** * xmlidDocTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing xml:id and check for errors and verify * that XPath queries will work on them as expected. * * Returns 0 in case of success, an error code otherwise */ static int xmlidDocTest(const char *filename, const char *result, const char *err, int options) { int res = 0; int ret = 0; char *temp; xpathDocument = xmlReadFile(filename, NULL, options | XML_PARSE_DTDATTR | XML_PARSE_NOENT); if (xpathDocument == NULL) { fprintf(stderr, "Failed to load %s\n", filename); return(-1); } temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } xpathOutput = fopen(temp, "wb"); if (xpathOutput == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); xmlFreeDoc(xpathDocument); free(temp); return(-1); } testXPath("id('bar')", 0, 0); fclose(xpathOutput); if (result != NULL) { ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); res = 1; } } if (temp != NULL) { unlink(temp); free(temp); } xmlFreeDoc(xpathDocument); if (err != NULL) { ret = compareFileMem(err, testErrors, testErrorsSize); if (ret != 0) { fprintf(stderr, "Error for %s failed\n", filename); res = 1; } } return(res); } #endif /* LIBXML_VALID_ENABLED */ #endif /* LIBXML_DEBUG_ENABLED */ #endif /* XPATH */ /************************************************************************ * * * URI based tests * * * ************************************************************************/ static void handleURI(const char *str, const char *base, FILE *o) { int ret; xmlURIPtr uri; xmlChar *res = NULL; uri = xmlCreateURI(); if (base == NULL) { ret = xmlParseURIReference(uri, str); if (ret != 0) fprintf(o, "%s : error %d\n", str, ret); else { xmlNormalizeURIPath(uri->path); xmlPrintURI(o, uri); fprintf(o, "\n"); } } else { res = xmlBuildURI((xmlChar *)str, (xmlChar *) base); if (res != NULL) { fprintf(o, "%s\n", (char *) res); } else fprintf(o, "::ERROR::\n"); } if (res != NULL) xmlFree(res); xmlFreeURI(uri); } /** * uriCommonTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing URI and check for errors * * Returns 0 in case of success, an error code otherwise */ static int uriCommonTest(const char *filename, const char *result, const char *err, const char *base) { char *temp; FILE *o, *f; char str[1024]; int res = 0, i, ret; temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } o = fopen(temp, "wb"); if (o == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); free(temp); return(-1); } f = fopen(filename, "rb"); if (f == NULL) { fprintf(stderr, "failed to open input file %s\n", filename); fclose(o); if (temp != NULL) { unlink(temp); free(temp); } return(-1); } while (1) { /* * read one line in string buffer. */ if (fgets (&str[0], sizeof (str) - 1, f) == NULL) break; /* * remove the ending spaces */ i = strlen(str); while ((i > 0) && ((str[i - 1] == '\n') || (str[i - 1] == '\r') || (str[i - 1] == ' ') || (str[i - 1] == '\t'))) { i--; str[i] = 0; } nb_tests++; handleURI(str, base, o); } fclose(f); fclose(o); if (result != NULL) { ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); res = 1; } } if (err != NULL) { ret = compareFileMem(err, testErrors, testErrorsSize); if (ret != 0) { fprintf(stderr, "Error for %s failed\n", filename); res = 1; } } if (temp != NULL) { unlink(temp); free(temp); } return(res); } /** * uriParseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing URI and check for errors * * Returns 0 in case of success, an error code otherwise */ static int uriParseTest(const char *filename, const char *result, const char *err, int options ATTRIBUTE_UNUSED) { return(uriCommonTest(filename, result, err, NULL)); } /** * uriBaseTest: * @filename: the file to parse * @result: the file with expected result * @err: the file with error messages * * Parse a file containing URI, compose them against a fixed base and * check for errors * * Returns 0 in case of success, an error code otherwise */ static int uriBaseTest(const char *filename, const char *result, const char *err, int options ATTRIBUTE_UNUSED) { return(uriCommonTest(filename, result, err, "http://foo.com/path/to/index.html?orig#help")); } static int urip_success = 1; static int urip_current = 0; static const char *urip_testURLs[] = { "urip://example.com/a b.html", "urip://example.com/a%20b.html", "file:///path/to/a b.html", "file:///path/to/a%20b.html", "/path/to/a b.html", "/path/to/a%20b.html", "urip://example.com/r" "\xe9" "sum" "\xe9" ".html", "urip://example.com/test?a=1&b=2%263&c=4#foo", NULL }; static const char *urip_rcvsURLs[] = { /* it is an URI the strings must be escaped */ "urip://example.com/a%20b.html", /* check that % escaping is not broken */ "urip://example.com/a%20b.html", /* it's an URI path the strings must be escaped */ "file:///path/to/a%20b.html", /* check that % escaping is not broken */ "file:///path/to/a%20b.html", /* this is not an URI, this is a path, so this should not be escaped */ "/path/to/a b.html", /* check that paths with % are not broken */ "/path/to/a%20b.html", /* out of context the encoding can't be guessed byte by byte conversion */ "urip://example.com/r%E9sum%E9.html", /* verify we don't destroy URIs especially the query part */ "urip://example.com/test?a=1&b=2%263&c=4#foo", NULL }; static const char *urip_res = ""; static const char *urip_cur = NULL; static int urip_rlen; /** * uripMatch: * @URI: an URI to test * * Check for an urip: query * * Returns 1 if yes and 0 if another Input module should be used */ static int uripMatch(const char * URI) { if ((URI == NULL) || (!strcmp(URI, "file://" SYSCONFDIR "/xml/catalog"))) return(0); /* Verify we received the escaped URL */ if (strcmp(urip_rcvsURLs[urip_current], URI)) urip_success = 0; return(1); } /** * uripOpen: * @URI: an URI to test * * Return a pointer to the urip: query handler, in this example simply * the urip_current pointer... * * Returns an Input context or NULL in case or error */ static void * uripOpen(const char * URI) { if ((URI == NULL) || (!strcmp(URI, "file://" SYSCONFDIR "/xml/catalog"))) return(NULL); /* Verify we received the escaped URL */ if (strcmp(urip_rcvsURLs[urip_current], URI)) urip_success = 0; urip_cur = urip_res; urip_rlen = strlen(urip_res); return((void *) urip_cur); } /** * uripClose: * @context: the read context * * Close the urip: query handler * * Returns 0 or -1 in case of error */ static int uripClose(void * context) { if (context == NULL) return(-1); urip_cur = NULL; urip_rlen = 0; return(0); } /** * uripRead: * @context: the read context * @buffer: where to store data * @len: number of bytes to read * * Implement an urip: query read. * * Returns the number of bytes read or -1 in case of error */ static int uripRead(void * context, char * buffer, int len) { const char *ptr = (const char *) context; if ((context == NULL) || (buffer == NULL) || (len < 0)) return(-1); if (len > urip_rlen) len = urip_rlen; memcpy(buffer, ptr, len); urip_rlen -= len; return(len); } static int urip_checkURL(const char *URL) { xmlDocPtr doc; doc = xmlReadFile(URL, NULL, 0); if (doc == NULL) return(-1); xmlFreeDoc(doc); return(1); } /** * uriPathTest: * @filename: ignored * @result: ignored * @err: ignored * * Run a set of tests to check how Path and URI are handled before * being passed to the I/O layer * * Returns 0 in case of success, an error code otherwise */ static int uriPathTest(const char *filename ATTRIBUTE_UNUSED, const char *result ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { int parsed; int failures = 0; /* * register the new I/O handlers */ if (xmlRegisterInputCallbacks(uripMatch, uripOpen, uripRead, uripClose) < 0) { fprintf(stderr, "failed to register HTTP handler\n"); return(-1); } for (urip_current = 0;urip_testURLs[urip_current] != NULL;urip_current++) { urip_success = 1; parsed = urip_checkURL(urip_testURLs[urip_current]); if (urip_success != 1) { fprintf(stderr, "failed the URL passing test for %s", urip_testURLs[urip_current]); failures++; } else if (parsed != 1) { fprintf(stderr, "failed the parsing test for %s", urip_testURLs[urip_current]); failures++; } nb_tests++; } xmlPopInputCallbacks(); return(failures); } #ifdef LIBXML_SCHEMAS_ENABLED /************************************************************************ * * * Schemas tests * * * ************************************************************************/ static int schemasOneTest(const char *sch, const char *filename, const char *result, const char *err, int options, xmlSchemaPtr schemas) { int ret = 0; int i; char *temp; int parseErrorsSize = testErrorsSize; temp = resultFilename(result, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); return(-1); } /* * Test both memory and streaming validation. */ for (i = 0; i < 2; i++) { xmlSchemaValidCtxtPtr ctxt; int validResult = 0; FILE *schemasOutput; testErrorsSize = parseErrorsSize; testErrors[parseErrorsSize] = 0; ctxt = xmlSchemaNewValidCtxt(schemas); xmlSchemaSetValidErrors(ctxt, testErrorHandler, testErrorHandler, ctxt); schemasOutput = fopen(temp, "wb"); if (schemasOutput == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); free(temp); return(-1); } if (i == 0) { xmlDocPtr doc; doc = xmlReadFile(filename, NULL, options); if (doc == NULL) { fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); return(-1); } validResult = xmlSchemaValidateDoc(ctxt, doc); xmlFreeDoc(doc); } else { validResult = xmlSchemaValidateFile(ctxt, filename, options); } if (validResult == 0) { fprintf(schemasOutput, "%s validates\n", filename); } else if (validResult > 0) { fprintf(schemasOutput, "%s fails to validate\n", filename); } else { fprintf(schemasOutput, "%s validation generated an internal error\n", filename); } fclose(schemasOutput); if (result) { if (compareFiles(temp, result)) { fprintf(stderr, "Result for %s on %s failed\n", filename, sch); ret = 1; } } if (compareFileMem(err, testErrors, testErrorsSize)) { fprintf(stderr, "Error for %s on %s failed\n", filename, sch); ret = 1; } unlink(temp); xmlSchemaFreeValidCtxt(ctxt); } free(temp); return(ret); } /** * schemasTest: * @filename: the schemas file * @result: the file with expected result * @err: the file with error messages * * Parse a file containing URI, compose them against a fixed base and * check for errors * * Returns 0 in case of success, an error code otherwise */ static int schemasTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *errr ATTRIBUTE_UNUSED, int options) { const char *base = baseFilename(filename); const char *base2; const char *instance; xmlSchemaParserCtxtPtr ctxt; xmlSchemaPtr schemas; int res = 0, len, ret; int parseErrorsSize; char pattern[500]; char prefix[500]; char result[500]; char err[500]; glob_t globbuf; size_t i; char count = 0; /* first compile the schemas if possible */ ctxt = xmlSchemaNewParserCtxt(filename); xmlSchemaSetParserErrors(ctxt, testErrorHandler, testErrorHandler, ctxt); schemas = xmlSchemaParse(ctxt); xmlSchemaFreeParserCtxt(ctxt); parseErrorsSize = testErrorsSize; /* * most of the mess is about the output filenames generated by the Makefile */ len = strlen(base); if ((len > 499) || (len < 5)) { xmlSchemaFree(schemas); return(-1); } len -= 4; /* remove trailing .xsd */ if (base[len - 2] == '_') { len -= 2; /* remove subtest number */ } if (base[len - 2] == '_') { len -= 2; /* remove subtest number */ } memcpy(prefix, base, len); prefix[len] = 0; if (snprintf(pattern, 499, "./test/schemas/%s_*.xml", prefix) >= 499) pattern[499] = 0; if (base[len] == '_') { len += 2; memcpy(prefix, base, len); prefix[len] = 0; } globbuf.gl_offs = 0; glob(pattern, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { testErrorsSize = parseErrorsSize; testErrors[parseErrorsSize] = 0; instance = globbuf.gl_pathv[i]; base2 = baseFilename(instance); len = strlen(base2); if ((len > 6) && (base2[len - 6] == '_')) { count = base2[len - 5]; ret = snprintf(result, 499, "result/schemas/%s_%c", prefix, count); if (ret >= 499) result[499] = 0; ret = snprintf(err, 499, "result/schemas/%s_%c.err", prefix, count); if (ret >= 499) err[499] = 0; } else { fprintf(stderr, "don't know how to process %s\n", instance); continue; } if (schemas != NULL) { nb_tests++; ret = schemasOneTest(filename, instance, result, err, options, schemas); if (ret != 0) res = ret; } } globfree(&globbuf); xmlSchemaFree(schemas); return(res); } /************************************************************************ * * * Schemas tests * * * ************************************************************************/ static int rngOneTest(const char *sch, const char *filename, const char *result, int options, xmlRelaxNGPtr schemas) { xmlDocPtr doc; xmlRelaxNGValidCtxtPtr ctxt; int ret = 0; char *temp; FILE *schemasOutput; doc = xmlReadFile(filename, NULL, options); if (doc == NULL) { fprintf(stderr, "failed to parse instance %s for %s\n", filename, sch); return(-1); } temp = resultFilename(result, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } schemasOutput = fopen(temp, "wb"); if (schemasOutput == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); xmlFreeDoc(doc); free(temp); return(-1); } ctxt = xmlRelaxNGNewValidCtxt(schemas); xmlRelaxNGSetValidErrors(ctxt, testErrorHandler, testErrorHandler, ctxt); ret = xmlRelaxNGValidateDoc(ctxt, doc); if (ret == 0) { testErrorHandler(NULL, "%s validates\n", filename); } else if (ret > 0) { testErrorHandler(NULL, "%s fails to validate\n", filename); } else { testErrorHandler(NULL, "%s validation generated an internal error\n", filename); } fclose(schemasOutput); ret = 0; if (result) { if (compareFiles(temp, result)) { fprintf(stderr, "Result for %s on %s failed\n", filename, sch); ret = 1; } } if (temp != NULL) { unlink(temp); free(temp); } xmlRelaxNGFreeValidCtxt(ctxt); xmlFreeDoc(doc); return(ret); } /** * rngTest: * @filename: the schemas file * @result: the file with expected result * @err: the file with error messages * * Parse an RNG schemas and then apply it to the related .xml * * Returns 0 in case of success, an error code otherwise */ static int rngTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *errr ATTRIBUTE_UNUSED, int options) { const char *base = baseFilename(filename); const char *base2; const char *instance; xmlRelaxNGParserCtxtPtr ctxt; xmlRelaxNGPtr schemas; int res = 0, len, ret = 0; int parseErrorsSize; char pattern[500]; char prefix[500]; char result[500]; char err[500]; glob_t globbuf; size_t i; char count = 0; /* first compile the schemas if possible */ ctxt = xmlRelaxNGNewParserCtxt(filename); xmlRelaxNGSetParserErrors(ctxt, testErrorHandler, testErrorHandler, ctxt); schemas = xmlRelaxNGParse(ctxt); xmlRelaxNGFreeParserCtxt(ctxt); if (schemas == NULL) testErrorHandler(NULL, "Relax-NG schema %s failed to compile\n", filename); parseErrorsSize = testErrorsSize; /* * most of the mess is about the output filenames generated by the Makefile */ len = strlen(base); if ((len > 499) || (len < 5)) { xmlRelaxNGFree(schemas); return(-1); } len -= 4; /* remove trailing .rng */ memcpy(prefix, base, len); prefix[len] = 0; if (snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix) >= 499) pattern[499] = 0; globbuf.gl_offs = 0; glob(pattern, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { testErrorsSize = parseErrorsSize; testErrors[parseErrorsSize] = 0; instance = globbuf.gl_pathv[i]; base2 = baseFilename(instance); len = strlen(base2); if ((len > 6) && (base2[len - 6] == '_')) { count = base2[len - 5]; res = snprintf(result, 499, "result/relaxng/%s_%c", prefix, count); if (res >= 499) result[499] = 0; res = snprintf(err, 499, "result/relaxng/%s_%c.err", prefix, count); if (res >= 499) err[499] = 0; } else { fprintf(stderr, "don't know how to process %s\n", instance); continue; } if (schemas != NULL) { nb_tests++; res = rngOneTest(filename, instance, result, options, schemas); if (res != 0) ret = res; } if (compareFileMem(err, testErrors, testErrorsSize)) { fprintf(stderr, "Error for %s on %s failed\n", instance, filename); res = 1; } } globfree(&globbuf); xmlRelaxNGFree(schemas); return(ret); } #ifdef LIBXML_READER_ENABLED /** * rngStreamTest: * @filename: the schemas file * @result: the file with expected result * @err: the file with error messages * * Parse a set of files with streaming, applying an RNG schemas * * Returns 0 in case of success, an error code otherwise */ static int rngStreamTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *errr ATTRIBUTE_UNUSED, int options) { const char *base = baseFilename(filename); const char *base2; const char *instance; int res = 0, len, ret; char pattern[500]; char prefix[500]; char result[500]; char err[500]; glob_t globbuf; size_t i; char count = 0; xmlTextReaderPtr reader; int disable_err = 0; /* * most of the mess is about the output filenames generated by the Makefile */ len = strlen(base); if ((len > 499) || (len < 5)) { fprintf(stderr, "len(base) == %d !\n", len); return(-1); } len -= 4; /* remove trailing .rng */ memcpy(prefix, base, len); prefix[len] = 0; /* * strictly unifying the error messages is nearly impossible this * hack is also done in the Makefile */ if ((!strcmp(prefix, "tutor10_1")) || (!strcmp(prefix, "tutor10_2")) || (!strcmp(prefix, "tutor3_2")) || (!strcmp(prefix, "307377")) || (!strcmp(prefix, "tutor8_2"))) disable_err = 1; if (snprintf(pattern, 499, "./test/relaxng/%s_?.xml", prefix) >= 499) pattern[499] = 0; globbuf.gl_offs = 0; glob(pattern, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { testErrorsSize = 0; testErrors[0] = 0; instance = globbuf.gl_pathv[i]; base2 = baseFilename(instance); len = strlen(base2); if ((len > 6) && (base2[len - 6] == '_')) { count = base2[len - 5]; ret = snprintf(result, 499, "result/relaxng/%s_%c", prefix, count); if (ret >= 499) result[499] = 0; ret = snprintf(err, 499, "result/relaxng/%s_%c.err", prefix, count); if (ret >= 499) err[499] = 0; } else { fprintf(stderr, "don't know how to process %s\n", instance); continue; } reader = xmlReaderForFile(instance, NULL, options); if (reader == NULL) { fprintf(stderr, "Failed to build reader for %s\n", instance); } if (disable_err == 1) ret = streamProcessTest(instance, result, NULL, reader, filename, options); else ret = streamProcessTest(instance, result, err, reader, filename, options); xmlFreeTextReader(reader); if (ret != 0) { fprintf(stderr, "instance %s failed\n", instance); res = ret; } } globfree(&globbuf); return(res); } #endif /* READER */ #endif #ifdef LIBXML_PATTERN_ENABLED #ifdef LIBXML_READER_ENABLED /************************************************************************ * * * Patterns tests * * * ************************************************************************/ static void patternNode(FILE *out, xmlTextReaderPtr reader, const char *pattern, xmlPatternPtr patternc, xmlStreamCtxtPtr patstream) { xmlChar *path = NULL; int match = -1; int type, empty; type = xmlTextReaderNodeType(reader); empty = xmlTextReaderIsEmptyElement(reader); if (type == XML_READER_TYPE_ELEMENT) { /* do the check only on element start */ match = xmlPatternMatch(patternc, xmlTextReaderCurrentNode(reader)); if (match) { path = xmlGetNodePath(xmlTextReaderCurrentNode(reader)); fprintf(out, "Node %s matches pattern %s\n", path, pattern); } } if (patstream != NULL) { int ret; if (type == XML_READER_TYPE_ELEMENT) { ret = xmlStreamPush(patstream, xmlTextReaderConstLocalName(reader), xmlTextReaderConstNamespaceUri(reader)); if (ret < 0) { fprintf(out, "xmlStreamPush() failure\n"); xmlFreeStreamCtxt(patstream); patstream = NULL; } else if (ret != match) { if (path == NULL) { path = xmlGetNodePath( xmlTextReaderCurrentNode(reader)); } fprintf(out, "xmlPatternMatch and xmlStreamPush disagree\n"); fprintf(out, " pattern %s node %s\n", pattern, path); } } if ((type == XML_READER_TYPE_END_ELEMENT) || ((type == XML_READER_TYPE_ELEMENT) && (empty))) { ret = xmlStreamPop(patstream); if (ret < 0) { fprintf(out, "xmlStreamPop() failure\n"); xmlFreeStreamCtxt(patstream); patstream = NULL; } } } if (path != NULL) xmlFree(path); } /** * patternTest: * @filename: the schemas file * @result: the file with expected result * @err: the file with error messages * * Parse a set of files with streaming, applying an RNG schemas * * Returns 0 in case of success, an error code otherwise */ static int patternTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options) { xmlPatternPtr patternc = NULL; xmlStreamCtxtPtr patstream = NULL; FILE *o, *f; char str[1024]; char xml[500]; char result[500]; int len, i; int ret = 0, res; char *temp; xmlTextReaderPtr reader; xmlDocPtr doc; len = strlen(filename); len -= 4; memcpy(xml, filename, len); xml[len] = 0; if (snprintf(result, 499, "result/pattern/%s", baseFilename(xml)) >= 499) result[499] = 0; memcpy(xml + len, ".xml", 5); if (!checkTestFile(xml) && !update_results) { fprintf(stderr, "Missing xml file %s\n", xml); return(-1); } f = fopen(filename, "rb"); if (f == NULL) { fprintf(stderr, "Failed to open %s\n", filename); return(-1); } temp = resultFilename(filename, temp_directory, ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } o = fopen(temp, "wb"); if (o == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); fclose(f); free(temp); return(-1); } while (1) { /* * read one line in string buffer. */ if (fgets (&str[0], sizeof (str) - 1, f) == NULL) break; /* * remove the ending spaces */ i = strlen(str); while ((i > 0) && ((str[i - 1] == '\n') || (str[i - 1] == '\r') || (str[i - 1] == ' ') || (str[i - 1] == '\t'))) { i--; str[i] = 0; } doc = xmlReadFile(xml, NULL, options); if (doc == NULL) { fprintf(stderr, "Failed to parse %s\n", xml); ret = 1; } else { xmlNodePtr root; const xmlChar *namespaces[22]; int j; xmlNsPtr ns; root = xmlDocGetRootElement(doc); for (ns = root->nsDef, j = 0;ns != NULL && j < 20;ns=ns->next) { namespaces[j++] = ns->href; namespaces[j++] = ns->prefix; } namespaces[j++] = NULL; namespaces[j] = NULL; patternc = xmlPatterncompile((const xmlChar *) str, doc->dict, 0, &namespaces[0]); if (patternc == NULL) { testErrorHandler(NULL, "Pattern %s failed to compile\n", str); xmlFreeDoc(doc); ret = 1; continue; } patstream = xmlPatternGetStreamCtxt(patternc); if (patstream != NULL) { ret = xmlStreamPush(patstream, NULL, NULL); if (ret < 0) { fprintf(stderr, "xmlStreamPush() failure\n"); xmlFreeStreamCtxt(patstream); patstream = NULL; } } nb_tests++; reader = xmlReaderWalker(doc); res = xmlTextReaderRead(reader); while (res == 1) { patternNode(o, reader, str, patternc, patstream); res = xmlTextReaderRead(reader); } if (res != 0) { fprintf(o, "%s : failed to parse\n", filename); } xmlFreeTextReader(reader); xmlFreeDoc(doc); xmlFreeStreamCtxt(patstream); patstream = NULL; xmlFreePattern(patternc); } } fclose(f); fclose(o); ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); ret = 1; } if (temp != NULL) { unlink(temp); free(temp); } return(ret); } #endif /* READER */ #endif /* PATTERN */ #ifdef LIBXML_C14N_ENABLED /************************************************************************ * * * Canonicalization tests * * * ************************************************************************/ static xmlXPathObjectPtr load_xpath_expr (xmlDocPtr parent_doc, const char* filename) { xmlXPathObjectPtr xpath; xmlDocPtr doc; xmlChar *expr; xmlXPathContextPtr ctx; xmlNodePtr node; xmlNsPtr ns; /* * load XPath expr as a file */ doc = xmlReadFile(filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT); if (doc == NULL) { fprintf(stderr, "Error: unable to parse file \"%s\"\n", filename); return(NULL); } /* * Check the document is of the right kind */ if(xmlDocGetRootElement(doc) == NULL) { fprintf(stderr,"Error: empty document for file \"%s\"\n", filename); xmlFreeDoc(doc); return(NULL); } node = doc->children; while(node != NULL && !xmlStrEqual(node->name, (const xmlChar *)"XPath")) { node = node->next; } if(node == NULL) { fprintf(stderr,"Error: XPath element expected in the file \"%s\"\n", filename); xmlFreeDoc(doc); return(NULL); } expr = xmlNodeGetContent(node); if(expr == NULL) { fprintf(stderr,"Error: XPath content element is NULL \"%s\"\n", filename); xmlFreeDoc(doc); return(NULL); } ctx = xmlXPathNewContext(parent_doc); if(ctx == NULL) { fprintf(stderr,"Error: unable to create new context\n"); xmlFree(expr); xmlFreeDoc(doc); return(NULL); } /* * Register namespaces */ ns = node->nsDef; while(ns != NULL) { if(xmlXPathRegisterNs(ctx, ns->prefix, ns->href) != 0) { fprintf(stderr,"Error: unable to register NS with prefix=\"%s\" and href=\"%s\"\n", ns->prefix, ns->href); xmlFree(expr); xmlXPathFreeContext(ctx); xmlFreeDoc(doc); return(NULL); } ns = ns->next; } /* * Evaluate xpath */ xpath = xmlXPathEvalExpression(expr, ctx); if(xpath == NULL) { fprintf(stderr,"Error: unable to evaluate xpath expression\n"); xmlFree(expr); xmlXPathFreeContext(ctx); xmlFreeDoc(doc); return(NULL); } /* print_xpath_nodes(xpath->nodesetval); */ xmlFree(expr); xmlXPathFreeContext(ctx); xmlFreeDoc(doc); return(xpath); } /* * Macro used to grow the current buffer. */ #define xxx_growBufferReentrant() { \ buffer_size *= 2; \ buffer = (xmlChar **) \ xmlRealloc(buffer, buffer_size * sizeof(xmlChar*)); \ if (buffer == NULL) { \ perror("realloc failed"); \ return(NULL); \ } \ } static xmlChar ** parse_list(xmlChar *str) { xmlChar **buffer; xmlChar **out = NULL; int buffer_size = 0; int len; if(str == NULL) { return(NULL); } len = xmlStrlen(str); if((str[0] == '\'') && (str[len - 1] == '\'')) { str[len - 1] = '\0'; str++; } /* * allocate an translation buffer. */ buffer_size = 1000; buffer = (xmlChar **) xmlMalloc(buffer_size * sizeof(xmlChar*)); if (buffer == NULL) { perror("malloc failed"); return(NULL); } out = buffer; while(*str != '\0') { if (out - buffer > buffer_size - 10) { int indx = out - buffer; xxx_growBufferReentrant(); out = &buffer[indx]; } (*out++) = str; while(*str != ',' && *str != '\0') ++str; if(*str == ',') *(str++) = '\0'; } (*out) = NULL; return buffer; } static int c14nRunTest(const char* xml_filename, int with_comments, int mode, const char* xpath_filename, const char *ns_filename, const char* result_file) { xmlDocPtr doc; xmlXPathObjectPtr xpath = NULL; xmlChar *result = NULL; int ret; xmlChar **inclusive_namespaces = NULL; const char *nslist = NULL; int nssize; /* * build an XML tree from a the file; we need to add default * attributes and resolve all character and entities references */ doc = xmlReadFile(xml_filename, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT); if (doc == NULL) { fprintf(stderr, "Error: unable to parse file \"%s\"\n", xml_filename); return(-1); } /* * Check the document is of the right kind */ if(xmlDocGetRootElement(doc) == NULL) { fprintf(stderr,"Error: empty document for file \"%s\"\n", xml_filename); xmlFreeDoc(doc); return(-1); } /* * load xpath file if specified */ if(xpath_filename) { xpath = load_xpath_expr(doc, xpath_filename); if(xpath == NULL) { fprintf(stderr,"Error: unable to evaluate xpath expression\n"); xmlFreeDoc(doc); return(-1); } } if (ns_filename != NULL) { if (loadMem(ns_filename, &nslist, &nssize)) { fprintf(stderr,"Error: unable to evaluate xpath expression\n"); if(xpath != NULL) xmlXPathFreeObject(xpath); xmlFreeDoc(doc); return(-1); } inclusive_namespaces = parse_list((xmlChar *) nslist); } /* * Canonical form */ /* fprintf(stderr,"File \"%s\" loaded: start canonization\n", xml_filename); */ ret = xmlC14NDocDumpMemory(doc, (xpath) ? xpath->nodesetval : NULL, mode, inclusive_namespaces, with_comments, &result); if (ret >= 0) { if(result != NULL) { if (compareFileMem(result_file, (const char *) result, ret)) { fprintf(stderr, "Result mismatch for %s\n", xml_filename); fprintf(stderr, "RESULT:\n%s\n", (const char*)result); ret = -1; } } } else { fprintf(stderr,"Error: failed to canonicalize XML file \"%s\" (ret=%d)\n", xml_filename, ret); ret = -1; } /* * Cleanup */ if (result != NULL) xmlFree(result); if(xpath != NULL) xmlXPathFreeObject(xpath); if (inclusive_namespaces != NULL) xmlFree(inclusive_namespaces); if (nslist != NULL) free((char *) nslist); xmlFreeDoc(doc); return(ret); } static int c14nCommonTest(const char *filename, int with_comments, int mode, const char *subdir) { char buf[500]; char prefix[500]; const char *base; int len; char *result = NULL; char *xpath = NULL; char *ns = NULL; int ret = 0; base = baseFilename(filename); len = strlen(base); len -= 4; memcpy(prefix, base, len); prefix[len] = 0; if (snprintf(buf, 499, "result/c14n/%s/%s", subdir, prefix) >= 499) buf[499] = 0; result = strdup(buf); if (snprintf(buf, 499, "test/c14n/%s/%s.xpath", subdir, prefix) >= 499) buf[499] = 0; if (checkTestFile(buf)) { xpath = strdup(buf); } if (snprintf(buf, 499, "test/c14n/%s/%s.ns", subdir, prefix) >= 499) buf[499] = 0; if (checkTestFile(buf)) { ns = strdup(buf); } nb_tests++; if (c14nRunTest(filename, with_comments, mode, xpath, ns, result) < 0) ret = 1; if (result != NULL) free(result); if (xpath != NULL) free(xpath); if (ns != NULL) free(ns); return(ret); } static int c14nWithCommentTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(c14nCommonTest(filename, 1, XML_C14N_1_0, "with-comments")); } static int c14nWithoutCommentTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(c14nCommonTest(filename, 0, XML_C14N_1_0, "without-comments")); } static int c14nExcWithoutCommentTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(c14nCommonTest(filename, 0, XML_C14N_EXCLUSIVE_1_0, "exc-without-comments")); } static int c14n11WithoutCommentTest(const char *filename, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(c14nCommonTest(filename, 0, XML_C14N_1_1, "1-1-without-comments")); } #endif #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) /************************************************************************ * * * Catalog and threads test * * * ************************************************************************/ /* * mostly a cut and paste from testThreads.c */ #define MAX_ARGC 20 typedef struct { const char *filename; int okay; } xmlThreadParams; static const char *catalog = "test/threads/complex.xml"; static xmlThreadParams threadParams[] = { { "test/threads/abc.xml", 0 }, { "test/threads/acb.xml", 0 }, { "test/threads/bac.xml", 0 }, { "test/threads/bca.xml", 0 }, { "test/threads/cab.xml", 0 }, { "test/threads/cba.xml", 0 }, { "test/threads/invalid.xml", 0 } }; static const unsigned int num_threads = sizeof(threadParams) / sizeof(threadParams[0]); static void * thread_specific_data(void *private_data) { xmlDocPtr myDoc; xmlThreadParams *params = (xmlThreadParams *) private_data; const char *filename = params->filename; int okay = 1; myDoc = xmlReadFile(filename, NULL, XML_PARSE_NOENT | XML_PARSE_DTDLOAD); if (myDoc) { xmlFreeDoc(myDoc); } else { printf("parse failed\n"); okay = 0; } params->okay = okay; return(NULL); } #if defined(_WIN32) #include #include #define TEST_REPEAT_COUNT 500 static HANDLE tid[MAX_ARGC]; static DWORD WINAPI win32_thread_specific_data(void *private_data) { thread_specific_data(private_data); return(0); } static int testThread(void) { unsigned int i, repeat; BOOL ret; int res = 0; xmlInitParser(); for (repeat = 0; repeat < TEST_REPEAT_COUNT; repeat++) { xmlLoadCatalog(catalog); nb_tests++; for (i = 0; i < num_threads; i++) { tid[i] = (HANDLE) - 1; } for (i = 0; i < num_threads; i++) { DWORD useless; tid[i] = CreateThread(NULL, 0, win32_thread_specific_data, (void *) &threadParams[i], 0, &useless); if (tid[i] == NULL) { fprintf(stderr, "CreateThread failed\n"); return(1); } } if (WaitForMultipleObjects(num_threads, tid, TRUE, INFINITE) == WAIT_FAILED) { fprintf(stderr, "WaitForMultipleObjects failed\n"); return(1); } for (i = 0; i < num_threads; i++) { DWORD exitCode; ret = GetExitCodeThread(tid[i], &exitCode); if (ret == 0) { fprintf(stderr, "GetExitCodeThread failed\n"); return(1); } CloseHandle(tid[i]); } xmlCatalogCleanup(); for (i = 0; i < num_threads; i++) { if (threadParams[i].okay == 0) { fprintf(stderr, "Thread %d handling %s failed\n", i, threadParams[i].filename); res = 1; } } } return (res); } #elif defined HAVE_PTHREAD_H #include static pthread_t tid[MAX_ARGC]; static int testThread(void) { unsigned int i, repeat; int ret; int res = 0; xmlInitParser(); for (repeat = 0; repeat < 500; repeat++) { xmlLoadCatalog(catalog); nb_tests++; for (i = 0; i < num_threads; i++) { tid[i] = (pthread_t) - 1; } for (i = 0; i < num_threads; i++) { ret = pthread_create(&tid[i], 0, thread_specific_data, (void *) &threadParams[i]); if (ret != 0) { fprintf(stderr, "pthread_create failed\n"); return (1); } } for (i = 0; i < num_threads; i++) { void *result; ret = pthread_join(tid[i], &result); if (ret != 0) { fprintf(stderr, "pthread_join failed\n"); return (1); } } xmlCatalogCleanup(); for (i = 0; i < num_threads; i++) if (threadParams[i].okay == 0) { fprintf(stderr, "Thread %d handling %s failed\n", i, threadParams[i].filename); res = 1; } } return (res); } #else static int testThread(void) { fprintf(stderr, "Specific platform thread support not detected\n"); return (-1); } #endif static int threadsTest(const char *filename ATTRIBUTE_UNUSED, const char *resul ATTRIBUTE_UNUSED, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { return(testThread()); } #endif #if defined(LIBXML_REGEXP_ENABLED) /************************************************************************ * * * Regexp tests * * * ************************************************************************/ static void testRegexp(FILE *output, xmlRegexpPtr comp, const char *value) { int ret; ret = xmlRegexpExec(comp, (const xmlChar *) value); if (ret == 1) fprintf(output, "%s: Ok\n", value); else if (ret == 0) fprintf(output, "%s: Fail\n", value); else fprintf(output, "%s: Error: %d\n", value, ret); } static int regexpTest(const char *filename, const char *result, const char *err, int options ATTRIBUTE_UNUSED) { xmlRegexpPtr comp = NULL; FILE *input, *output; char *temp; char expression[5000]; int len, ret, res = 0; nb_tests++; input = fopen(filename, "rb"); if (input == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot open %s for reading\n", filename); return(-1); } temp = resultFilename(filename, "", ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } output = fopen(temp, "wb"); if (output == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); free(temp); return(-1); } while (fgets(expression, 4500, input) != NULL) { len = strlen(expression); len--; while ((len >= 0) && ((expression[len] == '\n') || (expression[len] == '\t') || (expression[len] == '\r') || (expression[len] == ' '))) len--; expression[len + 1] = 0; if (len >= 0) { if (expression[0] == '#') continue; if ((expression[0] == '=') && (expression[1] == '>')) { char *pattern = &expression[2]; if (comp != NULL) { xmlRegFreeRegexp(comp); comp = NULL; } fprintf(output, "Regexp: %s\n", pattern) ; comp = xmlRegexpCompile((const xmlChar *) pattern); if (comp == NULL) { fprintf(output, " failed to compile\n"); break; } } else if (comp == NULL) { fprintf(output, "Regexp: %s\n", expression) ; comp = xmlRegexpCompile((const xmlChar *) expression); if (comp == NULL) { fprintf(output, " failed to compile\n"); break; } } else if (comp != NULL) { testRegexp(output, comp, expression); } } } fclose(output); fclose(input); if (comp != NULL) xmlRegFreeRegexp(comp); ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); res = 1; } if (temp != NULL) { unlink(temp); free(temp); } ret = compareFileMem(err, testErrors, testErrorsSize); if (ret != 0) { fprintf(stderr, "Error for %s failed\n", filename); res = 1; } return(res); } #endif /* LIBXML_REGEXPS_ENABLED */ #ifdef LIBXML_AUTOMATA_ENABLED /************************************************************************ * * * Automata tests * * * ************************************************************************/ static int scanNumber(char **ptr) { int ret = 0; char *cur; cur = *ptr; while ((*cur >= '0') && (*cur <= '9')) { ret = ret * 10 + (*cur - '0'); cur++; } *ptr = cur; return(ret); } static int automataTest(const char *filename, const char *result, const char *err ATTRIBUTE_UNUSED, int options ATTRIBUTE_UNUSED) { FILE *input, *output; char *temp; char expr[5000]; int len; int ret; int i; int res = 0; xmlAutomataPtr am; xmlAutomataStatePtr states[1000]; xmlRegexpPtr regexp = NULL; xmlRegExecCtxtPtr exec = NULL; nb_tests++; for (i = 0;i<1000;i++) states[i] = NULL; input = fopen(filename, "rb"); if (input == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot open %s for reading\n", filename); return(-1); } temp = resultFilename(filename, "", ".res"); if (temp == NULL) { fprintf(stderr, "Out of memory\n"); fatalError(); } output = fopen(temp, "wb"); if (output == NULL) { fprintf(stderr, "failed to open output file %s\n", temp); free(temp); return(-1); } am = xmlNewAutomata(); if (am == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot create automata\n"); fclose(input); return(-1); } states[0] = xmlAutomataGetInitState(am); if (states[0] == NULL) { xmlGenericError(xmlGenericErrorContext, "Cannot get start state\n"); xmlFreeAutomata(am); fclose(input); return(-1); } ret = 0; while (fgets(expr, 4500, input) != NULL) { if (expr[0] == '#') continue; len = strlen(expr); len--; while ((len >= 0) && ((expr[len] == '\n') || (expr[len] == '\t') || (expr[len] == '\r') || (expr[len] == ' '))) len--; expr[len + 1] = 0; if (len >= 0) { if ((am != NULL) && (expr[0] == 't') && (expr[1] == ' ')) { char *ptr = &expr[2]; int from, to; from = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } if (states[from] == NULL) states[from] = xmlAutomataNewState(am); ptr++; to = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } if (states[to] == NULL) states[to] = xmlAutomataNewState(am); ptr++; xmlAutomataNewTransition(am, states[from], states[to], BAD_CAST ptr, NULL); } else if ((am != NULL) && (expr[0] == 'e') && (expr[1] == ' ')) { char *ptr = &expr[2]; int from, to; from = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } if (states[from] == NULL) states[from] = xmlAutomataNewState(am); ptr++; to = scanNumber(&ptr); if (states[to] == NULL) states[to] = xmlAutomataNewState(am); xmlAutomataNewEpsilon(am, states[from], states[to]); } else if ((am != NULL) && (expr[0] == 'f') && (expr[1] == ' ')) { char *ptr = &expr[2]; int state; state = scanNumber(&ptr); if (states[state] == NULL) { xmlGenericError(xmlGenericErrorContext, "Bad state %d : %s\n", state, expr); break; } xmlAutomataSetFinalState(am, states[state]); } else if ((am != NULL) && (expr[0] == 'c') && (expr[1] == ' ')) { char *ptr = &expr[2]; int from, to; int min, max; from = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } if (states[from] == NULL) states[from] = xmlAutomataNewState(am); ptr++; to = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } if (states[to] == NULL) states[to] = xmlAutomataNewState(am); ptr++; min = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } ptr++; max = scanNumber(&ptr); if (*ptr != ' ') { xmlGenericError(xmlGenericErrorContext, "Bad line %s\n", expr); break; } ptr++; xmlAutomataNewCountTrans(am, states[from], states[to], BAD_CAST ptr, min, max, NULL); } else if ((am != NULL) && (expr[0] == '-') && (expr[1] == '-')) { /* end of the automata */ regexp = xmlAutomataCompile(am); xmlFreeAutomata(am); am = NULL; if (regexp == NULL) { xmlGenericError(xmlGenericErrorContext, "Failed to compile the automata"); break; } } else if ((expr[0] == '=') && (expr[1] == '>')) { if (regexp == NULL) { fprintf(output, "=> failed not compiled\n"); } else { if (exec == NULL) exec = xmlRegNewExecCtxt(regexp, NULL, NULL); if (ret == 0) { ret = xmlRegExecPushString(exec, NULL, NULL); } if (ret == 1) fprintf(output, "=> Passed\n"); else if ((ret == 0) || (ret == -1)) fprintf(output, "=> Failed\n"); else if (ret < 0) fprintf(output, "=> Error\n"); xmlRegFreeExecCtxt(exec); exec = NULL; } ret = 0; } else if (regexp != NULL) { if (exec == NULL) exec = xmlRegNewExecCtxt(regexp, NULL, NULL); ret = xmlRegExecPushString(exec, BAD_CAST expr, NULL); } else { xmlGenericError(xmlGenericErrorContext, "Unexpected line %s\n", expr); } } } fclose(output); fclose(input); if (regexp != NULL) xmlRegFreeRegexp(regexp); if (exec != NULL) xmlRegFreeExecCtxt(exec); if (am != NULL) xmlFreeAutomata(am); ret = compareFiles(temp, result); if (ret) { fprintf(stderr, "Result for %s failed in %s\n", filename, result); res = 1; } if (temp != NULL) { unlink(temp); free(temp); } return(res); } #endif /* LIBXML_AUTOMATA_ENABLED */ /************************************************************************ * * * Tests Descriptions * * * ************************************************************************/ static testDesc testDescriptions[] = { { "XML regression tests" , oldParseTest, "./test/*", "result/", "", NULL, 0 }, { "XML regression tests on memory" , memParseTest, "./test/*", "result/", "", NULL, 0 }, { "XML entity subst regression tests" , noentParseTest, "./test/*", "result/noent/", "", NULL, XML_PARSE_NOENT }, { "XML Namespaces regression tests", errParseTest, "./test/namespaces/*", "result/namespaces/", "", ".err", 0 }, #ifdef LIBXML_VALID_ENABLED { "Error cases regression tests", errParseTest, "./test/errors/*.xml", "result/errors/", "", ".err", 0 }, { "Error cases regression tests from file descriptor", fdParseTest, "./test/errors/*.xml", "result/errors/", "", ".err", 0 }, { "Error cases regression tests with entity substitution", errParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".ent", XML_PARSE_NOENT }, { "Error cases regression tests (old 1.0)", errParseTest, "./test/errors10/*.xml", "result/errors10/", "", ".err", XML_PARSE_OLD10 }, #endif #ifdef LIBXML_READER_ENABLED #ifdef LIBXML_VALID_ENABLED { "Error cases stream regression tests", streamParseTest, "./test/errors/*.xml", "result/errors/", NULL, ".str", 0 }, #endif { "Reader regression tests", streamParseTest, "./test/*", "result/", ".rdr", NULL, 0 }, { "Reader entities substitution regression tests", streamParseTest, "./test/*", "result/", ".rde", NULL, XML_PARSE_NOENT }, { "Reader on memory regression tests", streamMemParseTest, "./test/*", "result/", ".rdr", NULL, 0 }, { "Walker regression tests", walkerParseTest, "./test/*", "result/", ".rdr", NULL, 0 }, #endif #ifdef LIBXML_SAX1_ENABLED { "SAX1 callbacks regression tests" , saxParseTest, "./test/*", "result/", ".sax", NULL, XML_PARSE_SAX1 }, #endif { "SAX2 callbacks regression tests" , saxParseTest, "./test/*", "result/", ".sax2", NULL, 0 }, { "SAX2 callbacks regression tests with entity substitution" , saxParseTest, "./test/*", "result/noent/", ".sax2", NULL, XML_PARSE_NOENT }, #ifdef LIBXML_PUSH_ENABLED { "XML push regression tests" , pushParseTest, "./test/*", "result/", "", NULL, 0 }, { "XML push boundary tests" , pushBoundaryTest, "./test/*", "result/", "", NULL, 0 }, #endif #ifdef LIBXML_HTML_ENABLED { "HTML regression tests" , errParseTest, "./test/HTML/*", "result/HTML/", "", ".err", XML_PARSE_HTML }, { "HTML regression tests from file descriptor", fdParseTest, "./test/HTML/*", "result/HTML/", "", ".err", XML_PARSE_HTML }, #ifdef LIBXML_PUSH_ENABLED { "Push HTML regression tests" , pushParseTest, "./test/HTML/*", "result/HTML/", "", ".err", XML_PARSE_HTML }, { "Push HTML boundary tests" , pushBoundaryTest, "./test/HTML/*", "result/HTML/", "", NULL, XML_PARSE_HTML }, #endif { "HTML SAX regression tests" , saxParseTest, "./test/HTML/*", "result/HTML/", ".sax", NULL, XML_PARSE_HTML }, #endif #ifdef LIBXML_VALID_ENABLED { "Valid documents regression tests" , errParseTest, "./test/VCM/*", NULL, NULL, NULL, XML_PARSE_DTDVALID }, { "Validity checking regression tests" , errParseTest, "./test/VC/*", "result/VC/", NULL, "", XML_PARSE_DTDVALID }, #ifdef LIBXML_READER_ENABLED { "Streaming validity checking regression tests" , streamParseTest, "./test/valid/*.xml", "result/valid/", NULL, ".err.rdr", XML_PARSE_DTDVALID }, { "Streaming validity error checking regression tests" , streamParseTest, "./test/VC/*", "result/VC/", NULL, ".rdr", XML_PARSE_DTDVALID }, #endif { "General documents valid regression tests" , errParseTest, "./test/valid/*", "result/valid/", "", ".err", XML_PARSE_DTDVALID }, #endif #ifdef LIBXML_XINCLUDE_ENABLED { "XInclude regression tests" , errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", ".err", XML_PARSE_XINCLUDE }, #ifdef LIBXML_READER_ENABLED { "XInclude xmlReader regression tests", streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", ".err", XML_PARSE_XINCLUDE }, #endif { "XInclude regression tests stripping include nodes" , errParseTest, "./test/XInclude/docs/*", "result/XInclude/", "", ".err", XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, #ifdef LIBXML_READER_ENABLED { "XInclude xmlReader regression tests stripping include nodes", streamParseTest, "./test/XInclude/docs/*", "result/XInclude/", ".rdr", ".err", XML_PARSE_XINCLUDE | XML_PARSE_NOXINCNODE }, #endif { "XInclude regression tests without reader", errParseTest, "./test/XInclude/without-reader/*", "result/XInclude/", "", ".err", XML_PARSE_XINCLUDE }, #endif #ifdef LIBXML_XPATH_ENABLED #ifdef LIBXML_DEBUG_ENABLED { "XPath expressions regression tests" , xpathExprTest, "./test/XPath/expr/*", "result/XPath/expr/", "", NULL, 0 }, { "XPath document queries regression tests" , xpathDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, 0 }, #ifdef LIBXML_XPTR_ENABLED { "XPointer document queries regression tests" , xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, -1 }, #endif #ifdef LIBXML_XPTR_LOCS_ENABLED { "XPointer xpointer() queries regression tests" , xptrDocTest, "./test/XPath/docs/*", NULL, NULL, NULL, 0 }, #endif #ifdef LIBXML_VALID_ENABLED { "xml:id regression tests" , xmlidDocTest, "./test/xmlid/*", "result/xmlid/", "", ".err", 0 }, #endif #endif #endif { "URI parsing tests" , uriParseTest, "./test/URI/*.uri", "result/URI/", "", NULL, 0 }, { "URI base composition tests" , uriBaseTest, "./test/URI/*.data", "result/URI/", "", NULL, 0 }, { "Path URI conversion tests" , uriPathTest, NULL, NULL, NULL, NULL, 0 }, #ifdef LIBXML_SCHEMAS_ENABLED { "Schemas regression tests" , schemasTest, "./test/schemas/*_*.xsd", NULL, NULL, NULL, 0 }, { "Relax-NG regression tests" , rngTest, "./test/relaxng/*.rng", NULL, NULL, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT }, #ifdef LIBXML_READER_ENABLED { "Relax-NG streaming regression tests" , rngStreamTest, "./test/relaxng/*.rng", NULL, NULL, NULL, XML_PARSE_DTDATTR | XML_PARSE_NOENT }, #endif #endif #ifdef LIBXML_PATTERN_ENABLED #ifdef LIBXML_READER_ENABLED { "Pattern regression tests" , patternTest, "./test/pattern/*.pat", "result/pattern/", NULL, NULL, 0 }, #endif #endif #ifdef LIBXML_C14N_ENABLED { "C14N with comments regression tests" , c14nWithCommentTest, "./test/c14n/with-comments/*.xml", NULL, NULL, NULL, 0 }, { "C14N without comments regression tests" , c14nWithoutCommentTest, "./test/c14n/without-comments/*.xml", NULL, NULL, NULL, 0 }, { "C14N exclusive without comments regression tests" , c14nExcWithoutCommentTest, "./test/c14n/exc-without-comments/*.xml", NULL, NULL, NULL, 0 }, { "C14N 1.1 without comments regression tests" , c14n11WithoutCommentTest, "./test/c14n/1-1-without-comments/*.xml", NULL, NULL, NULL, 0 }, #endif #if defined(LIBXML_THREAD_ENABLED) && defined(LIBXML_CATALOG_ENABLED) { "Catalog and Threads regression tests" , threadsTest, NULL, NULL, NULL, NULL, 0 }, #endif { "SVG parsing regression tests" , oldParseTest, "./test/SVG/*.xml", "result/SVG/", "", NULL, 0 }, #if defined(LIBXML_REGEXP_ENABLED) { "Regexp regression tests" , regexpTest, "./test/regexp/*", "result/regexp/", "", ".err", 0 }, #endif #if defined(LIBXML_AUTOMATA_ENABLED) { "Automata regression tests" , automataTest, "./test/automata/*", "result/automata/", "", NULL, 0 }, #endif {NULL, NULL, NULL, NULL, NULL, NULL, 0} }; /************************************************************************ * * * The main code driving the tests * * * ************************************************************************/ static int launchTests(testDescPtr tst) { int res = 0, err = 0; size_t i; char *result; char *error; int mem; xmlCharEncodingHandlerPtr ebcdicHandler, eucJpHandler; ebcdicHandler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EBCDIC); eucJpHandler = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_EUC_JP); if (tst == NULL) return(-1); if (tst->in != NULL) { glob_t globbuf; globbuf.gl_offs = 0; glob(tst->in, GLOB_DOOFFS, NULL, &globbuf); for (i = 0;i < globbuf.gl_pathc;i++) { if (!checkTestFile(globbuf.gl_pathv[i])) continue; if (((ebcdicHandler == NULL) && (strstr(globbuf.gl_pathv[i], "ebcdic") != NULL)) || ((eucJpHandler == NULL) && (strstr(globbuf.gl_pathv[i], "icu_parse_test") != NULL))) continue; if (tst->suffix != NULL) { result = resultFilename(globbuf.gl_pathv[i], tst->out, tst->suffix); if (result == NULL) { fprintf(stderr, "Out of memory !\n"); fatalError(); } } else { result = NULL; } if (tst->err != NULL) { error = resultFilename(globbuf.gl_pathv[i], tst->out, tst->err); if (error == NULL) { fprintf(stderr, "Out of memory !\n"); fatalError(); } } else { error = NULL; } mem = xmlMemUsed(); extraMemoryFromResolver = 0; testErrorsSize = 0; testErrors[0] = 0; res = tst->func(globbuf.gl_pathv[i], result, error, tst->options | XML_PARSE_COMPACT); xmlResetLastError(); if (res != 0) { fprintf(stderr, "File %s generated an error\n", globbuf.gl_pathv[i]); nb_errors++; err++; } else if (xmlMemUsed() != mem) { if ((xmlMemUsed() != mem) && (extraMemoryFromResolver == 0)) { fprintf(stderr, "File %s leaked %d bytes\n", globbuf.gl_pathv[i], xmlMemUsed() - mem); nb_leaks++; err++; } } testErrorsSize = 0; if (result) free(result); if (error) free(error); } globfree(&globbuf); } else { testErrorsSize = 0; testErrors[0] = 0; extraMemoryFromResolver = 0; res = tst->func(NULL, NULL, NULL, tst->options); if (res != 0) { nb_errors++; err++; } } xmlCharEncCloseFunc(ebcdicHandler); xmlCharEncCloseFunc(eucJpHandler); return(err); } static int verbose = 0; static int tests_quiet = 0; static int runtest(int i) { int ret = 0, res; int old_errors, old_tests, old_leaks; old_errors = nb_errors; old_tests = nb_tests; old_leaks = nb_leaks; if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL)) printf("## %s\n", testDescriptions[i].desc); res = launchTests(&testDescriptions[i]); if (res != 0) ret++; if (verbose) { if ((nb_errors == old_errors) && (nb_leaks == old_leaks)) printf("Ran %d tests, no errors\n", nb_tests - old_tests); else printf("Ran %d tests, %d errors, %d leaks\n", nb_tests - old_tests, nb_errors - old_errors, nb_leaks - old_leaks); } return(ret); } int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { int i, a, ret = 0; int subset = 0; #if defined(_WIN32) setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && _MSC_VER < 1900 _set_output_format(_TWO_DIGIT_EXPONENT); #endif initializeLibxml2(); for (a = 1; a < argc;a++) { if (!strcmp(argv[a], "-v")) verbose = 1; else if (!strcmp(argv[a], "-u")) update_results = 1; else if (!strcmp(argv[a], "-quiet")) tests_quiet = 1; else if (!strcmp(argv[a], "--out")) temp_directory = argv[++a]; else { for (i = 0; testDescriptions[i].func != NULL; i++) { if (strstr(testDescriptions[i].desc, argv[a])) { ret += runtest(i); subset++; } } } } if (subset == 0) { for (i = 0; testDescriptions[i].func != NULL; i++) { ret += runtest(i); } } if ((nb_errors == 0) && (nb_leaks == 0)) { ret = 0; printf("Total %d tests, no errors\n", nb_tests); } else { ret = 1; printf("Total %d tests, %d errors, %d leaks\n", nb_tests, nb_errors, nb_leaks); } xmlCleanupParser(); return(ret); } #else /* ! LIBXML_OUTPUT_ENABLED */ int main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) { fprintf(stderr, "runtest requires output to be enabled in libxml2\n"); return(0); } #endif