/* * Created by Phil on 14/01/2011. * Copyright 2011 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_SELF_TEST_HPP_INCLUDED #define TWOBLUECUBES_CATCH_SELF_TEST_HPP_INCLUDED #include "catch.hpp" #include "catch_interfaces_static_registries.h" #include "set" namespace Catch { class MockReporter : public SharedImpl { public: static const std::string recordGroups; static const std::string recordTestCases; static const std::string recordSections; void recordAll() { addRecorder( recordGroups ); addRecorder( recordTestCases ); addRecorder( recordSections ); } MockReporter( const ReporterConfig& ) { recordAll(); } MockReporter() { recordAll(); } void addRecorder( const std::string& recorder ) { m_recorders.insert( recorder ); } static std::string getDescription() { return "mock reporter"; } std::string getLog() const { return m_log.str(); } private: // IReporter virtual bool shouldRedirectStdout() const { return false; } virtual void StartTesting() {} virtual void EndTesting( const Totals& ) {} virtual void StartGroup( const std::string& groupName ) { openLabel( recordGroups, groupName ); } virtual void EndGroup( const std::string& groupName, const Totals& ) { closeLabel( recordGroups, groupName ); } virtual void StartSection( const std::string& sectionName, const std::string& ) { openLabel( recordSections, sectionName ); } virtual void EndSection( const std::string& sectionName, const Counts& ) { closeLabel( recordSections, sectionName ); } virtual void StartTestCase( const TestCaseInfo& testInfo ) { openLabel( recordTestCases, testInfo.getName() ); } virtual void Aborted(){} virtual void EndTestCase( const TestCaseInfo& testInfo, const Totals&, const std::string&, const std::string& ) { closeLabel( recordTestCases, testInfo.getName() ); } virtual void Result( const ResultInfo& resultInfo ); private: bool shouldRecord( const std::string& recorder ) const { return m_recorders.find( recorder ) != m_recorders.end(); } void openLabel( const std::string& label, const std::string& arg = "" ); void closeLabel( const std::string& label, const std::string& arg = "" ); std::string m_indent; std::ostringstream m_log; std::set m_recorders; }; class EmbeddedRunner { public: EmbeddedRunner() : m_reporter( new MockReporter() ) {} std::size_t runMatching( const std::string& rawTestSpec, const std::string& reporter = "basic" ); std::string getOutput() { return m_output; } const Totals& getTotals() const { return m_totals; } void addRecorder( const std::string& recorder ) { m_reporter->addRecorder( recorder ); } std::string getLog() const { return m_reporter->getLog(); } private: Totals m_totals; std::string m_output; Ptr m_reporter; }; class MetaTestRunner { public: struct Expected { enum Result { ToSucceed, ToFail }; }; MetaTestRunner( Expected::Result expectedResult ) : m_expectedResult( expectedResult ) {} static void runMatching( const std::string& testSpec, Expected::Result expectedResult ) { forEach( getStatics().getTestCaseRegistry().getMatchingTestCases( testSpec ), MetaTestRunner( expectedResult ) ); } void operator()( const TestCaseInfo& testCase ) { EmbeddedRunner runner; runner.runMatching( testCase.getName() ); Totals totals = runner.getTotals(); switch( m_expectedResult ) { case Expected::ToSucceed: if( totals.assertions.failed > 0 ) { INFO( runner.getOutput() ); FAIL( "Expected test case '" << testCase.getName() << "' to succeed but there was/ were " << totals.assertions.failed << " failure(s)" ); } break; case Expected::ToFail: if( totals.assertions.passed > 0 ) { INFO( runner.getOutput() ); FAIL( "Expected test case '" << testCase.getName() << "' to fail but there was/ were " << totals.assertions.passed << " success(es)" ); } break; } }; private: Expected::Result m_expectedResult; }; struct LineInfoRegistry { static LineInfoRegistry& get() { static LineInfoRegistry s_instance; return s_instance; } void registerLineInfo( const std::string& name, const SourceLineInfo& info ) { m_registry.insert( std::make_pair( name, info ) ); } const SourceLineInfo* find( const std::string& name ) const { std::map::const_iterator it = m_registry.find( name ); return it == m_registry.end() ? NULL : &(it->second); } const std::string infoForName( const std::string& name ) const { std::map::const_iterator it = m_registry.find( name ); if( it == m_registry.end() ) return ""; std::ostringstream oss; oss << it->second; return oss.str(); } std::map m_registry; }; struct LineInfoRegistrar { LineInfoRegistrar( const char* name, const SourceLineInfo& lineInfo ) { LineInfoRegistry::get().registerLineInfo( name, lineInfo ); } }; } #define CATCH_REGISTER_LINE_INFO( name ) ::Catch::LineInfoRegistrar INTERNAL_CATCH_UNIQUE_NAME( lineRegistrar )( name, ::Catch::SourceLineInfo( __FILE__, __LINE__ ) ); #define CATCH_GET_LINE_INFO( name ) ::Catch::LineInfoRegistry::get().infoForName( name ) #endif // TWOBLUECUBES_CATCH_SELF_TEST_HPP_INCLUDED