| /* |
| * Created by Phil on 31/12/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_INTERFACES_REPORTER_H_INCLUDED |
| #define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED |
| |
| #include "catch_common.h" |
| #include "catch_totals.hpp" |
| #include "catch_ptr.hpp" |
| #include "catch_config.hpp" |
| #include "catch_test_case_info.h" |
| #include "catch_assertionresult.h" |
| #include "catch_message.h" |
| #include "catch_option.hpp" |
| |
| #include <string> |
| #include <ostream> |
| #include <map> |
| #include <assert.h> |
| |
| namespace Catch |
| { |
| struct ReporterConfig { |
| explicit ReporterConfig( Ptr<IConfig> const& _fullConfig ) |
| : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} |
| |
| ReporterConfig( Ptr<IConfig> const& _fullConfig, std::ostream& _stream ) |
| : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} |
| |
| std::ostream& stream() const { return *m_stream; } |
| Ptr<IConfig> fullConfig() const { return m_fullConfig; } |
| |
| private: |
| std::ostream* m_stream; |
| Ptr<IConfig> m_fullConfig; |
| }; |
| |
| struct ReporterPreferences { |
| ReporterPreferences() |
| : shouldRedirectStdOut( false ) |
| {} |
| |
| bool shouldRedirectStdOut; |
| }; |
| |
| template<typename T> |
| struct LazyStat : Option<T> { |
| LazyStat() : used( false ) {} |
| LazyStat& operator=( T const& _value ) { |
| Option<T>::operator=( _value ); |
| used = false; |
| return *this; |
| } |
| void reset() { |
| Option<T>::reset(); |
| used = false; |
| } |
| bool used; |
| }; |
| |
| struct TestRunInfo { |
| TestRunInfo( std::string const& _name ) : name( _name ) {} |
| std::string name; |
| }; |
| struct GroupInfo { |
| GroupInfo( std::string const& _name, |
| std::size_t _groupIndex, |
| std::size_t _groupsCount ) |
| : name( _name ), |
| groupIndex( _groupIndex ), |
| groupsCounts( _groupsCount ) |
| {} |
| |
| std::string name; |
| std::size_t groupIndex; |
| std::size_t groupsCounts; |
| }; |
| |
| struct SectionInfo { |
| SectionInfo( std::string const& _name, |
| std::string const& _description, |
| SourceLineInfo const& _lineInfo ) |
| : name( _name ), |
| description( _description ), |
| lineInfo( _lineInfo ) |
| {} |
| |
| std::string name; |
| std::string description; |
| SourceLineInfo lineInfo; |
| }; |
| |
| struct AssertionStats { |
| AssertionStats( AssertionResult const& _assertionResult, |
| std::vector<MessageInfo> const& _infoMessages, |
| Totals const& _totals ) |
| : assertionResult( _assertionResult ), |
| infoMessages( _infoMessages ), |
| totals( _totals ) |
| { |
| if( assertionResult.hasMessage() ) { |
| // Copy message into messages list. |
| // !TBD This should have been done earlier, somewhere |
| MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); |
| builder << assertionResult.getMessage(); |
| builder.m_info.message = builder.m_stream.str(); |
| |
| infoMessages.push_back( builder.m_info ); |
| } |
| } |
| virtual ~AssertionStats(); |
| |
| # ifdef CATCH_CPP11_OR_GREATER |
| AssertionStats( AssertionStats const& ) = default; |
| AssertionStats( AssertionStats && ) = default; |
| AssertionStats& operator = ( AssertionStats const& ) = default; |
| AssertionStats& operator = ( AssertionStats && ) = default; |
| # endif |
| |
| AssertionResult assertionResult; |
| std::vector<MessageInfo> infoMessages; |
| Totals totals; |
| }; |
| |
| struct SectionStats { |
| SectionStats( SectionInfo const& _sectionInfo, |
| Counts const& _assertions, |
| double _durationInSeconds, |
| bool _missingAssertions ) |
| : sectionInfo( _sectionInfo ), |
| assertions( _assertions ), |
| durationInSeconds( _durationInSeconds ), |
| missingAssertions( _missingAssertions ) |
| {} |
| virtual ~SectionStats(); |
| # ifdef CATCH_CPP11_OR_GREATER |
| SectionStats( SectionStats const& ) = default; |
| SectionStats( SectionStats && ) = default; |
| SectionStats& operator = ( SectionStats const& ) = default; |
| SectionStats& operator = ( SectionStats && ) = default; |
| # endif |
| |
| SectionInfo sectionInfo; |
| Counts assertions; |
| double durationInSeconds; |
| bool missingAssertions; |
| }; |
| |
| struct TestCaseStats { |
| TestCaseStats( TestCaseInfo const& _testInfo, |
| Totals const& _totals, |
| std::string const& _stdOut, |
| std::string const& _stdErr, |
| bool _aborting ) |
| : testInfo( _testInfo ), |
| totals( _totals ), |
| stdOut( _stdOut ), |
| stdErr( _stdErr ), |
| aborting( _aborting ) |
| {} |
| virtual ~TestCaseStats(); |
| |
| # ifdef CATCH_CPP11_OR_GREATER |
| TestCaseStats( TestCaseStats const& ) = default; |
| TestCaseStats( TestCaseStats && ) = default; |
| TestCaseStats& operator = ( TestCaseStats const& ) = default; |
| TestCaseStats& operator = ( TestCaseStats && ) = default; |
| # endif |
| |
| TestCaseInfo testInfo; |
| Totals totals; |
| std::string stdOut; |
| std::string stdErr; |
| bool aborting; |
| }; |
| |
| struct TestGroupStats { |
| TestGroupStats( GroupInfo const& _groupInfo, |
| Totals const& _totals, |
| bool _aborting ) |
| : groupInfo( _groupInfo ), |
| totals( _totals ), |
| aborting( _aborting ) |
| {} |
| TestGroupStats( GroupInfo const& _groupInfo ) |
| : groupInfo( _groupInfo ), |
| aborting( false ) |
| {} |
| virtual ~TestGroupStats(); |
| |
| # ifdef CATCH_CPP11_OR_GREATER |
| TestGroupStats( TestGroupStats const& ) = default; |
| TestGroupStats( TestGroupStats && ) = default; |
| TestGroupStats& operator = ( TestGroupStats const& ) = default; |
| TestGroupStats& operator = ( TestGroupStats && ) = default; |
| # endif |
| |
| GroupInfo groupInfo; |
| Totals totals; |
| bool aborting; |
| }; |
| |
| struct TestRunStats { |
| TestRunStats( TestRunInfo const& _runInfo, |
| Totals const& _totals, |
| bool _aborting ) |
| : runInfo( _runInfo ), |
| totals( _totals ), |
| aborting( _aborting ) |
| {} |
| virtual ~TestRunStats(); |
| |
| # ifndef CATCH_CPP11_OR_GREATER |
| TestRunStats( TestRunStats const& _other ) |
| : runInfo( _other.runInfo ), |
| totals( _other.totals ), |
| aborting( _other.aborting ) |
| {} |
| # else |
| TestRunStats( TestRunStats const& ) = default; |
| TestRunStats( TestRunStats && ) = default; |
| TestRunStats& operator = ( TestRunStats const& ) = default; |
| TestRunStats& operator = ( TestRunStats && ) = default; |
| # endif |
| |
| TestRunInfo runInfo; |
| Totals totals; |
| bool aborting; |
| }; |
| |
| |
| struct IStreamingReporter : IShared { |
| virtual ~IStreamingReporter(); |
| |
| // Implementing class must also provide the following static method: |
| // static std::string getDescription(); |
| |
| virtual ReporterPreferences getPreferences() const = 0; |
| |
| virtual void noMatchingTestCases( std::string const& spec ) = 0; |
| |
| virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; |
| virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; |
| |
| virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; |
| virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; |
| |
| virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; |
| |
| virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; |
| virtual void sectionEnded( SectionStats const& sectionStats ) = 0; |
| virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; |
| virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; |
| virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; |
| }; |
| |
| struct StreamingReporterBase : SharedImpl<IStreamingReporter> { |
| |
| StreamingReporterBase( ReporterConfig const& _config ) |
| : m_config( _config.fullConfig() ), |
| stream( _config.stream() ) |
| {} |
| |
| virtual ~StreamingReporterBase(); |
| |
| virtual void noMatchingTestCases( std::string const& ) {} |
| |
| virtual void testRunStarting( TestRunInfo const& _testRunInfo ) { |
| currentTestRunInfo = _testRunInfo; |
| } |
| virtual void testGroupStarting( GroupInfo const& _groupInfo ) { |
| currentGroupInfo = _groupInfo; |
| } |
| |
| virtual void testCaseStarting( TestCaseInfo const& _testInfo ) { |
| currentTestCaseInfo = _testInfo; |
| } |
| virtual void sectionStarting( SectionInfo const& _sectionInfo ) { |
| m_sectionStack.push_back( _sectionInfo ); |
| } |
| |
| virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) { |
| m_sectionStack.pop_back(); |
| } |
| virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) { |
| currentTestCaseInfo.reset(); |
| assert( m_sectionStack.empty() ); |
| } |
| virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) { |
| currentGroupInfo.reset(); |
| } |
| virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) { |
| currentTestCaseInfo.reset(); |
| currentGroupInfo.reset(); |
| currentTestRunInfo.reset(); |
| } |
| |
| Ptr<IConfig> m_config; |
| std::ostream& stream; |
| |
| LazyStat<TestRunInfo> currentTestRunInfo; |
| LazyStat<GroupInfo> currentGroupInfo; |
| LazyStat<TestCaseInfo> currentTestCaseInfo; |
| |
| std::vector<SectionInfo> m_sectionStack; |
| }; |
| |
| struct CumulativeReporterBase : SharedImpl<IStreamingReporter> { |
| template<typename T, typename ChildNodeT> |
| struct Node : SharedImpl<> { |
| explicit Node( T const& _value ) : value( _value ) {} |
| virtual ~Node() {} |
| |
| typedef std::vector<Ptr<ChildNodeT> > ChildNodes; |
| T value; |
| ChildNodes children; |
| }; |
| struct SectionNode : SharedImpl<> { |
| explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} |
| virtual ~SectionNode(); |
| |
| bool operator == ( SectionNode const& other ) const { |
| return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; |
| } |
| bool operator == ( Ptr<SectionNode> const& other ) const { |
| return operator==( *other ); |
| } |
| |
| SectionStats stats; |
| typedef std::vector<Ptr<SectionNode> > ChildSections; |
| typedef std::vector<AssertionStats> Assertions; |
| ChildSections childSections; |
| Assertions assertions; |
| std::string stdOut; |
| std::string stdErr; |
| }; |
| friend bool operator == ( Ptr<SectionNode> const& node, SectionInfo const& other ) { |
| return node->stats.sectionInfo.lineInfo == other.lineInfo; |
| } |
| |
| typedef Node<TestCaseStats, SectionNode> TestCaseNode; |
| typedef Node<TestGroupStats, TestCaseNode> TestGroupNode; |
| typedef Node<TestRunStats, TestGroupNode> TestRunNode; |
| |
| CumulativeReporterBase( ReporterConfig const& _config ) |
| : m_config( _config.fullConfig() ), |
| stream( _config.stream() ) |
| {} |
| ~CumulativeReporterBase(); |
| |
| virtual void testRunStarting( TestRunInfo const& ) {} |
| virtual void testGroupStarting( GroupInfo const& ) {} |
| |
| virtual void testCaseStarting( TestCaseInfo const& ) {} |
| |
| virtual void sectionStarting( SectionInfo const& sectionInfo ) { |
| SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); |
| Ptr<SectionNode> node; |
| if( m_sectionStack.empty() ) { |
| if( !m_rootSection ) |
| m_rootSection = new SectionNode( incompleteStats ); |
| node = m_rootSection; |
| } |
| else { |
| SectionNode& parentNode = *m_sectionStack.back(); |
| SectionNode::ChildSections::const_iterator it = |
| std::find( parentNode.childSections.begin(), parentNode.childSections.end(), sectionInfo ); |
| if( it == parentNode.childSections.end() ) { |
| node = new SectionNode( incompleteStats ); |
| parentNode.childSections.push_back( node ); |
| } |
| else |
| node = *it; |
| } |
| m_sectionStack.push_back( node ); |
| m_deepestSection = node; |
| } |
| |
| virtual void assertionStarting( AssertionInfo const& ) {} |
| |
| virtual bool assertionEnded( AssertionStats const& assertionStats ) { |
| assert( !m_sectionStack.empty() ); |
| SectionNode& sectionNode = *m_sectionStack.back(); |
| sectionNode.assertions.push_back( assertionStats ); |
| return true; |
| } |
| virtual void sectionEnded( SectionStats const& sectionStats ) { |
| assert( !m_sectionStack.empty() ); |
| SectionNode& node = *m_sectionStack.back(); |
| node.stats = sectionStats; |
| m_sectionStack.pop_back(); |
| } |
| virtual void testCaseEnded( TestCaseStats const& testCaseStats ) { |
| Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats ); |
| assert( m_sectionStack.size() == 0 ); |
| node->children.push_back( m_rootSection ); |
| m_testCases.push_back( node ); |
| m_rootSection.reset(); |
| |
| assert( m_deepestSection ); |
| m_deepestSection->stdOut = testCaseStats.stdOut; |
| m_deepestSection->stdErr = testCaseStats.stdErr; |
| } |
| virtual void testGroupEnded( TestGroupStats const& testGroupStats ) { |
| Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats ); |
| node->children.swap( m_testCases ); |
| m_testGroups.push_back( node ); |
| } |
| virtual void testRunEnded( TestRunStats const& testRunStats ) { |
| Ptr<TestRunNode> node = new TestRunNode( testRunStats ); |
| node->children.swap( m_testGroups ); |
| m_testRuns.push_back( node ); |
| testRunEnded(); |
| } |
| virtual void testRunEnded() = 0; |
| |
| Ptr<IConfig> m_config; |
| std::ostream& stream; |
| std::vector<AssertionStats> m_assertions; |
| std::vector<std::vector<Ptr<SectionNode> > > m_sections; |
| std::vector<Ptr<TestCaseNode> > m_testCases; |
| std::vector<Ptr<TestGroupNode> > m_testGroups; |
| |
| std::vector<Ptr<TestRunNode> > m_testRuns; |
| |
| Ptr<SectionNode> m_rootSection; |
| Ptr<SectionNode> m_deepestSection; |
| std::vector<Ptr<SectionNode> > m_sectionStack; |
| |
| }; |
| |
| // Deprecated |
| struct IReporter : IShared { |
| virtual ~IReporter(); |
| |
| virtual bool shouldRedirectStdout() const = 0; |
| |
| virtual void StartTesting() = 0; |
| virtual void EndTesting( Totals const& totals ) = 0; |
| virtual void StartGroup( std::string const& groupName ) = 0; |
| virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; |
| virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; |
| virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; |
| virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; |
| virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; |
| virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; |
| virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; |
| virtual void Aborted() = 0; |
| virtual void Result( AssertionResult const& result ) = 0; |
| }; |
| |
| |
| struct IReporterFactory { |
| virtual ~IReporterFactory(); |
| virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; |
| virtual std::string getDescription() const = 0; |
| }; |
| |
| struct IReporterRegistry { |
| typedef std::map<std::string, IReporterFactory*> FactoryMap; |
| |
| virtual ~IReporterRegistry(); |
| virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig> const& config ) const = 0; |
| virtual FactoryMap const& getFactories() const = 0; |
| }; |
| |
| inline std::string trim( std::string const& str ) { |
| static char const* whitespaceChars = "\n\r\t "; |
| std::string::size_type start = str.find_first_not_of( whitespaceChars ); |
| std::string::size_type end = str.find_last_not_of( whitespaceChars ); |
| |
| return start != std::string::npos ? str.substr( start, 1+end-start ) : ""; |
| } |
| } |
| |
| #endif // TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED |