mirror of
https://github.com/catchorg/Catch2.git
synced 2024-11-22 21:36:11 +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() );
|
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 )
|
||||||
|
@ -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,12 +259,12 @@ 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() )
|
||||||
static bool acceptResult( bool expressionResult )
|
return ResultAction::DebugFailed;
|
||||||
{
|
else
|
||||||
return acceptResult( expressionResult ? ResultWas::Ok : ResultWas::ExpressionFailed );
|
return ResultAction::Failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void acceptMessage( const std::string& msg )
|
static void acceptMessage( const std::string& 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
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
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
|
#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;
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user