mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 03:43:28 +01:00
Added ability to break into debugger on Windows and Mac
This commit is contained in:
parent
0025668584
commit
53da26eef1
@ -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 )
|
||||
|
@ -13,6 +13,7 @@
|
||||
#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
|
||||
|
||||
#include "catch_resultinfo.hpp"
|
||||
#include "catch_debugger.hpp"
|
||||
#include <sstream>
|
||||
#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 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<Approx>( 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
|
||||
|
||||
|
@ -25,6 +25,7 @@ namespace Catch
|
||||
// -r --report <type>
|
||||
// -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;
|
||||
|
87
internal/catch_debugger.hpp
Normal file
87
internal/catch_debugger.hpp
Normal 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
|
@ -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;
|
||||
|
@ -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<RunnerConfig*>( 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<ITestReporter> m_reporter;
|
||||
std::string m_filename;
|
||||
@ -120,6 +129,7 @@ namespace Catch
|
||||
std::string m_message;
|
||||
ListInfo m_listSpec;
|
||||
std::vector<std::string> m_testSpecs;
|
||||
bool m_shouldDebugBreak;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
Loading…
Reference in New Issue
Block a user