Added signal handlers (and placeholder for SEH handlers)

- based on PR 232 (https://github.com/philsquared/Catch/pull/232 - thanks Lukasz Forynski)
- Writes to reporter, so gets all the usual context, but then exits directly (since the stack cannot be resumed) so no summary
- On Windows does nothing, as yet.
This commit is contained in:
Phil Nash 2014-08-22 08:07:39 +01:00
parent aa31d1c275
commit c1a8e1c5dd
10 changed files with 128 additions and 8 deletions

View File

@ -0,0 +1,78 @@
/*
* Created by Phil on 21/08/2014
* Copyright 2014 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)
*
*/
#ifndef TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
namespace Catch {
// Report the error condition then exit the process
inline void fatal( std::string const& message, int exitCode ) {
IContext& context = Catch::getCurrentContext();
IResultCapture* resultCapture = context.getResultCapture();
ResultBuilder resultBuilder = resultCapture->makeUnexpectedResultBuilder();
resultBuilder.setResultType( ResultWas::FatalErrorCondition );
resultBuilder << message;
resultBuilder.captureExpression();
if( Catch::alwaysTrue() ) // avoids "no return" warnings
exit( exitCode );
}
} // namespace Catch
#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
namespace Catch {
struct FatalConditionHandler {};
} // namespace Catch
#else // Not Windows - assumed to be POSIX compatible //////////////////////////
#include <signal.h>
namespace Catch {
struct SignalDefs { int id; const char* name; };
extern SignalDefs signalDefs[];
SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" },
{ SIGFPE, "SIGFPE - Floating point error signal" },
{ SIGSEGV, "SIGSEGV - Segmentation violation signal" },
{ SIGTERM, "SIGTERM - Termination request signal" },
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
};
struct FatalConditionHandler {
static void handleSignal( int sig ) {
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
if( sig == signalDefs[i].id )
fatal( signalDefs[i].name, -sig );
fatal( "<unknown signal>", -sig );
}
FatalConditionHandler() {
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
signal( signalDefs[i].id, handleSignal );
}
~FatalConditionHandler() {
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i )
signal( signalDefs[i].id, SIG_DFL );
}
};
} // namespace Catch
#endif // not Windows
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED

View File

@ -21,6 +21,7 @@ namespace Catch {
struct MessageInfo; struct MessageInfo;
class ScopedMessageBuilder; class ScopedMessageBuilder;
struct Counts; struct Counts;
class ResultBuilder;
struct IResultCapture { struct IResultCapture {
@ -35,6 +36,8 @@ namespace Catch {
virtual std::string getCurrentTestName() const = 0; virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0; virtual const AssertionResult* getLastResult() const = 0;
virtual ResultBuilder makeUnexpectedResultBuilder() const = 0;
}; };
IResultCapture& getResultCapture(); IResultCapture& getResultCapture();

View File

@ -25,7 +25,9 @@ namespace Catch {
Exception = 0x100 | FailureBit, Exception = 0x100 | FailureBit,
ThrewException = Exception | 1, ThrewException = Exception | 1,
DidntThrowException = Exception | 2 DidntThrowException = Exception | 2,
FatalErrorCondition = 0x200 | FailureBit
}; }; }; };

View File

@ -20,6 +20,7 @@
#include "catch_test_case_tracker.hpp" #include "catch_test_case_tracker.hpp"
#include "catch_timer.h" #include "catch_timer.h"
#include "catch_result_builder.h" #include "catch_result_builder.h"
#include "catch_fatal_condition.hpp"
#include <set> #include <set>
#include <string> #include <string>
@ -215,6 +216,13 @@ namespace Catch {
return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() ); return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
} }
virtual ResultBuilder makeUnexpectedResultBuilder() const {
return ResultBuilder( m_lastAssertionInfo.macroName.c_str(),
m_lastAssertionInfo.lineInfo,
m_lastAssertionInfo.capturedExpression.c_str(),
m_lastAssertionInfo.resultDisposition );
}
private: private:
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
@ -232,10 +240,10 @@ namespace Catch {
if( m_reporter->getPreferences().shouldRedirectStdOut ) { if( m_reporter->getPreferences().shouldRedirectStdOut ) {
StreamRedirect coutRedir( std::cout, redirectedCout ); StreamRedirect coutRedir( std::cout, redirectedCout );
StreamRedirect cerrRedir( std::cerr, redirectedCerr ); StreamRedirect cerrRedir( std::cerr, redirectedCerr );
m_activeTestCase->invoke(); invokeActiveTestCase();
} }
else { else {
m_activeTestCase->invoke(); invokeActiveTestCase();
} }
duration = timer.getElapsedSeconds(); duration = timer.getElapsedSeconds();
} }
@ -243,11 +251,7 @@ namespace Catch {
// This just means the test was aborted due to failure // This just means the test was aborted due to failure
} }
catch(...) { catch(...) {
ResultBuilder exResult( m_lastAssertionInfo.macroName.c_str(), makeUnexpectedResultBuilder().useActiveException();
m_lastAssertionInfo.lineInfo,
m_lastAssertionInfo.capturedExpression.c_str(),
m_lastAssertionInfo.resultDisposition );
exResult.useActiveException();
} }
// If sections ended prematurely due to an exception we stored their // If sections ended prematurely due to an exception we stored their
// infos here so we can tear them down outside the unwind process. // infos here so we can tear them down outside the unwind process.
@ -272,6 +276,11 @@ namespace Catch {
m_reporter->sectionEnded( testCaseSectionStats ); m_reporter->sectionEnded( testCaseSectionStats );
} }
void invokeActiveTestCase() {
FatalConditionHandler fatalConditionHandler; // Handle signals
m_activeTestCase->invoke();
}
private: private:
struct UnfinishedSections { struct UnfinishedSections {
UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds ) UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )

View File

@ -109,6 +109,13 @@ namespace Catch {
printExpressionWas(); printExpressionWas();
printRemainingMessages(); printRemainingMessages();
break; break;
case ResultWas::FatalErrorCondition:
printResultType( Colour::Error, failedString() );
printIssue( "fatal error condition with message:" );
printMessage();
printExpressionWas();
printRemainingMessages();
break;
case ResultWas::DidntThrowException: case ResultWas::DidntThrowException:
printResultType( Colour::Error, failedString() ); printResultType( Colour::Error, failedString() );
printIssue( "expected exception, got none" ); printIssue( "expected exception, got none" );

View File

@ -149,6 +149,11 @@ namespace Catch {
passOrFail = "FAILED"; passOrFail = "FAILED";
messageLabel = "due to unexpected exception with message"; messageLabel = "due to unexpected exception with message";
break; break;
case ResultWas::FatalErrorCondition:
colour = Colour::Error;
passOrFail = "FAILED";
messageLabel = "due to a fatal error condition";
break;
case ResultWas::DidntThrowException: case ResultWas::DidntThrowException:
colour = Colour::Error; colour = Colour::Error;
passOrFail = "FAILED"; passOrFail = "FAILED";

View File

@ -168,6 +168,7 @@ namespace Catch {
std::string elementName; std::string elementName;
switch( result.getResultType() ) { switch( result.getResultType() ) {
case ResultWas::ThrewException: case ResultWas::ThrewException:
case ResultWas::FatalErrorCondition:
elementName = "error"; elementName = "error";
break; break;
case ResultWas::ExplicitFailure: case ResultWas::ExplicitFailure:

View File

@ -108,6 +108,13 @@ namespace Catch {
.writeText( assertionResult.getMessage() ); .writeText( assertionResult.getMessage() );
m_currentTestSuccess = false; m_currentTestSuccess = false;
break; break;
case ResultWas::FatalErrorCondition:
m_xml.scopedElement( "Fatal Error Condition" )
.writeAttribute( "filename", assertionResult.getSourceInfo().file )
.writeAttribute( "line", assertionResult.getSourceInfo().line )
.writeText( assertionResult.getMessage() );
m_currentTestSuccess = false;
break;
case ResultWas::Info: case ResultWas::Info:
m_xml.scopedElement( "Info" ) m_xml.scopedElement( "Info" )
.writeText( assertionResult.getMessage() ); .writeText( assertionResult.getMessage() );

View File

@ -380,3 +380,9 @@ TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) {
std::string result = Catch::toString( s ); std::string result = Catch::toString( s );
CHECK( result == "\"wide load\"" ); CHECK( result == "\"wide load\"" );
} }
TEST_CASE( "Divide by Zero signal handler", "[.][sig]" ) {
int i = 0;
int x = 10/i; // This should cause the signal to fire
CHECK( x == 0 );
}

View File

@ -66,6 +66,7 @@
2627F7061935B55F009BCE2D /* catch_result_builder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_result_builder.hpp; sourceTree = "<group>"; }; 2627F7061935B55F009BCE2D /* catch_result_builder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_result_builder.hpp; sourceTree = "<group>"; };
262E7399184673A800CAC268 /* catch_reporter_bases.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_bases.hpp; sourceTree = "<group>"; }; 262E7399184673A800CAC268 /* catch_reporter_bases.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_bases.hpp; sourceTree = "<group>"; };
262E739A1846759000CAC268 /* catch_common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_common.hpp; sourceTree = "<group>"; }; 262E739A1846759000CAC268 /* catch_common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_common.hpp; sourceTree = "<group>"; };
263F7A4519A66608009474C2 /* catch_fatal_condition.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_fatal_condition.hpp; sourceTree = "<group>"; };
263FD06017AF8DF200988A20 /* catch_timer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_timer.hpp; sourceTree = "<group>"; }; 263FD06017AF8DF200988A20 /* catch_timer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_timer.hpp; sourceTree = "<group>"; };
263FD06117AF8DF200988A20 /* catch_timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_timer.h; sourceTree = "<group>"; }; 263FD06117AF8DF200988A20 /* catch_timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_timer.h; sourceTree = "<group>"; };
2656C21F1925E5100040DB02 /* catch_test_spec_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_test_spec_parser.hpp; sourceTree = "<group>"; }; 2656C21F1925E5100040DB02 /* catch_test_spec_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_test_spec_parser.hpp; sourceTree = "<group>"; };
@ -453,6 +454,7 @@
268F47B018A93F7800D8C14F /* catch_clara.h */, 268F47B018A93F7800D8C14F /* catch_clara.h */,
2656C226192A77EF0040DB02 /* catch_suppress_warnings.h */, 2656C226192A77EF0040DB02 /* catch_suppress_warnings.h */,
2656C227192A78410040DB02 /* catch_reenable_warnings.h */, 2656C227192A78410040DB02 /* catch_reenable_warnings.h */,
263F7A4519A66608009474C2 /* catch_fatal_condition.hpp */,
); );
name = Infrastructure; name = Infrastructure;
sourceTree = "<group>"; sourceTree = "<group>";