mirror of
https://github.com/catchorg/Catch2.git
synced 2025-09-20 11:35:39 +02:00
Compare commits
111 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
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 |
12
.travis.yml
12
.travis.yml
@@ -9,8 +9,8 @@ common_sources: &all_sources
|
|||||||
- llvm-toolchain-trusty
|
- llvm-toolchain-trusty
|
||||||
- llvm-toolchain-trusty-3.9
|
- llvm-toolchain-trusty-3.9
|
||||||
- llvm-toolchain-trusty-4.0
|
- llvm-toolchain-trusty-4.0
|
||||||
- llvm-toolchain-trusty-5.0
|
- llvm-toolchain-xenial-5.0
|
||||||
- llvm-toolchain-trusty-6.0
|
- llvm-toolchain-xenial-6.0
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
@@ -60,6 +60,7 @@ matrix:
|
|||||||
env: COMPILER='clang++-4.0'
|
env: COMPILER='clang++-4.0'
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -68,6 +69,7 @@ matrix:
|
|||||||
env: COMPILER='clang++-5.0'
|
env: COMPILER='clang++-5.0'
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -153,6 +155,7 @@ matrix:
|
|||||||
env: COMPILER='clang++-4.0' CPP14=1
|
env: COMPILER='clang++-4.0' CPP14=1
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -161,6 +164,7 @@ matrix:
|
|||||||
env: COMPILER='clang++-5.0' CPP14=1
|
env: COMPILER='clang++-5.0' CPP14=1
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -255,6 +259,7 @@ matrix:
|
|||||||
env: COMPILER='g++-7' EXAMPLES=1 COVERAGE=1 EXTRAS=1 CPP17=1
|
env: COMPILER='g++-7' EXAMPLES=1 COVERAGE=1 EXTRAS=1 CPP17=1
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -263,6 +268,7 @@ matrix:
|
|||||||
env: COMPILER='clang++-6.0' CPP17=1
|
env: COMPILER='clang++-6.0' CPP17=1
|
||||||
|
|
||||||
- os: linux
|
- os: linux
|
||||||
|
dist: xenial
|
||||||
compiler: clang
|
compiler: clang
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
@@ -276,7 +282,7 @@ matrix:
|
|||||||
- "3.7"
|
- "3.7"
|
||||||
dist: xenial
|
dist: xenial
|
||||||
install:
|
install:
|
||||||
- pip install conan conan-package-tools
|
- pip install conan-package-tools
|
||||||
env:
|
env:
|
||||||
- CONAN_GCC_VERSIONS=8
|
- CONAN_GCC_VERSIONS=8
|
||||||
- CONAN_DOCKER_IMAGE=conanio/gcc8
|
- CONAN_DOCKER_IMAGE=conanio/gcc8
|
||||||
|
@@ -6,12 +6,16 @@ if(NOT DEFINED PROJECT_NAME)
|
|||||||
set(NOT_SUBPROJECT ON)
|
set(NOT_SUBPROJECT ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
project(Catch2 LANGUAGES CXX VERSION 2.9.2)
|
# 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
|
||||||
if (CMAKE_BINARY_DIR STREQUAL CMAKE_SOURCE_DIR)
|
# 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")
|
message(FATAL_ERROR "Building in-source is not supported! Create a build dir and remove ${CMAKE_SOURCE_DIR}/CMakeCache.txt")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
project(Catch2 LANGUAGES CXX VERSION 2.11.0)
|
||||||
|
|
||||||
# Provide path for scripts
|
# Provide path for scripts
|
||||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
|
||||||
|
|
||||||
@@ -92,6 +96,10 @@ target_include_directories(Catch2
|
|||||||
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
$<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
|
# provide a namespaced alias for clients to 'link' against if catch is included as a sub-project
|
||||||
add_library(Catch2::Catch2 ALIAS Catch2)
|
add_library(Catch2::Catch2 ALIAS Catch2)
|
||||||
|
|
||||||
|
@@ -5,11 +5,11 @@
|
|||||||
[](https://travis-ci.org/catchorg/Catch2)
|
[](https://travis-ci.org/catchorg/Catch2)
|
||||||
[](https://ci.appveyor.com/project/catchorg/catch2)
|
[](https://ci.appveyor.com/project/catchorg/catch2)
|
||||||
[](https://codecov.io/gh/catchorg/Catch2)
|
[](https://codecov.io/gh/catchorg/Catch2)
|
||||||
[](https://wandbox.org/permlink/8YrGVqYqqSC4Sc5R)
|
[](https://wandbox.org/permlink/HU1MkiBgFetFQJU4)
|
||||||
[](https://discord.gg/4CWS9zD)
|
[](https://discord.gg/4CWS9zD)
|
||||||
|
|
||||||
|
|
||||||
<a href="https://github.com/catchorg/Catch2/releases/download/v2.9.2/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.0/catch.hpp">The latest version of the single header can be downloaded directly using this link</a>
|
||||||
|
|
||||||
## Catch2 is released!
|
## Catch2 is released!
|
||||||
|
|
||||||
|
@@ -1,6 +1,11 @@
|
|||||||
# version string format -- This will be overwritten later anyway
|
# version string format -- This will be overwritten later anyway
|
||||||
version: "{build}"
|
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:
|
branches:
|
||||||
except:
|
except:
|
||||||
- /dev-travis.+/
|
- /dev-travis.+/
|
||||||
@@ -62,7 +67,7 @@ matrix:
|
|||||||
|
|
||||||
|
|
||||||
install:
|
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 }
|
- ps: if (($env:CONFIGURATION) -eq "Debug" -And ($env:coverage) -eq "1" ) { .\misc\installOpenCppCoverage.ps1 }
|
||||||
|
|
||||||
# Win32 and x64 are CMake-compatible solution platform names.
|
# 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_tap.hpp"
|
||||||
- "**/catch_reporter_automake.hpp"
|
- "**/catch_reporter_automake.hpp"
|
||||||
- "**/catch_reporter_teamcity.hpp"
|
- "**/catch_reporter_teamcity.hpp"
|
||||||
|
- "**/catch_reporter_sonarqube.hpp"
|
||||||
- "**/external/clara.hpp"
|
- "**/external/clara.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
@@ -12,7 +12,7 @@ Build Systems may refer to low-level tools, like CMake, or larger systems that r
|
|||||||
|
|
||||||
## Continuous Integration systems
|
## 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.
|
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.
|
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
|
## Low-level tools
|
||||||
|
|
||||||
### Precompiled headers (PCHs)
|
### Precompiled headers (PCHs)
|
||||||
|
@@ -99,6 +99,7 @@ exclude:notThis Matches all tests except, 'notThis'
|
|||||||
~*private* Matches all tests except those that contain 'private'
|
~*private* Matches all tests except those that contain 'private'
|
||||||
a* ~ab* abc Matches all tests that start with 'a', except those that
|
a* ~ab* abc Matches all tests that start with 'a', except those that
|
||||||
start with 'ab', except 'abc', which is included
|
start with 'ab', except 'abc', which is included
|
||||||
|
-# [#somefile] Matches all tests from the file 'somefile.cpp'
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
Names within square brackets are interpreted as tags.
|
Names within square brackets are interpreted as tags.
|
||||||
|
@@ -17,3 +17,4 @@ fact then please let us know - either directly, via a PR or
|
|||||||
- NASA
|
- NASA
|
||||||
- [Inscopix Inc.](https://www.inscopix.com/)
|
- [Inscopix Inc.](https://www.inscopix.com/)
|
||||||
- [Makimo](https://makimo.pl/)
|
- [Makimo](https://makimo.pl/)
|
||||||
|
- [UX3D] (https://ux3d.io)
|
||||||
|
@@ -155,6 +155,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_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_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_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.
|
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.
|
||||||
|
|
||||||
|
@@ -70,6 +70,10 @@ locally takes just a few minutes.
|
|||||||
$ cd debug-build
|
$ cd debug-build
|
||||||
$ ctest -j 2 --output-on-failure
|
$ 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
|
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
|
do not, it means that your changes weren't run as part of them. This
|
||||||
_might_ be intentional, but usually is not.
|
_might_ be intentional, but usually is not.
|
||||||
|
@@ -39,6 +39,11 @@ apart from writing it out for `--list-tests -v high`.
|
|||||||
Because it isn't actually used nor documented, and brings complications
|
Because it isn't actually used nor documented, and brings complications
|
||||||
to Catch2's internals, description support will be removed.
|
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.
|
||||||
|
|
||||||
|
|
||||||
## Planned changes
|
## Planned changes
|
||||||
|
|
||||||
@@ -88,6 +93,17 @@ positively match a testspec.
|
|||||||
The API for Catch2's console colour will be changed to take an extra
|
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.
|
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)
|
[Home](Readme.md#top)
|
||||||
|
@@ -36,8 +36,8 @@ Catch2's provided generator functionality consists of three parts,
|
|||||||
* `GENERATE` macro, that serves to integrate generator expression with
|
* `GENERATE` macro, that serves to integrate generator expression with
|
||||||
a test case,
|
a test case,
|
||||||
* 2 fundamental generators
|
* 2 fundamental generators
|
||||||
* `ValueGenerator<T>` -- contains only single element
|
* `SingleValueGenerator<T>` -- contains only single element
|
||||||
* `ValuesGenerator<T>` -- contains multiple elements
|
* `FixedValuesGenerator<T>` -- contains multiple elements
|
||||||
* 5 generic generators that modify other generators
|
* 5 generic generators that modify other generators
|
||||||
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
|
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
|
||||||
for which the predicate returns "false"
|
for which the predicate returns "false"
|
||||||
@@ -46,18 +46,22 @@ a test case,
|
|||||||
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
|
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
|
||||||
on elements from a different generator
|
on elements from a different generator
|
||||||
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a 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
|
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
|
||||||
* `RandomFloatGenerator<Float>` -- generates random Floats 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.
|
> `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
|
The generators also have associated helper functions that infer their
|
||||||
type, making their usage much nicer. These are
|
type, making their usage much nicer. These are
|
||||||
|
|
||||||
* `value(T&&)` for `ValueGenerator<T>`
|
* `value(T&&)` for `SingleValueGenerator<T>`
|
||||||
* `values(std::initializer_list<T>)` for `ValuesGenerator<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>`
|
* `filter(predicate, GeneratorWrapper<T>&&)` for `FilterGenerator<T, Predicate>`
|
||||||
* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
|
* `take(count, GeneratorWrapper<T>&&)` for `TakeGenerator<T>`
|
||||||
* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
|
* `repeat(repeats, GeneratorWrapper<T>&&)` for `RepeatGenerator<T>`
|
||||||
@@ -65,11 +69,17 @@ type, making their usage much nicer. These are
|
|||||||
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
|
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
|
||||||
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
|
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
|
||||||
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
|
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
|
||||||
* `range(start, end)` for `RangeGenerator<T>` with a step size of `1`
|
* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
|
||||||
* `range(start, end, step)` for `RangeGenerator<T>` with a custom step size
|
* `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.
|
> `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
|
And can be used as shown in the example below to create a generator
|
||||||
that returns 100 odd random number:
|
that returns 100 odd random number:
|
||||||
|
|
||||||
@@ -89,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
|
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
|
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
|
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
|
a special generator that concatenates other generators. It can also be
|
||||||
used with other generators as arguments, such as `auto i = GENERATE(0, 2,
|
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
|
take(100, random(300, 3000)));`. This is useful e.g. if you know that
|
||||||
|
@@ -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.
|
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
|
## Features
|
||||||
This section outlines some missing features, what is their status and their possible workarounds.
|
This section outlines some missing features, what is their status and their possible workarounds.
|
||||||
|
|
||||||
|
@@ -17,7 +17,7 @@ For example, to assert that a string ends with a certain substring:
|
|||||||
using Catch::Matchers::EndsWith; // or Catch::EndsWith
|
using Catch::Matchers::EndsWith; // or Catch::EndsWith
|
||||||
std::string str = getStringFromSomewhere();
|
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 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
|
The built-in string matchers, for example, take a second argument specifying whether the comparison is
|
||||||
@@ -35,8 +35,34 @@ REQUIRE_THAT( str,
|
|||||||
(StartsWith( "Big data" ) && !Contains( "web scale" ) ) );
|
(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
|
## 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
|
### 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.
|
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.
|
||||||
@@ -57,10 +83,30 @@ These are
|
|||||||
|
|
||||||
|
|
||||||
### Floating point matchers
|
### 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
|
### Generic matchers
|
||||||
Catch also aims to provide a set of generic matchers. Currently this set
|
Catch also aims to provide a set of generic matchers. Currently this set
|
||||||
@@ -81,6 +127,22 @@ The second argument is an optional description of the predicate, and is
|
|||||||
used only during reporting of the result.
|
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
|
## Custom matchers
|
||||||
It's easy to provide your own matchers to extend Catch or just to work with your own types.
|
It's easy to provide your own matchers to extend Catch or just to work with your own types.
|
||||||
|
|
||||||
|
@@ -23,6 +23,9 @@ C++11 implementation of Approval Tests, for quick, convenient testing of legacy
|
|||||||
### [Azmq](https://github.com/zeromq/azmq)
|
### [Azmq](https://github.com/zeromq/azmq)
|
||||||
Boost Asio style bindings for ZeroMQ.
|
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)
|
### [ChakraCore](https://github.com/Microsoft/ChakraCore)
|
||||||
The core part of the Chakra JavaScript engine that powers Microsoft Edge.
|
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)
|
### [Inja](https://github.com/pantor/inja)
|
||||||
A header-only template engine for modern C++.
|
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)
|
### [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.
|
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.
|
||||||
|
|
||||||
|
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
**Contents**<br>
|
**Contents**<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.2](#292)<br>
|
||||||
[2.9.1](#291)<br>
|
[2.9.1](#291)<br>
|
||||||
[2.9.0](#290)<br>
|
[2.9.0](#290)<br>
|
||||||
@@ -28,6 +32,95 @@
|
|||||||
[Even Older versions](#even-older-versions)<br>
|
[Even Older versions](#even-older-versions)<br>
|
||||||
|
|
||||||
|
|
||||||
|
## 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
|
## 2.9.2
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
@@ -61,6 +154,10 @@
|
|||||||
* Improved `*_THROWS_MATCHES` documentation a bit
|
* Improved `*_THROWS_MATCHES` documentation a bit
|
||||||
* CMake config file is now arch-independent even if `CMAKE_SIZEOF_VOID_P` is in CMake cache (#1660)
|
* 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)
|
* `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
|
## 2.9.1
|
||||||
|
@@ -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.
|
description should contain the release notes for the current release.
|
||||||
Single header version of `catch.hpp` *needs* to be attached as a binary,
|
Single header version of `catch.hpp` *needs* to be attached as a binary,
|
||||||
as that is where the official download link links to. Preferably
|
as that is where the official download link links to. Preferably
|
||||||
it should use linux line endings. All non-bundled reporters (Automake,
|
it should use linux line endings. All non-bundled reporters (Automake, TAP,
|
||||||
TAP, TeamCity) should also be attached as binaries, as they might be
|
TeamCity, SonarQube) should also be attached as binaries, as they might be
|
||||||
dependent on a specific version of the single-include header.
|
dependent on a specific version of the single-include header.
|
||||||
|
|
||||||
Since 2.5.0, the release tag and the "binaries" (headers) should be PGP
|
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_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_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_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._
|
_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)).
|
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.
|
* `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
|
* `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`.
|
You see what reporters are available from the command line by running with `--list-reporters`.
|
||||||
|
|
||||||
|
@@ -10,11 +10,12 @@
|
|||||||
#define CATCH_CONFIG_MAIN
|
#define CATCH_CONFIG_MAIN
|
||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
|
||||||
class out_buff : public std::stringbuf {
|
class out_buff : public std::stringbuf {
|
||||||
std::FILE* m_stream;
|
std::FILE* m_stream;
|
||||||
public:
|
public:
|
||||||
out_buff(std::FILE* stream) :m_stream(stream) {}
|
out_buff(std::FILE* stream):m_stream(stream) {}
|
||||||
~out_buff() { pubsync(); }
|
~out_buff();
|
||||||
int sync() {
|
int sync() {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
for (unsigned char c : str()) {
|
for (unsigned char c : str()) {
|
||||||
@@ -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 {
|
namespace Catch {
|
||||||
std::ostream& cout() {
|
std::ostream& cout() {
|
||||||
static std::ostream ret(new out_buff(stdout));
|
static std::ostream ret(new out_buff(stdout));
|
||||||
|
@@ -22,15 +22,17 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string const& get() const override {
|
std::string const& get() const override;
|
||||||
return m_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool next() override {
|
bool next() override {
|
||||||
return !!std::getline(m_stream, m_line);
|
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
|
// This helper function provides a nicer UX when instantiating the generator
|
||||||
// Notice that it returns an instance of GeneratorWrapper<std::string>, which
|
// Notice that it returns an instance of GeneratorWrapper<std::string>, which
|
||||||
// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
|
// is a value-wrapper around std::unique_ptr<IGenerator<std::string>>.
|
||||||
|
@@ -10,8 +10,8 @@
|
|||||||
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
|
#define TWOBLUECUBES_CATCH_HPP_INCLUDED
|
||||||
|
|
||||||
#define CATCH_VERSION_MAJOR 2
|
#define CATCH_VERSION_MAJOR 2
|
||||||
#define CATCH_VERSION_MINOR 9
|
#define CATCH_VERSION_MINOR 11
|
||||||
#define CATCH_VERSION_PATCH 2
|
#define CATCH_VERSION_PATCH 0
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# pragma clang system_header
|
# pragma clang system_header
|
||||||
@@ -80,7 +80,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||||
#include "internal/benchmark/catch_benchmark.hpp"
|
#include "internal/benchmark/catch_benchmarking_all.hpp"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif // ! CATCH_CONFIG_IMPL_ONLY
|
#endif // ! CATCH_CONFIG_IMPL_ONLY
|
||||||
|
@@ -79,7 +79,7 @@ namespace Catch {
|
|||||||
});
|
});
|
||||||
|
|
||||||
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
|
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);
|
getResultCapture().benchmarkEnded(stats);
|
||||||
|
|
||||||
} CATCH_CATCH_ALL{
|
} 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"
|
@@ -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) {
|
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
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||||
static std::random_device entropy;
|
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++
|
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
|
||||||
|
|
||||||
|
@@ -52,7 +52,8 @@ namespace Detail {
|
|||||||
bool Approx::equalityComparisonImpl(const double other) const {
|
bool Approx::equalityComparisonImpl(const double other) const {
|
||||||
// First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
|
// 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
|
// 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) {
|
void Approx::setMargin(double newMargin) {
|
||||||
|
@@ -45,7 +45,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool AssertionResult::hasExpression() const {
|
bool AssertionResult::hasExpression() const {
|
||||||
return m_info.capturedExpression[0] != 0;
|
return !m_info.capturedExpression.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AssertionResult::hasMessage() const {
|
bool AssertionResult::hasMessage() const {
|
||||||
@@ -53,16 +53,22 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string AssertionResult::getExpression() const {
|
std::string AssertionResult::getExpression() const {
|
||||||
if( isFalseTest( m_info.resultDisposition ) )
|
// Possibly overallocating by 3 characters should be basically free
|
||||||
return "!(" + m_info.capturedExpression + ")";
|
std::string expr; expr.reserve(m_info.capturedExpression.size() + 3);
|
||||||
else
|
if (isFalseTest(m_info.resultDisposition)) {
|
||||||
return m_info.capturedExpression;
|
expr += "!(";
|
||||||
|
}
|
||||||
|
expr += m_info.capturedExpression;
|
||||||
|
if (isFalseTest(m_info.resultDisposition)) {
|
||||||
|
expr += ')';
|
||||||
|
}
|
||||||
|
return expr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string AssertionResult::getExpressionInMacro() const {
|
std::string AssertionResult::getExpressionInMacro() const {
|
||||||
std::string expr;
|
std::string expr;
|
||||||
if( m_info.macroName[0] == 0 )
|
if( m_info.macroName.empty() )
|
||||||
expr = m_info.capturedExpression;
|
expr = static_cast<std::string>(m_info.capturedExpression);
|
||||||
else {
|
else {
|
||||||
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
|
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
|
||||||
expr += m_info.macroName;
|
expr += m_info.macroName;
|
||||||
|
@@ -43,9 +43,10 @@
|
|||||||
do { \
|
do { \
|
||||||
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
|
Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
|
||||||
INTERNAL_CATCH_TRY { \
|
INTERNAL_CATCH_TRY { \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||||
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
|
catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
|
} INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
|
||||||
INTERNAL_CATCH_REACT( 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
|
} 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
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "catch_capture.hpp"
|
#include "catch_capture.hpp"
|
||||||
#include "catch_matchers.h"
|
#include "catch_matchers.h"
|
||||||
|
#include "catch_matchers_exception.hpp"
|
||||||
#include "catch_matchers_floating.h"
|
#include "catch_matchers_floating.h"
|
||||||
#include "catch_matchers_generic.hpp"
|
#include "catch_matchers_generic.hpp"
|
||||||
#include "catch_matchers_string.h"
|
#include "catch_matchers_string.h"
|
||||||
|
@@ -49,9 +49,15 @@ namespace Catch {
|
|||||||
if( !line.empty() && !startsWith( line, '#' ) ) {
|
if( !line.empty() && !startsWith( line, '#' ) ) {
|
||||||
if( !startsWith( line, '"' ) )
|
if( !startsWith( line, '"' ) )
|
||||||
line = '"' + line + '"';
|
line = '"' + line + '"';
|
||||||
config.testsOrTags.push_back( line + ',' );
|
config.testsOrTags.push_back( line );
|
||||||
|
config.testsOrTags.push_back( "," );
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//Remove comma in the end
|
||||||
|
if(!config.testsOrTags.empty())
|
||||||
|
config.testsOrTags.erase( config.testsOrTags.end()-1 );
|
||||||
|
|
||||||
return ParserResult::ok( ParseResultType::Matched );
|
return ParserResult::ok( ParseResultType::Matched );
|
||||||
};
|
};
|
||||||
auto const setTestOrder = [&]( std::string const& order ) {
|
auto const setTestOrder = [&]( std::string const& order ) {
|
||||||
|
@@ -15,9 +15,6 @@
|
|||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
bool SourceLineInfo::empty() const noexcept {
|
|
||||||
return file[0] == '\0';
|
|
||||||
}
|
|
||||||
bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
|
bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
|
||||||
return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
|
return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
|
||||||
}
|
}
|
||||||
|
@@ -57,7 +57,7 @@ namespace Catch {
|
|||||||
SourceLineInfo( SourceLineInfo&& ) noexcept = default;
|
SourceLineInfo( SourceLineInfo&& ) noexcept = default;
|
||||||
SourceLineInfo& operator = ( 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;
|
||||||
bool operator < ( SourceLineInfo const& other ) const noexcept;
|
bool operator < ( SourceLineInfo const& other ) const noexcept;
|
||||||
|
|
||||||
|
@@ -43,32 +43,33 @@
|
|||||||
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
||||||
#endif
|
#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" )
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
|
||||||
|
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" )
|
||||||
|
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" )
|
||||||
|
|
||||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
_Pragma( "clang diagnostic push" ) \
|
|
||||||
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
|
_Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
|
||||||
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
|
_Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
|
||||||
_Pragma( "clang diagnostic pop" )
|
|
||||||
|
|
||||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
|
||||||
_Pragma( "clang diagnostic push" ) \
|
|
||||||
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
|
_Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
|
|
||||||
_Pragma( "clang diagnostic pop" )
|
|
||||||
|
|
||||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||||
_Pragma( "clang diagnostic push" ) \
|
|
||||||
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
|
_Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
|
|
||||||
_Pragma( "clang diagnostic pop" )
|
|
||||||
|
|
||||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||||
_Pragma( "clang diagnostic push" ) \
|
|
||||||
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
|
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
|
||||||
_Pragma( "clang diagnostic pop" )
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
|
_Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
|
||||||
|
|
||||||
#endif // __clang__
|
#endif // __clang__
|
||||||
|
|
||||||
@@ -94,6 +95,7 @@
|
|||||||
// Android somehow still does not support std::to_string
|
// Android somehow still does not support std::to_string
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
|
||||||
|
# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -127,8 +129,10 @@
|
|||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Visual C++
|
// 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
|
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
|
||||||
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
|
||||||
@@ -196,49 +200,43 @@
|
|||||||
#define CATCH_CONFIG_COLOUR_NONE
|
#define CATCH_CONFIG_COLOUR_NONE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
#if defined(__UCLIBC__)
|
||||||
// Check if string_view is available and usable
|
#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER
|
||||||
// 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
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// Various stdlib support checks that require __has_include
|
||||||
// Check if optional is available and usable
|
|
||||||
#if defined(__has_include)
|
#if defined(__has_include)
|
||||||
# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
// Check if string_view is available and usable
|
||||||
# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
|
#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
# define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
|
||||||
#endif // __has_include
|
#endif
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// Check if optional is available and usable
|
||||||
// Check if byte is available and usable
|
# if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
#if defined(__has_include)
|
# define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
|
||||||
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
# endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
|
||||||
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
|
||||||
#endif // __has_include
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
// Check if byte is available and usable
|
||||||
// Check if variant is available and usable
|
# if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
#if defined(__has_include)
|
# define CATCH_INTERNAL_CONFIG_CPP17_BYTE
|
||||||
# if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
# endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
# if defined(__clang__) && (__clang_major__ < 8)
|
|
||||||
|
// 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
|
// 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
|
// fix should be in clang 8, workaround in libstdc++ 8.2
|
||||||
# include <ciso646>
|
# include <ciso646>
|
||||||
# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
# if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||||
# define CATCH_CONFIG_NO_CPP17_VARIANT
|
# define CATCH_CONFIG_NO_CPP17_VARIANT
|
||||||
# else
|
# else
|
||||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||||
# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
# endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
|
||||||
# else
|
# else
|
||||||
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
# define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
|
||||||
# endif // defined(__clang__) && (__clang_major__ < 8)
|
# endif // defined(__clang__) && (__clang_major__ < 8)
|
||||||
# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
# endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
|
||||||
#endif // __has_include
|
#endif // defined(__has_include)
|
||||||
|
|
||||||
|
|
||||||
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
|
#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
|
||||||
@@ -301,21 +299,45 @@
|
|||||||
# define CATCH_CONFIG_USE_ASYNC
|
# define CATCH_CONFIG_USE_ASYNC
|
||||||
#endif
|
#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)
|
#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
|
||||||
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
|
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
|
|
||||||
#endif
|
#endif
|
||||||
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
|
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
|
||||||
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
|
||||||
#endif
|
#endif
|
||||||
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
|
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
|
||||||
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
|
# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
|
|
||||||
#endif
|
#endif
|
||||||
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
|
||||||
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
|
||||||
# define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS
|
#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
|
#endif
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
|
||||||
|
@@ -15,11 +15,23 @@ namespace Catch {
|
|||||||
: m_data( data ),
|
: m_data( data ),
|
||||||
m_stream( openStream() )
|
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());
|
TestSpecParser parser(ITagAliasRegistry::get());
|
||||||
if (!data.testsOrTags.empty()) {
|
if (!m_data.testsOrTags.empty()) {
|
||||||
m_hasTestFilters = true;
|
m_hasTestFilters = true;
|
||||||
for( auto const& testOrTags : data.testsOrTags )
|
for (auto const& testOrTags : m_data.testsOrTags) {
|
||||||
parser.parse( testOrTags );
|
parser.parse(testOrTags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_testSpec = parser.testSpec();
|
m_testSpec = parser.testSpec();
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
#include "catch_context.h"
|
#include "catch_context.h"
|
||||||
#include "catch_common.h"
|
#include "catch_common.h"
|
||||||
|
#include "catch_random_number_generator.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
@@ -59,4 +60,11 @@ namespace Catch {
|
|||||||
IContext::~IContext() = default;
|
IContext::~IContext() = default;
|
||||||
IMutableContext::~IMutableContext() = default;
|
IMutableContext::~IMutableContext() = default;
|
||||||
Context::~Context() = default;
|
Context::~Context() = default;
|
||||||
|
|
||||||
|
|
||||||
|
SimplePcg32& rng() {
|
||||||
|
static SimplePcg32 s_rng;
|
||||||
|
return s_rng;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -46,6 +46,7 @@ namespace Catch {
|
|||||||
{
|
{
|
||||||
if( !IMutableContext::currentContext )
|
if( !IMutableContext::currentContext )
|
||||||
IMutableContext::createContext();
|
IMutableContext::createContext();
|
||||||
|
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
|
||||||
return *IMutableContext::currentContext;
|
return *IMutableContext::currentContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,6 +56,9 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void cleanUpContext();
|
void cleanUpContext();
|
||||||
|
|
||||||
|
class SimplePcg32;
|
||||||
|
SimplePcg32& rng();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
|
#endif // TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
|
||||||
|
@@ -7,16 +7,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "catch_debug_console.h"
|
#include "catch_debug_console.h"
|
||||||
|
#include "catch_compiler_capabilities.h"
|
||||||
#include "catch_stream.h"
|
#include "catch_stream.h"
|
||||||
#include "catch_platform.h"
|
#include "catch_platform.h"
|
||||||
#include "catch_windows_h_proxy.h"
|
#include "catch_windows_h_proxy.h"
|
||||||
|
|
||||||
#if defined(__ANDROID__)
|
#if defined(CATCH_CONFIG_ANDROID_LOGWRITE)
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
void writeToDebugConsole( std::string const& text ) {
|
void writeToDebugConsole( std::string const& text ) {
|
||||||
__android_log_print( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
|
__android_log_write( ANDROID_LOG_DEBUG, "Catch", text.c_str() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -38,13 +38,13 @@ namespace Catch {
|
|||||||
(Catch::ReusableStringStream() << __VA_ARGS__).str()
|
(Catch::ReusableStringStream() << __VA_ARGS__).str()
|
||||||
|
|
||||||
#define CATCH_INTERNAL_ERROR(...) \
|
#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(...) \
|
#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(...) \
|
#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, ... ) \
|
#define CATCH_ENFORCE( condition, ... ) \
|
||||||
do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
|
do{ if( !(condition) ) CATCH_ERROR( __VA_ARGS__ ); } while(false)
|
||||||
|
@@ -18,13 +18,25 @@ namespace Catch {
|
|||||||
|
|
||||||
namespace Detail {
|
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, ',' );
|
auto enumValues = splitStringRef( enums, ',' );
|
||||||
std::vector<std::string> parsed;
|
std::vector<StringRef> parsed;
|
||||||
parsed.reserve( enumValues.size() );
|
parsed.reserve( enumValues.size() );
|
||||||
for( auto const& enumValue : enumValues ) {
|
for( auto const& enumValue : enumValues ) {
|
||||||
auto identifiers = splitStringRef( enumValue, ':' );
|
parsed.push_back(trim(extractInstanceName(enumValue)));
|
||||||
parsed.push_back( Catch::trim( identifiers.back() ) );
|
|
||||||
}
|
}
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
@@ -36,7 +48,7 @@ namespace Catch {
|
|||||||
if( valueToName.first == value )
|
if( valueToName.first == value )
|
||||||
return valueToName.second;
|
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 ) {
|
std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||||
@@ -54,10 +66,8 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
|
||||||
auto enumInfo = makeEnumInfo( enumName, allValueNames, values );
|
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
|
||||||
EnumInfo* raw = enumInfo.get();
|
return *m_enumInfos.back();
|
||||||
m_enumInfos.push_back( std::move( enumInfo ) );
|
|
||||||
return *raw;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Detail
|
} // Detail
|
||||||
|
@@ -26,7 +26,7 @@ namespace Catch {
|
|||||||
EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
|
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
|
} // Detail
|
||||||
|
|
||||||
|
@@ -71,7 +71,7 @@ namespace Generators {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class FixedValuesGenerator final : public IGenerator<T> {
|
class FixedValuesGenerator final : public IGenerator<T> {
|
||||||
static_assert(!std::is_same<T, bool>::value,
|
static_assert(!std::is_same<T, bool>::value,
|
||||||
"ValuesGenerator does not support bools because of std::vector<bool>"
|
"FixedValuesGenerator does not support bools because of std::vector<bool>"
|
||||||
"specialization, use SingleValue Generator instead.");
|
"specialization, use SingleValue Generator instead.");
|
||||||
std::vector<T> m_values;
|
std::vector<T> m_values;
|
||||||
size_t m_idx = 0;
|
size_t m_idx = 0;
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
#define TWOBLUECUBES_CATCH_GENERATORS_GENERIC_HPP_INCLUDED
|
||||||
|
|
||||||
#include "catch_generators.hpp"
|
#include "catch_generators.hpp"
|
||||||
|
#include "catch_meta.hpp"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Generators {
|
namespace Generators {
|
||||||
@@ -172,18 +173,7 @@ namespace Generators {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
|
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
|
||||||
// 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>>
|
|
||||||
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
|
GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
|
||||||
return GeneratorWrapper<T>(
|
return GeneratorWrapper<T>(
|
||||||
pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
|
pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include "catch_context.h"
|
#include "catch_context.h"
|
||||||
#include "catch_generators.hpp"
|
#include "catch_generators.hpp"
|
||||||
#include "catch_interfaces_config.h"
|
#include "catch_interfaces_config.h"
|
||||||
|
#include "catch_random_number_generator.h"
|
||||||
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
@@ -18,14 +19,13 @@ namespace Generators {
|
|||||||
|
|
||||||
template <typename Float>
|
template <typename Float>
|
||||||
class RandomFloatingGenerator final : public IGenerator<Float> {
|
class RandomFloatingGenerator final : public IGenerator<Float> {
|
||||||
// FIXME: What is the right seed?
|
Catch::SimplePcg32& m_rng;
|
||||||
std::minstd_rand m_rand;
|
|
||||||
std::uniform_real_distribution<Float> m_dist;
|
std::uniform_real_distribution<Float> m_dist;
|
||||||
Float m_current_number;
|
Float m_current_number;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RandomFloatingGenerator(Float a, Float b):
|
RandomFloatingGenerator(Float a, Float b):
|
||||||
m_rand(getCurrentContext().getConfig()->rngSeed()),
|
m_rng(rng()),
|
||||||
m_dist(a, b) {
|
m_dist(a, b) {
|
||||||
static_cast<void>(next());
|
static_cast<void>(next());
|
||||||
}
|
}
|
||||||
@@ -34,20 +34,20 @@ public:
|
|||||||
return m_current_number;
|
return m_current_number;
|
||||||
}
|
}
|
||||||
bool next() override {
|
bool next() override {
|
||||||
m_current_number = m_dist(m_rand);
|
m_current_number = m_dist(m_rng);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Integer>
|
template <typename Integer>
|
||||||
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
class RandomIntegerGenerator final : public IGenerator<Integer> {
|
||||||
std::minstd_rand m_rand;
|
Catch::SimplePcg32& m_rng;
|
||||||
std::uniform_int_distribution<Integer> m_dist;
|
std::uniform_int_distribution<Integer> m_dist;
|
||||||
Integer m_current_number;
|
Integer m_current_number;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
RandomIntegerGenerator(Integer a, Integer b):
|
RandomIntegerGenerator(Integer a, Integer b):
|
||||||
m_rand(getCurrentContext().getConfig()->rngSeed()),
|
m_rng(rng()),
|
||||||
m_dist(a, b) {
|
m_dist(a, b) {
|
||||||
static_cast<void>(next());
|
static_cast<void>(next());
|
||||||
}
|
}
|
||||||
@@ -56,7 +56,7 @@ public:
|
|||||||
return m_current_number;
|
return m_current_number;
|
||||||
}
|
}
|
||||||
bool next() override {
|
bool next() override {
|
||||||
m_current_number = m_dist(m_rand);
|
m_current_number = m_dist(m_rng);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -117,7 +117,7 @@ public:
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
|
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));
|
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 Generators
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#include "internal/catch_interfaces_config.h"
|
#include "catch_interfaces_config.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
IConfig::~IConfig() = default;
|
IConfig::~IConfig() = default;
|
||||||
|
@@ -17,7 +17,7 @@ namespace Catch {
|
|||||||
namespace Detail {
|
namespace Detail {
|
||||||
struct EnumInfo {
|
struct EnumInfo {
|
||||||
StringRef m_name;
|
StringRef m_name;
|
||||||
std::vector<std::pair<int, std::string>> m_values;
|
std::vector<std::pair<int, StringRef>> m_values;
|
||||||
|
|
||||||
~EnumInfo();
|
~EnumInfo();
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ namespace Catch {
|
|||||||
|
|
||||||
template<typename E>
|
template<typename E>
|
||||||
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
|
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;
|
std::vector<int> intValues;
|
||||||
intValues.reserve( values.size() );
|
intValues.reserve( values.size() );
|
||||||
for( auto enumValue : values )
|
for( auto enumValue : values )
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#include "internal/catch_interfaces_exception.h"
|
#include "catch_interfaces_exception.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
IExceptionTranslator::~IExceptionTranslator() = default;
|
IExceptionTranslator::~IExceptionTranslator() = default;
|
||||||
|
@@ -73,9 +73,10 @@ namespace Catch {
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
|
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
|
||||||
static std::string translatorName( signature ); \
|
static std::string translatorName( signature ); \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
|
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 )
|
static std::string translatorName( signature )
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), 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 {
|
namespace Catch {
|
||||||
IRegistryHub::~IRegistryHub() = default;
|
IRegistryHub::~IRegistryHub() = default;
|
||||||
|
@@ -214,6 +214,8 @@ namespace Catch {
|
|||||||
|
|
||||||
virtual void noMatchingTestCases( std::string const& spec ) = 0;
|
virtual void noMatchingTestCases( std::string const& spec ) = 0;
|
||||||
|
|
||||||
|
virtual void reportInvalidArguments(std::string const&) {}
|
||||||
|
|
||||||
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
|
virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
|
||||||
virtual void testGroupStarting( GroupInfo const& groupInfo ) = 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 {
|
namespace Catch {
|
||||||
IRunner::~IRunner() = default;
|
IRunner::~IRunner() = default;
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
#include "internal/catch_interfaces_testcase.h"
|
#include "catch_interfaces_testcase.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
ITestInvoker::~ITestInvoker() = default;
|
ITestInvoker::~ITestInvoker() = default;
|
||||||
|
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,54 +11,36 @@
|
|||||||
#include "catch_to_string.hpp"
|
#include "catch_to_string.hpp"
|
||||||
#include "catch_tostring.h"
|
#include "catch_tostring.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <type_traits>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Matchers {
|
|
||||||
namespace Floating {
|
|
||||||
enum class FloatingPointKind : uint8_t {
|
|
||||||
Float,
|
|
||||||
Double
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <typename T>
|
int32_t convert(float f) {
|
||||||
struct Converter;
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct Converter<float> {
|
|
||||||
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
|
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
|
||||||
Converter(float f) {
|
|
||||||
std::memcpy(&i, &f, sizeof(f));
|
|
||||||
}
|
|
||||||
int32_t i;
|
int32_t i;
|
||||||
};
|
std::memcpy(&i, &f, sizeof(f));
|
||||||
|
return i;
|
||||||
template <>
|
|
||||||
struct Converter<double> {
|
|
||||||
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
|
|
||||||
Converter(double d) {
|
|
||||||
std::memcpy(&i, &d, sizeof(d));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t convert(double d) {
|
||||||
|
static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
|
||||||
int64_t i;
|
int64_t i;
|
||||||
};
|
std::memcpy(&i, &d, sizeof(d));
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename FP>
|
||||||
auto convert(T t) -> Converter<T> {
|
bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
|
||||||
return Converter<T>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename FP>
|
|
||||||
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
|
|
||||||
// Comparison with NaN should always be false.
|
// Comparison with NaN should always be false.
|
||||||
// This way we can rule it out before getting into the ugly details
|
// This way we can rule it out before getting into the ugly details
|
||||||
if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
|
if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
|
||||||
@@ -68,29 +50,81 @@ bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
|
|||||||
auto lc = convert(lhs);
|
auto lc = convert(lhs);
|
||||||
auto rc = convert(rhs);
|
auto rc = convert(rhs);
|
||||||
|
|
||||||
if ((lc.i < 0) != (rc.i < 0)) {
|
if ((lc < 0) != (rc < 0)) {
|
||||||
// Potentially we can have +0 and -0
|
// Potentially we can have +0 and -0
|
||||||
return lhs == rhs;
|
return lhs == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ulpDiff = std::abs(lc.i - rc.i);
|
auto ulpDiff = std::abs(lc - rc);
|
||||||
return ulpDiff <= maxUlpDiff;
|
return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} //end anonymous namespace
|
||||||
|
|
||||||
|
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
// The long double overload is currently unused
|
||||||
|
#pragma clang diagnostic ignored "-Wunused-function"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float nextafter(float x, float y) {
|
||||||
|
return ::nextafterf(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
double nextafter(double x, double y) {
|
||||||
|
return ::nextafter(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
long double nextafter(long double x, long double y) {
|
||||||
|
return ::nextafterl(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
template <typename FP>
|
template <typename FP>
|
||||||
FP step(FP start, FP direction, int steps) {
|
FP step(FP start, FP direction, uint64_t steps) {
|
||||||
for (int i = 0; i < steps; ++i) {
|
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);
|
start = std::nextafter(start, direction);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return start;
|
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
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
|
||||||
namespace Catch {
|
|
||||||
namespace Matchers {
|
namespace Matchers {
|
||||||
namespace Floating {
|
namespace Floating {
|
||||||
|
|
||||||
|
enum class FloatingPointKind : uint8_t {
|
||||||
|
Float,
|
||||||
|
Double
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
|
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
|
||||||
:m_target{ target }, m_margin{ margin } {
|
:m_target{ target }, m_margin{ margin } {
|
||||||
CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
|
CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
|
||||||
@@ -108,10 +142,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 } {
|
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
|
||||||
CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
|
CATCH_ENFORCE(m_type == FloatingPointKind::Double
|
||||||
<< " ULPs have to be non-negative.");
|
|| m_ulps < (std::numeric_limits<uint32_t>::max)(),
|
||||||
|
"Provided ULP is impossibly large for a float comparison.");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__clang__)
|
#if defined(__clang__)
|
||||||
@@ -138,38 +173,59 @@ namespace Floating {
|
|||||||
std::string WithinUlpsMatcher::describe() const {
|
std::string WithinUlpsMatcher::describe() const {
|
||||||
std::stringstream ret;
|
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) {
|
if (m_type == FloatingPointKind::Float) {
|
||||||
|
write(ret, static_cast<float>(m_target));
|
||||||
ret << 'f';
|
ret << 'f';
|
||||||
|
} else {
|
||||||
|
write(ret, m_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret << " ([";
|
ret << " ([";
|
||||||
ret << std::fixed << std::setprecision(std::numeric_limits<double>::max_digits10);
|
|
||||||
if (m_type == FloatingPointKind::Double) {
|
if (m_type == FloatingPointKind::Double) {
|
||||||
ret << step(m_target, static_cast<double>(-INFINITY), m_ulps)
|
write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
|
||||||
<< ", "
|
ret << ", ";
|
||||||
<< step(m_target, static_cast<double>(INFINITY), m_ulps);
|
write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
|
||||||
} else {
|
} else {
|
||||||
ret << 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));
|
||||||
<< step<float>(static_cast<float>(m_target), INFINITY, m_ulps);
|
ret << ", ";
|
||||||
|
write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));
|
||||||
}
|
}
|
||||||
ret << "])";
|
ret << "])";
|
||||||
|
|
||||||
return ret.str();
|
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
|
}// 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);
|
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);
|
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +233,23 @@ Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
|
|||||||
return Floating::WithinAbsMatcher(target, 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 Matchers
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
|
@@ -9,9 +9,6 @@
|
|||||||
|
|
||||||
#include "catch_matchers.h"
|
#include "catch_matchers.h"
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
namespace Matchers {
|
namespace Matchers {
|
||||||
|
|
||||||
@@ -29,23 +26,43 @@ namespace Matchers {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct WithinUlpsMatcher : MatcherBase<double> {
|
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;
|
bool match(double const& matchee) const override;
|
||||||
std::string describe() const override;
|
std::string describe() const override;
|
||||||
private:
|
private:
|
||||||
double m_target;
|
double m_target;
|
||||||
int m_ulps;
|
uint64_t m_ulps;
|
||||||
FloatingPointKind m_type;
|
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
|
} // namespace Floating
|
||||||
|
|
||||||
// The following functions create the actual matcher objects.
|
// The following functions create the actual matcher objects.
|
||||||
// This allows the types to be inferred
|
// This allows the types to be inferred
|
||||||
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
|
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
|
||||||
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
|
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
|
||||||
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
|
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 Matchers
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
@@ -113,7 +113,7 @@ namespace Catch {
|
|||||||
case ',':
|
case ',':
|
||||||
if (start != pos && openings.size() == 0) {
|
if (start != pos && openings.size() == 0) {
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
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 += " := ";
|
m_messages.back().message += " := ";
|
||||||
start = pos;
|
start = pos;
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
assert(openings.size() == 0 && "Mismatched openings");
|
assert(openings.size() == 0 && "Mismatched openings");
|
||||||
m_messages.emplace_back(macroName, lineInfo, resultType);
|
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 += " := ";
|
m_messages.back().message += " := ";
|
||||||
}
|
}
|
||||||
Capturer::~Capturer() {
|
Capturer::~Capturer() {
|
||||||
|
@@ -12,22 +12,34 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct always_false : std::false_type {};
|
struct always_false : std::false_type {};
|
||||||
|
|
||||||
template <typename> struct true_given : std::true_type {};
|
template <typename> struct true_given : std::true_type {};
|
||||||
struct is_callable_tester {
|
struct is_callable_tester {
|
||||||
template <typename Fun, typename... Args>
|
template <typename Fun, typename... Args>
|
||||||
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
|
true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> static test(int);
|
||||||
template <typename...>
|
template <typename...>
|
||||||
std::false_type static test(...);
|
std::false_type static test(...);
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct is_callable;
|
struct is_callable;
|
||||||
|
|
||||||
template <typename Fun, typename... Args>
|
template <typename Fun, typename... Args>
|
||||||
struct is_callable<Fun(Args...)> : decltype(is_callable_tester::test<Fun, Args...>(0)) {};
|
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
|
} // namespace Catch
|
||||||
|
|
||||||
|
@@ -102,35 +102,49 @@
|
|||||||
template<typename...> struct TypeList {};\
|
template<typename...> struct TypeList {};\
|
||||||
template<typename...Ts>\
|
template<typename...Ts>\
|
||||||
constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
|
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> \
|
template<typename T> \
|
||||||
constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\
|
struct append<T> { using type = T; };\
|
||||||
template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
|
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>\
|
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>\
|
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>\
|
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>\
|
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>\
|
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, ...)\
|
#define INTERNAL_CATCH_NTTP_1(signature, ...)\
|
||||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
|
||||||
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
|
||||||
constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
|
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)>\
|
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>\
|
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>\
|
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_TEST0(TestName)
|
||||||
#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
|
#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
|
||||||
|
@@ -7,23 +7,67 @@
|
|||||||
|
|
||||||
#include "catch_random_number_generator.h"
|
#include "catch_random_number_generator.h"
|
||||||
#include "catch_context.h"
|
#include "catch_context.h"
|
||||||
|
#include "catch_run_context.h"
|
||||||
#include "catch_interfaces_config.h"
|
#include "catch_interfaces_config.h"
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
std::mt19937& rng() {
|
namespace {
|
||||||
static std::mt19937 s_rng;
|
|
||||||
return s_rng;
|
#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));
|
||||||
}
|
}
|
||||||
|
|
||||||
void seedRng( IConfig const& config ) {
|
#if defined(_MSC_VER)
|
||||||
if( config.rngSeed() != 0 ) {
|
#pragma warning(pop)
|
||||||
std::srand( config.rngSeed() );
|
#endif
|
||||||
rng().seed( config.rngSeed() );
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SimplePcg32::SimplePcg32(result_type seed_) {
|
||||||
|
seed(seed_);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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() {
|
SimplePcg32::result_type SimplePcg32::operator()() {
|
||||||
return getCurrentContext().getConfig()->rngSeed();
|
// 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
|
#ifndef TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||||
#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
#define TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||||
|
|
||||||
#include <algorithm>
|
#include <cstdint>
|
||||||
#include <random>
|
|
||||||
|
|
||||||
namespace Catch {
|
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();
|
// Provide some default initial state for the default constructor
|
||||||
void seedRng( IConfig const& config );
|
SimplePcg32():SimplePcg32(0xed743cc4U) {}
|
||||||
unsigned int rngSeed();
|
|
||||||
|
|
||||||
}
|
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
|
#endif // TWOBLUECUBES_CATCH_RANDOM_NUMBER_GENERATOR_H_INCLUDED
|
||||||
|
@@ -58,14 +58,16 @@ namespace Catch {
|
|||||||
#if !defined(CATCH_CONFIG_DISABLE)
|
#if !defined(CATCH_CONFIG_DISABLE)
|
||||||
|
|
||||||
#define CATCH_REGISTER_REPORTER( name, reporterType ) \
|
#define CATCH_REGISTER_REPORTER( name, reporterType ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
|
namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||||
|
|
||||||
#define CATCH_REGISTER_LISTENER( listenerType ) \
|
#define CATCH_REGISTER_LISTENER( listenerType ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
|
namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||||
#else // CATCH_CONFIG_DISABLE
|
#else // CATCH_CONFIG_DISABLE
|
||||||
|
|
||||||
#define CATCH_REGISTER_REPORTER(name, reporterType)
|
#define CATCH_REGISTER_REPORTER(name, reporterType)
|
||||||
|
@@ -279,7 +279,7 @@ namespace Catch {
|
|||||||
// Don't rebuild the result -- the stringification itself can cause more fatal errors
|
// Don't rebuild the result -- the stringification itself can cause more fatal errors
|
||||||
// Instead, fake a result data.
|
// Instead, fake a result data.
|
||||||
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
|
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
|
||||||
tempResult.message = message;
|
tempResult.message = static_cast<std::string>(message);
|
||||||
AssertionResult result(m_lastAssertionInfo, tempResult);
|
AssertionResult result(m_lastAssertionInfo, tempResult);
|
||||||
|
|
||||||
assertionEnded(result);
|
assertionEnded(result);
|
||||||
@@ -442,7 +442,7 @@ namespace Catch {
|
|||||||
m_lastAssertionInfo = info;
|
m_lastAssertionInfo = info;
|
||||||
|
|
||||||
AssertionResultData data( resultType, LazyExpression( false ) );
|
AssertionResultData data( resultType, LazyExpression( false ) );
|
||||||
data.message = message;
|
data.message = static_cast<std::string>(message);
|
||||||
AssertionResult assertionResult{ m_lastAssertionInfo, data };
|
AssertionResult assertionResult{ m_lastAssertionInfo, data };
|
||||||
assertionEnded( assertionResult );
|
assertionEnded( assertionResult );
|
||||||
if( !assertionResult.isOk() )
|
if( !assertionResult.isOk() )
|
||||||
@@ -506,4 +506,16 @@ namespace Catch {
|
|||||||
else
|
else
|
||||||
CATCH_INTERNAL_ERROR("No result capture instance");
|
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;
|
bool m_includeSuccessfulResults;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void seedRng(IConfig const& config);
|
||||||
|
unsigned int rngSeed();
|
||||||
} // end namespace Catch
|
} // end namespace Catch
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
|
#endif // TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
|
||||||
|
@@ -37,13 +37,15 @@ namespace Catch {
|
|||||||
} // end namespace Catch
|
} // end namespace Catch
|
||||||
|
|
||||||
#define INTERNAL_CATCH_SECTION( ... ) \
|
#define INTERNAL_CATCH_SECTION( ... ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
||||||
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
|
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( ... ) \
|
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
|
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() ) ) \
|
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
|
#endif // TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
|
||||||
|
@@ -68,8 +68,9 @@ namespace Catch {
|
|||||||
{
|
{
|
||||||
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
auto const& allTestCases = getAllTestCasesSorted(*m_config);
|
||||||
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
|
m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
|
||||||
|
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||||
|
|
||||||
if (m_matches.empty()) {
|
if (m_matches.empty() && invalidArgs.empty()) {
|
||||||
for (auto const& test : allTestCases)
|
for (auto const& test : allTestCases)
|
||||||
if (!test.isHidden())
|
if (!test.isHidden())
|
||||||
m_tests.emplace(&test);
|
m_tests.emplace(&test);
|
||||||
@@ -80,6 +81,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Totals execute() {
|
Totals execute() {
|
||||||
|
auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
|
||||||
Totals totals;
|
Totals totals;
|
||||||
m_context.testGroupStarting(m_config->name(), 1, 1);
|
m_context.testGroupStarting(m_config->name(), 1, 1);
|
||||||
for (auto const& testCase : m_tests) {
|
for (auto const& testCase : m_tests) {
|
||||||
@@ -95,6 +97,12 @@ namespace Catch {
|
|||||||
totals.error = -1;
|
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);
|
m_context.testGroupEnded(m_config->name(), totals, 1, 1);
|
||||||
return totals;
|
return totals;
|
||||||
}
|
}
|
||||||
@@ -175,7 +183,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
void Session::libIdentify() {
|
void Session::libIdentify() {
|
||||||
Catch::cout()
|
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) << "category: " << "testframework\n"
|
||||||
<< std::left << std::setw(16) << "framework: " << "Catch Test\n"
|
<< std::left << std::setw(16) << "framework: " << "Catch Test\n"
|
||||||
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
|
<< std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
|
||||||
|
@@ -9,6 +9,8 @@
|
|||||||
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
#ifndef TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
||||||
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
|
||||||
|
|
||||||
|
#include "catch_common.h"
|
||||||
|
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
@@ -28,7 +30,7 @@ namespace Catch {
|
|||||||
|
|
||||||
auto makeStream( StringRef const &filename ) -> IStream const*;
|
auto makeStream( StringRef const &filename ) -> IStream const*;
|
||||||
|
|
||||||
class ReusableStringStream {
|
class ReusableStringStream : NonCopyable {
|
||||||
std::size_t m_index;
|
std::size_t m_index;
|
||||||
std::ostream* m_oss;
|
std::ostream* m_oss;
|
||||||
public:
|
public:
|
||||||
|
@@ -53,6 +53,18 @@ namespace Catch {
|
|||||||
return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
|
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 replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
|
||||||
bool replaced = false;
|
bool replaced = false;
|
||||||
std::size_t i = str.find( replaceThis );
|
std::size_t i = str.find( replaceThis );
|
||||||
|
@@ -22,7 +22,10 @@ namespace Catch {
|
|||||||
bool contains( std::string const& s, std::string const& infix );
|
bool contains( std::string const& s, std::string const& infix );
|
||||||
void toLowerInPlace( std::string& s );
|
void toLowerInPlace( std::string& s );
|
||||||
std::string toLower( std::string const& 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 );
|
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
|
// !!! Be aware, returns refs into original string - make sure original string outlives them
|
||||||
std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
|
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)
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "catch_enforce.h"
|
||||||
#if defined(__clang__)
|
|
||||||
# pragma clang diagnostic push
|
|
||||||
# pragma clang diagnostic ignored "-Wexit-time-destructors"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "catch_stringref.h"
|
#include "catch_stringref.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdint>
|
#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 {
|
namespace Catch {
|
||||||
StringRef::StringRef( char const* rawChars ) noexcept
|
StringRef::StringRef( char const* rawChars ) noexcept
|
||||||
: StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
|
: 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* {
|
auto StringRef::c_str() const -> char const* {
|
||||||
if( !isSubstring() )
|
CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
|
||||||
return m_start;
|
return m_start;
|
||||||
|
|
||||||
const_cast<StringRef *>( this )->takeOwnership();
|
|
||||||
return m_data;
|
|
||||||
}
|
}
|
||||||
auto StringRef::currentData() const noexcept -> char const* {
|
auto StringRef::data() const noexcept -> char const* {
|
||||||
return m_start;
|
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 {
|
auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
|
||||||
if( start < m_size )
|
if (start < m_size) {
|
||||||
return StringRef( m_start+start, size );
|
return StringRef(m_start + start, (std::min)(m_size - start, size));
|
||||||
else
|
} else {
|
||||||
return StringRef();
|
return StringRef();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
|
auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
|
||||||
return
|
return m_size == other.m_size
|
||||||
size() == other.size() &&
|
&& (std::memcmp( m_start, other.m_start, m_size ) == 0);
|
||||||
(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 );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
|
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& {
|
auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
|
||||||
lhs.append(rhs.currentData(), rhs.size());
|
lhs.append(rhs.data(), rhs.size());
|
||||||
return lhs;
|
return lhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Catch
|
} // namespace Catch
|
||||||
|
|
||||||
#if defined(__clang__)
|
|
||||||
# pragma clang diagnostic pop
|
|
||||||
#endif
|
|
||||||
|
@@ -10,53 +10,30 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iosfwd>
|
#include <iosfwd>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
/// A non-owning string class (similar to the forthcoming std::string_view)
|
/// A non-owning string class (similar to the forthcoming std::string_view)
|
||||||
/// Note that, because a StringRef may be a substring of another string,
|
/// 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
|
/// it may not be 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.
|
|
||||||
class StringRef {
|
class StringRef {
|
||||||
public:
|
public:
|
||||||
using size_type = std::size_t;
|
using size_type = std::size_t;
|
||||||
|
using const_iterator = const char*;
|
||||||
|
|
||||||
private:
|
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 = "";
|
static constexpr char const* const s_empty = "";
|
||||||
|
|
||||||
public: // construction/ assignment
|
char const* m_start = s_empty;
|
||||||
StringRef() noexcept
|
size_type m_size = 0;
|
||||||
: StringRef( s_empty, 0 )
|
|
||||||
{}
|
|
||||||
|
|
||||||
StringRef( StringRef const& other ) noexcept
|
public: // construction
|
||||||
: m_start( other.m_start ),
|
constexpr StringRef() noexcept = default;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
StringRef( char const* rawChars ) noexcept;
|
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_start( rawChars ),
|
||||||
m_size( size )
|
m_size( size )
|
||||||
{}
|
{}
|
||||||
@@ -66,66 +43,61 @@ namespace Catch {
|
|||||||
m_size( stdString.size() )
|
m_size( stdString.size() )
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~StringRef() noexcept {
|
explicit operator std::string() const {
|
||||||
delete[] m_data;
|
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
|
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;
|
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
|
public: // named queries
|
||||||
auto empty() const noexcept -> bool {
|
constexpr auto empty() const noexcept -> bool {
|
||||||
return m_size == 0;
|
return m_size == 0;
|
||||||
}
|
}
|
||||||
auto size() const noexcept -> size_type {
|
constexpr auto size() const noexcept -> size_type {
|
||||||
return m_size;
|
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*;
|
auto c_str() const -> char const*;
|
||||||
|
|
||||||
public: // substrings and searches
|
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.
|
// Returns the current start pointer. May not be null-terminated.
|
||||||
// Note that the pointer can change when if the StringRef is a substring
|
auto data() const noexcept -> char const*;
|
||||||
auto currentData() const noexcept -> char const*;
|
|
||||||
|
|
||||||
private: // ownership queries - may not be consistent between calls
|
constexpr auto isNullTerminated() const noexcept -> bool {
|
||||||
auto isOwned() const noexcept -> bool;
|
return m_start[m_size] == '\0';
|
||||||
auto isSubstring() const noexcept -> bool;
|
}
|
||||||
|
|
||||||
|
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::string& lhs, StringRef const& sr ) -> std::string&;
|
||||||
auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
|
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 );
|
return StringRef( rawChars, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Catch
|
} // 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 );
|
return Catch::StringRef( rawChars, size );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -18,8 +18,9 @@ namespace Catch {
|
|||||||
} // end namespace Catch
|
} // end namespace Catch
|
||||||
|
|
||||||
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
|
#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
|
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
|
#endif // TWOBLUECUBES_CATCH_TAG_ALIAS_AUTOREGISTRAR_H_INCLUDED
|
||||||
|
@@ -59,8 +59,7 @@ namespace Catch {
|
|||||||
std::vector<std::string> tags;
|
std::vector<std::string> tags;
|
||||||
std::string desc, tag;
|
std::string desc, tag;
|
||||||
bool inTag = false;
|
bool inTag = false;
|
||||||
std::string _descOrTags = nameAndTags.tags;
|
for (char c : nameAndTags.tags) {
|
||||||
for (char c : _descOrTags) {
|
|
||||||
if( !inTag ) {
|
if( !inTag ) {
|
||||||
if( c == '[' )
|
if( c == '[' )
|
||||||
inTag = true;
|
inTag = true;
|
||||||
@@ -93,7 +92,7 @@ namespace Catch {
|
|||||||
tags.push_back( "." );
|
tags.push_back( "." );
|
||||||
}
|
}
|
||||||
|
|
||||||
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) );
|
return TestCase( _testCase, std::move(info) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include "catch_enforce.h"
|
#include "catch_enforce.h"
|
||||||
#include "catch_interfaces_registry_hub.h"
|
#include "catch_interfaces_registry_hub.h"
|
||||||
#include "catch_random_number_generator.h"
|
#include "catch_random_number_generator.h"
|
||||||
|
#include "catch_run_context.h"
|
||||||
#include "catch_string_manip.h"
|
#include "catch_string_manip.h"
|
||||||
#include "catch_test_case_info.h"
|
#include "catch_test_case_info.h"
|
||||||
|
|
||||||
@@ -105,7 +106,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
|
std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
|
||||||
std::string className = classOrQualifiedMethodName;
|
std::string className(classOrQualifiedMethodName);
|
||||||
if( startsWith( className, '&' ) )
|
if( startsWith( className, '&' ) )
|
||||||
{
|
{
|
||||||
std::size_t lastColons = className.rfind( "::" );
|
std::size_t lastColons = className.rfind( "::" );
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
#include "catch_test_case_tracker.h"
|
#include "catch_test_case_tracker.h"
|
||||||
|
|
||||||
#include "catch_enforce.h"
|
#include "catch_enforce.h"
|
||||||
|
#include "catch_string_manip.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
@@ -174,7 +175,8 @@ namespace TestCaseTracking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
|
SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
|
||||||
: TrackerBase( nameAndLocation, ctx, parent )
|
: TrackerBase( nameAndLocation, ctx, parent ),
|
||||||
|
m_trimmed_name(trim(nameAndLocation.name))
|
||||||
{
|
{
|
||||||
if( parent ) {
|
if( parent ) {
|
||||||
while( !parent->isSectionTracker() )
|
while( !parent->isSectionTracker() )
|
||||||
@@ -188,12 +190,11 @@ namespace TestCaseTracking {
|
|||||||
bool SectionTracker::isComplete() const {
|
bool SectionTracker::isComplete() const {
|
||||||
bool complete = true;
|
bool complete = true;
|
||||||
|
|
||||||
if ((m_filters.empty() || m_filters[0] == "") ||
|
if ((m_filters.empty() || m_filters[0] == "")
|
||||||
std::find(m_filters.begin(), m_filters.end(),
|
|| std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
|
||||||
m_nameAndLocation.name) != m_filters.end())
|
|
||||||
complete = TrackerBase::isComplete();
|
complete = TrackerBase::isComplete();
|
||||||
|
}
|
||||||
return complete;
|
return complete;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SectionTracker::isSectionTracker() const { return true; }
|
bool SectionTracker::isSectionTracker() const { return true; }
|
||||||
@@ -217,12 +218,13 @@ namespace TestCaseTracking {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SectionTracker::tryOpen() {
|
void SectionTracker::tryOpen() {
|
||||||
if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) )
|
if( !isComplete() )
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
|
void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
|
||||||
if( !filters.empty() ) {
|
if( !filters.empty() ) {
|
||||||
|
m_filters.reserve( m_filters.size() + filters.size() + 2 );
|
||||||
m_filters.push_back(""); // Root - should never be consulted
|
m_filters.push_back(""); // Root - should never be consulted
|
||||||
m_filters.push_back(""); // Test Case - not a section filter
|
m_filters.push_back(""); // Test Case - not a section filter
|
||||||
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
|
m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
|
||||||
@@ -230,7 +232,7 @@ namespace TestCaseTracking {
|
|||||||
}
|
}
|
||||||
void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
|
void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
|
||||||
if( filters.size() > 1 )
|
if( filters.size() > 1 )
|
||||||
m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
|
m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace TestCaseTracking
|
} // namespace TestCaseTracking
|
||||||
|
@@ -133,6 +133,7 @@ namespace TestCaseTracking {
|
|||||||
|
|
||||||
class SectionTracker : public TrackerBase {
|
class SectionTracker : public TrackerBase {
|
||||||
std::vector<std::string> m_filters;
|
std::vector<std::string> m_filters;
|
||||||
|
std::string m_trimmed_name;
|
||||||
public:
|
public:
|
||||||
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
|
SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
|
||||||
|
|
||||||
|
@@ -12,7 +12,6 @@
|
|||||||
#include "catch_interfaces_testcase.h"
|
#include "catch_interfaces_testcase.h"
|
||||||
#include "catch_compiler_capabilities.h"
|
#include "catch_compiler_capabilities.h"
|
||||||
#include "catch_stringref.h"
|
#include "catch_stringref.h"
|
||||||
#include "catch_type_traits.hpp"
|
|
||||||
#include "catch_preprocessor.hpp"
|
#include "catch_preprocessor.hpp"
|
||||||
#include "catch_meta.hpp"
|
#include "catch_meta.hpp"
|
||||||
|
|
||||||
@@ -106,21 +105,24 @@ struct AutoReg : NonCopyable {
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
|
#define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
|
||||||
static void TestName(); \
|
static void TestName(); \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
|
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
static void TestName()
|
static void TestName()
|
||||||
#define INTERNAL_CATCH_TESTCASE( ... ) \
|
#define INTERNAL_CATCH_TESTCASE( ... ) \
|
||||||
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
|
INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
|
#define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
|
namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
|
#define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
namespace{ \
|
namespace{ \
|
||||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
|
||||||
@@ -128,21 +130,24 @@ struct AutoReg : NonCopyable {
|
|||||||
}; \
|
}; \
|
||||||
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
|
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
|
||||||
} \
|
} \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
void TestName::test()
|
void TestName::test()
|
||||||
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
|
#define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
|
||||||
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
|
INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
|
#define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
|
Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
|
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
|
INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
|
||||||
namespace {\
|
namespace {\
|
||||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
||||||
@@ -164,8 +169,7 @@ struct AutoReg : NonCopyable {
|
|||||||
}();\
|
}();\
|
||||||
}\
|
}\
|
||||||
}\
|
}\
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
|
||||||
INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||||
@@ -185,8 +189,10 @@ struct AutoReg : NonCopyable {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
|
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
template<typename TestType> static void TestFuncName(); \
|
template<typename TestType> static void TestFuncName(); \
|
||||||
namespace {\
|
namespace {\
|
||||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) { \
|
||||||
@@ -204,15 +210,14 @@ struct AutoReg : NonCopyable {
|
|||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||||
using TestInit = decltype(create<TestName, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{})); \
|
using TestInit = typename create<TestName, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type; \
|
||||||
TestInit t; \
|
TestInit t; \
|
||||||
t.reg_tests(); \
|
t.reg_tests(); \
|
||||||
return 0; \
|
return 0; \
|
||||||
}(); \
|
}(); \
|
||||||
} \
|
} \
|
||||||
} \
|
} \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
static void TestFuncName()
|
static void TestFuncName()
|
||||||
|
|
||||||
@@ -233,7 +238,9 @@ struct AutoReg : NonCopyable {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
|
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
template<typename TestType> static void TestFunc(); \
|
template<typename TestType> static void TestFunc(); \
|
||||||
namespace {\
|
namespace {\
|
||||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
|
||||||
@@ -247,13 +254,13 @@ struct AutoReg : NonCopyable {
|
|||||||
} \
|
} \
|
||||||
};\
|
};\
|
||||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
|
||||||
using TestInit = decltype(convert<TestName>(std::declval<TmplList>())); \
|
using TestInit = typename convert<TestName, TmplList>::type; \
|
||||||
TestInit t; \
|
TestInit t; \
|
||||||
t.reg_tests(); \
|
t.reg_tests(); \
|
||||||
return 0; \
|
return 0; \
|
||||||
}(); \
|
}(); \
|
||||||
}}\
|
}}\
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
static void TestFunc()
|
static void TestFunc()
|
||||||
|
|
||||||
@@ -262,8 +269,10 @@ struct AutoReg : NonCopyable {
|
|||||||
|
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
|
#define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
namespace {\
|
namespace {\
|
||||||
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
|
namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
|
||||||
INTERNAL_CATCH_TYPE_GEN\
|
INTERNAL_CATCH_TYPE_GEN\
|
||||||
@@ -285,8 +294,7 @@ struct AutoReg : NonCopyable {
|
|||||||
}();\
|
}();\
|
||||||
}\
|
}\
|
||||||
}\
|
}\
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\
|
|
||||||
INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
|
||||||
|
|
||||||
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
|
||||||
@@ -306,8 +314,10 @@ struct AutoReg : NonCopyable {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
|
#define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||||
void test();\
|
void test();\
|
||||||
@@ -328,15 +338,14 @@ struct AutoReg : NonCopyable {
|
|||||||
}\
|
}\
|
||||||
};\
|
};\
|
||||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||||
using TestInit = decltype(create<TestNameClass, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{}));\
|
using TestInit = typename create<TestNameClass, decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>()), TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>>::type;\
|
||||||
TestInit t;\
|
TestInit t;\
|
||||||
t.reg_tests();\
|
t.reg_tests();\
|
||||||
return 0;\
|
return 0;\
|
||||||
}(); \
|
}(); \
|
||||||
}\
|
}\
|
||||||
}\
|
}\
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
|
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
void TestName<TestType>::test()
|
void TestName<TestType>::test()
|
||||||
|
|
||||||
@@ -357,7 +366,9 @@ struct AutoReg : NonCopyable {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
|
#define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
|
||||||
|
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
|
||||||
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
|
||||||
|
CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
|
||||||
void test();\
|
void test();\
|
||||||
@@ -374,13 +385,13 @@ struct AutoReg : NonCopyable {
|
|||||||
}\
|
}\
|
||||||
};\
|
};\
|
||||||
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
|
||||||
using TestInit = decltype(convert<TestNameClass>(std::declval<TmplList>()));\
|
using TestInit = typename convert<TestNameClass, TmplList>::type;\
|
||||||
TestInit t;\
|
TestInit t;\
|
||||||
t.reg_tests();\
|
t.reg_tests();\
|
||||||
return 0;\
|
return 0;\
|
||||||
}(); \
|
}(); \
|
||||||
}}\
|
}}\
|
||||||
CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
|
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
|
||||||
template<typename TestType> \
|
template<typename TestType> \
|
||||||
void TestName<TestType>::test()
|
void TestName<TestType>::test()
|
||||||
|
|
||||||
|
@@ -33,7 +33,7 @@ namespace Catch {
|
|||||||
{}
|
{}
|
||||||
|
|
||||||
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
|
bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
|
||||||
return m_wildcardPattern.matches( toLower( testCase.name ) );
|
return m_wildcardPattern.matches( testCase.name );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -92,4 +92,8 @@ namespace Catch {
|
|||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const TestSpec::vectorStrings& TestSpec::getInvalidArgs() const{
|
||||||
|
return (m_invalidArgs);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -73,14 +73,16 @@ namespace Catch {
|
|||||||
std::vector<TestCase const*> tests;
|
std::vector<TestCase const*> tests;
|
||||||
};
|
};
|
||||||
using Matches = std::vector<FilterMatch>;
|
using Matches = std::vector<FilterMatch>;
|
||||||
|
using vectorStrings = std::vector<std::string>;
|
||||||
|
|
||||||
bool hasFilters() const;
|
bool hasFilters() const;
|
||||||
bool matches( TestCaseInfo const& testCase ) const;
|
bool matches( TestCaseInfo const& testCase ) const;
|
||||||
Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
|
Matches matchesByFilter( std::vector<TestCase> const& testCases, IConfig const& config ) const;
|
||||||
|
const vectorStrings & getInvalidArgs() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Filter> m_filters;
|
std::vector<Filter> m_filters;
|
||||||
|
std::vector<std::string> m_invalidArgs;
|
||||||
friend class TestSpecParser;
|
friend class TestSpecParser;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "catch_test_spec_parser.h"
|
#include "catch_test_spec_parser.h"
|
||||||
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
|
TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
|
||||||
@@ -18,8 +19,14 @@ namespace Catch {
|
|||||||
m_escapeChars.clear();
|
m_escapeChars.clear();
|
||||||
m_substring.reserve(m_arg.size());
|
m_substring.reserve(m_arg.size());
|
||||||
m_patternName.reserve(m_arg.size());
|
m_patternName.reserve(m_arg.size());
|
||||||
|
m_realPatternPos = 0;
|
||||||
|
|
||||||
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
|
for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
|
||||||
visitChar( m_arg[m_pos] );
|
//if visitChar fails
|
||||||
|
if( !visitChar( m_arg[m_pos] ) ){
|
||||||
|
m_testSpec.m_invalidArgs.push_back(arg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
endMode();
|
endMode();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -27,35 +34,41 @@ namespace Catch {
|
|||||||
addFilter();
|
addFilter();
|
||||||
return m_testSpec;
|
return m_testSpec;
|
||||||
}
|
}
|
||||||
void TestSpecParser::visitChar( char c ) {
|
bool TestSpecParser::visitChar( char c ) {
|
||||||
if( c == ',' ) {
|
if( (m_mode != EscapedName) && (c == '\\') ) {
|
||||||
endMode();
|
escape();
|
||||||
addFilter();
|
addCharToPattern(c);
|
||||||
return;
|
return true;
|
||||||
|
}else if((m_mode != EscapedName) && (c == ',') ) {
|
||||||
|
return separate();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch( m_mode ) {
|
switch( m_mode ) {
|
||||||
case None:
|
case None:
|
||||||
if( processNoneChar( c ) )
|
if( processNoneChar( c ) )
|
||||||
return;
|
return true;
|
||||||
break;
|
break;
|
||||||
case Name:
|
case Name:
|
||||||
processNameChar( c );
|
processNameChar( c );
|
||||||
break;
|
break;
|
||||||
case EscapedName:
|
case EscapedName:
|
||||||
endMode();
|
endMode();
|
||||||
break;
|
addCharToPattern(c);
|
||||||
|
return true;
|
||||||
default:
|
default:
|
||||||
case Tag:
|
case Tag:
|
||||||
case QuotedName:
|
case QuotedName:
|
||||||
if( processOtherChar( c ) )
|
if( processOtherChar( c ) )
|
||||||
return;
|
return true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_substring += c;
|
m_substring += c;
|
||||||
if( !isControlChar( c ) )
|
if( !isControlChar( c ) ) {
|
||||||
m_patternName += c;
|
m_patternName += c;
|
||||||
|
m_realPatternPos++;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// Two of the processing methods return true to signal the caller to return
|
// Two of the processing methods return true to signal the caller to return
|
||||||
// without adding the given character to the current pattern strings
|
// without adding the given character to the current pattern strings
|
||||||
@@ -72,9 +85,6 @@ namespace Catch {
|
|||||||
case '"':
|
case '"':
|
||||||
startNewMode( QuotedName );
|
startNewMode( QuotedName );
|
||||||
return false;
|
return false;
|
||||||
case '\\':
|
|
||||||
escape();
|
|
||||||
return true;
|
|
||||||
default:
|
default:
|
||||||
startNewMode( Name );
|
startNewMode( Name );
|
||||||
return false;
|
return false;
|
||||||
@@ -103,19 +113,21 @@ namespace Catch {
|
|||||||
switch( m_mode ) {
|
switch( m_mode ) {
|
||||||
case Name:
|
case Name:
|
||||||
case QuotedName:
|
case QuotedName:
|
||||||
return addPattern<TestSpec::NamePattern>();
|
return addNamePattern();
|
||||||
case Tag:
|
case Tag:
|
||||||
return addPattern<TestSpec::TagPattern>();
|
return addTagPattern();
|
||||||
case EscapedName:
|
case EscapedName:
|
||||||
return startNewMode( Name );
|
revertBackToLastMode();
|
||||||
|
return;
|
||||||
case None:
|
case None:
|
||||||
default:
|
default:
|
||||||
return startNewMode( None );
|
return startNewMode( None );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void TestSpecParser::escape() {
|
void TestSpecParser::escape() {
|
||||||
|
saveLastMode();
|
||||||
m_mode = EscapedName;
|
m_mode = EscapedName;
|
||||||
m_escapeChars.push_back( m_pos );
|
m_escapeChars.push_back(m_realPatternPos);
|
||||||
}
|
}
|
||||||
bool TestSpecParser::isControlChar( char c ) const {
|
bool TestSpecParser::isControlChar( char c ) const {
|
||||||
switch( m_mode ) {
|
switch( m_mode ) {
|
||||||
@@ -141,6 +153,84 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestSpecParser::saveLastMode() {
|
||||||
|
lastMode = m_mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSpecParser::revertBackToLastMode() {
|
||||||
|
m_mode = lastMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TestSpecParser::separate() {
|
||||||
|
if( (m_mode==QuotedName) || (m_mode==Tag) ){
|
||||||
|
//invalid argument, signal failure to previous scope.
|
||||||
|
m_mode = None;
|
||||||
|
m_pos = m_arg.size();
|
||||||
|
m_substring.clear();
|
||||||
|
m_patternName.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
endMode();
|
||||||
|
addFilter();
|
||||||
|
return true; //success
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TestSpecParser::preprocessPattern() {
|
||||||
|
std::string token = m_patternName;
|
||||||
|
for (std::size_t i = 0; i < m_escapeChars.size(); ++i)
|
||||||
|
token = token.substr(0, m_escapeChars[i] - i) + token.substr(m_escapeChars[i] - i + 1);
|
||||||
|
m_escapeChars.clear();
|
||||||
|
if (startsWith(token, "exclude:")) {
|
||||||
|
m_exclusion = true;
|
||||||
|
token = token.substr(8);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_patternName.clear();
|
||||||
|
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSpecParser::addNamePattern() {
|
||||||
|
auto token = preprocessPattern();
|
||||||
|
|
||||||
|
if (!token.empty()) {
|
||||||
|
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::NamePattern>(token, m_substring);
|
||||||
|
if (m_exclusion)
|
||||||
|
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
|
||||||
|
m_currentFilter.m_patterns.push_back(pattern);
|
||||||
|
}
|
||||||
|
m_substring.clear();
|
||||||
|
m_exclusion = false;
|
||||||
|
m_mode = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestSpecParser::addTagPattern() {
|
||||||
|
auto token = preprocessPattern();
|
||||||
|
|
||||||
|
if (!token.empty()) {
|
||||||
|
// If the tag pattern is the "hide and tag" shorthand (e.g. [.foo])
|
||||||
|
// we have to create a separate hide tag and shorten the real one
|
||||||
|
if (token.size() > 1 && token[0] == '.') {
|
||||||
|
token.erase(token.begin());
|
||||||
|
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(".", m_substring);
|
||||||
|
if (m_exclusion) {
|
||||||
|
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
|
||||||
|
}
|
||||||
|
m_currentFilter.m_patterns.push_back(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
TestSpec::PatternPtr pattern = std::make_shared<TestSpec::TagPattern>(token, m_substring);
|
||||||
|
|
||||||
|
if (m_exclusion) {
|
||||||
|
pattern = std::make_shared<TestSpec::ExcludedPattern>(pattern);
|
||||||
|
}
|
||||||
|
m_currentFilter.m_patterns.push_back(pattern);
|
||||||
|
}
|
||||||
|
m_substring.clear();
|
||||||
|
m_exclusion = false;
|
||||||
|
m_mode = None;
|
||||||
|
}
|
||||||
|
|
||||||
TestSpec parseTestSpec( std::string const& arg ) {
|
TestSpec parseTestSpec( std::string const& arg ) {
|
||||||
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
|
return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
|
||||||
}
|
}
|
||||||
|
@@ -22,8 +22,10 @@ namespace Catch {
|
|||||||
class TestSpecParser {
|
class TestSpecParser {
|
||||||
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
|
enum Mode{ None, Name, QuotedName, Tag, EscapedName };
|
||||||
Mode m_mode = None;
|
Mode m_mode = None;
|
||||||
|
Mode lastMode = None;
|
||||||
bool m_exclusion = false;
|
bool m_exclusion = false;
|
||||||
std::size_t m_pos = 0;
|
std::size_t m_pos = 0;
|
||||||
|
std::size_t m_realPatternPos = 0;
|
||||||
std::string m_arg;
|
std::string m_arg;
|
||||||
std::string m_substring;
|
std::string m_substring;
|
||||||
std::string m_patternName;
|
std::string m_patternName;
|
||||||
@@ -39,7 +41,7 @@ namespace Catch {
|
|||||||
TestSpec testSpec();
|
TestSpec testSpec();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void visitChar( char c );
|
bool visitChar( char c );
|
||||||
void startNewMode( Mode mode );
|
void startNewMode( Mode mode );
|
||||||
bool processNoneChar( char c );
|
bool processNoneChar( char c );
|
||||||
void processNameChar( char c );
|
void processNameChar( char c );
|
||||||
@@ -47,30 +49,24 @@ namespace Catch {
|
|||||||
void endMode();
|
void endMode();
|
||||||
void escape();
|
void escape();
|
||||||
bool isControlChar( char c ) const;
|
bool isControlChar( char c ) const;
|
||||||
|
void saveLastMode();
|
||||||
template<typename T>
|
void revertBackToLastMode();
|
||||||
void addPattern() {
|
|
||||||
std::string token = m_patternName;
|
|
||||||
for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
|
|
||||||
token = token.substr( 0, m_escapeChars[i] - i ) + token.substr( m_escapeChars[i] -i +1 );
|
|
||||||
m_escapeChars.clear();
|
|
||||||
if( startsWith( token, "exclude:" ) ) {
|
|
||||||
m_exclusion = true;
|
|
||||||
token = token.substr( 8 );
|
|
||||||
}
|
|
||||||
if( !token.empty() ) {
|
|
||||||
TestSpec::PatternPtr pattern = std::make_shared<T>( token, m_substring );
|
|
||||||
if( m_exclusion )
|
|
||||||
pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
|
|
||||||
m_currentFilter.m_patterns.push_back( pattern );
|
|
||||||
}
|
|
||||||
m_substring.clear();
|
|
||||||
m_patternName.clear();
|
|
||||||
m_exclusion = false;
|
|
||||||
m_mode = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
void addFilter();
|
void addFilter();
|
||||||
|
bool separate();
|
||||||
|
|
||||||
|
// Handles common preprocessing of the pattern for name/tag patterns
|
||||||
|
std::string preprocessPattern();
|
||||||
|
// Adds the current pattern as a test name
|
||||||
|
void addNamePattern();
|
||||||
|
// Adds the current pattern as a tag
|
||||||
|
void addTagPattern();
|
||||||
|
|
||||||
|
inline void addCharToPattern(char c) {
|
||||||
|
m_substring += c;
|
||||||
|
m_patternName += c;
|
||||||
|
m_realPatternPos++;
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
TestSpec parseTestSpec( std::string const& arg );
|
TestSpec parseTestSpec( std::string const& arg );
|
||||||
|
|
||||||
|
@@ -38,13 +38,11 @@ namespace Detail {
|
|||||||
enum Arch { Big, Little };
|
enum Arch { Big, Little };
|
||||||
|
|
||||||
static Arch which() {
|
static Arch which() {
|
||||||
union _{
|
int one = 1;
|
||||||
int asInt;
|
// If the lowest byte we read is non-zero, we can assume
|
||||||
char asChar[sizeof (int)];
|
// that little endian format is used.
|
||||||
} u;
|
auto value = *reinterpret_cast<char*>(&one);
|
||||||
|
return value ? Little : Big;
|
||||||
u.asInt = 1;
|
|
||||||
return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@@ -44,9 +44,9 @@ namespace Catch {
|
|||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class IsStreamInsertable {
|
class IsStreamInsertable {
|
||||||
template<typename SS, typename TT>
|
template<typename Stream, typename U>
|
||||||
static auto test(int)
|
static auto test(int)
|
||||||
-> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
|
-> decltype(std::declval<Stream&>() << std::declval<U>(), std::true_type());
|
||||||
|
|
||||||
template<typename, typename>
|
template<typename, typename>
|
||||||
static auto test(...)->std::false_type;
|
static auto test(...)->std::false_type;
|
||||||
@@ -654,7 +654,7 @@ namespace Catch { \
|
|||||||
template<> struct StringMaker<enumName> { \
|
template<> struct StringMaker<enumName> { \
|
||||||
static std::string convert( enumName value ) { \
|
static std::string convert( enumName value ) { \
|
||||||
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
|
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
|
||||||
return enumInfo.lookup( static_cast<int>( value ) ); \
|
return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
|
||||||
} \
|
} \
|
||||||
}; \
|
}; \
|
||||||
}
|
}
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Created by Jozef on 12/11/2018.
|
|
||||||
* Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
|
|
||||||
*
|
|
||||||
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
|
||||||
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED
|
|
||||||
#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED
|
|
||||||
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Catch{
|
|
||||||
|
|
||||||
#ifdef CATCH_CPP17_OR_GREATER
|
|
||||||
template <typename...>
|
|
||||||
inline constexpr auto is_unique = std::true_type{};
|
|
||||||
|
|
||||||
template <typename T, typename... Rest>
|
|
||||||
inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
|
|
||||||
(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
|
|
||||||
>{};
|
|
||||||
#else
|
|
||||||
|
|
||||||
template <typename...>
|
|
||||||
struct is_unique : std::true_type{};
|
|
||||||
|
|
||||||
template <typename T0, typename T1, typename... Rest>
|
|
||||||
struct is_unique<T0, T1, Rest...> : std::integral_constant
|
|
||||||
<bool,
|
|
||||||
!std::is_same<T0, T1>::value
|
|
||||||
&& is_unique<T0, Rest...>::value
|
|
||||||
&& is_unique<T1, Rest...>::value
|
|
||||||
>{};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED
|
|
@@ -37,7 +37,7 @@ namespace Catch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Version const& libraryVersion() {
|
Version const& libraryVersion() {
|
||||||
static Version version( 2, 9, 2, "", 0 );
|
static Version version( 2, 11, 0, "", 0 );
|
||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -9,14 +9,12 @@
|
|||||||
#include "catch_enforce.h"
|
#include "catch_enforce.h"
|
||||||
#include "catch_string_manip.h"
|
#include "catch_string_manip.h"
|
||||||
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
WildcardPattern::WildcardPattern( std::string const& pattern,
|
WildcardPattern::WildcardPattern( std::string const& pattern,
|
||||||
CaseSensitive::Choice caseSensitivity )
|
CaseSensitive::Choice caseSensitivity )
|
||||||
: m_caseSensitivity( caseSensitivity ),
|
: m_caseSensitivity( caseSensitivity ),
|
||||||
m_pattern( adjustCase( pattern ) )
|
m_pattern( normaliseString( pattern ) )
|
||||||
{
|
{
|
||||||
if( startsWith( m_pattern, '*' ) ) {
|
if( startsWith( m_pattern, '*' ) ) {
|
||||||
m_pattern = m_pattern.substr( 1 );
|
m_pattern = m_pattern.substr( 1 );
|
||||||
@@ -31,19 +29,19 @@ namespace Catch {
|
|||||||
bool WildcardPattern::matches( std::string const& str ) const {
|
bool WildcardPattern::matches( std::string const& str ) const {
|
||||||
switch( m_wildcard ) {
|
switch( m_wildcard ) {
|
||||||
case NoWildcard:
|
case NoWildcard:
|
||||||
return m_pattern == adjustCase( str );
|
return m_pattern == normaliseString( str );
|
||||||
case WildcardAtStart:
|
case WildcardAtStart:
|
||||||
return endsWith( adjustCase( str ), m_pattern );
|
return endsWith( normaliseString( str ), m_pattern );
|
||||||
case WildcardAtEnd:
|
case WildcardAtEnd:
|
||||||
return startsWith( adjustCase( str ), m_pattern );
|
return startsWith( normaliseString( str ), m_pattern );
|
||||||
case WildcardAtBothEnds:
|
case WildcardAtBothEnds:
|
||||||
return contains( adjustCase( str ), m_pattern );
|
return contains( normaliseString( str ), m_pattern );
|
||||||
default:
|
default:
|
||||||
CATCH_INTERNAL_ERROR( "Unknown enum" );
|
CATCH_INTERNAL_ERROR( "Unknown enum" );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string WildcardPattern::adjustCase( std::string const& str ) const {
|
std::string WildcardPattern::normaliseString( std::string const& str ) const {
|
||||||
return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
|
return trim( m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,7 @@ namespace Catch
|
|||||||
virtual bool matches( std::string const& str ) const;
|
virtual bool matches( std::string const& str ) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string adjustCase( std::string const& str ) const;
|
std::string normaliseString( std::string const& str ) const;
|
||||||
CaseSensitive::Choice m_caseSensitivity;
|
CaseSensitive::Choice m_caseSensitivity;
|
||||||
WildcardPosition m_wildcard = NoWildcard;
|
WildcardPosition m_wildcard = NoWildcard;
|
||||||
std::string m_pattern;
|
std::string m_pattern;
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
#include "catch_enforce.h"
|
#include "catch_enforce.h"
|
||||||
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
using uchar = unsigned char;
|
using uchar = unsigned char;
|
||||||
|
|
||||||
@@ -51,8 +52,31 @@ namespace {
|
|||||||
os.flags(f);
|
os.flags(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool shouldNewline(XmlFormatting fmt) {
|
||||||
|
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Newline));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool shouldIndent(XmlFormatting fmt) {
|
||||||
|
return !!(static_cast<std::underlying_type<XmlFormatting>::type>(fmt & XmlFormatting::Indent));
|
||||||
|
}
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
|
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs) {
|
||||||
|
return static_cast<XmlFormatting>(
|
||||||
|
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) |
|
||||||
|
static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs) {
|
||||||
|
return static_cast<XmlFormatting>(
|
||||||
|
static_cast<std::underlying_type<XmlFormatting>::type>(lhs) &
|
||||||
|
static_cast<std::underlying_type<XmlFormatting>::type>(rhs)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
|
XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
|
||||||
: m_str( str ),
|
: m_str( str ),
|
||||||
m_forWhat( forWhat )
|
m_forWhat( forWhat )
|
||||||
@@ -157,13 +181,17 @@ namespace {
|
|||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
|
XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer, XmlFormatting fmt )
|
||||||
: m_writer( writer )
|
: m_writer( writer ),
|
||||||
|
m_fmt(fmt)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
|
XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
|
||||||
: m_writer( other.m_writer ){
|
: m_writer( other.m_writer ),
|
||||||
|
m_fmt(other.m_fmt)
|
||||||
|
{
|
||||||
other.m_writer = nullptr;
|
other.m_writer = nullptr;
|
||||||
|
other.m_fmt = XmlFormatting::None;
|
||||||
}
|
}
|
||||||
XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
|
XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
|
||||||
if ( m_writer ) {
|
if ( m_writer ) {
|
||||||
@@ -171,17 +199,20 @@ namespace {
|
|||||||
}
|
}
|
||||||
m_writer = other.m_writer;
|
m_writer = other.m_writer;
|
||||||
other.m_writer = nullptr;
|
other.m_writer = nullptr;
|
||||||
|
m_fmt = other.m_fmt;
|
||||||
|
other.m_fmt = XmlFormatting::None;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
XmlWriter::ScopedElement::~ScopedElement() {
|
XmlWriter::ScopedElement::~ScopedElement() {
|
||||||
if( m_writer )
|
if (m_writer) {
|
||||||
m_writer->endElement();
|
m_writer->endElement(m_fmt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
|
XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
|
||||||
m_writer->writeText( text, indent );
|
m_writer->writeText( text, fmt );
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,37 +222,47 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter::~XmlWriter() {
|
XmlWriter::~XmlWriter() {
|
||||||
while( !m_tags.empty() )
|
while (!m_tags.empty()) {
|
||||||
endElement();
|
endElement();
|
||||||
}
|
}
|
||||||
|
newlineIfNecessary();
|
||||||
|
}
|
||||||
|
|
||||||
XmlWriter& XmlWriter::startElement( std::string const& name ) {
|
XmlWriter& XmlWriter::startElement( std::string const& name, XmlFormatting fmt ) {
|
||||||
ensureTagClosed();
|
ensureTagClosed();
|
||||||
newlineIfNecessary();
|
newlineIfNecessary();
|
||||||
m_os << m_indent << '<' << name;
|
if (shouldIndent(fmt)) {
|
||||||
m_tags.push_back( name );
|
m_os << m_indent;
|
||||||
m_indent += " ";
|
m_indent += " ";
|
||||||
|
}
|
||||||
|
m_os << '<' << name;
|
||||||
|
m_tags.push_back( name );
|
||||||
m_tagIsOpen = true;
|
m_tagIsOpen = true;
|
||||||
|
applyFormatting(fmt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
|
XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name, XmlFormatting fmt ) {
|
||||||
ScopedElement scoped( this );
|
ScopedElement scoped( this, fmt );
|
||||||
startElement( name );
|
startElement( name, fmt );
|
||||||
return scoped;
|
return scoped;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter& XmlWriter::endElement() {
|
XmlWriter& XmlWriter::endElement(XmlFormatting fmt) {
|
||||||
newlineIfNecessary();
|
m_indent = m_indent.substr(0, m_indent.size() - 2);
|
||||||
m_indent = m_indent.substr( 0, m_indent.size()-2 );
|
|
||||||
if( m_tagIsOpen ) {
|
if( m_tagIsOpen ) {
|
||||||
m_os << "/>";
|
m_os << "/>";
|
||||||
m_tagIsOpen = false;
|
m_tagIsOpen = false;
|
||||||
|
} else {
|
||||||
|
newlineIfNecessary();
|
||||||
|
if (shouldIndent(fmt)) {
|
||||||
|
m_os << m_indent;
|
||||||
}
|
}
|
||||||
else {
|
m_os << "</" << m_tags.back() << ">";
|
||||||
m_os << m_indent << "</" << m_tags.back() << ">";
|
|
||||||
}
|
}
|
||||||
m_os << std::endl;
|
m_os << std::flush;
|
||||||
|
applyFormatting(fmt);
|
||||||
m_tags.pop_back();
|
m_tags.pop_back();
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@@ -237,22 +278,26 @@ namespace {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
|
XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
|
||||||
if( !text.empty() ){
|
if( !text.empty() ){
|
||||||
bool tagWasOpen = m_tagIsOpen;
|
bool tagWasOpen = m_tagIsOpen;
|
||||||
ensureTagClosed();
|
ensureTagClosed();
|
||||||
if( tagWasOpen && indent )
|
if (tagWasOpen && shouldIndent(fmt)) {
|
||||||
m_os << m_indent;
|
m_os << m_indent;
|
||||||
|
}
|
||||||
m_os << XmlEncode( text );
|
m_os << XmlEncode( text );
|
||||||
m_needsNewline = true;
|
applyFormatting(fmt);
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter& XmlWriter::writeComment( std::string const& text ) {
|
XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
|
||||||
ensureTagClosed();
|
ensureTagClosed();
|
||||||
m_os << m_indent << "<!--" << text << "-->";
|
if (shouldIndent(fmt)) {
|
||||||
m_needsNewline = true;
|
m_os << m_indent;
|
||||||
|
}
|
||||||
|
m_os << "<!--" << text << "-->";
|
||||||
|
applyFormatting(fmt);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,11 +313,16 @@ namespace {
|
|||||||
|
|
||||||
void XmlWriter::ensureTagClosed() {
|
void XmlWriter::ensureTagClosed() {
|
||||||
if( m_tagIsOpen ) {
|
if( m_tagIsOpen ) {
|
||||||
m_os << ">" << std::endl;
|
m_os << '>' << std::flush;
|
||||||
|
newlineIfNecessary();
|
||||||
m_tagIsOpen = false;
|
m_tagIsOpen = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void XmlWriter::applyFormatting(XmlFormatting fmt) {
|
||||||
|
m_needsNewline = shouldNewline(fmt);
|
||||||
|
}
|
||||||
|
|
||||||
void XmlWriter::writeDeclaration() {
|
void XmlWriter::writeDeclaration() {
|
||||||
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
||||||
}
|
}
|
||||||
|
@@ -14,6 +14,14 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
enum class XmlFormatting {
|
||||||
|
None = 0x00,
|
||||||
|
Indent = 0x01,
|
||||||
|
Newline = 0x02,
|
||||||
|
};
|
||||||
|
|
||||||
|
XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
|
||||||
|
XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
|
||||||
|
|
||||||
class XmlEncode {
|
class XmlEncode {
|
||||||
public:
|
public:
|
||||||
@@ -35,14 +43,14 @@ namespace Catch {
|
|||||||
|
|
||||||
class ScopedElement {
|
class ScopedElement {
|
||||||
public:
|
public:
|
||||||
ScopedElement( XmlWriter* writer );
|
ScopedElement( XmlWriter* writer, XmlFormatting fmt );
|
||||||
|
|
||||||
ScopedElement( ScopedElement&& other ) noexcept;
|
ScopedElement( ScopedElement&& other ) noexcept;
|
||||||
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
ScopedElement& operator=( ScopedElement&& other ) noexcept;
|
||||||
|
|
||||||
~ScopedElement();
|
~ScopedElement();
|
||||||
|
|
||||||
ScopedElement& writeText( std::string const& text, bool indent = true );
|
ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
|
ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
|
||||||
@@ -52,6 +60,7 @@ namespace Catch {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
mutable XmlWriter* m_writer = nullptr;
|
mutable XmlWriter* m_writer = nullptr;
|
||||||
|
XmlFormatting m_fmt;
|
||||||
};
|
};
|
||||||
|
|
||||||
XmlWriter( std::ostream& os = Catch::cout() );
|
XmlWriter( std::ostream& os = Catch::cout() );
|
||||||
@@ -60,11 +69,11 @@ namespace Catch {
|
|||||||
XmlWriter( XmlWriter const& ) = delete;
|
XmlWriter( XmlWriter const& ) = delete;
|
||||||
XmlWriter& operator=( XmlWriter const& ) = delete;
|
XmlWriter& operator=( XmlWriter const& ) = delete;
|
||||||
|
|
||||||
XmlWriter& startElement( std::string const& name );
|
XmlWriter& startElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
|
||||||
|
|
||||||
ScopedElement scopedElement( std::string const& name );
|
ScopedElement scopedElement( std::string const& name, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
|
||||||
|
|
||||||
XmlWriter& endElement();
|
XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
|
||||||
|
|
||||||
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
|
XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
|
||||||
|
|
||||||
@@ -77,9 +86,9 @@ namespace Catch {
|
|||||||
return writeAttribute( name, rss.str() );
|
return writeAttribute( name, rss.str() );
|
||||||
}
|
}
|
||||||
|
|
||||||
XmlWriter& writeText( std::string const& text, bool indent = true );
|
XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
|
||||||
|
|
||||||
XmlWriter& writeComment( std::string const& text );
|
XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
|
||||||
|
|
||||||
void writeStylesheetRef( std::string const& url );
|
void writeStylesheetRef( std::string const& url );
|
||||||
|
|
||||||
@@ -89,6 +98,8 @@ namespace Catch {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
void applyFormatting(XmlFormatting fmt);
|
||||||
|
|
||||||
void writeDeclaration();
|
void writeDeclaration();
|
||||||
|
|
||||||
void newlineIfNecessary();
|
void newlineIfNecessary();
|
||||||
|
@@ -51,6 +51,8 @@ namespace Catch {
|
|||||||
|
|
||||||
void noMatchingTestCases(std::string const&) override {}
|
void noMatchingTestCases(std::string const&) override {}
|
||||||
|
|
||||||
|
void reportInvalidArguments(std::string const&) override {}
|
||||||
|
|
||||||
void testRunStarting(TestRunInfo const& _testRunInfo) override {
|
void testRunStarting(TestRunInfo const& _testRunInfo) override {
|
||||||
currentTestRunInfo = _testRunInfo;
|
currentTestRunInfo = _testRunInfo;
|
||||||
}
|
}
|
||||||
|
@@ -8,7 +8,7 @@
|
|||||||
#include "catch_reporter_compact.h"
|
#include "catch_reporter_compact.h"
|
||||||
|
|
||||||
#include "../internal/catch_reporter_registrars.hpp"
|
#include "../internal/catch_reporter_registrars.hpp"
|
||||||
#include "internal/catch_console_colour.h"
|
#include "../internal/catch_console_colour.h"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@
|
|||||||
#include "catch_reporter_console.h"
|
#include "catch_reporter_console.h"
|
||||||
|
|
||||||
#include "../internal/catch_reporter_registrars.hpp"
|
#include "../internal/catch_reporter_registrars.hpp"
|
||||||
#include "internal/catch_console_colour.h"
|
#include "../internal/catch_console_colour.h"
|
||||||
#include "../internal/catch_version.h"
|
#include "../internal/catch_version.h"
|
||||||
#include "../internal/catch_text.h"
|
#include "../internal/catch_text.h"
|
||||||
#include "../internal/catch_stringref.h"
|
#include "../internal/catch_stringref.h"
|
||||||
@@ -268,7 +268,7 @@ public:
|
|||||||
|
|
||||||
}
|
}
|
||||||
friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
|
friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
|
||||||
return os << duration.value() << " " << duration.unitsAsString();
|
return os << duration.value() << ' ' << duration.unitsAsString();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // end anon namespace
|
} // end anon namespace
|
||||||
@@ -300,9 +300,9 @@ public:
|
|||||||
headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
|
headerCols += Column(info.name).width(static_cast<std::size_t>(info.width - 2));
|
||||||
headerCols += spacer;
|
headerCols += spacer;
|
||||||
}
|
}
|
||||||
m_os << headerCols << "\n";
|
m_os << headerCols << '\n';
|
||||||
|
|
||||||
m_os << Catch::getLineOfChars<'-'>() << "\n";
|
m_os << Catch::getLineOfChars<'-'>() << '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void close() {
|
void close() {
|
||||||
@@ -321,30 +321,29 @@ public:
|
|||||||
|
|
||||||
friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
|
friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
|
||||||
auto colStr = tp.m_oss.str();
|
auto colStr = tp.m_oss.str();
|
||||||
// This takes account of utf8 encodings
|
const auto strSize = colStr.size();
|
||||||
auto strSize = Catch::StringRef(colStr).numberOfCharacters();
|
|
||||||
tp.m_oss.str("");
|
tp.m_oss.str("");
|
||||||
tp.open();
|
tp.open();
|
||||||
if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
|
if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
|
||||||
tp.m_currentColumn = -1;
|
tp.m_currentColumn = -1;
|
||||||
tp.m_os << "\n";
|
tp.m_os << '\n';
|
||||||
}
|
}
|
||||||
tp.m_currentColumn++;
|
tp.m_currentColumn++;
|
||||||
|
|
||||||
auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
|
auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
|
||||||
auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width))
|
auto padding = (strSize + 1 < static_cast<std::size_t>(colInfo.width))
|
||||||
? std::string(colInfo.width - (strSize + 2), ' ')
|
? std::string(colInfo.width - (strSize + 1), ' ')
|
||||||
: std::string();
|
: std::string();
|
||||||
if (colInfo.justification == ColumnInfo::Left)
|
if (colInfo.justification == ColumnInfo::Left)
|
||||||
tp.m_os << colStr << padding << " ";
|
tp.m_os << colStr << padding << ' ';
|
||||||
else
|
else
|
||||||
tp.m_os << padding << colStr << " ";
|
tp.m_os << padding << colStr << ' ';
|
||||||
return tp;
|
return tp;
|
||||||
}
|
}
|
||||||
|
|
||||||
friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
|
friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
|
||||||
if (tp.m_currentColumn > 0) {
|
if (tp.m_currentColumn > 0) {
|
||||||
tp.m_os << "\n";
|
tp.m_os << '\n';
|
||||||
tp.m_currentColumn = -1;
|
tp.m_currentColumn = -1;
|
||||||
}
|
}
|
||||||
return tp;
|
return tp;
|
||||||
@@ -354,12 +353,26 @@ public:
|
|||||||
ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
|
ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
|
||||||
: StreamingReporterBase(config),
|
: StreamingReporterBase(config),
|
||||||
m_tablePrinter(new TablePrinter(config.stream(),
|
m_tablePrinter(new TablePrinter(config.stream(),
|
||||||
|
[&config]() -> std::vector<ColumnInfo> {
|
||||||
|
if (config.fullConfig()->benchmarkNoAnalysis())
|
||||||
{
|
{
|
||||||
|
return{
|
||||||
|
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 43, ColumnInfo::Left },
|
||||||
|
{ " samples", 14, ColumnInfo::Right },
|
||||||
|
{ " iterations", 14, ColumnInfo::Right },
|
||||||
|
{ " mean", 14, ColumnInfo::Right }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return{
|
||||||
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
|
{ "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
|
||||||
{ "samples mean std dev", 14, ColumnInfo::Right },
|
{ "samples mean std dev", 14, ColumnInfo::Right },
|
||||||
{ "iterations low mean low std dev", 14, ColumnInfo::Right },
|
{ "iterations low mean low std dev", 14, ColumnInfo::Right },
|
||||||
{ "estimated high mean high std dev", 14, ColumnInfo::Right }
|
{ "estimated high mean high std dev", 14, ColumnInfo::Right }
|
||||||
})) {}
|
};
|
||||||
|
}
|
||||||
|
}())) {}
|
||||||
ConsoleReporter::~ConsoleReporter() = default;
|
ConsoleReporter::~ConsoleReporter() = default;
|
||||||
|
|
||||||
std::string ConsoleReporter::getDescription() {
|
std::string ConsoleReporter::getDescription() {
|
||||||
@@ -370,6 +383,10 @@ void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
|
|||||||
stream << "No test cases matched '" << spec << '\'' << std::endl;
|
stream << "No test cases matched '" << spec << '\'' << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConsoleReporter::reportInvalidArguments(std::string const&arg){
|
||||||
|
stream << "Invalid Filter: " << arg << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
|
void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
|
||||||
|
|
||||||
bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
|
bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
|
||||||
@@ -433,10 +450,17 @@ void ConsoleReporter::benchmarkPreparing(std::string const& name) {
|
|||||||
|
|
||||||
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
|
||||||
(*m_tablePrinter) << info.samples << ColumnBreak()
|
(*m_tablePrinter) << info.samples << ColumnBreak()
|
||||||
<< info.iterations << ColumnBreak()
|
<< info.iterations << ColumnBreak();
|
||||||
<< Duration(info.estimatedDuration) << ColumnBreak();
|
if (!m_config->benchmarkNoAnalysis())
|
||||||
|
(*m_tablePrinter) << Duration(info.estimatedDuration) << ColumnBreak();
|
||||||
}
|
}
|
||||||
void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
||||||
|
if (m_config->benchmarkNoAnalysis())
|
||||||
|
{
|
||||||
|
(*m_tablePrinter) << Duration(stats.mean.point.count()) << ColumnBreak();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
(*m_tablePrinter) << ColumnBreak()
|
(*m_tablePrinter) << ColumnBreak()
|
||||||
<< Duration(stats.mean.point.count()) << ColumnBreak()
|
<< Duration(stats.mean.point.count()) << ColumnBreak()
|
||||||
<< Duration(stats.mean.lower_bound.count()) << ColumnBreak()
|
<< Duration(stats.mean.lower_bound.count()) << ColumnBreak()
|
||||||
@@ -444,12 +468,13 @@ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
|
|||||||
<< Duration(stats.standardDeviation.point.count()) << ColumnBreak()
|
<< Duration(stats.standardDeviation.point.count()) << ColumnBreak()
|
||||||
<< Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
|
<< Duration(stats.standardDeviation.lower_bound.count()) << ColumnBreak()
|
||||||
<< Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
|
<< Duration(stats.standardDeviation.upper_bound.count()) << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak() << ColumnBreak();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleReporter::benchmarkFailed(std::string const& error) {
|
void ConsoleReporter::benchmarkFailed(std::string const& error) {
|
||||||
Colour colour(Colour::Red);
|
Colour colour(Colour::Red);
|
||||||
(*m_tablePrinter)
|
(*m_tablePrinter)
|
||||||
<< "Benchmark failed (" << error << ")"
|
<< "Benchmark failed (" << error << ')'
|
||||||
<< ColumnBreak() << RowBreak();
|
<< ColumnBreak() << RowBreak();
|
||||||
}
|
}
|
||||||
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
#endif // CATCH_CONFIG_ENABLE_BENCHMARKING
|
||||||
@@ -531,11 +556,10 @@ void ConsoleReporter::printTestCaseAndSectionHeader() {
|
|||||||
|
|
||||||
SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
|
SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
|
||||||
|
|
||||||
if (!lineInfo.empty()) {
|
|
||||||
stream << getLineOfChars<'-'>() << '\n';
|
stream << getLineOfChars<'-'>() << '\n';
|
||||||
Colour colourGuard(Colour::FileName);
|
Colour colourGuard(Colour::FileName);
|
||||||
stream << lineInfo << '\n';
|
stream << lineInfo << '\n';
|
||||||
}
|
|
||||||
stream << getLineOfChars<'.'>() << '\n' << std::endl;
|
stream << getLineOfChars<'.'>() << '\n' << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,6 +32,8 @@ namespace Catch {
|
|||||||
|
|
||||||
void noMatchingTestCases(std::string const& spec) override;
|
void noMatchingTestCases(std::string const& spec) override;
|
||||||
|
|
||||||
|
void reportInvalidArguments(std::string const&arg) override;
|
||||||
|
|
||||||
void assertionStarting(AssertionInfo const&) override;
|
void assertionStarting(AssertionInfo const&) override;
|
||||||
|
|
||||||
bool assertionEnded(AssertionStats const& _assertionStats) override;
|
bool assertionEnded(AssertionStats const& _assertionStats) override;
|
||||||
|
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include "../internal/catch_tostring.h"
|
#include "../internal/catch_tostring.h"
|
||||||
#include "../internal/catch_reporter_registrars.hpp"
|
#include "../internal/catch_reporter_registrars.hpp"
|
||||||
|
#include "../internal/catch_text.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@@ -146,8 +147,8 @@ namespace Catch {
|
|||||||
for( auto const& child : groupNode.children )
|
for( auto const& child : groupNode.children )
|
||||||
writeTestCase( *child );
|
writeTestCase( *child );
|
||||||
|
|
||||||
xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
|
xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
|
||||||
xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
|
xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), XmlFormatting::Newline );
|
||||||
}
|
}
|
||||||
|
|
||||||
void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
|
void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
|
||||||
@@ -196,9 +197,9 @@ namespace Catch {
|
|||||||
writeAssertions( sectionNode );
|
writeAssertions( sectionNode );
|
||||||
|
|
||||||
if( !sectionNode.stdOut.empty() )
|
if( !sectionNode.stdOut.empty() )
|
||||||
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
|
xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), XmlFormatting::Newline );
|
||||||
if( !sectionNode.stdErr.empty() )
|
if( !sectionNode.stdErr.empty() )
|
||||||
xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
|
xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), XmlFormatting::Newline );
|
||||||
}
|
}
|
||||||
for( auto const& childNode : sectionNode.childSections )
|
for( auto const& childNode : sectionNode.childSections )
|
||||||
if( className.empty() )
|
if( className.empty() )
|
||||||
@@ -244,10 +245,25 @@ namespace Catch {
|
|||||||
|
|
||||||
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
|
XmlWriter::ScopedElement e = xml.scopedElement( elementName );
|
||||||
|
|
||||||
xml.writeAttribute( "message", result.getExpandedExpression() );
|
xml.writeAttribute( "message", result.getExpression() );
|
||||||
xml.writeAttribute( "type", result.getTestMacroName() );
|
xml.writeAttribute( "type", result.getTestMacroName() );
|
||||||
|
|
||||||
ReusableStringStream rss;
|
ReusableStringStream rss;
|
||||||
|
if (stats.totals.assertions.total() > 0) {
|
||||||
|
rss << "FAILED" << ":\n";
|
||||||
|
if (result.hasExpression()) {
|
||||||
|
rss << " ";
|
||||||
|
rss << result.getExpressionInMacro();
|
||||||
|
rss << '\n';
|
||||||
|
}
|
||||||
|
if (result.hasExpandedExpression()) {
|
||||||
|
rss << "with expansion:\n";
|
||||||
|
rss << Column(result.getExpandedExpression()).indent(2) << '\n';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rss << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
if( !result.getMessage().empty() )
|
if( !result.getMessage().empty() )
|
||||||
rss << result.getMessage() << '\n';
|
rss << result.getMessage() << '\n';
|
||||||
for( auto const& msg : stats.infoMessages )
|
for( auto const& msg : stats.infoMessages )
|
||||||
@@ -255,7 +271,7 @@ namespace Catch {
|
|||||||
rss << msg.message << '\n';
|
rss << msg.message << '\n';
|
||||||
|
|
||||||
rss << "at " << result.getSourceInfo();
|
rss << "at " << result.getSourceInfo();
|
||||||
xml.writeText( rss.str(), false );
|
xml.writeText( rss.str(), XmlFormatting::Newline );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -42,6 +42,13 @@ namespace Catch {
|
|||||||
m_reporter->noMatchingTestCases( spec );
|
m_reporter->noMatchingTestCases( spec );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ListeningReporter::reportInvalidArguments(std::string const&arg){
|
||||||
|
for ( auto const& listener : m_listeners ) {
|
||||||
|
listener->reportInvalidArguments( arg );
|
||||||
|
}
|
||||||
|
m_reporter->reportInvalidArguments( arg );
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||||
void ListeningReporter::benchmarkPreparing( std::string const& name ) {
|
void ListeningReporter::benchmarkPreparing( std::string const& name ) {
|
||||||
for (auto const& listener : m_listeners) {
|
for (auto const& listener : m_listeners) {
|
||||||
|
@@ -29,6 +29,8 @@ namespace Catch {
|
|||||||
|
|
||||||
void noMatchingTestCases( std::string const& spec ) override;
|
void noMatchingTestCases( std::string const& spec ) override;
|
||||||
|
|
||||||
|
void reportInvalidArguments(std::string const&arg) override;
|
||||||
|
|
||||||
static std::set<Verbosity> getSupportedVerbosities();
|
static std::set<Verbosity> getSupportedVerbosities();
|
||||||
|
|
||||||
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
#if defined(CATCH_CONFIG_ENABLE_BENCHMARKING)
|
||||||
|
181
include/reporters/catch_reporter_sonarqube.hpp
Normal file
181
include/reporters/catch_reporter_sonarqube.hpp
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* Created by Daniel Garcia on 2018-12-04.
|
||||||
|
* Copyright Social Point SL. All rights reserved.
|
||||||
|
*
|
||||||
|
* Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||||
|
* file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||||
|
*/
|
||||||
|
#ifndef CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||||
|
#define CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
||||||
|
|
||||||
|
|
||||||
|
// Don't #include any Catch headers here - we can assume they are already
|
||||||
|
// included before this header.
|
||||||
|
// This is not good practice in general but is necessary in this case so this
|
||||||
|
// file can be distributed as a single header that works with the main
|
||||||
|
// Catch single header.
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace Catch {
|
||||||
|
|
||||||
|
struct SonarQubeReporter : CumulativeReporterBase<SonarQubeReporter> {
|
||||||
|
|
||||||
|
SonarQubeReporter(ReporterConfig const& config)
|
||||||
|
: CumulativeReporterBase(config)
|
||||||
|
, xml(config.stream()) {
|
||||||
|
m_reporterPrefs.shouldRedirectStdOut = true;
|
||||||
|
m_reporterPrefs.shouldReportAllAssertions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SonarQubeReporter() override;
|
||||||
|
|
||||||
|
static std::string getDescription() {
|
||||||
|
return "Reports test results in the Generic Test Data SonarQube XML format";
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::set<Verbosity> getSupportedVerbosities() {
|
||||||
|
return { Verbosity::Normal };
|
||||||
|
}
|
||||||
|
|
||||||
|
void noMatchingTestCases(std::string const& /*spec*/) override {}
|
||||||
|
|
||||||
|
void testRunStarting(TestRunInfo const& testRunInfo) override {
|
||||||
|
CumulativeReporterBase::testRunStarting(testRunInfo);
|
||||||
|
xml.startElement("testExecutions");
|
||||||
|
xml.writeAttribute("version", "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
void testGroupEnded(TestGroupStats const& testGroupStats) override {
|
||||||
|
CumulativeReporterBase::testGroupEnded(testGroupStats);
|
||||||
|
writeGroup(*m_testGroups.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
void testRunEndedCumulative() override {
|
||||||
|
xml.endElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeGroup(TestGroupNode const& groupNode) {
|
||||||
|
std::map<std::string, TestGroupNode::ChildNodes> testsPerFile;
|
||||||
|
for(auto const& child : groupNode.children)
|
||||||
|
testsPerFile[child->value.testInfo.lineInfo.file].push_back(child);
|
||||||
|
|
||||||
|
for(auto const& kv : testsPerFile)
|
||||||
|
writeTestFile(kv.first.c_str(), kv.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeTestFile(const char* filename, TestGroupNode::ChildNodes const& testCaseNodes) {
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement("file");
|
||||||
|
xml.writeAttribute("path", filename);
|
||||||
|
|
||||||
|
for(auto const& child : testCaseNodes)
|
||||||
|
writeTestCase(*child);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeTestCase(TestCaseNode const& testCaseNode) {
|
||||||
|
// All test cases have exactly one section - which represents the
|
||||||
|
// test case itself. That section may have 0-n nested sections
|
||||||
|
assert(testCaseNode.children.size() == 1);
|
||||||
|
SectionNode const& rootSection = *testCaseNode.children.front();
|
||||||
|
writeSection("", rootSection, testCaseNode.value.testInfo.okToFail());
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeSection(std::string const& rootName, SectionNode const& sectionNode, bool okToFail) {
|
||||||
|
std::string name = trim(sectionNode.stats.sectionInfo.name);
|
||||||
|
if(!rootName.empty())
|
||||||
|
name = rootName + '/' + name;
|
||||||
|
|
||||||
|
if(!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement("testCase");
|
||||||
|
xml.writeAttribute("name", name);
|
||||||
|
xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
|
||||||
|
|
||||||
|
writeAssertions(sectionNode, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto const& childNode : sectionNode.childSections)
|
||||||
|
writeSection(name, *childNode, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeAssertions(SectionNode const& sectionNode, bool okToFail) {
|
||||||
|
for(auto const& assertion : sectionNode.assertions)
|
||||||
|
writeAssertion( assertion, okToFail);
|
||||||
|
}
|
||||||
|
|
||||||
|
void writeAssertion(AssertionStats const& stats, bool okToFail) {
|
||||||
|
AssertionResult const& result = stats.assertionResult;
|
||||||
|
if(!result.isOk()) {
|
||||||
|
std::string elementName;
|
||||||
|
if(okToFail) {
|
||||||
|
elementName = "skipped";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch(result.getResultType()) {
|
||||||
|
case ResultWas::ThrewException:
|
||||||
|
case ResultWas::FatalErrorCondition:
|
||||||
|
elementName = "error";
|
||||||
|
break;
|
||||||
|
case ResultWas::ExplicitFailure:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
case ResultWas::ExpressionFailed:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
case ResultWas::DidntThrowException:
|
||||||
|
elementName = "failure";
|
||||||
|
break;
|
||||||
|
|
||||||
|
// We should never see these here:
|
||||||
|
case ResultWas::Info:
|
||||||
|
case ResultWas::Warning:
|
||||||
|
case ResultWas::Ok:
|
||||||
|
case ResultWas::Unknown:
|
||||||
|
case ResultWas::FailureBit:
|
||||||
|
case ResultWas::Exception:
|
||||||
|
elementName = "internalError";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XmlWriter::ScopedElement e = xml.scopedElement(elementName);
|
||||||
|
|
||||||
|
ReusableStringStream messageRss;
|
||||||
|
messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
|
||||||
|
xml.writeAttribute("message", messageRss.str());
|
||||||
|
|
||||||
|
ReusableStringStream textRss;
|
||||||
|
if (stats.totals.assertions.total() > 0) {
|
||||||
|
textRss << "FAILED:\n";
|
||||||
|
if (result.hasExpression()) {
|
||||||
|
textRss << "\t" << result.getExpressionInMacro() << "\n";
|
||||||
|
}
|
||||||
|
if (result.hasExpandedExpression()) {
|
||||||
|
textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!result.getMessage().empty())
|
||||||
|
textRss << result.getMessage() << "\n";
|
||||||
|
|
||||||
|
for(auto const& msg : stats.infoMessages)
|
||||||
|
if(msg.type == ResultWas::Info)
|
||||||
|
textRss << msg.message << "\n";
|
||||||
|
|
||||||
|
textRss << "at " << result.getSourceInfo();
|
||||||
|
xml.writeText(textRss.str(), XmlFormatting::Newline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
XmlWriter xml;
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef CATCH_IMPL
|
||||||
|
SonarQubeReporter::~SonarQubeReporter() {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
CATCH_REGISTER_REPORTER( "sonarqube", SonarQubeReporter )
|
||||||
|
|
||||||
|
} // end namespace Catch
|
||||||
|
|
||||||
|
#endif // CATCH_REPORTER_SONARQUBE_HPP_INCLUDED
|
@@ -183,7 +183,6 @@ namespace Catch {
|
|||||||
|
|
||||||
SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
|
SourceLineInfo lineInfo = m_sectionStack.front().lineInfo;
|
||||||
|
|
||||||
if( !lineInfo.empty() )
|
|
||||||
os << lineInfo << "\n";
|
os << lineInfo << "\n";
|
||||||
os << getLineOfChars<'.'>() << "\n\n";
|
os << getLineOfChars<'.'>() << "\n\n";
|
||||||
}
|
}
|
||||||
|
@@ -193,9 +193,9 @@ namespace Catch {
|
|||||||
e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
|
e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
|
||||||
|
|
||||||
if( !testCaseStats.stdOut.empty() )
|
if( !testCaseStats.stdOut.empty() )
|
||||||
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
|
m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
|
||||||
if( !testCaseStats.stdErr.empty() )
|
if( !testCaseStats.stdErr.empty() )
|
||||||
m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
|
m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), XmlFormatting::Newline );
|
||||||
|
|
||||||
m_xml.endElement();
|
m_xml.endElement();
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user