Compare commits

...

15 Commits

Author SHA1 Message Date
Martin Hořeňovský
5c88067bd3 v2.13.6 2021-04-16 20:14:58 +02:00
Georg Schwab
86a4d704bc fixed inconsistent semicolon expansion in catch_discover_tests (Bug #2214) 2021-04-16 17:26:08 +02:00
Matteo Beniamino
469a717395 Fixed [dis]engage_platform declarations mismatch 2021-04-13 19:50:32 +02:00
Martin Hořeňovský
42e368dd0a v2.13.5 2021-04-10 23:48:32 +02:00
Clare Macrae
1ff1f2741d Prevent Catch2 v2 tests running if loaded as a sub-project
See #2202
2021-04-09 23:54:13 +02:00
Julien Brianceau
269f96e2bc Fix typos in the code base
Note that only documentation and comments are impacted by this change.
2021-04-08 18:06:36 +02:00
Martin Hořeňovský
cec35630fb Don't build Catch2WithMain target by default for v2
The `Catch2WithMain` target was added experimentally for v2.13.4
to provide potentially better compilation (and link) times to users.
However, having it compiled by default causes worse experience for
people who do not use it, and for the v2 versions the single include
distribution is still the main one.

Closes #2142
2021-04-05 18:04:31 +02:00
Martin Hořeňovský
b8b03da534 Mangle doccoments to avoid breaking single include stitching 2021-04-04 18:09:29 +02:00
Martin Hořeňovský
8f277a54c0 Significantly refactor fatal error handling
Because new glibc has changed `MINSIGSTKSZ` to be a syscall instead
of being constant, the signal posix handling needed changes, as it
used the value in constexpr context, for deciding size of an array.
It would be simple to fix it by having the handler determine the
signal handling stack size and allocate the memory every time the
handler is being installed, but that would add another allocation
and a syscall every time a test case is entered.

Instead, I split apart the idea of preparing fatal error handlers,
and engaging them, so that the memory can be allocated only once
and still be guarded by RAII.

Also turns out that Catch2's use of `MINSIGSTKSZ` was wrong, and
we should've been using `SIGSTKSZ` the whole time, which we use now.

Closes #2178
2021-04-04 18:09:26 +02:00
Pavel Kamenov
4cb3220a8a Add lcc to the list of unwanted compilers that mimic gcc 2021-04-04 14:05:39 +02:00
Scott Hutchinson
b025a007b9 Wrap all std::min and std::max calls in parentheses 2021-02-20 23:28:20 +01:00
Rob Boehne
68975e3ff3 [Issue 2154] Correct error when building with IBM's latest XLC (#2155)
* [Issue 2154] Correct error when building with IBM's latest XLC compiler with xlclang++ front-end.

On AIX, the XLC 16.1.0.1 compiler considers the call to `std::abs` ambigious, so it needs help with a static_cast to the type of the template argument.

Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2021-01-21 22:50:49 +01:00
Martin Hořeňovský
bcb9ea8cb5 Merge pull request #2157 from tdegeus/patch
Making target detection on Mac more robust
2021-01-21 15:35:23 +01:00
Tom de Geus
d4c9494eb5 Making target detection on Mac more robust 2021-01-20 21:12:18 +01:00
Mathieu Mirmont
bed285af07 Make the build reproducible with g++-8 and clang-10
Make the build reproducible by removing references to the source
directory from the Catch2WithMain static library.
2021-01-13 18:09:24 +01:00
21 changed files with 494 additions and 316 deletions

View File

@@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.5)
# disable testsuite in that case # disable testsuite in that case
if(NOT DEFINED PROJECT_NAME) if(NOT DEFINED PROJECT_NAME)
set(NOT_SUBPROJECT ON) set(NOT_SUBPROJECT ON)
else()
set(NOT_SUBPROJECT OFF)
endif() endif()
# Catch2's build breaks if done in-tree. You probably should not build # Catch2's build breaks if done in-tree. You probably should not build
@@ -14,7 +16,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif() endif()
project(Catch2 LANGUAGES CXX VERSION 2.13.4) project(Catch2 LANGUAGES CXX VERSION 2.13.6)
# Provide path for scripts # Provide path for scripts
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
@@ -25,12 +27,12 @@ option(CATCH_USE_VALGRIND "Perform SelfTests with Valgrind" OFF)
option(CATCH_BUILD_TESTING "Build SelfTest project" ON) option(CATCH_BUILD_TESTING "Build SelfTest project" ON)
option(CATCH_BUILD_EXAMPLES "Build documentation examples" OFF) option(CATCH_BUILD_EXAMPLES "Build documentation examples" OFF)
option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF) option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF)
option(CATCH_BUILD_STATIC_LIBRARY "Builds static library from the main implementation. EXPERIMENTAL" OFF)
option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF) option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF)
option(CATCH_ENABLE_WERROR "Enable all warnings as errors" ON) option(CATCH_ENABLE_WERROR "Enable all warnings as errors" ON)
option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON) option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON)
option(CATCH_INSTALL_HELPERS "Install contrib alongside library" ON) option(CATCH_INSTALL_HELPERS "Install contrib alongside library" ON)
# define some folders # define some folders
set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest) set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
@@ -104,10 +106,17 @@ endif()
add_library(Catch2::Catch2 ALIAS Catch2) add_library(Catch2::Catch2 ALIAS Catch2)
# Hacky support for compiling the impl into a static lib # Hacky support for compiling the impl into a static lib
add_library(Catch2WithMain ${CMAKE_CURRENT_LIST_DIR}/src/catch_with_main.cpp) if (CATCH_BUILD_STATIC_LIBRARY)
target_link_libraries(Catch2WithMain PUBLIC Catch2) add_library(Catch2WithMain ${CMAKE_CURRENT_LIST_DIR}/src/catch_with_main.cpp)
add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain) target_link_libraries(Catch2WithMain PUBLIC Catch2)
add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain)
# Make the build reproducible on versions of g++ and clang that supports -ffile-prefix-map
if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 8) OR
("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 10))
target_compile_options(Catch2WithMain PRIVATE "-ffile-prefix-map=${CMAKE_SOURCE_DIR}=.")
endif()
endif(CATCH_BUILD_STATIC_LIBRARY)
# Only perform the installation steps when Catch is not being used as # Only perform the installation steps when Catch is not being used as
# a subproject via `add_subdirectory`, or the destinations will break, # a subproject via `add_subdirectory`, or the destinations will break,
@@ -123,11 +132,17 @@ if (NOT_SUBPROJECT)
${CATCH_CMAKE_CONFIG_DESTINATION} ${CATCH_CMAKE_CONFIG_DESTINATION}
) )
# Workaround lack of generator expressions in install(TARGETS
set(InstallationTargets Catch2)
if (TARGET Catch2WithMain)
list(APPEND InstallationTargets Catch2WithMain)
endif()
# create and install an export set for catch target as Catch2::Catch # create and install an export set for catch target as Catch2::Catch
install( install(
TARGETS TARGETS
Catch2 Catch2WithMain ${InstallationTargets}
EXPORT EXPORT
Catch2Targets Catch2Targets
DESTINATION DESTINATION

View File

@@ -9,7 +9,7 @@
[![Join the chat in Discord: https://discord.gg/4CWS9zD](https://img.shields.io/badge/Discord-Chat!-brightgreen.svg)](https://discord.gg/4CWS9zD) [![Join the chat in Discord: https://discord.gg/4CWS9zD](https://img.shields.io/badge/Discord-Chat!-brightgreen.svg)](https://discord.gg/4CWS9zD)
<a href="https://github.com/catchorg/Catch2/releases/download/v2.13.4/catch.hpp">The latest version of the single header can be downloaded directly using this link</a> <a href="https://github.com/catchorg/Catch2/releases/download/v2.13.6/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
## Catch2 is released! ## Catch2 is released!

View File

@@ -16,7 +16,10 @@ set(tests)
function(add_command NAME) function(add_command NAME)
set(_args "") set(_args "")
foreach(_arg ${ARGN}) # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
math(EXPR _last_arg ${ARGC}-1)
foreach(_n RANGE 1 ${_last_arg})
set(_arg "${ARGV${_n}}")
if(_arg MATCHES "[^-./:a-zA-Z0-9_]") if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
else() else()

View File

@@ -200,7 +200,7 @@ function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
# Escape commas in the test spec # Escape commas in the test spec
string(REPLACE "," "\\," Name ${Name}) string(REPLACE "," "\\," Name ${Name})
# Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were neccessary, # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary,
# only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1 # only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1
# And properly introduced in 3.19 with the CMP0110 policy # And properly introduced in 3.19 with the CMP0110 policy
if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18") if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18")

View File

@@ -26,7 +26,7 @@ Ongoing development happens in the `v2.x` branch for Catch2 v2, and in
Commits should be small and atomic. A commit is atomic when, after it is Commits should be small and atomic. A commit is atomic when, after it is
applied, the codebase, tests and all, still works as expected. Small applied, the codebase, tests and all, still works as expected. Small
commits are also prefered, as they make later operations with git history, commits are also preferred, as they make later operations with git history,
whether it is bisecting, reverting, or something else, easier. whether it is bisecting, reverting, or something else, easier.
_When submitting a pull request please do not include changes to the _When submitting a pull request please do not include changes to the

View File

@@ -2,6 +2,8 @@
# Release notes # Release notes
**Contents**<br> **Contents**<br>
[2.13.6](#2136)<br>
[2.13.5](#2135)<br>
[2.13.4](#2134)<br> [2.13.4](#2134)<br>
[2.13.3](#2133)<br> [2.13.3](#2133)<br>
[2.13.2](#2132)<br> [2.13.2](#2132)<br>
@@ -45,6 +47,36 @@
[Even Older versions](#even-older-versions)<br> [Even Older versions](#even-older-versions)<br>
## 2.13.6
### Fixes
* Disabling all signal handlers no longer breaks compilation (#2212, #2213)
### Miscellaneous
* `catch_discover_tests` should handle escaped semicolon (`;`) better (#2214, #2215)
## 2.13.5
### Improvements
* Detection of MAC and IPHONE platforms has been improved (#2140, #2157)
* Added workaround for bug in XLC 16.1.0.1 (#2155)
* Add detection for LCC when it is masquerading as GCC (#2199)
* Modified posix signal handling so it supports newer libcs (#2178)
* `MINSIGSTKSZ` was no longer usable in constexpr context.
### Fixes
* Fixed compilation of benchmarking when `min` and `max` macros are defined (#2159)
* Including `windows.h` without `NOMINMAX` remains a really bad idea, don't do it
### Miscellaneous
* `Catch2WithMain` target (static library) is no longer built by default (#2142)
* Building it by default was at best unnecessary overhead for people not using it, and at worst it caused trouble with install paths
* To have it built, set CMake option `CATCH_BUILD_STATIC_LIBRARY` to `ON`
* The check whether Catch2 is being built as a subproject is now more reliable (#2202, #2204)
* The problem was that if the variable name used internally was defined the project including Catch2 as subproject, it would not be properly overwritten for Catch2's CMake.
## 2.13.4 ## 2.13.4
### Improvements ### Improvements

View File

@@ -43,7 +43,7 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
/* Possible simplifications where less legacy toolchain support is needed: /* Possible simplifications where less legacy toolchain support is needed:
* *
* - With libstdc++6 or newer, the make_tuple() calls can be ommitted * - With libstdc++6 or newer, the make_tuple() calls can be omitted
* (technically C++17 but does not require -std in GCC/Clang). See * (technically C++17 but does not require -std in GCC/Clang). See
* https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list * https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
* *

View File

@@ -11,7 +11,7 @@
#define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_MINOR 13
#define CATCH_VERSION_PATCH 4 #define CATCH_VERSION_PATCH 6
#ifdef __clang__ #ifdef __clang__
# pragma clang system_header # pragma clang system_header

View File

@@ -68,7 +68,9 @@ namespace Catch {
} }
template <typename Clock> template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit)); auto time_limit = (std::min)(
resolution * clock_cost_estimation_tick_limit,
FloatDuration<Clock>(clock_cost_estimation_time_limit));
auto time_clock = [](int k) { auto time_clock = [](int k) {
return Detail::measure<Clock>([k] { return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) { for (int i = 0; i < k; ++i) {

View File

@@ -152,7 +152,7 @@ namespace Catch {
double sb = stddev.point; double sb = stddev.point;
double mn = mean.point / n; double mn = mean.point / n;
double mg_min = mn / 2.; double mg_min = mn / 2.;
double sg = std::min(mg_min / 4., sb / std::sqrt(n)); double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
double sg2 = sg * sg; double sg2 = sg * sg;
double sb2 = sb * sb; double sb2 = sb * sb;
@@ -171,7 +171,7 @@ namespace Catch {
return (nc / n) * (sb2 - nc * sg2); return (nc / n) * (sb2 - nc * sg2);
}; };
return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
} }

View File

@@ -138,8 +138,8 @@ namespace Catch {
double b2 = bias - z1; double b2 = bias - z1;
double a1 = a(b1); double a1 = a(b1);
double a2 = a(b2); double a2 = a(b2);
auto lo = std::max(cumn(a1), 0); auto lo = (std::max)(cumn(a1), 0);
auto hi = std::min(cumn(a2), n - 1); auto hi = (std::min)(cumn(a2), n - 1);
return { point, resample[lo], resample[hi], confidence_level }; return { point, resample[lo], resample[hi], confidence_level };
} }

View File

@@ -39,9 +39,9 @@
#endif #endif
// We have to avoid both ICC and Clang, because they try to mask themselves // Only GCC compiler should be used in this block, so other compilers trying to
// as gcc, and we want only GCC in this block // mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )

View File

@@ -22,7 +22,7 @@ namespace Catch {
// Extracts the actual name part of an enum instance // Extracts the actual name part of an enum instance
// In other words, it returns the Blue part of Bikeshed::Colour::Blue // In other words, it returns the Blue part of Bikeshed::Colour::Blue
StringRef extractInstanceName(StringRef enumInstance) { StringRef extractInstanceName(StringRef enumInstance) {
// Find last occurence of ":" // Find last occurrence of ":"
size_t name_start = enumInstance.size(); size_t name_start = enumInstance.size();
while (name_start > 0 && enumInstance[name_start - 1] != ':') { while (name_start > 0 && enumInstance[name_start - 1] != ':') {
--name_start; --name_start;

View File

@@ -7,30 +7,72 @@
* *
*/ */
/** \file
* This file provides platform specific implementations of FatalConditionHandler
*
* This means that there is a lot of conditional compilation, and platform
* specific code. Currently, Catch2 supports a dummy handler (if no
* handler is desired), and 2 platform specific handlers:
* * Windows' SEH
* * POSIX signals
*
* Consequently, various pieces of code below are compiled if either of
* the platform specific handlers is enabled, or if none of them are
* enabled. It is assumed that both cannot be enabled at the same time,
* and doing so should cause a compilation error.
*
* If another platform specific handler is added, the compile guards
* below will need to be updated taking these assumptions into account.
*/
#include "catch_fatal_condition.h" #include "catch_fatal_condition.h"
#include "catch_context.h" #include "catch_context.h"
#include "catch_interfaces_capture.h" #include "catch_enforce.h"
#include "catch_run_context.h"
#include "catch_windows_h_proxy.h"
#if defined(__GNUC__) #include <algorithm>
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers" #if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
#endif
namespace Catch {
// If neither SEH nor signal handling is required, the handler impls
// do not have to do anything, and can be empty.
void FatalConditionHandler::engage_platform() {}
void FatalConditionHandler::disengage_platform() {}
FatalConditionHandler::FatalConditionHandler() = default;
FatalConditionHandler::~FatalConditionHandler() = default;
} // end namespace Catch
#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace { namespace {
// Report the error condition //! Signals fatal error message to the run context
void reportFatal( char const * const message ) { void reportFatal( char const * const message ) {
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
} }
}
#endif // signals/SEH handling //! Minimal size Catch2 needs for its own fatal error handling.
//! Picked anecdotally, so it might not be sufficient on all
//! platforms, and for all configurations.
constexpr std::size_t minStackSizeForErrors = 32 * 1024;
} // end unnamed namespace
#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) #if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch { namespace Catch {
struct SignalDefs { DWORD id; const char* name; }; struct SignalDefs { DWORD id; const char* name; };
// There is no 1-1 mapping between signals and windows exceptions. // There is no 1-1 mapping between signals and windows exceptions.
@@ -43,7 +85,7 @@ namespace Catch {
{ static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
}; };
LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) { for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name); reportFatal(def.name);
@@ -54,39 +96,52 @@ namespace Catch {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
FatalConditionHandler::FatalConditionHandler() { // Since we do not support multiple instantiations, we put these
isSet = true; // into global variables and rely on cleaning them up in outlined
// 32k seems enough for Catch to handle stack overflow, // constructors/destructors
// but the value was found experimentally, so there is no strong guarantee static PVOID exceptionHandlerHandle = nullptr;
guaranteeSize = 32 * 1024;
exceptionHandlerHandle = nullptr;
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
// Pass in guarantee size to be filled
SetThreadStackGuarantee(&guaranteeSize);
}
void FatalConditionHandler::reset() {
if (isSet) { // For MSVC, we reserve part of the stack memory for handling
RemoveVectoredExceptionHandler(exceptionHandlerHandle); // memory overflow structured exception.
SetThreadStackGuarantee(&guaranteeSize); FatalConditionHandler::FatalConditionHandler() {
exceptionHandlerHandle = nullptr; ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
isSet = false; if (!SetThreadStackGuarantee(&guaranteeSize)) {
// We do not want to fully error out, because needing
// the stack reserve should be rare enough anyway.
Catch::cerr()
<< "Failed to reserve piece of stack."
<< " Stack overflows will not be reported successfully.";
} }
} }
FatalConditionHandler::~FatalConditionHandler() { // We do not attempt to unset the stack guarantee, because
reset(); // Windows does not support lowering the stack size guarantee.
FatalConditionHandler::~FatalConditionHandler() = default;
void FatalConditionHandler::engage_platform() {
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
if (!exceptionHandlerHandle) {
CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
}
} }
bool FatalConditionHandler::isSet = false; void FatalConditionHandler::disengage_platform() {
ULONG FatalConditionHandler::guaranteeSize = 0; if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
}
exceptionHandlerHandle = nullptr;
}
} // end namespace Catch
} // namespace Catch #endif // CATCH_CONFIG_WINDOWS_SEH
#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) #if defined( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch { namespace Catch {
@@ -94,10 +149,6 @@ namespace Catch {
int id; int id;
const char* name; const char* name;
}; };
// 32kb for the alternate stack seems to be sufficient. However, this value
// is experimentally determined, so that's not guaranteed.
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
static SignalDefs signalDefs[] = { static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGINT, "SIGINT - Terminal interrupt signal" },
@@ -108,8 +159,32 @@ namespace Catch {
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
}; };
// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
// which is zero initialization, but not explicit. We want to avoid
// that.
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
void FatalConditionHandler::handleSignal( int sig ) { static char* altStackMem = nullptr;
static std::size_t altStackSize = 0;
static stack_t oldSigStack{};
static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
static void restorePreviousSignalHandlers() {
// We set signal handlers back to the previous ones. Hopefully
// nobody overwrote them in the meantime, and doesn't expect
// their signal handlers to live past ours given that they
// installed them after ours..
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
}
static void handleSignal( int sig ) {
char const * name = "<unknown signal>"; char const * name = "<unknown signal>";
for (auto const& def : signalDefs) { for (auto const& def : signalDefs) {
if (sig == def.id) { if (sig == def.id) {
@@ -117,16 +192,33 @@ namespace Catch {
break; break;
} }
} }
reset(); // We need to restore previous signal handlers and let them do
reportFatal(name); // their thing, so that the users can have the debugger break
// when a signal is raised, and so on.
restorePreviousSignalHandlers();
reportFatal( name );
raise( sig ); raise( sig );
} }
FatalConditionHandler::FatalConditionHandler() { FatalConditionHandler::FatalConditionHandler() {
isSet = true; assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
if (altStackSize == 0) {
altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
}
altStackMem = new char[altStackSize]();
}
FatalConditionHandler::~FatalConditionHandler() {
delete[] altStackMem;
// We signal that another instance can be constructed by zeroing
// out the pointer.
altStackMem = nullptr;
}
void FatalConditionHandler::engage_platform() {
stack_t sigStack; stack_t sigStack;
sigStack.ss_sp = altStackMem; sigStack.ss_sp = altStackMem;
sigStack.ss_size = sigStackSize; sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0; sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack); sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { }; struct sigaction sa = { };
@@ -138,39 +230,15 @@ namespace Catch {
} }
} }
FatalConditionHandler::~FatalConditionHandler() {
reset();
}
void FatalConditionHandler::reset() {
if( isSet ) {
// Set signals back to previous values -- hopefully nobody overwrote them in the meantime
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
isSet = false;
}
}
bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
stack_t FatalConditionHandler::oldSigStack = {};
char FatalConditionHandler::altStackMem[sigStackSize] = {};
} // namespace Catch
#else
namespace Catch {
void FatalConditionHandler::reset() {}
}
#endif // signals/SEH handling
#if defined(__GNUC__) #if defined(__GNUC__)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
void FatalConditionHandler::disengage_platform() {
restorePreviousSignalHandlers();
}
} // end namespace Catch
#endif // CATCH_CONFIG_POSIX_SIGNALS

View File

@@ -11,59 +11,58 @@
#include "catch_platform.h" #include "catch_platform.h"
#include "catch_compiler_capabilities.h" #include "catch_compiler_capabilities.h"
#include "catch_windows_h_proxy.h"
#include <cassert>
#if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch { namespace Catch {
struct FatalConditionHandler { // Wrapper for platform-specific fatal error (signals/SEH) handlers
//
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); // Tries to be cooperative with other handlers, and not step over
FatalConditionHandler(); // other handlers. This means that unknown structured exceptions
static void reset(); // are passed on, previous signal handlers are called, and so on.
~FatalConditionHandler(); //
// Can only be instantiated once, and assumes that once a signal
private: // is caught, the binary will end up terminating. Thus, there
static bool isSet; class FatalConditionHandler {
static ULONG guaranteeSize; bool m_started = false;
static PVOID exceptionHandlerHandle;
};
} // namespace Catch
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch {
struct FatalConditionHandler {
static bool isSet;
static struct sigaction oldSigActions[];
static stack_t oldSigStack;
static char altStackMem[];
static void handleSignal( int sig );
// Install/disengage implementation for specific platform.
// Should be if-defed to work on current platform, can assume
// engage-disengage 1:1 pairing.
void engage_platform();
void disengage_platform();
public:
// Should also have platform-specific implementations as needed
FatalConditionHandler(); FatalConditionHandler();
~FatalConditionHandler(); ~FatalConditionHandler();
static void reset();
void engage() {
assert(!m_started && "Handler cannot be installed twice.");
m_started = true;
engage_platform();
}
void disengage() {
assert(m_started && "Handler cannot be uninstalled without being installed first");
m_started = false;
disengage_platform();
}
}; };
} // namespace Catch //! Simple RAII guard for (dis)engaging the FatalConditionHandler
class FatalConditionHandlerGuard {
FatalConditionHandler* m_handler;
#else public:
FatalConditionHandlerGuard(FatalConditionHandler* handler):
namespace Catch { m_handler(handler) {
struct FatalConditionHandler { m_handler->engage();
void reset(); }
~FatalConditionHandlerGuard() {
m_handler->disengage();
}
}; };
}
#endif } // end namespace Catch
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED #endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED

View File

@@ -55,7 +55,8 @@ namespace {
return lhs == rhs; return lhs == rhs;
} }
auto ulpDiff = std::abs(lc - rc); // static cast as a workaround for IBM XLC
auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
} }
@@ -234,4 +235,3 @@ Floating::WithinRelMatcher WithinRel(float target) {
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch

View File

@@ -9,13 +9,16 @@
#ifndef TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #ifndef TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED #define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
// See e.g.:
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__ #ifdef __APPLE__
# include <TargetConditionals.h> # include <TargetConditionals.h>
# if TARGET_OS_OSX == 1 # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
# define CATCH_PLATFORM_MAC (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
# elif TARGET_OS_IPHONE == 1 # define CATCH_PLATFORM_MAC
# define CATCH_PLATFORM_IPHONE # elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
# endif # define CATCH_PLATFORM_IPHONE
# endif
#elif defined(linux) || defined(__linux) || defined(__linux__) #elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX # define CATCH_PLATFORM_LINUX

View File

@@ -452,9 +452,8 @@ namespace Catch {
} }
void RunContext::invokeActiveTestCase() { void RunContext::invokeActiveTestCase() {
FatalConditionHandler fatalConditionHandler; // Handle signals FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke(); m_activeTestCase->invoke();
fatalConditionHandler.reset();
} }
void RunContext::handleUnfinishedSections() { void RunContext::handleUnfinishedSections() {

View File

@@ -146,6 +146,7 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections; std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections; std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext; TrackerContext m_trackerContext;
FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false; bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true; bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults; bool m_includeSuccessfulResults;

View File

@@ -37,7 +37,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 2, 13, 4, "", 0 ); static Version version( 2, 13, 6, "", 0 );
return version; return version;
} }

View File

@@ -1,9 +1,9 @@
/* /*
* Catch v2.13.4 * Catch v2.13.6
* Generated: 2020-12-29 14:48:00.116107 * Generated: 2021-04-16 18:23:38.044268
* ---------------------------------------------------------- * ----------------------------------------------------------
* This file has been merged from multiple headers. Please don't edit it directly * This file has been merged from multiple headers. Please don't edit it directly
* Copyright (c) 2020 Two Blue Cubes Ltd. All rights reserved. * Copyright (c) 2021 Two Blue Cubes Ltd. All rights reserved.
* *
* Distributed under the Boost Software License, Version 1.0. (See accompanying * 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) * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -15,7 +15,7 @@
#define CATCH_VERSION_MAJOR 2 #define CATCH_VERSION_MAJOR 2
#define CATCH_VERSION_MINOR 13 #define CATCH_VERSION_MINOR 13
#define CATCH_VERSION_PATCH 4 #define CATCH_VERSION_PATCH 6
#ifdef __clang__ #ifdef __clang__
# pragma clang system_header # pragma clang system_header
@@ -66,13 +66,16 @@
#if !defined(CATCH_CONFIG_IMPL_ONLY) #if !defined(CATCH_CONFIG_IMPL_ONLY)
// start catch_platform.h // start catch_platform.h
// See e.g.:
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
#ifdef __APPLE__ #ifdef __APPLE__
# include <TargetConditionals.h> # include <TargetConditionals.h>
# if TARGET_OS_OSX == 1 # if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
# define CATCH_PLATFORM_MAC (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
# elif TARGET_OS_IPHONE == 1 # define CATCH_PLATFORM_MAC
# define CATCH_PLATFORM_IPHONE # elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
# endif # define CATCH_PLATFORM_IPHONE
# endif
#elif defined(linux) || defined(__linux) || defined(__linux__) #elif defined(linux) || defined(__linux) || defined(__linux__)
# define CATCH_PLATFORM_LINUX # define CATCH_PLATFORM_LINUX
@@ -132,9 +135,9 @@ namespace Catch {
#endif #endif
// We have to avoid both ICC and Clang, because they try to mask themselves // Only GCC compiler should be used in this block, so other compilers trying to
// as gcc, and we want only GCC in this block // mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) # define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) # define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -7054,8 +7057,8 @@ namespace Catch {
double b2 = bias - z1; double b2 = bias - z1;
double a1 = a(b1); double a1 = a(b1);
double a2 = a(b2); double a2 = a(b2);
auto lo = std::max(cumn(a1), 0); auto lo = (std::max)(cumn(a1), 0);
auto hi = std::min(cumn(a2), n - 1); auto hi = (std::min)(cumn(a2), n - 1);
return { point, resample[lo], resample[hi], confidence_level }; return { point, resample[lo], resample[hi], confidence_level };
} }
@@ -7124,7 +7127,9 @@ namespace Catch {
} }
template <typename Clock> template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit)); auto time_limit = (std::min)(
resolution * clock_cost_estimation_tick_limit,
FloatDuration<Clock>(clock_cost_estimation_time_limit));
auto time_clock = [](int k) { auto time_clock = [](int k) {
return Detail::measure<Clock>([k] { return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) { for (int i = 0; i < k; ++i) {
@@ -7771,7 +7776,7 @@ namespace Catch {
double sb = stddev.point; double sb = stddev.point;
double mn = mean.point / n; double mn = mean.point / n;
double mg_min = mn / 2.; double mg_min = mn / 2.;
double sg = std::min(mg_min / 4., sb / std::sqrt(n)); double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
double sg2 = sg * sg; double sg2 = sg * sg;
double sb2 = sb * sb; double sb2 = sb * sb;
@@ -7790,7 +7795,7 @@ namespace Catch {
return (nc / n) * (sb2 - nc * sg2); return (nc / n) * (sb2 - nc * sg2);
}; };
return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2; return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
} }
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) { bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
@@ -7980,86 +7985,58 @@ namespace Catch {
// start catch_fatal_condition.h // start catch_fatal_condition.h
// start catch_windows_h_proxy.h #include <cassert>
#if defined(CATCH_PLATFORM_WINDOWS)
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
# define CATCH_DEFINED_NOMINMAX
# define NOMINMAX
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#ifdef __AFXDLL
#include <AfxWin.h>
#else
#include <windows.h>
#endif
#ifdef CATCH_DEFINED_NOMINMAX
# undef NOMINMAX
#endif
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif // defined(CATCH_PLATFORM_WINDOWS)
// end catch_windows_h_proxy.h
#if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch { namespace Catch {
struct FatalConditionHandler { // Wrapper for platform-specific fatal error (signals/SEH) handlers
//
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo); // Tries to be cooperative with other handlers, and not step over
FatalConditionHandler(); // other handlers. This means that unknown structured exceptions
static void reset(); // are passed on, previous signal handlers are called, and so on.
~FatalConditionHandler(); //
// Can only be instantiated once, and assumes that once a signal
private: // is caught, the binary will end up terminating. Thus, there
static bool isSet; class FatalConditionHandler {
static ULONG guaranteeSize; bool m_started = false;
static PVOID exceptionHandlerHandle;
};
} // namespace Catch
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch {
struct FatalConditionHandler {
static bool isSet;
static struct sigaction oldSigActions[];
static stack_t oldSigStack;
static char altStackMem[];
static void handleSignal( int sig );
// Install/disengage implementation for specific platform.
// Should be if-defed to work on current platform, can assume
// engage-disengage 1:1 pairing.
void engage_platform();
void disengage_platform();
public:
// Should also have platform-specific implementations as needed
FatalConditionHandler(); FatalConditionHandler();
~FatalConditionHandler(); ~FatalConditionHandler();
static void reset();
void engage() {
assert(!m_started && "Handler cannot be installed twice.");
m_started = true;
engage_platform();
}
void disengage() {
assert(m_started && "Handler cannot be uninstalled without being installed first");
m_started = false;
disengage_platform();
}
}; };
} // namespace Catch //! Simple RAII guard for (dis)engaging the FatalConditionHandler
class FatalConditionHandlerGuard {
#else FatalConditionHandler* m_handler;
public:
namespace Catch { FatalConditionHandlerGuard(FatalConditionHandler* handler):
struct FatalConditionHandler { m_handler(handler) {
void reset(); m_handler->engage();
}
~FatalConditionHandlerGuard() {
m_handler->disengage();
}
}; };
}
#endif } // end namespace Catch
// end catch_fatal_condition.h // end catch_fatal_condition.h
#include <string> #include <string>
@@ -8185,6 +8162,7 @@ namespace Catch {
std::vector<SectionEndInfo> m_unfinishedSections; std::vector<SectionEndInfo> m_unfinishedSections;
std::vector<ITracker*> m_activeSections; std::vector<ITracker*> m_activeSections;
TrackerContext m_trackerContext; TrackerContext m_trackerContext;
FatalConditionHandler m_fatalConditionhandler;
bool m_lastAssertionPassed = false; bool m_lastAssertionPassed = false;
bool m_shouldReportUnexpected = true; bool m_shouldReportUnexpected = true;
bool m_includeSuccessfulResults; bool m_includeSuccessfulResults;
@@ -10057,6 +10035,36 @@ namespace Catch {
} }
// end catch_errno_guard.h // end catch_errno_guard.h
// start catch_windows_h_proxy.h
#if defined(CATCH_PLATFORM_WINDOWS)
#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
# define CATCH_DEFINED_NOMINMAX
# define NOMINMAX
#endif
#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
# define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
#endif
#ifdef __AFXDLL
#include <AfxWin.h>
#else
#include <windows.h>
#endif
#ifdef CATCH_DEFINED_NOMINMAX
# undef NOMINMAX
#endif
#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
# undef WIN32_LEAN_AND_MEAN
#endif
#endif // defined(CATCH_PLATFORM_WINDOWS)
// end catch_windows_h_proxy.h
#include <sstream> #include <sstream>
namespace Catch { namespace Catch {
@@ -10573,7 +10581,7 @@ namespace Catch {
// Extracts the actual name part of an enum instance // Extracts the actual name part of an enum instance
// In other words, it returns the Blue part of Bikeshed::Colour::Blue // In other words, it returns the Blue part of Bikeshed::Colour::Blue
StringRef extractInstanceName(StringRef enumInstance) { StringRef extractInstanceName(StringRef enumInstance) {
// Find last occurence of ":" // Find last occurrence of ":"
size_t name_start = enumInstance.size(); size_t name_start = enumInstance.size();
while (name_start > 0 && enumInstance[name_start - 1] != ':') { while (name_start > 0 && enumInstance[name_start - 1] != ':') {
--name_start; --name_start;
@@ -10735,25 +10743,47 @@ namespace Catch {
// end catch_exception_translator_registry.cpp // end catch_exception_translator_registry.cpp
// start catch_fatal_condition.cpp // start catch_fatal_condition.cpp
#if defined(__GNUC__) #include <algorithm>
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers" #if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
#endif
namespace Catch {
// If neither SEH nor signal handling is required, the handler impls
// do not have to do anything, and can be empty.
void FatalConditionHandler::engage_platform() {}
void FatalConditionHandler::disengage_platform() {}
FatalConditionHandler::FatalConditionHandler() = default;
FatalConditionHandler::~FatalConditionHandler() = default;
} // end namespace Catch
#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS ) #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
namespace { namespace {
// Report the error condition //! Signals fatal error message to the run context
void reportFatal( char const * const message ) { void reportFatal( char const * const message ) {
Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message ); Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
} }
}
#endif // signals/SEH handling //! Minimal size Catch2 needs for its own fatal error handling.
//! Picked anecdotally, so it might not be sufficient on all
//! platforms, and for all configurations.
constexpr std::size_t minStackSizeForErrors = 32 * 1024;
} // end unnamed namespace
#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
#if defined( CATCH_CONFIG_WINDOWS_SEH ) #if defined( CATCH_CONFIG_WINDOWS_SEH )
namespace Catch { namespace Catch {
struct SignalDefs { DWORD id; const char* name; }; struct SignalDefs { DWORD id; const char* name; };
// There is no 1-1 mapping between signals and windows exceptions. // There is no 1-1 mapping between signals and windows exceptions.
@@ -10766,7 +10796,7 @@ namespace Catch {
{ static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" }, { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
}; };
LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) { for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) { if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name); reportFatal(def.name);
@@ -10777,38 +10807,50 @@ namespace Catch {
return EXCEPTION_CONTINUE_SEARCH; return EXCEPTION_CONTINUE_SEARCH;
} }
FatalConditionHandler::FatalConditionHandler() { // Since we do not support multiple instantiations, we put these
isSet = true; // into global variables and rely on cleaning them up in outlined
// 32k seems enough for Catch to handle stack overflow, // constructors/destructors
// but the value was found experimentally, so there is no strong guarantee static PVOID exceptionHandlerHandle = nullptr;
guaranteeSize = 32 * 1024;
exceptionHandlerHandle = nullptr;
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
// Pass in guarantee size to be filled
SetThreadStackGuarantee(&guaranteeSize);
}
void FatalConditionHandler::reset() { // For MSVC, we reserve part of the stack memory for handling
if (isSet) { // memory overflow structured exception.
RemoveVectoredExceptionHandler(exceptionHandlerHandle); FatalConditionHandler::FatalConditionHandler() {
SetThreadStackGuarantee(&guaranteeSize); ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
exceptionHandlerHandle = nullptr; if (!SetThreadStackGuarantee(&guaranteeSize)) {
isSet = false; // We do not want to fully error out, because needing
// the stack reserve should be rare enough anyway.
Catch::cerr()
<< "Failed to reserve piece of stack."
<< " Stack overflows will not be reported successfully.";
} }
} }
FatalConditionHandler::~FatalConditionHandler() { // We do not attempt to unset the stack guarantee, because
reset(); // Windows does not support lowering the stack size guarantee.
FatalConditionHandler::~FatalConditionHandler() = default;
void FatalConditionHandler::engage_platform() {
// Register as first handler in current chain
exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
if (!exceptionHandlerHandle) {
CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
}
} }
bool FatalConditionHandler::isSet = false; void FatalConditionHandler::disengage_platform() {
ULONG FatalConditionHandler::guaranteeSize = 0; if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr; CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
}
exceptionHandlerHandle = nullptr;
}
} // namespace Catch } // end namespace Catch
#elif defined( CATCH_CONFIG_POSIX_SIGNALS ) #endif // CATCH_CONFIG_WINDOWS_SEH
#if defined( CATCH_CONFIG_POSIX_SIGNALS )
#include <signal.h>
namespace Catch { namespace Catch {
@@ -10817,10 +10859,6 @@ namespace Catch {
const char* name; const char* name;
}; };
// 32kb for the alternate stack seems to be sufficient. However, this value
// is experimentally determined, so that's not guaranteed.
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
static SignalDefs signalDefs[] = { static SignalDefs signalDefs[] = {
{ SIGINT, "SIGINT - Terminal interrupt signal" }, { SIGINT, "SIGINT - Terminal interrupt signal" },
{ SIGILL, "SIGILL - Illegal instruction signal" }, { SIGILL, "SIGILL - Illegal instruction signal" },
@@ -10830,7 +10868,32 @@ namespace Catch {
{ SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
}; };
void FatalConditionHandler::handleSignal( int sig ) { // Older GCCs trigger -Wmissing-field-initializers for T foo = {}
// which is zero initialization, but not explicit. We want to avoid
// that.
#if defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
#endif
static char* altStackMem = nullptr;
static std::size_t altStackSize = 0;
static stack_t oldSigStack{};
static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
static void restorePreviousSignalHandlers() {
// We set signal handlers back to the previous ones. Hopefully
// nobody overwrote them in the meantime, and doesn't expect
// their signal handlers to live past ours given that they
// installed them after ours..
for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
}
static void handleSignal( int sig ) {
char const * name = "<unknown signal>"; char const * name = "<unknown signal>";
for (auto const& def : signalDefs) { for (auto const& def : signalDefs) {
if (sig == def.id) { if (sig == def.id) {
@@ -10838,16 +10901,33 @@ namespace Catch {
break; break;
} }
} }
reset(); // We need to restore previous signal handlers and let them do
reportFatal(name); // their thing, so that the users can have the debugger break
// when a signal is raised, and so on.
restorePreviousSignalHandlers();
reportFatal( name );
raise( sig ); raise( sig );
} }
FatalConditionHandler::FatalConditionHandler() { FatalConditionHandler::FatalConditionHandler() {
isSet = true; assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
if (altStackSize == 0) {
altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
}
altStackMem = new char[altStackSize]();
}
FatalConditionHandler::~FatalConditionHandler() {
delete[] altStackMem;
// We signal that another instance can be constructed by zeroing
// out the pointer.
altStackMem = nullptr;
}
void FatalConditionHandler::engage_platform() {
stack_t sigStack; stack_t sigStack;
sigStack.ss_sp = altStackMem; sigStack.ss_sp = altStackMem;
sigStack.ss_size = sigStackSize; sigStack.ss_size = altStackSize;
sigStack.ss_flags = 0; sigStack.ss_flags = 0;
sigaltstack(&sigStack, &oldSigStack); sigaltstack(&sigStack, &oldSigStack);
struct sigaction sa = { }; struct sigaction sa = { };
@@ -10859,40 +10939,17 @@ namespace Catch {
} }
} }
FatalConditionHandler::~FatalConditionHandler() {
reset();
}
void FatalConditionHandler::reset() {
if( isSet ) {
// Set signals back to previous values -- hopefully nobody overwrote them in the meantime
for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
}
// Return the old stack
sigaltstack(&oldSigStack, nullptr);
isSet = false;
}
}
bool FatalConditionHandler::isSet = false;
struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
stack_t FatalConditionHandler::oldSigStack = {};
char FatalConditionHandler::altStackMem[sigStackSize] = {};
} // namespace Catch
#else
namespace Catch {
void FatalConditionHandler::reset() {}
}
#endif // signals/SEH handling
#if defined(__GNUC__) #if defined(__GNUC__)
# pragma GCC diagnostic pop # pragma GCC diagnostic pop
#endif #endif
void FatalConditionHandler::disengage_platform() {
restorePreviousSignalHandlers();
}
} // end namespace Catch
#endif // CATCH_CONFIG_POSIX_SIGNALS
// end catch_fatal_condition.cpp // end catch_fatal_condition.cpp
// start catch_generators.cpp // start catch_generators.cpp
@@ -11447,7 +11504,8 @@ namespace {
return lhs == rhs; return lhs == rhs;
} }
auto ulpDiff = std::abs(lc - rc); // static cast as a workaround for IBM XLC
auto ulpDiff = std::abs(static_cast<FP>(lc - rc));
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff; return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
} }
@@ -11621,7 +11679,6 @@ Floating::WithinRelMatcher WithinRel(float target) {
} // namespace Matchers } // namespace Matchers
} // namespace Catch } // namespace Catch
// end catch_matchers_floating.cpp // end catch_matchers_floating.cpp
// start catch_matchers_generic.cpp // start catch_matchers_generic.cpp
@@ -12955,9 +13012,8 @@ namespace Catch {
} }
void RunContext::invokeActiveTestCase() { void RunContext::invokeActiveTestCase() {
FatalConditionHandler fatalConditionHandler; // Handle signals FatalConditionHandlerGuard _(&m_fatalConditionhandler);
m_activeTestCase->invoke(); m_activeTestCase->invoke();
fatalConditionHandler.reset();
} }
void RunContext::handleUnfinishedSections() { void RunContext::handleUnfinishedSections() {
@@ -15320,7 +15376,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 2, 13, 4, "", 0 ); static Version version( 2, 13, 6, "", 0 );
return version; return version;
} }