mirror of
https://github.com/catchorg/Catch2.git
synced 2025-04-29 04:03:51 +00:00
Compare commits
14 Commits
520b6dace9
...
313071e8fe
Author | SHA1 | Date | |
---|---|---|---|
|
313071e8fe | ||
|
f9bb2668e4 | ||
|
baf0cd0be4 | ||
|
c0d0a50bdb | ||
|
cbcab2dbcd | ||
|
ea44e73961 | ||
|
d61fe3ecc3 | ||
|
b325c6d81e | ||
|
d4a3cd9992 | ||
|
342dd3445c | ||
|
23760327ae | ||
|
48f220b68a | ||
|
031a57e7b7 | ||
|
cdf4748d1c |
@ -77,3 +77,13 @@ function(add_warnings_to_targets targets)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
# Adds flags required for reproducible build to the target
|
||||||
|
# Currently only supports GCC and Clang
|
||||||
|
function(add_build_reproducibility_settings target)
|
||||||
|
# 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(${target} PRIVATE "-ffile-prefix-map=${CATCH_DIR}=.")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
@ -60,7 +60,7 @@ with automatically registering their `TEST_CASE`s with CTest. They
|
|||||||
can be found in the `extras` folder, and are
|
can be found in the `extras` folder, and are
|
||||||
|
|
||||||
1) `Catch.cmake` (and its dependency `CatchAddTests.cmake`)
|
1) `Catch.cmake` (and its dependency `CatchAddTests.cmake`)
|
||||||
2) `ParseAndAddCatchTests.cmake`
|
2) `ParseAndAddCatchTests.cmake` (deprecated)
|
||||||
|
|
||||||
If Catch2 has been installed in system, both of these can be used after
|
If Catch2 has been installed in system, both of these can be used after
|
||||||
doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
|
doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
|
||||||
@ -179,10 +179,17 @@ the output file name e.g. ".xml".
|
|||||||
|
|
||||||
### `ParseAndAddCatchTests.cmake`
|
### `ParseAndAddCatchTests.cmake`
|
||||||
|
|
||||||
|
⚠ This script is [deprecated](https://github.com/catchorg/Catch2/pull/2120)
|
||||||
|
in Catch2 2.13.4 and superseded by the above approach using `catch_discover_tests`.
|
||||||
|
See [#2092](https://github.com/catchorg/Catch2/issues/2092) for details.
|
||||||
|
|
||||||
`ParseAndAddCatchTests` works by parsing all implementation files
|
`ParseAndAddCatchTests` works by parsing all implementation files
|
||||||
associated with the provided target, and registering them via CTest's
|
associated with the provided target, and registering them via CTest's
|
||||||
`add_test`. This approach has some limitations, such as the fact that
|
`add_test`. This approach has some limitations, such as the fact that
|
||||||
commented-out tests will be registered anyway.
|
commented-out tests will be registered anyway. More serious, only a
|
||||||
|
subset of the assertion macros currently available in Catch can be
|
||||||
|
detected by this script and tests with any macros that cannot be
|
||||||
|
parsed are *silently ignored*.
|
||||||
|
|
||||||
|
|
||||||
#### Usage
|
#### Usage
|
||||||
|
@ -9,6 +9,13 @@ either of these is a breaking change, and thus will not happen until
|
|||||||
at least the next major release.
|
at least the next major release.
|
||||||
|
|
||||||
|
|
||||||
|
### `ParseAndAddCatchTests.cmake`
|
||||||
|
|
||||||
|
The CMake/CTest integration using `ParseAndAddCatchTests.cmake` is deprecated,
|
||||||
|
as it can be replaced by `Catch.cmake` that provides the function
|
||||||
|
`catch_discover_tests` to get tests directly from a CMake target via the
|
||||||
|
command line interface instead of parsing C++ code with regular expressions.
|
||||||
|
|
||||||
|
|
||||||
## Planned changes
|
## Planned changes
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
# Release notes
|
# Release notes
|
||||||
**Contents**<br>
|
**Contents**<br>
|
||||||
[3.0.1](#301)<br>
|
[3.0.1](#301)<br>
|
||||||
|
[2.13.6](#2136)<br>
|
||||||
|
[2.13.5](#2135)<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>
|
||||||
[2.13.1](#2131)<br>
|
[2.13.1](#2131)<br>
|
||||||
@ -171,6 +174,48 @@ new design.
|
|||||||
* `catch2-with-main` also links in the default main
|
* `catch2-with-main` also links in the default main
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
||||||
|
* 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
|
||||||
|
|
||||||
|
### Improvements
|
||||||
|
* Improved the hashing algorithm used for shuffling test cases (#2070)
|
||||||
|
* `TEST_CASE`s that differ only in the last character should be properly shuffled
|
||||||
|
* Note that this means that v2.13.4 gives you a different order of test cases than 2.13.3, even given the same seed.
|
||||||
|
|
||||||
|
### Miscellaneous
|
||||||
|
* Deprecated `ParseAndAddCatchTests` CMake integration (#2092)
|
||||||
|
* It is impossible to implement it properly for all the different test case variants Catch2 provides, and there are better options provided.
|
||||||
|
* Use `catch_discover_tests` instead, which uses runtime information about available tests.
|
||||||
|
* Fixed bug in `catch_discover_tests` that would cause it to fail when used in specific project structures (#2119)
|
||||||
|
* Added Bazel build file
|
||||||
|
* Added an experimental static library target to CMake
|
||||||
|
|
||||||
|
|
||||||
## 2.13.3
|
## 2.13.3
|
||||||
|
|
||||||
|
@ -202,4 +202,5 @@ endfunction()
|
|||||||
|
|
||||||
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
||||||
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
|
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
|
||||||
|
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
|
||||||
)
|
)
|
||||||
|
@ -241,6 +241,7 @@ endfunction()
|
|||||||
|
|
||||||
# entry point
|
# entry point
|
||||||
function(ParseAndAddCatchTests TestTarget)
|
function(ParseAndAddCatchTests TestTarget)
|
||||||
|
message(DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'")
|
||||||
ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
|
ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
|
||||||
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
||||||
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
||||||
|
@ -240,6 +240,7 @@ add_library(Catch2 STATIC
|
|||||||
${BENCHMARK_HEADERS}
|
${BENCHMARK_HEADERS}
|
||||||
${BENCHMARK_SOURCES}
|
${BENCHMARK_SOURCES}
|
||||||
)
|
)
|
||||||
|
add_build_reproducibility_settings(Catch2)
|
||||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
@ -281,6 +282,7 @@ target_include_directories(Catch2
|
|||||||
add_library(Catch2WithMain STATIC
|
add_library(Catch2WithMain STATIC
|
||||||
${SOURCES_DIR}/internal/catch_main.cpp
|
${SOURCES_DIR}/internal/catch_main.cpp
|
||||||
)
|
)
|
||||||
|
add_build_reproducibility_settings(Catch2WithMain)
|
||||||
add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain)
|
add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain)
|
||||||
target_link_libraries(Catch2WithMain PUBLIC Catch2)
|
target_link_libraries(Catch2WithMain PUBLIC Catch2)
|
||||||
set_target_properties(Catch2WithMain
|
set_target_properties(Catch2WithMain
|
||||||
|
@ -66,7 +66,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) {
|
||||||
|
@ -171,7 +171,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;
|
||||||
|
|
||||||
@ -190,7 +190,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -116,8 +116,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 };
|
||||||
}
|
}
|
||||||
|
@ -38,9 +38,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" )
|
||||||
|
|
||||||
|
@ -5,30 +5,73 @@
|
|||||||
// https://www.boost.org/LICENSE_1_0.txt)
|
// https://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
|
||||||
// SPDX-License-Identifier: BSL-1.0
|
// SPDX-License-Identifier: BSL-1.0
|
||||||
|
|
||||||
|
/** \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 <catch2/internal/catch_fatal_condition_handler.hpp>
|
#include <catch2/internal/catch_fatal_condition_handler.hpp>
|
||||||
|
|
||||||
#include <catch2/internal/catch_context.hpp>
|
#include <catch2/internal/catch_context.hpp>
|
||||||
|
#include <catch2/internal/catch_enforce.hpp>
|
||||||
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
#include <catch2/interfaces/catch_interfaces_capture.hpp>
|
||||||
|
#include <catch2/internal/catch_windows_h_proxy.hpp>
|
||||||
|
|
||||||
#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 empirically, 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.
|
||||||
@ -41,7 +84,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);
|
||||||
@ -52,35 +95,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.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FatalConditionHandler::isSet = false;
|
// We do not attempt to unset the stack guarantee, because
|
||||||
ULONG FatalConditionHandler::guaranteeSize = 0;
|
// Windows does not support lowering the stack size guarantee.
|
||||||
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
|
FatalConditionHandler::~FatalConditionHandler() = default;
|
||||||
|
|
||||||
|
|
||||||
} // namespace Catch
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
|
void FatalConditionHandler::disengage_platform() {
|
||||||
|
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
|
||||||
|
CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
|
||||||
|
}
|
||||||
|
exceptionHandlerHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end namespace Catch
|
||||||
|
|
||||||
|
#endif // CATCH_CONFIG_WINDOWS_SEH
|
||||||
|
|
||||||
|
#if defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
@ -89,10 +149,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" },
|
||||||
@ -102,8 +158,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) {
|
||||||
@ -111,16 +191,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 = { };
|
||||||
@ -132,28 +229,15 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
#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
|
||||||
|
@ -10,57 +10,60 @@
|
|||||||
|
|
||||||
#include <catch2/internal/catch_platform.hpp>
|
#include <catch2/internal/catch_platform.hpp>
|
||||||
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
#include <catch2/internal/catch_compiler_capabilities.hpp>
|
||||||
#include <catch2/internal/catch_windows_h_proxy.hpp>
|
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#if defined( CATCH_CONFIG_WINDOWS_SEH )
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
struct FatalConditionHandler {
|
/**
|
||||||
|
* Wrapper for platform-specific fatal error (signals/SEH) handlers
|
||||||
|
*
|
||||||
|
* Tries to be cooperative with other handlers, and not step over
|
||||||
|
* other handlers. This means that unknown structured exceptions
|
||||||
|
* are passed on, previous signal handlers are called, and so on.
|
||||||
|
*
|
||||||
|
* Can only be instantiated once, and assumes that once a signal
|
||||||
|
* is caught, the binary will end up terminating. Thus, there
|
||||||
|
*/
|
||||||
|
class FatalConditionHandler {
|
||||||
|
bool m_started = false;
|
||||||
|
|
||||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
|
// 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();
|
||||||
static void reset();
|
~FatalConditionHandler();
|
||||||
~FatalConditionHandler() { reset(); }
|
|
||||||
|
|
||||||
private:
|
void engage() {
|
||||||
static bool isSet;
|
assert(!m_started && "Handler cannot be installed twice.");
|
||||||
static ULONG guaranteeSize;
|
m_started = true;
|
||||||
static PVOID exceptionHandlerHandle;
|
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 {
|
||||||
#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
|
FatalConditionHandler* m_handler;
|
||||||
|
public:
|
||||||
#include <signal.h>
|
FatalConditionHandlerGuard(FatalConditionHandler* handler):
|
||||||
|
m_handler(handler) {
|
||||||
namespace Catch {
|
m_handler->engage();
|
||||||
|
}
|
||||||
struct FatalConditionHandler {
|
~FatalConditionHandlerGuard() {
|
||||||
|
m_handler->disengage();
|
||||||
static bool isSet;
|
}
|
||||||
static struct sigaction oldSigActions[];
|
|
||||||
static stack_t oldSigStack;
|
|
||||||
static char altStackMem[];
|
|
||||||
|
|
||||||
static void handleSignal( int sig );
|
|
||||||
|
|
||||||
FatalConditionHandler();
|
|
||||||
~FatalConditionHandler() { reset(); }
|
|
||||||
static void reset();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Catch
|
} // end namespace Catch
|
||||||
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
struct FatalConditionHandler {};
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
|
#endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
|
||||||
|
@ -8,13 +8,16 @@
|
|||||||
#ifndef CATCH_PLATFORM_HPP_INCLUDED
|
#ifndef CATCH_PLATFORM_HPP_INCLUDED
|
||||||
#define CATCH_PLATFORM_HPP_INCLUDED
|
#define CATCH_PLATFORM_HPP_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
|
||||||
|
@ -459,10 +459,10 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void RunContext::invokeActiveTestCase() {
|
void RunContext::invokeActiveTestCase() {
|
||||||
// We need to register a handler for signals/structured exceptions
|
// We need to engage a handler for signals/structured exceptions
|
||||||
// before running the tests themselves, or the binary can crash
|
// before running the tests themselves, or the binary can crash
|
||||||
// without failed test being reported.
|
// without failed test being reported.
|
||||||
FatalConditionHandler _;
|
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
|
||||||
m_activeTestCase->invoke();
|
m_activeTestCase->invoke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <catch2/interfaces/catch_interfaces_runner.hpp>
|
#include <catch2/interfaces/catch_interfaces_runner.hpp>
|
||||||
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
|
||||||
#include <catch2/internal/catch_test_registry.hpp>
|
#include <catch2/internal/catch_test_registry.hpp>
|
||||||
|
#include <catch2/internal/catch_fatal_condition_handler.hpp>
|
||||||
#include <catch2/catch_test_case_info.hpp>
|
#include <catch2/catch_test_case_info.hpp>
|
||||||
#include <catch2/catch_message.hpp>
|
#include <catch2/catch_message.hpp>
|
||||||
#include <catch2/catch_totals.hpp>
|
#include <catch2/catch_totals.hpp>
|
||||||
@ -139,6 +140,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;
|
||||||
|
@ -22,24 +22,27 @@
|
|||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct HashTest {
|
struct TestHasher {
|
||||||
explicit HashTest(SimplePcg32& rng_inst) {
|
using hash_t = uint64_t;
|
||||||
basis = rng_inst();
|
|
||||||
basis <<= 32;
|
|
||||||
basis |= rng_inst();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t basis;
|
explicit TestHasher( hash_t hashSuffix ):
|
||||||
|
m_hashSuffix( hashSuffix ) {}
|
||||||
|
|
||||||
uint64_t operator()(TestCaseInfo const& t) const {
|
uint64_t m_hashSuffix;
|
||||||
// Modified FNV-1a hash
|
|
||||||
static constexpr uint64_t prime = 1099511628211;
|
uint32_t operator()( TestCaseInfo const& t ) const {
|
||||||
uint64_t hash = basis;
|
// FNV-1a hash with multiplication fold.
|
||||||
|
const hash_t prime = 1099511628211u;
|
||||||
|
hash_t hash = 14695981039346656037u;
|
||||||
for (const char c : t.name) {
|
for (const char c : t.name) {
|
||||||
hash ^= c;
|
hash ^= c;
|
||||||
hash *= prime;
|
hash *= prime;
|
||||||
}
|
}
|
||||||
return hash;
|
hash ^= m_hashSuffix;
|
||||||
|
hash *= prime;
|
||||||
|
const uint32_t low{ static_cast<uint32_t>(hash) };
|
||||||
|
const uint32_t high{ static_cast<uint32_t>(hash >> 32) };
|
||||||
|
return low * high;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
@ -56,8 +59,8 @@ namespace {
|
|||||||
}
|
}
|
||||||
case TestRunOrder::Randomized: {
|
case TestRunOrder::Randomized: {
|
||||||
seedRng(config);
|
seedRng(config);
|
||||||
HashTest h(rng());
|
TestHasher h{ config.rngSeed() };
|
||||||
std::vector<std::pair<uint64_t, TestCaseHandle>> indexed_tests;
|
std::vector<std::pair<TestHasher::hash_t, TestCaseHandle>> indexed_tests;
|
||||||
indexed_tests.reserve(unsortedTestCases.size());
|
indexed_tests.reserve(unsortedTestCases.size());
|
||||||
|
|
||||||
for (auto const& handle : unsortedTestCases) {
|
for (auto const& handle : unsortedTestCases) {
|
||||||
|
@ -54,7 +54,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,8 +127,8 @@ def updateVersionDefine(version):
|
|||||||
def updateVersionPlaceholder(filename, version):
|
def updateVersionPlaceholder(filename, version):
|
||||||
with open(filename, 'rb') as file:
|
with open(filename, 'rb') as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
placeholderRegex = re.compile(b' in Catch[0-9]? X.Y.Z')
|
placeholderRegex = re.compile(b'in Catch[0-9]? X.Y.Z')
|
||||||
replacement = ' in Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
|
replacement = 'in Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
|
||||||
with open(filename, 'wb') as file:
|
with open(filename, 'wb') as file:
|
||||||
for line in lines:
|
for line in lines:
|
||||||
file.write(placeholderRegex.sub(replacement, line))
|
file.write(placeholderRegex.sub(replacement, line))
|
||||||
|
Loading…
x
Reference in New Issue
Block a user