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
|
|
|
|
|
2011-06-02 09:49:47 +02:00
|
|
|
#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
|
|
|
|
2012-12-02 01:05:51 +01:00
|
|
|
#include <assert.h>
|
|
|
|
|
2012-05-16 15:53:59 +02:00
|
|
|
namespace Catch {
|
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
class JunitReporter : public SharedImpl<AccumulatingReporterBase> {
|
2010-12-10 09:01:42 +01:00
|
|
|
public:
|
2013-07-23 14:10:37 +02:00
|
|
|
JunitReporter( ReporterConfig const& config ) : m_config( config ) {}
|
2012-08-13 08:46:10 +02:00
|
|
|
virtual ~JunitReporter();
|
2013-07-22 20:49:54 +02:00
|
|
|
|
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";
|
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
virtual ReporterPreferences getPreferences() const {
|
|
|
|
ReporterPreferences prefs;
|
|
|
|
prefs.shouldRedirectStdOut = true;
|
|
|
|
return prefs;
|
2013-07-22 20:49:54 +02:00
|
|
|
}
|
2012-02-17 10:28:21 +01:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
virtual void noMatchingTestCases( std::string const& spec ) {
|
|
|
|
(void)spec;
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
virtual void processResults( AccumTestRunStats const& stats ) {
|
|
|
|
XmlWriter xml( m_config.stream() );
|
|
|
|
OutputTestSuites( xml, stats );
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
private:
|
|
|
|
static void OutputTestSuites( XmlWriter& xml, AccumTestRunStats const& stats ) {
|
|
|
|
xml.startElement( "testsuites" );
|
2013-07-23 17:11:43 +02:00
|
|
|
xml.writeAttribute( "name", stats.testRun.runInfo.name );
|
2013-07-23 14:10:37 +02:00
|
|
|
xml.writeAttribute( "time", stats.testRun.timeSecs );
|
|
|
|
|
|
|
|
std::vector<AccumTestGroupStats>::const_iterator it = stats.testGroups.begin();
|
|
|
|
std::vector<AccumTestGroupStats>::const_iterator itEnd = stats.testGroups.end();
|
|
|
|
|
|
|
|
std::ostringstream stdErr, stdOut;
|
|
|
|
for( ; it != itEnd; ++it ) {
|
|
|
|
OutputTestSuite( xml, *it);
|
|
|
|
CollectErrAndOutMessages( *it, stdErr, stdOut );
|
|
|
|
}
|
2012-08-31 09:10:36 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
OutputTextIfNotEmpty( xml, "system-out", stdOut.str() );
|
|
|
|
OutputTextIfNotEmpty( xml, "system-err", stdErr.str() );
|
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void OutputTestSuite( XmlWriter& xml, AccumTestGroupStats const& stats ) {
|
|
|
|
size_t errors = 0, failures = 0;
|
|
|
|
CountErrorAndFailures(stats, errors, failures);
|
|
|
|
|
|
|
|
XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
|
|
|
|
xml.writeAttribute( "name", stats.testGroup.groupInfo.name );
|
|
|
|
xml.writeAttribute( "errors", errors );
|
|
|
|
xml.writeAttribute( "failures", failures );
|
|
|
|
xml.writeAttribute( "tests", stats.testCases.size() );
|
|
|
|
xml.writeAttribute( "hostname", "tbd" );
|
|
|
|
xml.writeAttribute( "time", stats.testGroup.timeSecs );
|
|
|
|
xml.writeAttribute( "timestamp", "tbd" );
|
|
|
|
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator it2 = stats.testCases.begin();
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator it2End = stats.testCases.end();
|
|
|
|
for(; it2 != it2End; ++it2 ) {
|
|
|
|
OutputTestCase( xml, *it2 );
|
|
|
|
}
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void OutputTestCase( XmlWriter& xml, AccumTestCaseStats const& stats ) {
|
|
|
|
XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
|
|
|
|
xml.writeAttribute( "classname", stats.testCase.testInfo.className );
|
|
|
|
xml.writeAttribute( "name", stats.testCase.testInfo.name );
|
|
|
|
xml.writeAttribute( "time", stats.testCase.timeSecs );
|
2012-05-16 15:53:59 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
std::vector<AssertionStats>::const_iterator it = stats.tests.begin();
|
|
|
|
std::vector<AssertionStats>::const_iterator itEnd = stats.tests.end();
|
|
|
|
for( ; it != itEnd; ++it ) {
|
|
|
|
OutputTestResult( xml, *it );
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
OutputTextIfNotEmpty( xml, "system-out", stats.testCase.stdOut );
|
|
|
|
OutputTextIfNotEmpty( xml, "system-err", stats.testCase.stdErr );
|
2013-07-22 20:49:54 +02:00
|
|
|
}
|
2010-12-10 09:01:42 +01:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static std::string GetResultTag( AssertionStats const& test ) {
|
|
|
|
switch(test.assertionResult.getResultType()) {
|
|
|
|
case ResultWas::Ok: return "success";
|
|
|
|
case ResultWas::ThrewException: return "error";
|
|
|
|
case ResultWas::Info: return "info";
|
|
|
|
case ResultWas::Warning: return "warning";
|
|
|
|
case ResultWas::ExplicitFailure:
|
|
|
|
case ResultWas::ExpressionFailed:
|
|
|
|
case ResultWas::DidntThrowException: return "failure";
|
|
|
|
case ResultWas::Unknown:
|
|
|
|
case ResultWas::FailureBit:
|
|
|
|
case ResultWas::Exception:
|
|
|
|
default: return "* internal error *";
|
|
|
|
}
|
2012-06-01 20:40:27 +02:00
|
|
|
}
|
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void OutputTestResult( XmlWriter& xml, AssertionStats const& test ) {
|
|
|
|
std::string tag = GetResultTag(test);
|
|
|
|
if( tag != "success" ) {
|
|
|
|
XmlWriter::ScopedElement e = xml.scopedElement( tag );
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
xml.writeAttribute( "message", test.assertionResult.getExpandedExpression() );
|
|
|
|
xml.writeAttribute( "type", test.assertionResult.getTestMacroName() );
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
std::ostringstream oss;
|
|
|
|
if( !test.assertionResult.getMessage().empty() ) {
|
|
|
|
oss << test.assertionResult.getMessage() << " at ";
|
|
|
|
}
|
|
|
|
oss << test.assertionResult.getSourceInfo();
|
|
|
|
xml.writeText( oss.str() );
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void OutputTextIfNotEmpty( XmlWriter& xml, std::string const& elementName, std::string const& text ) {
|
|
|
|
std::string trimmed = trim( text );
|
|
|
|
if( !trimmed.empty() ) {
|
|
|
|
xml.scopedElement( elementName ).writeText( trimmed, false );
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void CountErrorAndFailures(AccumTestGroupStats const& stats, size_t& outErrors, size_t& outFailures) {
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator it = stats.testCases.begin();
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator itEnd = stats.testCases.end();
|
|
|
|
for( ; it != itEnd; ++it ) {
|
|
|
|
std::vector<AssertionStats>::const_iterator it2 = it->tests.begin();
|
|
|
|
std::vector<AssertionStats>::const_iterator it2End = it->tests.end();
|
|
|
|
for( ; it2 != it2End; ++it2 ) {
|
|
|
|
std::string tag = GetResultTag(*it2);
|
|
|
|
if( tag == "error" ) { ++outErrors; }
|
|
|
|
if( tag == "failure" ) { ++outFailures; }
|
2010-12-10 09:01:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2013-07-23 14:10:37 +02:00
|
|
|
static void CollectErrAndOutMessages(AccumTestGroupStats const& stats, std::ostream& outErr, std::ostream& outOut) {
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator it = stats.testCases.begin();
|
|
|
|
std::vector<AccumTestCaseStats>::const_iterator itEnd = stats.testCases.end();
|
|
|
|
for( ; it != itEnd; ++it ) {
|
|
|
|
std::string err = trim( it->testCase.stdErr );
|
|
|
|
if( !err.empty() ) { outErr << err << std::endl; }
|
|
|
|
std::string out = trim( it->testCase.stdOut );
|
|
|
|
if( !out.empty() ) { outOut << out << std::endl; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
};
|
2013-07-22 20:49:54 +02:00
|
|
|
|
2010-12-10 09:01:42 +01:00
|
|
|
} // end namespace Catch
|
|
|
|
|
2010-12-28 15:42:46 +01:00
|
|
|
#endif // TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
|