catch2/include/reporters/catch_reporter_junit.hpp

259 lines
10 KiB
C++
Raw Normal View History

2010-12-10 09:01:42 +01:00
/*
* Created by Phil on 26/11/2010.
* Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
*
* Distributed under the Boost Software License, Version 1.0. (See accompanying
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
#ifndef TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
#include "../internal/catch_capture.hpp"
#include "../internal/catch_interfaces_reporter.h"
#include "../internal/catch_reporter_registrars.hpp"
#include "../internal/catch_xmlwriter.hpp"
2010-12-10 09:01:42 +01:00
#include <assert.h>
2012-05-16 15:53:59 +02:00
namespace Catch {
class JunitReporter : public SharedImpl<IReporter> {
2012-05-16 15:53:59 +02:00
struct TestStats {
2011-01-31 11:10:20 +01:00
std::string m_element;
std::string m_resultType;
std::string m_message;
std::string m_content;
2010-12-10 09:01:42 +01:00
};
2012-05-16 15:53:59 +02:00
struct TestCaseStats {
TestCaseStats( const std::string& className, const std::string& name )
: m_className( className ),
m_name( name )
{}
2011-01-31 11:10:20 +01:00
double m_timeInSeconds;
std::string m_status;
std::string m_className;
std::string m_name;
std::string m_stdOut;
std::string m_stdErr;
2011-01-31 11:10:20 +01:00
std::vector<TestStats> m_testStats;
2012-11-22 20:17:20 +01:00
std::vector<TestCaseStats> m_sections;
2010-12-10 09:01:42 +01:00
};
2012-05-16 15:53:59 +02:00
struct Stats {
2010-12-10 09:01:42 +01:00
Stats( const std::string& name = std::string() )
2011-01-31 11:10:20 +01:00
: m_testsCount( 0 ),
m_failuresCount( 0 ),
m_disabledCount( 0 ),
m_errorsCount( 0 ),
m_timeInSeconds( 0 ),
m_name( name )
2012-05-16 15:53:59 +02:00
{}
2011-01-31 11:10:20 +01:00
std::size_t m_testsCount;
std::size_t m_failuresCount;
std::size_t m_disabledCount;
std::size_t m_errorsCount;
double m_timeInSeconds;
std::string m_name;
2011-01-31 11:10:20 +01:00
std::vector<TestCaseStats> m_testCaseStats;
2010-12-10 09:01:42 +01:00
};
2010-12-10 09:01:42 +01:00
public:
JunitReporter( ReporterConfig const& config )
2010-12-10 09:01:42 +01:00
: m_config( config ),
m_testSuiteStats( "AllTests" ),
m_currentStats( &m_testSuiteStats )
{}
virtual ~JunitReporter();
2012-05-16 15:53:59 +02:00
static std::string getDescription() {
2010-12-10 09:01:42 +01:00
return "Reports test results in an XML format that looks like Ant's junitreport target";
}
private: // IReporter
2012-05-16 15:53:59 +02:00
virtual bool shouldRedirectStdout() const {
return true;
}
virtual void StartTesting() {}
virtual void StartGroup( const std::string& groupName ) {
if( groupName.empty() )
m_statsForSuites.push_back( Stats( m_config.fullConfig()->name() ) );
else
m_statsForSuites.push_back( Stats( groupName ) );
m_currentStats = &m_statsForSuites.back();
2010-12-10 09:01:42 +01:00
}
2012-05-16 15:53:59 +02:00
virtual void EndGroup( const std::string&, const Totals& totals ) {
m_currentStats->m_testsCount = totals.assertions.total();
2010-12-10 09:01:42 +01:00
m_currentStats = &m_testSuiteStats;
}
2012-05-16 15:53:59 +02:00
virtual void StartSection( const std::string&, const std::string& ){}
virtual void NoAssertionsInSection( const std::string& ) {}
virtual void NoAssertionsInTestCase( const std::string& ) {}
virtual void EndSection( const std::string&, const Counts& ) {}
virtual void StartTestCase( const Catch::TestCaseInfo& testInfo ) {
m_currentStats->m_testCaseStats.push_back( TestCaseStats( testInfo.className, testInfo.name ) );
2012-11-22 20:17:20 +01:00
m_currentTestCaseStats.push_back( &m_currentStats->m_testCaseStats.back() );
2010-12-10 09:01:42 +01:00
}
virtual void Result( const Catch::AssertionResult& assertionResult ) {
if( assertionResult.getResultType() != ResultWas::Ok || m_config.fullConfig()->includeSuccessfulResults() ) {
2011-01-31 11:10:20 +01:00
TestCaseStats& testCaseStats = m_currentStats->m_testCaseStats.back();
2010-12-10 09:01:42 +01:00
TestStats stats;
std::ostringstream oss;
if( !assertionResult.getMessage().empty() )
oss << assertionResult.getMessage() << " at ";
2012-10-24 22:59:47 +02:00
oss << assertionResult.getSourceInfo();
2011-01-31 11:10:20 +01:00
stats.m_content = oss.str();
stats.m_message = assertionResult.getExpandedExpression();
stats.m_resultType = assertionResult.getTestMacroName();
2012-05-16 15:53:59 +02:00
switch( assertionResult.getResultType() ) {
2010-12-10 09:01:42 +01:00
case ResultWas::ThrewException:
2011-01-31 11:10:20 +01:00
stats.m_element = "error";
m_currentStats->m_errorsCount++;
2010-12-10 09:01:42 +01:00
break;
case ResultWas::Info:
2011-01-31 11:10:20 +01:00
stats.m_element = "info"; // !TBD ?
2010-12-10 09:01:42 +01:00
break;
case ResultWas::Warning:
2011-01-31 11:10:20 +01:00
stats.m_element = "warning"; // !TBD ?
2010-12-10 09:01:42 +01:00
break;
case ResultWas::ExplicitFailure:
2011-01-31 11:10:20 +01:00
stats.m_element = "failure";
m_currentStats->m_failuresCount++;
2010-12-10 09:01:42 +01:00
break;
case ResultWas::ExpressionFailed:
2011-01-31 11:10:20 +01:00
stats.m_element = "failure";
m_currentStats->m_failuresCount++;
2010-12-10 09:01:42 +01:00
break;
case ResultWas::Ok:
2011-01-31 11:10:20 +01:00
stats.m_element = "success";
2010-12-10 09:01:42 +01:00
break;
case ResultWas::DidntThrowException:
stats.m_element = "failure";
m_currentStats->m_failuresCount++;
break;
2011-09-29 09:58:40 +02:00
case ResultWas::Unknown:
case ResultWas::FailureBit:
case ResultWas::Exception:
stats.m_element = "* internal error *";
2010-12-10 09:01:42 +01:00
break;
}
testCaseStats.m_testStats.push_back( stats );
2010-12-10 09:01:42 +01:00
}
}
virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string& stdOut, const std::string& stdErr ) {
2012-11-22 20:17:20 +01:00
m_currentTestCaseStats.pop_back();
assert( m_currentTestCaseStats.empty() );
TestCaseStats& testCaseStats = m_currentStats->m_testCaseStats.back();
testCaseStats.m_stdOut = stdOut;
testCaseStats.m_stdErr = stdErr;
2010-12-10 09:01:42 +01:00
if( !stdOut.empty() )
m_stdOut << stdOut << "\n";
if( !stdErr.empty() )
m_stdErr << stdErr << "\n";
}
2010-12-10 09:01:42 +01:00
virtual void Aborted() {
// !TBD
}
2012-05-16 15:53:59 +02:00
virtual void EndTesting( const Totals& ) {
XmlWriter xml( m_config.stream() );
if( m_statsForSuites.size() > 0 )
xml.startElement( "testsuites" );
std::vector<Stats>::const_iterator it = m_statsForSuites.begin();
std::vector<Stats>::const_iterator itEnd = m_statsForSuites.end();
for(; it != itEnd; ++it ) {
XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
xml.writeAttribute( "name", it->m_name );
xml.writeAttribute( "errors", it->m_errorsCount );
xml.writeAttribute( "failures", it->m_failuresCount );
xml.writeAttribute( "tests", it->m_testsCount );
xml.writeAttribute( "hostname", "tbd" );
if( m_config.fullConfig()->showDurations() == ShowDurations::Never )
xml.writeAttribute( "time", it->m_timeInSeconds );
else
xml.writeAttribute( "time", "" );
xml.writeAttribute( "timestamp", "tbd" );
OutputTestCases( xml, *it );
2010-12-10 09:01:42 +01:00
}
xml.scopedElement( "system-out" ).writeText( trim( m_stdOut.str() ), false );
xml.scopedElement( "system-err" ).writeText( trim( m_stdErr.str() ), false );
2010-12-10 09:01:42 +01:00
}
2012-05-16 15:53:59 +02:00
void OutputTestCases( XmlWriter& xml, const Stats& stats ) {
2011-01-31 11:10:20 +01:00
std::vector<TestCaseStats>::const_iterator it = stats.m_testCaseStats.begin();
std::vector<TestCaseStats>::const_iterator itEnd = stats.m_testCaseStats.end();
2012-05-16 15:53:59 +02:00
for(; it != itEnd; ++it ) {
2010-12-10 09:01:42 +01:00
XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
2011-01-31 11:10:20 +01:00
xml.writeAttribute( "classname", it->m_className );
xml.writeAttribute( "name", it->m_name );
if( m_config.fullConfig()->showDurations() == ShowDurations::Never )
xml.writeAttribute( "time", "" );
else
xml.writeAttribute( "time", stats.m_timeInSeconds );
2010-12-10 09:01:42 +01:00
OutputTestResult( xml, *it );
std::string stdOut = trim( it->m_stdOut );
if( !stdOut.empty() )
xml.scopedElement( "system-out" ).writeText( stdOut, false );
std::string stdErr = trim( it->m_stdErr );
if( !stdErr.empty() )
xml.scopedElement( "system-err" ).writeText( stdErr, false );
2010-12-10 09:01:42 +01:00
}
}
2012-05-16 15:53:59 +02:00
void OutputTestResult( XmlWriter& xml, const TestCaseStats& stats ) {
2011-01-31 11:10:20 +01:00
std::vector<TestStats>::const_iterator it = stats.m_testStats.begin();
std::vector<TestStats>::const_iterator itEnd = stats.m_testStats.end();
2012-05-16 15:53:59 +02:00
for(; it != itEnd; ++it ) {
if( it->m_element != "success" ) {
2011-01-31 11:10:20 +01:00
XmlWriter::ScopedElement e = xml.scopedElement( it->m_element );
2011-01-31 11:10:20 +01:00
xml.writeAttribute( "message", it->m_message );
xml.writeAttribute( "type", it->m_resultType );
if( !it->m_content.empty() )
xml.writeText( it->m_content );
2010-12-10 09:01:42 +01:00
}
}
}
2010-12-10 09:01:42 +01:00
private:
2012-07-20 20:07:42 +02:00
ReporterConfig m_config;
2010-12-10 09:01:42 +01:00
Stats m_testSuiteStats;
Stats* m_currentStats;
std::vector<Stats> m_statsForSuites;
2012-11-22 20:17:20 +01:00
std::vector<const TestCaseStats*> m_currentTestCaseStats;
2010-12-10 09:01:42 +01:00
std::ostringstream m_stdOut;
std::ostringstream m_stdErr;
};
2010-12-10 09:01:42 +01:00
} // end namespace Catch
#endif // TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED