#include <cppunit/Exception.h>
#include <cppunit/Test.h>
#include <cppunit/TestFailure.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/XmlOutputter.h>
#include <map>
#include <stdlib.h>
namespace CppUnit
{
// XmlOutputter::Node
// //////////////////////////////////////////////////////////////////
XmlOutputter::Node::Node( std::string elementName,
std::string content ) :
m_name( elementName ),
m_content( content )
{
}
XmlOutputter::Node::Node( std::string elementName,
int numericContent ) :
m_name( elementName )
{
m_content = asString( numericContent );
}
XmlOutputter::Node::~Node()
{
Nodes::iterator itNode = m_nodes.begin();
while ( itNode != m_nodes.end() )
delete *itNode++;
}
void
XmlOutputter::Node::addAttribute( std::string attributeName,
std::string value )
{
m_attributes.push_back( Attribute( attributeName, value ) );
}
void
XmlOutputter::Node::addAttribute( std::string attributeName,
int numericValue )
{
addAttribute( attributeName, asString( numericValue ) );
}
void
XmlOutputter::Node::addNode( Node *node )
{
m_nodes.push_back( node );
}
std::string
XmlOutputter::Node::toString( const std::string &indent ) const
{
std::string element( indent );
element += "<";
element += m_name;
if ( !m_attributes.empty() )
{
element += " ";
element += attributesAsString();
}
element += ">";
if ( !m_nodes.empty() )
{
element += "\n";
std::string subNodeIndent( indent + " " );
Nodes::const_iterator itNode = m_nodes.begin();
while ( itNode != m_nodes.end() )
{
const Node *node = *itNode++;
element += node->toString( subNodeIndent );
}
element += indent;
}
if ( !m_content.empty() )
{
element += escape( m_content );
if ( !m_nodes.empty() )
{
element += "\n";
element += indent;
}
}
element += "</";
element += m_name;
element += ">\n";
return element;
}
std::string
XmlOutputter::Node::attributesAsString() const
{
std::string attributes;
Attributes::const_iterator itAttribute = m_attributes.begin();
while ( itAttribute != m_attributes.end() )
{
const Attribute &attribute = *itAttribute++;
attributes += attribute.first;
attributes += "=\"";
attributes += escape( attribute.second );
attributes += "\"";
}
return attributes;
}
std::string
XmlOutputter::Node::escape( std::string value ) const
{
std::string escaped;
for ( int index =0; index < value.length(); ++index )
{
char c = value[index ];
switch ( c ) // escape all predefined XML entity (safe?)
{
case '<':
escaped += "<";
break;
case '>':
escaped += ">";
break;
case '&':
escaped += "&";
break;
case '\'':
escaped += "'";
break;
case '"':
escaped += """;
break;
default:
escaped += c;
}
}
return escaped;
}
// should be somewhere else... Future CppUnit::String ?
std::string
XmlOutputter::Node::asString( int value )
{
OStringStream stream;
stream << value;
return stream.str();
}
// XmlOutputter
// //////////////////////////////////////////////////////////////////
XmlOutputter::XmlOutputter( TestResultCollector *result,
std::ostream &stream,
std::string encoding ) :
m_result( result ),
m_stream( stream ),
m_encoding( encoding )
{
}
XmlOutputter::~XmlOutputter()
{
}
void
XmlOutputter::write()
{
writeProlog();
writeTestsResult();
}
void
XmlOutputter::setStyleSheet( const std::string &styleSheet )
{
m_styleSheet = styleSheet;
}
void
XmlOutputter::writeProlog()
{
m_stream << "<?xml version=\"1.0\" "
"encoding='" << m_encoding << "' standalone='yes' ?>"
<< std::endl;
if ( !m_styleSheet.empty() )
{
m_stream << "<?xml-stylesheet type=\"text/xsl\" href="/?originalUrl=https%3A%2F%2Fsourceforge.net%2F%253C%2Fspan">\""
<< m_styleSheet << "\"?>"
<< std::endl;
}
}
void
XmlOutputter::writeTestsResult()
{
Node *rootNode = makeRootNode();
m_stream << rootNode->toString();
delete rootNode;
}
XmlOutputter::Node *
XmlOutputter::makeRootNode()
{
Node *rootNode = new Node( "TestRun" );
FailedTests failedTests;
fillFailedTestsMap( failedTests );
addFailedTests( failedTests, rootNode );
addSuccessfulTests( failedTests, rootNode );
addStatistics( rootNode );
return rootNode;
}
void
XmlOutputter::fillFailedTestsMap( FailedTests &failedTests )
{
const TestResultCollector::TestFailures &failures = m_result->failures();
TestResultCollector::TestFailures::const_iterator itFailure = failures.begin();
while ( itFailure != failures.end() )
{
TestFailure *failure = *itFailure++;
failedTests.insert( std::make_pair(failure->failedTest(), failure ) );
}
}
void
XmlOutputter::addFailedTests( FailedTests &failedTests,
Node *rootNode )
{
Node *testsNode = new Node( "FailedTests" );
rootNode->addNode( testsNode );
const TestResultCollector::Tests &tests = m_result->tests();
for ( int testNumber = 0; testNumber < tests.size(); ++testNumber )
{
Test *test = tests[testNumber];
if ( failedTests.find( test ) != failedTests.end() )
addFailedTest( test, failedTests[test], testNumber+1, testsNode );
}
}
void
XmlOutputter::addSuccessfulTests( FailedTests &failedTests,
Node *rootNode )
{
Node *testsNode = new Node( "SuccessfulTests" );
rootNode->addNode( testsNode );
const TestResultCollector::Tests &tests = m_result->tests();
for ( int testNumber = 0; testNumber < tests.size(); ++testNumber )
{
Test *test = tests[testNumber];
if ( failedTests.find( test ) == failedTests.end() )
addSuccessfulTest( test, testNumber+1, testsNode );
}
}
void
XmlOutputter::addStatistics( Node *rootNode )
{
Node *statisticsNode = new Node( "Statistics" );
rootNode->addNode( statisticsNode );
statisticsNode->addNode( new Node( "Tests", m_result->runTests() ) );
statisticsNode->addNode( new Node( "FailuresTotal",
m_result->testFailuresTotal() ) );
statisticsNode->addNode( new Node( "Errors", m_result->testErrors() ) );
statisticsNode->addNode( new Node( "Failures", m_result->testFailures() ) );
}
void
XmlOutputter::addFailedTest( Test *test,
TestFailure *failure,
int testNumber,
Node *testsNode )
{
Exception *thrownException = failure->thrownException();
Node *testNode = new Node( "FailedTest" );
testsNode->addNode( testNode );
testNode->addAttribute( "id", testNumber );
testNode->addNode( new Node( "Name", test->getName() ) );
testNode->addNode( new Node( "FailureType",
failure->isError() ? "Error" : "Assertion" ) );
if ( failure->sourceLine().isValid() )
addFailureLocation( failure, testNode );
testNode->addNode( new Node( "Message", thrownException->what() ) );
}
void
XmlOutputter::addFailureLocation( TestFailure *failure,
Node *testNode )
{
Node *locationNode = new Node( "Location" );
testNode->addNode( locationNode );
SourceLine sourceLine = failure->sourceLine();
locationNode->addNode( new Node( "File", sourceLine.fileName() ) );
locationNode->addNode( new Node( "Line", sourceLine.lineNumber() ) );
}
void
XmlOutputter::addSuccessfulTest( Test *test,
int testNumber,
Node *testsNode )
{
Node *testNode = new Node( "Test" );
testsNode->addNode( testNode );
testNode->addAttribute( "id", testNumber );
testNode->addNode( new Node( "Name", test->getName() ) );
}
} // namespace CppUnit