mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-31 20:27:11 +01:00 
			
		
		
		
	Added ability to break into debugger on Windows and Mac
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Phil Nash
					Phil Nash