diff --git a/catch_runner.hpp b/catch_runner.hpp index 6de96e00..b89ddfec 100644 --- a/catch_runner.hpp +++ b/catch_runner.hpp @@ -53,7 +53,7 @@ namespace Catch config.getReporterConfig().setStreamBuf( ofs.rdbuf() ); } - Runner runner( config.getReporter() ); + Runner runner( config ); // Run test specs specified on the command line - or default to all if( config.m_testSpecs.size() == 0 ) diff --git a/internal/catch_capture.hpp b/internal/catch_capture.hpp index 90d69365..b81e03c6 100644 --- a/internal/catch_capture.hpp +++ b/internal/catch_capture.hpp @@ -13,6 +13,7 @@ #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED #include "catch_resultinfo.hpp" +#include "catch_debugger.hpp" #include #include @@ -200,8 +201,20 @@ struct IResultListener virtual void sectionEnded( const std::string& name, std::size_t successes, std::size_t failures ) = 0; virtual void pushScopedInfo( ScopedInfo* scopedInfo ) = 0; virtual void popScopedInfo( ScopedInfo* scopedInfo ) = 0; + virtual bool shouldDebugBreak() const = 0; }; + + struct ResultAction + { + enum Value + { + None, + Failed = 1, // Failure - but no debug break if Debug bit not set + DebugFailed = 3 // Indicates that the debugger should break, if possible + }; + }; + class ResultsCapture { private: @@ -230,20 +243,12 @@ public: instance().currentResult = resultInfo; } - static void acceptResult( bool result, bool stopOnFail ) + static ResultAction::Value acceptResult( bool result ) { - acceptResult( result ? ResultWas::Ok : ResultWas::ExpressionFailed, stopOnFail ); + return acceptResult( result ? ResultWas::Ok : ResultWas::ExpressionFailed ); } - static void acceptResult( ResultWas::OfType result, bool stopOnFail ) - { - if( !acceptResult( result ) && stopOnFail ) - { - throw TestFailureException(); - } - } - - static bool acceptResult( ResultWas::OfType result ) + static ResultAction::Value acceptResult( ResultWas::OfType result ) { MutableResultInfo& currentResult = instance().currentResult; currentResult.setResultType( result ); @@ -254,14 +259,14 @@ public: } bool ok = currentResult.ok(); instance().currentResult = MutableResultInfo(); - return ok; + if( ok ) + return ResultAction::None; + else if( instance().m_listener->shouldDebugBreak() ) + return ResultAction::DebugFailed; + else + return ResultAction::Failed; } - static bool acceptResult( bool expressionResult ) - { - return acceptResult( expressionResult ? ResultWas::Ok : ResultWas::ExpressionFailed ); - } - static void acceptMessage( const std::string& msg ) { instance().currentResult.setMessage( msg ); @@ -367,27 +372,34 @@ inline std::string toString( const Approx& value ) } // end namespace Catch +#define INTERNAL_CATCH_ACCEPT_RESULT( result, stopOnFailure ) \ + if( Catch::ResultAction::Value action = Catch::ResultsCapture::acceptResult( result ) ) \ + { \ + if( action == Catch::ResultAction::DebugFailed ) DebugBreak(); \ + if( stopOnFailure ) throw Catch::TestFailureException(); \ + } + #define INTERNAL_CATCH_TEST( expr, isNot, stopOnFailure, macroName ) \ Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, isNot, __FILE__, __LINE__, macroName )->*expr ); \ - Catch::ResultsCapture::acceptResult( expr, stopOnFailure ); + INTERNAL_CATCH_ACCEPT_RESULT( expr, stopOnFailure ) #define INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, false, __FILE__, __LINE__, macroName ) ); \ try \ { \ expr; \ - Catch::ResultsCapture::acceptResult( nothrow, stopOnFailure ); \ + INTERNAL_CATCH_ACCEPT_RESULT( nothrow, stopOnFailure ) \ } \ catch( exceptionType ) \ { \ - Catch::ResultsCapture::acceptResult( !(nothrow), stopOnFailure ); \ + INTERNAL_CATCH_ACCEPT_RESULT( !(nothrow), stopOnFailure ) \ } #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ catch( ... ) \ { \ - Catch::ResultsCapture::acceptResult( false, stopOnFailure ); \ + INTERNAL_CATCH_ACCEPT_RESULT( false, stopOnFailure ) \ } #define INTERNAL_CATCH_MSG( reason, resultType, stopOnFailure, macroName ) \ @@ -395,7 +407,7 @@ catch( ... ) \ INTERNAL_CATCH_UNIQUE_NAME( strm ) << reason; \ Catch::ResultsCapture::acceptExpression( Catch::MutableResultInfo( "", false, __FILE__, __LINE__, macroName ) ); \ Catch::ResultsCapture::acceptMessage( INTERNAL_CATCH_UNIQUE_NAME( strm ).str() ); \ - Catch::ResultsCapture::acceptResult( resultType, stopOnFailure ); + INTERNAL_CATCH_ACCEPT_RESULT( resultType, stopOnFailure ) \ #define INTERNAL_CATCH_SCOPED_INFO( log ) Catch::ScopedInfo INTERNAL_CATCH_UNIQUE_NAME( info ); INTERNAL_CATCH_UNIQUE_NAME( info ) << log diff --git a/internal/catch_commandline.hpp b/internal/catch_commandline.hpp index 0c10dead..f2865515 100644 --- a/internal/catch_commandline.hpp +++ b/internal/catch_commandline.hpp @@ -25,6 +25,7 @@ namespace Catch // -r --report // -o --output filename to write to // -s --success report successful cases too + // -b --break breaks into debugger on test failure class ArgParser : NonCopyable { enum Mode @@ -35,6 +36,7 @@ namespace Catch modeReport, modeOutput, modeSuccess, + modeBreak, modeError }; @@ -59,6 +61,8 @@ namespace Catch changeMode( cmd, modeOutput ); else if( cmd == "-s" || cmd == "--success" ) changeMode( cmd, modeSuccess ); + else if( cmd == "-b" || cmd == "--break" ) + changeMode( cmd, modeBreak ); } else { @@ -148,6 +152,11 @@ namespace Catch return setErrorMode( m_command + " does not accept arguments" ); m_config.setIncludeAll( true ); break; + case modeBreak: + if( m_args.size() != 0 ) + return setErrorMode( m_command + " does not accept arguments" ); + m_config.setShouldDebugBreak( true ); + break; } m_args.clear(); m_mode = mode; diff --git a/internal/catch_debugger.hpp b/internal/catch_debugger.hpp new file mode 100644 index 00000000..54fc8f1b --- /dev/null +++ b/internal/catch_debugger.hpp @@ -0,0 +1,87 @@ +/* + * catch_debugger.hpp + * Catch + * + * Created by Phil on 27/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) + * + * Provides a DebugBreak() macro for platforms other than Windows + * (currently only Macs are supported) + */ + +#ifndef TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#if defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + + #include + #include + #include + #include + #include + + namespace Catch + { + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + inline bool AmIBeingDebugged(void) + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + { + int junk; + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + assert(junk == 0); + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } + + // The following code snippet taken from: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #ifdef DEBUG + #if __ppc64__ || __ppc__ + #define DebugBreak() \ + if( Catch::AmIBeingDebugged() ) \ + { \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ); \ + } + #else + #define DebugBreak() if( Catch::AmIBeingDebugged() ) {__asm__("int $3\n" : : );} + #endif + #endif + +#endif + +#ifndef DebugBreak + #define DebugBreak() +#endif + + +#endif // TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED \ No newline at end of file diff --git a/internal/catch_runner_impl.hpp b/internal/catch_runner_impl.hpp index 86b85ab6..747299ab 100644 --- a/internal/catch_runner_impl.hpp +++ b/internal/catch_runner_impl.hpp @@ -13,6 +13,7 @@ #define TWOBLUECUBES_INTERNAL_CATCH_RUNNER_HPP_INCLUDED #include "catch_reporter_registry.hpp" +#include "catch_runnerconfig.hpp" #include "catch_registry.hpp" #include "catch_capture.hpp" @@ -72,10 +73,11 @@ namespace Catch class Runner : public IResultListener { public: - explicit Runner( ITestReporter* reporter ) + explicit Runner( const RunnerConfig& config ) : m_successes( 0 ), m_failures( 0 ), - m_reporter( reporter ) + m_config( config ), + m_reporter( m_config.getReporter() ) { m_reporter->StartTesting(); } @@ -189,8 +191,13 @@ namespace Catch if( m_scopedInfos.back() == scopedInfo ) m_scopedInfos.pop_back(); } + virtual bool shouldDebugBreak() const + { + return m_config.shouldDebugBreak(); + } private: + const RunnerConfig& m_config; std::size_t m_successes; std::size_t m_failures; ITestReporter* m_reporter; diff --git a/internal/catch_runnerconfig.hpp b/internal/catch_runnerconfig.hpp index 2eb38e4e..ea18d2f6 100644 --- a/internal/catch_runnerconfig.hpp +++ b/internal/catch_runnerconfig.hpp @@ -44,7 +44,8 @@ namespace Catch RunnerConfig() : m_listSpec( listNone ), - m_reporter( NULL ) + m_reporter( NULL ), + m_shouldDebugBreak( false ) {} void setReporterInfo( const std::string& reporterName ) @@ -90,7 +91,7 @@ namespace Catch return m_reporter.get(); } - const ITestReporter* getReporter() const + ITestReporter* getReporter() const { return const_cast( this )->getReporter(); } @@ -113,6 +114,14 @@ namespace Catch { m_reporterConfig.setIncludeWhat( includeAll ? ReporterConfig::Include::SuccessfulResults : ReporterConfig::Include::FailedOnly ); } + void setShouldDebugBreak( bool shouldDebugBreak ) + { + m_shouldDebugBreak = shouldDebugBreak; + } + bool shouldDebugBreak() const + { + return m_shouldDebugBreak; + } std::auto_ptr m_reporter; std::string m_filename; @@ -120,6 +129,7 @@ namespace Catch std::string m_message; ListInfo m_listSpec; std::vector m_testSpecs; + bool m_shouldDebugBreak; }; } // end namespace Catch