CATCH_CONFIG_FAST_COMPILE now disables trys in REQUIRE*

This seems to give about 15% speedup when compiling tests using GCC.

The tradeoff is that under certain circumstances, there is a chance for
false negative result, when the expression under test throws exception
and the test code catches it before it gets to the test runner.

Example:
``` cpp
TEST_CASE("False negative") {
try {
REQUIRE(throws() == "");
} catch (...) {}
}
```
This test case will succeed, reporting no assertions checked, instead of
failing as it would with `CATCH_CONFIG_FAST_COMPILE` disabled. However,
just removing the try-catch block inside client's code will fix this, so
it is worthwhile.

This change does not apply to CHECK* macros, because these are currently
specified as continuing on exception and thus need the local try-catch
to work as intended.
This commit is contained in:
Martin Hořeňovský 2017-03-17 13:21:40 +01:00
parent 4dc06bdb70
commit 7a8a0205b4
2 changed files with 50 additions and 0 deletions

View File

@ -91,8 +91,13 @@ LeakDetector leakDetector;
// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
#ifdef CATCH_CONFIG_PREFIX_ALL
#if defined(CATCH_CONFIG_FAST_COMPILE)
#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
#else
#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" )
#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" )
#endif
#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" )
#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" )
@ -111,7 +116,13 @@ LeakDetector leakDetector;
#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" )
#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" )
#if defined(CATCH_CONFIG_FAST_COMPILE)
#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
#else
#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" )
#endif
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" )
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg )
@ -162,8 +173,14 @@ LeakDetector leakDetector;
// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
#else
#if defined(CATCH_CONFIG_FAST_COMPILE)
#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
#else
#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" )
#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" )
#endif
#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" )
#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" )
@ -182,7 +199,13 @@ LeakDetector leakDetector;
#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" )
#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" )
#if defined(CATCH_CONFIG_FAST_COMPILE)
#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
#else
#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" )
#endif
#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" )
#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg )

View File

@ -26,6 +26,33 @@
// macro in each assertion
#define INTERNAL_CATCH_REACT( resultBuilder ) \
resultBuilder.react();
///////////////////////////////////////////////////////////////////////////////
// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
// macros.
// This can potentially cause false negative, if the test code catches
// the exception before it propagates back up to the runner.
#define INTERNAL_CATCH_TEST_NO_TRY( expr, resultDisposition, macroName ) \
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
( __catchResult <= expr ).endExpression(); \
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
INTERNAL_CATCH_REACT( __catchResult ) \
} while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
#define INTERNAL_CHECK_THAT_NO_TRY( arg, matcher, resultDisposition, macroName ) \
do { \
Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
__catchResult.captureMatch( arg, matcher, #matcher ); \
__catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
INTERNAL_CATCH_REACT( __catchResult ) \
} while( Catch::alwaysFalse() )
#define INTERNAL_CATCH_
#else
///////////////////////////////////////////////////////////////////////////////
// In the event of a failure works out if the debugger needs to be invoked