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.
diff --git a/include/internal/catch_fatal_condition.hpp b/include/internal/catch_fatal_condition.hpp
new file mode 100644
index 0000000..08c4dcc
--- /dev/null
+++ b/include/internal/catch_fatal_condition.hpp
@@ -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
diff --git a/include/internal/catch_interfaces_capture.h b/include/internal/catch_interfaces_capture.h
index 6565f0e..b9d1e87 100644
--- a/include/internal/catch_interfaces_capture.h
+++ b/include/internal/catch_interfaces_capture.h
@@ -21,6 +21,7 @@
struct MessageInfo;
class ScopedMessageBuilder;
struct Counts;
+ class ResultBuilder;
struct IResultCapture {
@@ -35,6 +36,8 @@
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
+
+ virtual ResultBuilder makeUnexpectedResultBuilder() const = 0;
};
IResultCapture& getResultCapture();
diff --git a/include/internal/catch_result_type.h b/include/internal/catch_result_type.h
index ce00ef0..31ad3e6 100644
--- a/include/internal/catch_result_type.h
+++ b/include/internal/catch_result_type.h
@@ -25,7 +25,9 @@
Exception = 0x100 | FailureBit,
ThrewException = Exception | 1,
- DidntThrowException = Exception | 2
+ DidntThrowException = Exception | 2,
+
+ FatalErrorCondition = 0x200 | FailureBit
}; };
diff --git a/include/internal/catch_runner_impl.hpp b/include/internal/catch_runner_impl.hpp
index fa4f319..47b2e2b 100644
--- a/include/internal/catch_runner_impl.hpp
+++ b/include/internal/catch_runner_impl.hpp
@@ -20,6 +20,7 @@
#include "catch_test_case_tracker.hpp"
#include "catch_timer.h"
#include "catch_result_builder.h"
+#include "catch_fatal_condition.hpp"
#include <set>
#include <string>
@@ -215,6 +216,13 @@
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:
void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
@@ -232,10 +240,10 @@
if( m_reporter->getPreferences().shouldRedirectStdOut ) {
StreamRedirect coutRedir( std::cout, redirectedCout );
StreamRedirect cerrRedir( std::cerr, redirectedCerr );
- m_activeTestCase->invoke();
+ invokeActiveTestCase();
}
else {
- m_activeTestCase->invoke();
+ invokeActiveTestCase();
}
duration = timer.getElapsedSeconds();
}
@@ -243,11 +251,7 @@
// This just means the test was aborted due to failure
}
catch(...) {
- ResultBuilder exResult( m_lastAssertionInfo.macroName.c_str(),
- m_lastAssertionInfo.lineInfo,
- m_lastAssertionInfo.capturedExpression.c_str(),
- m_lastAssertionInfo.resultDisposition );
- exResult.useActiveException();
+ makeUnexpectedResultBuilder().useActiveException();
}
// If sections ended prematurely due to an exception we stored their
// infos here so we can tear them down outside the unwind process.
@@ -272,6 +276,11 @@
m_reporter->sectionEnded( testCaseSectionStats );
}
+ void invokeActiveTestCase() {
+ FatalConditionHandler fatalConditionHandler; // Handle signals
+ m_activeTestCase->invoke();
+ }
+
private:
struct UnfinishedSections {
UnfinishedSections( SectionInfo const& _info, Counts const& _prevAssertions, double _durationInSeconds )
diff --git a/include/reporters/catch_reporter_compact.hpp b/include/reporters/catch_reporter_compact.hpp
index 7a6353e..a5a1729 100644
--- a/include/reporters/catch_reporter_compact.hpp
+++ b/include/reporters/catch_reporter_compact.hpp
@@ -109,6 +109,13 @@
printExpressionWas();
printRemainingMessages();
break;
+ case ResultWas::FatalErrorCondition:
+ printResultType( Colour::Error, failedString() );
+ printIssue( "fatal error condition with message:" );
+ printMessage();
+ printExpressionWas();
+ printRemainingMessages();
+ break;
case ResultWas::DidntThrowException:
printResultType( Colour::Error, failedString() );
printIssue( "expected exception, got none" );
diff --git a/include/reporters/catch_reporter_console.hpp b/include/reporters/catch_reporter_console.hpp
index 7246d32..8349832 100644
--- a/include/reporters/catch_reporter_console.hpp
+++ b/include/reporters/catch_reporter_console.hpp
@@ -149,6 +149,11 @@
passOrFail = "FAILED";
messageLabel = "due to unexpected exception with message";
break;
+ case ResultWas::FatalErrorCondition:
+ colour = Colour::Error;
+ passOrFail = "FAILED";
+ messageLabel = "due to a fatal error condition";
+ break;
case ResultWas::DidntThrowException:
colour = Colour::Error;
passOrFail = "FAILED";
diff --git a/include/reporters/catch_reporter_junit.hpp b/include/reporters/catch_reporter_junit.hpp
index 63f7e14..1108794 100644
--- a/include/reporters/catch_reporter_junit.hpp
+++ b/include/reporters/catch_reporter_junit.hpp
@@ -168,6 +168,7 @@
std::string elementName;
switch( result.getResultType() ) {
case ResultWas::ThrewException:
+ case ResultWas::FatalErrorCondition:
elementName = "error";
break;
case ResultWas::ExplicitFailure:
diff --git a/include/reporters/catch_reporter_xml.hpp b/include/reporters/catch_reporter_xml.hpp
index aa2cfed..4687471 100644
--- a/include/reporters/catch_reporter_xml.hpp
+++ b/include/reporters/catch_reporter_xml.hpp
@@ -108,6 +108,13 @@
.writeText( assertionResult.getMessage() );
m_currentTestSuccess = false;
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:
m_xml.scopedElement( "Info" )
.writeText( assertionResult.getMessage() );
diff --git a/projects/SelfTest/MiscTests.cpp b/projects/SelfTest/MiscTests.cpp
index b229c76..49cfcf6 100644
--- a/projects/SelfTest/MiscTests.cpp
+++ b/projects/SelfTest/MiscTests.cpp
@@ -380,3 +380,9 @@
std::string result = Catch::toString( s );
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 );
+}
diff --git a/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj b/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj
index 31fc525..d0420a7 100644
--- a/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj
+++ b/projects/XCode/CatchSelfTest/CatchSelfTest.xcodeproj/project.pbxproj
@@ -66,6 +66,7 @@
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>"; };
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>"; };
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>"; };
@@ -453,6 +454,7 @@
268F47B018A93F7800D8C14F /* catch_clara.h */,
2656C226192A77EF0040DB02 /* catch_suppress_warnings.h */,
2656C227192A78410040DB02 /* catch_reenable_warnings.h */,
+ 263F7A4519A66608009474C2 /* catch_fatal_condition.hpp */,
);
name = Infrastructure;
sourceTree = "<group>";