mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-11 07:55:39 +02:00
Compare commits
217 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
255aa5f2af | ||
![]() |
3a33315ff8 | ||
![]() |
5f94c8dafb | ||
![]() |
695a356f93 | ||
![]() |
020b655635 | ||
![]() |
5561d0342d | ||
![]() |
3a15315a37 | ||
![]() |
b7b346c3e5 | ||
![]() |
9e09d79946 | ||
![]() |
7048c2c269 | ||
![]() |
6f77d59b16 | ||
![]() |
022b61ff92 | ||
![]() |
87b5bf77bc | ||
![]() |
81d52c4a5f | ||
![]() |
200b8b6fc0 | ||
![]() |
e70fd2a4b9 | ||
![]() |
36170d60ca | ||
![]() |
917e92c162 | ||
![]() |
f54950744f | ||
![]() |
b3b07215d1 | ||
![]() |
2652bb86e3 | ||
![]() |
1715b6b923 | ||
![]() |
f20a9dbc6e | ||
![]() |
273c3f85eb | ||
![]() |
6a3d0dc176 | ||
![]() |
ccb1f70629 | ||
![]() |
4a5bc0f39a | ||
![]() |
f96e89e016 | ||
![]() |
481f54b357 | ||
![]() |
2d172dc688 | ||
![]() |
587a20b312 | ||
![]() |
bff44f8b01 | ||
![]() |
4ea74ff966 | ||
![]() |
d10b9bd02e | ||
![]() |
55794e9b27 | ||
![]() |
fa6211bfc2 | ||
![]() |
4e90f910dc | ||
![]() |
0c59cc83cf | ||
![]() |
e4004e0adb | ||
![]() |
6c9a255dc2 | ||
![]() |
9a8963133f | ||
![]() |
cfba9dce97 | ||
![]() |
a537ccae22 | ||
![]() |
e1c9d5569d | ||
![]() |
d512decaac | ||
![]() |
f23f96883a | ||
![]() |
d7b8c3ace3 | ||
![]() |
32733e08c0 | ||
![]() |
930f49a641 | ||
![]() |
c409dccee5 | ||
![]() |
95bfb33167 | ||
![]() |
59d2d08c0f | ||
![]() |
fa6d52e2a3 | ||
![]() |
5ac348cd6e | ||
![]() |
776a4686c7 | ||
![]() |
3136c4fb6a | ||
![]() |
74e0e737a6 | ||
![]() |
0685216175 | ||
![]() |
fc320f6b8f | ||
![]() |
5290d4bedc | ||
![]() |
7ada02e21e | ||
![]() |
849f2848bd | ||
![]() |
2fbd66c51c | ||
![]() |
51b29ced1a | ||
![]() |
9a558171d8 | ||
![]() |
c5c688820c | ||
![]() |
6a08225863 | ||
![]() |
4327baba40 | ||
![]() |
50cc14c94c | ||
![]() |
87b745da66 | ||
![]() |
7d0b205564 | ||
![]() |
8fb1219013 | ||
![]() |
23c80bcc92 | ||
![]() |
a2c8dce85c | ||
![]() |
1e379de9d7 | ||
![]() |
4eea438b73 | ||
![]() |
407ee0af2f | ||
![]() |
060a41ec7b | ||
![]() |
90825a4f7a | ||
![]() |
9e8ae7d470 | ||
![]() |
84856844e1 | ||
![]() |
01ef7076f5 | ||
![]() |
ae14a47360 | ||
![]() |
f2b23db6d1 | ||
![]() |
1aa98c76ac | ||
![]() |
3195c242c2 | ||
![]() |
31906d83ec | ||
![]() |
91fa55303b | ||
![]() |
7c9f92bc1c | ||
![]() |
a92a7d0229 | ||
![]() |
e4d61e4cd8 | ||
![]() |
9ba48e2c9b | ||
![]() |
2cc0c71856 | ||
![]() |
28663fb959 | ||
![]() |
d2d418a9cb | ||
![]() |
c8db4e77c4 | ||
![]() |
1c5749669e | ||
![]() |
3109add95c | ||
![]() |
adb4789136 | ||
![]() |
75200e199e | ||
![]() |
a5a22cdadb | ||
![]() |
535da5c513 | ||
![]() |
2331249a8d | ||
![]() |
319cb9e1da | ||
![]() |
b8b765d55e | ||
![]() |
a0ebd63806 | ||
![]() |
4bd2c3ad6a | ||
![]() |
c38a5caa2e | ||
![]() |
ebc5609484 | ||
![]() |
fcda35f645 | ||
![]() |
02ee130bd0 | ||
![]() |
815f99541d | ||
![]() |
da0062f7c1 | ||
![]() |
de42f8a93e | ||
![]() |
af84f1350e | ||
![]() |
fc2066bf18 | ||
![]() |
2bcff9dd35 | ||
![]() |
3beccfb429 | ||
![]() |
af8b2538a6 | ||
![]() |
a156440b19 | ||
![]() |
dab0296b64 | ||
![]() |
9f4c4777a5 | ||
![]() |
293012a002 | ||
![]() |
e2b3443fe7 | ||
![]() |
7b865daccc | ||
![]() |
14362533bb | ||
![]() |
a5bb3e3d91 | ||
![]() |
923db16322 | ||
![]() |
fbbaadb704 | ||
![]() |
dd1f0f1c72 | ||
![]() |
d27d580d0b | ||
![]() |
6da00c1b64 | ||
![]() |
fe967b1f41 | ||
![]() |
f2c2711bdc | ||
![]() |
b77ab74b72 | ||
![]() |
4038ee6bc6 | ||
![]() |
789f3591ef | ||
![]() |
6e8d769775 | ||
![]() |
1189a73be2 | ||
![]() |
071bacad5e | ||
![]() |
addf799040 | ||
![]() |
155274f0df | ||
![]() |
18d597cf10 | ||
![]() |
6629c11ef8 | ||
![]() |
c6bf56b3d5 | ||
![]() |
623e348d9e | ||
![]() |
46f767e602 | ||
![]() |
ce42deb72f | ||
![]() |
46a70071a7 | ||
![]() |
378cc1a670 | ||
![]() |
e2d863b090 | ||
![]() |
ebe6a07c23 | ||
![]() |
edcfd7fc62 | ||
![]() |
738818ae1d | ||
![]() |
2c869e17e4 | ||
![]() |
0ab11aa9b4 | ||
![]() |
7a6af7ba76 | ||
![]() |
fa096b26d1 | ||
![]() |
820b1f12bf | ||
![]() |
6070745cab | ||
![]() |
3d9e7db2e0 | ||
![]() |
cf55cfd76f | ||
![]() |
3701c2e2e6 | ||
![]() |
7dc7d77af2 | ||
![]() |
06bc20cf37 | ||
![]() |
7a4beed6a6 | ||
![]() |
67b4ada6b0 | ||
![]() |
119569a67e | ||
![]() |
ab713894cc | ||
![]() |
69fc94d6f8 | ||
![]() |
49cd7c96b4 | ||
![]() |
e998d152cc | ||
![]() |
42a5903188 | ||
![]() |
c071f07e1a | ||
![]() |
53776a90cf | ||
![]() |
4511dc0c16 | ||
![]() |
e7c3bdb351 | ||
![]() |
9aab958667 | ||
![]() |
8cd58f75ec | ||
![]() |
d5a69cd400 | ||
![]() |
1d13d88833 | ||
![]() |
de0674c116 | ||
![]() |
3d7282c2bd | ||
![]() |
e5c0e3322d | ||
![]() |
dc8c8e957f | ||
![]() |
ba9193370b | ||
![]() |
7b70b11c23 | ||
![]() |
ab80277a86 | ||
![]() |
7e7ab0e28b | ||
![]() |
425957dc63 | ||
![]() |
d017f6d18f | ||
![]() |
91244d30a7 | ||
![]() |
62b3f6c3c2 | ||
![]() |
e7c26f09d1 | ||
![]() |
a22b7df46c | ||
![]() |
032068b889 | ||
![]() |
2aed6233cf | ||
![]() |
fb74bb133c | ||
![]() |
0b42ada60d | ||
![]() |
c424ca47f9 | ||
![]() |
52f3abadbb | ||
![]() |
53281b471f | ||
![]() |
03ffc1014c | ||
![]() |
87739ad3fe | ||
![]() |
0c27554af5 | ||
![]() |
11488e63b6 | ||
![]() |
820271bf24 | ||
![]() |
56d4510138 | ||
![]() |
c0d3a2e08f | ||
![]() |
2c3018a9d5 | ||
![]() |
9a6551b22b | ||
![]() |
800f1b1d3d | ||
![]() |
9cf5897a11 | ||
![]() |
6f32c67ea7 | ||
![]() |
7eea3ab245 | ||
![]() |
80af9ca687 | ||
![]() |
33286fdc37 |
12
.travis.yml
12
.travis.yml
@@ -9,8 +9,8 @@ common_sources: &all_sources
|
||||
- llvm-toolchain-trusty
|
||||
- llvm-toolchain-trusty-3.9
|
||||
- llvm-toolchain-trusty-4.0
|
||||
- llvm-toolchain-trusty-5.0
|
||||
- llvm-toolchain-trusty-6.0
|
||||
- llvm-toolchain-xenial-5.0
|
||||
- llvm-toolchain-xenial-6.0
|
||||
|
||||
matrix:
|
||||
include:
|
||||
@@ -60,6 +60,7 @@ matrix:
|
||||
env: COMPILER='clang++-4.0'
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -68,6 +69,7 @@ matrix:
|
||||
env: COMPILER='clang++-5.0'
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -153,6 +155,7 @@ matrix:
|
||||
env: COMPILER='clang++-4.0' CPP14=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -161,6 +164,7 @@ matrix:
|
||||
env: COMPILER='clang++-5.0' CPP14=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -255,6 +259,7 @@ matrix:
|
||||
env: COMPILER='g++-7' EXAMPLES=1 COVERAGE=1 EXTRAS=1 CPP17=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -263,6 +268,7 @@ matrix:
|
||||
env: COMPILER='clang++-6.0' CPP17=1
|
||||
|
||||
- os: linux
|
||||
dist: xenial
|
||||
compiler: clang
|
||||
addons:
|
||||
apt:
|
||||
@@ -276,7 +282,7 @@ matrix:
|
||||
- "3.7"
|
||||
dist: xenial
|
||||
install:
|
||||
- pip install conan==1.10.2 conan-package-tools
|
||||
- pip install conan-package-tools
|
||||
env:
|
||||
- CONAN_GCC_VERSIONS=8
|
||||
- CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||
|
@@ -6,12 +6,16 @@ if(NOT DEFINED PROJECT_NAME)
|
||||
set(NOT_SUBPROJECT ON)
|
||||
endif()
|
||||
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.9.1)
|
||||
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||
# Catch2's build breaks if done in-tree. You probably should not build
|
||||
# things in tree anyway, but we can allow projects that include Catch2
|
||||
# as a subproject to build in-tree as long as it is not in our tree.
|
||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||
endif()
|
||||
|
||||
|
||||
project(Catch2 LANGUAGES CXX VERSION 2.11.3)
|
||||
|
||||
# Provide path for scripts
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||
|
||||
@@ -92,6 +96,10 @@ target_include_directories(Catch2
|
||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||
)
|
||||
|
||||
if (ANDROID)
|
||||
target_link_libraries(Catch2 INTERFACE log)
|
||||
endif()
|
||||
|
||||
# provide a namespaced alias for clients to 'link' against if catch is included as a sub-project
|
||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||
|
||||
@@ -136,9 +144,11 @@ if (NOT_SUBPROJECT)
|
||||
#
|
||||
# CMake does not provide a direct customization point for this in
|
||||
# `write_basic_package_version_file`, but it can be accomplished
|
||||
# indirectly by temporarily undefining `CMAKE_SIZEOF_VOID_P`.
|
||||
# indirectly by temporarily redefining `CMAKE_SIZEOF_VOID_P` to an
|
||||
# empty string. Note that just undefining the variable could be
|
||||
# insufficient in cases where the variable was already in CMake cache
|
||||
set(CATCH2_CMAKE_SIZEOF_VOID_P ${CMAKE_SIZEOF_VOID_P})
|
||||
unset(CMAKE_SIZEOF_VOID_P)
|
||||
set(CMAKE_SIZEOF_VOID_P "")
|
||||
write_basic_package_version_file(
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/Catch2ConfigVersion.cmake"
|
||||
COMPATIBILITY
|
||||
|
@@ -5,11 +5,11 @@
|
||||
[](https://travis-ci.org/catchorg/Catch2)
|
||||
[](https://ci.appveyor.com/project/catchorg/catch2)
|
||||
[](https://codecov.io/gh/catchorg/Catch2)
|
||||
[](https://wandbox.org/permlink/5icuqPwk9miJLAL1)
|
||||
[](https://wandbox.org/permlink/p9Pcgple8QWwgNR0)
|
||||
[](https://discord.gg/4CWS9zD)
|
||||
|
||||
|
||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.9.1/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.11.3/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||
|
||||
## Catch2 is released!
|
||||
|
||||
@@ -19,9 +19,8 @@ before moving to Catch2. You might also like to read [this blog post](https://le
|
||||
|
||||
## What's the Catch?
|
||||
|
||||
Catch2 stands for C++ Automated Test Cases in a Header and is a
|
||||
multi-paradigm test framework for C++. which also supports Objective-C
|
||||
(and maybe C).
|
||||
Catch2 is a multi-paradigm test framework for C++. which also supports
|
||||
Objective-C (and maybe C).
|
||||
It is primarily distributed as a single header file, although certain
|
||||
extensions may require additional headers.
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
# version string format -- This will be overwritten later anyway
|
||||
version: "{build}"
|
||||
|
||||
# We need a more up to date pip because Python 2.7 is EOL soon
|
||||
init:
|
||||
- set PATH=C:\Python35\Scripts;%PATH%
|
||||
|
||||
|
||||
branches:
|
||||
except:
|
||||
- /dev-travis.+/
|
||||
@@ -62,7 +67,7 @@ matrix:
|
||||
|
||||
|
||||
install:
|
||||
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { python -m pip --disable-pip-version-check install codecov }
|
||||
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { pip --disable-pip-version-check install codecov }
|
||||
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\misc\installOpenCppCoverage.ps1 }
|
||||
|
||||
# Win32 and x64 are CMake-compatible solution platform names.
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 20 KiB |
@@ -14,6 +14,7 @@ coverage:
|
||||
- "**/catch_reporter_tap.hpp"
|
||||
- "**/catch_reporter_automake.hpp"
|
||||
- "**/catch_reporter_teamcity.hpp"
|
||||
- "**/catch_reporter_sonarqube.hpp"
|
||||
- "**/external/clara.hpp"
|
||||
|
||||
|
||||
|
@@ -22,39 +22,6 @@ function(add_command NAME)
|
||||
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
macro(_add_catch_test_labels LINE)
|
||||
# convert to list of tags
|
||||
string(REPLACE "][" "]\\;[" tags ${line})
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
LABELS "${tags}"
|
||||
)
|
||||
endmacro()
|
||||
|
||||
macro(_add_catch_test LINE)
|
||||
set(test ${line})
|
||||
# use escape commas to handle properly test cases with commans inside the name
|
||||
string(REPLACE "," "\\," test_name ${test})
|
||||
# ...and add to script
|
||||
add_command(
|
||||
add_test "${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
|
||||
add_command(
|
||||
set_tests_properties "${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endmacro()
|
||||
|
||||
# Run test executable to get list of available tests
|
||||
if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
message(FATAL_ERROR
|
||||
@@ -62,7 +29,7 @@ if(NOT EXISTS "${TEST_EXECUTABLE}")
|
||||
)
|
||||
endif()
|
||||
execute_process(
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests
|
||||
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-test-names-only
|
||||
OUTPUT_VARIABLE output
|
||||
RESULT_VARIABLE result
|
||||
)
|
||||
@@ -80,22 +47,30 @@ elseif(${result} LESS 0)
|
||||
endif()
|
||||
|
||||
string(REPLACE "\n" ";" output "${output}")
|
||||
set(test)
|
||||
set(tags_regex "(\\[([^\\[]*)\\])+$")
|
||||
|
||||
# Parse output
|
||||
foreach(line ${output})
|
||||
# lines without leading whitespaces are catch output not tests
|
||||
if(${line} MATCHES "^[ \t]+")
|
||||
# strip leading spaces and tabs
|
||||
string(REGEX REPLACE "^[ \t]+" "" line ${line})
|
||||
|
||||
if(${line} MATCHES "${tags_regex}")
|
||||
_add_catch_test_labels(${line})
|
||||
else()
|
||||
_add_catch_test(${line})
|
||||
endif()
|
||||
endif()
|
||||
set(test ${line})
|
||||
# Escape characters in test case names that would be parsed by Catch2
|
||||
set(test_name ${test})
|
||||
foreach(char , [ ])
|
||||
string(REPLACE ${char} "\\${char}" test_name ${test_name})
|
||||
endforeach(char)
|
||||
# ...and add to script
|
||||
add_command(add_test
|
||||
"${prefix}${test}${suffix}"
|
||||
${TEST_EXECUTOR}
|
||||
"${TEST_EXECUTABLE}"
|
||||
"${test_name}"
|
||||
${extra_args}
|
||||
)
|
||||
add_command(set_tests_properties
|
||||
"${prefix}${test}${suffix}"
|
||||
PROPERTIES
|
||||
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
|
||||
${properties}
|
||||
)
|
||||
list(APPEND tests "${prefix}${test}${suffix}")
|
||||
endforeach()
|
||||
|
||||
# Create a list of all discovered tests, which users may use to e.g. set
|
||||
|
@@ -35,6 +35,7 @@ FAQ:
|
||||
Other:
|
||||
* [Why Catch?](why-catch.md#top)
|
||||
* [Open Source Projects using Catch](opensource-users.md#top)
|
||||
* [Commercial Projects using Catch](commercial-users.md#top)
|
||||
* [Contributing](contributing.md#top)
|
||||
* [Release Notes](release-notes.md#top)
|
||||
* [Deprecations and incoming changes](deprecations.md#top)
|
||||
|
@@ -136,7 +136,7 @@ REQUIRE_THROWS_WITH( dismantleHal(), "My mind is going" );
|
||||
* **REQUIRE_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)** and
|
||||
* **CHECK_THROWS_MATCHES(** _expression_, _exception type_, _matcher for given exception type_ **)**
|
||||
|
||||
Expects that exception of _exception type_ is thrown and it matches provided matcher (see next section for Matchers).
|
||||
Expects that exception of _exception type_ is thrown and it matches provided matcher (see the [documentation for Matchers](matchers.md#top)).
|
||||
|
||||
|
||||
_Please note that the `THROW` family of assertions expects to be passed a single expression, not a statement or series of statements. If you want to check a more complicated sequence of operations, you can use a C++11 lambda function._
|
||||
|
@@ -1,6 +1,12 @@
|
||||
<a id="top"></a>
|
||||
# Authoring benchmarks
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
_Note that benchmarking support is disabled by default and to enable it,
|
||||
you need to define `CATCH_CONFIG_ENABLE_BENCHMARKING`. For more details,
|
||||
see the [compile-time configuration documentation](configuration.md#top)._
|
||||
|
||||
Writing benchmarks is not easy. Catch simplifies certain aspects but you'll
|
||||
always need to take care about various aspects. Understanding a few things about
|
||||
the way Catch runs your code will be very helpful when writing your benchmarks.
|
||||
@@ -162,7 +168,7 @@ Note that it is not possible to simply use the same instance for different runs
|
||||
and resetting it between each run since that would pollute the measurements with
|
||||
the resetting code.
|
||||
|
||||
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
|
||||
It is also possible to just provide an argument name to the simple `BENCHMARK` macro to get
|
||||
the same semantics as providing a callable to `meter.measure` with `int` argument:
|
||||
|
||||
```c++
|
||||
@@ -183,19 +189,17 @@ construct and destroy objects without dynamic allocation and in a way that lets
|
||||
you measure construction and destruction separately.
|
||||
|
||||
```c++
|
||||
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
BENCHMARK_ADVANCED("construct")(Catch::Benchmark::Chronometer meter) {
|
||||
std::vector<Catch::Benchmark::storage_for<std::string>> storage(meter.runs());
|
||||
meter.measure([&](int i) { storage[i].construct("thing"); });
|
||||
})
|
||||
};
|
||||
|
||||
BENCHMARK_ADVANCED("destroy", [](Catch::Benchmark::Chronometer meter)
|
||||
{
|
||||
BENCHMARK_ADVANCED("destroy")(Catch::Benchmark::Chronometer meter) {
|
||||
std::vector<Catch::Benchmark::destructable_object<std::string>> storage(meter.runs());
|
||||
for(auto&& o : storage)
|
||||
o.construct("thing");
|
||||
meter.measure([&](int i) { storage[i].destruct(); });
|
||||
})
|
||||
};
|
||||
```
|
||||
|
||||
`Catch::Benchmark::storage_for<T>` objects are just pieces of raw storage suitable for `T`
|
||||
|
@@ -12,7 +12,7 @@ Build Systems may refer to low-level tools, like CMake, or larger systems that r
|
||||
|
||||
## Continuous Integration systems
|
||||
|
||||
Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP and Automake reporters).
|
||||
Probably the most important aspect to using Catch with a build server is the use of different reporters. Catch comes bundled with three reporters that should cover the majority of build servers out there - although adding more for better integration with some is always a possibility (currently we also offer TeamCity, TAP, Automake and SonarQube reporters).
|
||||
|
||||
Two of these reporters are built in (XML and JUnit) and the third (TeamCity) is included as a separate header. It's possible that the other two may be split out in the future too - as that would make the core of Catch smaller for those that don't need them.
|
||||
|
||||
@@ -65,6 +65,10 @@ The Automake Reporter writes out the [meta tags](https://www.gnu.org/software/au
|
||||
|
||||
Because of the incremental nature of Catch's test suites and ability to run specific tests, our implementation of TAP reporter writes out the number of tests in a suite last.
|
||||
|
||||
### SonarQube Reporter
|
||||
```-r sonarqube```
|
||||
[SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format for tests metrics.
|
||||
|
||||
## Low-level tools
|
||||
|
||||
### Precompiled headers (PCHs)
|
||||
|
@@ -21,9 +21,10 @@
|
||||
[Identify framework and version according to the libIdentify standard](#identify-framework-and-version-according-to-the-libidentify-standard)<br>
|
||||
[Wait for key before continuing](#wait-for-key-before-continuing)<br>
|
||||
[Specify the number of benchmark samples to collect](#specify-the-number-of-benchmark-samples-to-collect)<br>
|
||||
[Specify the number of benchmark resamples for bootstrapping](#specify-the-number-of-resamples-for-bootstrapping)<br>
|
||||
[Specify the confidence interval for bootstrapping](#specify-the-confidence-interval-for-bootstrapping)<br>
|
||||
[Specify the number of resamples for bootstrapping](#specify-the-number-of-resamples-for-bootstrapping)<br>
|
||||
[Specify the confidence-interval for bootstrapping](#specify-the-confidence-interval-for-bootstrapping)<br>
|
||||
[Disable statistical analysis of collected benchmark samples](#disable-statistical-analysis-of-collected-benchmark-samples)<br>
|
||||
[Specify the amount of time in milliseconds spent on warming up each test](#specify-the-amount-of-time-in-milliseconds-spent-on-warming-up-each-test)<br>
|
||||
[Usage](#usage)<br>
|
||||
[Specify the section to run](#specify-the-section-to-run)<br>
|
||||
[Filenames as tags](#filenames-as-tags)<br>
|
||||
@@ -64,6 +65,7 @@ Click one of the following links to take you straight to that option - or scroll
|
||||
<a href="#benchmark-resamples"> ` --benchmark-resamples`</a><br />
|
||||
<a href="#benchmark-confidence-interval"> ` --benchmark-confidence-interval`</a><br />
|
||||
<a href="#benchmark-no-analysis"> ` --benchmark-no-analysis`</a><br />
|
||||
<a href="#benchmark-warmup-time"> ` --benchmark-warmup-time`</a><br />
|
||||
<a href="#use-colour"> ` --use-colour`</a><br />
|
||||
|
||||
</br>
|
||||
@@ -99,6 +101,7 @@ exclude:notThis Matches all tests except, 'notThis'
|
||||
~*private* Matches all tests except those that contain 'private'
|
||||
a* ~ab* abc Matches all tests that start with 'a', except those that
|
||||
start with 'ab', except 'abc', which is included
|
||||
-# [#somefile] Matches all tests from the file 'somefile.cpp'
|
||||
</pre>
|
||||
|
||||
Names within square brackets are interpreted as tags.
|
||||
@@ -242,7 +245,7 @@ Test cases are ordered one of three ways:
|
||||
|
||||
|
||||
### decl
|
||||
Declaration order. The order the tests were originally declared in. Note that ordering between files is not guaranteed and is implementation dependent.
|
||||
Declaration order (this is the default order if no --order argument is provided). The order the tests were originally declared in. Note that ordering between files is not guaranteed and is implementation dependent.
|
||||
|
||||
### lex
|
||||
Lexicographically sorted. Tests are sorted, alpha-numerically, by name.
|
||||
@@ -256,7 +259,7 @@ Randomly sorted. Test names are sorted using ```std::random_shuffle()```. By def
|
||||
|
||||
Sets a seed for the random number generator using ```std::srand()```.
|
||||
If a number is provided this is used directly as the seed so the random pattern is repeatable.
|
||||
Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable.
|
||||
Alternatively if the keyword ```time``` is provided then the result of calling ```std::time(0)``` is used and so the pattern becomes unpredictable. In some cases, you might need to pass the keyword ```time``` in double quotes instead of single quotes.
|
||||
|
||||
In either case the actual value for the seed is printed as part of Catch's output so if an issue is discovered that is sensitive to test ordering the ordering can be reproduced - even if it was originally seeded from ```std::time(0)```.
|
||||
|
||||
@@ -268,7 +271,7 @@ See [The LibIdentify repo for more information and examples](https://github.com/
|
||||
|
||||
<a id="wait-for-keypress"></a>
|
||||
## Wait for key before continuing
|
||||
<pre>--wait-for-keypress <start|exit|both></pre>
|
||||
<pre>--wait-for-keypress <never|start|exit|both></pre>
|
||||
|
||||
Will cause the executable to print a message and wait until the return/ enter key is pressed before continuing -
|
||||
either before running any tests, after running all tests - or both, depending on the argument.
|
||||
@@ -277,6 +280,8 @@ either before running any tests, after running all tests - or both, depending on
|
||||
## Specify the number of benchmark samples to collect
|
||||
<pre>--benchmark-samples <# of samples></pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
When running benchmarks a number of "samples" is collected. This is the base data for later statistical analysis.
|
||||
Per sample a clock resolution dependent number of iterations of the user code is run, which is independent of the number of samples. Defaults to 100.
|
||||
|
||||
@@ -284,6 +289,8 @@ Per sample a clock resolution dependent number of iterations of the user code is
|
||||
## Specify the number of resamples for bootstrapping
|
||||
<pre>--benchmark-resamples <# of resamples></pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
After the measurements are performed, statistical [bootstrapping] is performed
|
||||
on the samples. The number of resamples for that bootstrapping is configurable
|
||||
but defaults to 100000. Due to the bootstrapping it is possible to give
|
||||
@@ -297,6 +304,8 @@ defaults to 95%).
|
||||
## Specify the confidence-interval for bootstrapping
|
||||
<pre>--benchmark-confidence-interval <confidence-interval></pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
The confidence-interval is used for statistical bootstrapping on the samples to
|
||||
calculate the upper and lower bounds of mean and standard deviation.
|
||||
Must be between 0 and 1 and defaults to 0.95.
|
||||
@@ -305,9 +314,19 @@ Must be between 0 and 1 and defaults to 0.95.
|
||||
## Disable statistical analysis of collected benchmark samples
|
||||
<pre>--benchmark-no-analysis</pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
|
||||
|
||||
When this flag is specified no bootstrapping or any other statistical analysis is performed.
|
||||
Instead the user code is only measured and the plain mean from the samples is reported.
|
||||
|
||||
<a id="benchmark-warmup-time"></a>
|
||||
## Specify the amount of time in milliseconds spent on warming up each test
|
||||
<pre>--benchmark-warmup-time</pre>
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/pull/1844) in Catch 2.11.2.
|
||||
|
||||
Configure the amount of time spent warming up each test.
|
||||
|
||||
<a id="usage"></a>
|
||||
## Usage
|
||||
<pre>-h, -?, --help</pre>
|
||||
|
@@ -17,3 +17,6 @@ fact then please let us know - either directly, via a PR or
|
||||
- NASA
|
||||
- [Inscopix Inc.](https://www.inscopix.com/)
|
||||
- [Makimo](https://makimo.pl/)
|
||||
- [UX3D](https://ux3d.io)
|
||||
- [King](https://king.com)
|
||||
|
||||
|
@@ -16,6 +16,7 @@
|
||||
[Windows header clutter](#windows-header-clutter)<br>
|
||||
[Enabling stringification](#enabling-stringification)<br>
|
||||
[Disabling exceptions](#disabling-exceptions)<br>
|
||||
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
|
||||
|
||||
Catch is designed to "just work" as much as possible. For most people the only configuration needed is telling Catch which source file should host all the implementation code (```CATCH_CONFIG_MAIN```).
|
||||
|
||||
@@ -127,8 +128,12 @@ Catch's selection, by defining either `CATCH_CONFIG_CPP11_TO_STRING` or
|
||||
## C++17 toggles
|
||||
|
||||
CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS // Use std::uncaught_exceptions instead of std::uncaught_exception
|
||||
CATCH_CONFIG_CPP17_STRING_VIEW // Provide StringMaker specialization for std::string_view
|
||||
CATCH_CONFIG_CPP17_VARIANT // Override C++17 detection for CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
|
||||
CATCH_CONFIG_CPP17_STRING_VIEW // Override std::string_view support detection(Catch provides a StringMaker specialization by default)
|
||||
CATCH_CONFIG_CPP17_VARIANT // Override std::variant support detection (checked by CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER)
|
||||
CATCH_CONFIG_CPP17_OPTIONAL // Override std::optional support detection (checked by CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
|
||||
CATCH_CONFIG_CPP17_BYTE // Override std::byte support detection (Catch provides a StringMaker specialization by default)
|
||||
|
||||
> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch 2.4.1.
|
||||
|
||||
Catch contains basic compiler/standard detection and attempts to use
|
||||
some C++17 features whenever appropriate. This automatic detection
|
||||
@@ -151,6 +156,10 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
|
||||
CATCH_CONFIG_EXPERIMENTAL_REDIRECT // Enables the new (experimental) way of capturing stdout/stderr
|
||||
CATCH_CONFIG_ENABLE_BENCHMARKING // Enables the integrated benchmarking features (has a significant effect on compilation speed)
|
||||
CATCH_CONFIG_USE_ASYNC // Force parallel statistical processing of samples during benchmarking
|
||||
CATCH_CONFIG_ANDROID_LOGWRITE // Use android's logging system for debug output
|
||||
CATCH_CONFIG_GLOBAL_NEXTAFTER // Use nextafter{,f,l} instead of std::nextafter
|
||||
|
||||
> [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch 2.10.0
|
||||
|
||||
Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
|
||||
|
||||
@@ -209,9 +218,14 @@ By default, Catch does not stringify some types from the standard library. This
|
||||
CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional (on C++17)
|
||||
CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS // Defines all of the above
|
||||
|
||||
> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch 2.4.1.
|
||||
|
||||
> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch 2.6.0.
|
||||
|
||||
## Disabling exceptions
|
||||
|
||||
> Introduced in Catch 2.4.0.
|
||||
|
||||
By default, Catch2 uses exceptions to signal errors and to abort tests
|
||||
when an assertion from the `REQUIRE` family of assertions fails. We also
|
||||
provide an experimental support for disabling exceptions. Catch2 should
|
||||
@@ -244,6 +258,18 @@ namespace Catch {
|
||||
}
|
||||
```
|
||||
|
||||
## Overriding Catch's debug break (`-b`)
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/pull/1846) in Catch 2.11.2.
|
||||
|
||||
You can override Catch2's break-into-debugger code by defining the
|
||||
`CATCH_BREAK_INTO_DEBUGGER()` macro. This can be used if e.g. Catch2 does
|
||||
not know your platform, or your platform is misdetected.
|
||||
|
||||
The macro will be used as is, that is, `CATCH_BREAK_INTO_DEBUGGER();`
|
||||
must compile and must break into debugger.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -1,6 +1,13 @@
|
||||
<a id="top"></a>
|
||||
# Contributing to Catch
|
||||
|
||||
**Contents**<br>
|
||||
[Branches](#branches)<br>
|
||||
[Directory structure](#directory-structure)<br>
|
||||
[Testing your changes](#testing-your-changes)<br>
|
||||
[Documenting your code](#documenting-your-code)<br>
|
||||
[Code constructs to watch out for](#code-constructs-to-watch-out-for)<br>
|
||||
|
||||
So you want to contribute something to Catch? That's great! Whether it's a bug fix, a new feature, support for
|
||||
additional compilers - or just a fix to the documentation - all contributions are very welcome and very much appreciated.
|
||||
Of course so are bug reports and other comments and questions.
|
||||
@@ -63,6 +70,10 @@ locally takes just a few minutes.
|
||||
$ cd debug-build
|
||||
$ ctest -j 2 --output-on-failure
|
||||
```
|
||||
__Note:__ When running your tests with multi-configuration generators like
|
||||
Visual Studio, you might get errors "Test not available without configuration."
|
||||
You then have to pick one configuration (e.g. ` -C Debug`) in the `ctest` call.
|
||||
|
||||
If you added new tests, approval tests are very likely to fail. If they
|
||||
do not, it means that your changes weren't run as part of them. This
|
||||
_might_ be intentional, but usually is not.
|
||||
@@ -75,6 +86,59 @@ before you do so, you need to check that the introduced changes are indeed
|
||||
intentional.
|
||||
|
||||
|
||||
## Documenting your code
|
||||
|
||||
If you have added new feature to Catch2, it needs documentation, so that
|
||||
other people can use it as well. This section collects some technical
|
||||
information that you will need for updating Catch2's documentation, and
|
||||
possibly some generic advise as well.
|
||||
|
||||
First, the technicalities:
|
||||
|
||||
* We introduced version tags to the documentation, which show users in
|
||||
which version a specific feature was introduced. This means that newly
|
||||
written documentation should be tagged with a placeholder, that will
|
||||
be replaced with the actual version upon release. There are 2 styles
|
||||
of placeholders used through the documentation, you should pick one that
|
||||
fits your text better (if in doubt, take a look at the existing version
|
||||
tags for other features).
|
||||
* `> [Introduced](link-to-issue-or-PR) in Catch X.Y.Z` - this
|
||||
placeholder is usually used after a section heading
|
||||
* `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch X.Y.Z`
|
||||
- this placeholder is used when you need to tag a subpart of something,
|
||||
e.g. list
|
||||
* Crosslinks to different pages should target the `top` anchor, like this
|
||||
`[link to contributing](contributing.md#top)`.
|
||||
* If you have introduced a new document, there is a simple template you
|
||||
should use. It provides you with the top anchor mentioned above, and also
|
||||
with a backlink to the top of the documentation:
|
||||
```markdown
|
||||
<a id="top"></a>
|
||||
# Cool feature
|
||||
|
||||
Text that explains how to use the cool feature.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
```
|
||||
* For pages with more than 4 subheadings, we provide a table of contents
|
||||
(ToC) at the top of the page. Because GitHub markdown does not support
|
||||
automatic generation of ToC, it has to be handled semi-manually. Thus,
|
||||
if you've added a new subheading to some page, you should add it to the
|
||||
ToC. This can be done either manually, or by running the
|
||||
`updateDocumentToC.py` script in the `scripts/` folder.
|
||||
|
||||
|
||||
Now, for the generic tips:
|
||||
* Usage examples are good
|
||||
* Don't be afraid to introduce new pages
|
||||
* Try to be reasonably consistent with the surrounding documentation
|
||||
|
||||
|
||||
|
||||
|
||||
## Code constructs to watch out for
|
||||
|
||||
This section is a (sadly incomplete) listing of various constructs that
|
||||
|
@@ -39,6 +39,39 @@ apart from writing it out for `--list-tests -v high`.
|
||||
Because it isn't actually used nor documented, and brings complications
|
||||
to Catch2's internals, description support will be removed.
|
||||
|
||||
### SourceLineInfo::empty()
|
||||
|
||||
There should be no reason to ever have an empty `SourceLineInfo`, so the
|
||||
method will be removed.
|
||||
|
||||
|
||||
### Composing lvalues of already composed matchers
|
||||
|
||||
Because a significant bug in this use case has persisted for 2+ years
|
||||
without a bug report, and to simplify the implementation, code that
|
||||
composes lvalues of composed matchers will not compile. That is,
|
||||
this code will no longer work:
|
||||
|
||||
```cpp
|
||||
auto m1 = Contains("string");
|
||||
auto m2 = Contains("random");
|
||||
auto composed1 = m1 || m2;
|
||||
auto m3 = Contains("different");
|
||||
auto composed2 = composed1 || m3;
|
||||
REQUIRE_THAT(foo(), !composed1);
|
||||
REQUIRE_THAT(foo(), composed2);
|
||||
```
|
||||
|
||||
Instead you will have to write this:
|
||||
|
||||
```cpp
|
||||
auto m1 = Contains("string");
|
||||
auto m2 = Contains("random");
|
||||
auto m3 = Contains("different");
|
||||
REQUIRE_THAT(foo(), !(m1 || m2));
|
||||
REQUIRE_THAT(foo(), m1 || m2 || m3);
|
||||
```
|
||||
|
||||
|
||||
## Planned changes
|
||||
|
||||
@@ -88,6 +121,17 @@ positively match a testspec.
|
||||
The API for Catch2's console colour will be changed to take an extra
|
||||
argument, the stream to which the colour code should be applied.
|
||||
|
||||
|
||||
### Type erasure in the `PredicateMatcher`
|
||||
|
||||
Currently, the `PredicateMatcher` uses `std::function` for type erasure,
|
||||
so that type of the matcher is always `PredicateMatcher<T>`, regardless
|
||||
of the type of the predicate. Because of the high compilation overhead
|
||||
of `std::function`, and the fact that the type erasure is used only rarely,
|
||||
`PredicateMatcher` will no longer be type erased in the future. Instead,
|
||||
the predicate type will be made part of the PredicateMatcher's type.
|
||||
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<a id="top"></a>
|
||||
# Data Generators
|
||||
|
||||
> Introduced in Catch 2.6.0.
|
||||
|
||||
Data generators (also known as _data driven/parametrized test cases_)
|
||||
let you reuse the same set of assertions across different input values.
|
||||
In Catch2, this means that they respect the ordering and nesting
|
||||
@@ -34,8 +36,8 @@ Catch2's provided generator functionality consists of three parts,
|
||||
* `GENERATE` macro, that serves to integrate generator expression with
|
||||
a test case,
|
||||
* 2 fundamental generators
|
||||
* `ValueGenerator<T>` -- contains only single element
|
||||
* `ValuesGenerator<T>` -- contains multiple elements
|
||||
* `SingleValueGenerator<T>` -- contains only single element
|
||||
* `FixedValuesGenerator<T>` -- contains multiple elements
|
||||
* 5 generic generators that modify other generators
|
||||
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
|
||||
for which the predicate returns "false"
|
||||
@@ -44,16 +46,22 @@ a test case,
|
||||
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
|
||||
on elements from a different generator
|
||||
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
|
||||
* 3 specific purpose generators
|
||||
* 4 specific purpose generators
|
||||
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
|
||||
* `RandomFloatGenerator<Float>` -- generates random Floats from range
|
||||
* `RangeGenerator<T>` -- generates all values inside a specific range
|
||||
* `RangeGenerator<T>` -- generates all values inside an arithmetic range
|
||||
* `IteratorGenerator<T>` -- copies and returns values from an iterator range
|
||||
|
||||
> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch 2.7.0.
|
||||
|
||||
> `IteratorGenerator<T>` was introduced in Catch 2.10.0.
|
||||
|
||||
The generators also have associated helper functions that infer their
|
||||
type, making their usage much nicer. These are
|
||||
|
||||
* `value(T&&)` for `ValueGenerator<T>`
|
||||
* `values(std::initializer_list<T>)` for `ValuesGenerator<T>`
|
||||
* `value(T&&)` for `SingleValueGenerator<T>`
|
||||
* `values(std::initializer_list<T>)` for `FixedValuesGenerator<T>`
|
||||
* `table<Ts...>(std::initializer_list<std::tuple<Ts...>>)` for `FixedValuesGenerator<std::tuple<Ts...>>`
|
||||
* `filter(predicate, GeneratorWrapper<T>&&)` for `FilterGenerator<T, Predicate>`
|
||||
* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
|
||||
* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
|
||||
@@ -61,9 +69,16 @@ type, making their usage much nicer. These are
|
||||
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
|
||||
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
|
||||
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
|
||||
* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
|
||||
* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
|
||||
* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
|
||||
* `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator<Arithmetic>` with a custom step size
|
||||
* `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
|
||||
* `from_range(Container const&)` for `IteratorGenerator<T>`
|
||||
|
||||
> `chunk()`, `random()` and both `range()` functions were introduced in Catch 2.7.0.
|
||||
|
||||
> `from_range` has been introduced in Catch 2.10.0
|
||||
|
||||
> `range()` for floating point numbers has been introduced in Catch 2.11.0
|
||||
|
||||
And can be used as shown in the example below to create a generator
|
||||
that returns 100 odd random number:
|
||||
@@ -84,7 +99,7 @@ Apart from registering generators with Catch2, the `GENERATE` macro has
|
||||
one more purpose, and that is to provide simple way of generating trivial
|
||||
generators, as seen in the first example on this page, where we used it
|
||||
as `auto i = GENERATE(1, 2, 3);`. This usage converted each of the three
|
||||
literals into a single `ValueGenerator<int>` and then placed them all in
|
||||
literals into a single `SingleValueGenerator<int>` and then placed them all in
|
||||
a special generator that concatenates other generators. It can also be
|
||||
used with other generators as arguments, such as `auto i = GENERATE(0, 2,
|
||||
take(100, random(300, 3000)));`. This is useful e.g. if you know that
|
||||
@@ -96,6 +111,8 @@ scope and thus capturing references is dangerous. If you need to use
|
||||
variables inside the generator expression, make sure you thought through
|
||||
the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**
|
||||
|
||||
> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch 2.7.1.
|
||||
|
||||
You can also override the inferred type by using `as<type>` as the first
|
||||
argument to the macro. This can be useful when dealing with string literals,
|
||||
if you want them to come out as `std::string`:
|
||||
|
@@ -45,6 +45,15 @@ the `REQUIRE` family of macros), Catch2 does not know that there are no
|
||||
more sections in that test case and must run the test case again.
|
||||
|
||||
|
||||
### MinGW/CygWin compilation (linking) is extremely slow
|
||||
|
||||
Compiling Catch2 with MinGW can be exceedingly slow, especially during
|
||||
the linking step. As far as we can tell, this is caused by deficiencies
|
||||
in its default linker. If you can tell MinGW to instead use lld, via
|
||||
`-fuse-ld=lld`, the link time should drop down to reasonable length
|
||||
again.
|
||||
|
||||
|
||||
## Features
|
||||
This section outlines some missing features, what is their status and their possible workarounds.
|
||||
|
||||
|
@@ -30,6 +30,8 @@ When the last `CHECK` fails in the "Bar" test case, then only one message will b
|
||||
|
||||
## Logging without local scope
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
|
||||
|
||||
`UNSCOPED_INFO` is similar to `INFO` with two key differences:
|
||||
|
||||
- Lifetime of an unscoped message is not tied to its own scope.
|
||||
@@ -104,6 +106,8 @@ This semicolon will be removed with next major version. It is highly advised to
|
||||
|
||||
**UNSCOPED_INFO(** _message expression_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
|
||||
|
||||
Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.
|
||||
|
||||
**WARN(** _message expression_ **)**
|
||||
|
@@ -12,31 +12,57 @@ The first argument is the thing (object or value) under test. The second part is
|
||||
which consists of either a single matcher or one or more matchers combined using `&&`, `||` or `!` operators.
|
||||
|
||||
For example, to assert that a string ends with a certain substring:
|
||||
|
||||
|
||||
```c++
|
||||
using Catch::Matchers::EndsWith; // or Catch::EndsWith
|
||||
std::string str = getStringFromSomewhere();
|
||||
REQUIRE_THAT( str, EndsWith( "as a service" ) );
|
||||
```
|
||||
REQUIRE_THAT( str, EndsWith( "as a service" ) );
|
||||
```
|
||||
|
||||
The matcher objects can take multiple arguments, allowing more fine tuning.
|
||||
The built-in string matchers, for example, take a second argument specifying whether the comparison is
|
||||
case sensitive or not:
|
||||
|
||||
```c++
|
||||
REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) );
|
||||
REQUIRE_THAT( str, EndsWith( "as a service", Catch::CaseSensitive::No ) );
|
||||
```
|
||||
|
||||
And matchers can be combined:
|
||||
|
||||
```c++
|
||||
REQUIRE_THAT( str,
|
||||
EndsWith( "as a service" ) ||
|
||||
(StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
|
||||
REQUIRE_THAT( str,
|
||||
EndsWith( "as a service" ) ||
|
||||
(StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
|
||||
```
|
||||
|
||||
_The combining operators do not take ownership of the matcher objects.
|
||||
This means that if you store the combined object, you have to ensure that
|
||||
the matcher objects outlive its last use. What this means is that code
|
||||
like this leads to a use-after-free and (hopefully) a crash:_
|
||||
|
||||
```cpp
|
||||
TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
|
||||
std::string str = "Bugs as a service";
|
||||
|
||||
auto match_expression = Catch::EndsWith( "as a service" ) ||
|
||||
(Catch::StartsWith( "Big data" ) && !Catch::Contains( "web scale" ) );
|
||||
REQUIRE_THAT(str, match_expression);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Built in matchers
|
||||
Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces.
|
||||
Catch2 provides some matchers by default. They can be found in the
|
||||
`Catch::Matchers::foo` namespace and are imported into the `Catch`
|
||||
namespace as well.
|
||||
|
||||
There are two parts to each of the built-in matchers, the matcher
|
||||
type itself and a helper function that provides template argument
|
||||
deduction when creating templated matchers. As an example, the matcher
|
||||
for checking that two instances of `std::vector` are identical is
|
||||
`EqualsMatcher<T>`, but the user is expected to use the `Equals`
|
||||
helper function instead.
|
||||
|
||||
|
||||
### String matchers
|
||||
The string matchers are `StartsWith`, `EndsWith`, `Contains`, `Equals` and `Matches`. The first four match a literal (sub)string against a result, while `Matches` takes and matches an ECMAScript regex. Do note that `Matches` matches the string as a whole, meaning that "abc" will not match against "abcd", but "abc.*" will.
|
||||
@@ -45,13 +71,42 @@ Each of the provided `std::string` matchers also takes an optional second argume
|
||||
|
||||
|
||||
### Vector matchers
|
||||
The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
|
||||
Catch2 currently provides 5 built-in matchers that work on `std::vector`.
|
||||
These are
|
||||
|
||||
* `Contains` which checks whether a specified vector is present in the result
|
||||
* `VectorContains` which checks whether a specified element is present in the result
|
||||
* `Equals` which checks whether the result is exactly equal (order matters) to a specific vector
|
||||
* `UnorderedEquals` which checks whether the result is equal to a specific vector under a permutation
|
||||
* `Approx` which checks whether the result is "approx-equal" (order matters, but comparison is done via `Approx`) to a specific vector
|
||||
> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch 2.7.2.
|
||||
|
||||
|
||||
### Floating point matchers
|
||||
The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart.
|
||||
Catch2 provides 3 matchers for working with floating point numbers. These
|
||||
are `WithinAbsMatcher`, `WithinUlpsMatcher` and `WithinRelMatcher`.
|
||||
|
||||
Do note that ULP-based checks only make sense when both compared numbers are of the same type and `WithinULP` will use type of its argument as the target type. This means that `WithinULP(1.f, 1)` will expect to compare `float`s, but `WithinULP(1., 1)` will expect to compare `double`s.
|
||||
The `WithinAbsMatcher` matcher accepts floating point numbers that are
|
||||
within a certain distance of target. It should be constructed with the
|
||||
`WithinAbs(double target, double margin)` helper.
|
||||
|
||||
The `WithinUlpsMatcher` matcher accepts floating point numbers that are
|
||||
within a certain number of [ULPs](https://en.wikipedia.org/wiki/Unit_in_the_last_place)
|
||||
of the target. Because ULP comparisons need to be done differently for
|
||||
`float`s and for `double`s, there are two overloads of the helpers for
|
||||
this matcher, `WithinULP(float target, int64_t ULPs)`, and
|
||||
`WithinULP(double target, int64_t ULPs)`.
|
||||
|
||||
The `WithinRelMatcher` matcher accepts floating point numbers that are
|
||||
_approximately equal_ with the target number with some specific tolerance.
|
||||
In other words, it checks that `|lhs - rhs| <= epsilon * max(|lhs|, |rhs|)`,
|
||||
with special casing for `INFINITY` and `NaN`. There are _4_ overloads of
|
||||
the helpers for this matcher, `WithinRel(double target, double margin)`,
|
||||
`WithinRel(float target, float margin)`, `WithinRel(double target)`, and
|
||||
`WithinRel(float target)`. The latter two provide a default epsilon of
|
||||
machine epsilon * 100.
|
||||
|
||||
> `WithinRel` matcher was introduced in Catch 2.10.0
|
||||
|
||||
### Generic matchers
|
||||
Catch also aims to provide a set of generic matchers. Currently this set
|
||||
@@ -72,13 +127,29 @@ The second argument is an optional description of the predicate, and is
|
||||
used only during reporting of the result.
|
||||
|
||||
|
||||
### Exception matchers
|
||||
Catch2 also provides an exception matcher that can be used to verify
|
||||
that an exception's message exactly matches desired string. The matcher
|
||||
is `ExceptionMessageMatcher`, and we also provide a helper function
|
||||
`Message`.
|
||||
|
||||
The matched exception must publicly derive from `std::exception` and
|
||||
the message matching is done _exactly_, including case.
|
||||
|
||||
> `ExceptionMessageMatcher` was introduced in Catch 2.10.0
|
||||
|
||||
Example use:
|
||||
```cpp
|
||||
REQUIRE_THROWS_MATCHES(throwsDerivedException(), DerivedException, Message("DerivedException::what"));
|
||||
```
|
||||
|
||||
## Custom matchers
|
||||
It's easy to provide your own matchers to extend Catch or just to work with your own types.
|
||||
|
||||
You need to provide two things:
|
||||
You need to provide two things:
|
||||
1. A matcher class, derived from `Catch::MatcherBase<T>` - where `T` is the type being tested.
|
||||
The constructor takes and stores any arguments needed (e.g. something to compare against) and you must
|
||||
override two methods: `match()` and `describe()`.
|
||||
override two methods: `match()` and `describe()`.
|
||||
2. A simple builder function. This is what is actually called from the test code and allows overloading.
|
||||
|
||||
Here's an example for asserting that an integer falls within a given range
|
||||
@@ -123,7 +194,7 @@ TEST_CASE("Integers are within a range")
|
||||
```
|
||||
|
||||
Running this test gives the following in the console:
|
||||
|
||||
|
||||
```
|
||||
/**/TestFile.cpp:123: FAILED:
|
||||
CHECK_THAT( 100, IsBetween( 1, 10 ) )
|
||||
|
@@ -23,6 +23,9 @@ C++11 implementation of Approval Tests, for quick, convenient testing of legacy
|
||||
### [Azmq](https://github.com/zeromq/azmq)
|
||||
Boost Asio style bindings for ZeroMQ.
|
||||
|
||||
### [Cataclysm: Dark Days Ahead](https://github.com/CleverRaven/Cataclysm-DDA)
|
||||
Post-apocalyptic survival RPG.
|
||||
|
||||
### [ChakraCore](https://github.com/Microsoft/ChakraCore)
|
||||
The core part of the Chakra JavaScript engine that powers Microsoft Edge.
|
||||
|
||||
@@ -50,9 +53,6 @@ Open source Oracle Tuxedo-like XATMI middleware for C and C++.
|
||||
### [Inja](https://github.com/pantor/inja)
|
||||
A header-only template engine for modern C++.
|
||||
|
||||
### [JSON for Modern C++](https://github.com/nlohmann/json)
|
||||
A, single-header, JSON parsing library that takes advantage of what C++ has to offer.
|
||||
|
||||
### [libcluon](https://github.com/chrberger/libcluon)
|
||||
A single-header-only library written in C++14 to glue distributed software components (UDP, TCP, shared memory) supporting natively Protobuf, LCM/ZCM, MsgPack, and JSON for dynamic message transformations in-between.
|
||||
|
||||
@@ -112,6 +112,9 @@ SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gr
|
||||
### [Standardese](https://github.com/foonathan/standardese)
|
||||
Standardese aims to be a nextgen Doxygen.
|
||||
|
||||
### [PopHead](https://github.com/SPC-Some-Polish-Coders/PopHead)
|
||||
A 2D, Zombie, RPG game which is being made on our own engine.
|
||||
|
||||
---
|
||||
|
||||
[Home](Readme.md#top)
|
||||
|
@@ -59,6 +59,8 @@ TEST_CASE( "SUCCEED showcase" ) {
|
||||
|
||||
* `STATIC_REQUIRE`
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch 2.4.2.
|
||||
|
||||
`STATIC_REQUIRE( expr )` is a macro that can be used the same way as a
|
||||
`static_assert`, but also registers the success with Catch2, so it is
|
||||
reported as a success at runtime. The whole check can also be deferred
|
||||
@@ -132,6 +134,8 @@ ANON_TEST_CASE() {
|
||||
|
||||
* `DYNAMIC_SECTION`
|
||||
|
||||
> Introduced in Catch 2.3.0.
|
||||
|
||||
`DYNAMIC_SECTION` is a `SECTION` where the user can use `operator<<` to
|
||||
create the final name for that section. This can be useful with e.g.
|
||||
generators, or when creating a `SECTION` dynamically, within a loop.
|
||||
|
@@ -2,6 +2,14 @@
|
||||
|
||||
# Release notes
|
||||
**Contents**<br>
|
||||
[2.11.3](#2113)<br>
|
||||
[2.11.2](#2112)<br>
|
||||
[2.11.1](#2111)<br>
|
||||
[2.11.0](#2110)<br>
|
||||
[2.10.2](#2102)<br>
|
||||
[2.10.1](#2101)<br>
|
||||
[2.10.0](#2100)<br>
|
||||
[2.9.2](#292)<br>
|
||||
[2.9.1](#291)<br>
|
||||
[2.9.0](#290)<br>
|
||||
[2.8.0](#280)<br>
|
||||
@@ -26,6 +34,178 @@
|
||||
[Older versions](#older-versions)<br>
|
||||
[Even Older versions](#even-older-versions)<br>
|
||||
|
||||
## 2.11.3
|
||||
|
||||
### Fixes
|
||||
* Fixed compilation error caused by lambdas in assertions under MSVC
|
||||
|
||||
|
||||
## 2.11.2
|
||||
|
||||
### Improvements
|
||||
* GCC and Clang now issue warnings for suspicious code in assertions (#1880)
|
||||
* E.g. `REQUIRE( int != unsigned int )` will now issue mixed signedness comparison warning
|
||||
* This has always worked on MSVC, but it now also works for GCC and current Clang versions
|
||||
* Colorization of "Test filters" output should be more robust now
|
||||
* `--wait-for-keypress` now also accepts `never` as an option (#1866)
|
||||
* Reporters no longer round-off nanoseconds when reporting benchmarking results (#1876)
|
||||
* Catch2's debug break now supports iOS while using Thumb instruction set (#1862)
|
||||
* It is now possible to customize benchmark's warm-up time when running the test binary (#1844)
|
||||
* `--benchmark-warmup-time {ms}`
|
||||
* User can now specify how Catch2 should break into debugger (#1846)
|
||||
|
||||
### Fixes
|
||||
* Fixes missing `<random>` include in benchmarking (#1831)
|
||||
* Fixed missing `<iterator>` include in benchmarking (#1874)
|
||||
* Hidden test cases are now also tagged with `[!hide]` as per documentation (#1847)
|
||||
* Detection of whether libc provides `std::nextafter` has been improved (#1854)
|
||||
* Detection of `wmain` no longer incorrectly looks for `WIN32` macro (#1849)
|
||||
* Now it just detects Windows platform
|
||||
* Composing already-composed matchers no longer modifies the partially-composed matcher expression
|
||||
* This bug has been present for the last ~2 years and nobody reported it
|
||||
|
||||
|
||||
## 2.11.1
|
||||
|
||||
### Improvements
|
||||
* Breaking into debugger is supported on iOS (#1817)
|
||||
* `google-build-using-namespace` clang-tidy warning is suppressed (#1799)
|
||||
|
||||
### Fixes
|
||||
* Clang on Windows is no longer assumed to implement MSVC's traditional preprocessor (#1806)
|
||||
* `ObjectStorage` now behaves properly in `const` contexts (#1820)
|
||||
* `GENERATE_COPY(a, b)` now compiles properly (#1809, #1815)
|
||||
* Some more cleanups in the benchmarking support
|
||||
|
||||
|
||||
## 2.11.0
|
||||
|
||||
### Improvements
|
||||
* JUnit reporter output now contains more details in case of failure (#1347, #1719)
|
||||
* Added SonarQube Test Data reporter (#1738)
|
||||
* It is in a separate header, just like the TAP, Automake, and TeamCity reporters
|
||||
* `range` generator now allows floating point numbers (#1776)
|
||||
* Reworked part of internals to increase throughput
|
||||
|
||||
|
||||
### Fixes
|
||||
* The single header version should contain full benchmarking support (#1800)
|
||||
* `[.foo]` is now properly parsed as `[.][foo]` when used on the command line (#1798)
|
||||
* Fixed compilation of benchmarking on platforms where `steady_clock::period` is not `std::nano` (#1794)
|
||||
|
||||
|
||||
|
||||
## 2.10.2
|
||||
|
||||
### Improvements
|
||||
* Catch2 will now compile on platform where `INFINITY` is double (#1782)
|
||||
|
||||
|
||||
### Fixes
|
||||
* Warning suppressed during listener registration will no longer leak
|
||||
|
||||
|
||||
|
||||
## 2.10.1
|
||||
|
||||
### Improvements
|
||||
* Catch2 now guards itself against `min` and `max` macros from `windows.h` (#1772)
|
||||
* Templated tests will now compile with ICC (#1748)
|
||||
* `WithinULP` matcher now uses scientific notation for stringification (#1760)
|
||||
|
||||
|
||||
### Fixes
|
||||
* Templated tests no longer trigger `-Wunused-templates` (#1762)
|
||||
* Suppressed clang-analyzer false positive in context getter (#1230, #1735)
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
* CMake no longer prohibits in-tree build when Catch2 is used as a subproject (#1773, #1774)
|
||||
|
||||
|
||||
|
||||
## 2.10.0
|
||||
|
||||
### Fixes
|
||||
* `TEMPLATE_LIST_TEST_CASE` now properly handles non-copyable and non-movable types (#1729)
|
||||
* Fixed compilation error on Solaris caused by a system header defining macro `TT` (#1722, #1723)
|
||||
* `REGISTER_ENUM` will now fail at compilation time if the registered enum is too large
|
||||
* Removed use of `std::is_same_v` in C++17 mode (#1757)
|
||||
* Fixed parsing of escaped special characters when reading test specs from a file (#1767, #1769)
|
||||
|
||||
|
||||
### Improvements
|
||||
* Trailing and leading whitespace in test/section specs are now ignored.
|
||||
* Writing to Android debug log now uses `__android_log_write` instead of `__android_log_print`
|
||||
* Android logging support can now be turned on/off at compile time (#1743)
|
||||
* The toggle is `CATCH_CONFIG_ANDROID_LOGWRITE`
|
||||
* Added a generator that returns elements of a range
|
||||
* Use via `from_range(from, to)` or `from_range(container)`
|
||||
* Added support for CRTs that do not provide `std::nextafter` (#1739)
|
||||
* They must still provide global `nextafter{f,l,}`
|
||||
* Enabled via `CATCH_CONFIG_GLOBAL_NEXTAFTER`
|
||||
* Special cased `Approx(inf)` not to match non-infinite values
|
||||
* Very strictly speaking this might be a breaking change, but it should match user expectations better
|
||||
* The output of benchmarking through the Console reporter when `--benchmark-no-analysis` is set is now much simpler (#1768)
|
||||
* Added a matcher that can be used for checking an exceptions message (#1649, #1728)
|
||||
* The matcher helper function is called `Message`
|
||||
* The exception must publicly derive from `std::exception`
|
||||
* The matching is done exactly, including case and whitespace
|
||||
* Added a matcher that can be used for checking relative equality of floating point numbers (#1746)
|
||||
* Unlike `Approx`, it considers both sides when determining the allowed margin
|
||||
* Special cases `NaN` and `INFINITY` to match user expectations
|
||||
* The matcher helper function is called `WithinRel`
|
||||
* The ULP matcher now allows for any possible distance between the two numbers
|
||||
* The random number generators now use Catch-global instance of RNG (#1734, #1736)
|
||||
* This means that nested random number generators actually generate different numbers
|
||||
|
||||
|
||||
### Miscellaneous
|
||||
* In-repo PNGs have been optimized to lower overhead of using Catch2 via git clone
|
||||
* Catch2 now uses its own implementation of the URBG concept
|
||||
* In the future we also plan to use our own implementation of the distributions from `<random>` to provide cross-platform repeatability of random results
|
||||
|
||||
|
||||
|
||||
## 2.9.2
|
||||
|
||||
### Fixes
|
||||
* `ChunkGenerator` can now be used with chunks of size 0 (#1671)
|
||||
* Nested subsections are now run properly when specific section is run via the `-c` argument (#1670, #1673)
|
||||
* Catch2 now consistently uses `_WIN32` to detect Windows platform (#1676)
|
||||
* `TEMPLATE_LIST_TEST_CASE` now support non-default constructible type lists (#1697)
|
||||
* Fixed a crash in the XMLReporter when a benchmark throws exception during warmup (#1706)
|
||||
* Fixed a possible infinite loop in CompactReporter (#1715)
|
||||
* Fixed `-w NoTests` returning 0 even when no tests were matched (#1449, #1683, #1684)
|
||||
* Fixed matcher compilation under Obj-C++ (#1661)
|
||||
|
||||
### Improvements
|
||||
* `RepeatGenerator` and `FixedValuesGenerator` now fail to compile when used with `bool` (#1692)
|
||||
* Previously they would fail at runtime.
|
||||
* Catch2 now supports Android's debug logging for its debug output (#1710)
|
||||
* Catch2 now detects and configures itself for the RTX platform (#1693)
|
||||
* You still need to pass `--benchmark-no-analysis` if you are using benchmarking under RTX
|
||||
* Removed a "storage class is not first" warning when compiling Catch2 with PGI compiler (#1717)
|
||||
|
||||
### Miscellaneous
|
||||
* Documentation now contains indication when a specific feature was introduced (#1695)
|
||||
* These start with Catch2 v2.3.0, (a bit over a year ago).
|
||||
* `docs/contributing.md` has been updated to provide contributors guidance on how to add these to newly written documentation
|
||||
* Various other documentation improvements
|
||||
* ToC fixes
|
||||
* Documented `--order` and `--rng-seed` command line options
|
||||
* Benchmarking documentation now clearly states that it requires opt-in
|
||||
* Documented `CATCH_CONFIG_CPP17_OPTIONAL` and `CATCH_CONFIG_CPP17_BYTE` macros
|
||||
* Properly documented built-in vector matchers
|
||||
* Improved `*_THROWS_MATCHES` documentation a bit
|
||||
* CMake config file is now arch-independent even if `CMAKE_SIZEOF_VOID_P` is in CMake cache (#1660)
|
||||
* `CatchAddTests` now properly escapes `[` and `]` in test names (#1634, #1698)
|
||||
* Reverted `CatchAddTests` adding tags as CTest labels (#1658)
|
||||
* The script broke when test names were too long
|
||||
* Overwriting `LABELS` caused trouble for users who set them manually
|
||||
* CMake does not let users append to `LABELS` if the test name has spaces
|
||||
|
||||
|
||||
## 2.9.1
|
||||
|
||||
### Fixes
|
||||
@@ -88,7 +268,7 @@
|
||||
|
||||
### Improvements
|
||||
* Reporters now print out the filters applied to test cases (#1550, #1585)
|
||||
* Added `GENERATE_COPY` and `GENERATE_VAR` macros that can use variables inside the generator expression
|
||||
* Added `GENERATE_COPY` and `GENERATE_REF` macros that can use variables inside the generator expression
|
||||
* Because of the significant danger of lifetime issues, the default `GENERATE` macro still does not allow variables
|
||||
* The `map` generator helper now deduces the mapped return type (#1576)
|
||||
|
||||
|
@@ -42,8 +42,8 @@ Tag version and release title should be same as the new version,
|
||||
description should contain the release notes for the current release.
|
||||
Single header version of `catch.hpp` *needs* to be attached as a binary,
|
||||
as that is where the official download link links to. Preferably
|
||||
it should use linux line endings. All non-bundled reporters (Automake,
|
||||
TAP, TeamCity) should also be attached as binaries, as they might be
|
||||
it should use linux line endings. All non-bundled reporters (Automake, TAP,
|
||||
TeamCity, SonarQube) should also be attached as binaries, as they might be
|
||||
dependent on a specific version of the single-include header.
|
||||
|
||||
Since 2.5.0, the release tag and the "binaries" (headers) should be PGP
|
||||
@@ -67,6 +67,7 @@ $ gpg2 --armor --output catch.hpp.asc --detach-sig catch.hpp
|
||||
$ gpg2 --armor --output catch_reporter_automake.hpp.asc --detach-sig catch_reporter_automake.hpp
|
||||
$ gpg2 --armor --output catch_reporter_teamcity.hpp.asc --detach-sig catch_reporter_teamcity.hpp
|
||||
$ gpg2 --armor --output catch_reporter_tap.hpp.asc --detach-sig catch_reporter_tap.hpp
|
||||
$ gpg2 --armor --output catch_reporter_sonarqube.hpp.asc --detach-sig catch_reporter_sonarqube.hpp
|
||||
```
|
||||
|
||||
_GPG does not support signing multiple files in single invocation._
|
||||
|
@@ -29,6 +29,7 @@ Do this in one source file - the same one you have `CATCH_CONFIG_MAIN` or `CATCH
|
||||
Use this when building as part of a TeamCity build to see results as they happen ([code example](../examples/207-Rpt-TeamCityReporter.cpp)).
|
||||
* `tap` writes in the TAP ([Test Anything Protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol)) format.
|
||||
* `automake` writes in a format that correspond to [automake .trs](https://www.gnu.org/software/automake/manual/html_node/Log-files-generation-and-test-results-recording.html) files
|
||||
* `sonarqube` writes the [SonarQube Generic Test Data](https://docs.sonarqube.org/latest/analysis/generic-test/) XML format.
|
||||
|
||||
You see what reporters are available from the command line by running with `--list-reporters`.
|
||||
|
||||
|
@@ -22,7 +22,7 @@ But functions and methods can also be written inline in header files. The downsi
|
||||
|
||||
Because Catch is implemented *entirely* in headers you might think that the whole of Catch must be compiled into every translation unit that uses it! Actually it's not quite as bad as that. Catch mitigates this situation by effectively maintaining the traditional separation between the implementation code and declarations. Internally the implementation code is protected by ```#ifdef```s and is conditionally compiled into only one translation unit. This translation unit is that one that ```#define```s ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER```. Let's call this the main source file.
|
||||
|
||||
As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch
|
||||
As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch.
|
||||
|
||||
## Practical example
|
||||
Assume you have the `Factorial` function from the [tutorial](tutorial.md#top) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`:
|
||||
|
@@ -84,10 +84,13 @@ This macro maps onto ```TEST_CASE``` and works in the same way, except that the
|
||||
|
||||
These macros map onto ```SECTION```s except that the section names are the _something_s prefixed by "given: ", "when: " or "then: " respectively.
|
||||
|
||||
* **AND_GIVEN(** _something_ **)**
|
||||
* **AND_WHEN(** _something_ **)**
|
||||
* **AND_THEN(** _something_ **)**
|
||||
|
||||
Similar to ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```WHEN```s and ```THEN```s together.
|
||||
Similar to ```GIVEN```, ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together.
|
||||
|
||||
> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch 2.4.0.
|
||||
|
||||
When any of these macros are used the console reporter recognises them and formats the test case header such that the Givens, Whens and Thens are aligned to aid readability.
|
||||
|
||||
@@ -101,6 +104,8 @@ by types, in the form of `TEMPLATE_TEST_CASE`,
|
||||
|
||||
* **TEMPLATE_TEST_CASE(** _test name_ , _tags_, _type1_, _type2_, ..., _typen_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch 2.5.0.
|
||||
|
||||
_test name_ and _tag_ are exactly the same as they are in `TEST_CASE`,
|
||||
with the difference that the tag string must be provided (however, it
|
||||
can be empty). _type1_ through _typen_ is the list of types for which
|
||||
@@ -151,6 +156,8 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in
|
||||
|
||||
* **TEMPLATE_PRODUCT_TEST_CASE(** _test name_ , _tags_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch 2.6.0.
|
||||
|
||||
_template-type1_ through _template-typen_ is list of template template
|
||||
types which should be combined with each of _template-arg1_ through
|
||||
_template-argm_, resulting in _n * m_ test cases. Inside the test case,
|
||||
@@ -194,6 +201,8 @@ is very high and should not be encountered in practice._
|
||||
|
||||
* **TEMPLATE_LIST_TEST_CASE(** _test name_, _tags_, _type list_ **)**
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch 2.9.0.
|
||||
|
||||
_type list_ is a generic list of types on which test case should be instantiated.
|
||||
List can be `std::tuple`, `boost::mpl::list`, `boost::mp11::mp_list` or anything with
|
||||
`template <typename...>` signature.
|
||||
@@ -212,6 +221,8 @@ TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std
|
||||
|
||||
## Signature based parametrised test cases
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
|
||||
|
||||
In addition to [type parametrised test cases](#type-parametrised-test-cases) Catch2 also supports
|
||||
signature base parametrised test cases, in form of `TEMPLATE_TEST_CASE_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_SIG`.
|
||||
These test cases have similar syntax like [type parametrised test cases](#type-parametrised-test-cases), with one
|
||||
|
@@ -1,6 +1,8 @@
|
||||
<a id="top"></a>
|
||||
# Test fixtures
|
||||
|
||||
## Defining test fixtures
|
||||
|
||||
Although Catch allows you to group tests together as sections within a test case, it can still be convenient, sometimes, to group them using a more traditional test fixture. Catch fully supports this too. You define the test fixture as a simple structure:
|
||||
|
||||
```c++
|
||||
@@ -84,6 +86,9 @@ _While there is an upper limit on the number of types you can specify
|
||||
in single `TEMPLATE_TEST_CASE_METHOD` or `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
the limit is very high and should not be encountered in practice._
|
||||
|
||||
## Signature-based parametrised test fixtures
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
|
||||
|
||||
Catch2 also provides `TEMPLATE_TEST_CASE_METHOD_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG` to support
|
||||
fixtures using non-type template parameters. These test cases work similar to `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
|
||||
@@ -100,6 +105,13 @@ TEMPLATE_TEST_CASE_METHOD_SIG(Nttp_Fixture, "A TEMPLATE_TEST_CASE_METHOD_SIG bas
|
||||
REQUIRE(Nttp_Fixture<V>::value > 0);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct Template_Fixture_2 {
|
||||
Template_Fixture_2() {}
|
||||
|
||||
T m_a;
|
||||
};
|
||||
|
||||
template< typename T, size_t V>
|
||||
struct Template_Foo_2 {
|
||||
size_t size() { return V; }
|
||||
@@ -111,6 +123,8 @@ TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG(Template_Fixture_2, "A TEMPLATE_PRODUCT_TE
|
||||
}
|
||||
```
|
||||
|
||||
## Template fixtures with types specified in template type lists
|
||||
|
||||
Catch2 also provides `TEMPLATE_LIST_TEST_CASE_METHOD` to support template fixtures with types specified in
|
||||
template type lists like `std::tuple`, `boost::mpl::list` or `boost::mp11::mp_list`. This test case works the same as `TEMPLATE_TEST_CASE_METHOD`,
|
||||
only difference is the source of types. This allows you to reuse the template type list in multiple test cases.
|
||||
|
@@ -71,6 +71,8 @@ CATCH_TRANSLATE_EXCEPTION( MyType& ex ) {
|
||||
|
||||
## Enums
|
||||
|
||||
> Introduced in Catch 2.8.0.
|
||||
|
||||
Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
|
||||
If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
|
||||
However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code.
|
||||
@@ -108,6 +110,8 @@ TEST_CASE() {
|
||||
|
||||
## Floating point precision
|
||||
|
||||
> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch 2.8.0.
|
||||
|
||||
Catch provides a built-in `StringMaker` specialization for both `float`
|
||||
and `double`. By default, it uses what we think is a reasonable precision,
|
||||
but you can customize it by modifying the `precision` static variable
|
||||
|
@@ -106,7 +106,7 @@ Of course there are still more issues to deal with. For example we'll hit proble
|
||||
Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take a moment to consider those before we move on.
|
||||
|
||||
1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md#top). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md#top)).
|
||||
2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>, ). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests.
|
||||
2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests.
|
||||
3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names.
|
||||
4. We write our individual test assertions using the ```REQUIRE``` macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced.
|
||||
|
||||
|
@@ -24,7 +24,7 @@ std::string ws(int const level) {
|
||||
template< typename T >
|
||||
std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
|
||||
os << "{ ";
|
||||
for ( auto x : v )
|
||||
for ( const auto& x : v )
|
||||
os << x << ", ";
|
||||
return os << "}";
|
||||
}
|
||||
@@ -57,7 +57,7 @@ void print( std::ostream& os, int const level, Catch::MessageInfo const& info )
|
||||
|
||||
void print( std::ostream& os, int const level, std::string const& title, std::vector<Catch::MessageInfo> const& v ) {
|
||||
os << ws(level ) << title << ":\n";
|
||||
for ( auto x : v )
|
||||
for ( const auto& x : v )
|
||||
{
|
||||
os << ws(level+1) << "{\n";
|
||||
print( os, level+2, x );
|
||||
@@ -300,7 +300,7 @@ char const * dashed_line =
|
||||
struct MyListener : Catch::TestEventListenerBase {
|
||||
|
||||
using TestEventListenerBase::TestEventListenerBase; // inherit constructor
|
||||
|
||||
|
||||
// Get rid of Wweak-tables
|
||||
~MyListener();
|
||||
|
||||
|
@@ -10,12 +10,13 @@
|
||||
#define CATCH_CONFIG_MAIN
|
||||
#include <catch2/catch.hpp>
|
||||
|
||||
|
||||
class out_buff : public std::stringbuf {
|
||||
std::FILE* m_stream;
|
||||
public:
|
||||
out_buff(std::FILE* stream) :m_stream(stream) {}
|
||||
~out_buff() { pubsync(); }
|
||||
int sync() {
|
||||
out_buff(std::FILE* stream):m_stream(stream) {}
|
||||
~out_buff();
|
||||
int sync() override {
|
||||
int ret = 0;
|
||||
for (unsigned char c : str()) {
|
||||
if (putc(c, m_stream) == EOF) {
|
||||
@@ -29,6 +30,12 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
out_buff::~out_buff() { pubsync(); }
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic ignored "-Wexit-time-destructors" // static variables in cout/cerr/clog
|
||||
#endif
|
||||
|
||||
namespace Catch {
|
||||
std::ostream& cout() {
|
||||
static std::ostream ret(new out_buff(stdout));
|
||||
|
@@ -22,15 +22,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::string const& get() const override {
|
||||
return m_line;
|
||||
}
|
||||
std::string const& get() const override;
|
||||
|
||||
bool next() override {
|
||||
return !!std::getline(m_stream, m_line);
|
||||
}
|
||||
};
|
||||
|
||||
std::string const& LineGenerator::get() const {
|
||||
return m_line;
|
||||
}
|
||||
|
||||
// This helper function provides a nicer UX when instantiating the generator
|
||||
// Notice that it returns an instance of GeneratorWrapper<std::string>, which
|
||||
// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
|
||||
|
@@ -10,8 +10,8 @@
|
||||
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
|
||||
|
||||
#define CATCH_VERSION_MAJOR 2
|
||||
#define CATCH_VERSION_MINOR 9
|
||||
#define CATCH_VERSION_PATCH 1
|
||||
#define CATCH_VERSION_MINOR 11
|
||||
#define CATCH_VERSION_PATCH 3
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang system_header
|
||||
@@ -80,7 +80,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
#include "internal/benchmark/catch_benchmark.hpp"
|
||||
#include "internal/benchmark/catch_benchmarking_all.hpp"
|
||||
#endif
|
||||
|
||||
#endif // ! CATCH_CONFIG_IMPL_ONLY
|
||||
|
@@ -44,10 +44,10 @@ namespace Catch {
|
||||
template <typename Clock>
|
||||
ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
|
||||
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
|
||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(Detail::warmup_time));
|
||||
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
|
||||
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun);
|
||||
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
|
||||
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(Detail::warmup_time), Detail::warmup_iterations };
|
||||
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
|
||||
}
|
||||
|
||||
template <typename Clock = default_clock>
|
||||
@@ -79,7 +79,7 @@ namespace Catch {
|
||||
});
|
||||
|
||||
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
|
||||
BenchmarkStats<std::chrono::duration<double, std::nano>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
||||
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
|
||||
getResultCapture().benchmarkEnded(stats);
|
||||
|
||||
} CATCH_CATCH_ALL{
|
||||
|
29
include/internal/benchmark/catch_benchmarking_all.hpp
Normal file
29
include/internal/benchmark/catch_benchmarking_all.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
|
||||
// A proxy header that includes all of the benchmarking headers to allow
|
||||
// concise include of the benchmarking features. You should prefer the
|
||||
// individual includes in standard use.
|
||||
|
||||
#include "catch_benchmark.hpp"
|
||||
#include "catch_chronometer.hpp"
|
||||
#include "catch_clock.hpp"
|
||||
#include "catch_constructor.hpp"
|
||||
#include "catch_environment.hpp"
|
||||
#include "catch_estimate.hpp"
|
||||
#include "catch_execution_plan.hpp"
|
||||
#include "catch_optimizer.hpp"
|
||||
#include "catch_outlier_classification.hpp"
|
||||
#include "catch_sample_analysis.hpp"
|
||||
#include "detail/catch_analyse.hpp"
|
||||
#include "detail/catch_benchmark_function.hpp"
|
||||
#include "detail/catch_complete_invoke.hpp"
|
||||
#include "detail/catch_estimate_clock.hpp"
|
||||
#include "detail/catch_measure.hpp"
|
||||
#include "detail/catch_repeat.hpp"
|
||||
#include "detail/catch_run_for_at_least.hpp"
|
||||
#include "detail/catch_stats.hpp"
|
||||
#include "detail/catch_timing.hpp"
|
@@ -14,60 +14,66 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Detail {
|
||||
template <typename T, bool Destruct>
|
||||
struct ObjectStorage
|
||||
{
|
||||
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
|
||||
|
||||
ObjectStorage() : data() {}
|
||||
|
||||
ObjectStorage(const ObjectStorage& other)
|
||||
namespace Benchmark {
|
||||
namespace Detail {
|
||||
template <typename T, bool Destruct>
|
||||
struct ObjectStorage
|
||||
{
|
||||
new(&data) T(other.stored_object());
|
||||
}
|
||||
using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
|
||||
|
||||
ObjectStorage(ObjectStorage&& other)
|
||||
{
|
||||
new(&data) T(std::move(other.stored_object()));
|
||||
}
|
||||
ObjectStorage() : data() {}
|
||||
|
||||
~ObjectStorage() { destruct_on_exit<T>(); }
|
||||
ObjectStorage(const ObjectStorage& other)
|
||||
{
|
||||
new(&data) T(other.stored_object());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args)
|
||||
{
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
}
|
||||
ObjectStorage(ObjectStorage&& other)
|
||||
{
|
||||
new(&data) T(std::move(other.stored_object()));
|
||||
}
|
||||
|
||||
template <bool AllowManualDestruction = !Destruct>
|
||||
typename std::enable_if<AllowManualDestruction>::type destruct()
|
||||
{
|
||||
stored_object().~T();
|
||||
}
|
||||
~ObjectStorage() { destruct_on_exit<T>(); }
|
||||
|
||||
private:
|
||||
// If this is a constructor benchmark, destruct the underlying object
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
|
||||
// Otherwise, don't
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
|
||||
template <typename... Args>
|
||||
void construct(Args&&... args)
|
||||
{
|
||||
new (&data) T(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
T& stored_object()
|
||||
{
|
||||
return *static_cast<T*>(static_cast<void*>(&data));
|
||||
}
|
||||
template <bool AllowManualDestruction = !Destruct>
|
||||
typename std::enable_if<AllowManualDestruction>::type destruct()
|
||||
{
|
||||
stored_object().~T();
|
||||
}
|
||||
|
||||
TStorage data;
|
||||
};
|
||||
private:
|
||||
// If this is a constructor benchmark, destruct the underlying object
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
|
||||
// Otherwise, don't
|
||||
template <typename U>
|
||||
void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
|
||||
|
||||
T& stored_object() {
|
||||
return *static_cast<T*>(static_cast<void*>(&data));
|
||||
}
|
||||
|
||||
T const& stored_object() const {
|
||||
return *static_cast<T*>(static_cast<void*>(&data));
|
||||
}
|
||||
|
||||
|
||||
TStorage data;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using storage_for = Detail::ObjectStorage<T, true>;
|
||||
|
||||
template <typename T>
|
||||
using destructable_object = Detail::ObjectStorage<T, false>;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
using storage_for = Detail::ObjectStorage<T, true>;
|
||||
|
||||
template <typename T>
|
||||
using destructable_object = Detail::ObjectStorage<T, false>;
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CONSTRUCTOR_HPP_INCLUDED
|
||||
|
@@ -176,9 +176,10 @@ namespace Catch {
|
||||
|
||||
|
||||
bootstrap_analysis analyse_samples(double confidence_level, int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
static std::random_device entropy;
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
|
||||
|
||||
|
@@ -18,11 +18,13 @@
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <cmath>
|
||||
#include <utility>
|
||||
#include <cstddef>
|
||||
#include <random>
|
||||
|
||||
namespace Catch {
|
||||
namespace Benchmark {
|
||||
|
@@ -52,7 +52,8 @@ namespace Detail {
|
||||
bool Approx::equalityComparisonImpl(const double other) const {
|
||||
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
|
||||
// Thanks to Richard Harris for his help refining the scaled margin value
|
||||
return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
|
||||
return marginComparison(m_value, other, m_margin)
|
||||
|| marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(std::isinf(m_value)? 0 : m_value)));
|
||||
}
|
||||
|
||||
void Approx::setMargin(double newMargin) {
|
||||
|
@@ -45,7 +45,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
bool AssertionResult::hasExpression() const {
|
||||
return m_info.capturedExpression[0] != 0;
|
||||
return !m_info.capturedExpression.empty();
|
||||
}
|
||||
|
||||
bool AssertionResult::hasMessage() const {
|
||||
@@ -53,16 +53,22 @@ namespace Catch {
|
||||
}
|
||||
|
||||
std::string AssertionResult::getExpression() const {
|
||||
if( isFalseTest( m_info.resultDisposition ) )
|
||||
return "!(" + m_info.capturedExpression + ")";
|
||||
else
|
||||
return m_info.capturedExpression;
|
||||
// Possibly overallocating by 3 characters should be basically free
|
||||
std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
|
||||
if (isFalseTest(m_info.resultDisposition)) {
|
||||
expr += "!(";
|
||||
}
|
||||
expr += m_info.capturedExpression;
|
||||
if (isFalseTest(m_info.resultDisposition)) {
|
||||
expr += ')';
|
||||
}
|
||||
return expr;
|
||||
}
|
||||
|
||||
std::string AssertionResult::getExpressionInMacro() const {
|
||||
std::string expr;
|
||||
if( m_info.macroName[0] == 0 )
|
||||
expr = m_info.capturedExpression;
|
||||
if( m_info.macroName.empty() )
|
||||
expr = static_cast<std::string>(m_info.capturedExpression);
|
||||
else {
|
||||
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
|
||||
expr += m_info.macroName;
|
||||
|
@@ -41,15 +41,17 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
|
||||
do { \
|
||||
/* The expression should not be evaluated, but warnings should hopefully be checked */ \
|
||||
CATCH_INTERNAL_IGNORE_BUT_WARN(__VA_ARGS__); \
|
||||
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
|
||||
INTERNAL_CATCH_TRY { \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
|
||||
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
|
||||
INTERNAL_CATCH_REACT( catchAssertionHandler ) \
|
||||
} while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
|
||||
// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
|
||||
} while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
|
||||
|
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "catch_capture.hpp"
|
||||
#include "catch_matchers.h"
|
||||
#include "catch_matchers_exception.hpp"
|
||||
#include "catch_matchers_floating.h"
|
||||
#include "catch_matchers_generic.hpp"
|
||||
#include "catch_matchers_string.h"
|
||||
|
@@ -49,9 +49,14 @@ namespace Catch {
|
||||
if( !line.empty() && !startsWith( line, '#' ) ) {
|
||||
if( !startsWith( line, '"' ) )
|
||||
line = '"' + line + '"';
|
||||
config.testsOrTags.push_back( line + ',' );
|
||||
config.testsOrTags.push_back( line );
|
||||
config.testsOrTags.emplace_back( "," );
|
||||
}
|
||||
}
|
||||
//Remove comma in the end
|
||||
if(!config.testsOrTags.empty())
|
||||
config.testsOrTags.erase( config.testsOrTags.end()-1 );
|
||||
|
||||
return ParserResult::ok( ParseResultType::Matched );
|
||||
};
|
||||
auto const setTestOrder = [&]( std::string const& order ) {
|
||||
@@ -86,14 +91,16 @@ namespace Catch {
|
||||
};
|
||||
auto const setWaitForKeypress = [&]( std::string const& keypress ) {
|
||||
auto keypressLc = toLower( keypress );
|
||||
if( keypressLc == "start" )
|
||||
if (keypressLc == "never")
|
||||
config.waitForKeypress = WaitForKeypress::Never;
|
||||
else if( keypressLc == "start" )
|
||||
config.waitForKeypress = WaitForKeypress::BeforeStart;
|
||||
else if( keypressLc == "exit" )
|
||||
config.waitForKeypress = WaitForKeypress::BeforeExit;
|
||||
else if( keypressLc == "both" )
|
||||
config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
|
||||
else
|
||||
return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
|
||||
return ParserResult::runtimeError( "keypress argument must be one of: never, start, exit or both. '" + keypress + "' not recognised" );
|
||||
return ParserResult::ok( ParseResultType::Matched );
|
||||
};
|
||||
auto const setVerbosity = [&]( std::string const& verbosity ) {
|
||||
@@ -193,7 +200,7 @@ namespace Catch {
|
||||
| Opt( config.libIdentify )
|
||||
["--libidentify"]
|
||||
( "report name and version according to libidentify standard" )
|
||||
| Opt( setWaitForKeypress, "start|exit|both" )
|
||||
| Opt( setWaitForKeypress, "never|start|exit|both" )
|
||||
["--wait-for-keypress"]
|
||||
( "waits for a keypress before exiting" )
|
||||
| Opt( config.benchmarkSamples, "samples" )
|
||||
@@ -208,7 +215,10 @@ namespace Catch {
|
||||
| Opt( config.benchmarkNoAnalysis )
|
||||
["--benchmark-no-analysis"]
|
||||
( "perform only measurements; do not perform any analysis" )
|
||||
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||
| Opt( config.benchmarkWarmupTime, "benchmarkWarmupTime" )
|
||||
["--benchmark-warmup-time"]
|
||||
( "amount of time in milliseconds spent on warming up each test (default: 100)" )
|
||||
| Arg( config.testsOrTags, "test name|pattern|tags" )
|
||||
( "which test or tests to use" );
|
||||
|
||||
return cli;
|
||||
|
@@ -15,9 +15,6 @@
|
||||
|
||||
namespace Catch {
|
||||
|
||||
bool SourceLineInfo::empty() const noexcept {
|
||||
return file[0] == '\0';
|
||||
}
|
||||
bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
|
||||
return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
|
||||
}
|
||||
|
@@ -57,7 +57,7 @@ namespace Catch {
|
||||
SourceLineInfo( SourceLineInfo&& ) noexcept = default;
|
||||
SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
|
||||
|
||||
bool empty() const noexcept;
|
||||
bool empty() const noexcept { return file[0] == '\0'; }
|
||||
bool operator == ( SourceLineInfo const& other ) const noexcept;
|
||||
bool operator < ( SourceLineInfo const& other ) const noexcept;
|
||||
|
||||
|
@@ -43,32 +43,39 @@
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
// 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)
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
#endif
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
#if defined(__clang__)
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
_Pragma( "clang diagnostic push" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
_Pragma( "clang diagnostic pop" )
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
|
||||
|
||||
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
|
||||
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
|
||||
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
|
||||
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||
_Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
|
||||
|
||||
#endif // __clang__
|
||||
|
||||
@@ -94,6 +101,7 @@
|
||||
// Android somehow still does not support std::to_string
|
||||
#if defined(__ANDROID__)
|
||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||
# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -115,7 +123,7 @@
|
||||
// Required for some versions of Cygwin to declare gettimeofday
|
||||
// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
|
||||
# define _BSD_SOURCE
|
||||
// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
|
||||
// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
|
||||
// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
|
||||
# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
|
||||
&& !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
|
||||
@@ -127,8 +135,10 @@
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Visual C++
|
||||
#ifdef _MSC_VER
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) )
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) )
|
||||
|
||||
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
||||
@@ -145,9 +155,12 @@
|
||||
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
|
||||
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
|
||||
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
|
||||
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
|
||||
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
# endif
|
||||
# if !defined(__clang__) // Handle Clang masquerading for msvc
|
||||
# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
|
||||
# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||
# endif // MSVC_TRADITIONAL
|
||||
# endif // __clang__
|
||||
|
||||
#endif // _MSC_VER
|
||||
|
||||
#if defined(_REENTRANT) || defined(_MSC_VER)
|
||||
@@ -184,41 +197,55 @@
|
||||
#define CATCH_INTERNAL_CONFIG_COUNTER
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if string_view is available and usable
|
||||
// The check is split apart to work around v140 (VS2015) preprocessor issue...
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if optional is available and usable
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
|
||||
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
#endif // __has_include
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Check if variant is available and usable
|
||||
// RTX is a special version of Windows that is real time.
|
||||
// This means that it is detected as Windows, but does not provide
|
||||
// the same set of capabilities as real Windows does.
|
||||
#if defined(UNDER_RTSS) || defined(RTX64_BUILD)
|
||||
#define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
|
||||
#define CATCH_INTERNAL_CONFIG_NO_ASYNC
|
||||
#define CATCH_CONFIG_COLOUR_NONE
|
||||
#endif
|
||||
|
||||
#if !defined(_GLIBCXX_USE_C99_MATH_TR1)
|
||||
#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
|
||||
#endif
|
||||
|
||||
// Various stdlib support checks that require __has_include
|
||||
#if defined(__has_include)
|
||||
# if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# if defined(__clang__) && (__clang_major__ < 8)
|
||||
// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
|
||||
// fix should be in clang 8, workaround in libstdc++ 8.2
|
||||
# include <ciso646>
|
||||
# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||
# define CATCH_CONFIG_NO_CPP17_VARIANT
|
||||
# else
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||
# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||
# else
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||
# endif // defined(__clang__) && (__clang_major__ < 8)
|
||||
# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
#endif // __has_include
|
||||
// Check if string_view is available and usable
|
||||
#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
|
||||
#endif
|
||||
|
||||
// Check if optional is available and usable
|
||||
# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
|
||||
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
|
||||
// Check if byte is available and usable
|
||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
|
||||
// Check if variant is available and usable
|
||||
# if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
# if defined(__clang__) && (__clang_major__ < 8)
|
||||
// work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
|
||||
// fix should be in clang 8, workaround in libstdc++ 8.2
|
||||
# include <ciso646>
|
||||
# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||
# define CATCH_CONFIG_NO_CPP17_VARIANT
|
||||
# else
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||
# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||
# else
|
||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||
# endif // defined(__clang__) && (__clang_major__ < 8)
|
||||
# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
||||
#endif // defined(__has_include)
|
||||
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
|
||||
@@ -256,6 +283,11 @@
|
||||
# define CATCH_CONFIG_CPP17_VARIANT
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE)
|
||||
# define CATCH_CONFIG_CPP17_BYTE
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
|
||||
# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
|
||||
#endif
|
||||
@@ -272,25 +304,54 @@
|
||||
# define CATCH_CONFIG_POLYFILL_ISNAN
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
|
||||
#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC)
|
||||
# define CATCH_CONFIG_USE_ASYNC
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE)
|
||||
# define CATCH_CONFIG_ANDROID_LOGWRITE
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
|
||||
# define CATCH_CONFIG_GLOBAL_NEXTAFTER
|
||||
#endif
|
||||
|
||||
|
||||
// Even if we do not think the compiler has that warning, we still have
|
||||
// to provide a macro that can be used by the code.
|
||||
#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION)
|
||||
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION)
|
||||
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
|
||||
#endif
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||
#endif
|
||||
|
||||
// The goal of this macro is to avoid evaluation of the arguments, but
|
||||
// still have the compiler warn on problems inside...
|
||||
#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN)
|
||||
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...)
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
|
||||
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#elif defined(__clang__) && (__clang_major__ < 5)
|
||||
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#endif
|
||||
|
||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
|
||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
|
||||
#endif
|
||||
|
||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||
|
@@ -15,11 +15,23 @@ namespace Catch {
|
||||
: m_data( data ),
|
||||
m_stream( openStream() )
|
||||
{
|
||||
// We need to trim filter specs to avoid trouble with superfluous
|
||||
// whitespace (esp. important for bdd macros, as those are manually
|
||||
// aligned with whitespace).
|
||||
|
||||
for (auto& elem : m_data.testsOrTags) {
|
||||
elem = trim(elem);
|
||||
}
|
||||
for (auto& elem : m_data.sectionsToRun) {
|
||||
elem = trim(elem);
|
||||
}
|
||||
|
||||
TestSpecParser parser(ITagAliasRegistry::get());
|
||||
if (!data.testsOrTags.empty()) {
|
||||
if (!m_data.testsOrTags.empty()) {
|
||||
m_hasTestFilters = true;
|
||||
for( auto const& testOrTags : data.testsOrTags )
|
||||
parser.parse( testOrTags );
|
||||
for (auto const& testOrTags : m_data.testsOrTags) {
|
||||
parser.parse(testOrTags);
|
||||
}
|
||||
}
|
||||
m_testSpec = parser.testSpec();
|
||||
}
|
||||
@@ -32,7 +44,7 @@ namespace Catch {
|
||||
bool Config::listTestNamesOnly() const { return m_data.listTestNamesOnly; }
|
||||
bool Config::listTags() const { return m_data.listTags; }
|
||||
bool Config::listReporters() const { return m_data.listReporters; }
|
||||
|
||||
|
||||
std::string Config::getProcessName() const { return m_data.processName; }
|
||||
std::string const& Config::getReporterName() const { return m_data.reporterName; }
|
||||
|
||||
@@ -60,10 +72,11 @@ namespace Catch {
|
||||
bool Config::showInvisibles() const { return m_data.showInvisibles; }
|
||||
Verbosity Config::verbosity() const { return m_data.verbosity; }
|
||||
|
||||
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
|
||||
int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
|
||||
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
|
||||
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
|
||||
bool Config::benchmarkNoAnalysis() const { return m_data.benchmarkNoAnalysis; }
|
||||
int Config::benchmarkSamples() const { return m_data.benchmarkSamples; }
|
||||
double Config::benchmarkConfidenceInterval() const { return m_data.benchmarkConfidenceInterval; }
|
||||
unsigned int Config::benchmarkResamples() const { return m_data.benchmarkResamples; }
|
||||
std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
|
||||
|
||||
IStream const* Config::openStream() {
|
||||
return Catch::makeStream(m_data.outputFilename);
|
||||
|
@@ -47,6 +47,7 @@ namespace Catch {
|
||||
unsigned int benchmarkSamples = 100;
|
||||
double benchmarkConfidenceInterval = 0.95;
|
||||
unsigned int benchmarkResamples = 100000;
|
||||
std::chrono::milliseconds::rep benchmarkWarmupTime = 100;
|
||||
|
||||
Verbosity verbosity = Verbosity::Normal;
|
||||
WarnAbout::What warnings = WarnAbout::Nothing;
|
||||
@@ -113,6 +114,7 @@ namespace Catch {
|
||||
int benchmarkSamples() const override;
|
||||
double benchmarkConfidenceInterval() const override;
|
||||
unsigned int benchmarkResamples() const override;
|
||||
std::chrono::milliseconds benchmarkWarmupTime() const override;
|
||||
|
||||
private:
|
||||
|
||||
|
@@ -34,7 +34,7 @@ namespace Catch {
|
||||
};
|
||||
|
||||
struct NoColourImpl : IColourImpl {
|
||||
void use( Colour::Code ) {}
|
||||
void use( Colour::Code ) override {}
|
||||
|
||||
static IColourImpl* instance() {
|
||||
static NoColourImpl s_instance;
|
||||
@@ -167,7 +167,7 @@ namespace {
|
||||
|
||||
bool useColourOnPlatform() {
|
||||
return
|
||||
#ifdef CATCH_PLATFORM_MAC
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
|
||||
!isDebuggerActive() &&
|
||||
#endif
|
||||
#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
|
||||
@@ -208,13 +208,13 @@ namespace Catch {
|
||||
namespace Catch {
|
||||
|
||||
Colour::Colour( Code _colourCode ) { use( _colourCode ); }
|
||||
Colour::Colour( Colour&& rhs ) noexcept {
|
||||
m_moved = rhs.m_moved;
|
||||
rhs.m_moved = true;
|
||||
Colour::Colour( Colour&& other ) noexcept {
|
||||
m_moved = other.m_moved;
|
||||
other.m_moved = true;
|
||||
}
|
||||
Colour& Colour::operator=( Colour&& rhs ) noexcept {
|
||||
m_moved = rhs.m_moved;
|
||||
rhs.m_moved = true;
|
||||
Colour& Colour::operator=( Colour&& other ) noexcept {
|
||||
m_moved = other.m_moved;
|
||||
other.m_moved = true;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace Catch {
|
||||
// However, under some conditions it does happen (see #1626),
|
||||
// and this change is small enough that we can let practicality
|
||||
// triumph over purity in this case.
|
||||
if (impl != NULL) {
|
||||
if (impl != nullptr) {
|
||||
impl->use( _colourCode );
|
||||
}
|
||||
}
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
#include "catch_context.h"
|
||||
#include "catch_common.h"
|
||||
#include "catch_random_number_generator.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -59,4 +60,11 @@ namespace Catch {
|
||||
IContext::~IContext() = default;
|
||||
IMutableContext::~IMutableContext() = default;
|
||||
Context::~Context() = default;
|
||||
|
||||
|
||||
SimplePcg32& rng() {
|
||||
static SimplePcg32 s_rng;
|
||||
return s_rng;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@ namespace Catch {
|
||||
{
|
||||
if( !IMutableContext::currentContext )
|
||||
IMutableContext::createContext();
|
||||
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||
return *IMutableContext::currentContext;
|
||||
}
|
||||
|
||||
@@ -55,6 +56,9 @@ namespace Catch {
|
||||
}
|
||||
|
||||
void cleanUpContext();
|
||||
|
||||
class SimplePcg32;
|
||||
SimplePcg32& rng();
|
||||
}
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
|
||||
|
@@ -7,11 +7,21 @@
|
||||
*/
|
||||
|
||||
#include "catch_debug_console.h"
|
||||
#include "catch_compiler_capabilities.h"
|
||||
#include "catch_stream.h"
|
||||
#include "catch_platform.h"
|
||||
#include "catch_windows_h_proxy.h"
|
||||
|
||||
#ifdef CATCH_PLATFORM_WINDOWS
|
||||
#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
|
||||
#include <android/log.h>
|
||||
|
||||
namespace Catch {
|
||||
void writeToDebugConsole( std::string const& text ) {
|
||||
__android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(CATCH_PLATFORM_WINDOWS)
|
||||
|
||||
namespace Catch {
|
||||
void writeToDebugConsole( std::string const& text ) {
|
||||
|
@@ -12,7 +12,7 @@
|
||||
#include "catch_stream.h"
|
||||
#include "catch_platform.h"
|
||||
|
||||
#ifdef CATCH_PLATFORM_MAC
|
||||
#if defined(CATCH_PLATFORM_MAC) || defined(CATCH_PLATFORM_IPHONE)
|
||||
|
||||
# include <assert.h>
|
||||
# include <stdbool.h>
|
||||
|
@@ -19,6 +19,19 @@ namespace Catch {
|
||||
|
||||
#define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
|
||||
|
||||
#elif defined(CATCH_PLATFORM_IPHONE)
|
||||
|
||||
// use inline assembler
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define CATCH_TRAP() __asm__("int $3")
|
||||
#elif defined(__aarch64__)
|
||||
#define CATCH_TRAP() __asm__(".inst 0xd4200000")
|
||||
#elif defined(__arm__) && !defined(__thumb__)
|
||||
#define CATCH_TRAP() __asm__(".inst 0xe7f001f0")
|
||||
#elif defined(__arm__) && defined(__thumb__)
|
||||
#define CATCH_TRAP() __asm__(".inst 0xde01")
|
||||
#endif
|
||||
|
||||
#elif defined(CATCH_PLATFORM_LINUX)
|
||||
// If we can use inline assembler, do it because this allows us to break
|
||||
// directly at the location of the failing check instead of breaking inside
|
||||
@@ -37,10 +50,12 @@ namespace Catch {
|
||||
#define CATCH_TRAP() DebugBreak()
|
||||
#endif
|
||||
|
||||
#ifdef CATCH_TRAP
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
|
||||
#else
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() []{}()
|
||||
#ifndef CATCH_BREAK_INTO_DEBUGGER
|
||||
#ifdef CATCH_TRAP
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
|
||||
#else
|
||||
#define CATCH_BREAK_INTO_DEBUGGER() []{}()
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
|
||||
|
@@ -9,10 +9,11 @@
|
||||
#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
|
||||
|
||||
#include "catch_session.h"
|
||||
#include "catch_platform.h"
|
||||
|
||||
#ifndef __OBJC__
|
||||
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
|
||||
// Standard C/C++ Win32 Unicode wmain entry point
|
||||
extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
|
||||
#else
|
||||
|
@@ -38,13 +38,13 @@ namespace Catch {
|
||||
(Catch::ReusableStringStream() << __VA_ARGS__).str()
|
||||
|
||||
#define CATCH_INTERNAL_ERROR(...) \
|
||||
Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__));
|
||||
Catch::throw_logic_error(CATCH_MAKE_MSG( CATCH_INTERNAL_LINEINFO << ": Internal Catch2 error: " << __VA_ARGS__))
|
||||
|
||||
#define CATCH_ERROR(...) \
|
||||
Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
Catch::throw_domain_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
|
||||
|
||||
#define CATCH_RUNTIME_ERROR(...) \
|
||||
Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ));
|
||||
Catch::throw_runtime_error(CATCH_MAKE_MSG( __VA_ARGS__ ))
|
||||
|
||||
#define CATCH_ENFORCE( condition, ... ) \
|
||||
do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
|
||||
|
@@ -18,13 +18,25 @@ namespace Catch {
|
||||
|
||||
namespace Detail {
|
||||
|
||||
std::vector<std::string> parseEnums( StringRef enums ) {
|
||||
namespace {
|
||||
// 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 ":"
|
||||
size_t name_start = enumInstance.size();
|
||||
while (name_start > 0 && enumInstance[name_start - 1] != ':') {
|
||||
--name_start;
|
||||
}
|
||||
return enumInstance.substr(name_start, enumInstance.size() - name_start);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<StringRef> parseEnums( StringRef enums ) {
|
||||
auto enumValues = splitStringRef( enums, ',' );
|
||||
std::vector<std::string> parsed;
|
||||
std::vector<StringRef> parsed;
|
||||
parsed.reserve( enumValues.size() );
|
||||
for( auto const& enumValue : enumValues ) {
|
||||
auto identifiers = splitStringRef( enumValue, ':' );
|
||||
parsed.push_back( Catch::trim( identifiers.back() ) );
|
||||
parsed.push_back(trim(extractInstanceName(enumValue)));
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
@@ -36,7 +48,7 @@ namespace Catch {
|
||||
if( valueToName.first == value )
|
||||
return valueToName.second;
|
||||
}
|
||||
return "{** unexpected enum value **}";
|
||||
return "{** unexpected enum value **}"_sr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||
@@ -48,16 +60,14 @@ namespace Catch {
|
||||
assert( valueNames.size() == values.size() );
|
||||
std::size_t i = 0;
|
||||
for( auto value : values )
|
||||
enumInfo->m_values.push_back({ value, valueNames[i++] });
|
||||
enumInfo->m_values.emplace_back(value, valueNames[i++]);
|
||||
|
||||
return enumInfo;
|
||||
}
|
||||
|
||||
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||
auto enumInfo = makeEnumInfo( enumName, allValueNames, values );
|
||||
EnumInfo* raw = enumInfo.get();
|
||||
m_enumInfos.push_back( std::move( enumInfo ) );
|
||||
return *raw;
|
||||
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
|
||||
return *m_enumInfos.back();
|
||||
}
|
||||
|
||||
} // Detail
|
||||
|
@@ -26,7 +26,7 @@ namespace Catch {
|
||||
EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
|
||||
};
|
||||
|
||||
std::vector<std::string> parseEnums( StringRef enums );
|
||||
std::vector<StringRef> parseEnums( StringRef enums );
|
||||
|
||||
} // Detail
|
||||
|
||||
|
@@ -97,7 +97,7 @@ namespace Catch {
|
||||
|
||||
// 32kb for the alternate stack seems to be sufficient. However, this value
|
||||
// is experimentally determined, so that's not guaranteed.
|
||||
constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
|
||||
static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
|
||||
|
||||
static SignalDefs signalDefs[] = {
|
||||
{ SIGINT, "SIGINT - Terminal interrupt signal" },
|
||||
|
@@ -57,7 +57,6 @@ namespace Generators {
|
||||
class SingleValueGenerator final : public IGenerator<T> {
|
||||
T m_value;
|
||||
public:
|
||||
SingleValueGenerator(T const& value) : m_value( value ) {}
|
||||
SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
|
||||
|
||||
T const& get() const override {
|
||||
@@ -70,6 +69,9 @@ namespace Generators {
|
||||
|
||||
template<typename T>
|
||||
class FixedValuesGenerator final : public IGenerator<T> {
|
||||
static_assert(!std::is_same<T, bool>::value,
|
||||
"FixedValuesGenerator does not support bools because of std::vector<bool>"
|
||||
"specialization, use SingleValue Generator instead.");
|
||||
std::vector<T> m_values;
|
||||
size_t m_idx = 0;
|
||||
public:
|
||||
@@ -117,21 +119,21 @@ namespace Generators {
|
||||
m_generators.emplace_back(std::move(generator));
|
||||
}
|
||||
void populate(T&& val) {
|
||||
m_generators.emplace_back(value(std::move(val)));
|
||||
m_generators.emplace_back(value(std::forward<T>(val)));
|
||||
}
|
||||
template<typename U>
|
||||
void populate(U&& val) {
|
||||
populate(T(std::move(val)));
|
||||
populate(T(std::forward<U>(val)));
|
||||
}
|
||||
template<typename U, typename... Gs>
|
||||
void populate(U&& valueOrGenerator, Gs... moreGenerators) {
|
||||
void populate(U&& valueOrGenerator, Gs &&... moreGenerators) {
|
||||
populate(std::forward<U>(valueOrGenerator));
|
||||
populate(std::forward<Gs>(moreGenerators)...);
|
||||
}
|
||||
|
||||
public:
|
||||
template <typename... Gs>
|
||||
Generators(Gs... moreGenerators) {
|
||||
Generators(Gs &&... moreGenerators) {
|
||||
m_generators.reserve(sizeof...(Gs));
|
||||
populate(std::forward<Gs>(moreGenerators)...);
|
||||
}
|
||||
@@ -163,7 +165,7 @@ namespace Generators {
|
||||
struct as {};
|
||||
|
||||
template<typename T, typename... Gs>
|
||||
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
|
||||
auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
|
||||
return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
|
||||
}
|
||||
template<typename T>
|
||||
@@ -171,11 +173,11 @@ namespace Generators {
|
||||
return Generators<T>(std::move(generator));
|
||||
}
|
||||
template<typename T, typename... Gs>
|
||||
auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
|
||||
auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<T> {
|
||||
return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
|
||||
}
|
||||
template<typename T, typename U, typename... Gs>
|
||||
auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> {
|
||||
auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
|
||||
return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
|
||||
}
|
||||
|
||||
@@ -201,10 +203,10 @@ namespace Generators {
|
||||
} // namespace Catch
|
||||
|
||||
#define GENERATE( ... ) \
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
|
||||
#define GENERATE_COPY( ... ) \
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
|
||||
#define GENERATE_REF( ... ) \
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
|
||||
Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
|
||||
|
@@ -8,6 +8,7 @@
|
||||
#define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
||||
|
||||
#include "catch_generators.hpp"
|
||||
#include "catch_meta.hpp"
|
||||
|
||||
namespace Catch {
|
||||
namespace Generators {
|
||||
@@ -91,6 +92,9 @@ namespace Generators {
|
||||
|
||||
template <typename T>
|
||||
class RepeatGenerator : public IGenerator<T> {
|
||||
static_assert(!std::is_same<T, bool>::value,
|
||||
"RepeatGenerator currently does not support bools"
|
||||
"because of std::vector<bool> specialization");
|
||||
GeneratorWrapper<T> m_generator;
|
||||
mutable std::vector<T> m_returned;
|
||||
size_t m_target_repeats;
|
||||
@@ -169,18 +173,7 @@ namespace Generators {
|
||||
}
|
||||
};
|
||||
|
||||
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
|
||||
// std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
|
||||
// replaced with std::invoke_result here. Also *_t format is preferred over
|
||||
// typename *::type format.
|
||||
template <typename Func, typename U>
|
||||
using MapFunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
|
||||
#else
|
||||
template <typename Func, typename U>
|
||||
using MapFunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
|
||||
#endif
|
||||
|
||||
template <typename Func, typename U, typename T = MapFunctionReturnType<Func, U>>
|
||||
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
|
||||
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
|
||||
return GeneratorWrapper<T>(
|
||||
pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
|
||||
@@ -205,12 +198,14 @@ namespace Generators {
|
||||
m_chunk_size(size), m_generator(std::move(generator))
|
||||
{
|
||||
m_chunk.reserve(m_chunk_size);
|
||||
m_chunk.push_back(m_generator.get());
|
||||
for (size_t i = 1; i < m_chunk_size; ++i) {
|
||||
if (!m_generator.next()) {
|
||||
Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
|
||||
}
|
||||
if (m_chunk_size != 0) {
|
||||
m_chunk.push_back(m_generator.get());
|
||||
for (size_t i = 1; i < m_chunk_size; ++i) {
|
||||
if (!m_generator.next()) {
|
||||
Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
|
||||
}
|
||||
m_chunk.push_back(m_generator.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<T> const& get() const override {
|
||||
|
@@ -10,6 +10,7 @@
|
||||
#include "catch_context.h"
|
||||
#include "catch_generators.hpp"
|
||||
#include "catch_interfaces_config.h"
|
||||
#include "catch_random_number_generator.h"
|
||||
|
||||
#include <random>
|
||||
|
||||
@@ -18,14 +19,13 @@ namespace Generators {
|
||||
|
||||
template <typename Float>
|
||||
class RandomFloatingGenerator final : public IGenerator<Float> {
|
||||
// FIXME: What is the right seed?
|
||||
std::minstd_rand m_rand;
|
||||
Catch::SimplePcg32& m_rng;
|
||||
std::uniform_real_distribution<Float> m_dist;
|
||||
Float m_current_number;
|
||||
public:
|
||||
|
||||
RandomFloatingGenerator(Float a, Float b):
|
||||
m_rand(getCurrentContext().getConfig()->rngSeed()),
|
||||
m_rng(rng()),
|
||||
m_dist(a, b) {
|
||||
static_cast<void>(next());
|
||||
}
|
||||
@@ -34,20 +34,20 @@ public:
|
||||
return m_current_number;
|
||||
}
|
||||
bool next() override {
|
||||
m_current_number = m_dist(m_rand);
|
||||
m_current_number = m_dist(m_rng);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Integer>
|
||||
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
||||
std::minstd_rand m_rand;
|
||||
Catch::SimplePcg32& m_rng;
|
||||
std::uniform_int_distribution<Integer> m_dist;
|
||||
Integer m_current_number;
|
||||
public:
|
||||
|
||||
RandomIntegerGenerator(Integer a, Integer b):
|
||||
m_rand(getCurrentContext().getConfig()->rngSeed()),
|
||||
m_rng(rng()),
|
||||
m_dist(a, b) {
|
||||
static_cast<void>(next());
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
return m_current_number;
|
||||
}
|
||||
bool next() override {
|
||||
m_current_number = m_dist(m_rand);
|
||||
m_current_number = m_dist(m_rng);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -117,7 +117,7 @@ public:
|
||||
|
||||
template <typename T>
|
||||
GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
|
||||
static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
|
||||
static_assert(std::is_arithmetic<T>::value && !std::is_same<T, bool>::value, "Type must be numeric");
|
||||
return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
|
||||
}
|
||||
|
||||
@@ -128,6 +128,46 @@ GeneratorWrapper<T> range(T const& start, T const& end) {
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
class IteratorGenerator final : public IGenerator<T> {
|
||||
static_assert(!std::is_same<T, bool>::value,
|
||||
"IteratorGenerator currently does not support bools"
|
||||
"because of std::vector<bool> specialization");
|
||||
|
||||
std::vector<T> m_elems;
|
||||
size_t m_current = 0;
|
||||
public:
|
||||
template <typename InputIterator, typename InputSentinel>
|
||||
IteratorGenerator(InputIterator first, InputSentinel last):m_elems(first, last) {
|
||||
if (m_elems.empty()) {
|
||||
Catch::throw_exception(GeneratorException("IteratorGenerator received no valid values"));
|
||||
}
|
||||
}
|
||||
|
||||
T const& get() const override {
|
||||
return m_elems[m_current];
|
||||
}
|
||||
|
||||
bool next() override {
|
||||
++m_current;
|
||||
return m_current != m_elems.size();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename InputIterator,
|
||||
typename InputSentinel,
|
||||
typename ResultType = typename std::iterator_traits<InputIterator>::value_type>
|
||||
GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
|
||||
return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(from, to));
|
||||
}
|
||||
|
||||
template <typename Container,
|
||||
typename ResultType = typename Container::value_type>
|
||||
GeneratorWrapper<ResultType> from_range(Container const& cnt) {
|
||||
return GeneratorWrapper<ResultType>(pf::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end()));
|
||||
}
|
||||
|
||||
|
||||
} // namespace Generators
|
||||
} // namespace Catch
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "internal/catch_interfaces_config.h"
|
||||
#include "catch_interfaces_config.h"
|
||||
|
||||
namespace Catch {
|
||||
IConfig::~IConfig() = default;
|
||||
|
@@ -11,6 +11,7 @@
|
||||
#include "catch_common.h"
|
||||
#include "catch_option.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -81,6 +82,7 @@ namespace Catch {
|
||||
virtual int benchmarkSamples() const = 0;
|
||||
virtual double benchmarkConfidenceInterval() const = 0;
|
||||
virtual unsigned int benchmarkResamples() const = 0;
|
||||
virtual std::chrono::milliseconds benchmarkWarmupTime() const = 0;
|
||||
};
|
||||
|
||||
using IConfigPtr = std::shared_ptr<IConfig const>;
|
||||
|
@@ -17,7 +17,7 @@ namespace Catch {
|
||||
namespace Detail {
|
||||
struct EnumInfo {
|
||||
StringRef m_name;
|
||||
std::vector<std::pair<int, std::string>> m_values;
|
||||
std::vector<std::pair<int, StringRef>> m_values;
|
||||
|
||||
~EnumInfo();
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace Catch {
|
||||
|
||||
template<typename E>
|
||||
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
|
||||
static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
|
||||
std::vector<int> intValues;
|
||||
intValues.reserve( values.size() );
|
||||
for( auto enumValue : values )
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "internal/catch_interfaces_exception.h"
|
||||
#include "catch_interfaces_exception.h"
|
||||
|
||||
namespace Catch {
|
||||
IExceptionTranslator::~IExceptionTranslator() = default;
|
||||
|
@@ -73,9 +73,10 @@ namespace Catch {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
|
||||
static std::string translatorName( signature ); \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||
static std::string translatorName( signature )
|
||||
|
||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "internal/catch_interfaces_registry_hub.h"
|
||||
#include "catch_interfaces_registry_hub.h"
|
||||
|
||||
namespace Catch {
|
||||
IRegistryHub::~IRegistryHub() = default;
|
||||
|
@@ -214,6 +214,8 @@ namespace Catch {
|
||||
|
||||
virtual void noMatchingTestCases( std::string const& spec ) = 0;
|
||||
|
||||
virtual void reportInvalidArguments(std::string const&) {}
|
||||
|
||||
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
|
||||
virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "internal/catch_interfaces_runner.h"
|
||||
#include "catch_interfaces_runner.h"
|
||||
|
||||
namespace Catch {
|
||||
IRunner::~IRunner() = default;
|
||||
|
@@ -1,4 +1,4 @@
|
||||
#include "internal/catch_interfaces_testcase.h"
|
||||
#include "catch_interfaces_testcase.h"
|
||||
|
||||
namespace Catch {
|
||||
ITestInvoker::~ITestInvoker() = default;
|
||||
|
@@ -28,6 +28,7 @@ namespace Catch {
|
||||
virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
|
||||
};
|
||||
|
||||
bool isThrowSafe( TestCase const& testCase, IConfig const& config );
|
||||
bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
|
||||
std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
|
||||
|
@@ -28,7 +28,7 @@
|
||||
namespace Catch {
|
||||
|
||||
std::size_t listTests( Config const& config ) {
|
||||
TestSpec testSpec = config.testSpec();
|
||||
TestSpec const& testSpec = config.testSpec();
|
||||
if( config.hasTestFilters() )
|
||||
Catch::cout() << "Matching test cases:\n";
|
||||
else {
|
||||
@@ -62,7 +62,7 @@ namespace Catch {
|
||||
}
|
||||
|
||||
std::size_t listTestsNamesOnly( Config const& config ) {
|
||||
TestSpec testSpec = config.testSpec();
|
||||
TestSpec const& testSpec = config.testSpec();
|
||||
std::size_t matchedTests = 0;
|
||||
std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
|
||||
for( auto const& testCaseInfo : matchedTestCases ) {
|
||||
@@ -84,14 +84,23 @@ namespace Catch {
|
||||
}
|
||||
|
||||
std::string TagInfo::all() const {
|
||||
std::string out;
|
||||
for( auto const& spelling : spellings )
|
||||
out += "[" + spelling + "]";
|
||||
size_t size = 0;
|
||||
for (auto const& spelling : spellings) {
|
||||
// Add 2 for the brackes
|
||||
size += spelling.size() + 2;
|
||||
}
|
||||
|
||||
std::string out; out.reserve(size);
|
||||
for (auto const& spelling : spellings) {
|
||||
out += '[';
|
||||
out += spelling;
|
||||
out += ']';
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::size_t listTags( Config const& config ) {
|
||||
TestSpec testSpec = config.testSpec();
|
||||
TestSpec const& testSpec = config.testSpec();
|
||||
if( config.hasTestFilters() )
|
||||
Catch::cout() << "Tags for matching test cases:\n";
|
||||
else {
|
||||
|
@@ -44,6 +44,15 @@ namespace Matchers {
|
||||
virtual bool match( ObjectT const& arg ) const = 0;
|
||||
};
|
||||
|
||||
#if defined(__OBJC__)
|
||||
// Hack to fix Catch GH issue #1661. Could use id for generic Object support.
|
||||
// use of const for Object pointers is very uncommon and under ARC it causes some kind of signature mismatch that breaks compilation
|
||||
template<>
|
||||
struct MatcherMethod<NSString*> {
|
||||
virtual bool match( NSString* arg ) const = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
@@ -82,9 +91,10 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
|
||||
m_matchers.push_back( &other );
|
||||
return *this;
|
||||
MatchAllOf<ArgT> operator && ( MatcherBase<ArgT> const& other ) {
|
||||
auto copy(*this);
|
||||
copy.m_matchers.push_back( &other );
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
||||
@@ -115,9 +125,10 @@ namespace Matchers {
|
||||
return description;
|
||||
}
|
||||
|
||||
MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
|
||||
m_matchers.push_back( &other );
|
||||
return *this;
|
||||
MatchAnyOf<ArgT> operator || ( MatcherBase<ArgT> const& other ) {
|
||||
auto copy(*this);
|
||||
copy.m_matchers.push_back( &other );
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::vector<MatcherBase<ArgT> const*> m_matchers;
|
||||
|
30
include/internal/catch_matchers_exception.cpp
Normal file
30
include/internal/catch_matchers_exception.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Created by Martin Hořeňovský on 13/10/2019.
|
||||
*
|
||||
* 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)
|
||||
*/
|
||||
|
||||
#include "catch_matchers_exception.hpp"
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
namespace Exception {
|
||||
|
||||
bool ExceptionMessageMatcher::match(std::exception const& ex) const {
|
||||
return ex.what() == m_message;
|
||||
}
|
||||
|
||||
std::string ExceptionMessageMatcher::describe() const {
|
||||
return "exception message matches \"" + m_message + "\"";
|
||||
}
|
||||
|
||||
}
|
||||
Exception::ExceptionMessageMatcher Message(std::string const& message) {
|
||||
return Exception::ExceptionMessageMatcher(message);
|
||||
}
|
||||
|
||||
// namespace Exception
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
36
include/internal/catch_matchers_exception.hpp
Normal file
36
include/internal/catch_matchers_exception.hpp
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Created by Martin Hořeňovský on 13/10/2019.
|
||||
*
|
||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
#ifndef TWOBLUECUBES_CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
||||
|
||||
#include "catch_matchers.h"
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
namespace Exception {
|
||||
|
||||
class ExceptionMessageMatcher : public MatcherBase<std::exception> {
|
||||
std::string m_message;
|
||||
public:
|
||||
|
||||
ExceptionMessageMatcher(std::string const& message):
|
||||
m_message(message)
|
||||
{}
|
||||
|
||||
bool match(std::exception const& ex) const override;
|
||||
|
||||
std::string describe() const override;
|
||||
};
|
||||
|
||||
} // namespace Exception
|
||||
|
||||
Exception::ExceptionMessageMatcher Message(std::string const& message);
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_MATCHERS_EXCEPTION_HPP_INCLUDED
|
@@ -11,86 +11,102 @@
|
||||
#include "catch_to_string.hpp"
|
||||
#include "catch_tostring.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
namespace Floating {
|
||||
enum class FloatingPointKind : uint8_t {
|
||||
Float,
|
||||
Double
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace Catch {
|
||||
namespace {
|
||||
|
||||
template <typename T>
|
||||
struct Converter;
|
||||
|
||||
template <>
|
||||
struct Converter<float> {
|
||||
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
|
||||
Converter(float f) {
|
||||
int32_t convert(float f) {
|
||||
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
|
||||
int32_t i;
|
||||
std::memcpy(&i, &f, sizeof(f));
|
||||
return i;
|
||||
}
|
||||
int32_t i;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Converter<double> {
|
||||
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
|
||||
Converter(double d) {
|
||||
int64_t convert(double d) {
|
||||
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
|
||||
int64_t i;
|
||||
std::memcpy(&i, &d, sizeof(d));
|
||||
return i;
|
||||
}
|
||||
int64_t i;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
auto convert(T t) -> Converter<T> {
|
||||
return Converter<T>(t);
|
||||
}
|
||||
template <typename FP>
|
||||
bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
|
||||
// Comparison with NaN should always be false.
|
||||
// This way we can rule it out before getting into the ugly details
|
||||
if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lc = convert(lhs);
|
||||
auto rc = convert(rhs);
|
||||
|
||||
if ((lc < 0) != (rc < 0)) {
|
||||
// Potentially we can have +0 and -0
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
auto ulpDiff = std::abs(lc - rc);
|
||||
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
|
||||
|
||||
float nextafter(float x, float y) {
|
||||
return ::nextafterf(x, y);
|
||||
}
|
||||
|
||||
double nextafter(double x, double y) {
|
||||
return ::nextafter(x, y);
|
||||
}
|
||||
|
||||
#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
|
||||
|
||||
template <typename FP>
|
||||
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
|
||||
// Comparison with NaN should always be false.
|
||||
// This way we can rule it out before getting into the ugly details
|
||||
if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto lc = convert(lhs);
|
||||
auto rc = convert(rhs);
|
||||
|
||||
if ((lc.i < 0) != (rc.i < 0)) {
|
||||
// Potentially we can have +0 and -0
|
||||
return lhs == rhs;
|
||||
}
|
||||
|
||||
auto ulpDiff = std::abs(lc.i - rc.i);
|
||||
return ulpDiff <= maxUlpDiff;
|
||||
}
|
||||
|
||||
template <typename FP>
|
||||
FP step(FP start, FP direction, int steps) {
|
||||
for (int i = 0; i < steps; ++i) {
|
||||
FP step(FP start, FP direction, uint64_t steps) {
|
||||
for (uint64_t i = 0; i < steps; ++i) {
|
||||
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
|
||||
start = Catch::nextafter(start, direction);
|
||||
#else
|
||||
start = std::nextafter(start, direction);
|
||||
#endif
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
||||
// But without the subtraction to allow for INFINITY in comparison
|
||||
bool marginComparison(double lhs, double rhs, double margin) {
|
||||
return (lhs + margin >= rhs) && (rhs + margin >= lhs);
|
||||
}
|
||||
|
||||
template <typename FloatingPoint>
|
||||
void write(std::ostream& out, FloatingPoint num) {
|
||||
out << std::scientific
|
||||
<< std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)
|
||||
<< num;
|
||||
}
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
namespace Floating {
|
||||
|
||||
enum class FloatingPointKind : uint8_t {
|
||||
Float,
|
||||
Double
|
||||
};
|
||||
|
||||
|
||||
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
|
||||
:m_target{ target }, m_margin{ margin } {
|
||||
CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
|
||||
@@ -108,10 +124,11 @@ namespace Floating {
|
||||
}
|
||||
|
||||
|
||||
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
|
||||
WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
|
||||
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
|
||||
CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
|
||||
<< " ULPs have to be non-negative.");
|
||||
CATCH_ENFORCE(m_type == FloatingPointKind::Double
|
||||
|| m_ulps < (std::numeric_limits<uint32_t>::max)(),
|
||||
"Provided ULP is impossibly large for a float comparison.");
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
@@ -138,38 +155,59 @@ namespace Floating {
|
||||
std::string WithinUlpsMatcher::describe() const {
|
||||
std::stringstream ret;
|
||||
|
||||
ret << "is within " << m_ulps << " ULPs of " << ::Catch::Detail::stringify(m_target);
|
||||
ret << "is within " << m_ulps << " ULPs of ";
|
||||
|
||||
if (m_type == FloatingPointKind::Float) {
|
||||
write(ret, static_cast<float>(m_target));
|
||||
ret << 'f';
|
||||
} else {
|
||||
write(ret, m_target);
|
||||
}
|
||||
|
||||
ret << " ([";
|
||||
ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10);
|
||||
if (m_type == FloatingPointKind::Double) {
|
||||
ret << step(m_target, static_cast<double>(-INFINITY), m_ulps)
|
||||
<< ", "
|
||||
<< step(m_target, static_cast<double>(INFINITY), m_ulps);
|
||||
write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
|
||||
ret << ", ";
|
||||
write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
|
||||
} else {
|
||||
ret << step<float>(static_cast<float>(m_target), -INFINITY, m_ulps)
|
||||
<< ", "
|
||||
<< step<float>(static_cast<float>(m_target), INFINITY, m_ulps);
|
||||
// We have to cast INFINITY to float because of MinGW, see #1782
|
||||
write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));
|
||||
ret << ", ";
|
||||
write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
|
||||
}
|
||||
ret << "])";
|
||||
|
||||
return ret.str();
|
||||
//return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
|
||||
}
|
||||
|
||||
WithinRelMatcher::WithinRelMatcher(double target, double epsilon):
|
||||
m_target(target),
|
||||
m_epsilon(epsilon){
|
||||
CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon < 0 does not make sense.");
|
||||
CATCH_ENFORCE(m_epsilon < 1., "Relative comparison with epsilon >= 1 does not make sense.");
|
||||
}
|
||||
|
||||
bool WithinRelMatcher::match(double const& matchee) const {
|
||||
const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));
|
||||
return marginComparison(matchee, m_target,
|
||||
std::isinf(relMargin)? 0 : relMargin);
|
||||
}
|
||||
|
||||
std::string WithinRelMatcher::describe() const {
|
||||
Catch::ReusableStringStream sstr;
|
||||
sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";
|
||||
return sstr.str();
|
||||
}
|
||||
|
||||
}// namespace Floating
|
||||
|
||||
|
||||
|
||||
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
|
||||
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
|
||||
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
|
||||
}
|
||||
|
||||
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
|
||||
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
|
||||
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
|
||||
}
|
||||
|
||||
@@ -177,6 +215,23 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
|
||||
return Floating::WithinAbsMatcher(target, margin);
|
||||
}
|
||||
|
||||
Floating::WithinRelMatcher WithinRel(double target, double eps) {
|
||||
return Floating::WithinRelMatcher(target, eps);
|
||||
}
|
||||
|
||||
Floating::WithinRelMatcher WithinRel(double target) {
|
||||
return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
|
||||
}
|
||||
|
||||
Floating::WithinRelMatcher WithinRel(float target, float eps) {
|
||||
return Floating::WithinRelMatcher(target, eps);
|
||||
}
|
||||
|
||||
Floating::WithinRelMatcher WithinRel(float target) {
|
||||
return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
||||
|
@@ -9,9 +9,6 @@
|
||||
|
||||
#include "catch_matchers.h"
|
||||
|
||||
#include <type_traits>
|
||||
#include <cmath>
|
||||
|
||||
namespace Catch {
|
||||
namespace Matchers {
|
||||
|
||||
@@ -29,23 +26,43 @@ namespace Matchers {
|
||||
};
|
||||
|
||||
struct WithinUlpsMatcher : MatcherBase<double> {
|
||||
WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
|
||||
WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
|
||||
bool match(double const& matchee) const override;
|
||||
std::string describe() const override;
|
||||
private:
|
||||
double m_target;
|
||||
int m_ulps;
|
||||
uint64_t m_ulps;
|
||||
FloatingPointKind m_type;
|
||||
};
|
||||
|
||||
// Given IEEE-754 format for floats and doubles, we can assume
|
||||
// that float -> double promotion is lossless. Given this, we can
|
||||
// assume that if we do the standard relative comparison of
|
||||
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
|
||||
// the same result if we do this for floats, as if we do this for
|
||||
// doubles that were promoted from floats.
|
||||
struct WithinRelMatcher : MatcherBase<double> {
|
||||
WithinRelMatcher(double target, double epsilon);
|
||||
bool match(double const& matchee) const override;
|
||||
std::string describe() const override;
|
||||
private:
|
||||
double m_target;
|
||||
double m_epsilon;
|
||||
};
|
||||
|
||||
} // namespace Floating
|
||||
|
||||
// The following functions create the actual matcher objects.
|
||||
// This allows the types to be inferred
|
||||
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
|
||||
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
|
||||
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
|
||||
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
|
||||
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
|
||||
Floating::WithinRelMatcher WithinRel(double target, double eps);
|
||||
// defaults epsilon to 100*numeric_limits<double>::epsilon()
|
||||
Floating::WithinRelMatcher WithinRel(double target);
|
||||
Floating::WithinRelMatcher WithinRel(float target, float eps);
|
||||
// defaults epsilon to 100*numeric_limits<float>::epsilon()
|
||||
Floating::WithinRelMatcher WithinRel(float target);
|
||||
|
||||
} // namespace Matchers
|
||||
} // namespace Catch
|
||||
|
@@ -111,17 +111,17 @@ namespace Catch {
|
||||
pos = skipq(pos, c);
|
||||
break;
|
||||
case ',':
|
||||
if (start != pos && openings.size() == 0) {
|
||||
if (start != pos && openings.empty()) {
|
||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||
m_messages.back().message = trimmed(start, pos);
|
||||
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
|
||||
m_messages.back().message += " := ";
|
||||
start = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(openings.size() == 0 && "Mismatched openings");
|
||||
assert(openings.empty() && "Mismatched openings");
|
||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
||||
m_messages.back().message = trimmed(start, names.size() - 1);
|
||||
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
|
||||
m_messages.back().message += " := ";
|
||||
}
|
||||
Capturer::~Capturer() {
|
||||
|
@@ -12,22 +12,34 @@
|
||||
#include <type_traits>
|
||||
|
||||
namespace Catch {
|
||||
template<typename T>
|
||||
struct always_false : std::false_type {};
|
||||
template<typename T>
|
||||
struct always_false : std::false_type {};
|
||||
|
||||
template <typename> struct true_given : std::true_type {};
|
||||
struct is_callable_tester {
|
||||
template <typename Fun, typename... Args>
|
||||
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
|
||||
template <typename...>
|
||||
std::false_type static test(...);
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct is_callable;
|
||||
|
||||
template <typename> struct true_given : std::true_type {};
|
||||
struct is_callable_tester {
|
||||
template <typename Fun, typename... Args>
|
||||
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
|
||||
template <typename...>
|
||||
std::false_type static test(...);
|
||||
};
|
||||
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
|
||||
|
||||
template <typename T>
|
||||
struct is_callable;
|
||||
|
||||
template <typename Fun, typename... Args>
|
||||
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
|
||||
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
|
||||
// std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
|
||||
// replaced with std::invoke_result here. Also *_t format is preferred over
|
||||
// typename *::type format.
|
||||
template <typename Func, typename U>
|
||||
using FunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
|
||||
#else
|
||||
template <typename Func, typename U>
|
||||
using FunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
|
||||
#endif
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
|
@@ -116,7 +116,7 @@ namespace Catch {
|
||||
arcSafeRelease( m_substr );
|
||||
}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Catch {
|
||||
struct Equals : StringHolder {
|
||||
Equals( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str isEqualToString:m_substr];
|
||||
}
|
||||
@@ -139,7 +139,7 @@ namespace Catch {
|
||||
struct Contains : StringHolder {
|
||||
Contains( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location != NSNotFound;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ namespace Catch {
|
||||
struct StartsWith : StringHolder {
|
||||
StartsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == 0;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ namespace Catch {
|
||||
struct EndsWith : StringHolder {
|
||||
EndsWith( NSString* substr ) : StringHolder( substr ){}
|
||||
|
||||
bool match( NSString* const& str ) const override {
|
||||
bool match( NSString* str ) const override {
|
||||
return (str != nil || m_substr == nil ) &&
|
||||
[str rangeOfString:m_substr].location == [str length] - [m_substr length];
|
||||
}
|
||||
|
@@ -102,35 +102,49 @@
|
||||
template<typename...> struct TypeList {};\
|
||||
template<typename...Ts>\
|
||||
constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
|
||||
template<template<typename...> class...> struct TemplateTypeList{};\
|
||||
template<template<typename...> class...Cs>\
|
||||
constexpr auto get_wrapper() noexcept -> TemplateTypeList<Cs...> { return {}; }\
|
||||
template<typename...>\
|
||||
struct append;\
|
||||
template<typename...>\
|
||||
struct rewrap;\
|
||||
template<template<typename...> class, typename...>\
|
||||
struct create;\
|
||||
template<template<typename...> class, typename>\
|
||||
struct convert;\
|
||||
\
|
||||
template<template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2> \
|
||||
constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\
|
||||
template<typename T> \
|
||||
struct append<T> { using type = T; };\
|
||||
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
||||
constexpr auto append(L1<E1...>, L2<E2...>, Rest...) noexcept -> decltype(append(L1<E1...,E2...>{}, Rest{}...)) { return {}; }\
|
||||
struct append<L1<E1...>, L2<E2...>, Rest...> { using type = typename append<L1<E1...,E2...>, Rest...>::type; };\
|
||||
template< template<typename...> class L1, typename...E1, typename...Rest>\
|
||||
constexpr auto append(L1<E1...>, TypeList<mpl_::na>, Rest...) noexcept -> L1<E1...> { return {}; }\
|
||||
struct append<L1<E1...>, TypeList<mpl_::na>, Rest...> { using type = L1<E1...>; };\
|
||||
\
|
||||
template< template<typename...> class Container, template<typename...> class List, typename...elems>\
|
||||
constexpr auto rewrap(List<elems...>) noexcept -> TypeList<Container<elems...>> { return {}; }\
|
||||
struct rewrap<TemplateTypeList<Container>, List<elems...>> { using type = TypeList<Container<elems...>>; };\
|
||||
template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
|
||||
constexpr auto rewrap(List<Elems...>,Elements...) noexcept -> decltype(append(TypeList<Container<Elems...>>{}, rewrap<Container>(Elements{}...))) { return {}; }\
|
||||
struct rewrap<TemplateTypeList<Container>, List<Elems...>, Elements...> { using type = typename append<TypeList<Container<Elems...>>, typename rewrap<TemplateTypeList<Container>, Elements...>::type>::type; };\
|
||||
\
|
||||
template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
|
||||
constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }\
|
||||
struct create<Final, TemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<TemplateTypeList<Containers>, Types...>::type...>::type; };\
|
||||
template<template <typename...> class Final, template <typename...> class List, typename...Ts>\
|
||||
constexpr auto convert(List<Ts...>) noexcept -> decltype(append(Final<>{},TypeList<Ts>{}...)) { return {}; }
|
||||
struct convert<Final, List<Ts...>> { using type = typename append<Final<>,TypeList<Ts>...>::type; };
|
||||
|
||||
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...> struct NttpTemplateTypeList{};\
|
||||
template<template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Cs>\
|
||||
constexpr auto get_wrapper() noexcept -> NttpTemplateTypeList<Cs...> { return {}; } \
|
||||
\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||
constexpr auto rewrap(List<__VA_ARGS__>) noexcept -> TypeList<Container<__VA_ARGS__>> { return {}; }\
|
||||
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>> { using type = TypeList<Container<__VA_ARGS__>>; };\
|
||||
template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
|
||||
constexpr auto rewrap(List<__VA_ARGS__>,Elements...elems) noexcept -> decltype(append(TypeList<Container<__VA_ARGS__>>{}, rewrap<Container>(elems...))) { return {}; }\
|
||||
struct rewrap<NttpTemplateTypeList<Container>, List<__VA_ARGS__>, Elements...> { using type = typename append<TypeList<Container<__VA_ARGS__>>, typename rewrap<NttpTemplateTypeList<Container>, Elements...>::type>::type; };\
|
||||
template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
|
||||
constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }
|
||||
struct create<Final, NttpTemplateTypeList<Containers...>, TypeList<Types...>> { using type = typename append<Final<>, typename rewrap<NttpTemplateTypeList<Containers>, Types...>::type...>::type; };
|
||||
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
|
||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
|
||||
|
@@ -7,23 +7,67 @@
|
||||
|
||||
#include "catch_random_number_generator.h"
|
||||
#include "catch_context.h"
|
||||
#include "catch_run_context.h"
|
||||
#include "catch_interfaces_config.h"
|
||||
|
||||
namespace Catch {
|
||||
|
||||
std::mt19937& rng() {
|
||||
static std::mt19937 s_rng;
|
||||
return s_rng;
|
||||
namespace {
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4146) // we negate uint32 during the rotate
|
||||
#endif
|
||||
// Safe rotr implementation thanks to John Regehr
|
||||
uint32_t rotate_right(uint32_t val, uint32_t count) {
|
||||
const uint32_t mask = 31;
|
||||
count &= mask;
|
||||
return (val >> count) | (val << (-count & mask));
|
||||
}
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
SimplePcg32::SimplePcg32(result_type seed_) {
|
||||
seed(seed_);
|
||||
}
|
||||
|
||||
void seedRng( IConfig const& config ) {
|
||||
if( config.rngSeed() != 0 ) {
|
||||
std::srand( config.rngSeed() );
|
||||
rng().seed( config.rngSeed() );
|
||||
|
||||
void SimplePcg32::seed(result_type seed_) {
|
||||
m_state = 0;
|
||||
(*this)();
|
||||
m_state += seed_;
|
||||
(*this)();
|
||||
}
|
||||
|
||||
void SimplePcg32::discard(uint64_t skip) {
|
||||
// We could implement this to run in O(log n) steps, but this
|
||||
// should suffice for our use case.
|
||||
for (uint64_t s = 0; s < skip; ++s) {
|
||||
static_cast<void>((*this)());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int rngSeed() {
|
||||
return getCurrentContext().getConfig()->rngSeed();
|
||||
SimplePcg32::result_type SimplePcg32::operator()() {
|
||||
// prepare the output value
|
||||
const uint32_t xorshifted = static_cast<uint32_t>(((m_state >> 18u) ^ m_state) >> 27u);
|
||||
const auto output = rotate_right(xorshifted, m_state >> 59u);
|
||||
|
||||
// advance state
|
||||
m_state = m_state * 6364136223846793005ULL + s_inc;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
|
||||
return lhs.m_state == rhs.m_state;
|
||||
}
|
||||
|
||||
bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs) {
|
||||
return lhs.m_state != rhs.m_state;
|
||||
}
|
||||
}
|
||||
|
@@ -7,17 +7,52 @@
|
||||
#ifndef TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
|
||||
#include <algorithm>
|
||||
#include <random>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
struct IConfig;
|
||||
// This is a simple implementation of C++11 Uniform Random Number
|
||||
// Generator. It does not provide all operators, because Catch2
|
||||
// does not use it, but it should behave as expected inside stdlib's
|
||||
// distributions.
|
||||
// The implementation is based on the PCG family (http://pcg-random.org)
|
||||
class SimplePcg32 {
|
||||
using state_type = std::uint64_t;
|
||||
public:
|
||||
using result_type = std::uint32_t;
|
||||
static constexpr result_type (min)() {
|
||||
return 0;
|
||||
}
|
||||
static constexpr result_type (max)() {
|
||||
return static_cast<result_type>(-1);
|
||||
}
|
||||
|
||||
std::mt19937& rng();
|
||||
void seedRng( IConfig const& config );
|
||||
unsigned int rngSeed();
|
||||
// Provide some default initial state for the default constructor
|
||||
SimplePcg32():SimplePcg32(0xed743cc4U) {}
|
||||
|
||||
}
|
||||
explicit SimplePcg32(result_type seed_);
|
||||
|
||||
void seed(result_type seed_);
|
||||
void discard(uint64_t skip);
|
||||
|
||||
result_type operator()();
|
||||
|
||||
private:
|
||||
friend bool operator==(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
|
||||
friend bool operator!=(SimplePcg32 const& lhs, SimplePcg32 const& rhs);
|
||||
|
||||
// In theory we also need operator<< and operator>>
|
||||
// In practice we do not use them, so we will skip them for now
|
||||
|
||||
|
||||
std::uint64_t m_state;
|
||||
// This part of the state determines which "stream" of the numbers
|
||||
// is chosen -- we take it as a constant for Catch2, so we only
|
||||
// need to deal with seeding the main state.
|
||||
// Picked by reading 8 bytes from `/dev/random` :-)
|
||||
static const std::uint64_t s_inc = (0x13ed0cc53f939476ULL << 1ULL) | 1ULL;
|
||||
};
|
||||
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||
|
@@ -58,14 +58,16 @@ namespace Catch {
|
||||
#if !defined(CATCH_CONFIG_DISABLE)
|
||||
|
||||
#define CATCH_REGISTER_REPORTER( name, reporterType ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#define CATCH_REGISTER_LISTENER( listenerType ) \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
#else // CATCH_CONFIG_DISABLE
|
||||
|
||||
#define CATCH_REGISTER_REPORTER(name, reporterType)
|
||||
|
@@ -230,7 +230,7 @@ namespace Catch {
|
||||
|
||||
m_unfinishedSections.push_back(endInfo);
|
||||
}
|
||||
|
||||
|
||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||
void RunContext::benchmarkPreparing(std::string const& name) {
|
||||
m_reporter->benchmarkPreparing(name);
|
||||
@@ -279,7 +279,7 @@ namespace Catch {
|
||||
// Don't rebuild the result -- the stringification itself can cause more fatal errors
|
||||
// Instead, fake a result data.
|
||||
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
|
||||
tempResult.message = message;
|
||||
tempResult.message = static_cast<std::string>(message);
|
||||
AssertionResult result(m_lastAssertionInfo, tempResult);
|
||||
|
||||
assertionEnded(result);
|
||||
@@ -442,7 +442,7 @@ namespace Catch {
|
||||
m_lastAssertionInfo = info;
|
||||
|
||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||
data.message = message;
|
||||
data.message = static_cast<std::string>(message);
|
||||
AssertionResult assertionResult{ m_lastAssertionInfo, data };
|
||||
assertionEnded( assertionResult );
|
||||
if( !assertionResult.isOk() )
|
||||
@@ -506,4 +506,16 @@ namespace Catch {
|
||||
else
|
||||
CATCH_INTERNAL_ERROR("No result capture instance");
|
||||
}
|
||||
|
||||
void seedRng(IConfig const& config) {
|
||||
if (config.rngSeed() != 0) {
|
||||
std::srand(config.rngSeed());
|
||||
rng().seed(config.rngSeed());
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int rngSeed() {
|
||||
return getCurrentContext().getConfig()->rngSeed();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -151,6 +151,8 @@ namespace Catch {
|
||||
bool m_includeSuccessfulResults;
|
||||
};
|
||||
|
||||
void seedRng(IConfig const& config);
|
||||
unsigned int rngSeed();
|
||||
} // end namespace Catch
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
|
||||
|
@@ -37,13 +37,15 @@ namespace Catch {
|
||||
} // end namespace Catch
|
||||
|
||||
#define INTERNAL_CATCH_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
|
||||
CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
|
||||
CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
|
||||
|
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <cstdlib>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <iterator>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
@@ -58,46 +60,61 @@ namespace Catch {
|
||||
return ret;
|
||||
}
|
||||
|
||||
class TestGroup {
|
||||
public:
|
||||
explicit TestGroup(std::shared_ptr<Config> const& config)
|
||||
: m_config{config}
|
||||
, m_context{config, makeReporter(config)}
|
||||
{
|
||||
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
||||
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
|
||||
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||
|
||||
Catch::Totals runTests(std::shared_ptr<Config> const& config) {
|
||||
auto reporter = makeReporter(config);
|
||||
|
||||
RunContext context(config, std::move(reporter));
|
||||
|
||||
Totals totals;
|
||||
|
||||
context.testGroupStarting(config->name(), 1, 1);
|
||||
|
||||
TestSpec testSpec = config->testSpec();
|
||||
|
||||
auto const& allTestCases = getAllTestCasesSorted(*config);
|
||||
for (auto const& testCase : allTestCases) {
|
||||
bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) ||
|
||||
(testSpec.hasFilters() && matchTest(testCase, testSpec, *config));
|
||||
|
||||
if (!context.aborting() && matching)
|
||||
totals += context.runTest(testCase);
|
||||
else
|
||||
context.reporter().skipTest(testCase);
|
||||
if (m_matches.empty() && invalidArgs.empty()) {
|
||||
for (auto const& test : allTestCases)
|
||||
if (!test.isHidden())
|
||||
m_tests.emplace(&test);
|
||||
} else {
|
||||
for (auto const& match : m_matches)
|
||||
m_tests.insert(match.tests.begin(), match.tests.end());
|
||||
}
|
||||
}
|
||||
|
||||
if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
|
||||
ReusableStringStream testConfig;
|
||||
|
||||
bool first = true;
|
||||
for (const auto& input : config->getTestsOrTags()) {
|
||||
if (!first) { testConfig << ' '; }
|
||||
first = false;
|
||||
testConfig << input;
|
||||
Totals execute() {
|
||||
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||
Totals totals;
|
||||
m_context.testGroupStarting(m_config->name(), 1, 1);
|
||||
for (auto const& testCase : m_tests) {
|
||||
if (!m_context.aborting())
|
||||
totals += m_context.runTest(*testCase);
|
||||
else
|
||||
m_context.reporter().skipTest(*testCase);
|
||||
}
|
||||
|
||||
context.reporter().noMatchingTestCases(testConfig.str());
|
||||
totals.error = -1;
|
||||
for (auto const& match : m_matches) {
|
||||
if (match.tests.empty()) {
|
||||
m_context.reporter().noMatchingTestCases(match.name);
|
||||
totals.error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!invalidArgs.empty()) {
|
||||
for (auto const& invalidArg: invalidArgs)
|
||||
m_context.reporter().reportInvalidArguments(invalidArg);
|
||||
}
|
||||
|
||||
m_context.testGroupEnded(m_config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
|
||||
context.testGroupEnded(config->name(), totals, 1, 1);
|
||||
return totals;
|
||||
}
|
||||
private:
|
||||
using Tests = std::set<TestCase const*>;
|
||||
|
||||
std::shared_ptr<Config> m_config;
|
||||
RunContext m_context;
|
||||
Tests m_tests;
|
||||
TestSpec::Matches m_matches;
|
||||
};
|
||||
|
||||
void applyFilenamesAsTags(Catch::IConfig const& config) {
|
||||
auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
|
||||
@@ -166,7 +183,7 @@ namespace Catch {
|
||||
}
|
||||
void Session::libIdentify() {
|
||||
Catch::cout()
|
||||
<< std::left << std::setw(16) << "description: " << "A Catch test executable\n"
|
||||
<< std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
|
||||
<< std::left << std::setw(16) << "category: " << "testframework\n"
|
||||
<< std::left << std::setw(16) << "framework: " << "Catch Test\n"
|
||||
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
|
||||
@@ -197,17 +214,17 @@ namespace Catch {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
||||
int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
|
||||
|
||||
char **utf8Argv = new char *[ argc ];
|
||||
|
||||
for ( int i = 0; i < argc; ++i ) {
|
||||
int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
|
||||
int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, nullptr, 0, nullptr, nullptr );
|
||||
|
||||
utf8Argv[ i ] = new char[ bufSize ];
|
||||
|
||||
WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
|
||||
WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, nullptr, nullptr );
|
||||
}
|
||||
|
||||
int returnCode = applyCommandLine( argc, utf8Argv );
|
||||
@@ -274,7 +291,12 @@ namespace Catch {
|
||||
if( Option<std::size_t> listed = list( m_config ) )
|
||||
return static_cast<int>( *listed );
|
||||
|
||||
auto totals = runTests( m_config );
|
||||
TestGroup tests { m_config };
|
||||
auto const totals = tests.execute();
|
||||
|
||||
if( m_config->warnAboutNoTests() && totals.error == -1 )
|
||||
return 2;
|
||||
|
||||
// Note that on unices only the lower 8 bits are usually used, clamping
|
||||
// the return value to 255 prevents false negative when some multiple
|
||||
// of 256 tests has failed
|
||||
|
@@ -26,7 +26,7 @@ namespace Catch {
|
||||
void libIdentify();
|
||||
|
||||
int applyCommandLine( int argc, char const * const * argv );
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
|
||||
#if defined(CATCH_CONFIG_WCHAR) && defined(_WIN32) && defined(UNICODE)
|
||||
int applyCommandLine( int argc, wchar_t const * const * argv );
|
||||
#endif
|
||||
|
||||
|
@@ -9,6 +9,8 @@
|
||||
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
||||
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
||||
|
||||
#include "catch_common.h"
|
||||
|
||||
#include <iosfwd>
|
||||
#include <cstddef>
|
||||
#include <ostream>
|
||||
@@ -28,7 +30,7 @@ namespace Catch {
|
||||
|
||||
auto makeStream( StringRef const &filename ) -> IStream const*;
|
||||
|
||||
class ReusableStringStream {
|
||||
class ReusableStringStream : NonCopyable {
|
||||
std::size_t m_index;
|
||||
std::ostream* m_oss;
|
||||
public:
|
||||
|
@@ -53,6 +53,18 @@ namespace Catch {
|
||||
return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
|
||||
}
|
||||
|
||||
StringRef trim(StringRef ref) {
|
||||
const auto is_ws = [](char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||
};
|
||||
size_t real_begin = 0;
|
||||
while (real_begin < ref.size() && is_ws(ref[real_begin])) { ++real_begin; }
|
||||
size_t real_end = ref.size();
|
||||
while (real_end > real_begin && is_ws(ref[real_end - 1])) { --real_end; }
|
||||
|
||||
return ref.substr(real_begin, real_end - real_begin);
|
||||
}
|
||||
|
||||
bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
|
||||
bool replaced = false;
|
||||
std::size_t i = str.find( replaceThis );
|
||||
|
@@ -22,7 +22,10 @@ namespace Catch {
|
||||
bool contains( std::string const& s, std::string const& infix );
|
||||
void toLowerInPlace( std::string& s );
|
||||
std::string toLower( std::string const& s );
|
||||
//! Returns a new string without whitespace at the start/end
|
||||
std::string trim( std::string const& str );
|
||||
//! Returns a substring of the original ref without whitespace. Beware lifetimes!
|
||||
StringRef trim(StringRef ref);
|
||||
|
||||
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
||||
|
@@ -5,124 +5,46 @@
|
||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
*/
|
||||
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
||||
#endif
|
||||
|
||||
#include "catch_enforce.h"
|
||||
#include "catch_stringref.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <ostream>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
namespace {
|
||||
const uint32_t byte_2_lead = 0xC0;
|
||||
const uint32_t byte_3_lead = 0xE0;
|
||||
const uint32_t byte_4_lead = 0xF0;
|
||||
}
|
||||
|
||||
namespace Catch {
|
||||
StringRef::StringRef( char const* rawChars ) noexcept
|
||||
: StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
|
||||
{}
|
||||
|
||||
StringRef::operator std::string() const {
|
||||
return std::string( m_start, m_size );
|
||||
}
|
||||
|
||||
void StringRef::swap( StringRef& other ) noexcept {
|
||||
std::swap( m_start, other.m_start );
|
||||
std::swap( m_size, other.m_size );
|
||||
std::swap( m_data, other.m_data );
|
||||
}
|
||||
|
||||
auto StringRef::c_str() const -> char const* {
|
||||
if( !isSubstring() )
|
||||
return m_start;
|
||||
|
||||
const_cast<StringRef *>( this )->takeOwnership();
|
||||
return m_data;
|
||||
CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
|
||||
return m_start;
|
||||
}
|
||||
auto StringRef::currentData() const noexcept -> char const* {
|
||||
auto StringRef::data() const noexcept -> char const* {
|
||||
return m_start;
|
||||
}
|
||||
|
||||
auto StringRef::isOwned() const noexcept -> bool {
|
||||
return m_data != nullptr;
|
||||
}
|
||||
auto StringRef::isSubstring() const noexcept -> bool {
|
||||
return m_start[m_size] != '\0';
|
||||
}
|
||||
|
||||
void StringRef::takeOwnership() {
|
||||
if( !isOwned() ) {
|
||||
m_data = new char[m_size+1];
|
||||
memcpy( m_data, m_start, m_size );
|
||||
m_data[m_size] = '\0';
|
||||
}
|
||||
}
|
||||
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
|
||||
if( start < m_size )
|
||||
return StringRef( m_start+start, size );
|
||||
else
|
||||
if (start < m_size) {
|
||||
return StringRef(m_start + start, (std::min)(m_size - start, size));
|
||||
} else {
|
||||
return StringRef();
|
||||
}
|
||||
}
|
||||
auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
|
||||
return
|
||||
size() == other.size() &&
|
||||
(std::strncmp( m_start, other.m_start, size() ) == 0);
|
||||
}
|
||||
auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
|
||||
return !operator==( other );
|
||||
}
|
||||
|
||||
auto StringRef::operator[](size_type index) const noexcept -> char {
|
||||
return m_start[index];
|
||||
}
|
||||
|
||||
auto StringRef::numberOfCharacters() const noexcept -> size_type {
|
||||
size_type noChars = m_size;
|
||||
// Make adjustments for uft encodings
|
||||
for( size_type i=0; i < m_size; ++i ) {
|
||||
char c = m_start[i];
|
||||
if( ( c & byte_2_lead ) == byte_2_lead ) {
|
||||
noChars--;
|
||||
if (( c & byte_3_lead ) == byte_3_lead )
|
||||
noChars--;
|
||||
if( ( c & byte_4_lead ) == byte_4_lead )
|
||||
noChars--;
|
||||
}
|
||||
}
|
||||
return noChars;
|
||||
}
|
||||
|
||||
auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string {
|
||||
std::string str;
|
||||
str.reserve( lhs.size() + rhs.size() );
|
||||
str += lhs;
|
||||
str += rhs;
|
||||
return str;
|
||||
}
|
||||
auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string {
|
||||
return std::string( lhs ) + std::string( rhs );
|
||||
}
|
||||
auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string {
|
||||
return std::string( lhs ) + std::string( rhs );
|
||||
return m_size == other.m_size
|
||||
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
|
||||
}
|
||||
|
||||
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
|
||||
return os.write(str.currentData(), str.size());
|
||||
return os.write(str.data(), str.size());
|
||||
}
|
||||
|
||||
auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
|
||||
lhs.append(rhs.currentData(), rhs.size());
|
||||
lhs.append(rhs.data(), rhs.size());
|
||||
return lhs;
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
#if defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
@@ -10,53 +10,30 @@
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <cassert>
|
||||
|
||||
namespace Catch {
|
||||
|
||||
/// A non-owning string class (similar to the forthcoming std::string_view)
|
||||
/// Note that, because a StringRef may be a substring of another string,
|
||||
/// it may not be null terminated. c_str() must return a null terminated
|
||||
/// string, however, and so the StringRef will internally take ownership
|
||||
/// (taking a copy), if necessary. In theory this ownership is not externally
|
||||
/// visible - but it does mean (substring) StringRefs should not be shared between
|
||||
/// threads.
|
||||
/// it may not be null terminated.
|
||||
class StringRef {
|
||||
public:
|
||||
using size_type = std::size_t;
|
||||
using const_iterator = const char*;
|
||||
|
||||
private:
|
||||
friend struct StringRefTestAccess;
|
||||
|
||||
char const* m_start;
|
||||
size_type m_size;
|
||||
|
||||
char* m_data = nullptr;
|
||||
|
||||
void takeOwnership();
|
||||
|
||||
static constexpr char const* const s_empty = "";
|
||||
|
||||
public: // construction/ assignment
|
||||
StringRef() noexcept
|
||||
: StringRef( s_empty, 0 )
|
||||
{}
|
||||
char const* m_start = s_empty;
|
||||
size_type m_size = 0;
|
||||
|
||||
StringRef( StringRef const& other ) noexcept
|
||||
: m_start( other.m_start ),
|
||||
m_size( other.m_size )
|
||||
{}
|
||||
|
||||
StringRef( StringRef&& other ) noexcept
|
||||
: m_start( other.m_start ),
|
||||
m_size( other.m_size ),
|
||||
m_data( other.m_data )
|
||||
{
|
||||
other.m_data = nullptr;
|
||||
}
|
||||
public: // construction
|
||||
constexpr StringRef() noexcept = default;
|
||||
|
||||
StringRef( char const* rawChars ) noexcept;
|
||||
|
||||
StringRef( char const* rawChars, size_type size ) noexcept
|
||||
constexpr StringRef( char const* rawChars, size_type size ) noexcept
|
||||
: m_start( rawChars ),
|
||||
m_size( size )
|
||||
{}
|
||||
@@ -66,66 +43,61 @@ namespace Catch {
|
||||
m_size( stdString.size() )
|
||||
{}
|
||||
|
||||
~StringRef() noexcept {
|
||||
delete[] m_data;
|
||||
explicit operator std::string() const {
|
||||
return std::string(m_start, m_size);
|
||||
}
|
||||
|
||||
auto operator = ( StringRef const &other ) noexcept -> StringRef& {
|
||||
delete[] m_data;
|
||||
m_data = nullptr;
|
||||
m_start = other.m_start;
|
||||
m_size = other.m_size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
operator std::string() const;
|
||||
|
||||
void swap( StringRef& other ) noexcept;
|
||||
|
||||
public: // operators
|
||||
auto operator == ( StringRef const& other ) const noexcept -> bool;
|
||||
auto operator != ( StringRef const& other ) const noexcept -> bool;
|
||||
auto operator != (StringRef const& other) const noexcept -> bool {
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
auto operator[] ( size_type index ) const noexcept -> char;
|
||||
auto operator[] ( size_type index ) const noexcept -> char {
|
||||
assert(index < m_size);
|
||||
return m_start[index];
|
||||
}
|
||||
|
||||
public: // named queries
|
||||
auto empty() const noexcept -> bool {
|
||||
constexpr auto empty() const noexcept -> bool {
|
||||
return m_size == 0;
|
||||
}
|
||||
auto size() const noexcept -> size_type {
|
||||
constexpr auto size() const noexcept -> size_type {
|
||||
return m_size;
|
||||
}
|
||||
|
||||
auto numberOfCharacters() const noexcept -> size_type;
|
||||
// Returns the current start pointer. If the StringRef is not
|
||||
// null-terminated, throws std::domain_exception
|
||||
auto c_str() const -> char const*;
|
||||
|
||||
public: // substrings and searches
|
||||
auto substr( size_type start, size_type size ) const noexcept -> StringRef;
|
||||
// Returns a substring of [start, start + length).
|
||||
// If start + length > size(), then the substring is [start, size()).
|
||||
// If start > size(), then the substring is empty.
|
||||
auto substr( size_type start, size_type length ) const noexcept -> StringRef;
|
||||
|
||||
// Returns the current start pointer.
|
||||
// Note that the pointer can change when if the StringRef is a substring
|
||||
auto currentData() const noexcept -> char const*;
|
||||
// Returns the current start pointer. May not be null-terminated.
|
||||
auto data() const noexcept -> char const*;
|
||||
|
||||
private: // ownership queries - may not be consistent between calls
|
||||
auto isOwned() const noexcept -> bool;
|
||||
auto isSubstring() const noexcept -> bool;
|
||||
constexpr auto isNullTerminated() const noexcept -> bool {
|
||||
return m_start[m_size] == '\0';
|
||||
}
|
||||
|
||||
public: // iterators
|
||||
constexpr const_iterator begin() const { return m_start; }
|
||||
constexpr const_iterator end() const { return m_start + m_size; }
|
||||
};
|
||||
|
||||
auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
|
||||
auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
|
||||
auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
|
||||
|
||||
auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
|
||||
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
|
||||
|
||||
|
||||
inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
|
||||
constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
|
||||
return StringRef( rawChars, size );
|
||||
}
|
||||
|
||||
} // namespace Catch
|
||||
|
||||
inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
|
||||
constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
|
||||
return Catch::StringRef( rawChars, size );
|
||||
}
|
||||
|
||||
|
@@ -18,8 +18,9 @@ namespace Catch {
|
||||
} // end namespace Catch
|
||||
|
||||
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
|
||||
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
|
||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
||||
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||
|
||||
#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED
|
||||
|
@@ -59,8 +59,7 @@ namespace Catch {
|
||||
std::vector<std::string> tags;
|
||||
std::string desc, tag;
|
||||
bool inTag = false;
|
||||
std::string _descOrTags = nameAndTags.tags;
|
||||
for (char c : _descOrTags) {
|
||||
for (char c : nameAndTags.tags) {
|
||||
if( !inTag ) {
|
||||
if( c == '[' )
|
||||
inTag = true;
|
||||
@@ -90,10 +89,11 @@ namespace Catch {
|
||||
}
|
||||
}
|
||||
if( isHidden ) {
|
||||
tags.push_back( "." );
|
||||
// Add all "hidden" tags to make them behave identically
|
||||
tags.insert( tags.end(), { ".", "!hide" } );
|
||||
}
|
||||
|
||||
TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
|
||||
TestCaseInfo info( static_cast<std::string>(nameAndTags.name), _className, desc, tags, _lineInfo );
|
||||
return TestCase( _testCase, std::move(info) );
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user