Added ability to break into debugger on Windows and Mac

This commit is contained in:
Phil Nash 2010-12-27 20:49:19 +00:00
parent 0025668584
commit 53da26eef1
6 changed files with 152 additions and 27 deletions

View File

@ -53,7 +53,7 @@ namespace Catch
config.getReporterConfig().setStreamBuf( ofs.rdbuf() ); config.getReporterConfig().setStreamBuf( ofs.rdbuf() );
} }
Runner runner( config.getReporter() ); Runner runner( config );
// Run test specs specified on the command line - or default to all // Run test specs specified on the command line - or default to all
if( config.m_testSpecs.size() == 0 ) if( config.m_testSpecs.size() == 0 )

View File

@ -13,6 +13,7 @@
#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED #define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
#include "catch_resultinfo.hpp" #include "catch_resultinfo.hpp"
#include "catch_debugger.hpp"
#include <sstream> #include <sstream>
#include <cmath> #include <cmath>
@ -200,8 +201,20 @@ struct IResultListener
virtual void sectionEnded( const std::string& name, std::size_t successes, std::size_t failures ) = 0; virtual void sectionEnded( const std::string& name, std::size_t successes, std::size_t failures ) = 0;
virtual void pushScopedInfo( ScopedInfo* scopedInfo ) = 0; virtual void pushScopedInfo( ScopedInfo* scopedInfo ) = 0;
virtual void popScopedInfo( 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 class ResultsCapture
{ {
private: private:
@ -230,20 +243,12 @@ public:
instance().currentResult = resultInfo; 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 ) static ResultAction::Value acceptResult( ResultWas::OfType result )
{
if( !acceptResult( result ) && stopOnFail )
{
throw TestFailureException();
}
}
static bool acceptResult( ResultWas::OfType result )
{ {
MutableResultInfo& currentResult = instance().currentResult; MutableResultInfo& currentResult = instance().currentResult;
currentResult.setResultType( result ); currentResult.setResultType( result );
@ -254,14 +259,14 @@ public:
} }
bool ok = currentResult.ok(); bool ok = currentResult.ok();
instance().currentResult = MutableResultInfo(); 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 ) static void acceptMessage( const std::string& msg )
{ {
instance().currentResult.setMessage( msg ); instance().currentResult.setMessage( msg );
@ -367,27 +372,34 @@ inline std::string toString<Approx>( const Approx& value )
} // end namespace Catch } // 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 ) \ #define INTERNAL_CATCH_TEST( expr, isNot, stopOnFailure, macroName ) \
Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, isNot, __FILE__, __LINE__, macroName )->*expr ); \ 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 ) \ #define INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \
Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, false, __FILE__, __LINE__, macroName ) ); \ Catch::ResultsCapture::acceptExpression( Catch::ResultBuilder( #expr, false, __FILE__, __LINE__, macroName ) ); \
try \ try \
{ \ { \
expr; \ expr; \
Catch::ResultsCapture::acceptResult( nothrow, stopOnFailure ); \ INTERNAL_CATCH_ACCEPT_RESULT( nothrow, stopOnFailure ) \
} \ } \
catch( exceptionType ) \ catch( exceptionType ) \
{ \ { \
Catch::ResultsCapture::acceptResult( !(nothrow), stopOnFailure ); \ INTERNAL_CATCH_ACCEPT_RESULT( !(nothrow), stopOnFailure ) \
} }
#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ #define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \
INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \ INTERNAL_CATCH_THROWS( expr, exceptionType, nothrow, stopOnFailure, macroName ) \
catch( ... ) \ catch( ... ) \
{ \ { \
Catch::ResultsCapture::acceptResult( false, stopOnFailure ); \ INTERNAL_CATCH_ACCEPT_RESULT( false, stopOnFailure ) \
} }
#define INTERNAL_CATCH_MSG( reason, resultType, stopOnFailure, macroName ) \ #define INTERNAL_CATCH_MSG( reason, resultType, stopOnFailure, macroName ) \
@ -395,7 +407,7 @@ catch( ... ) \
INTERNAL_CATCH_UNIQUE_NAME( strm ) << reason; \ INTERNAL_CATCH_UNIQUE_NAME( strm ) << reason; \
Catch::ResultsCapture::acceptExpression( Catch::MutableResultInfo( "", false, __FILE__, __LINE__, macroName ) ); \ Catch::ResultsCapture::acceptExpression( Catch::MutableResultInfo( "", false, __FILE__, __LINE__, macroName ) ); \
Catch::ResultsCapture::acceptMessage( INTERNAL_CATCH_UNIQUE_NAME( strm ).str() ); \ 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 #define INTERNAL_CATCH_SCOPED_INFO( log ) Catch::ScopedInfo INTERNAL_CATCH_UNIQUE_NAME( info ); INTERNAL_CATCH_UNIQUE_NAME( info ) << log

View File

@ -25,6 +25,7 @@ namespace Catch
// -r --report <type> // -r --report <type>
// -o --output filename to write to // -o --output filename to write to
// -s --success report successful cases too // -s --success report successful cases too
// -b --break breaks into debugger on test failure
class ArgParser : NonCopyable class ArgParser : NonCopyable
{ {
enum Mode enum Mode
@ -35,6 +36,7 @@ namespace Catch
modeReport, modeReport,
modeOutput, modeOutput,
modeSuccess, modeSuccess,
modeBreak,
modeError modeError
}; };
@ -59,6 +61,8 @@ namespace Catch
changeMode( cmd, modeOutput ); changeMode( cmd, modeOutput );
else if( cmd == "-s" || cmd == "--success" ) else if( cmd == "-s" || cmd == "--success" )
changeMode( cmd, modeSuccess ); changeMode( cmd, modeSuccess );
else if( cmd == "-b" || cmd == "--break" )
changeMode( cmd, modeBreak );
} }
else else
{ {
@ -148,6 +152,11 @@ namespace Catch
return setErrorMode( m_command + " does not accept arguments" ); return setErrorMode( m_command + " does not accept arguments" );
m_config.setIncludeAll( true ); m_config.setIncludeAll( true );
break; 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_args.clear();
m_mode = mode; m_mode = mode;

View File

@ -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 <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>
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

View File

@ -13,6 +13,7 @@
#define TWOBLUECUBES_INTERNAL_CATCH_RUNNER_HPP_INCLUDED #define TWOBLUECUBES_INTERNAL_CATCH_RUNNER_HPP_INCLUDED
#include "catch_reporter_registry.hpp" #include "catch_reporter_registry.hpp"
#include "catch_runnerconfig.hpp"
#include "catch_registry.hpp" #include "catch_registry.hpp"
#include "catch_capture.hpp" #include "catch_capture.hpp"
@ -72,10 +73,11 @@ namespace Catch
class Runner : public IResultListener class Runner : public IResultListener
{ {
public: public:
explicit Runner( ITestReporter* reporter ) explicit Runner( const RunnerConfig& config )
: m_successes( 0 ), : m_successes( 0 ),
m_failures( 0 ), m_failures( 0 ),
m_reporter( reporter ) m_config( config ),
m_reporter( m_config.getReporter() )
{ {
m_reporter->StartTesting(); m_reporter->StartTesting();
} }
@ -189,8 +191,13 @@ namespace Catch
if( m_scopedInfos.back() == scopedInfo ) if( m_scopedInfos.back() == scopedInfo )
m_scopedInfos.pop_back(); m_scopedInfos.pop_back();
} }
virtual bool shouldDebugBreak() const
{
return m_config.shouldDebugBreak();
}
private: private:
const RunnerConfig& m_config;
std::size_t m_successes; std::size_t m_successes;
std::size_t m_failures; std::size_t m_failures;
ITestReporter* m_reporter; ITestReporter* m_reporter;

View File

@ -44,7 +44,8 @@ namespace Catch
RunnerConfig() RunnerConfig()
: m_listSpec( listNone ), : m_listSpec( listNone ),
m_reporter( NULL ) m_reporter( NULL ),
m_shouldDebugBreak( false )
{} {}
void setReporterInfo( const std::string& reporterName ) void setReporterInfo( const std::string& reporterName )
@ -90,7 +91,7 @@ namespace Catch
return m_reporter.get(); return m_reporter.get();
} }
const ITestReporter* getReporter() const ITestReporter* getReporter() const
{ {
return const_cast<RunnerConfig*>( this )->getReporter(); return const_cast<RunnerConfig*>( this )->getReporter();
} }
@ -113,6 +114,14 @@ namespace Catch
{ {
m_reporterConfig.setIncludeWhat( includeAll ? ReporterConfig::Include::SuccessfulResults : ReporterConfig::Include::FailedOnly ); 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<ITestReporter> m_reporter; std::auto_ptr<ITestReporter> m_reporter;
std::string m_filename; std::string m_filename;
@ -120,6 +129,7 @@ namespace Catch
std::string m_message; std::string m_message;
ListInfo m_listSpec; ListInfo m_listSpec;
std::vector<std::string> m_testSpecs; std::vector<std::string> m_testSpecs;
bool m_shouldDebugBreak;
}; };
} // end namespace Catch } // end namespace Catch