From d4fa900b84b497c46d8c5b528a15b3c9e8f85dc0 Mon Sep 17 00:00:00 2001 From: Konstantin Baumann Date: Tue, 23 Jul 2013 14:10:37 +0200 Subject: [PATCH] JunitReporter reimplemented using the new IStreamingReporter interface * created new AccumulatingReporterBase class for accumulating test results hierarchically and store them for a single processResults() call after all tests have been executed; sections are currently not handled, since their usage are optional and/or could be nested arbitrarily, which would result in overly complex code, IMHO * JunitReporter reimplemented on top of this new AccumulatingReporterBase class * added support for tracking time spend in each test case, each test group, and overall tests to the base "*Stats" classes; this enables each reporter (derived from IStreamingReporter interface) to report the timings; for now only the JunitReporter takes advantage of that. --- include/internal/catch_impl.hpp | 5 +- include/internal/catch_interfaces_reporter.h | 124 +++++-- include/internal/catch_runner_impl.hpp | 28 +- include/internal/catch_test_case_info.h | 3 +- include/internal/catch_test_case_info.hpp | 10 - include/reporters/catch_reporter_junit.hpp | 327 +++++++------------ 6 files changed, 250 insertions(+), 247 deletions(-) diff --git a/include/internal/catch_impl.hpp b/include/internal/catch_impl.hpp index ff95675b..2ded17bf 100644 --- a/include/internal/catch_impl.hpp +++ b/include/internal/catch_impl.hpp @@ -58,11 +58,10 @@ namespace Catch { TestGroupStats::~TestGroupStats() {} TestRunStats::~TestRunStats() {} ThreadedSectionInfo::~ThreadedSectionInfo() {} - TestGroupNode::~TestGroupNode() {} - TestRunNode::~TestRunNode() {} BasicReporter::~BasicReporter() {} StreamingReporterBase::~StreamingReporterBase() {} + AccumulatingReporterBase::~AccumulatingReporterBase() {} ConsoleReporter::~ConsoleReporter() {} IRunner::~IRunner() {} IMutableContext::~IMutableContext() {} @@ -86,7 +85,7 @@ namespace Catch { INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "basic", BasicReporter ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "xml", XmlReporter ) - INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( "junit", JunitReporter ) + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) } diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index 6419afc0..89fa2918 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -17,6 +17,7 @@ #include "catch_message.h" #include "catch_option.hpp" +#include #include #include #include @@ -47,10 +48,11 @@ namespace Catch }; struct TestRunInfo { - TestRunInfo( std::string const& _name ) : name( _name ) {} + TestRunInfo( std::string const& _name = "" ) : name( _name ) {} std::string name; }; struct GroupInfo { + GroupInfo() : groupIndex( 0 ), groupsCounts( 0 ) {} GroupInfo( std::string const& _name, std::size_t _groupIndex, std::size_t _groupsCount ) @@ -65,6 +67,7 @@ namespace Catch }; struct SectionInfo { + SectionInfo() {} SectionInfo( std::string const& _name, std::string const& _description, SourceLineInfo const& _lineInfo ) @@ -113,6 +116,7 @@ namespace Catch }; struct SectionStats { + SectionStats() : missingAssertions( false ) {} SectionStats( SectionInfo const& _sectionInfo, Counts const& _assertions, bool _missingAssertions ) @@ -128,14 +132,20 @@ namespace Catch }; struct TestCaseStats { + TestCaseStats() + : missingAssertions( false ), + aborting( false) + {} TestCaseStats( TestCaseInfo const& _testInfo, Totals const& _totals, + double _timeSecs, std::string const& _stdOut, std::string const& _stdErr, bool _missingAssertions, bool _aborting ) : testInfo( _testInfo ), totals( _totals ), + timeSecs( _timeSecs ), stdOut( _stdOut ), stdErr( _stdErr ), missingAssertions( _missingAssertions ), @@ -145,6 +155,7 @@ namespace Catch TestCaseInfo testInfo; Totals totals; + double timeSecs; std::string stdOut; std::string stdErr; bool missingAssertions; @@ -152,41 +163,51 @@ namespace Catch }; struct TestGroupStats { + TestGroupStats() + : aborting( false ) + {} + TestGroupStats( GroupInfo const& _groupInfo, Totals const& _totals, + double _timeSecs, bool _aborting ) : groupInfo( _groupInfo ), totals( _totals ), + timeSecs( _timeSecs ), aborting( _aborting ) {} TestGroupStats( GroupInfo const& _groupInfo ) : groupInfo( _groupInfo ), + timeSecs( 0.0 ), aborting( false ) {} virtual ~TestGroupStats(); GroupInfo groupInfo; Totals totals; + double timeSecs; bool aborting; }; struct TestRunStats { + TestRunStats() + : timeSecs( 0.0 ), + aborting( false ) + {} TestRunStats( TestRunInfo const& _runInfo, Totals const& _totals, + double _timeSecs, bool _aborting ) : runInfo( _runInfo ), totals( _totals ), + timeSecs( _timeSecs ), aborting( _aborting ) {} - TestRunStats( TestRunStats const& _other ) - : runInfo( _other.runInfo ), - totals( _other.totals ), - aborting( _other.aborting ) - {} virtual ~TestRunStats(); TestRunInfo runInfo; Totals totals; + double timeSecs; bool aborting; }; @@ -276,21 +297,83 @@ namespace Catch std::vector > m_rootSections; }; - struct TestGroupNode : TestGroupStats { - TestGroupNode( TestGroupStats const& _stats ) : TestGroupStats( _stats ) {} -// TestGroupNode( GroupInfo const& _info ) : TestGroupStats( _stats ) {} - ~TestGroupNode(); + struct AccumulatingReporterBase : SharedImpl { + protected: + struct AccumTestCaseStats { + TestCaseStats testCase; + std::vector tests; + }; + struct AccumTestGroupStats { + TestGroupStats testGroup; + std::vector testCases; + }; + struct AccumTestRunStats { + TestRunStats testRun; + std::vector testGroups; + }; - }; - - struct TestRunNode : TestRunStats { - - TestRunNode( TestRunStats const& _stats ) : TestRunStats( _stats ) {} - ~TestRunNode(); + public: + virtual ~AccumulatingReporterBase(); - std::vector groups; + virtual void testRunStarting( TestRunInfo const& testRunInfo ) { + (void)testRunInfo; + assert( m_accumGroups.empty() ); + assert( m_accumCases.empty() ); + assert( m_accumTests.empty() ); + } + virtual void testGroupStarting( GroupInfo const& groupInfo ) { + (void)groupInfo; + assert( m_accumCases.empty() ); + assert( m_accumTests.empty() ); + } + virtual void testCaseStarting( TestCaseInfo const& testInfo ) { + (void)testInfo; + assert( m_accumTests.empty() ); + } + virtual void sectionStarting( SectionInfo const& sectionInfo ) { + (void)sectionInfo; + } + virtual void assertionStarting( AssertionInfo const& assertionInfo ) { + (void)assertionInfo; + } + + virtual void assertionEnded( AssertionStats const& assertionStats ) { + m_accumTests.push_back( assertionStats ); + } + virtual void sectionEnded( SectionStats const& sectionStats ) { + (void)sectionStats; + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { + AccumTestCaseStats stats; + stats.testCase = testCaseStats; + std::swap(stats.tests, m_accumTests); + m_accumCases.push_back( stats ); + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { + assert( m_accumTests.empty() ); + AccumTestGroupStats stats; + stats.testGroup = testGroupStats; + std::swap(stats.testCases, m_accumCases); + m_accumGroups.push_back( stats ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) { + assert( m_accumTests.empty() ); + assert( m_accumCases.empty() ); + AccumTestRunStats stats; + stats.testRun = testRunStats; + std::swap(stats.testGroups, m_accumGroups); + + processResults( stats ); + } + + virtual void processResults( AccumTestRunStats const& testRun ) = 0; + + private: + std::vector m_accumGroups; + std::vector m_accumCases; + std::vector m_accumTests; }; - + // Deprecated struct IReporter : IShared { virtual ~IReporter(); @@ -327,8 +410,9 @@ namespace Catch }; inline std::string trim( std::string const& str ) { - std::string::size_type start = str.find_first_not_of( "\n\r\t " ); - std::string::size_type end = str.find_last_not_of( "\n\r\t " ); + const char* const stripAway = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( stripAway ); + std::string::size_type end = str.find_last_not_of( stripAway ); return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; } diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp index 7058d827..7db92a83 100644 --- a/include/internal/catch_runner_impl.hpp +++ b/include/internal/catch_runner_impl.hpp @@ -22,8 +22,20 @@ #include #include +// C++11 specific start +#include +// C++11 specific end + namespace Catch { + // C++11 specific start + typedef decltype(std::chrono::high_resolution_clock::now()) time_point; + static inline time_point time_now() { return std::chrono::high_resolution_clock::now(); } + static inline double time_diff_secs(time_point const& end, time_point const& start) { + return (1e-6 * std::chrono::duration_cast(end - start).count()); + } + // C++11 specific end + class StreamRedirect { public: @@ -64,7 +76,9 @@ namespace Catch { m_reporter( reporter ), m_prevRunner( &m_context.getRunner() ), m_prevResultCapture( &m_context.getResultCapture() ), - m_prevConfig( m_context.getConfig() ) + m_prevConfig( m_context.getConfig() ), + m_startRun( time_now() ), + m_startGroup( m_startRun ) { m_context.setRunner( this ); m_context.setConfig( m_config ); @@ -73,7 +87,8 @@ namespace Catch { } virtual ~RunContext() { - m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + double timing = time_diff_secs( time_now() , m_startRun ); + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, timing, aborting() ) ); m_context.setRunner( m_prevRunner ); m_context.setConfig( NULL ); m_context.setResultCapture( m_prevResultCapture ); @@ -82,9 +97,11 @@ namespace Catch { void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + m_startGroup = time_now(); } void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { - m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + double timing = time_diff_secs( time_now() , m_startGroup ); + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, timing, aborting() ) ); } Totals runMatching( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { @@ -116,6 +133,7 @@ namespace Catch { m_runningTest = new RunningTest( testCase ); + time_point timeStart = time_now(); do { do { runCurrentTest( redirectedCout, redirectedCerr ); @@ -123,6 +141,7 @@ namespace Catch { while( m_runningTest->hasUntestedSections() && !aborting() ); } while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + time_point timeEnd = time_now(); Totals deltaTotals = m_totals.delta( prevTotals ); bool missingAssertions = false; @@ -136,6 +155,7 @@ namespace Catch { m_reporter->testCaseEnded( TestCaseStats( testInfo, deltaTotals, + time_diff_secs( timeEnd, timeStart ), redirectedCout, redirectedCerr, missingAssertions, @@ -323,6 +343,8 @@ namespace Catch { Ptr m_prevConfig; AssertionInfo m_lastAssertionInfo; std::vector m_unfinishedSections; + time_point m_startRun; + time_point m_startGroup; }; } // end namespace Catch diff --git a/include/internal/catch_test_case_info.h b/include/internal/catch_test_case_info.h index 72d73f01..c8cb115f 100644 --- a/include/internal/catch_test_case_info.h +++ b/include/internal/catch_test_case_info.h @@ -24,6 +24,7 @@ namespace Catch { struct ITestCase; struct TestCaseInfo { + TestCaseInfo() : isHidden( false ) {} TestCaseInfo( std::string const& _name, std::string const& _className, std::string const& _description, @@ -31,8 +32,6 @@ namespace Catch { bool _isHidden, SourceLineInfo const& _lineInfo ); - TestCaseInfo( TestCaseInfo const& other ); - std::string name; std::string className; std::string description; diff --git a/include/internal/catch_test_case_info.hpp b/include/internal/catch_test_case_info.hpp index d3a2719b..d8ae6c20 100644 --- a/include/internal/catch_test_case_info.hpp +++ b/include/internal/catch_test_case_info.hpp @@ -51,16 +51,6 @@ namespace Catch { tagsAsString = oss.str(); } - TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) - : name( other.name ), - className( other.className ), - description( other.description ), - tags( other.tags ), - tagsAsString( other.tagsAsString ), - lineInfo( other.lineInfo ), - isHidden( other.isHidden ) - {} - TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} TestCase::TestCase( TestCase const& other ) diff --git a/include/reporters/catch_reporter_junit.hpp b/include/reporters/catch_reporter_junit.hpp index 30343251..4b79de09 100644 --- a/include/reporters/catch_reporter_junit.hpp +++ b/include/reporters/catch_reporter_junit.hpp @@ -17,243 +17,152 @@ namespace Catch { - class JunitReporter : public SharedImpl { - - // C++11 specific start => needs to be adjusted for pre-C++11-compilers - typedef decltype(std::chrono::high_resolution_clock::now()) time_point; - static time_point time_now() { return std::chrono::high_resolution_clock::now(); } - static double time_diff(const time_point& end, const time_point& start) { return (1e-6 * std::chrono::duration_cast(end - start).count()); } - // C++11 specific end - - struct TestStats { - std::string m_element; - std::string m_resultType; - std::string m_message; - std::string m_content; - }; - - struct TestCaseStats { - - TestCaseStats( const std::string& className, const std::string& name ) - : m_className( className ), - m_name( name ), - m_startTime( time_now() ), m_endTime( m_startTime ) - {} - - std::string m_status; - std::string m_className; - std::string m_name; - std::string m_stdOut; - std::string m_stdErr; - time_point m_startTime, m_endTime; - std::vector m_testStats; - std::vector m_sections; - }; - - struct Stats { - - Stats( const std::string& name = std::string() ) - : m_testsCount( 0 ), - m_failuresCount( 0 ), - m_disabledCount( 0 ), - m_errorsCount( 0 ), - m_name( name ), - m_startTime( time_now() ), m_endTime( m_startTime ) - {} - - std::size_t m_testsCount; - std::size_t m_failuresCount; - std::size_t m_disabledCount; - std::size_t m_errorsCount; - std::string m_name; - time_point m_startTime, m_endTime; - std::vector m_testCaseStats; - }; - + class JunitReporter : public SharedImpl { public: - JunitReporter( ReporterConfig const& config ) - : m_config( config ), - m_testSuiteStats( "AllTests" ), - m_currentStats( &m_testSuiteStats ) - {} + JunitReporter( ReporterConfig const& config ) : m_config( config ) {} virtual ~JunitReporter(); static std::string getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; } - private: // IReporter - - virtual bool shouldRedirectStdout() const { - return true; + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = true; + return prefs; } - 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(); + virtual void noMatchingTestCases( std::string const& spec ) { + (void)spec; } - virtual void EndGroup( const std::string&, const Totals& totals ) { - m_currentStats->m_testsCount = totals.assertions.total(); - m_currentStats->m_endTime = time_now(); - m_currentStats = &m_testSuiteStats; - } - - 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 ) ); - m_currentTestCaseStats.push_back( &m_currentStats->m_testCaseStats.back() ); - } - - virtual void Result( const Catch::AssertionResult& assertionResult ) { - if( assertionResult.getResultType() != ResultWas::Ok || m_config.fullConfig()->includeSuccessfulResults() ) { - TestCaseStats& testCaseStats = m_currentStats->m_testCaseStats.back(); - TestStats stats; - std::ostringstream oss; - if( !assertionResult.getMessage().empty() ) - oss << assertionResult.getMessage() << " at "; - oss << assertionResult.getSourceInfo(); - stats.m_content = oss.str(); - stats.m_message = assertionResult.getExpandedExpression(); - stats.m_resultType = assertionResult.getTestMacroName(); - - switch( assertionResult.getResultType() ) { - case ResultWas::ThrewException: - stats.m_element = "error"; - m_currentStats->m_errorsCount++; - break; - case ResultWas::Info: - stats.m_element = "info"; // !TBD ? - break; - case ResultWas::Warning: - stats.m_element = "warning"; // !TBD ? - break; - case ResultWas::ExplicitFailure: - stats.m_element = "failure"; - m_currentStats->m_failuresCount++; - break; - case ResultWas::ExpressionFailed: - stats.m_element = "failure"; - m_currentStats->m_failuresCount++; - break; - case ResultWas::Ok: - stats.m_element = "success"; - break; - case ResultWas::DidntThrowException: - stats.m_element = "failure"; - m_currentStats->m_failuresCount++; - break; - case ResultWas::Unknown: - case ResultWas::FailureBit: - case ResultWas::Exception: - stats.m_element = "* internal error *"; - break; - } - testCaseStats.m_testStats.push_back( stats ); - } - } - - virtual void EndTestCase( const Catch::TestCaseInfo&, const Totals&, const std::string& stdOut, const std::string& stdErr ) { - m_currentTestCaseStats.pop_back(); - assert( m_currentTestCaseStats.empty() ); - TestCaseStats& testCaseStats = m_currentStats->m_testCaseStats.back(); - testCaseStats.m_stdOut = stdOut; - testCaseStats.m_stdErr = stdErr; - testCaseStats.m_endTime = time_now(); - if( !stdOut.empty() ) - m_stdOut << stdOut << "\n"; - if( !stdErr.empty() ) - m_stdErr << stdErr << "\n"; - } - - virtual void Aborted() { - // !TBD - } - - virtual void EndTesting( const Totals& ) { - m_testSuiteStats.m_endTime = time_now(); - + virtual void processResults( AccumTestRunStats const& stats ) { XmlWriter xml( m_config.stream() ); - - if( m_statsForSuites.size() > 0 ) - xml.startElement( "testsuites" ); - - std::vector::const_iterator it = m_statsForSuites.begin(); - std::vector::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" ); - xml.writeAttribute( "time", time_diff(it->m_endTime, it->m_startTime) ); - xml.writeAttribute( "timestamp", "tbd" ); - - OutputTestCases( xml, *it ); - } - - xml.scopedElement( "system-out" ).writeText( trim( m_stdOut.str() ), false ); - xml.scopedElement( "system-err" ).writeText( trim( m_stdErr.str() ), false ); + OutputTestSuites( xml, stats ); } - void OutputTestCases( XmlWriter& xml, const Stats& stats ) { - std::vector::const_iterator it = stats.m_testCaseStats.begin(); - std::vector::const_iterator itEnd = stats.m_testCaseStats.end(); - for(; it != itEnd; ++it ) { - XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); - xml.writeAttribute( "classname", it->m_className ); - xml.writeAttribute( "name", it->m_name ); - xml.writeAttribute( "time", time_diff(it->m_endTime, it->m_startTime) ); + private: + static void OutputTestSuites( XmlWriter& xml, AccumTestRunStats const& stats ) { + xml.startElement( "testsuites" ); + + xml.writeAttribute( "time", stats.testRun.timeSecs ); + std::vector::const_iterator it = stats.testGroups.begin(); + std::vector::const_iterator itEnd = stats.testGroups.end(); + + std::ostringstream stdErr, stdOut; + for( ; it != itEnd; ++it ) { + OutputTestSuite( xml, *it); + CollectErrAndOutMessages( *it, stdErr, stdOut ); + } + + OutputTextIfNotEmpty( xml, "system-out", stdOut.str() ); + OutputTextIfNotEmpty( xml, "system-err", stdErr.str() ); + } + + 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::const_iterator it2 = stats.testCases.begin(); + std::vector::const_iterator it2End = stats.testCases.end(); + for(; it2 != it2End; ++it2 ) { + OutputTestCase( xml, *it2 ); + } + } + + 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 ); + + std::vector::const_iterator it = stats.tests.begin(); + std::vector::const_iterator itEnd = stats.tests.end(); + for( ; it != itEnd; ++it ) { 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 ); + OutputTextIfNotEmpty( xml, "system-out", stats.testCase.stdOut ); + OutputTextIfNotEmpty( xml, "system-err", stats.testCase.stdErr ); + } + + 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 *"; } } + static void OutputTestResult( XmlWriter& xml, AssertionStats const& test ) { + std::string tag = GetResultTag(test); + if( tag != "success" ) { + XmlWriter::ScopedElement e = xml.scopedElement( tag ); - void OutputTestResult( XmlWriter& xml, const TestCaseStats& stats ) { - std::vector::const_iterator it = stats.m_testStats.begin(); - std::vector::const_iterator itEnd = stats.m_testStats.end(); - for(; it != itEnd; ++it ) { - if( it->m_element != "success" ) { - XmlWriter::ScopedElement e = xml.scopedElement( it->m_element ); + xml.writeAttribute( "message", test.assertionResult.getExpandedExpression() ); + xml.writeAttribute( "type", test.assertionResult.getTestMacroName() ); - xml.writeAttribute( "message", it->m_message ); - xml.writeAttribute( "type", it->m_resultType ); - if( !it->m_content.empty() ) - xml.writeText( it->m_content ); + std::ostringstream oss; + if( !test.assertionResult.getMessage().empty() ) { + oss << test.assertionResult.getMessage() << " at "; } + oss << test.assertionResult.getSourceInfo(); + xml.writeText( oss.str() ); + } + } + + 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 ); + } + } + + static void CountErrorAndFailures(AccumTestGroupStats const& stats, size_t& outErrors, size_t& outFailures) { + std::vector::const_iterator it = stats.testCases.begin(); + std::vector::const_iterator itEnd = stats.testCases.end(); + for( ; it != itEnd; ++it ) { + std::vector::const_iterator it2 = it->tests.begin(); + std::vector::const_iterator it2End = it->tests.end(); + for( ; it2 != it2End; ++it2 ) { + std::string tag = GetResultTag(*it2); + if( tag == "error" ) { ++outErrors; } + if( tag == "failure" ) { ++outFailures; } + } + } + } + + static void CollectErrAndOutMessages(AccumTestGroupStats const& stats, std::ostream& outErr, std::ostream& outOut) { + std::vector::const_iterator it = stats.testCases.begin(); + std::vector::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; } } } private: ReporterConfig m_config; - - Stats m_testSuiteStats; - Stats* m_currentStats; - std::vector m_statsForSuites; - std::vector m_currentTestCaseStats; - std::ostringstream m_stdOut; - std::ostringstream m_stdErr; }; } // end namespace Catch