mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-11 16:05:40 +02:00
Compare commits
32 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c4e3767e26 | ||
![]() |
eea3e9a5b5 | ||
![]() |
7727c15290 | ||
![]() |
531a149ae7 | ||
![]() |
9683570be7 | ||
![]() |
581c46249a | ||
![]() |
b15b862d86 | ||
![]() |
6971476563 | ||
![]() |
5c88067bd3 | ||
![]() |
86a4d704bc | ||
![]() |
469a717395 | ||
![]() |
42e368dd0a | ||
![]() |
1ff1f2741d | ||
![]() |
269f96e2bc | ||
![]() |
cec35630fb | ||
![]() |
b8b03da534 | ||
![]() |
8f277a54c0 | ||
![]() |
4cb3220a8a | ||
![]() |
b025a007b9 | ||
![]() |
68975e3ff3 | ||
![]() |
bcb9ea8cb5 | ||
![]() |
d4c9494eb5 | ||
![]() |
bed285af07 | ||
![]() |
de6fe184a9 | ||
![]() |
fe3dddcc6d | ||
![]() |
18ab353e55 | ||
![]() |
2375a7f5b7 | ||
![]() |
fac517d571 | ||
![]() |
578f8b8006 | ||
![]() |
92f8b01dfa | ||
![]() |
dc7e705672 | ||
![]() |
3ba745552b |
22
.travis.yml
22
.travis.yml
@@ -258,6 +258,15 @@ matrix:
|
||||
addons: *gcc7
|
||||
env: COMPILER='g++-7' EXAMPLES=1 COVERAGE=1 EXTRAS=1 CPP17=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
sources: *all_sources
|
||||
packages: ['clang-5.0']
|
||||
env: COMPILER='clang++-5.0' CPP17=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
@@ -276,19 +285,6 @@ matrix:
|
||||
packages: ['clang-6.0', 'libstdc++-8-dev']
|
||||
env: COMPILER='clang++-6.0' CPP17=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
|
||||
|
||||
# 8/ Conan
|
||||
- language: python
|
||||
python:
|
||||
- "3.7"
|
||||
dist: xenial
|
||||
install:
|
||||
- pip install conan-package-tools
|
||||
env:
|
||||
- CONAN_GCC_VERSIONS=8
|
||||
- CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||
script:
|
||||
- python .conan/build.py
|
||||
|
||||
install:
|
||||
- DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
|
||||
- mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR}
|
||||
|
15
BUILD.bazel
15
BUILD.bazel
@@ -3,8 +3,15 @@ load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
|
||||
# Header-only rule to export catch2/catch.hpp.
|
||||
cc_library(
|
||||
name = "catch2",
|
||||
hdrs = ["single_include/catch2/catch.hpp"],
|
||||
visibility = ["//visibility:public"],
|
||||
includes = ["single_include/"],
|
||||
name = "catch2",
|
||||
hdrs = ["single_include/catch2/catch.hpp"],
|
||||
includes = ["single_include/"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "catch2_with_main",
|
||||
srcs = ["src/catch_with_main.cpp"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//:catch2"],
|
||||
)
|
||||
|
@@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.5)
|
||||
# disable testsuite in that case
|
||||
if(NOT DEFINED PROJECT_NAME)
|
||||
set(NOT_SUBPROJECT ON)
|
||||
else()
|
||||
set(NOT_SUBPROJECT OFF)
|
||||
endif()
|
||||
|
||||
# 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()
|
||||
|
||||
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.13.3)
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.13.7)
|
||||
|
||||
# Provide path for scripts
|
||||
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_EXAMPLES "Build documentation examples" 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_WERROR "Enable all warnings as errors" ON)
|
||||
option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON)
|
||||
option(CATCH_INSTALL_HELPERS "Install contrib alongside library" ON)
|
||||
|
||||
|
||||
# define some folders
|
||||
set(CATCH_DIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(SELF_TEST_DIR ${CATCH_DIR}/projects/SelfTest)
|
||||
@@ -103,6 +105,19 @@ endif()
|
||||
# provide a namespaced alias for clients to 'link' against if catch is included as a sub-project
|
||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||
|
||||
# Hacky support for compiling the impl into a static lib
|
||||
if (CATCH_BUILD_STATIC_LIBRARY)
|
||||
add_library(Catch2WithMain ${CMAKE_CURRENT_LIST_DIR}/src/catch_with_main.cpp)
|
||||
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
|
||||
# a subproject via `add_subdirectory`, or the destinations will break,
|
||||
# see https://github.com/catchorg/Catch2/issues/1373
|
||||
@@ -117,11 +132,17 @@ if (NOT_SUBPROJECT)
|
||||
${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
|
||||
install(
|
||||
TARGETS
|
||||
Catch2
|
||||
${InstallationTargets}
|
||||
EXPORT
|
||||
Catch2Targets
|
||||
DESTINATION
|
||||
|
@@ -9,7 +9,7 @@
|
||||
[](https://discord.gg/4CWS9zD)
|
||||
|
||||
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.13.3/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.7/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
|
||||
## Catch2 is released!
|
||||
|
||||
|
@@ -10,7 +10,7 @@ class CatchConan(ConanFile):
|
||||
homepage = url
|
||||
license = "BSL-1.0"
|
||||
exports = "LICENSE.txt"
|
||||
exports_sources = ("single_include/*", "CMakeLists.txt", "CMake/*", "contrib/*")
|
||||
exports_sources = ("single_include/*", "CMakeLists.txt", "CMake/*", "contrib/*", "src/*")
|
||||
generators = "cmake"
|
||||
|
||||
def package(self):
|
||||
|
@@ -202,4 +202,5 @@ endfunction()
|
||||
|
||||
set(_CATCH_DISCOVER_TESTS_SCRIPT
|
||||
${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
|
||||
CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
|
||||
)
|
||||
|
@@ -16,7 +16,10 @@ set(tests)
|
||||
|
||||
function(add_command NAME)
|
||||
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_]")
|
||||
set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
|
||||
else()
|
||||
|
@@ -200,7 +200,7 @@ function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
|
||||
# Escape commas in the test spec
|
||||
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
|
||||
# And properly introduced in 3.19 with the CMP0110 policy
|
||||
if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18")
|
||||
@@ -241,6 +241,7 @@ endfunction()
|
||||
|
||||
# entry point
|
||||
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}")
|
||||
get_target_property(SourceFiles ${TestTarget} SOURCES)
|
||||
ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
|
||||
|
@@ -57,7 +57,7 @@ with automatically registering their `TEST_CASE`s with CTest. They
|
||||
can be found in the `contrib` folder, and are
|
||||
|
||||
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
|
||||
doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
|
||||
@@ -176,10 +176,17 @@ the output file name e.g. ".xml".
|
||||
|
||||
### `ParseAndAddCatchTests.cmake`
|
||||
|
||||
⚠ This script is [deprecated](https://github.com/catchorg/Catch2/pull/2120)
|
||||
in Catch 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
|
||||
associated with the provided target, and registering them via CTest's
|
||||
`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
|
||||
|
@@ -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
|
||||
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.
|
||||
|
||||
_When submitting a pull request please do not include changes to the
|
||||
|
@@ -72,6 +72,13 @@ Instead you will have to write this:
|
||||
REQUIRE_THAT(foo(), m1 || m2 || m3);
|
||||
```
|
||||
|
||||
### `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
|
||||
|
||||
|
@@ -2,6 +2,10 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[2.13.7](#2137)<br>
|
||||
[2.13.6](#2136)<br>
|
||||
[2.13.5](#2135)<br>
|
||||
[2.13.4](#2134)<br>
|
||||
[2.13.3](#2133)<br>
|
||||
[2.13.2](#2132)<br>
|
||||
[2.13.1](#2131)<br>
|
||||
@@ -44,6 +48,62 @@
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
|
||||
## 2.13.7
|
||||
|
||||
### Fixes
|
||||
* Added missing `<iterator>` include in benchmarking. (#2231)
|
||||
* Fixed noexcept build with benchmarking enabled (#2235)
|
||||
* Fixed build for compilers with C++17 support but without C++17 library support (#2195)
|
||||
* JUnit only uses 3 decimal places when reporting durations (#2221)
|
||||
* `!mayfail` tagged tests are now marked as `skipped` in JUnit reporter output (#2116)
|
||||
|
||||
|
||||
## 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
|
||||
|
||||
### 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
|
||||
|
||||
### Fixes
|
||||
|
@@ -5,6 +5,7 @@
|
||||
[Short answer](#short-answer)<br>
|
||||
[Long answer](#long-answer)<br>
|
||||
[Practical example](#practical-example)<br>
|
||||
[Using the static library Catch2WithMain](#using-the-static-library-catch2withmain)<br>
|
||||
[Other possible solutions](#other-possible-solutions)<br>
|
||||
|
||||
Several people have reported that test code written with Catch takes much longer to compile than they would expect. Why is that?
|
||||
@@ -64,6 +65,39 @@ tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
|
||||
Failed 1 test case, failed 1 assertion.
|
||||
```
|
||||
|
||||
|
||||
## Using the static library Catch2WithMain
|
||||
|
||||
Catch2 also provides a static library that implements the runner. Note
|
||||
that this support is experimental, due to interactions between Catch2 v2
|
||||
implementation and C++ linking limitations.
|
||||
|
||||
As with the `Catch2` target, the `Catch2WithMain` CMake target can be used
|
||||
either from a subdirectory, or from installed build.
|
||||
|
||||
|
||||
### CMake
|
||||
```cmake
|
||||
add_executable(tests-factorial tests-factorial.cpp)
|
||||
|
||||
target_link_libraries(tests-factorial Catch2::Catch2WithMain)
|
||||
```
|
||||
|
||||
### bazel
|
||||
```python
|
||||
cc_test(
|
||||
name = "hello_world_test",
|
||||
srcs = [
|
||||
"test/hello_world_test.cpp",
|
||||
],
|
||||
deps = [
|
||||
"lib_hello_world",
|
||||
"@catch2//:catch2_with_main",
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Other possible solutions
|
||||
You can also opt to sacrifice some features in order to speed-up Catch's compilation times. For details see the [documentation on Catch's compile-time configuration](configuration.md#other-toggles).
|
||||
|
||||
|
@@ -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:
|
||||
*
|
||||
* - 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
|
||||
* https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
|
||||
*
|
||||
|
@@ -11,7 +11,7 @@
|
||||
|
||||
#define CATCH_VERSION_MAJOR 2
|
||||
#define CATCH_VERSION_MINOR 13
|
||||
#define CATCH_VERSION_PATCH 3
|
||||
#define CATCH_VERSION_PATCH 7
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang system_header
|
||||
|
@@ -19,6 +19,7 @@
|
||||
#include "detail/catch_run_for_at_least.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
|
@@ -68,7 +68,9 @@ namespace Catch {
|
||||
}
|
||||
template <typename Clock>
|
||||
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) {
|
||||
return Detail::measure<Clock>([k] {
|
||||
for (int i = 0; i < k; ++i) {
|
||||
|
@@ -56,7 +56,7 @@ namespace Catch {
|
||||
}
|
||||
iters *= 2;
|
||||
}
|
||||
throw optimized_away_error{};
|
||||
Catch::throw_exception(optimized_away_error{});
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
|
@@ -152,7 +152,7 @@ namespace Catch {
|
||||
double sb = stddev.point;
|
||||
double mn = mean.point / n;
|
||||
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 sb2 = sb * sb;
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace Catch {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -138,8 +138,8 @@ namespace Catch {
|
||||
double b2 = bias - z1;
|
||||
double a1 = a(b1);
|
||||
double a2 = a(b2);
|
||||
auto lo = std::max(cumn(a1), 0);
|
||||
auto hi = std::min(cumn(a2), n - 1);
|
||||
auto lo = (std::max)(cumn(a1), 0);
|
||||
auto hi = (std::min)(cumn(a2), n - 1);
|
||||
|
||||
return { point, resample[lo], resample[hi], confidence_level };
|
||||
}
|
||||
|
@@ -39,9 +39,9 @@
|
||||
|
||||
#endif
|
||||
|
||||
// We have to avoid both ICC and Clang, because they try to mask themselves
|
||||
// as gcc, and we want only GCC in this block
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
|
||||
// Only GCC compiler should be used in this block, so other compilers trying to
|
||||
// mask themselves as GCC should be ignored.
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
|
||||
|
||||
@@ -234,7 +234,7 @@
|
||||
// Check if byte is available and usable
|
||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# include <cstddef>
|
||||
# if __cpp_lib_byte > 0
|
||||
# if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||
# endif
|
||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
|
@@ -22,7 +22,7 @@ namespace Catch {
|
||||
// Extracts the actual name part of an enum instance
|
||||
// In other words, it returns the Blue part of Bikeshed::Colour::Blue
|
||||
StringRef extractInstanceName(StringRef enumInstance) {
|
||||
// Find last occurence of ":"
|
||||
// Find last occurrence of ":"
|
||||
size_t name_start = enumInstance.size();
|
||||
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
|
||||
--name_start;
|
||||
|
@@ -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_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__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
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 )
|
||||
|
||||
namespace {
|
||||
// Report the error condition
|
||||
//! Signals fatal error message to the run context
|
||||
void reportFatal( char const * const 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 )
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct SignalDefs { DWORD id; const char* name; };
|
||||
|
||||
// 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" },
|
||||
};
|
||||
|
||||
LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
|
||||
for (auto const& def : signalDefs) {
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
||||
reportFatal(def.name);
|
||||
@@ -54,39 +96,52 @@ namespace Catch {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
isSet = true;
|
||||
// 32k seems enough for Catch to handle stack overflow,
|
||||
// but the value was found experimentally, so there is no strong guarantee
|
||||
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);
|
||||
}
|
||||
// Since we do not support multiple instantiations, we put these
|
||||
// into global variables and rely on cleaning them up in outlined
|
||||
// constructors/destructors
|
||||
static PVOID exceptionHandlerHandle = nullptr;
|
||||
|
||||
void FatalConditionHandler::reset() {
|
||||
if (isSet) {
|
||||
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
|
||||
SetThreadStackGuarantee(&guaranteeSize);
|
||||
exceptionHandlerHandle = nullptr;
|
||||
isSet = false;
|
||||
|
||||
// For MSVC, we reserve part of the stack memory for handling
|
||||
// memory overflow structured exception.
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
|
||||
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() {
|
||||
reset();
|
||||
// We do not attempt to unset the stack guarantee, because
|
||||
// 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;
|
||||
ULONG FatalConditionHandler::guaranteeSize = 0;
|
||||
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
|
||||
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 {
|
||||
|
||||
@@ -94,10 +149,6 @@ namespace Catch {
|
||||
int id;
|
||||
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[] = {
|
||||
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
||||
@@ -108,8 +159,32 @@ namespace Catch {
|
||||
{ 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>";
|
||||
for (auto const& def : signalDefs) {
|
||||
if (sig == def.id) {
|
||||
@@ -117,16 +192,33 @@ namespace Catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
reportFatal(name);
|
||||
// We need to restore previous signal handlers and let them do
|
||||
// their thing, so that the users can have the debugger break
|
||||
// when a signal is raised, and so on.
|
||||
restorePreviousSignalHandlers();
|
||||
reportFatal( name );
|
||||
raise( sig );
|
||||
}
|
||||
|
||||
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;
|
||||
sigStack.ss_sp = altStackMem;
|
||||
sigStack.ss_size = sigStackSize;
|
||||
sigStack.ss_size = altStackSize;
|
||||
sigStack.ss_flags = 0;
|
||||
sigaltstack(&sigStack, &oldSigStack);
|
||||
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__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
restorePreviousSignalHandlers();
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_CONFIG_POSIX_SIGNALS
|
||||
|
@@ -11,59 +11,58 @@
|
||||
|
||||
#include "catch_platform.h"
|
||||
#include "catch_compiler_capabilities.h"
|
||||
#include "catch_windows_h_proxy.h"
|
||||
|
||||
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH )
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct FatalConditionHandler {
|
||||
|
||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
|
||||
FatalConditionHandler();
|
||||
static void reset();
|
||||
~FatalConditionHandler();
|
||||
|
||||
private:
|
||||
static bool isSet;
|
||||
static ULONG guaranteeSize;
|
||||
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 );
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
|
||||
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
|
||||
|
||||
|
||||
#else
|
||||
|
||||
namespace Catch {
|
||||
struct FatalConditionHandler {
|
||||
void reset();
|
||||
//! Simple RAII guard for (dis)engaging the FatalConditionHandler
|
||||
class FatalConditionHandlerGuard {
|
||||
FatalConditionHandler* m_handler;
|
||||
public:
|
||||
FatalConditionHandlerGuard(FatalConditionHandler* handler):
|
||||
m_handler(handler) {
|
||||
m_handler->engage();
|
||||
}
|
||||
~FatalConditionHandlerGuard() {
|
||||
m_handler->disengage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
|
||||
|
@@ -21,6 +21,8 @@
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "benchmark/catch_estimate.hpp"
|
||||
#include "benchmark/catch_outlier_classification.hpp"
|
||||
|
||||
#include <iterator>
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
|
||||
|
@@ -55,7 +55,8 @@ namespace {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -234,4 +235,3 @@ Floating::WithinRelMatcher WithinRel(float target) {
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
|
@@ -9,13 +9,16 @@
|
||||
#ifndef 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__
|
||||
# include <TargetConditionals.h>
|
||||
# if TARGET_OS_OSX == 1
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif TARGET_OS_IPHONE == 1
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
# include <TargetConditionals.h>
|
||||
# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
|
||||
(defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
|
||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||
# define CATCH_PLATFORM_LINUX
|
||||
|
@@ -452,9 +452,8 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void RunContext::invokeActiveTestCase() {
|
||||
FatalConditionHandler fatalConditionHandler; // Handle signals
|
||||
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
|
||||
m_activeTestCase->invoke();
|
||||
fatalConditionHandler.reset();
|
||||
}
|
||||
|
||||
void RunContext::handleUnfinishedSections() {
|
||||
|
@@ -146,6 +146,7 @@ namespace Catch {
|
||||
std::vector<SectionEndInfo> m_unfinishedSections;
|
||||
std::vector<ITracker*> m_activeSections;
|
||||
TrackerContext m_trackerContext;
|
||||
FatalConditionHandler m_fatalConditionhandler;
|
||||
bool m_lastAssertionPassed = false;
|
||||
bool m_shouldReportUnexpected = true;
|
||||
bool m_includeSuccessfulResults;
|
||||
|
@@ -22,24 +22,28 @@ namespace Catch {
|
||||
|
||||
namespace {
|
||||
struct TestHasher {
|
||||
explicit TestHasher(Catch::SimplePcg32& rng_instance) {
|
||||
basis = rng_instance();
|
||||
basis <<= 32;
|
||||
basis |= rng_instance();
|
||||
}
|
||||
using hash_t = uint64_t;
|
||||
|
||||
uint64_t basis;
|
||||
explicit TestHasher( hash_t hashSuffix ):
|
||||
m_hashSuffix{ hashSuffix } {}
|
||||
|
||||
uint64_t operator()(TestCase const& t) const {
|
||||
// Modified FNV-1a hash
|
||||
static constexpr uint64_t prime = 1099511628211;
|
||||
uint64_t hash = basis;
|
||||
for (const char c : t.name) {
|
||||
uint32_t operator()( TestCase const& t ) const {
|
||||
// FNV-1a hash with multiplication fold.
|
||||
const hash_t prime = 1099511628211u;
|
||||
hash_t hash = 14695981039346656037u;
|
||||
for ( const char c : t.name ) {
|
||||
hash ^= c;
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
hash_t m_hashSuffix;
|
||||
};
|
||||
} // end unnamed namespace
|
||||
|
||||
@@ -58,9 +62,9 @@ namespace Catch {
|
||||
|
||||
case RunTests::InRandomOrder: {
|
||||
seedRng( config );
|
||||
TestHasher h( rng() );
|
||||
TestHasher h{ config.rngSeed() };
|
||||
|
||||
using hashedTest = std::pair<uint64_t, TestCase const*>;
|
||||
using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
|
||||
std::vector<hashedTest> indexed_tests;
|
||||
indexed_tests.reserve( unsortedTestCases.size() );
|
||||
|
||||
|
@@ -37,7 +37,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 2, 13, 3, "", 0 );
|
||||
static Version version( 2, 13, 7, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
|
@@ -18,6 +18,7 @@
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -45,7 +46,7 @@ namespace Catch {
|
||||
#else
|
||||
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
||||
#endif
|
||||
return std::string(timeStamp);
|
||||
return std::string(timeStamp, timeStampSize-1);
|
||||
}
|
||||
|
||||
std::string fileNameTag(const std::vector<std::string> &tags) {
|
||||
@@ -56,6 +57,17 @@ namespace Catch {
|
||||
return it->substr(1);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Formats the duration in seconds to 3 decimal places.
|
||||
// This is done because some genius defined Maven Surefire schema
|
||||
// in a way that only accepts 3 decimal places, and tools like
|
||||
// Jenkins use that schema for validation JUnit reporter output.
|
||||
std::string formatDuration( double seconds ) {
|
||||
ReusableStringStream rss;
|
||||
rss << std::fixed << std::setprecision( 3 ) << seconds;
|
||||
return rss.str();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
JunitReporter::JunitReporter( ReporterConfig const& _config )
|
||||
@@ -125,7 +137,7 @@ namespace Catch {
|
||||
if( m_config->showDurations() == ShowDurations::Never )
|
||||
xml.writeAttribute( "time", "" );
|
||||
else
|
||||
xml.writeAttribute( "time", suiteTime );
|
||||
xml.writeAttribute( "time", formatDuration( suiteTime ) );
|
||||
xml.writeAttribute( "timestamp", getCurrentTimestamp() );
|
||||
|
||||
// Write properties if there are any
|
||||
@@ -170,12 +182,13 @@ namespace Catch {
|
||||
if ( !m_config->name().empty() )
|
||||
className = m_config->name() + "." + className;
|
||||
|
||||
writeSection( className, "", rootSection );
|
||||
writeSection( className, "", rootSection, stats.testInfo.okToFail() );
|
||||
}
|
||||
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode ) {
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail) {
|
||||
std::string name = trim( sectionNode.stats.sectionInfo.name );
|
||||
if( !rootName.empty() )
|
||||
name = rootName + '/' + name;
|
||||
@@ -192,15 +205,21 @@ namespace Catch {
|
||||
xml.writeAttribute( "classname", className );
|
||||
xml.writeAttribute( "name", name );
|
||||
}
|
||||
xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
|
||||
xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
|
||||
// This is not ideal, but it should be enough to mimic gtest's
|
||||
// junit output.
|
||||
// Ideally the JUnit reporter would also handle `skipTest`
|
||||
// events and write those out appropriately.
|
||||
xml.writeAttribute( "status", "run" );
|
||||
|
||||
if (sectionNode.stats.assertions.failedButOk) {
|
||||
xml.scopedElement("skipped")
|
||||
.writeAttribute("message", "TEST_CASE tagged with !mayfail");
|
||||
}
|
||||
|
||||
writeAssertions( sectionNode );
|
||||
|
||||
|
||||
if( !sectionNode.stdOut.empty() )
|
||||
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
|
||||
if( !sectionNode.stdErr.empty() )
|
||||
@@ -208,9 +227,9 @@ namespace Catch {
|
||||
}
|
||||
for( auto const& childNode : sectionNode.childSections )
|
||||
if( className.empty() )
|
||||
writeSection( name, "", *childNode );
|
||||
writeSection( name, "", *childNode, testOkToFail );
|
||||
else
|
||||
writeSection( className, name, *childNode );
|
||||
writeSection( className, name, *childNode, testOkToFail );
|
||||
}
|
||||
|
||||
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
|
||||
|
@@ -41,9 +41,10 @@ namespace Catch {
|
||||
|
||||
void writeTestCase(TestCaseNode const& testCaseNode);
|
||||
|
||||
void writeSection(std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode);
|
||||
void writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail );
|
||||
|
||||
void writeAssertions(SectionNode const& sectionNode);
|
||||
void writeAssertion(AssertionStats const& stats);
|
||||
|
@@ -846,6 +846,10 @@ Matchers.tests.cpp:<line number>: passed: testStringForMatching(), (Contains("st
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
|
||||
Matchers.tests.cpp:<line number>: passed: testStringForMatching(), !Contains("different") for: "this string contains 'abc' as a substring" not contains: "different"
|
||||
Matchers.tests.cpp:<line number>: failed: testStringForMatching(), !Contains("substring") for: "this string contains 'abc' as a substring" not contains: "substring"
|
||||
Condition.tests.cpp:<line number>: failed: explicitly
|
||||
Condition.tests.cpp:<line number>: failed: explicitly
|
||||
Condition.tests.cpp:<line number>: failed: explicitly
|
||||
Condition.tests.cpp:<line number>: failed: explicitly
|
||||
Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
|
||||
Exception.tests.cpp:<line number>: failed: thisThrows(), "should fail" for: "expected exception" equals: "should fail"
|
||||
Generators.tests.cpp:<line number>: passed: values > -6 for: 3 > -6
|
||||
|
@@ -694,6 +694,46 @@ Matchers.tests.cpp:<line number>: FAILED:
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" not contains: "substring"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
A
|
||||
1
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
A
|
||||
2
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
B
|
||||
1
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
B
|
||||
2
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mismatching exception messages failing the test
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -1380,6 +1420,6 @@ due to unexpected exception with message:
|
||||
Why would you throw a std::string?
|
||||
|
||||
===============================================================================
|
||||
test cases: 322 | 248 passed | 70 failed | 4 failed as expected
|
||||
assertions: 1759 | 1607 passed | 131 failed | 21 failed as expected
|
||||
test cases: 323 | 248 passed | 70 failed | 5 failed as expected
|
||||
assertions: 1763 | 1607 passed | 131 failed | 25 failed as expected
|
||||
|
||||
|
@@ -6492,6 +6492,46 @@ Matchers.tests.cpp:<line number>: FAILED:
|
||||
with expansion:
|
||||
"this string contains 'abc' as a substring" not contains: "substring"
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
A
|
||||
1
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
A
|
||||
2
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
B
|
||||
1
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mayfail test case with nested sections
|
||||
B
|
||||
2
|
||||
-------------------------------------------------------------------------------
|
||||
Condition.tests.cpp:<line number>
|
||||
...............................................................................
|
||||
|
||||
Condition.tests.cpp:<line number>: FAILED:
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
Mismatching exception messages failing the test
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -14138,6 +14178,6 @@ Misc.tests.cpp:<line number>
|
||||
Misc.tests.cpp:<line number>: PASSED:
|
||||
|
||||
===============================================================================
|
||||
test cases: 322 | 232 passed | 86 failed | 4 failed as expected
|
||||
assertions: 1776 | 1607 passed | 148 failed | 21 failed as expected
|
||||
test cases: 323 | 232 passed | 86 failed | 5 failed as expected
|
||||
assertions: 1780 | 1607 passed | 148 failed | 25 failed as expected
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<testsuitesloose text artifact
|
||||
>
|
||||
<testsuite name="<exe-name>" errors="17" failures="132" tests="1777" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<testsuite name="<exe-name>" errors="17" failures="132" tests="1781" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
|
||||
<properties>
|
||||
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals]"/>
|
||||
<property name="random-seed" value="1"/>
|
||||
@@ -48,6 +48,7 @@ Nor would this
|
||||
<testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<error type="TEST_CASE">
|
||||
FAILED:
|
||||
expected exception
|
||||
@@ -56,6 +57,7 @@ Exception.tests.cpp:<line number>
|
||||
</error>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/inside REQUIRE_NOTHROW" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<error message="thisThrows()" type="REQUIRE_NOTHROW">
|
||||
FAILED:
|
||||
REQUIRE_NOTHROW( thisThrows() )
|
||||
@@ -68,6 +70,7 @@ Exception.tests.cpp:<line number>
|
||||
<testcase classname="<exe-name>.global" name="#809" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="#833" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="#835 -- errno should not be touched by Catch" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure message="f() == 0" type="CHECK">
|
||||
FAILED:
|
||||
CHECK( f() == 0 )
|
||||
@@ -443,6 +446,7 @@ Matchers.tests.cpp:<line number>
|
||||
<testcase classname="<exe-name>.global" name="Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Epsilon only applies to Approx's value" time="{duration}" status="run"/>
|
||||
<testcase classname="<exe-name>.global" name="Equality checks that should fail" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure message="data.int_seven == 6" type="CHECK">
|
||||
FAILED:
|
||||
CHECK( data.int_seven == 6 )
|
||||
@@ -736,6 +740,7 @@ Message.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Inequality checks that should fail" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure message="data.int_seven != 7" type="CHECK">
|
||||
FAILED:
|
||||
CHECK( data.int_seven != 7 )
|
||||
@@ -799,6 +804,34 @@ with expansion:
|
||||
Matchers.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Mayfail test case with nested sections/1/A" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure type="FAIL">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Mayfail test case with nested sections/2/A" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure type="FAIL">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Mayfail test case with nested sections/1/B" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure type="FAIL">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Mayfail test case with nested sections/2/B" time="{duration}" status="run">
|
||||
<skipped message="TEST_CASE tagged with !mayfail"/>
|
||||
<failure type="FAIL">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</failure>
|
||||
</testcase>
|
||||
<testcase classname="<exe-name>.global" name="Mismatching exception messages failing the test" time="{duration}" status="run">
|
||||
<failure message="thisThrows(), "should fail"" type="REQUIRE_THROWS_WITH">
|
||||
FAILED:
|
||||
|
@@ -575,6 +575,30 @@ Condition.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="Inequality checks that should succeed" duration="{duration}"/>
|
||||
<testCase name="Mayfail test case with nested sections/1/A" duration="{duration}">
|
||||
<skipped message="FAIL()">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="Mayfail test case with nested sections/2/A" duration="{duration}">
|
||||
<skipped message="FAIL()">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="Mayfail test case with nested sections/1/B" duration="{duration}">
|
||||
<skipped message="FAIL()">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="Mayfail test case with nested sections/2/B" duration="{duration}">
|
||||
<skipped message="FAIL()">
|
||||
FAILED:
|
||||
Condition.tests.cpp:<line number>
|
||||
</skipped>
|
||||
</testCase>
|
||||
<testCase name="Ordering comparison checks that should fail" duration="{duration}">
|
||||
<failure message="CHECK(data.int_seven > 7)">
|
||||
FAILED:
|
||||
|
@@ -7956,6 +7956,43 @@ Nor would this
|
||||
</Expression>
|
||||
<OverallResult success="false"/>
|
||||
</TestCase>
|
||||
<TestCase name="Mayfail test case with nested sections" tags="[!mayfail]" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Section name="A" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Section name="1" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Failure filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" />
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<Section name="A" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Section name="2" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Failure filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" />
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<Section name="A" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<OverallResults successes="0" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<Section name="B" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Section name="1" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Failure filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" />
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<Section name="B" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Section name="2" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<Failure filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" />
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<OverallResults successes="0" failures="0" expectedFailures="1"/>
|
||||
</Section>
|
||||
<Section name="B" filename="projects/<exe-name>/UsageTests/Condition.tests.cpp" >
|
||||
<OverallResults successes="0" failures="0" expectedFailures="0"/>
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<TestCase name="Mismatching exception messages failing the test" tags="[!hide][!throws][.][failing]" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" >
|
||||
<Expression success="true" type="REQUIRE_THROWS_WITH" filename="projects/<exe-name>/UsageTests/Exception.tests.cpp" >
|
||||
<Original>
|
||||
@@ -16722,9 +16759,9 @@ loose text artifact
|
||||
</Section>
|
||||
<OverallResult success="true"/>
|
||||
</TestCase>
|
||||
<OverallResults successes="1607" failures="149" expectedFailures="21"/>
|
||||
<OverallResultsCases successes="232" failures="86" expectedFailures="4"/>
|
||||
<OverallResults successes="1607" failures="149" expectedFailures="25"/>
|
||||
<OverallResultsCases successes="232" failures="86" expectedFailures="5"/>
|
||||
</Group>
|
||||
<OverallResults successes="1607" failures="148" expectedFailures="21"/>
|
||||
<OverallResultsCases successes="232" failures="86" expectedFailures="4"/>
|
||||
<OverallResults successes="1607" failures="148" expectedFailures="25"/>
|
||||
<OverallResultsCases successes="232" failures="86" expectedFailures="5"/>
|
||||
</Catch>
|
||||
|
@@ -11,7 +11,7 @@
|
||||
// Wdouble-promotion is not supported until 3.8
|
||||
# if (__clang_major__ > 3) || (__clang_major__ == 3 && __clang_minor__ > 7)
|
||||
# pragma clang diagnostic ignored "-Wdouble-promotion"
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include "catch.hpp"
|
||||
@@ -89,6 +89,19 @@ TEST_CASE( "Equality checks that should fail", "[.][failing][!mayfail]" )
|
||||
CHECK( x == Approx( 1.301 ) );
|
||||
}
|
||||
|
||||
// Needed to test junit reporter's handling of mayfail test cases and sections
|
||||
TEST_CASE("Mayfail test case with nested sections", "[!mayfail]") {
|
||||
SECTION("A") {
|
||||
SECTION("1") { FAIL(); }
|
||||
SECTION("2") { FAIL(); }
|
||||
}
|
||||
SECTION("B") {
|
||||
SECTION("1") { FAIL(); }
|
||||
SECTION("2") { FAIL(); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE( "Inequality checks that should succeed" )
|
||||
{
|
||||
TestData data;
|
||||
|
@@ -58,12 +58,12 @@ TEST_CASE("tables", "[generators]") {
|
||||
|
||||
// Structured bindings make the table utility much nicer to use
|
||||
TEST_CASE( "strlen2", "[approvals][generators]" ) {
|
||||
auto [test_input, expected] = GENERATE( table<std::string, size_t>({
|
||||
{"one", 3},
|
||||
{"two", 3},
|
||||
{"three", 5},
|
||||
{"four", 4}
|
||||
}));
|
||||
using tuple_type = std::tuple<std::string, int>; // see above workaround
|
||||
auto [test_input, expected] =
|
||||
GENERATE( table<std::string, size_t>( { tuple_type{ "one", 3 },
|
||||
tuple_type{ "two", 3 },
|
||||
tuple_type{ "three", 5 },
|
||||
tuple_type{ "four", 4 } } ) );
|
||||
|
||||
REQUIRE( test_input.size() == expected );
|
||||
}
|
||||
@@ -99,11 +99,9 @@ TEST_CASE( "strlen3", "[generators]" ) {
|
||||
static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
|
||||
|
||||
SCENARIO("Eating cucumbers", "[generators][approvals]") {
|
||||
|
||||
auto [start, eat, left] = GENERATE( table<int,int,int> ({
|
||||
{ 12, 5, 7 },
|
||||
{ 20, 5, 15 }
|
||||
}));
|
||||
using tuple_type = std::tuple<int, int, int>;
|
||||
auto [start, eat, left] = GENERATE( table<int, int, int>(
|
||||
{ tuple_type{ 12, 5, 7 }, tuple_type{ 20, 5, 15 } } ) );
|
||||
|
||||
GIVEN( "there are " << start << " cucumbers" )
|
||||
WHEN( "I eat " << eat << " cucumbers" )
|
||||
|
@@ -28,7 +28,10 @@ filelocParser = re.compile(r'''
|
||||
''', re.VERBOSE)
|
||||
lineNumberParser = re.compile(r' line="[0-9]*"')
|
||||
hexParser = re.compile(r'\b(0[xX][0-9a-fA-F]+)\b')
|
||||
durationsParser = re.compile(r' time="[0-9]*\.[0-9]*"')
|
||||
# Note: junit must serialize time with 3 (or or less) decimal places
|
||||
# before generalizing this parser, make sure that this is checked
|
||||
# in other places too.
|
||||
junitDurationsParser = re.compile(r' time="[0-9]*\.[0-9]{3}"')
|
||||
sonarqubeDurationParser = re.compile(r' duration="[0-9]+"')
|
||||
timestampsParser = re.compile(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}Z')
|
||||
versionParser = re.compile(r'Catch v[0-9]+\.[0-9]+\.[0-9]+(-develop\.[0-9]+)?')
|
||||
@@ -138,7 +141,7 @@ def filterLine(line, isCompact):
|
||||
line = hexParser.sub("0x<hex digits>", line)
|
||||
|
||||
# strip durations and timestamps
|
||||
line = durationsParser.sub(' time="{duration}"', line)
|
||||
line = junitDurationsParser.sub(' time="{duration}"', line)
|
||||
line = sonarqubeDurationParser.sub(' duration="{duration}"', line)
|
||||
line = timestampsParser.sub('{iso8601-timestamp}', line)
|
||||
line = specialCaseParser.sub('file:\g<1>', line)
|
||||
|
@@ -129,8 +129,8 @@ def updateVersionDefine(version):
|
||||
def updateVersionPlaceholder(filename, version):
|
||||
with open(filename, 'rb') as file:
|
||||
lines = file.readlines()
|
||||
placeholderRegex = re.compile(b' in Catch X.Y.Z')
|
||||
replacement = ' in Catch {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
|
||||
placeholderRegex = re.compile(b'in Catch X.Y.Z')
|
||||
replacement = 'in Catch {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
|
||||
with open(filename, 'wb') as file:
|
||||
for line in lines:
|
||||
file.write(placeholderRegex.sub(replacement, line))
|
||||
|
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* Catch v2.13.3
|
||||
* Generated: 2020-10-31 18:20:31.045274
|
||||
* Catch v2.13.7
|
||||
* Generated: 2021-07-28 20:29:27.753164
|
||||
* ----------------------------------------------------------
|
||||
* 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
|
||||
* 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_MINOR 13
|
||||
#define CATCH_VERSION_PATCH 3
|
||||
#define CATCH_VERSION_PATCH 7
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang system_header
|
||||
@@ -66,13 +66,16 @@
|
||||
#if !defined(CATCH_CONFIG_IMPL_ONLY)
|
||||
// start catch_platform.h
|
||||
|
||||
// See e.g.:
|
||||
// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
|
||||
#ifdef __APPLE__
|
||||
# include <TargetConditionals.h>
|
||||
# if TARGET_OS_OSX == 1
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif TARGET_OS_IPHONE == 1
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
# include <TargetConditionals.h>
|
||||
# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
|
||||
(defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
|
||||
# define CATCH_PLATFORM_MAC
|
||||
# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
|
||||
# define CATCH_PLATFORM_IPHONE
|
||||
# endif
|
||||
|
||||
#elif defined(linux) || defined(__linux) || defined(__linux__)
|
||||
# define CATCH_PLATFORM_LINUX
|
||||
@@ -132,9 +135,9 @@ namespace Catch {
|
||||
|
||||
#endif
|
||||
|
||||
// We have to avoid both ICC and Clang, because they try to mask themselves
|
||||
// as gcc, and we want only GCC in this block
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
|
||||
// Only GCC compiler should be used in this block, so other compilers trying to
|
||||
// mask themselves as GCC should be ignored.
|
||||
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
|
||||
|
||||
@@ -323,7 +326,7 @@ namespace Catch {
|
||||
// Check if byte is available and usable
|
||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# include <cstddef>
|
||||
# if __cpp_lib_byte > 0
|
||||
# if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||
# endif
|
||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
@@ -5455,6 +5458,8 @@ namespace Catch {
|
||||
} // namespace Catch
|
||||
|
||||
// end catch_outlier_classification.hpp
|
||||
|
||||
#include <iterator>
|
||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||
|
||||
#include <string>
|
||||
@@ -6339,9 +6344,10 @@ namespace Catch {
|
||||
|
||||
void writeTestCase(TestCaseNode const& testCaseNode);
|
||||
|
||||
void writeSection(std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode);
|
||||
void writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail );
|
||||
|
||||
void writeAssertions(SectionNode const& sectionNode);
|
||||
void writeAssertion(AssertionStats const& stats);
|
||||
@@ -6876,7 +6882,7 @@ namespace Catch {
|
||||
}
|
||||
iters *= 2;
|
||||
}
|
||||
throw optimized_away_error{};
|
||||
Catch::throw_exception(optimized_away_error{});
|
||||
}
|
||||
} // namespace Detail
|
||||
} // namespace Benchmark
|
||||
@@ -6884,6 +6890,7 @@ namespace Catch {
|
||||
|
||||
// end catch_run_for_at_least.hpp
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
@@ -7054,8 +7061,8 @@ namespace Catch {
|
||||
double b2 = bias - z1;
|
||||
double a1 = a(b1);
|
||||
double a2 = a(b2);
|
||||
auto lo = std::max(cumn(a1), 0);
|
||||
auto hi = std::min(cumn(a2), n - 1);
|
||||
auto lo = (std::max)(cumn(a1), 0);
|
||||
auto hi = (std::min)(cumn(a2), n - 1);
|
||||
|
||||
return { point, resample[lo], resample[hi], confidence_level };
|
||||
}
|
||||
@@ -7124,7 +7131,9 @@ namespace Catch {
|
||||
}
|
||||
template <typename Clock>
|
||||
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) {
|
||||
return Detail::measure<Clock>([k] {
|
||||
for (int i = 0; i < k; ++i) {
|
||||
@@ -7771,7 +7780,7 @@ namespace Catch {
|
||||
double sb = stddev.point;
|
||||
double mn = mean.point / n;
|
||||
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 sb2 = sb * sb;
|
||||
|
||||
@@ -7790,7 +7799,7 @@ namespace Catch {
|
||||
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) {
|
||||
@@ -7980,86 +7989,58 @@ namespace Catch {
|
||||
|
||||
// start catch_fatal_condition.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
|
||||
#if defined( CATCH_CONFIG_WINDOWS_SEH )
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct FatalConditionHandler {
|
||||
|
||||
static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
|
||||
FatalConditionHandler();
|
||||
static void reset();
|
||||
~FatalConditionHandler();
|
||||
|
||||
private:
|
||||
static bool isSet;
|
||||
static ULONG guaranteeSize;
|
||||
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 );
|
||||
// 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;
|
||||
|
||||
// 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();
|
||||
|
||||
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
|
||||
|
||||
#else
|
||||
|
||||
namespace Catch {
|
||||
struct FatalConditionHandler {
|
||||
void reset();
|
||||
//! Simple RAII guard for (dis)engaging the FatalConditionHandler
|
||||
class FatalConditionHandlerGuard {
|
||||
FatalConditionHandler* m_handler;
|
||||
public:
|
||||
FatalConditionHandlerGuard(FatalConditionHandler* handler):
|
||||
m_handler(handler) {
|
||||
m_handler->engage();
|
||||
}
|
||||
~FatalConditionHandlerGuard() {
|
||||
m_handler->disengage();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
} // end namespace Catch
|
||||
|
||||
// end catch_fatal_condition.h
|
||||
#include <string>
|
||||
@@ -8185,6 +8166,7 @@ namespace Catch {
|
||||
std::vector<SectionEndInfo> m_unfinishedSections;
|
||||
std::vector<ITracker*> m_activeSections;
|
||||
TrackerContext m_trackerContext;
|
||||
FatalConditionHandler m_fatalConditionhandler;
|
||||
bool m_lastAssertionPassed = false;
|
||||
bool m_shouldReportUnexpected = true;
|
||||
bool m_includeSuccessfulResults;
|
||||
@@ -10057,6 +10039,36 @@ namespace Catch {
|
||||
}
|
||||
|
||||
// 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>
|
||||
|
||||
namespace Catch {
|
||||
@@ -10573,7 +10585,7 @@ namespace Catch {
|
||||
// Extracts the actual name part of an enum instance
|
||||
// In other words, it returns the Blue part of Bikeshed::Colour::Blue
|
||||
StringRef extractInstanceName(StringRef enumInstance) {
|
||||
// Find last occurence of ":"
|
||||
// Find last occurrence of ":"
|
||||
size_t name_start = enumInstance.size();
|
||||
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
|
||||
--name_start;
|
||||
@@ -10735,25 +10747,47 @@ namespace Catch {
|
||||
// end catch_exception_translator_registry.cpp
|
||||
// start catch_fatal_condition.cpp
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmissing-field-initializers"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
|
||||
|
||||
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 )
|
||||
|
||||
namespace {
|
||||
// Report the error condition
|
||||
//! Signals fatal error message to the run context
|
||||
void reportFatal( char const * const 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 )
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct SignalDefs { DWORD id; const char* name; };
|
||||
|
||||
// There is no 1-1 mapping between signals and windows exceptions.
|
||||
@@ -10766,7 +10800,7 @@ namespace Catch {
|
||||
{ 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) {
|
||||
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
|
||||
reportFatal(def.name);
|
||||
@@ -10777,38 +10811,50 @@ namespace Catch {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
isSet = true;
|
||||
// 32k seems enough for Catch to handle stack overflow,
|
||||
// but the value was found experimentally, so there is no strong guarantee
|
||||
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);
|
||||
}
|
||||
// Since we do not support multiple instantiations, we put these
|
||||
// into global variables and rely on cleaning them up in outlined
|
||||
// constructors/destructors
|
||||
static PVOID exceptionHandlerHandle = nullptr;
|
||||
|
||||
void FatalConditionHandler::reset() {
|
||||
if (isSet) {
|
||||
RemoveVectoredExceptionHandler(exceptionHandlerHandle);
|
||||
SetThreadStackGuarantee(&guaranteeSize);
|
||||
exceptionHandlerHandle = nullptr;
|
||||
isSet = false;
|
||||
// For MSVC, we reserve part of the stack memory for handling
|
||||
// memory overflow structured exception.
|
||||
FatalConditionHandler::FatalConditionHandler() {
|
||||
ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
|
||||
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() {
|
||||
reset();
|
||||
// We do not attempt to unset the stack guarantee, because
|
||||
// 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;
|
||||
ULONG FatalConditionHandler::guaranteeSize = 0;
|
||||
PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
|
||||
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 {
|
||||
|
||||
@@ -10817,10 +10863,6 @@ namespace Catch {
|
||||
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[] = {
|
||||
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
||||
{ SIGILL, "SIGILL - Illegal instruction signal" },
|
||||
@@ -10830,7 +10872,32 @@ namespace Catch {
|
||||
{ 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>";
|
||||
for (auto const& def : signalDefs) {
|
||||
if (sig == def.id) {
|
||||
@@ -10838,16 +10905,33 @@ namespace Catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
reset();
|
||||
reportFatal(name);
|
||||
// We need to restore previous signal handlers and let them do
|
||||
// their thing, so that the users can have the debugger break
|
||||
// when a signal is raised, and so on.
|
||||
restorePreviousSignalHandlers();
|
||||
reportFatal( name );
|
||||
raise( sig );
|
||||
}
|
||||
|
||||
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;
|
||||
sigStack.ss_sp = altStackMem;
|
||||
sigStack.ss_size = sigStackSize;
|
||||
sigStack.ss_size = altStackSize;
|
||||
sigStack.ss_flags = 0;
|
||||
sigaltstack(&sigStack, &oldSigStack);
|
||||
struct sigaction sa = { };
|
||||
@@ -10859,40 +10943,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__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
void FatalConditionHandler::disengage_platform() {
|
||||
restorePreviousSignalHandlers();
|
||||
}
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // CATCH_CONFIG_POSIX_SIGNALS
|
||||
// end catch_fatal_condition.cpp
|
||||
// start catch_generators.cpp
|
||||
|
||||
@@ -11447,7 +11508,8 @@ namespace {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -11621,7 +11683,6 @@ Floating::WithinRelMatcher WithinRel(float target) {
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
// end catch_matchers_floating.cpp
|
||||
// start catch_matchers_generic.cpp
|
||||
|
||||
@@ -12955,9 +13016,8 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void RunContext::invokeActiveTestCase() {
|
||||
FatalConditionHandler fatalConditionHandler; // Handle signals
|
||||
FatalConditionHandlerGuard _(&m_fatalConditionhandler);
|
||||
m_activeTestCase->invoke();
|
||||
fatalConditionHandler.reset();
|
||||
}
|
||||
|
||||
void RunContext::handleUnfinishedSections() {
|
||||
@@ -14126,24 +14186,28 @@ namespace Catch {
|
||||
|
||||
namespace {
|
||||
struct TestHasher {
|
||||
explicit TestHasher(Catch::SimplePcg32& rng_instance) {
|
||||
basis = rng_instance();
|
||||
basis <<= 32;
|
||||
basis |= rng_instance();
|
||||
}
|
||||
using hash_t = uint64_t;
|
||||
|
||||
uint64_t basis;
|
||||
explicit TestHasher( hash_t hashSuffix ):
|
||||
m_hashSuffix{ hashSuffix } {}
|
||||
|
||||
uint64_t operator()(TestCase const& t) const {
|
||||
// Modified FNV-1a hash
|
||||
static constexpr uint64_t prime = 1099511628211;
|
||||
uint64_t hash = basis;
|
||||
for (const char c : t.name) {
|
||||
uint32_t operator()( TestCase const& t ) const {
|
||||
// FNV-1a hash with multiplication fold.
|
||||
const hash_t prime = 1099511628211u;
|
||||
hash_t hash = 14695981039346656037u;
|
||||
for ( const char c : t.name ) {
|
||||
hash ^= c;
|
||||
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;
|
||||
}
|
||||
|
||||
private:
|
||||
hash_t m_hashSuffix;
|
||||
};
|
||||
} // end unnamed namespace
|
||||
|
||||
@@ -14161,9 +14225,9 @@ namespace Catch {
|
||||
|
||||
case RunTests::InRandomOrder: {
|
||||
seedRng( config );
|
||||
TestHasher h( rng() );
|
||||
TestHasher h{ config.rngSeed() };
|
||||
|
||||
using hashedTest = std::pair<uint64_t, TestCase const*>;
|
||||
using hashedTest = std::pair<TestHasher::hash_t, TestCase const*>;
|
||||
std::vector<hashedTest> indexed_tests;
|
||||
indexed_tests.reserve( unsortedTestCases.size() );
|
||||
|
||||
@@ -15316,7 +15380,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
Version const& libraryVersion() {
|
||||
static Version version( 2, 13, 3, "", 0 );
|
||||
static Version version( 2, 13, 7, "", 0 );
|
||||
return version;
|
||||
}
|
||||
|
||||
@@ -16729,6 +16793,7 @@ CATCH_REGISTER_REPORTER("console", ConsoleReporter)
|
||||
#include <sstream>
|
||||
#include <ctime>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -16756,7 +16821,7 @@ namespace Catch {
|
||||
#else
|
||||
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
|
||||
#endif
|
||||
return std::string(timeStamp);
|
||||
return std::string(timeStamp, timeStampSize-1);
|
||||
}
|
||||
|
||||
std::string fileNameTag(const std::vector<std::string> &tags) {
|
||||
@@ -16767,6 +16832,17 @@ namespace Catch {
|
||||
return it->substr(1);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Formats the duration in seconds to 3 decimal places.
|
||||
// This is done because some genius defined Maven Surefire schema
|
||||
// in a way that only accepts 3 decimal places, and tools like
|
||||
// Jenkins use that schema for validation JUnit reporter output.
|
||||
std::string formatDuration( double seconds ) {
|
||||
ReusableStringStream rss;
|
||||
rss << std::fixed << std::setprecision( 3 ) << seconds;
|
||||
return rss.str();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
JunitReporter::JunitReporter( ReporterConfig const& _config )
|
||||
@@ -16836,7 +16912,7 @@ namespace Catch {
|
||||
if( m_config->showDurations() == ShowDurations::Never )
|
||||
xml.writeAttribute( "time", "" );
|
||||
else
|
||||
xml.writeAttribute( "time", suiteTime );
|
||||
xml.writeAttribute( "time", formatDuration( suiteTime ) );
|
||||
xml.writeAttribute( "timestamp", getCurrentTimestamp() );
|
||||
|
||||
// Write properties if there are any
|
||||
@@ -16881,12 +16957,13 @@ namespace Catch {
|
||||
if ( !m_config->name().empty() )
|
||||
className = m_config->name() + "." + className;
|
||||
|
||||
writeSection( className, "", rootSection );
|
||||
writeSection( className, "", rootSection, stats.testInfo.okToFail() );
|
||||
}
|
||||
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode ) {
|
||||
void JunitReporter::writeSection( std::string const& className,
|
||||
std::string const& rootName,
|
||||
SectionNode const& sectionNode,
|
||||
bool testOkToFail) {
|
||||
std::string name = trim( sectionNode.stats.sectionInfo.name );
|
||||
if( !rootName.empty() )
|
||||
name = rootName + '/' + name;
|
||||
@@ -16903,13 +16980,18 @@ namespace Catch {
|
||||
xml.writeAttribute( "classname", className );
|
||||
xml.writeAttribute( "name", name );
|
||||
}
|
||||
xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
|
||||
xml.writeAttribute( "time", formatDuration( sectionNode.stats.durationInSeconds ) );
|
||||
// This is not ideal, but it should be enough to mimic gtest's
|
||||
// junit output.
|
||||
// Ideally the JUnit reporter would also handle `skipTest`
|
||||
// events and write those out appropriately.
|
||||
xml.writeAttribute( "status", "run" );
|
||||
|
||||
if (sectionNode.stats.assertions.failedButOk) {
|
||||
xml.scopedElement("skipped")
|
||||
.writeAttribute("message", "TEST_CASE tagged with !mayfail");
|
||||
}
|
||||
|
||||
writeAssertions( sectionNode );
|
||||
|
||||
if( !sectionNode.stdOut.empty() )
|
||||
@@ -16919,9 +17001,9 @@ namespace Catch {
|
||||
}
|
||||
for( auto const& childNode : sectionNode.childSections )
|
||||
if( className.empty() )
|
||||
writeSection( name, "", *childNode );
|
||||
writeSection( name, "", *childNode, testOkToFail );
|
||||
else
|
||||
writeSection( className, name, *childNode );
|
||||
writeSection( className, name, *childNode, testOkToFail );
|
||||
}
|
||||
|
||||
void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
|
||||
|
2
src/catch_with_main.cpp
Normal file
2
src/catch_with_main.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
Reference in New Issue
Block a user