diff --git a/include/internal/catch_config.hpp b/include/internal/catch_config.hpp index e23f40b8..85961890 100644 --- a/include/internal/catch_config.hpp +++ b/include/internal/catch_config.hpp @@ -56,8 +56,7 @@ namespace Catch /////////////////////////////////////////////////////////////////////////// Config() - : m_reporter( NULL ), - m_listSpec( List::None ), + : m_listSpec( List::None ), m_shouldDebugBreak( false ), m_showHelp( false ), m_streambuf( NULL ), @@ -137,15 +136,15 @@ namespace Catch /////////////////////////////////////////////////////////////////////////// void setReporter( IReporter* reporter ) { - m_reporter = std::auto_ptr( reporter ); + m_reporter = reporter; } /////////////////////////////////////////////////////////////////////////// - IReporter* getReporter() const + Ptr getReporter() { if( !m_reporter.get() ) const_cast( this )->setReporter( Hub::getReporterRegistry().create( "basic", *this ) ); - return m_reporter.get(); + return m_reporter; } /////////////////////////////////////////////////////////////////////////// @@ -221,7 +220,7 @@ namespace Catch setStreamBuf( newBuf ); delete m_streambuf; m_streambuf = newBuf; - } + } /////////////////////////////////////////////////////////////////////////// virtual bool includeSuccessfulResults() const @@ -230,7 +229,7 @@ namespace Catch } private: - std::auto_ptr m_reporter; + Ptr m_reporter; std::string m_filename; std::string m_message; List::What m_listSpec; diff --git a/include/internal/catch_interfaces_reporter.h b/include/internal/catch_interfaces_reporter.h index f7690d5e..fd0e87bf 100644 --- a/include/internal/catch_interfaces_reporter.h +++ b/include/internal/catch_interfaces_reporter.h @@ -15,6 +15,7 @@ #include "catch_common.h" #include "catch_totals.hpp" +#include "catch_ptr.hpp" #include #include @@ -43,7 +44,7 @@ namespace Catch class ResultInfo; /////////////////////////////////////////////////////////////////////////// - struct IReporter : NonCopyable + struct IReporter : IShared { virtual ~IReporter (){} diff --git a/include/internal/catch_list.hpp b/include/internal/catch_list.hpp index 255df919..e0246057 100644 --- a/include/internal/catch_list.hpp +++ b/include/internal/catch_list.hpp @@ -21,7 +21,7 @@ namespace Catch /////////////////////////////////////////////////////////////////////////// inline int List ( - const Config& config + Config& config ) { if( config.listWhat() & Config::List::Reports ) @@ -54,7 +54,7 @@ namespace Catch return (std::numeric_limits::max)(); } - if( config.getReporter() ) + if( config.getReporter().get() ) { std::cerr << "Reporters ignored when listing" << std::endl; } diff --git a/include/internal/catch_ptr.hpp b/include/internal/catch_ptr.hpp new file mode 100644 index 00000000..97cc5074 --- /dev/null +++ b/include/internal/catch_ptr.hpp @@ -0,0 +1,100 @@ +/* + * catch_ptr.hpp + * Catch + * + * Created by Phil on 02/05/2012. + * Copyright 2012 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_PTR_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#include "catch_common.h" + +namespace Catch +{ + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr + { + public: + Ptr() : m_p( NULL ){} + Ptr( T* p ) : m_p( p ){ + m_p->addRef(); + } + Ptr( const Ptr& other ) : m_p( other.m_p ){ + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ){ + std::swap( m_p, other.m_p ); + } + + T* get(){ + return m_p; + } + const T* get() const{ + return m_p; + } + + T& operator*(){ + return *m_p; + } + const T& operator*() const{ + return *m_p; + } + + T* operator->(){ + return m_p; + } + const T* operator->() const{ + return m_p; + } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(){} + virtual void addRef() = 0; + virtual void release() = 0; + }; + + template + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef(){ + ++m_rc; + } + virtual void release(){ + if( --m_rc == 0 ) + delete this; + } + + int m_rc; + }; + +} // end namespace Catch + +#endif // TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED diff --git a/include/internal/catch_resultinfo.hpp b/include/internal/catch_resultinfo.hpp index 1e504e4a..9119bd44 100644 --- a/include/internal/catch_resultinfo.hpp +++ b/include/internal/catch_resultinfo.hpp @@ -108,6 +108,14 @@ namespace Catch return m_expr; } + /////////////////////////////////////////////////////////////////////////// + bool hasExpandedExpression + () + const + { + return hasExpression() && getExpandedExpressionInternal() != m_expr; + } + /////////////////////////////////////////////////////////////////////////// std::string getExpandedExpression () @@ -162,7 +170,7 @@ namespace Catch else if( m_op != "!" ) return m_lhs + " " + m_op + " " + m_rhs; else - return "{can't expand - use " + m_macroName + "_NOT( " + m_expr.substr(1) + " ) instead of " + m_macroName + "( " + m_expr + " ) for better diagnostics}"; + return "{can't expand - use " + m_macroName + "_FALSE( " + m_expr.substr(1) + " ) instead of " + m_macroName + "( " + m_expr + " ) for better diagnostics}"; } /////////////////////////////////////////////////////////////////////////// diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp index 45bd9e4a..7267f44a 100644 --- a/include/internal/catch_runner_impl.hpp +++ b/include/internal/catch_runner_impl.hpp @@ -300,11 +300,11 @@ namespace Catch /////////////////////////////////////////////////////////////////////////// explicit Runner ( - const Config& config + Config& config ) : m_runningTest( NULL ), m_config( config ), - m_reporter( m_config.getReporter() ), + m_reporter( config.getReporter() ), m_prevRunner( &Hub::getRunner() ), m_prevResultCapture( &Hub::getResultCapture() ) { @@ -376,9 +376,11 @@ namespace Catch { do { + m_reporter->StartGroup( "test case run" ); m_currentResult.setFileAndLine( m_runningTest->getTestCaseInfo().getFilename(), m_runningTest->getTestCaseInfo().getLine() ); runCurrentTest( redirectedCout, redirectedCerr ); + m_reporter->EndGroup( "test case run", m_totals - prevTotals ); } while( m_runningTest->hasUntestedSections() ); } @@ -608,7 +610,7 @@ namespace Catch const Config& m_config; Totals m_totals; - IReporter* m_reporter; + Ptr m_reporter; std::vector m_scopedInfos; std::vector m_info; IRunner* m_prevRunner; diff --git a/include/reporters/catch_reporter_basic.hpp b/include/reporters/catch_reporter_basic.hpp index 6bca6b90..7db72734 100644 --- a/include/reporters/catch_reporter_basic.hpp +++ b/include/reporters/catch_reporter_basic.hpp @@ -38,7 +38,7 @@ namespace Catch std::string m_label; }; - class BasicReporter : public IReporter + class BasicReporter : public SharedImpl { struct SpanInfo { @@ -311,7 +311,7 @@ namespace Catch break; } - if( resultInfo.hasExpression() && resultInfo.getExpression() != resultInfo.getExpandedExpression() ) + if( resultInfo.hasExpandedExpression() ) { m_config.stream() << " for: "; TextColour colour( TextColour::ReconstructedExpression ); diff --git a/include/reporters/catch_reporter_junit.hpp b/include/reporters/catch_reporter_junit.hpp index e2238d73..0314901c 100644 --- a/include/reporters/catch_reporter_junit.hpp +++ b/include/reporters/catch_reporter_junit.hpp @@ -19,7 +19,7 @@ namespace Catch { - class JunitReporter : public Catch::IReporter + class JunitReporter : public SharedImpl { struct TestStats { diff --git a/include/reporters/catch_reporter_xml.hpp b/include/reporters/catch_reporter_xml.hpp index 910932c7..44141019 100644 --- a/include/reporters/catch_reporter_xml.hpp +++ b/include/reporters/catch_reporter_xml.hpp @@ -19,7 +19,7 @@ namespace Catch { - class XmlReporter : public Catch::IReporter + class XmlReporter : public SharedImpl { public: /////////////////////////////////////////////////////////////////////////// @@ -29,7 +29,7 @@ namespace Catch ) : m_config( config ) { - } + } /////////////////////////////////////////////////////////////////////////// static std::string getDescription diff --git a/projects/SelfTest/ExceptionTests.cpp b/projects/SelfTest/ExceptionTests.cpp index 7bc01387..376ce809 100644 --- a/projects/SelfTest/ExceptionTests.cpp +++ b/projects/SelfTest/ExceptionTests.cpp @@ -126,22 +126,22 @@ TEST_CASE( "./failing/exceptions/in-section", "Exceptions thrown from sections r TEST_CASE( "./succeeding/exceptions/error messages", "The error messages produced by exceptions caught by Catch matched the expected form" ) { Catch::EmbeddedRunner runner; + using namespace Catch::Matchers; SECTION( "custom, unexpected", "" ) { runner.runMatching( "./failing/exceptions/custom" ); - INFO( runner.getOutput() ); - CHECK( runner.getOutput().find( "Unexpected exception" ) != std::string::npos ); - CHECK( runner.getOutput().find( "custom exception" ) != std::string::npos ); +// CHECK_THAT( runner.getLog(), Contains( "Unexpected exception" ) ); // Mock reporter doesn't say this + CHECK_THAT( runner.getLog(), Contains( "custom exception" ) ); } SECTION( "in section", "" ) { runner.runMatching( "./failing/exceptions/in-section" ); - INFO( runner.getOutput() ); - CHECK( runner.getOutput().find( "Unexpected exception" ) != std::string::npos ); - CHECK( runner.getOutput().find( "Exception from section" ) != std::string::npos ); - CHECK( runner.getOutput().find( CATCH_GET_LINE_INFO( "the section2" ) ) != std::string::npos ); + INFO( runner.getLog() ); +// CHECK( runner.getLog().find( "Unexpected exception" ) != std::string::npos ); // Mock reporter doesn't say this + CHECK_THAT( runner.getLog(), Contains( "Exception from section" ) ); +// CHECK( runner.getLog().find( CATCH_GET_LINE_INFO( "the section2" ) ) != std::string::npos ); // Mock reporter doesn't say this } } diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp index d4b814a5..d7595140 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -11,6 +11,8 @@ */ #include "catch.hpp" +#include "catch_self_test.hpp" + #include TEST_CASE( "./succeeding/Misc/Sections", "random SECTION tests" ) @@ -70,6 +72,53 @@ TEST_CASE( "./mixed/Misc/Sections/nested2", "nested SECTION tests" ) } } +TEST_CASE( "./Sections/nested/a/b", "nested SECTION tests" ) +{ + SECTION( "c", "" ) + { + SECTION( "d (leaf)", "" ) + { + } + + SECTION( "e (leaf)", "" ) + { + } + } + + SECTION( "f (leaf)", "" ) + { + } +} + +TEST_CASE( "Sections/nested3", "nested SECTION tests" ) +{ + Catch::EmbeddedRunner runner; + + runner.runMatching( "./Sections/nested/a/b", "mock" ); + CHECK( runner.getLog() == + "[tc]( ./Sections/nested/a/b ){ " + + "[g]( test case run ){ " + "[s]( c ){ " + "[s]( d (leaf) ){ } [s]( d (leaf) ) " + "} [s]( c ) " + "} [g]( test case run )" + + "[g]( test case run ){ " + "[s]( c ){ " + "[s]( e (leaf) ){ } [s]( e (leaf) ) " + "} [s]( c ) " + "} [g]( test case run )" + + "[g]( test case run ){ " + "[s]( c ){ } [s]( c ) " + "[s]( f (leaf) ){ } [s]( f (leaf) ) " + "} [g]( test case run ) " + + "} [tc]( ./Sections/nested/a/b )" ); + +} + TEST_CASE( "./mixed/Misc/Sections/loops", "looped SECTION tests" ) { int a = 1; diff --git a/projects/SelfTest/TestMain.cpp b/projects/SelfTest/TestMain.cpp index 97bdb6a2..a95402dc 100644 --- a/projects/SelfTest/TestMain.cpp +++ b/projects/SelfTest/TestMain.cpp @@ -43,7 +43,7 @@ TEST_CASE( "selftest/main", "Runs all Catch self tests and checks their results" "Number of 'succeeding' tests is fixed" ) { runner.runMatching( "./succeeding/*" ); - CHECK( runner.getTotals().assertions.passed == 276 ); + CHECK( runner.getTotals().assertions.passed == 273 ); CHECK( runner.getTotals().assertions.failed == 0 ); } diff --git a/projects/SelfTest/catch_self_test.cpp b/projects/SelfTest/catch_self_test.cpp index 73a4da5f..7c9a3b5c 100644 --- a/projects/SelfTest/catch_self_test.cpp +++ b/projects/SelfTest/catch_self_test.cpp @@ -13,18 +13,18 @@ #define CATCH_CONFIG_MAIN #include "catch_self_test.hpp" -namespace Catch -{ - /////////////////////////////////////////////////////////////////////////// - std::size_t EmbeddedRunner::runMatching - ( - const std::string& rawTestSpec - ) +namespace Catch{ + + std::size_t EmbeddedRunner::runMatching( + const std::string& rawTestSpec, + const std::string& reporter ) { std::ostringstream oss; Config config; config.setStreamBuf( oss.rdbuf() ); - config.setReporter( "basic" ); + + //if( reporter == "mock" ) // !TBD + config.setReporter( m_reporter.get() ); std::size_t result; @@ -37,4 +37,11 @@ namespace Catch m_output = oss.str(); return result; } + + const std::string MockReporter::recordGroups = "[g]"; + const std::string MockReporter::recordTestCases = "[tc]"; + const std::string MockReporter::recordSections =" [s]"; + + INTERNAL_CATCH_REGISTER_REPORTER( "mock", MockReporter ) + } diff --git a/projects/SelfTest/catch_self_test.hpp b/projects/SelfTest/catch_self_test.hpp index 9b7fa1fd..ca180772 100644 --- a/projects/SelfTest/catch_self_test.hpp +++ b/projects/SelfTest/catch_self_test.hpp @@ -14,19 +14,223 @@ #include "catch.hpp" +#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 IReporterConfig& config + ) + { + 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& totals + ) + { + } + + virtual void StartGroup + ( + const std::string& groupName + ) + { + if( shouldRecord( recordGroups ) ) + m_log << recordGroups << "( " << groupName << " ){ "; + } + + virtual void EndGroup + ( + const std::string& groupName, + const Totals& totals + ) + { + if( shouldRecord( recordGroups ) ) + m_log << " } " << recordGroups << "( " << groupName << " )"; + } + + virtual void StartSection + ( + const std::string& sectionName, + const std::string description + ) + { + if( shouldRecord( recordSections ) ) + m_log << recordSections << "( " << sectionName << " ){ "; + } + + virtual void EndSection + ( + const std::string& sectionName, + const Counts& assertions + ) + { + if( shouldRecord( recordSections ) ) + m_log << " } " << recordSections << "( " << sectionName << " )"; + } + + virtual void StartTestCase + ( + const TestCaseInfo& testInfo + ) + { + if( shouldRecord( recordTestCases ) ) + m_log << recordTestCases << "( " << testInfo.getName() << " ){ "; + } + + virtual void EndTestCase + ( + const TestCaseInfo& testInfo, + const Totals& totals, + const std::string& stdOut, + const std::string& stdErr + ) + { + if( shouldRecord( recordTestCases ) ) + m_log << " } " << recordTestCases << "( " << testInfo.getName() << " )"; + } + + virtual void Result + ( + const ResultInfo& resultInfo + ) + { + if( resultInfo.getResultType() == ResultWas::Ok ) + return; + + + switch( resultInfo.getResultType() ) + { + case ResultWas::Info: + m_log << "Info"; + break; + case ResultWas::Warning: + m_log << "Warning"; + break; + case ResultWas::ExplicitFailure: + m_log << "ExplicitFailure"; + break; + case ResultWas::ExpressionFailed: + m_log << "ExpressionFailed"; + break; + case ResultWas::Unknown: + m_log << "Unknown"; + break; + case ResultWas::ThrewException: + m_log << "ThrewException"; + break; + case ResultWas::DidntThrowException: + m_log << "DidntThrowException"; + break; + + // We shouldn't ever see these + case ResultWas::Ok: + m_log << "Ok"; + break; + case ResultWas::FailureBit: + m_log << "FailureBit"; + break; + case ResultWas::Exception: + m_log << "Exception"; + break; + default: + m_log << "{unrecognised ResultType enum value}"; + break; + } + + if( resultInfo.hasExpression() ) + m_log << resultInfo.getExpression(); + + if( resultInfo.hasMessage() ) + m_log << "'" << resultInfo.getMessage() << "'"; + + if( resultInfo.hasExpandedExpression() ) + m_log << resultInfo.getExpandedExpression(); + } + + private: + + bool shouldRecord( const std::string& recorder ) const + { + return m_recorders.find( recorder ) != m_recorders.end(); + } + 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& rawTestSpec, + const std::string& reporter = "basic" ); /////////////////////////////////////////////////////////////////////////// std::string getOutput @@ -42,10 +246,23 @@ namespace Catch { 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 @@ -60,6 +277,15 @@ namespace Catch }; }; + /////////////////////////////////////////////////////////////////////////// + MetaTestRunner + ( + Expected::Result expectedResult + ) + : m_expectedResult( expectedResult ) + { + } + /////////////////////////////////////////////////////////////////////////// static void runMatching ( @@ -71,15 +297,6 @@ namespace Catch MetaTestRunner( expectedResult ) ); } - /////////////////////////////////////////////////////////////////////////// - MetaTestRunner - ( - Expected::Result expectedResult - ) - : m_expectedResult( expectedResult ) - { - } - /////////////////////////////////////////////////////////////////////////// void operator() ( @@ -163,6 +380,7 @@ namespace Catch LineInfoRegistry::get().registerLineInfo( name, lineInfo ); } }; + } #define CATCH_REGISTER_LINE_INFO( name ) ::Catch::LineInfoRegistrar INTERNAL_CATCH_UNIQUE_NAME( lineRegistrar )( name, ::Catch::SourceLineInfo( __FILE__, __LINE__ ) ); diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj index d3cc91a0..44613af2 100644 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj +++ b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj @@ -87,6 +87,7 @@ 4A7ADB4314F631E10094FE10 /* catch_totals.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_totals.hpp; sourceTree = ""; }; 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_console_colour_impl.hpp; sourceTree = ""; }; 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_console_colour.hpp; sourceTree = ""; }; + 4AB77CB51551AEA200857BF0 /* catch_ptr.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_ptr.hpp; sourceTree = ""; }; 4AE1840A14EE4F230066340D /* catch_self_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = catch_self_test.cpp; path = ../../../SelfTest/catch_self_test.cpp; sourceTree = ""; }; /* End PBXFileReference section */ @@ -195,6 +196,7 @@ 4AB1C73514F97BDA00F31DF7 /* catch_console_colour_impl.hpp */, 4AB1C73714F97C1300F31DF7 /* catch_console_colour.hpp */, 4A3D7DD01503869D005F9203 /* catch_matchers.hpp */, + 4AB77CB51551AEA200857BF0 /* catch_ptr.hpp */, ); name = internal; path = ../../../../include/internal; @@ -237,7 +239,7 @@ 4A6D0C17149B3D3B00DB3EAA /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0420; + LastUpgradeCheck = 0430; }; buildConfigurationList = 4A6D0C1A149B3D3B00DB3EAA /* Build configuration list for PBXProject "CatchSelfTest" */; compatibilityVersion = "Xcode 3.2"; diff --git a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/xcuserdata/Phil.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist b/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/xcuserdata/Phil.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist deleted file mode 100644 index 3cc315ed..00000000 --- a/projects/XCode4/CatchSelfTest/CatchSelfTest.xcodeproj/xcuserdata/Phil.xcuserdatad/xcdebugger/Breakpoints.xcbkptlist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - -