mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-23 11:43:29 +01:00
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:
parent
aa31d1c275
commit
c1a8e1c5dd
78
include/internal/catch_fatal_condition.hpp
Normal file
78
include/internal/catch_fatal_condition.hpp
Normal 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
|
@ -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();
|
||||||
|
@ -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
|
||||||
|
|
||||||
}; };
|
}; };
|
||||||
|
|
||||||
|
@ -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 )
|
||||||
|
@ -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" );
|
||||||
|
@ -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";
|
||||||
|
@ -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:
|
||||||
|
@ -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() );
|
||||||
|
@ -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 );
|
||||||
|
}
|
||||||
|
@ -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>";
|
||||||
|
Loading…
Reference in New Issue
Block a user