diff --git a/include/internal/catch_fatal_condition.hpp b/include/internal/catch_fatal_condition.hpp new file mode 100644 index 00000000..08c4dcc2 --- /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 + +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( "", -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 6565f0e6..b9d1e874 100644 --- a/include/internal/catch_interfaces_capture.h +++ b/include/internal/catch_interfaces_capture.h @@ -21,6 +21,7 @@ namespace Catch { struct MessageInfo; class ScopedMessageBuilder; struct Counts; + class ResultBuilder; struct IResultCapture { @@ -35,6 +36,8 @@ namespace Catch { 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 ce00ef03..31ad3e60 100644 --- a/include/internal/catch_result_type.h +++ b/include/internal/catch_result_type.h @@ -25,7 +25,9 @@ namespace Catch { 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 fa4f3193..47b2e2be 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 #include @@ -215,6 +216,13 @@ namespace Catch { return m_totals.assertions.failed == static_cast( 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 @@ namespace Catch { 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 @@ namespace Catch { // 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 @@ namespace Catch { 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 7a6353e6..a5a17297 100644 --- a/include/reporters/catch_reporter_compact.hpp +++ b/include/reporters/catch_reporter_compact.hpp @@ -109,6 +109,13 @@ namespace Catch { 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 7246d328..83498327 100644 --- a/include/reporters/catch_reporter_console.hpp +++ b/include/reporters/catch_reporter_console.hpp @@ -149,6 +149,11 @@ namespace Catch { 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 63f7e148..1108794b 100644 --- a/include/reporters/catch_reporter_junit.hpp +++ b/include/reporters/catch_reporter_junit.hpp @@ -168,6 +168,7 @@ namespace Catch { 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 aa2cfeda..4687471a 100644 --- a/include/reporters/catch_reporter_xml.hpp +++ b/include/reporters/catch_reporter_xml.hpp @@ -108,6 +108,13 @@ namespace Catch { .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 b229c766..49cfcf63 100644 --- a/projects/SelfTest/MiscTests.cpp +++ b/projects/SelfTest/MiscTests.cpp @@ -380,3 +380,9 @@ TEST_CASE( "toString on wchar_t returns the string contents", "[toString]" ) { 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 31fc5253..d0420a71 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 = ""; }; 262E7399184673A800CAC268 /* catch_reporter_bases.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_reporter_bases.hpp; sourceTree = ""; }; 262E739A1846759000CAC268 /* catch_common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_common.hpp; sourceTree = ""; }; + 263F7A4519A66608009474C2 /* catch_fatal_condition.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = catch_fatal_condition.hpp; sourceTree = ""; }; 263FD06017AF8DF200988A20 /* catch_timer.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_timer.hpp; sourceTree = ""; }; 263FD06117AF8DF200988A20 /* catch_timer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = catch_timer.h; sourceTree = ""; }; 2656C21F1925E5100040DB02 /* catch_test_spec_parser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = catch_test_spec_parser.hpp; sourceTree = ""; }; @@ -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 = "";