blob: 226425dbce5e9ba2effcfb0d76970015ae9a1c83 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Xalan" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation and was
* originally based on software copyright (c) 1999, International
* Business Machines, Inc., http://www.ibm.com. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
#include "FileUtility.hpp"
#include <cstdlib>
#include <cstdio>
#include <ctime>
#include <vector>
#include <climits>
#include <cstring>
#if defined(WIN32)
#include <direct.h>
#define PATH_MAX _MAX_PATH
#define chdir _chdir
#define getcwd _getcwd
#define mkdir _mkdir
#else
#define DIR_MODE_BITS 509
#include <dirent.h>
#include <unistd.h>
extern "C" int mkdir(const char*, mode_t mode);
#endif
#if defined(XALAN_OLD_STREAM_HEADERS)
#include <iostream.h>
#include <strstrea.h>
#else
#include <iostream>
#include <strstream>
#endif
#if !defined(NDEBUG) && defined(_MSC_VER)
#include <crtdbg.h>
#endif
#include <xercesc/sax/SAXException.hpp>
#include <PlatformSupport/DirectoryEnumerator.hpp>
#include <PlatformSupport/DOMStringHelper.hpp>
#include <PlatformSupport/XalanOutputStreamPrintWriter.hpp>
#include <PlatformSupport/XalanFileOutputStream.hpp>
#include <XMLSupport/FormatterToXML.hpp>
#include <XMLSupport/FormatterTreeWalker.hpp>
#include <XalanSourceTree/XalanSourceTreeDOMSupport.hpp>
#include <XalanSourceTree/XalanSourceTreeParserLiaison.hpp>
#include <XalanSourceTree/XalanSourceTreeDocument.hpp>
#include <XSLT/StylesheetRoot.hpp>
#include <XalanTransformer/XalanCompiledStylesheet.hpp>
#include <XalanTransformer/XalanTransformer.hpp>
#include "XMLFileReporter.hpp"
const char* const xalanNodeTypes[] =
{
"UNKNOWN_NODE",
"ELEMENT_NODE",
"ATTRIBUTE_NODE",
"TEXT_NODE",
"CDATA_SECTION_NODE",
"ENTITY_REFERENCE_NODE",
"ENTITY_NODE",
"PROCESSING_INSTRUCTION_NODE",
"COMMENT_NODE",
"DOCUMENT_NODE",
"DOCUMENT_TYPE_NODE",
"DOCUMENT_FRAGMENT_NODE",
"NOTATION_NODE"
};
#if !defined(XALAN_NO_NAMESPACES)
using std::cout;
using std::endl;
#endif
FileUtility::reportStruct::reportStruct() :
theDrive(),
testOrFile(),
xmlFileURL(),
xslFileURL(),
xmlFormat(),
msg(0),
currentNode(),
actual(),
expected(),
pass(0),
fail(0),
nogold(0)
{
}
void
FileUtility::reportStruct::reset()
{
clear(testOrFile);
msg = "";
clear(currentNode);
clear(actual);
clear(expected);
}
FileUtility::cmdParams::cmdParams() :
help(),
data(0),
base(),
output(),
gold(),
sub(),
source(0),
skip(false),
iters(0)
{
}
FileUtility::FileUtility() :
data(),
args()
{
cout << endl << "Using Xerces Version " << gXercesFullVersionStr << endl;
}
#if !defined(WIN32)
XalanDOMString
FileUtility::getDrive()
{
return XalanDOMString();
}
#else
XalanDOMString
FileUtility::getDrive()
{
const char temp[] =
{
char(_getdrive() + 'A' - 1),
':',
'\0'
};
return XalanDOMString(temp, sizeof(temp) - 1);
}
#endif
bool
FileUtility::getParams(int argc,
const char* argv[],
char* outDir,
bool fsetGold)
{
bool fSuccess = true; // Used to continue argument loop
bool fsetOut = true; // Set default output directory, set to false if data is provided
args.skip = true; // Default values for performance testing parameters.
args.iters = 3;
// Insure that required "-base" argument is there.
//
if (argc == 1 || argv[1][0] == '-')
{
cout << args.getHelpMessage();
return false;
}
else
{
if (checkDir(XalanDOMString(argv[1])))
{
assign(args.base, XalanDOMString(argv[1]));
}
else
{
cout << endl << "Given base directory \"" << argv[1] << "\" does not exist" << endl;
cout << args.getHelpMessage();
return false;
}
}
// Get the rest of the arguments.
//
for (int i = 2; i < argc && fSuccess == true; ++i)
{
if(!stricmp("-out", argv[i]))
{
++i;
if(i < argc && argv[i][0] != '-')
{
assign(args.output, XalanDOMString(argv[i]));
append(args.output, s_pathSep);
checkAndCreateDir(args.output);
fsetOut = false;
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else if(!stricmp("-gold", argv[i]))
{
++i;
if(i < argc && argv[i][0] != '-')
{
assign(args.gold, XalanDOMString(argv[i]));
if ( !checkDir(args.gold) )
{
cout << "Given Gold dir - " << c_str(TranscodeToLocalCodePage(args.gold)) << " - does not exist" << endl;
fSuccess = false;
}
append(args.gold, s_pathSep);
fsetGold = false;
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else if(!stricmp("-source", argv[i]))
{
++i;
if(i < argc && argv[i][0] != '-')
{
if (stricmp(argv[i],"XPL") == 0)
{
args.source = 1;
outDir = "DOM-XALAN";
}
else if (stricmp(argv[i], "DOM") == 0)
{
args.source = 2;
outDir = "DOM-XERCES";
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else if(!stricmp("-sub", argv[i]))
{
++i;
if(i < argc && argv[i][0] != '-')
{
assign(args.sub, XalanDOMString(argv[i]));
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else if(!stricmp("-i", argv[i]))
{
args.skip = false;
}
else if(!stricmp("-iter", argv[i]))
{
++i;
// Make sure number is there and is greater then zero
if(i < argc && atol(argv[i]) > 0)
{
args.iters = atol(argv[i]);
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
}
else
{
cout << args.getHelpMessage();
fSuccess = false;
}
} // End of for-loop
// Do we need to set the default output directory??
//
if (fsetOut)
{
unsigned int ii = lastIndexOf(args.base,charAt(s_pathSep,0));
if (ii < length(args.base))
{
args.output.assign(args.base, 0, ii + 1);
}
append(args.output,XalanDOMString(outDir));
checkAndCreateDir(args.output);
append(args.output,s_pathSep);
}
// Do we need to set the default gold directory??
//
if (fsetGold)
{
args.gold = args.base;
append(args.gold,XalanDOMString("-gold"));
if ( !checkDir(args.gold) )
{
cout << "Assumed Gold dir - " << c_str(TranscodeToLocalCodePage(args.gold)) << " - does not exist" << endl;
fSuccess = false;
}
append(args.gold,s_pathSep);
}
// Add the path seperator to the end of the base directory
// here after we've finished using it for all directory creation.
//
append(args.base,s_pathSep);
return fSuccess;
}
// This routine retrieves test file names from specified directories.
// Inputs: baseDir: typically "conf" or "perf"
// relDir: sub-directory to search.
//
// Notes: It builds the searchSpecification by concatenating all the
// necessary components.
//
FileNameVectorType
FileUtility::getTestFileNames(
const XalanDOMString& baseDir,
const XalanDOMString& relDir,
bool useDirPrefix)
{
const XalanDOMString searchSuffix(XALAN_STATIC_UCODE_STRING("*.xsl"));
XalanDOMString searchSpecification;
// Allow directory search w/o mandating files start with directory name. Required for files
// garnered from XSLTMARK performance directory exm.
if (useDirPrefix)
{
assign(searchSpecification, baseDir + relDir + s_pathSep + relDir + searchSuffix);
}
else
{
assign(searchSpecification, baseDir + relDir + s_pathSep + searchSuffix);
}
DirectoryEnumeratorFunctor<FileNameVectorType, XalanDOMString> theEnumerator;
FileNameVectorType theFiles;
theEnumerator(searchSpecification, theFiles);
return theFiles;
}
/* This routine retrieves all sub-directories from the specified directories.
// Inputs: rootDirectory: typically "conf" or "perf"
//
// Notes: The searchSpecification in this case is just "*".
// */
FileNameVectorType FileUtility::getDirectoryNames(const XalanDOMString& rootDirectory)
{
const XalanDOMString dirSpec(XALAN_STATIC_UCODE_STRING("*"));
DirectoryEnumeratorFunctor<FileNameVectorType, XalanDOMString, DirectoryFilterPredicate> theEnumerator;
FileNameVectorType theFiles;
theEnumerator(XalanDOMString(rootDirectory), XalanDOMString(dirSpec), theFiles);
return theFiles;
}
bool FileUtility::checkDir(const XalanDOMString& directory )
{
char buffer[PATH_MAX];
getcwd(buffer, PATH_MAX);
bool fResult = false;
if ( !chdir(c_str(TranscodeToLocalCodePage(directory))) )
{
fResult = true;
}
chdir(buffer);
return fResult;
}
void FileUtility::checkAndCreateDir(const XalanDOMString& directory)
{
char buffer[PATH_MAX];
getcwd(buffer, PATH_MAX);
if ( (chdir(c_str(TranscodeToLocalCodePage(directory)))) )
{
//cout << "Couldn't change to " << directory << ", will create it." << endl;
#if defined(WIN32)
if ( !mkdir(c_str(TranscodeToLocalCodePage(directory))))
#else
if ( !mkdir(c_str(TranscodeToLocalCodePage(directory)), DIR_MODE_BITS))
#endif
{
cout << directory << " created." << endl;
}
else
{
cout << directory << " NOT created." << endl;
}
}
chdir(buffer);
}
/* This routine generates file names based on the provide suffix
// Inputs: theXMLFileName: typically "conf" or "perf"
// suffix: typically "xsl" or "out".
//
// Notes:
*/
XalanDOMString
FileUtility::generateFileName(
const XalanDOMString& theXMLFileName,
const char* suffix,
bool* status)
{
XalanDOMString targetFile;
int thePeriodIndex = -1;
const int theLength = length(theXMLFileName);
for (int i = theLength - 1; i > 0; i--)
{
if (charAt(theXMLFileName, i) == XalanUnicode::charFullStop)
{
thePeriodIndex = i; // charFullStop is the dot (x2E)
break;
}
}
if (thePeriodIndex != -1)
{
targetFile.assign(theXMLFileName, 0, thePeriodIndex + 1);
targetFile += XalanDOMString(suffix);
}
// Check the .xml file exists.
if (!strcmp(suffix,"xml"))
{
FILE* fileHandle = fopen(c_str(TranscodeToLocalCodePage(targetFile)), "r");
if (fileHandle == 0)
{
cout << "TEST ERROR: File Missing: " << targetFile << endl;
if (status != 0)
{
*status = false;
}
}
else
{
fclose(fileHandle);
}
}
return targetFile;
}
/* This routine generates a Unique Runid.
// Inputs: None
//
// Notes: The format is mmddhhmm. For example
// 03151046 is "Mar 15 10:46"
*/
XalanDOMString
FileUtility::generateUniqRunid()
{
struct tm *newtime;
time_t long_time;
char tmpbuf[10];
time( &long_time ); /* Get time as long integer. */
newtime = localtime( &long_time ); /* Convert to local time. */
strftime( tmpbuf, 10,"%m%d%H%M",newtime );
return XalanDOMString(tmpbuf);
}
// This routine gets Xerces Version number. It's used to put the Xerces Version
// into the output xml results file as an attribute of 'PerfData' element.
// Inputs: None
//
XalanDOMString
FileUtility::getXercesVersion()
{
return XalanDOMString(gXercesFullVersionStr);
}
/* This routine creates a FormatterToXML FormatterListener. This is used to format
// the output DOM so a comparision can be done with the expected GOLD file.
// Inputs: None
//
*/
FormatterListener*
FileUtility::getXMLFormatter(
bool shouldWriteXMLHeader,
bool stripCData,
bool escapeCData,
PrintWriter& resultWriter,
int indentAmount,
const XalanDOMString& mimeEncoding,
const StylesheetRoot* stylesheet)
{
XalanDOMString version;
bool outputIndent= 0;
XalanDOMString mediatype;
XalanDOMString doctypeSystem;
XalanDOMString doctypePublic;
XalanDOMString standalone;
if (stylesheet != 0)
{
version = stylesheet->m_version;
mediatype = stylesheet->m_mediatype;
doctypeSystem = stylesheet->getOutputDoctypeSystem();
doctypePublic = stylesheet->getOutputDoctypePublic();
standalone = stylesheet->m_standalone;
outputIndent = stylesheet->m_indentResult;
}
return new FormatterToXML(
resultWriter,
version,
outputIndent,
indentAmount,
mimeEncoding,
mediatype,
doctypeSystem,
doctypePublic,
true, // xmlDecl
standalone);
}
/* This routine is used to compares the results of a transform and report the results.
// When a failure is detected the 'data' structure used to report detailed info about
// a failure is filled in.
// Inputs:
// goldFile - Name of gold file
// outputFile - Name of result file.
// logfile - Name of log file reporter.
//
// Returns:
// Void
*/
void
FileUtility::checkResults(
const XalanDOMString& outputFile,
const XalanDOMString& goldFile,
XMLFileReporter& logfile)
{
int ambgFlag = data.nogold; // get the current number of tests w/o gold files.
// Compare the results, report success if compareSerializedResults returns true.
if(compareSerializedResults(outputFile, goldFile))
{
cout << "Passed: " << data.testOrFile << endl;
logfile.logCheckPass(data.testOrFile);
data.pass += 1;
}
else
{ // if the compairson fails gather up the failure data and determine if it failed
// due to bad output or missing Gold file. Lastly, log the failure.
Hashtable attrs;
Hashtable actexp;
reportError();
attrs.insert(Hashtable::value_type(XalanDOMString("reason"), XalanDOMString(data.msg)));
attrs.insert(Hashtable::value_type(XalanDOMString("atNode"), data.currentNode));
actexp.insert(Hashtable::value_type(XalanDOMString("exp"), data.expected));
actexp.insert(Hashtable::value_type(XalanDOMString("act"), data.actual));
actexp.insert(Hashtable::value_type(XalanDOMString("xsl"), data.xslFileURL));
actexp.insert(Hashtable::value_type(XalanDOMString("xml"), data.xmlFileURL));
actexp.insert(Hashtable::value_type(XalanDOMString("result"), outputFile));
actexp.insert(Hashtable::value_type(XalanDOMString("gold"), goldFile));
if (ambgFlag < data.nogold)
{
logfile.logCheckAmbiguous(data.testOrFile);
}
else
{
logfile.logCheckFail(data.testOrFile, attrs, actexp);
}
}
}
void
FileUtility::checkAPIResults(
const XalanDOMString& actual,
const XalanDOMString& expected,
const char* msg,
XMLFileReporter& logfile,
const XalanDOMString& outputFile,
const XalanDOMString& goldFile)
{
if(actual == expected)
{
data.pass += 1;
cout << "Passed: " << data.testOrFile << endl;
logfile.logCheckPass(data.testOrFile);
}
else
{ data.actual = actual;
data.expected = expected;
data.currentNode = "API Test";
data.msg = msg;
data.fail += 1;
reportError();
Hashtable actexp;
actexp.insert(Hashtable::value_type(XalanDOMString("exp"), expected));
actexp.insert(Hashtable::value_type(XalanDOMString("act"), actual));
actexp.insert(Hashtable::value_type(XalanDOMString("xsl"), data.xslFileURL));
actexp.insert(Hashtable::value_type(XalanDOMString("xml"), data.xmlFileURL));
actexp.insert(Hashtable::value_type(XalanDOMString("result"), outputFile));
actexp.insert(Hashtable::value_type(XalanDOMString("gold"), goldFile));
// Todo: Need to determine if I should check for missing gold in these cases.
logfile.logCheckFail(data.testOrFile, actexp);
}
}
/* This routine compares the results of a transform with the gold file.
// It in turn call the domCompare routine to do the actual comparision.
// Inputs:
// gold - Dom tree for the expected results
// doc - Dom tree created during transformation
// filename - Current filename
//
// Returns:
// Void
//
*/
void
FileUtility::checkDOMResults(
const XalanDOMString& theOutputFile,
const XalanCompiledStylesheet* compiledSS,
const XalanSourceTreeDocument* dom,
const XSLTInputSource& goldInputSource,
XMLFileReporter& logfile)
{
const int ambgFlag = data.nogold;
const XalanDOMString mimeEncoding("");
XalanFileOutputStream myOutput(theOutputFile);
XalanOutputStreamPrintWriter myResultWriter(myOutput);
FormatterListener* const theFormatter =
getXMLFormatter(
true,
true,
true,
myResultWriter,
0,
mimeEncoding,
compiledSS->getStylesheetRoot());
FormatterTreeWalker theTreeWalker(*theFormatter);
theTreeWalker.traverse(dom);
delete theFormatter;
XalanSourceTreeDOMSupport domSupport;
XalanSourceTreeParserLiaison parserLiaison(domSupport);
domSupport.setParserLiaison(&parserLiaison);
const XalanDocument* const goldDom =
parserLiaison.parseXMLStream(goldInputSource);
if(domCompare(*goldDom, *dom))
{
cout << "Passed: " << data.testOrFile << endl;
logfile.logCheckPass(data.testOrFile);
data.pass += 1;
}
else
{ // if the compairson fails gather up the failure data and determine if it failed
// due to bad output or missing Gold file. Lastly, log the failure.
Hashtable attrs;
Hashtable actexp;
reportError();
attrs.insert(Hashtable::value_type(XalanDOMString("reason"), XalanDOMString(data.msg)));
attrs.insert(Hashtable::value_type(XalanDOMString("atNode"), data.currentNode));
actexp.insert(Hashtable::value_type(XalanDOMString("exp"), data.expected));
actexp.insert(Hashtable::value_type(XalanDOMString("act"), data.actual));
if (ambgFlag < data.nogold)
{
logfile.logCheckAmbiguous(data.testOrFile);
}
else
{
logfile.logCheckFail(data.testOrFile, attrs, actexp);
}
}
}
/* This routine takes the result file and gold file and parses them.
// If either of the files fails to parse and a SAXException is throw,
// then the files are compared using a char by char file compare,
// otherwise the domCompare routine is used.
// Inputs:
// outputFile: Name of result file
// goldFile: Name of gold file
//
// Returns:
// True or False
//
*/
bool
FileUtility::compareSerializedResults(
const XalanDOMString& outputFile,
const XalanDOMString& goldFile)
{
const XSLTInputSource resultInputSource(c_wstr(outputFile));
const XSLTInputSource goldInputSource(c_wstr(goldFile));
XalanSourceTreeDOMSupport domSupport;
XalanSourceTreeParserLiaison parserLiaison(domSupport);
domSupport.setParserLiaison(&parserLiaison);
try
{
const XalanDocument* const transformDom =
parserLiaison.parseXMLStream(resultInputSource);
assert(transformDom != 0);
const XalanDocument* const goldDom =
parserLiaison.parseXMLStream(goldInputSource);
assert(goldDom != 0);
return domCompare(*goldDom, *transformDom);
}
// This exception is being reported prior to this Catch, however, however, I clarify that it's a SAX exception.
// It's a good indication that the Gold file is not a valid XML. When this happens the transform result needs
// to be compared with the Gold, with a character by character basis, not via the DOM compair.
catch (const SAXException&)
{
cout << "SAXException: Using fileCompare to check output.\n";
return fileCompare(c_str(TranscodeToLocalCodePage(goldFile)), c_str(TranscodeToLocalCodePage(outputFile)));
}
}
/* This routine is used to compare the results against the gold when one or both of
// fails to parse without throwing a SAXException. When a failure is detected the 'data'
// structure used to report detailed info about a failure is filled in.
// Inputs:
// outputFile: Name of result file
// goldFile: Name of gold file
//
// Returns:
// True or False
//
*/
bool
FileUtility::fileCompare(
const char* goldFile,
const char* outputFile)
{
char rline[132] = {'0'}; // declare buffers to hold single line from file
char gline[132] = {'0'};
char temp[10]; // buffer to hold line number
char lineNum = 1;
// Set fail data incase there are i/o problems with the files to compare.
data.expected = XalanDOMString(" ");
data.actual = XalanDOMString(" ");
data.currentNode = XalanDOMString("Line: 0");
// Attempt to open the files.
FILE* const result = fopen(outputFile, "r");
FILE* const gold = fopen(goldFile, "r");
// If the result file fails to open report this as a failure.
if (!result)
{
data.msg = "No Result (Transform failed)";
data.fail += 1;
return false;
}
// If the gold file fails to open report this as ambiguous.
if (!gold)
{
data.msg = "No Gold file";
data.nogold += 1;
return false;
}
// Start file comparison, line by line..
while(!feof(result) && !feof(gold))
{
fgets(gline, sizeof(gline), gold );
fgets(rline, sizeof(rline), result );
sprintf(temp,"%d",lineNum);
if (ferror(gold) || ferror(result))
{
data.msg = "Read Error - Gold/Result file";
data.currentNode = XalanDOMString("Line: ") + XalanDOMString(temp);
return false;
}
// Compare the lines character by charcter ....
unsigned int i = 0;
while(i < strlen(gline))
{
if (gline[i] == rline[i])
{
i++;
continue;
}
else
{ // If there is a mismatch collect up the fail data and return false. To ensure that
// the results can be seen in the browser enclose the actual/expected in CDATA Sections.
data.msg = "Text based comparison failure";
data.expected = XalanDOMString("<![CDATA[") + XalanDOMString(gline) + XalanDOMString("]]>");
data.actual = XalanDOMString("<![CDATA[") + XalanDOMString(rline) + XalanDOMString("]]>");
data.currentNode = XalanDOMString("Line: ") + XalanDOMString(temp);
data.fail += 1;
return false;
}
}
lineNum += 1;
}
return true;
}
/* This routine performs a DOM Comparision.
// Inputs:
// gold - Dom tree for the expected results
// doc - Dom tree created during transformation
// filename - Current filename
//
// Returns:
// True or False
//
*/
bool
FileUtility::domCompare(
const XalanNode& gold,
const XalanNode& doc)
{
const XalanNode::NodeType docNodeType = doc.getNodeType();
const XalanNode::NodeType goldNodeType = gold.getNodeType();
const XalanDOMString& docNodeName = doc.getNodeName();
const XalanDOMString& goldNodeName = gold.getNodeName();
if (goldNodeType != docNodeType)
{
collectData("NodeType mismatch.",
docNodeName,
XalanDOMString(xalanNodeTypes[docNodeType]),
XalanDOMString(xalanNodeTypes[goldNodeType]));
return false;
}
switch (goldNodeType)
{
case XalanNode::ELEMENT_NODE: // ATTRIBUTE_NODE's are processed with diffElement().
{
if ( ! diffElement(gold, doc) )
{
return false;
}
break;
}
case XalanNode::TEXT_NODE:
{
const XalanDOMString& docNodeValue = doc.getNodeValue();
const XalanDOMString& goldNodeValue = gold.getNodeValue();
//debugNodeData(docNodeName, docNodeValue);
if(goldNodeValue != docNodeValue)
{
collectData("Text node mismatch. ",
docNodeName,
goldNodeValue,
docNodeValue);
return false;
}
// Need to process textnode siblings. Note that text nodes do not have child nodes.
const XalanNode *goldNextNode;
const XalanNode *domNextNode;
goldNextNode = gold.getNextSibling();
domNextNode = doc.getNextSibling();
if (0 != goldNextNode)
{
if (0 != domNextNode)
{
if ( ! domCompare(*goldNextNode, *domNextNode) )
return false;
}
else
{
collectData("Element missing SiblingNode. ",
docNodeName,
goldNextNode->getNodeName(),
goldNextNode->getNodeName());
return false;
}
}
break;
}
case XalanNode::CDATA_SECTION_NODE:
case XalanNode::ENTITY_REFERENCE_NODE:
case XalanNode::ENTITY_NODE:
case XalanNode::PROCESSING_INSTRUCTION_NODE:
case XalanNode::COMMENT_NODE:
{
break;
}
case XalanNode::DOCUMENT_NODE:
{
//debugNodeData(docNodeName);
const XalanNode *goldNextNode;
const XalanNode *domNextNode;
goldNextNode = gold.getFirstChild();
domNextNode = doc.getFirstChild();
if (0 != goldNextNode)
{
if( ! domCompare(*goldNextNode,*domNextNode) )
return false;
}
break;
}
case XalanNode::DOCUMENT_TYPE_NODE:
case XalanNode::DOCUMENT_FRAGMENT_NODE:
case XalanNode::NOTATION_NODE:
{
break;
}
default:
cout << "What are you doing? " << endl;
}
return true;
}
/* This routine compares two element nodes.
// Inputs:
// gold - Dom tree for the expected results
// doc - Dom tree created during transformation
// filename - Current filenam
//
// Returns:
// True or False
//
*/
bool
FileUtility::diffElement(
const XalanNode& gold,
const XalanNode& doc)
{
const XalanDOMString& docNodeName = doc.getNodeName();
const XalanDOMString& goldNodeName = gold.getNodeName();
const XalanDOMString& docNsUri = doc.getNamespaceURI();
const XalanDOMString& goldNsUri = gold.getNamespaceURI();
//debugNodeData(docNodeName);
// This essentially checks 2 things, that the prefix and localname are the
// same. So specific checks of these items are not necessary.
if (goldNodeName != docNodeName)
{
collectData("Element mismatch. ",
docNodeName,
goldNodeName,
docNodeName);
return false;
}
if ( goldNsUri != docNsUri)
{
collectData("Element NamespaceURI mismatch. ",
docNodeName,
goldNsUri,
docNsUri);
return false;
}
// Get Attributes for each Element Node.
const XalanNamedNodeMap* const goldAttrs = gold.getAttributes();
const XalanNamedNodeMap* const docAttrs = doc.getAttributes();
// Get number of Attributes
const unsigned int numGoldAttr = goldAttrs->getLength();
const unsigned int numDomAttr = docAttrs ->getLength();
/*
// This needs to be uncommented if 'compare.exe' is to work.
// If this is the 'root' element strip off the xmlns:xml namespace attribute,
// that is lurking around on the gold file, but not the dom. This is necessary
// only for the 'compare' test, that uses a pure DOM, that has not been serialized.
//if (goldNodeName == XalanDOMString("root"))
{
numGoldAttr -= 1;
XalanNode *gXMLAttr = goldAttrs->item(1);
}
*/
// Check that each Element has same number of Attributes. If they don't report error
if ( numGoldAttr == numDomAttr )
{
// Compare Attributes one at a time.
//for (int i=1; i < numGoldAttr; i++) // To be used with 'compare'
for (unsigned int i = 0; i < numGoldAttr; ++i)
{
// Attribute order is irrelvant, so comparision is base on Attribute name.
const XalanNode* const gAttr = goldAttrs->item(i);
const XalanDOMString& goldAttrName = gAttr->getNodeName();
const XalanNode* const dAttr = docAttrs->getNamedItem(goldAttrName);
if (dAttr != 0)
{
if( ! (diffAttr(gAttr, dAttr)) )
return false;
}
else
{
collectData("Element missing named Attribute. ",
docNodeName,
goldAttrName,
XalanDOMString("NOTHING"));
return false;
}
}
}
else
{
char buf1[2], buf2[2];
sprintf(buf1, "%u", numGoldAttr);
sprintf(buf2, "%u", numDomAttr);
collectData("Wrong number of attributes. ",
docNodeName,
XalanDOMString(buf1),
XalanDOMString(buf2));
return false;
}
const XalanNode* goldNextNode = gold.getFirstChild();
const XalanNode* domNextNode = doc.getFirstChild();
if (0 != goldNextNode)
{
if (0 != domNextNode)
{
if ( ! domCompare(*goldNextNode, *domNextNode) )
return false;
}
else
{
collectData("Element missing ChildNode. ",
docNodeName,
XalanDOMString(goldNextNode->getNodeName()),
XalanDOMString("NOTHING"));
return false;
}
}
else if (domNextNode != 0)
{
// The result doc has additional Children. If the additional node is a text node
// then gather up the text and print it out.
if ( domNextNode->getNodeType() == XalanNode::TEXT_NODE)
{
collectData("Result has additional Child node: ",
docNodeName,
XalanDOMString("NOTHING"),
XalanDOMString(domNextNode->getNodeName()) + XalanDOMString(" \"") +
XalanDOMString(domNextNode->getNodeValue()) + XalanDOMString("\""));
}
// Additional node is NOT text, so just print it's Name.
else
{
collectData("Result has additional Child node: ",
docNodeName,
XalanDOMString("NOTHING"),
XalanDOMString(domNextNode->getNodeName()));
}
return false;
}
goldNextNode = gold.getNextSibling();
domNextNode = doc.getNextSibling();
if (0 != goldNextNode)
{
if (0 != domNextNode)
{
if ( ! domCompare(*goldNextNode, *domNextNode) )
return false;
}
else
{ // domcomtest10 used to fail here, now it is caught above, with the error
// "Transformed Doc has additional Child nodes:"
collectData("Element missing SiblingNode. ",
docNodeName,
XalanDOMString(goldNextNode->getNodeName()),
XalanDOMString("NOTHING"));
return false;
}
}
else if ( domNextNode)
{
// The result doc has additional siblings. If the additional node is a text node
// then gather up the text and print it out.
if ( domNextNode->getNodeType() == XalanNode::TEXT_NODE)
{
collectData("Result has additional sibling node: ",
docNodeName,
XalanDOMString("NOTHING"),
XalanDOMString(domNextNode->getNodeName()) + XalanDOMString(" \"") +
XalanDOMString(domNextNode->getNodeValue()) + XalanDOMString("\""));
}
// Additional node is NOT text, so just print it's Name.
else
{
collectData("Result has additional sibling node: ",
docNodeName,
XalanDOMString("NOTHING"),
XalanDOMString(domNextNode->getNodeName()));
}
return false;
}
return true;
}
/* This routine compares two attribute nodes.
// Inputs:
// gAttr - attribute from Gold dom tree
// dAttr - attribute from Dom tree created during transformation
// fileName - Current filenam
//
// Returns:
// True or False
//
*/
bool FileUtility::diffAttr(const XalanNode* gAttr, const XalanNode* dAttr)
{
const XalanDOMString& docAttrName = dAttr->getNodeName();
const XalanDOMString& goldAttrName = gAttr->getNodeName();
//debugAttributeData(goldAttrName);
const XalanDOMString& goldAttrValue = gAttr->getNodeValue();
const XalanDOMString& docAttrValue = dAttr->getNodeValue();
if (goldAttrValue != docAttrValue)
{
collectData(
"Attribute Value mismatch. ",
docAttrName,
goldAttrValue,
docAttrValue);
return false;
}
const XalanDOMString& goldAttrNsUri = gAttr->getNamespaceURI();
const XalanDOMString& docAttrNsUri = dAttr->getNamespaceURI();
if (goldAttrNsUri != docAttrNsUri)
{
collectData(
"Attribute NamespaceURI mismatch. ",
docAttrName,
goldAttrNsUri,
docAttrNsUri);
return false;
}
return true;
}
/* This routine reports DOM comparison errors.
// Inputs:
// file - Name of current file
// node - Current node that fails
// msg - Failure message
//
*/
void
FileUtility::reportError()
{
cout << endl
<< "* Failed "
<< data.testOrFile
<< " Error: "
<< data.msg
<< endl
<< " "
<< "Processing Node: "
<< data.currentNode
<< endl
<< " Expected: "
<< data.expected
<< endl
<< " Actual: "
<< data.actual
<< endl
<< endl;
}
#if !defined(NDEBUG)
void
FileUtility::debugNodeData(const XalanDOMString& value) const
{
cout << "Node is: " << c_str(TranscodeToLocalCodePage(value)) << endl;
}
void
FileUtility::debugNodeData(
const XalanDOMString& node,
const XalanDOMString& value) const
{
cout << "Node is: " << c_str(TranscodeToLocalCodePage(node)) << " "
<< "Value is: \"" << c_str(TranscodeToLocalCodePage(value)) << "\"\n";
}
void
FileUtility::debugAttributeData(const XalanDOMString& value) const
{
cout << "Attribute is: " << c_str(TranscodeToLocalCodePage(value)) << endl;
}
#endif
/* This routine collects up data pertinent to a dom comparison failure.
// Inputs:
// errmsg: Reason for the failure.
// currentnode: Node in the dom tree where the mismatch occured
// expdata: Expected data based on the Gold file.
// actdata: Actual data returned in the result file.
// Returns: Void
*/
void
FileUtility::collectData(
const char* errmsg,
const XalanDOMString& currentnode,
const XalanDOMString& expdata,
const XalanDOMString& actdata)
{
data.msg = errmsg;
data.currentNode = currentnode;
data.expected = expdata;
data.actual = actdata;
data.fail += 1;
}
/* Routine prints the result to the console, as well as adds summary info into the logfile.
// Inputs:
// logfile: Current log file
// runid: Unique runid
// Returns: Void
*/
void
FileUtility::reportPassFail(
XMLFileReporter& logfile,
const XalanDOMString& runid)
{
Hashtable runResults;
char temp[5];
// Create entrys that contain runid, xerces version, and numbers for Pass, Fail and No Gold.
runResults.insert(Hashtable::value_type(XalanDOMString("UniqRunid"), runid));
runResults.insert(Hashtable::value_type(XalanDOMString("Xerces-Version "), getXercesVersion()));
runResults.insert(Hashtable::value_type(XalanDOMString("BaseDrive "), XalanDOMString(getDrive())));
runResults.insert(Hashtable::value_type(XalanDOMString("TestBase "), XalanDOMString(args.base)));
runResults.insert(Hashtable::value_type(XalanDOMString("xmlFormat "), data.xmlFormat));
sprintf(temp, "%ld", args.iters);
runResults.insert(Hashtable::value_type(XalanDOMString("Iters "), XalanDOMString(temp)));
sprintf(temp, "%d", data.pass);
runResults.insert(Hashtable::value_type(XalanDOMString("Passed"), XalanDOMString(temp)));
sprintf(temp, "%d", data.fail);
runResults.insert(Hashtable::value_type(XalanDOMString("Failed"), XalanDOMString(temp)));
sprintf(temp, "%d", data.nogold);
runResults.insert(Hashtable::value_type(XalanDOMString("No_Gold_Files"), XalanDOMString(temp)));
logfile.logElementWAttrs(10, "RunResults", runResults, "xxx");
cout << "\nPassed " << data.pass;
cout << "\nFailed " << data.fail;
cout << "\nMissing Gold " << data.nogold << endl;
}
/* Routine runs a stylesheet on the log file and displays the results in HTML.
// Inputs:
// xalan: An instance of the transformer
// resultsFile: logfile
// Returns: Void
*/
void
FileUtility::analyzeResults(XalanTransformer& xalan, const XalanDOMString& resultsFile)
{
XalanDOMString paramValue;
bool fileStatus;
// Pass the results .xml file as a parameter to the stylesheet. It must be wrapped in single
// quotes so that it is not considered an expression.
//
assign(paramValue, XalanDOMString("'"));
append(paramValue, resultsFile);
append(paramValue, XalanDOMString("'"));
// Set the parameter
//
xalan.setStylesheetParam(XalanDOMString("testfile"), paramValue);
// Generate the input and output file names.
//
const XalanDOMString theHTMLFile = generateFileName(resultsFile,"html", &fileStatus);
const XalanDOMString theStylesheet = args.base + XalanDOMString("cconf.xsl");
const XalanDOMString theXMLSource = args.base + XalanDOMString("cconf.xml");
// Check that we can find the stylesheet to analyze the results.
//
FILE* fileHandle = fopen(c_str(TranscodeToLocalCodePage(theStylesheet)), "r");
if (fileHandle == 0)
{
cout << "ANALYSIS ERROR: File Missing: " << c_str(TranscodeToLocalCodePage(theStylesheet)) << endl;
return;
}
else
{
fclose(fileHandle);
}
// Create the InputSources and ResultTarget.
const XSLTInputSource xslInputSource(c_wstr(theStylesheet));
const XSLTInputSource xmlInputSource(c_wstr(theXMLSource));
const XSLTResultTarget resultFile(theHTMLFile);
// Do the transform, display the output HTML, or report any failure.
const int result = xalan.transform(xmlInputSource, xslInputSource, resultFile);
if (result == 0)
{
system(c_str(TranscodeToLocalCodePage(theHTMLFile)));
}
else
{
cout << "Analysis failed due to following error: "
<< xalan.getLastError()
<< endl;
}
}
static XalanDOMString s_xmlSuffix;
static XalanDOMString s_pathSep;
const XalanDOMString& FileUtility::s_xmlSuffix = ::s_xmlSuffix;
const XalanDOMString& FileUtility::s_pathSep = ::s_pathSep;
void
FileUtility::initialize()
{
::s_xmlSuffix = XALAN_STATIC_UCODE_STRING(".xml");
#if defined(WIN32)
::s_pathSep = XALAN_STATIC_UCODE_STRING("\\");
#else
::s_pathSep = XALAN_STATIC_UCODE_STRING("/");
#endif
}
void
FileUtility::terminate()
{
releaseMemory(::s_pathSep);
releaseMemory(::s_xmlSuffix);
}