Compare commits

...

64 Commits

Author SHA1 Message Date
Martin Hořeňovský
53d0d913a4 v3.5.0 2023-12-11 00:55:40 +01:00
Martin Hořeňovský
1648c30ec3 Look just for 'Catch2 X.Y.Z' in doc placeholder update 2023-12-11 00:52:22 +01:00
Simhon Chourasia
d4e9fb8aa5 Highlight that SECTIONs rerun the entire test case from beginning (#2749) 2023-12-10 22:35:54 +01:00
Martin Hořeňovský
b606bc2802 Remove obsolete section in limitations.md
We no longer use `std::shuffle` to implement random test order, so
a debug mode bug in old versions of libstdc++ cannot apply to us.
2023-12-10 22:09:48 +01:00
Blake-Madden
4ab0af8baf Fix minor typos in documentation (#2769)
Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2023-12-10 22:01:13 +01:00
Martin Hořeňovský
b7d70ddcd6 Ensure we always read 32 bit seed from std::random_device 2023-12-10 21:37:12 +01:00
Martin Hořeňovský
a6f22c5169 Remove static instance of std::random_device in Benchmark::analyse_samples 2023-12-10 21:22:43 +01:00
Martin Hořeňovský
1887d42e3d Use our PCG32 RNG instead of mt19937 in Benchmark::analyse_samples 2023-12-10 21:18:23 +01:00
Martin Hořeňovský
1774dbfd53 Make it clearer that the JSON reporter is WIP 2023-12-10 21:04:56 +01:00
Martin Hořeňovský
cb07ff9a7e Fix uniform_floating_point_distribution for unit ranges 2023-12-10 19:53:46 +01:00
Martin Hořeňovský
ae4fe16b81 Make the user-facing random Generators reproducible
Thanks to the new distributions, this is almost trivial change.
2023-12-10 19:53:44 +01:00
Martin Hořeňovský
28c66fdc5a Make uniform_floating_point_distribution reproducible
By moving to use our `uniform_integer_distribution`, which is
reproducible across different platforms, instead of the stdlib
one which is not, we can provide reproducible results for `float`s
and `double`s. Still no reproducibility for `long double`s, because
those are too different across different platforms.
2023-12-10 19:53:41 +01:00
Martin Hořeňovský
ed9d672b5c Add uniform_integer_distribution 2023-12-10 19:53:39 +01:00
Martin Hořeňovský
04a829b0e1 Add helpers for implementing uniform integer distribution
* Utility for extended mult n x n bits -> 2n bits
* Utility to adapt output from URBG to target (unsigned) integral
  type
* Utility to reorder signed values into unsigned type while keeping
  the order.
2023-12-10 19:53:36 +01:00
Martin Hořeňovský
ab1b079e4d Add uniform_floating_point_distribution 2023-12-10 18:44:12 +01:00
Martin Hořeňovský
d139b4ff7c Add implementation of helpers for uniform float distribution
Specifically we add
 * `gamma(a, b)`, which returns the magnitude of largest 1-ULP
   step in range [a, b].
 * `count_equidistant_float(a, b, distance)`, which returns the
   number of equi-distant floats in range [a, b].
2023-12-10 18:44:10 +01:00
Martin Hořeňovský
bfd9f0f5a6 Move nextafter polyfill to polyfills.hpp 2023-12-10 18:44:06 +01:00
Martin Hořeňovský
9a1e73568c Add test showing literals and complex generators in one GENERATE 2023-12-10 18:32:45 +01:00
Ikko Eltociear Ashimine
21d2da23bc Fix typo in tostring.md
specialiation -> specialization
2023-12-09 21:00:24 +01:00
Martin Hořeňovský
d1d7414eb9 Always run apt-get update before apt-get install
The base images for GitHub Actions are updated weekly, but
sometimes that is not enough to be able to install the packages
we require. The recommended fix for this is to always run
`apt-get update` before `apt-get install`.
2023-12-09 12:05:45 +01:00
Martin Hořeňovský
dacbf4fd6c Drop VS 2017 support
We do not support specific compilers, but rather compilers with
reasonable quality of their C++14 support. While developing the
new random generators, I ran into issues with VS2017 where it
rejects perfectly valid C++14 code, **and** the error does not
point me in the right direction to try and work around the issue.

It is time for VS2017 to go.
2023-12-08 23:54:00 +01:00
Pablo Duboue
0520ff4436 [DOC] Replaced broken link (fixes #2770)
The original link is no longer available but a fork is still on GitHub.
2023-12-02 22:05:48 +01:00
SupSuper
4a7be16c8c Fix compilation on Xbox platforms
Xbox does not support getenv
2023-12-02 22:03:48 +01:00
Martin Hořeňovský
32d9ae24bc JSONWriter deals in StringRefs instead of std::strings
Together with liberal use of `_sr` UDL to compile-time convert
string literals into StringRefs, this will reduce the number of
allocation and remove most of the strcpy calls inherent in
converting string lits into `std::string`s.
2023-11-17 09:54:03 +01:00
Sergei Iskakov
de7ba4e889 fn need to be in parenthesis
Otherwise intel c++ 19.1 cause an error
"expression preceding parentheses of apparent call must have (pointer-to-) function type CATCH2"
2023-11-16 12:33:35 +01:00
Martin Hořeňovský
733b901dd2 Fix special character escaping in JsonWriter 2023-11-14 23:35:22 +01:00
Beartama
7bf136b501 Add JSON reporter (#2706)
Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2023-11-14 22:52:15 +01:00
Gerald Senarclens de Grancy
2c68a0d05f lifted suggested version
Did this, because with 3.0.1, the files
- catch_xmlwriter.cpp
- catch_string_manip.hpp
- catch_test_case_info.hpp

were missing

This led to some obvious and some obscure compile errors when compiling
with g++ 13.2.0 (std c++17 or c++20)
One of these errors being "Elaborated-type-specifier for a scoped enum
must not use the ‘class’ keyword"

Since this is a rather bad experience and debugging it is
time-consuming, I suggest to simply boost the recommended version in the
snippet which is likely copied by new Catch2 users
2023-11-14 17:10:31 +01:00
Krzysiek Karbowiak
01cac90c62 Bump up actions/checkout version to v4 (#2760) 2023-11-09 10:31:49 +01:00
Krzysiek Karbowiak
b735dfce2d Increase build parallelism on macOS (#2759)
Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2023-11-07 22:17:47 +01:00
Martin Hořeňovský
caffe79a31 Fix missing include in catch_message.hpp
Because the issue comes from the expansions of `UNSCOPED_INFO`,
surrogate TUs could not catch this bug, and in common usage, the
include transitively comes from `catch_test_macros.hpp`.

Fixes #2758
2023-11-04 00:33:18 +01:00
Chris Thrasher
a8cf3e6710 Mark CATCH_CONFIG_ options as advanced
These options are rather low-level and don't need to be seen in the
CMake cache unless you opt into seeing all other advanced options.

This removes a lot of cache entries from the screen when using a GUI
or TUI to view the cache thus making it easier for users to focus on
the cache variables they're more likely to change on a frequent
basis.
2023-10-31 17:27:46 -06:00
Martin Hořeňovský
79d39a1954 Fix tests for C++23's multi-arg index operator
Closes #2744
2023-10-28 21:49:58 +02:00
Martin Hořeňovský
6ebc013b8c Fix UDL definitions for C++23
Technically, the declaration should not have a space between
the quotes and the underscore, because `_foo` is a reserved
identifier, but `""_foo` is not. In general it works, but newer
Clang versions warn about this, because WG21 wants to deprecate
and later remove this form completely.
2023-10-28 21:35:03 +02:00
Alex Merry
966d361551 Improve formatting of test specification docs
The existing formatting created one-element lists separated by paragraphs, when it would make more sense to have the paragraphs that are providing more information about one of those list entries be part of the list entry itself.

I think this makes the documentation easier to read in both markdown and html form, and should also improve the structure for assistive technologies.
2023-10-24 16:01:56 +02:00
Per Lundberg
766541d12d why-catch.md: Add JetBrains survey link 2023-09-27 20:14:35 +02:00
Holger Kaelberer
7b793314e5 Catch.cmake: Support CMake multi-config with PRE_TEST discovery mode (#2739)
Closes #2746 

---------

Co-authored-by: Holger Kaelberer <Holger.Kaelberer@bmw.de>
2023-09-25 19:40:35 +02:00
Christian Tacke
0fb817e41f fix some bugprone-macro-parentheses warnings
When using the public headers of catch2 in another project
that uses clang-tidy with some checks enabled, then some
warnings in catch2's headers are also reported.

This fixes a bunch of bugprone-macro-parentheses warnings.

See: https://clang.llvm.org/extra/clang-tidy/checks/bugprone/macro-parentheses.html
2023-09-25 10:56:45 +02:00
Martin Hořeňovský
f161110be4 Merge pull request #2747 from xandox/devel
correct argument references in CatchAddTests.cmake
2023-09-25 10:52:10 +02:00
Ilya Arzhannikov
db495acdbb correct argument references in CatchAddTests.cmake 2023-09-20 15:47:11 +02:00
Martin Hořeňovský
9c541ca72e Add test for multiple streaming parts in UNSCOPED_INFO 2023-09-17 10:44:31 +02:00
Martin Hořeňovský
92672591c1 Make jackknife TU-local to stats.cpp 2023-09-16 21:29:47 +02:00
Martin Hořeňovský
56fcd584c1 Make directCompare TU-local to stats.cpp 2023-09-16 21:18:44 +02:00
Jonathan Allen Grant
aafe09bc1c Update meson.build to fix #2722 (#2742) 2023-09-13 10:06:14 +02:00
Martin Hořeňovský
47a2c96938 Reduce the number of templates in Benchmarking
The basic idea was to reduce the number of things dependent on the `Clock`
type. To that end, I replaced `Duration<Clock>` with `IDuration` typedef
for `std::nanoseconds`, and `FloatDuration<Clock>` with `FDuration`
typedef for `Duration<double, std::nano>`. We can generally assume that
any clock's duration can be expressed in nanoseconds, as long as we insert
`duration_cast`s into the right places.

Note that we cannot remove all dependence on `Clock` as a template
arguments, because functions that actually measure the elapsed time have
to use the Clock.

We also changed some template function arguments to pass plain function
pointers, so that the actual implementation can be placed into a cpp file.
2023-09-08 10:04:31 +02:00
Martin Hořeňovský
fb96279aed Remove superfluous stdlib includes from catch_benchmark.hpp 2023-09-08 10:04:24 +02:00
Martin Hořeňovský
e14a08d734 Remove unused typedef from Benchmark::Environment 2023-09-08 10:04:22 +02:00
Martin Hořeňovský
9bba07cb87 Replace vector iterator args in benchmarks with ptr args 2023-09-08 10:04:19 +02:00
Martin Hořeňovský
b4ffba5087 Update sample output in docs/benchmarks.md 2023-09-08 10:04:17 +02:00
Martin Jeřábek
3a5cde55b7 implement stringify for std::nullopt_t 2023-08-30 16:21:02 +02:00
Martin Hořeňovský
2a19ae16b8 Rewrite commandline test spec docs
Closes #2738
2023-08-30 16:18:34 +02:00
Martin Hořeňovský
f24d39e42b Support C arrays and ADL ranges in from_range generator
Closes #2737
2023-08-29 15:38:13 +02:00
Martin Hořeňovský
85eb4652b4 Add nice license headers to files in examples/ and fuzzing/
Related to #2730
2023-08-24 16:34:31 +02:00
Ryan Pavlik
5bba3e4038 Edited amalgamated file generator, to block REUSE from getting confused
It struggled with the copyright lines included as string literals.
2023-08-19 14:31:27 +02:00
Martin Hořeňovský
e09de7222c Small cleanup in XML reporter 2023-08-14 12:41:52 +02:00
Martin Hořeňovský
a64ff326bf Change 'estimated' to 'est run time' in console reporter output 2023-08-14 10:22:59 +02:00
Martin Hořeňovský
ad56463477 Flush stream after benchmarkStarting in ConsoleReporter
This means that the user will see the estimation of full benchmark
running time when it is available, unlike now when it often only
ends up flushed after the benchmark is fully finished.

This means that the user will almost immediately see the start
of table like this

```
benchmark name                       samples       iterations    estimated
                                     mean          low mean      high mean
                                     std dev       low std dev   high std dev
-------------------------------------------------------------------------------
Fill vector generated                          100            54     3.0834 ms
```

This presents significant improvement in user experience especially
for long running benchmarks.
2023-08-13 23:09:02 +02:00
Martin Hořeňovský
9538d16005 Mention missing catch_user_config.hpp in FAQ 2023-08-11 15:36:23 +02:00
Martin Hořeňovský
a94bee771e Add missing line for v3.4.0 to ToC in release-notes.md 2023-08-11 15:36:03 +02:00
Martin Hořeňovský
d7304f0c41 Constify section hints in static-analysis mode
This prevents a `misc-const-correctness` in clang-tidy
2023-08-10 21:02:18 +02:00
rosstang
cd60a0301c Assert Info reset need to also reset result disposition to normal to handle uncaught exception correctly (#2723)
* AssertionEnd does not reset the assertion info yet. That is done after populateReaction. And reset assertion info would also reset the result disposition to normal, so that any uncaught exception would be reported as failure

* Approving test output changes due to added unit tests

* Unit tests to throw std::runtime_error instead of std::exception

* Add a unit test to test incomplete assertion handler

---------

Co-authored-by: Ross <ross.tang@gfo-x.com>
2023-08-07 22:07:31 +02:00
Martin Hořeňovský
b593be2116 Always default empty destructors 2023-08-05 18:21:38 +02:00
Vitalii Trubchaninov
ed4acded38 Don't define tryTranslators function if exception are disabled
"-Wunused-function -Wall" produces if this function is defined when exceptions are disabled
2023-08-04 15:28:55 +02:00
Riom
4acc51828f Introduce CATCH_CONFIG_PREFIX_MESSAGES to only prefix a few logging related macros. (#2544)
* Add missing include for VxWorks build.

std::min is defined in algorithm provides std::min. It appears to be transitively included for most platforms. For VxWorks however this explicit include is required.

* Add option CATCH_CONFIG_PREFIX_MESSAGES to selectively prefix message macros only.

In contrast to CATCH_CONFIG_PREFIX_ALL, this will only prefix the following macros:
I.e. INFO, UNSCOPED_INFO, WARN and CATCH_CAPTURE

This is mainly useful for codebases that use INFO or WARN for their own logging macros.
2023-07-19 17:04:43 +02:00
147 changed files with 7784 additions and 934 deletions

View File

@@ -11,7 +11,7 @@ jobs:
compilation_mode: [fastbuild, dbg, opt] compilation_mode: [fastbuild, dbg, opt]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Mount bazel cache - name: Mount bazel cache
uses: actions/cache@v3 uses: actions/cache@v3

View File

@@ -18,10 +18,12 @@ jobs:
other_pkgs: clang-11 other_pkgs: clang-11
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Prepare environment - name: Prepare environment
run: sudo apt-get install -y meson ninja-build ${{matrix.other_pkgs}} run: |
sudo apt-get update
sudo apt-get install -y meson ninja-build ${{matrix.other_pkgs}}
- name: Configure build - name: Configure build
env: env:

View File

@@ -70,10 +70,12 @@ jobs:
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Prepare environment - name: Prepare environment
run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} run: |
sudo apt-get update
sudo apt-get install -y ninja-build ${{matrix.other_pkgs}}
- name: Configure build - name: Configure build
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}

View File

@@ -83,7 +83,7 @@ jobs:
other_pkgs: g++-10 other_pkgs: g++-10
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Add repositories for older GCC - name: Add repositories for older GCC
run: | run: |
@@ -92,7 +92,9 @@ jobs:
if: ${{ matrix.cxx == 'g++-5' || matrix.cxx == 'g++-6' }} if: ${{ matrix.cxx == 'g++-5' || matrix.cxx == 'g++-6' }}
- name: Prepare environment - name: Prepare environment
run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}} run: |
sudo apt-get update
sudo apt-get install -y ninja-build ${{matrix.other_pkgs}}
- name: Configure build - name: Configure build
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}

View File

@@ -22,7 +22,7 @@ jobs:
extra_tests: ON extra_tests: ON
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Configure build - name: Configure build
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}
@@ -42,11 +42,10 @@ jobs:
- name: Build tests + lib - name: Build tests + lib
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
run: make -j 2 run: make -j `sysctl -n hw.ncpu`
- name: Run tests - name: Run tests
env: env:
CTEST_OUTPUT_ON_FAILURE: 1 CTEST_OUTPUT_ON_FAILURE: 1
working-directory: ${{runner.workspace}}/build working-directory: ${{runner.workspace}}/build
# Hardcode 2 cores we know are there run: ctest -C ${{matrix.build_type}} -j `sysctl -n hw.ncpu`
run: ctest -C ${{matrix.build_type}} -j 2

View File

@@ -9,7 +9,7 @@ jobs:
steps: steps:
- name: Checkout source code - name: Checkout source code
uses: actions/checkout@v2 uses: actions/checkout@v4
- name: Setup Dependencies - name: Setup Dependencies
uses: actions/setup-python@v2 uses: actions/setup-python@v2

View File

@@ -13,7 +13,7 @@ jobs:
build_type: [Debug, Release] build_type: [Debug, Release]
std: [14, 17] std: [14, 17]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v4
- name: Configure build - name: Configure build
working-directory: ${{runner.workspace}} working-directory: ${{runner.workspace}}

View File

@@ -49,6 +49,7 @@ expand_template(
"#cmakedefine CATCH_CONFIG_NOSTDOUT": "", "#cmakedefine CATCH_CONFIG_NOSTDOUT": "",
"#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "", "#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "",
"#cmakedefine CATCH_CONFIG_PREFIX_ALL": "", "#cmakedefine CATCH_CONFIG_PREFIX_ALL": "",
"#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES": "",
"#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "", "#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "",
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "", "#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "",
"#cmakedefine CATCH_CONFIG_USE_ASYNC": "", "#cmakedefine CATCH_CONFIG_USE_ASYNC": "",

View File

@@ -18,10 +18,12 @@
macro(AddOverridableConfigOption OptionBaseName) macro(AddOverridableConfigOption OptionBaseName)
option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF) option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF)
option(CATCH_CONFIG_NO_${OptionBaseName} "Read docs/configuration.md for details" OFF) option(CATCH_CONFIG_NO_${OptionBaseName} "Read docs/configuration.md for details" OFF)
mark_as_advanced(CATCH_CONFIG_${OptionBaseName} CATCH_CONFIG_NO_${OptionBaseName})
endmacro() endmacro()
macro(AddConfigOption OptionBaseName) macro(AddConfigOption OptionBaseName)
option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF) option(CATCH_CONFIG_${OptionBaseName} "Read docs/configuration.md for details" OFF)
mark_as_advanced(CATCH_CONFIG_${OptionBaseName})
endmacro() endmacro()
set(_OverridableOptions set(_OverridableOptions
@@ -62,6 +64,7 @@ set(_OtherConfigOptions
"FAST_COMPILE" "FAST_COMPILE"
"NOSTDOUT" "NOSTDOUT"
"PREFIX_ALL" "PREFIX_ALL"
"PREFIX_MESSAGES"
"WINDOWS_CRTDBG" "WINDOWS_CRTDBG"
) )
@@ -78,6 +81,8 @@ endif()
set(CATCH_CONFIG_DEFAULT_REPORTER "console" CACHE STRING "Read docs/configuration.md for details. The name of the reporter should be without quotes.") set(CATCH_CONFIG_DEFAULT_REPORTER "console" CACHE STRING "Read docs/configuration.md for details. The name of the reporter should be without quotes.")
set(CATCH_CONFIG_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.") set(CATCH_CONFIG_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.")
mark_as_advanced(CATCH_CONFIG_SHARED_LIBRARY CATCH_CONFIG_DEFAULT_REPORTER CATCH_CONFIG_CONSOLE_WIDTH)
# There is no good way to both turn this into a CMake cache variable, # There is no good way to both turn this into a CMake cache variable,
# and keep reasonable default semantics inside the project. Thus we do # and keep reasonable default semantics inside the project. Thus we do
# not define it and users have to provide it as an outside variable. # not define it and users have to provide it as an outside variable.

View File

@@ -46,7 +46,6 @@ function(add_warnings_to_targets targets)
set(CHECKED_WARNING_FLAGS set(CHECKED_WARNING_FLAGS
"-Wabsolute-value" "-Wabsolute-value"
"-Wall" "-Wall"
"-Wc++20-compat"
"-Wcall-to-pure-virtual-from-ctor-dtor" "-Wcall-to-pure-virtual-from-ctor-dtor"
"-Wcast-align" "-Wcast-align"
"-Wcatch-value" "-Wcatch-value"

View File

@@ -32,7 +32,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif() endif()
project(Catch2 project(Catch2
VERSION 3.4.0 # CML version placeholder, don't delete VERSION 3.5.0 # CML version placeholder, don't delete
LANGUAGES CXX LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which # HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet. # we do not target yet.

View File

@@ -70,14 +70,3 @@ environment:
additional_flags: "/permissive- /std:c++latest" additional_flags: "/permissive- /std:c++latest"
platform: x64 platform: x64
configuration: Debug configuration: Debug
- FLAVOR: VS 2017 x64 Debug
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
platform: x64
configuration: Debug
- FLAVOR: VS 2017 x64 Release Coverage
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
coverage: 1
platform: x64
configuration: Debug

View File

@@ -93,7 +93,7 @@ Fibonacci
------------------------------------------------------------------------------- -------------------------------------------------------------------------------
C:\path\to\Catch2\Benchmark.tests.cpp(10) C:\path\to\Catch2\Benchmark.tests.cpp(10)
............................................................................... ...............................................................................
benchmark name samples iterations estimated benchmark name samples iterations est run time
mean low mean high mean mean low mean high mean
std dev low std dev high std dev std dev low std dev high std dev
------------------------------------------------------------------------------- -------------------------------------------------------------------------------

View File

@@ -82,7 +82,7 @@ variable set to "1".
### CodeCoverage module (GCOV, LCOV...) ### CodeCoverage module (GCOV, LCOV...)
If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/fkromer/catch_cmake_coverage If you are using GCOV tool to get testing coverage of your code, and are not sure how to integrate it with CMake and Catch, there should be an external example over at https://github.com/claremacrae/catch_cmake_coverage
### pkg-config ### pkg-config

View File

@@ -51,7 +51,7 @@ Include(FetchContent)
FetchContent_Declare( FetchContent_Declare(
Catch2 Catch2
GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_REPOSITORY https://github.com/catchorg/Catch2.git
GIT_TAG v3.0.1 # or a later release GIT_TAG v3.4.0 # or a later release
) )
FetchContent_MakeAvailable(Catch2) FetchContent_MakeAvailable(Catch2)
@@ -203,7 +203,7 @@ the output file name e.g. ".xml".
If specified allows control over when test discovery is performed. If specified allows control over when test discovery is performed.
For a value of `POST_BUILD` (default) test discovery is performed at build time. For a value of `POST_BUILD` (default) test discovery is performed at build time.
For a a value of `PRE_TEST` test discovery is delayed until just prior to test For a value of `PRE_TEST` test discovery is delayed until just prior to test
execution (useful e.g. in cross-compilation environments). execution (useful e.g. in cross-compilation environments).
``DISCOVERY_MODE`` defaults to the value of the ``DISCOVERY_MODE`` defaults to the value of the
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when ``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when

View File

@@ -85,43 +85,102 @@ Click one of the following links to take you straight to that option - or scroll
<pre>&lt;test-spec> ...</pre> <pre>&lt;test-spec> ...</pre>
Test cases, wildcarded test cases, tags and tag expressions are all passed directly as arguments. Tags are distinguished by being enclosed in square brackets. By providing a test spec, you filter which tests will be run. If you call
Catch2 without any test spec, then it will run all non-hidden test
cases. A test case is hidden if it has the `[!benchmark]` tag, any tag
with a dot at the start, e.g. `[.]` or `[.foo]`.
If no test specs are supplied then all test cases, except "hidden" tests, are run. There are three basic test specs that can then be combined into more
A test is hidden by giving it any tag starting with (or just) a period (```.```) - or, in the deprecated case, tagged ```[hide]``` or given name starting with `'./'`. To specify hidden tests from the command line ```[.]``` or ```[hide]``` can be used *regardless of how they were declared*. complex specs:
Specs must be enclosed in quotes if they contain spaces. If they do not contain spaces the quotes are optional. * Full test name, e.g. `"Test 1"`.
Wildcards consist of the `*` character at the beginning and/or end of test case names and can substitute for any number of any characters (including none). This allows only test cases whose name is "Test 1".
Test specs are case insensitive. * Wildcarded test name, e.g. `"*Test"`, or `"Test*"`, or `"*Test*"`.
If a spec is prefixed with `exclude:` or the `~` character then the pattern matches an exclusion. This means that tests matching the pattern are excluded from the set - even if a prior inclusion spec included them. Subsequent inclusion specs will take precedence, however. This allows any test case whose name ends with, starts with, or contains
Inclusions and exclusions are evaluated in left-to-right order. in the middle the string "Test". Note that the wildcard can only be at
the start or end.
Test case examples: * Tag name, e.g. `[some-tag]`.
This allows any test case tagged with "[some-tag]". Remember that some
tags are special, e.g. those that start with "." or with "!".
You can also combine the basic test specs to create more complex test
specs. You can:
* Concatenate specs to apply all of them, e.g. `[some-tag][other-tag]`.
This allows test cases that are tagged with **both** "[some-tag]" **and**
"[other-tag]". A test case with just "[some-tag]" will not pass the filter,
nor will test case with just "[other-tag]".
* Comma-join specs to apply any of them, e.g. `[some-tag],[other-tag]`.
This allows test cases that are tagged with **either** "[some-tag]" **or**
"[other-tag]". A test case with both will obviously also pass the filter.
Note that commas take precendence over simple concatenation. This means
that `[a][b],[c]` accepts tests that are tagged with either both "[a]" and
"[b]", or tests that are tagged with just "[c]".
* Negate the spec by prepending it with `~`, e.g. `~[some-tag]`.
This rejects any test case that is tagged with "[some-tag]". Note that
rejection takes precedence over other filters.
Note that negations always binds to the following _basic_ test spec.
This means that `~[foo][bar]` negates only the "[foo]" tag and not the
"[bar]" tag.
Note that when Catch2 is deciding whether to include a test, first it
checks whether the test matches any negative filters. If it does,
the test is rejected. After that, the behaviour depends on whether there
are positive filters as well. If there are no positive filters, all
remaining non-hidden tests are included. If there are positive filters,
only tests that match the positive filters are included.
You can also match test names with special characters by escaping them
with a backslash (`"\"`), e.g. a test named `"Do A, then B"` is matched
by "Do A\, then B" test spec. Backslash also escapes itself.
### Examples
Given these TEST_CASEs,
``` ```
thisTestOnly Matches the test case called, 'thisTestOnly' TEST_CASE("Test 1") {}
"this test only" Matches the test case called, 'this test only'
these* Matches all cases starting with 'these' TEST_CASE("Test 2", "[.foo]") {}
exclude:notThis Matches all tests except, 'notThis'
~notThis Matches all tests except, 'notThis' TEST_CASE("Test 3", "[.bar]") {}
~*private* Matches all tests except those that contain 'private'
a* ~ab* abc Matches all tests that start with 'a', except those that TEST_CASE("Test 4", "[.][foo][bar]") {}
start with 'ab', except 'abc', which is included
~[tag1] Matches all tests except those tagged with '[tag1]'
-# [#somefile] Matches all tests from the file 'somefile.cpp'
``` ```
Names within square brackets are interpreted as tags. this is the result of these filters
A series of tags form an AND expression whereas a comma-separated sequence forms an OR expression. e.g.: ```
./tests # Selects only the first test, others are hidden
./tests "Test 1" # Selects only the first test, other do not match
./tests ~"Test 1" # Selects no tests. Test 1 is rejected, other tests are hidden
./tests "Test *" # Selects all tests.
./tests [bar] # Selects tests 3 and 4. Other tests are not tagged [bar]
./tests ~[foo] # Selects test 1, because it is the only non-hidden test without [foo] tag
./tests [foo][bar] # Selects test 4.
./tests [foo],[bar] # Selects tests 2, 3, 4.
./tests ~[foo][bar] # Selects test 3. 2 and 4 are rejected due to having [foo] tag
./tests ~"Test 2"[foo] # Selects test 4, because test 2 is explicitly rejected
./tests [foo][bar],"Test 1" # Selects tests 1 and 4.
./tests "Test 1*" # Selects test 1, wildcard can match zero characters
```
<pre>[one][two],[three]</pre> _Note: Using plain asterisk on a command line can cause issues with shell
This matches all tests tagged `[one]` and `[two]`, as well as all tests tagged `[three]` expansion. Make sure that the asterisk is passed to Catch2 and is not
interpreted by the shell._
Test names containing special characters, such as `,` or `[` can specify them on the command line using `\`.
`\` also escapes itself.
<a id="choosing-a-reporter-to-use"></a> <a id="choosing-a-reporter-to-use"></a>
## Choosing a reporter to use ## Choosing a reporter to use

View File

@@ -26,7 +26,8 @@ with the same name.
## Prefixing Catch macros ## Prefixing Catch macros
CATCH_CONFIG_PREFIX_ALL CATCH_CONFIG_PREFIX_ALL // Prefix all macros with CATCH_
CATCH_CONFIG_PREFIX_MESSAGES // Prefix only INFO, UNSCOPED_INFO, WARN and CAPTURE
To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```). To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```).

View File

@@ -10,6 +10,7 @@
[Does Catch2 support running tests in parallel?](#does-catch2-support-running-tests-in-parallel)<br> [Does Catch2 support running tests in parallel?](#does-catch2-support-running-tests-in-parallel)<br>
[Can I compile Catch2 into a dynamic library?](#can-i-compile-catch2-into-a-dynamic-library)<br> [Can I compile Catch2 into a dynamic library?](#can-i-compile-catch2-into-a-dynamic-library)<br>
[What repeatability guarantees does Catch2 provide?](#what-repeatability-guarantees-does-catch2-provide)<br> [What repeatability guarantees does Catch2 provide?](#what-repeatability-guarantees-does-catch2-provide)<br>
[My build cannot find `catch2/catch_user_config.hpp`, how can I fix it?](#my-build-cannot-find-catch2catch_user_confighpp-how-can-i-fix-it)<br>
## How do I run global setup/teardown only if tests will be run? ## How do I run global setup/teardown only if tests will be run?
@@ -83,12 +84,30 @@ and it is also generally repeatable across versions, but we might break
it from time to time. E.g. we broke repeatability with previous versions it from time to time. E.g. we broke repeatability with previous versions
in v2.13.4 so that test cases with similar names are shuffled better. in v2.13.4 so that test cases with similar names are shuffled better.
Random generators currently rely on platform's stdlib, specifically Since Catch2 3.5.0 the random generators use custom distributions,
the distributions from `<random>`. We thus provide no extra guarantee that should be repeatable across different platforms, with few caveats.
above what your platform does. **Important: `<random>`'s distributions For details see the section on random generators in the [Generator
documentation](generators.md#random-number-generators-details).
Before this version, random generators relied on distributions from
platform's stdlib. We thus can provide no extra guarantee on top of the
ones given by your platform. **Important: `<random>`'s distributions
are not specified to be repeatable across different platforms.** are not specified to be repeatable across different platforms.**
## My build cannot find `catch2/catch_user_config.hpp`, how can I fix it?
`catch2/catch_user_config.hpp` is a generated header that contains user
compile time configuration. It is generated by CMake/Meson/Bazel during
build. If you are not using either of these, your three options are to
1) Build Catch2 separately using build tool that will generate the header
2) Use the amalgamated files to build Catch2
3) Use CMake to configure a build. This will generate the header and you
can copy it into your own checkout of Catch2.
--- ---
[Home](Readme.md#top) [Home](Readme.md#top)

View File

@@ -189,6 +189,31 @@ TEST_CASE("type conversion", "[generators]") {
} }
``` ```
### Random number generators: details
> This section applies from Catch2 3.5.0. Before that, random generators
> were a thin wrapper around distributions from `<random>`.
All of the `random(a, b)` generators in Catch2 currently generate uniformly
distributed number in closed interval \[a; b\]. This is different from
`std::uniform_real_distribution`, which should return numbers in interval
\[a; b) (but due to rounding can end up returning b anyway), but the
difference is intentional, so that `random(a, a)` makes sense. If there is
enough interest from users, we can provide API to pick any of CC, CO, OC,
or OO ranges.
Unlike `std::uniform_int_distribution`, Catch2's generators also support
various single-byte integral types, such as `char` or `bool`.
Given the same seed, the output from the integral generators is
reproducible across different platforms. For floating point generators,
we only promise reproducibility on platforms that obey the IEEE 754
standard, and where `float` is 4 bytes and `double` is 8 bytes. We provide
no guarantees for `long double`, as the internals of `long double` can
vary wildly across different platforms.
## Generator interface ## Generator interface
You can also implement your own generators, by deriving from the You can also implement your own generators, by deriving from the

View File

@@ -173,13 +173,3 @@ TEST_CASE("b") {
If you are seeing a problem like this, i.e. weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library. If you are seeing a problem like this, i.e. weird test paths that trigger only under Clang with `libc++`, or only under very specific version of `libstdc++`, it is very likely you are seeing this. The only known workaround is to use a fixed version of your standard library.
### libstdc++, `_GLIBCXX_DEBUG` macro and random ordering of tests
Running a Catch2 binary compiled against libstdc++ with `_GLIBCXX_DEBUG`
macro defined with `--order rand` will cause a debug check to trigger and
abort the run due to self-assignment.
[This is a known bug inside libstdc++](https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle/23691322)
Workaround: Don't use `--order rand` when compiling against debug-enabled
libstdc++.

View File

@@ -286,7 +286,7 @@ comparable. (e.g. you may compare `std::vector<int>` to `std::array<char>`).
`UnorderedRangeEquals` is similar to `RangeEquals`, but the order `UnorderedRangeEquals` is similar to `RangeEquals`, but the order
does not matter. For example "1, 2, 3" would match "3, 2, 1", but not does not matter. For example "1, 2, 3" would match "3, 2, 1", but not
"1, 1, 2, 3" As with `RangeEquals`, `UnorderedRangeEquals` compares "1, 1, 2, 3" As with `RangeEquals`, `UnorderedRangeEquals` compares
the individual elements using using `operator==` by default. the individual elements using `operator==` by default.
Both `RangeEquals` and `UnorderedRangeEquals` optionally accept a Both `RangeEquals` and `UnorderedRangeEquals` optionally accept a
predicate which can be used to compare the containers element-wise. predicate which can be used to compare the containers element-wise.

View File

@@ -2,6 +2,8 @@
# Release notes # Release notes
**Contents**<br> **Contents**<br>
[3.5.0](#350)<br>
[3.4.0](#340)<br>
[3.3.2](#332)<br> [3.3.2](#332)<br>
[3.3.1](#331)<br> [3.3.1](#331)<br>
[3.3.0](#330)<br> [3.3.0](#330)<br>
@@ -57,6 +59,41 @@
## 3.5.0
### Improvements
* Introduced `CATCH_CONFIG_PREFIX_MESSAGES` to prefix only logging macros (#2544)
* This means `INFO`, `UNSCOPED_INFO`, `WARN` and `CAPTURE`.
* Section hints in static analysis mode are now `const`
* This prevents Clang-Tidy from complaining about `misc-const-correctness`.
* `from_range` generator supports C arrays and ranges that require ADL (#2737)
* Stringification support for `std::optional` now also includes `std::nullopt` (#2740)
* The Console reporter flushes output after writing benchmark runtime estimate.
* This means that you can immediately see for how long the benchmark is expected to run.
* Added workaround to enable compilation with ICC 19.1 (#2551, #2766)
* Compiling Catch2 for XBox should work out of the box (#2772)
* Catch2 should automatically disable getenv when compiled for XBox.
* Compiling Catch2 with exceptions disabled no longer triggers `Wunused-function` (#2726)
* **`random` Generators for integral types are now reproducible across different platforms**
* Unlike `<rando>`, Catch2's generators also support 1 byte integral types (`char`, `bool`, ...)
* **`random` Generators for `float` and `double` are now reproducible across different platforms**
* `long double` varies across different platforms too much to be reproducible
* This guarantee applies only to platforms with IEEE 754 floats.
### Fixes
* UDL declaration inside Catch2 are now strictly conforming to the standard
* `operator "" _a` is UB, `operator ""_a` is fine. Seriously.
* Fixed `CAPTURE` tests failing to compile in C++23 mode (#2744)
* Fixed missing include in `catch_message.hpp` (#2758)
* Fixed `CHECK_ELSE` suppressing failure from uncaught exceptions(#2723)
### Miscellaneous
* The documentation for specifying which tests to run through commandline has been completely rewritten (#2738)
* Fixed installation when building Catch2 with meson (#2722, #2742)
* Fixed `catch_discover_tests` when using custom reporter and `PRE_TEST` discovery mode (#2747)
* `catch_discover_tests` supports multi-config CMake generator in `PRE_TEST` discovery mode (#2739, #2746)
## 3.4.0 ## 3.4.0
### Improvements ### Improvements
@@ -380,7 +417,7 @@ v3 releases.
* Added `STATIC_CHECK` macro, similar to `STATIC_REQUIRE` (#2318) * Added `STATIC_CHECK` macro, similar to `STATIC_REQUIRE` (#2318)
* When deferred tu runtime, it behaves like `CHECK`, and not like `REQUIRE`. * When deferred tu runtime, it behaves like `CHECK`, and not like `REQUIRE`.
* You can have multiple tests with the same name, as long as other parts of the test identity differ (#1915, #1999, #2175) * You can have multiple tests with the same name, as long as other parts of the test identity differ (#1915, #1999, #2175)
* Test identity includes test's name, test's tags and and test's class name if applicable. * Test identity includes test's name, test's tags and test's class name if applicable.
* Added new warning, `UnmatchedTestSpec`, to error on test specs with no matching tests * Added new warning, `UnmatchedTestSpec`, to error on test specs with no matching tests
* The `-w`, `--warn` warning flags can now be provided multiple times to enable multiple warnings * The `-w`, `--warn` warning flags can now be provided multiple times to enable multiple warnings
* The case-insensitive handling of tags is now more reliable and takes up less memory * The case-insensitive handling of tags is now more reliable and takes up less memory

View File

@@ -231,7 +231,7 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in
> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0. > [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0.
_template-type1_ through _template-typen_ is list of template template _template-type1_ through _template-typen_ is list of template
types which should be combined with each of _template-arg1_ through types which should be combined with each of _template-arg1_ through
_template-argm_, resulting in _n * m_ test cases. Inside the test case, _template-argm_, resulting in _n * m_ test cases. Inside the test case,
the resulting type is available under the name of `TestType`. the resulting type is available under the name of `TestType`.

View File

@@ -75,7 +75,7 @@ CATCH_TRANSLATE_EXCEPTION( MyType const& ex ) {
Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected. Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type. If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialiation for you with minimal code. However, as a convenience, Catch provides the `REGISTER_ENUM` helper macro that will generate the `StringMaker` specialisation for you with minimal code.
Simply provide it the (qualified) enum name, followed by all the enum values, and you're done! Simply provide it the (qualified) enum name, followed by all the enum values, and you're done!
E.g. E.g.

View File

@@ -119,7 +119,7 @@ This is best explained through an example ([code](../examples/100-Fix-Section.cp
```c++ ```c++
TEST_CASE( "vectors can be sized and resized", "[vector]" ) { TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
// This setup will be done 4 times in total, once for each section
std::vector<int> v( 5 ); std::vector<int> v( 5 );
REQUIRE( v.size() == 5 ); REQUIRE( v.size() == 5 );
@@ -152,11 +152,12 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
} }
``` ```
For each `SECTION` the `TEST_CASE` is executed from the start. This means For each `SECTION` the `TEST_CASE` is **executed from the start**. This means
that each section is entered with a freshly constructed vector `v`, that that each section is entered with a freshly constructed vector `v`, that
we know has size 5 and capacity at least 5, because the two assertions we know has size 5 and capacity at least 5, because the two assertions
are also checked before the section is entered. Each run through a test are also checked before the section is entered. This behaviour may not be
case will execute one, and only one, leaf section. ideal for tests where setup is expensive. Each run through a test case will
execute one, and only one, leaf section.
Section can also be nested, in which case the parent section can be Section can also be nested, in which case the parent section can be
entered multiple times, once for each leaf section. Nested sections are entered multiple times, once for each leaf section. Nested sections are

View File

@@ -30,7 +30,7 @@ So what does Catch2 bring to the party that differentiates it from these? Apart
* Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added. * Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added.
* JUnit xml output is supported for integration with third-party tools, such as CI servers. * JUnit xml output is supported for integration with third-party tools, such as CI servers.
* A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI). * A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI).
* A command line parser is provided and can still be used if you choose to provided your own main() function. * A command line parser is provided and can still be used if you choose to provide your own main() function.
* Alternative assertion macro(s) report failures but don't abort the test case * Alternative assertion macro(s) report failures but don't abort the test case
* Good set of facilities for floating point comparisons (`Catch::Approx` and full set of matchers) * Good set of facilities for floating point comparisons (`Catch::Approx` and full set of matchers)
* Internal and friendly macros are isolated so name clashes can be managed * Internal and friendly macros are isolated so name clashes can be managed
@@ -41,8 +41,8 @@ So what does Catch2 bring to the party that differentiates it from these? Apart
## Who else is using Catch2? ## Who else is using Catch2?
A whole lot of people. According to the 2021 JetBrains C++ ecosystem survey, A whole lot of people. According to [the 2022 JetBrains C++ ecosystem survey](https://www.jetbrains.com/lp/devecosystem-2022/cpp/#Which-unit-testing-frameworks-do-you-regularly-use),
about 11% of C++ programmers use Catch2 for unit testing, making it the about 12% of C++ programmers use Catch2 for unit testing, making it the
second most popular unit testing framework. second most popular unit testing framework.
You can also take a look at the (incomplete) list of [open source projects](opensource-users.md#top) You can also take a look at the (incomplete) list of [open source projects](opensource-users.md#top)

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 010-TestCase.cpp // 010-TestCase.cpp
// And write tests in the same file: // And write tests in the same file:
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 020-TestCase-1.cpp // 020-TestCase-1.cpp
#include <catch2/catch_test_macros.hpp> #include <catch2/catch_test_macros.hpp>

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 020-TestCase-2.cpp // 020-TestCase-2.cpp
// main() provided by Catch in file 020-TestCase-1.cpp. // main() provided by Catch in file 020-TestCase-1.cpp.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 030-Asn-Require-Check.cpp // 030-Asn-Require-Check.cpp
// Catch has two natural expression assertion macro's: // Catch has two natural expression assertion macro's:

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 100-Fix-Section.cpp // 100-Fix-Section.cpp
// Catch has two ways to express fixtures: // Catch has two ways to express fixtures:

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 110-Fix-ClassFixture.cpp // 110-Fix-ClassFixture.cpp
// Catch has two ways to express fixtures: // Catch has two ways to express fixtures:

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 120-Bdd-ScenarioGivenWhenThen.cpp // 120-Bdd-ScenarioGivenWhenThen.cpp
// main() provided by linkage with Catch2WithMain // main() provided by linkage with Catch2WithMain

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 210-Evt-EventListeners.cpp // 210-Evt-EventListeners.cpp
// Contents: // Contents:

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 231-Cfg-OutputStreams.cpp // 231-Cfg-OutputStreams.cpp
// Show how to replace the streams with a simple custom made streambuf. // Show how to replace the streams with a simple custom made streambuf.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 300-Gen-OwnGenerator.cpp // 300-Gen-OwnGenerator.cpp
// Shows how to define a custom generator. // Shows how to define a custom generator.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 301-Gen-MapTypeConversion.cpp // 301-Gen-MapTypeConversion.cpp
// Shows how to use map to modify generator's return type. // Shows how to use map to modify generator's return type.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 302-Gen-Table.cpp // 302-Gen-Table.cpp
// Shows how to use table to run a test many times with different inputs. Lifted from examples on // Shows how to use table to run a test many times with different inputs. Lifted from examples on
// issue #850. // issue #850.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 310-Gen-VariablesInGenerator.cpp // 310-Gen-VariablesInGenerator.cpp
// Shows how to use variables when creating generators. // Shows how to use variables when creating generators.

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// 311-Gen-CustomCapture.cpp // 311-Gen-CustomCapture.cpp
// Shows how to provide custom capture list to the generator expression // Shows how to provide custom capture list to the generator expression

View File

@@ -176,8 +176,10 @@ function(catch_discover_tests TARGET)
string(SUBSTRING ${args_hash} 0 7 args_hash) string(SUBSTRING ${args_hash} 0 7 args_hash)
# Define rule to generate test list for aforementioned test executable # Define rule to generate test list for aforementioned test executable
set(ctest_include_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_include-${args_hash}.cmake") set(ctest_file_base "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}-${args_hash}")
set(ctest_tests_file "${CMAKE_CURRENT_BINARY_DIR}/${TARGET}_tests-${args_hash}.cmake") set(ctest_include_file "${ctest_file_base}_include.cmake")
set(ctest_tests_file "${ctest_file_base}_tests.cmake")
get_property(crosscompiling_emulator get_property(crosscompiling_emulator
TARGET ${TARGET} TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR PROPERTY CROSSCOMPILING_EMULATOR
@@ -218,6 +220,14 @@ function(catch_discover_tests TARGET)
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST") elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL
PROPERTY GENERATOR_IS_MULTI_CONFIG
)
if(GENERATOR_IS_MULTI_CONFIG)
set(ctest_tests_file "${ctest_file_base}_tests-$<CONFIG>.cmake")
endif()
string(CONCAT ctest_include_content string(CONCAT ctest_include_content
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n" "if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
" if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n" " if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n"
@@ -249,7 +259,22 @@ function(catch_discover_tests TARGET)
"endif()" "\n" "endif()" "\n"
) )
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_content}") if(GENERATOR_IS_MULTI_CONFIG)
foreach(_config ${CMAKE_CONFIGURATION_TYPES})
file(GENERATE OUTPUT "${ctest_file_base}_include-${_config}.cmake" CONTENT "${ctest_include_content}" CONDITION $<CONFIG:${_config}>)
endforeach()
string(CONCAT ctest_include_multi_content
"if(NOT CTEST_CONFIGURATION_TYPE)" "\n"
" message(\"No configuration for testing specified, use '-C <cfg>'.\")" "\n"
"else()" "\n"
" include(\"${ctest_file_base}_include-\${CTEST_CONFIGURATION_TYPE}.cmake\")" "\n"
"endif()" "\n"
)
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_multi_content}")
else()
file(GENERATE OUTPUT "${ctest_file_base}_include.cmake" CONTENT "${ctest_include_content}")
file(WRITE "${ctest_include_file}" "include(\"${ctest_file_base}_include.cmake\")")
endif()
endif() endif()
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0") if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")

View File

@@ -88,10 +88,10 @@ function(catch_discover_tests_impl)
# note that the use of --list-reporters is not the important part, # note that the use of --list-reporters is not the important part,
# we only want to check whether the execution succeeds with ${reporter_arg} # we only want to check whether the execution succeeds with ${reporter_arg}
execute_process( execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters
OUTPUT_VARIABLE reporter_check_output OUTPUT_VARIABLE reporter_check_output
RESULT_VARIABLE reporter_check_result RESULT_VARIABLE reporter_check_result
WORKING_DIRECTORY "${TEST_WORKING_DIR}" WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
) )
if(${reporter_check_result} EQUAL 255) if(${reporter_check_result} EQUAL 255)
message(FATAL_ERROR message(FATAL_ERROR
@@ -99,7 +99,7 @@ function(catch_discover_tests_impl)
) )
elseif(NOT ${reporter_check_result} EQUAL 0) elseif(NOT ${reporter_check_result} EQUAL 0)
message(FATAL_ERROR message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n" "Error running test executable '${_TEST_EXECUTABLE}':\n"
" Result: ${reporter_check_result}\n" " Result: ${reporter_check_result}\n"
" Output: ${reporter_check_output}\n" " Output: ${reporter_check_output}\n"
) )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include "NullOStream.h" #include "NullOStream.h"
void NullOStream::avoidOutOfLineVirtualCompilerWarning() void NullOStream::avoidOutOfLineVirtualCompilerWarning()

View File

@@ -1,3 +1,11 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#pragma once #pragma once
#include <ostream> #include <ostream>

View File

@@ -1,4 +1,10 @@
//License: Boost 1.0
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
//By Paul Dreik 2020 //By Paul Dreik 2020
#include <catch2/internal/catch_test_spec_parser.hpp> #include <catch2/internal/catch_test_spec_parser.hpp>

View File

@@ -1,4 +1,10 @@
//License: Boost 1.0
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
//By Paul Dreik 2020 //By Paul Dreik 2020
#include <catch2/internal/catch_xmlwriter.hpp> #include <catch2/internal/catch_xmlwriter.hpp>

View File

@@ -1,4 +1,10 @@
//License: Boost 1.0
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
//By Paul Dreik 2020 //By Paul Dreik 2020
#include <catch2/internal/catch_textflow.hpp> #include <catch2/internal/catch_textflow.hpp>

View File

@@ -8,7 +8,7 @@
project( project(
'catch2', 'catch2',
'cpp', 'cpp',
version: '3.4.0', # CML version placeholder, don't delete version: '3.5.0', # CML version placeholder, don't delete
license: 'BSL-1.0', license: 'BSL-1.0',
meson_version: '>=0.54.1', meson_version: '>=0.54.1',
) )

View File

@@ -33,6 +33,7 @@ set(BENCHMARK_HEADERS
) )
set(BENCHMARK_SOURCES set(BENCHMARK_SOURCES
${SOURCES_DIR}/benchmark/catch_chronometer.cpp ${SOURCES_DIR}/benchmark/catch_chronometer.cpp
${SOURCES_DIR}/benchmark/detail/catch_analyse.cpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp ${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.cpp
${SOURCES_DIR}/benchmark/detail/catch_run_for_at_least.cpp ${SOURCES_DIR}/benchmark/detail/catch_run_for_at_least.cpp
${SOURCES_DIR}/benchmark/detail/catch_stats.cpp ${SOURCES_DIR}/benchmark/detail/catch_stats.cpp
@@ -92,6 +93,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_getenv.hpp ${SOURCES_DIR}/internal/catch_getenv.hpp
${SOURCES_DIR}/internal/catch_istream.hpp ${SOURCES_DIR}/internal/catch_istream.hpp
${SOURCES_DIR}/internal/catch_is_permutation.hpp ${SOURCES_DIR}/internal/catch_is_permutation.hpp
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp ${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp ${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_list.hpp ${SOURCES_DIR}/internal/catch_list.hpp
@@ -107,6 +109,8 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_polyfills.hpp ${SOURCES_DIR}/internal/catch_polyfills.hpp
${SOURCES_DIR}/internal/catch_preprocessor.hpp ${SOURCES_DIR}/internal/catch_preprocessor.hpp
${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp ${SOURCES_DIR}/internal/catch_preprocessor_remove_parens.hpp
${SOURCES_DIR}/internal/catch_random_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_random_integer_helpers.hpp
${SOURCES_DIR}/internal/catch_random_number_generator.hpp ${SOURCES_DIR}/internal/catch_random_number_generator.hpp
${SOURCES_DIR}/internal/catch_random_seed_generation.hpp ${SOURCES_DIR}/internal/catch_random_seed_generation.hpp
${SOURCES_DIR}/internal/catch_reporter_registry.hpp ${SOURCES_DIR}/internal/catch_reporter_registry.hpp
@@ -136,6 +140,8 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_textflow.hpp ${SOURCES_DIR}/internal/catch_textflow.hpp
${SOURCES_DIR}/internal/catch_to_string.hpp ${SOURCES_DIR}/internal/catch_to_string.hpp
${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp ${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp
${SOURCES_DIR}/internal/catch_uniform_floating_point_distribution.hpp
${SOURCES_DIR}/internal/catch_uniform_integer_distribution.hpp
${SOURCES_DIR}/internal/catch_unique_name.hpp ${SOURCES_DIR}/internal/catch_unique_name.hpp
${SOURCES_DIR}/internal/catch_unique_ptr.hpp ${SOURCES_DIR}/internal/catch_unique_ptr.hpp
${SOURCES_DIR}/internal/catch_void_type.hpp ${SOURCES_DIR}/internal/catch_void_type.hpp
@@ -176,6 +182,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp ${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
${SOURCES_DIR}/internal/catch_getenv.cpp ${SOURCES_DIR}/internal/catch_getenv.cpp
${SOURCES_DIR}/internal/catch_istream.cpp ${SOURCES_DIR}/internal/catch_istream.cpp
${SOURCES_DIR}/internal/catch_jsonwriter.cpp
${SOURCES_DIR}/internal/catch_lazy_expr.cpp ${SOURCES_DIR}/internal/catch_lazy_expr.cpp
${SOURCES_DIR}/internal/catch_leak_detector.cpp ${SOURCES_DIR}/internal/catch_leak_detector.cpp
${SOURCES_DIR}/internal/catch_list.cpp ${SOURCES_DIR}/internal/catch_list.cpp
@@ -288,6 +295,7 @@ set(REPORTER_HEADERS
${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.hpp ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.hpp
${SOURCES_DIR}/reporters/catch_reporter_event_listener.hpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.hpp
${SOURCES_DIR}/reporters/catch_reporter_helpers.hpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.hpp
${SOURCES_DIR}/reporters/catch_reporter_json.hpp
${SOURCES_DIR}/reporters/catch_reporter_junit.hpp ${SOURCES_DIR}/reporters/catch_reporter_junit.hpp
${SOURCES_DIR}/reporters/catch_reporter_multi.hpp ${SOURCES_DIR}/reporters/catch_reporter_multi.hpp
${SOURCES_DIR}/reporters/catch_reporter_registrars.hpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.hpp
@@ -306,6 +314,7 @@ set(REPORTER_SOURCES
${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.cpp ${SOURCES_DIR}/reporters/catch_reporter_cumulative_base.cpp
${SOURCES_DIR}/reporters/catch_reporter_event_listener.cpp ${SOURCES_DIR}/reporters/catch_reporter_event_listener.cpp
${SOURCES_DIR}/reporters/catch_reporter_helpers.cpp ${SOURCES_DIR}/reporters/catch_reporter_helpers.cpp
${SOURCES_DIR}/reporters/catch_reporter_json.cpp
${SOURCES_DIR}/reporters/catch_reporter_junit.cpp ${SOURCES_DIR}/reporters/catch_reporter_junit.cpp
${SOURCES_DIR}/reporters/catch_reporter_multi.cpp ${SOURCES_DIR}/reporters/catch_reporter_multi.cpp
${SOURCES_DIR}/reporters/catch_reporter_registrars.cpp ${SOURCES_DIR}/reporters/catch_reporter_registrars.cpp

View File

@@ -31,9 +31,7 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include <exception> #include <exception>
#include <functional>
#include <string> #include <string>
#include <vector>
#include <cmath> #include <cmath>
namespace Catch { namespace Catch {
@@ -47,16 +45,18 @@ namespace Catch {
: fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {} : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
template <typename Clock> template <typename Clock>
ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { ExecutionPlan prepare(const IConfig &cfg, Environment env) const {
auto min_time = env.clock_resolution.mean * Detail::minimum_ticks; auto min_time = env.clock_resolution.mean * Detail::minimum_ticks;
auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime())); auto run_time = std::max(min_time, std::chrono::duration_cast<decltype(min_time)>(cfg.benchmarkWarmupTime()));
auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(run_time), 1, fun); auto&& test = Detail::run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(run_time), 1, fun);
int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed)); int new_iters = static_cast<int>(std::ceil(min_time * test.iterations / test.elapsed));
return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FloatDuration<Clock>>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations }; return { new_iters, test.elapsed / test.iterations * new_iters * cfg.benchmarkSamples(), fun, std::chrono::duration_cast<FDuration>(cfg.benchmarkWarmupTime()), Detail::warmup_iterations };
} }
template <typename Clock = default_clock> template <typename Clock = default_clock>
void run() { void run() {
static_assert( Clock::is_steady,
"Benchmarking clock should be steady" );
auto const* cfg = getCurrentContext().getConfig(); auto const* cfg = getCurrentContext().getConfig();
auto env = Detail::measure_environment<Clock>(); auto env = Detail::measure_environment<Clock>();
@@ -83,8 +83,8 @@ namespace Catch {
return plan.template run<Clock>(*cfg, env); return plan.template run<Clock>(*cfg, env);
}); });
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end()); auto analysis = Detail::analyse(*cfg, samples.data(), samples.data() + samples.size());
BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance }; BenchmarkStats<> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats); getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException const&) { } CATCH_CATCH_ANON (TestFailureException const&) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr); getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);

View File

@@ -32,7 +32,10 @@ namespace Catch {
void start() override { started = Clock::now(); } void start() override { started = Clock::now(); }
void finish() override { finished = Clock::now(); } void finish() override { finished = Clock::now(); }
ClockDuration<Clock> elapsed() const { return finished - started; } IDuration elapsed() const {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
finished - started );
}
TimePoint<Clock> started; TimePoint<Clock> started;
TimePoint<Clock> finished; TimePoint<Clock> finished;

View File

@@ -11,28 +11,16 @@
#define CATCH_CLOCK_HPP_INCLUDED #define CATCH_CLOCK_HPP_INCLUDED
#include <chrono> #include <chrono>
#include <ratio>
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Clock> using IDuration = std::chrono::nanoseconds;
using ClockDuration = typename Clock::duration; using FDuration = std::chrono::duration<double, std::nano>;
template <typename Clock>
using FloatDuration = std::chrono::duration<double, typename Clock::period>;
template <typename Clock> template <typename Clock>
using TimePoint = typename Clock::time_point; using TimePoint = typename Clock::time_point;
using default_clock = std::chrono::steady_clock; using default_clock = std::chrono::steady_clock;
template <typename Clock>
struct now {
TimePoint<Clock> operator()() const {
return Clock::now();
}
};
using fp_seconds = std::chrono::duration<double, std::ratio<1>>;
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -15,21 +15,13 @@
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Duration>
struct EnvironmentEstimate { struct EnvironmentEstimate {
Duration mean; FDuration mean;
OutlierClassification outliers; OutlierClassification outliers;
template <typename Duration2>
operator EnvironmentEstimate<Duration2>() const {
return { mean, outliers };
}
}; };
template <typename Clock>
struct Environment { struct Environment {
using clock_type = Clock; EnvironmentEstimate clock_resolution;
EnvironmentEstimate<FloatDuration<Clock>> clock_resolution; EnvironmentEstimate clock_cost;
EnvironmentEstimate<FloatDuration<Clock>> clock_cost;
}; };
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -12,17 +12,12 @@
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Duration> template <typename Type>
struct Estimate { struct Estimate {
Duration point; Type point;
Duration lower_bound; Type lower_bound;
Duration upper_bound; Type upper_bound;
double confidence_interval; double confidence_interval;
template <typename Duration2>
operator Estimate<Duration2>() const {
return { point, lower_bound, upper_bound, confidence_interval };
}
}; };
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -21,33 +21,31 @@
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Duration>
struct ExecutionPlan { struct ExecutionPlan {
int iterations_per_sample; int iterations_per_sample;
Duration estimated_duration; FDuration estimated_duration;
Detail::BenchmarkFunction benchmark; Detail::BenchmarkFunction benchmark;
Duration warmup_time; FDuration warmup_time;
int warmup_iterations; int warmup_iterations;
template <typename Duration2>
operator ExecutionPlan<Duration2>() const {
return { iterations_per_sample, estimated_duration, benchmark, warmup_time, warmup_iterations };
}
template <typename Clock> template <typename Clock>
std::vector<FloatDuration<Clock>> run(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const { std::vector<FDuration> run(const IConfig &cfg, Environment env) const {
// warmup a bit // warmup a bit
Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{})); Detail::run_for_at_least<Clock>(
std::chrono::duration_cast<IDuration>( warmup_time ),
warmup_iterations,
Detail::repeat( []() { return Clock::now(); } )
);
std::vector<FloatDuration<Clock>> times; std::vector<FDuration> times;
const auto num_samples = cfg.benchmarkSamples(); const auto num_samples = cfg.benchmarkSamples();
times.reserve( num_samples ); times.reserve( num_samples );
for ( size_t i = 0; i < num_samples; ++i ) { for ( size_t i = 0; i < num_samples; ++i ) {
Detail::ChronometerModel<Clock> model; Detail::ChronometerModel<Clock> model;
this->benchmark( Chronometer( model, iterations_per_sample ) ); this->benchmark( Chronometer( model, iterations_per_sample ) );
auto sample_time = model.elapsed() - env.clock_cost.mean; auto sample_time = model.elapsed() - env.clock_cost.mean;
if ( sample_time < FloatDuration<Clock>::zero() ) { if ( sample_time < FDuration::zero() ) {
sample_time = FloatDuration<Clock>::zero(); sample_time = FDuration::zero();
} }
times.push_back(sample_time / iterations_per_sample); times.push_back(sample_time / iterations_per_sample);
} }

View File

@@ -70,7 +70,7 @@ namespace Catch {
template <typename Fn, typename... Args> template <typename Fn, typename... Args>
inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> { inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
CATCH_FORWARD(fn) (CATCH_FORWARD(args)...); CATCH_FORWARD((fn)) (CATCH_FORWARD(args)...);
} }
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -12,35 +12,18 @@
#include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/benchmark/catch_clock.hpp>
#include <vector> #include <vector>
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Duration>
struct SampleAnalysis { struct SampleAnalysis {
std::vector<Duration> samples; std::vector<FDuration> samples;
Estimate<Duration> mean; Estimate<FDuration> mean;
Estimate<Duration> standard_deviation; Estimate<FDuration> standard_deviation;
OutlierClassification outliers; OutlierClassification outliers;
double outlier_variance; double outlier_variance;
template <typename Duration2>
operator SampleAnalysis<Duration2>() const {
std::vector<Duration2> samples2;
samples2.reserve(samples.size());
for (auto const& d : samples) {
samples2.push_back(Duration2(d));
}
return {
CATCH_MOVE(samples2),
mean,
standard_deviation,
outliers,
outlier_variance,
};
}
}; };
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -0,0 +1,85 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
// Adapted from donated nonius code.
#include <catch2/benchmark/detail/catch_analyse.hpp>
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_sample_analysis.hpp>
#include <catch2/benchmark/detail/catch_stats.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <vector>
namespace Catch {
namespace Benchmark {
namespace Detail {
SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last) {
if (!cfg.benchmarkNoAnalysis()) {
std::vector<double> samples;
samples.reserve(static_cast<size_t>(last - first));
for (auto current = first; current != last; ++current) {
samples.push_back( current->count() );
}
auto analysis = Catch::Benchmark::Detail::analyse_samples(
cfg.benchmarkConfidenceInterval(),
cfg.benchmarkResamples(),
samples.data(),
samples.data() + samples.size() );
auto outliers = Catch::Benchmark::Detail::classify_outliers(
samples.data(), samples.data() + samples.size() );
auto wrap_estimate = [](Estimate<double> e) {
return Estimate<FDuration> {
FDuration(e.point),
FDuration(e.lower_bound),
FDuration(e.upper_bound),
e.confidence_interval,
};
};
std::vector<FDuration> samples2;
samples2.reserve(samples.size());
for (auto s : samples) {
samples2.push_back( FDuration( s ) );
}
return {
CATCH_MOVE(samples2),
wrap_estimate(analysis.mean),
wrap_estimate(analysis.standard_deviation),
outliers,
analysis.outlier_variance,
};
} else {
std::vector<FDuration> samples;
samples.reserve(static_cast<size_t>(last - first));
FDuration mean = FDuration(0);
int i = 0;
for (auto it = first; it < last; ++it, ++i) {
samples.push_back(FDuration(*it));
mean += FDuration(*it);
}
mean /= i;
return SampleAnalysis{
CATCH_MOVE(samples),
Estimate<FDuration>{ mean, mean, mean, 0.0 },
Estimate<FDuration>{ FDuration( 0 ),
FDuration( 0 ),
FDuration( 0 ),
0.0 },
OutlierClassification{},
0.0
};
}
}
} // namespace Detail
} // namespace Benchmark
} // namespace Catch

View File

@@ -10,71 +10,16 @@
#ifndef CATCH_ANALYSE_HPP_INCLUDED #ifndef CATCH_ANALYSE_HPP_INCLUDED
#define CATCH_ANALYSE_HPP_INCLUDED #define CATCH_ANALYSE_HPP_INCLUDED
#include <catch2/benchmark/catch_environment.hpp> #include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_sample_analysis.hpp> #include <catch2/benchmark/catch_sample_analysis.hpp>
#include <catch2/benchmark/detail/catch_stats.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <vector>
namespace Catch { namespace Catch {
class IConfig;
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
template <typename Duration, typename Iterator> SampleAnalysis analyse(const IConfig &cfg, FDuration* first, FDuration* last);
SampleAnalysis<Duration> analyse(const IConfig &cfg, Environment<Duration>, Iterator first, Iterator last) {
if (!cfg.benchmarkNoAnalysis()) {
std::vector<double> samples;
samples.reserve(static_cast<size_t>(last - first));
for (auto current = first; current != last; ++current) {
samples.push_back( current->count() );
}
auto analysis = Catch::Benchmark::Detail::analyse_samples(cfg.benchmarkConfidenceInterval(), cfg.benchmarkResamples(), samples.begin(), samples.end());
auto outliers = Catch::Benchmark::Detail::classify_outliers(samples.begin(), samples.end());
auto wrap_estimate = [](Estimate<double> e) {
return Estimate<Duration> {
Duration(e.point),
Duration(e.lower_bound),
Duration(e.upper_bound),
e.confidence_interval,
};
};
std::vector<Duration> samples2;
samples2.reserve(samples.size());
for (auto s : samples) {
samples2.push_back( Duration( s ) );
}
return {
CATCH_MOVE(samples2),
wrap_estimate(analysis.mean),
wrap_estimate(analysis.standard_deviation),
outliers,
analysis.outlier_variance,
};
} else {
std::vector<Duration> samples;
samples.reserve(static_cast<size_t>(last - first));
Duration mean = Duration(0);
int i = 0;
for (auto it = first; it < last; ++it, ++i) {
samples.push_back(Duration(*it));
mean += Duration(*it);
}
mean /= i;
return {
CATCH_MOVE(samples),
Estimate<Duration>{mean, mean, mean, 0.0},
Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
OutlierClassification{},
0.0
};
}
}
} // namespace Detail } // namespace Detail
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -8,7 +8,6 @@
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED #ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_HPP_INCLUDED
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp>
// The fwd decl & default specialization needs to be seen by VS2017 before // The fwd decl & default specialization needs to be seen by VS2017 before
@@ -30,32 +29,17 @@ namespace Catch {
double clockCost; double clockCost;
}; };
template <class Duration> // We need to keep template parameter for backwards compatibility,
// but we also do not want to use the template paraneter.
template <class Dummy>
struct BenchmarkStats { struct BenchmarkStats {
BenchmarkInfo info; BenchmarkInfo info;
std::vector<Duration> samples; std::vector<Benchmark::FDuration> samples;
Benchmark::Estimate<Duration> mean; Benchmark::Estimate<Benchmark::FDuration> mean;
Benchmark::Estimate<Duration> standardDeviation; Benchmark::Estimate<Benchmark::FDuration> standardDeviation;
Benchmark::OutlierClassification outliers; Benchmark::OutlierClassification outliers;
double outlierVariance; double outlierVariance;
template <typename Duration2>
operator BenchmarkStats<Duration2>() const {
std::vector<Duration2> samples2;
samples2.reserve(samples.size());
for (auto const& sample : samples) {
samples2.push_back(Duration2(sample));
}
return {
info,
CATCH_MOVE(samples2),
mean,
standardDeviation,
outliers,
outlierVariance,
};
}
}; };

View File

@@ -8,14 +8,14 @@
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED #define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#include <chrono> #include <catch2/benchmark/catch_clock.hpp>
namespace Catch { namespace Catch {
// We cannot forward declare the type with default template argument // We cannot forward declare the type with default template argument
// multiple times, so it is split out into a separate header so that // multiple times, so it is split out into a separate header so that
// we can prevent multiple declarations in dependees // we can prevent multiple declarations in dependees
template <typename Duration = std::chrono::duration<double, std::nano>> template <typename Duration = Benchmark::FDuration>
struct BenchmarkStats; struct BenchmarkStats;
} // end namespace Catch } // end namespace Catch

View File

@@ -55,23 +55,23 @@ namespace Catch {
template <typename Clock> template <typename Clock>
int warmup() { int warmup() {
return run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_seed, &resolution<Clock>) return run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(warmup_time), warmup_seed, &resolution<Clock>)
.iterations; .iterations;
} }
template <typename Clock> template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_resolution(int iterations) { EnvironmentEstimate estimate_clock_resolution(int iterations) {
auto r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_resolution_estimation_time), iterations, &resolution<Clock>) auto r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_resolution_estimation_time), iterations, &resolution<Clock>)
.result; .result;
return { return {
FloatDuration<Clock>(mean(r.begin(), r.end())), FDuration(mean(r.data(), r.data() + r.size())),
classify_outliers(r.begin(), r.end()), classify_outliers(r.data(), r.data() + r.size()),
}; };
} }
template <typename Clock> template <typename Clock>
EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) { EnvironmentEstimate estimate_clock_cost(FDuration resolution) {
auto time_limit = (std::min)( auto time_limit = (std::min)(
resolution * clock_cost_estimation_tick_limit, resolution * clock_cost_estimation_tick_limit,
FloatDuration<Clock>(clock_cost_estimation_time_limit)); FDuration(clock_cost_estimation_time_limit));
auto time_clock = [](int k) { auto time_clock = [](int k) {
return Detail::measure<Clock>([k] { return Detail::measure<Clock>([k] {
for (int i = 0; i < k; ++i) { for (int i = 0; i < k; ++i) {
@@ -82,7 +82,7 @@ namespace Catch {
}; };
time_clock(1); time_clock(1);
int iters = clock_cost_estimation_iterations; int iters = clock_cost_estimation_iterations;
auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(clock_cost_estimation_time), iters, time_clock); auto&& r = run_for_at_least<Clock>(std::chrono::duration_cast<IDuration>(clock_cost_estimation_time), iters, time_clock);
std::vector<double> times; std::vector<double> times;
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed)); int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
times.reserve(static_cast<size_t>(nsamples)); times.reserve(static_cast<size_t>(nsamples));
@@ -92,18 +92,18 @@ namespace Catch {
.count() ) ); .count() ) );
} }
return { return {
FloatDuration<Clock>(mean(times.begin(), times.end())), FDuration(mean(times.data(), times.data() + times.size())),
classify_outliers(times.begin(), times.end()), classify_outliers(times.data(), times.data() + times.size()),
}; };
} }
template <typename Clock> template <typename Clock>
Environment<FloatDuration<Clock>> measure_environment() { Environment measure_environment() {
#if defined(__clang__) #if defined(__clang__)
# pragma clang diagnostic push # pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wexit-time-destructors" # pragma clang diagnostic ignored "-Wexit-time-destructors"
#endif #endif
static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env; static Catch::Detail::unique_ptr<Environment> env;
#if defined(__clang__) #if defined(__clang__)
# pragma clang diagnostic pop # pragma clang diagnostic pop
#endif #endif
@@ -115,7 +115,7 @@ namespace Catch {
auto resolution = Detail::estimate_clock_resolution<Clock>(iters); auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean); auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
env = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} ); env = Catch::Detail::make_unique<Environment>( Environment{resolution, cost} );
return *env; return *env;
} }
} // namespace Detail } // namespace Detail

View File

@@ -18,7 +18,7 @@ namespace Catch {
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
template <typename Clock, typename Fun, typename... Args> template <typename Clock, typename Fun, typename... Args>
TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) { TimingOf<Fun, Args...> measure(Fun&& fun, Args&&... args) {
auto start = Clock::now(); auto start = Clock::now();
auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...); auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
auto end = Clock::now(); auto end = Clock::now();

View File

@@ -24,11 +24,11 @@ namespace Catch {
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
template <typename Clock, typename Fun> template <typename Clock, typename Fun>
TimingOf<Clock, Fun, int> measure_one(Fun&& fun, int iters, std::false_type) { TimingOf<Fun, int> measure_one(Fun&& fun, int iters, std::false_type) {
return Detail::measure<Clock>(fun, iters); return Detail::measure<Clock>(fun, iters);
} }
template <typename Clock, typename Fun> template <typename Clock, typename Fun>
TimingOf<Clock, Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) { TimingOf<Fun, Chronometer> measure_one(Fun&& fun, int iters, std::true_type) {
Detail::ChronometerModel<Clock> meter; Detail::ChronometerModel<Clock> meter;
auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters)); auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
@@ -43,8 +43,8 @@ namespace Catch {
void throw_optimized_away_error(); void throw_optimized_away_error();
template <typename Clock, typename Fun> template <typename Clock, typename Fun>
TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> TimingOf<Fun, run_for_at_least_argument_t<Clock, Fun>>
run_for_at_least(ClockDuration<Clock> how_long, run_for_at_least(IDuration how_long,
const int initial_iterations, const int initial_iterations,
Fun&& fun) { Fun&& fun) {
auto iters = initial_iterations; auto iters = initial_iterations;

View File

@@ -10,8 +10,12 @@
#include <catch2/benchmark/detail/catch_stats.hpp> #include <catch2/benchmark/detail/catch_stats.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
#include <algorithm>
#include <cassert> #include <cassert>
#include <cmath>
#include <cstddef> #include <cstddef>
#include <numeric> #include <numeric>
#include <random> #include <random>
@@ -30,8 +34,8 @@ namespace Catch {
static sample static sample
resample( URng& rng, resample( URng& rng,
unsigned int resamples, unsigned int resamples,
std::vector<double>::const_iterator first, double const* first,
std::vector<double>::const_iterator last, double const* last,
Estimator& estimator ) { Estimator& estimator ) {
auto n = static_cast<size_t>( last - first ); auto n = static_cast<size_t>( last - first );
std::uniform_int_distribution<decltype( n )> dist( 0, std::uniform_int_distribution<decltype( n )> dist( 0,
@@ -51,7 +55,7 @@ namespace Catch {
dist( rng ) )] ); dist( rng ) )] );
} }
const auto estimate = const auto estimate =
estimator( resampled.begin(), resampled.end() ); estimator( resampled.data(), resampled.data() + resampled.size() );
out.push_back( estimate ); out.push_back( estimate );
} }
std::sort( out.begin(), out.end() ); std::sort( out.begin(), out.end() );
@@ -168,8 +172,7 @@ namespace Catch {
} }
static double static double
standard_deviation( std::vector<double>::const_iterator first, standard_deviation( double const* first, double const* last ) {
std::vector<double>::const_iterator last ) {
auto m = Catch::Benchmark::Detail::mean( first, last ); auto m = Catch::Benchmark::Detail::mean( first, last );
double variance = double variance =
std::accumulate( first, std::accumulate( first,
@@ -183,6 +186,23 @@ namespace Catch {
return std::sqrt( variance ); return std::sqrt( variance );
} }
static sample jackknife( double ( *estimator )( double const*,
double const* ),
double* first,
double* last ) {
const auto second = first + 1;
sample results;
results.reserve( static_cast<size_t>( last - first ) );
for ( auto it = first; it != last; ++it ) {
std::iter_swap( it, first );
results.push_back( estimator( second, last ) );
}
return results;
}
} // namespace } // namespace
} // namespace Detail } // namespace Detail
} // namespace Benchmark } // namespace Benchmark
@@ -192,23 +212,17 @@ namespace Catch {
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
#if defined( __GNUC__ ) || defined( __clang__ ) double weighted_average_quantile( int k,
# pragma GCC diagnostic push int q,
# pragma GCC diagnostic ignored "-Wfloat-equal" double* first,
#endif double* last ) {
bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic pop
#endif
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last) {
auto count = last - first; auto count = last - first;
double idx = (count - 1) * k / static_cast<double>(q); double idx = (count - 1) * k / static_cast<double>(q);
int j = static_cast<int>(idx); int j = static_cast<int>(idx);
double g = idx - j; double g = idx - j;
std::nth_element(first, first + j, last); std::nth_element(first, first + j, last);
auto xj = first[j]; auto xj = first[j];
if ( directCompare( g, 0 ) ) { if ( Catch::Detail::directCompare( g, 0 ) ) {
return xj; return xj;
} }
@@ -217,12 +231,11 @@ namespace Catch {
} }
OutlierClassification OutlierClassification
classify_outliers( std::vector<double>::const_iterator first, classify_outliers( double const* first, double const* last ) {
std::vector<double>::const_iterator last ) {
std::vector<double> copy( first, last ); std::vector<double> copy( first, last );
auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() ); auto q1 = weighted_average_quantile( 1, 4, copy.data(), copy.data() + copy.size() );
auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() ); auto q3 = weighted_average_quantile( 3, 4, copy.data(), copy.data() + copy.size() );
auto iqr = q3 - q1; auto iqr = q3 - q1;
auto los = q1 - ( iqr * 3. ); auto los = q1 - ( iqr * 3. );
auto lom = q1 - ( iqr * 1.5 ); auto lom = q1 - ( iqr * 1.5 );
@@ -246,8 +259,7 @@ namespace Catch {
return o; return o;
} }
double mean( std::vector<double>::const_iterator first, double mean( double const* first, double const* last ) {
std::vector<double>::const_iterator last ) {
auto count = last - first; auto count = last - first;
double sum = 0.; double sum = 0.;
while (first != last) { while (first != last) {
@@ -257,6 +269,9 @@ namespace Catch {
return sum / static_cast<double>(count); return sum / static_cast<double>(count);
} }
double normal_cdf( double x ) {
return std::erfc( -x / std::sqrt( 2.0 ) ) / 2.0;
}
double erfc_inv(double x) { double erfc_inv(double x) {
return erf_inv(1.0 - x); return erf_inv(1.0 - x);
@@ -278,26 +293,77 @@ namespace Catch {
return result; return result;
} }
Estimate<double>
bootstrap( double confidence_level,
double* first,
double* last,
sample const& resample,
double ( *estimator )( double const*, double const* ) ) {
auto n_samples = last - first;
double point = estimator( first, last );
// Degenerate case with a single sample
if ( n_samples == 1 )
return { point, point, point, confidence_level };
sample jack = jackknife( estimator, first, last );
double jack_mean =
mean( jack.data(), jack.data() + jack.size() );
double sum_squares = 0, sum_cubes = 0;
for ( double x : jack ) {
auto difference = jack_mean - x;
auto square = difference * difference;
auto cube = square * difference;
sum_squares += square;
sum_cubes += cube;
}
double accel = sum_cubes / ( 6 * std::pow( sum_squares, 1.5 ) );
long n = static_cast<long>( resample.size() );
double prob_n =
std::count_if( resample.begin(),
resample.end(),
[point]( double x ) { return x < point; } ) /
static_cast<double>( n );
// degenerate case with uniform samples
if ( Catch::Detail::directCompare( prob_n, 0. ) ) {
return { point, point, point, confidence_level };
}
double bias = normal_quantile( prob_n );
double z1 = normal_quantile( ( 1. - confidence_level ) / 2. );
auto cumn = [n]( double x ) -> long {
return std::lround( normal_cdf( x ) *
static_cast<double>( n ) );
};
auto a = [bias, accel]( double b ) {
return bias + b / ( 1. - accel * b );
};
double b1 = bias + z1;
double b2 = bias - z1;
double a1 = a( b1 );
double a2 = a( b2 );
auto lo = static_cast<size_t>( (std::max)( cumn( a1 ), 0l ) );
auto hi =
static_cast<size_t>( (std::min)( cumn( a2 ), n - 1 ) );
return { point, resample[lo], resample[hi], confidence_level };
}
bootstrap_analysis analyse_samples(double confidence_level, bootstrap_analysis analyse_samples(double confidence_level,
unsigned int n_resamples, unsigned int n_resamples,
std::vector<double>::iterator first, double* first,
std::vector<double>::iterator last) { double* last) {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static std::random_device entropy;
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
auto mean = &Detail::mean; auto mean = &Detail::mean;
auto stddev = &standard_deviation; auto stddev = &standard_deviation;
#if defined(CATCH_CONFIG_USE_ASYNC) #if defined(CATCH_CONFIG_USE_ASYNC)
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, auto Estimate = [=](double(*f)(double const*, double const*)) {
std::vector<double>::const_iterator)) { std::random_device rd;
auto seed = entropy(); auto seed = rd();
return std::async(std::launch::async, [=] { return std::async(std::launch::async, [=] {
std::mt19937 rng(seed); SimplePcg32 rng( seed );
auto resampled = resample(rng, n_resamples, first, last, f); auto resampled = resample(rng, n_resamples, first, last, f);
return bootstrap(confidence_level, first, last, resampled, f); return bootstrap(confidence_level, first, last, resampled, f);
}); });
@@ -309,10 +375,10 @@ namespace Catch {
auto mean_estimate = mean_future.get(); auto mean_estimate = mean_future.get();
auto stddev_estimate = stddev_future.get(); auto stddev_estimate = stddev_future.get();
#else #else
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator, auto Estimate = [=](double(*f)(double const* , double const*)) {
std::vector<double>::const_iterator)) { std::random_device rd;
auto seed = entropy(); auto seed = rd();
std::mt19937 rng(seed); SimplePcg32 rng( seed );
auto resampled = resample(rng, n_resamples, first, last, f); auto resampled = resample(rng, n_resamples, first, last, f);
return bootstrap(confidence_level, first, last, resampled, f); return bootstrap(confidence_level, first, last, resampled, f);
}; };
@@ -321,6 +387,7 @@ namespace Catch {
auto stddev_estimate = Estimate(stddev); auto stddev_estimate = Estimate(stddev);
#endif // CATCH_USE_ASYNC #endif // CATCH_USE_ASYNC
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n); double outlier_variance = Detail::outlier_variance(mean_estimate, stddev_estimate, n);
return { mean_estimate, stddev_estimate, outlier_variance }; return { mean_estimate, stddev_estimate, outlier_variance };

View File

@@ -13,100 +13,35 @@
#include <catch2/benchmark/catch_estimate.hpp> #include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp> #include <catch2/benchmark/catch_outlier_classification.hpp>
#include <algorithm>
#include <vector> #include <vector>
#include <cmath>
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
namespace Detail { namespace Detail {
using sample = std::vector<double>; using sample = std::vector<double>;
// Used when we know we want == comparison of two doubles double weighted_average_quantile( int k,
// to centralize warning suppression int q,
bool directCompare( double lhs, double rhs ); double* first,
double* last );
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
OutlierClassification OutlierClassification
classify_outliers( std::vector<double>::const_iterator first, classify_outliers( double const* first, double const* last );
std::vector<double>::const_iterator last );
double mean( std::vector<double>::const_iterator first, double mean( double const* first, double const* last );
std::vector<double>::const_iterator last );
template <typename Estimator> double normal_cdf( double x );
sample jackknife(Estimator&& estimator,
std::vector<double>::iterator first,
std::vector<double>::iterator last) {
auto n = static_cast<size_t>(last - first);
auto second = first;
++second;
sample results;
results.reserve(n);
for (auto it = first; it != last; ++it) {
std::iter_swap(it, first);
results.push_back(estimator(second, last));
}
return results;
}
inline double normal_cdf(double x) {
return std::erfc(-x / std::sqrt(2.0)) / 2.0;
}
double erfc_inv(double x); double erfc_inv(double x);
double normal_quantile(double p); double normal_quantile(double p);
template <typename Estimator> Estimate<double>
Estimate<double> bootstrap( double confidence_level, bootstrap( double confidence_level,
std::vector<double>::iterator first, double* first,
std::vector<double>::iterator last, double* last,
sample const& resample, sample const& resample,
Estimator&& estimator ) { double ( *estimator )( double const*, double const* ) );
auto n_samples = last - first;
double point = estimator(first, last);
// Degenerate case with a single sample
if (n_samples == 1) return { point, point, point, confidence_level };
sample jack = jackknife(estimator, first, last);
double jack_mean = mean(jack.begin(), jack.end());
double sum_squares = 0, sum_cubes = 0;
for (double x : jack) {
auto difference = jack_mean - x;
auto square = difference * difference;
auto cube = square * difference;
sum_squares += square; sum_cubes += cube;
}
double accel = sum_cubes / (6 * std::pow(sum_squares, 1.5));
long n = static_cast<long>(resample.size());
double prob_n = std::count_if(resample.begin(), resample.end(), [point](double x) { return x < point; }) / static_cast<double>(n);
// degenerate case with uniform samples
if ( directCompare( prob_n, 0. ) ) {
return { point, point, point, confidence_level };
}
double bias = normal_quantile(prob_n);
double z1 = normal_quantile((1. - confidence_level) / 2.);
auto cumn = [n]( double x ) -> long {
return std::lround( normal_cdf( x ) * static_cast<double>(n) );
};
auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); };
double b1 = bias + z1;
double b2 = bias - z1;
double a1 = a(b1);
double a2 = a(b2);
auto lo = static_cast<size_t>((std::max)(cumn(a1), 0l));
auto hi = static_cast<size_t>((std::min)(cumn(a2), n - 1));
return { point, resample[lo], resample[hi], confidence_level };
}
struct bootstrap_analysis { struct bootstrap_analysis {
Estimate<double> mean; Estimate<double> mean;
@@ -116,8 +51,8 @@ namespace Catch {
bootstrap_analysis analyse_samples(double confidence_level, bootstrap_analysis analyse_samples(double confidence_level,
unsigned int n_resamples, unsigned int n_resamples,
std::vector<double>::iterator first, double* first,
std::vector<double>::iterator last); double* last);
} // namespace Detail } // namespace Detail
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -17,14 +17,14 @@
namespace Catch { namespace Catch {
namespace Benchmark { namespace Benchmark {
template <typename Duration, typename Result> template <typename Result>
struct Timing { struct Timing {
Duration elapsed; IDuration elapsed;
Result result; Result result;
int iterations; int iterations;
}; };
template <typename Clock, typename Func, typename... Args> template <typename Func, typename... Args>
using TimingOf = Timing<ClockDuration<Clock>, Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>; using TimingOf = Timing<Detail::CompleteType_t<FunctionReturnType<Func, Args...>>>;
} // namespace Benchmark } // namespace Benchmark
} // namespace Catch } // namespace Catch

View File

@@ -54,6 +54,7 @@
#include <catch2/internal/catch_compiler_capabilities.hpp> #include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_android_logwrite.hpp> #include <catch2/internal/catch_config_android_logwrite.hpp>
#include <catch2/internal/catch_config_counter.hpp> #include <catch2/internal/catch_config_counter.hpp>
#include <catch2/internal/catch_config_prefix_messages.hpp>
#include <catch2/internal/catch_config_static_analysis_support.hpp> #include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_config_uncaught_exceptions.hpp> #include <catch2/internal/catch_config_uncaught_exceptions.hpp>
#include <catch2/internal/catch_config_wchar.hpp> #include <catch2/internal/catch_config_wchar.hpp>
@@ -73,6 +74,7 @@
#include <catch2/internal/catch_getenv.hpp> #include <catch2/internal/catch_getenv.hpp>
#include <catch2/internal/catch_is_permutation.hpp> #include <catch2/internal/catch_is_permutation.hpp>
#include <catch2/internal/catch_istream.hpp> #include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp> #include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp> #include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_list.hpp> #include <catch2/internal/catch_list.hpp>
@@ -89,6 +91,8 @@
#include <catch2/internal/catch_preprocessor.hpp> #include <catch2/internal/catch_preprocessor.hpp>
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp> #include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/internal/catch_preprocessor_remove_parens.hpp> #include <catch2/internal/catch_preprocessor_remove_parens.hpp>
#include <catch2/internal/catch_random_floating_point_helpers.hpp>
#include <catch2/internal/catch_random_integer_helpers.hpp>
#include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp> #include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_reporter_registry.hpp> #include <catch2/internal/catch_reporter_registry.hpp>
@@ -118,6 +122,8 @@
#include <catch2/internal/catch_textflow.hpp> #include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_to_string.hpp> #include <catch2/internal/catch_to_string.hpp>
#include <catch2/internal/catch_uncaught_exceptions.hpp> #include <catch2/internal/catch_uncaught_exceptions.hpp>
#include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
#include <catch2/internal/catch_unique_name.hpp> #include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_unique_ptr.hpp> #include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_void_type.hpp> #include <catch2/internal/catch_void_type.hpp>

View File

@@ -8,11 +8,13 @@
#ifndef CATCH_MESSAGE_HPP_INCLUDED #ifndef CATCH_MESSAGE_HPP_INCLUDED
#define CATCH_MESSAGE_HPP_INCLUDED #define CATCH_MESSAGE_HPP_INCLUDED
#include <catch2/internal/catch_config_prefix_messages.hpp>
#include <catch2/internal/catch_result_type.hpp> #include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp> #include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp> #include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_message_info.hpp> #include <catch2/internal/catch_message_info.hpp>
#include <catch2/catch_tostring.hpp> #include <catch2/catch_tostring.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -112,28 +114,28 @@ namespace Catch {
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log ) Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg ) #define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg ) #define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ ) #define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ )
#elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define CATCH_INFO( msg ) (void)(0) #define CATCH_INFO( msg ) (void)(0)
#define CATCH_UNSCOPED_INFO( msg ) (void)(0) #define CATCH_UNSCOPED_INFO( msg ) (void)(0)
#define CATCH_WARN( msg ) (void)(0) #define CATCH_WARN( msg ) (void)(0)
#define CATCH_CAPTURE( ... ) (void)(0) #define CATCH_CAPTURE( ... ) (void)(0)
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg ) #define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg ) #define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg ) #define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ ) #define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ )
#elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) (void)(0) #define INFO( msg ) (void)(0)
#define UNSCOPED_INFO( msg ) (void)(0) #define UNSCOPED_INFO( msg ) (void)(0)

View File

@@ -398,6 +398,12 @@ namespace Catch {
} }
} }
}; };
template <>
struct StringMaker<std::nullopt_t> {
static std::string convert(const std::nullopt_t&) {
return "{ }";
}
};
} }
#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER #endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER

View File

@@ -198,6 +198,7 @@
#cmakedefine CATCH_CONFIG_FAST_COMPILE #cmakedefine CATCH_CONFIG_FAST_COMPILE
#cmakedefine CATCH_CONFIG_NOSTDOUT #cmakedefine CATCH_CONFIG_NOSTDOUT
#cmakedefine CATCH_CONFIG_PREFIX_ALL #cmakedefine CATCH_CONFIG_PREFIX_ALL
#cmakedefine CATCH_CONFIG_PREFIX_MESSAGES
#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG #cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG
#cmakedefine CATCH_CONFIG_SHARED_LIBRARY #cmakedefine CATCH_CONFIG_SHARED_LIBRARY

View File

@@ -36,7 +36,7 @@ namespace Catch {
} }
Version const& libraryVersion() { Version const& libraryVersion() {
static Version version( 3, 4, 0, "", 0 ); static Version version( 3, 5, 0, "", 0 );
return version; return version;
} }

View File

@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED #define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3 #define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 4 #define CATCH_VERSION_MINOR 5
#define CATCH_VERSION_PATCH 0 #define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED #endif // CATCH_VERSION_MACROS_HPP_INCLUDED

View File

@@ -7,7 +7,35 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/generators/catch_generators_random.hpp> #include <catch2/generators/catch_generators_random.hpp>
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
std::uint32_t Catch::Generators::Detail::getSeed() { return sharedRng()(); } #include <random>
namespace Catch {
namespace Generators {
namespace Detail {
std::uint32_t getSeed() { return sharedRng()(); }
} // namespace Detail
struct RandomFloatingGenerator<long double>::PImpl {
PImpl( long double a, long double b, uint32_t seed ):
rng( seed ), dist( a, b ) {}
Catch::SimplePcg32 rng;
std::uniform_real_distribution<long double> dist;
};
RandomFloatingGenerator<long double>::RandomFloatingGenerator(
long double a, long double b, std::uint32_t seed) :
m_pimpl(Catch::Detail::make_unique<PImpl>(a, b, seed)) {
static_cast<void>( next() );
}
RandomFloatingGenerator<long double>::~RandomFloatingGenerator() =
default;
bool RandomFloatingGenerator<long double>::next() {
m_current_number = m_pimpl->dist( m_pimpl->rng );
return true;
}
} // namespace Generators
} // namespace Catch

View File

@@ -11,8 +11,9 @@
#include <catch2/internal/catch_context.hpp> #include <catch2/internal/catch_context.hpp>
#include <catch2/generators/catch_generators.hpp> #include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_random_number_generator.hpp> #include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
#include <random> #include <catch2/internal/catch_uniform_floating_point_distribution.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
namespace Catch { namespace Catch {
namespace Generators { namespace Generators {
@@ -26,7 +27,7 @@ namespace Detail {
template <typename Float> template <typename Float>
class RandomFloatingGenerator final : public IGenerator<Float> { class RandomFloatingGenerator final : public IGenerator<Float> {
Catch::SimplePcg32 m_rng; Catch::SimplePcg32 m_rng;
std::uniform_real_distribution<Float> m_dist; Catch::uniform_floating_point_distribution<Float> m_dist;
Float m_current_number; Float m_current_number;
public: public:
RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ): RandomFloatingGenerator( Float a, Float b, std::uint32_t seed ):
@@ -44,10 +45,27 @@ public:
} }
}; };
template <>
class RandomFloatingGenerator<long double> final : public IGenerator<long double> {
// We still rely on <random> for this specialization, but we don't
// want to drag it into the header.
struct PImpl;
Catch::Detail::unique_ptr<PImpl> m_pimpl;
long double m_current_number;
public:
RandomFloatingGenerator( long double a, long double b, std::uint32_t seed );
long double const& get() const override { return m_current_number; }
bool next() override;
~RandomFloatingGenerator() override; // = default
};
template <typename Integer> template <typename Integer>
class RandomIntegerGenerator final : public IGenerator<Integer> { class RandomIntegerGenerator final : public IGenerator<Integer> {
Catch::SimplePcg32 m_rng; Catch::SimplePcg32 m_rng;
std::uniform_int_distribution<Integer> m_dist; Catch::uniform_integer_distribution<Integer> m_dist;
Integer m_current_number; Integer m_current_number;
public: public:
RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ): RandomIntegerGenerator( Integer a, Integer b, std::uint32_t seed ):
@@ -68,14 +86,6 @@ public:
template <typename T> template <typename T>
std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>> std::enable_if_t<std::is_integral<T>::value, GeneratorWrapper<T>>
random(T a, T b) { random(T a, T b) {
static_assert(
!std::is_same<T, char>::value &&
!std::is_same<T, int8_t>::value &&
!std::is_same<T, uint8_t>::value &&
!std::is_same<T, signed char>::value &&
!std::is_same<T, unsigned char>::value &&
!std::is_same<T, bool>::value,
"The requested type is not supported by the underlying random distributions from std" );
return GeneratorWrapper<T>( return GeneratorWrapper<T>(
Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed()) Catch::Detail::make_unique<RandomIntegerGenerator<T>>(a, b, Detail::getSeed())
); );

View File

@@ -96,10 +96,11 @@ GeneratorWrapper<ResultType> from_range(InputIterator from, InputSentinel to) {
return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to)); return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(from, to));
} }
template <typename Container, template <typename Container>
typename ResultType = typename Container::value_type> auto from_range(Container const& cnt) {
GeneratorWrapper<ResultType> from_range(Container const& cnt) { using std::begin;
return GeneratorWrapper<ResultType>(Catch::Detail::make_unique<IteratorGenerator<ResultType>>(cnt.begin(), cnt.end())); using std::end;
return from_range( begin( cnt ), end( cnt ) );
} }

View File

@@ -156,7 +156,9 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
// Assume that some platforms do not support getenv. // Assume that some platforms do not support getenv.
#if defined(CATCH_PLATFORM_WINDOWS_UWP) || defined(CATCH_PLATFORM_PLAYSTATION) #if defined( CATCH_PLATFORM_WINDOWS_UWP ) || \
defined( CATCH_PLATFORM_PLAYSTATION ) || \
defined( _GAMING_XBOX )
# define CATCH_INTERNAL_CONFIG_NO_GETENV # define CATCH_INTERNAL_CONFIG_NO_GETENV
#else #else
# define CATCH_INTERNAL_CONFIG_GETENV # define CATCH_INTERNAL_CONFIG_GETENV

View File

@@ -0,0 +1,29 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
/** \file
* Wrapper for the CATCH_CONFIG_PREFIX_MESSAGES configuration option
*
* CATCH_CONFIG_PREFIX_ALL can be used to avoid clashes with other macros
* by prepending CATCH_. This may not be desirable if the only clashes are with
* logger macros such as INFO and WARN. In this cases
* CATCH_CONFIG_PREFIX_MESSAGES can be used to only prefix a small subset
* of relevant macros.
*
*/
#ifndef CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
#define CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
#if defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_PREFIX_MESSAGES)
#define CATCH_CONFIG_PREFIX_MESSAGES
#endif
#endif // CATCH_CONFIG_PREFIX_MESSAGES_HPP_INCLUDED

View File

@@ -39,7 +39,7 @@ namespace Catch {
return parsed; return parsed;
} }
EnumInfo::~EnumInfo() {} EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const { StringRef EnumInfo::lookup( int value ) const {
for( auto const& valueToName : m_values ) { for( auto const& valueToName : m_values ) {

View File

@@ -15,6 +15,7 @@
namespace Catch { namespace Catch {
#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
namespace { namespace {
static std::string tryTranslators( static std::string tryTranslators(
std::vector< std::vector<
@@ -28,9 +29,9 @@ namespace Catch {
} }
} }
#endif //!defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() { ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() = default;
}
void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) { void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
m_translators.push_back( CATCH_MOVE( translator ) ); m_translators.push_back( CATCH_MOVE( translator ) );

View File

@@ -27,6 +27,17 @@ namespace Catch {
return i; return i;
} }
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
bool directCompare( float lhs, float rhs ) { return lhs == rhs; }
bool directCompare( double lhs, double rhs ) { return lhs == rhs; }
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic pop
#endif
} // end namespace Detail } // end namespace Detail
} // end namespace Catch } // end namespace Catch

View File

@@ -22,6 +22,11 @@ namespace Catch {
uint32_t convertToBits(float f); uint32_t convertToBits(float f);
uint64_t convertToBits(double d); uint64_t convertToBits(double d);
// Used when we know we want == comparison of two doubles
// to centralize warning suppression
bool directCompare( float lhs, float rhs );
bool directCompare( double lhs, double rhs );
} // end namespace Detail } // end namespace Detail

View File

@@ -0,0 +1,148 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
namespace Catch {
void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
for ( std::uint64_t i = 0; i < level; ++i ) {
os << " ";
}
}
void JsonUtils::appendCommaNewline( std::ostream& os,
bool& should_comma,
std::uint64_t level ) {
if ( should_comma ) { os << ','; }
should_comma = true;
os << '\n';
indent( os, level );
}
JsonObjectWriter::JsonObjectWriter( std::ostream& os ):
JsonObjectWriter{ os, 0 } {}
JsonObjectWriter::JsonObjectWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {
m_os << '{';
}
JsonObjectWriter::JsonObjectWriter( JsonObjectWriter&& source ):
m_os{ source.m_os },
m_indent_level{ source.m_indent_level },
m_should_comma{ source.m_should_comma },
m_active{ source.m_active } {
source.m_active = false;
}
JsonObjectWriter::~JsonObjectWriter() {
if ( !m_active ) { return; }
m_os << '\n';
JsonUtils::indent( m_os, m_indent_level );
m_os << '}';
}
JsonValueWriter JsonObjectWriter::write( StringRef key ) {
JsonUtils::appendCommaNewline(
m_os, m_should_comma, m_indent_level + 1 );
m_os << '"' << key << "\": ";
return JsonValueWriter{ m_os, m_indent_level + 1 };
}
JsonArrayWriter::JsonArrayWriter( std::ostream& os ):
JsonArrayWriter{ os, 0 } {}
JsonArrayWriter::JsonArrayWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {
m_os << '[';
}
JsonArrayWriter::JsonArrayWriter( JsonArrayWriter&& source ):
m_os{ source.m_os },
m_indent_level{ source.m_indent_level },
m_should_comma{ source.m_should_comma },
m_active{ source.m_active } {
source.m_active = false;
}
JsonArrayWriter::~JsonArrayWriter() {
if ( !m_active ) { return; }
m_os << '\n';
JsonUtils::indent( m_os, m_indent_level );
m_os << ']';
}
JsonObjectWriter JsonArrayWriter::writeObject() {
JsonUtils::appendCommaNewline(
m_os, m_should_comma, m_indent_level + 1 );
return JsonObjectWriter{ m_os, m_indent_level + 1 };
}
JsonArrayWriter JsonArrayWriter::writeArray() {
JsonUtils::appendCommaNewline(
m_os, m_should_comma, m_indent_level + 1 );
return JsonArrayWriter{ m_os, m_indent_level + 1 };
}
JsonArrayWriter& JsonArrayWriter::write( bool value ) {
return writeImpl( value );
}
JsonValueWriter::JsonValueWriter( std::ostream& os ):
JsonValueWriter{ os, 0 } {}
JsonValueWriter::JsonValueWriter( std::ostream& os,
std::uint64_t indent_level ):
m_os{ os }, m_indent_level{ indent_level } {}
JsonObjectWriter JsonValueWriter::writeObject() && {
return JsonObjectWriter{ m_os, m_indent_level };
}
JsonArrayWriter JsonValueWriter::writeArray() && {
return JsonArrayWriter{ m_os, m_indent_level };
}
void JsonValueWriter::write( Catch::StringRef value ) && {
writeImpl( value, true );
}
void JsonValueWriter::write( bool value ) && {
writeImpl( value ? "true"_sr : "false"_sr, false );
}
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
for (char c : value) {
// Escape list taken from https://www.json.org/json-en.html,
// string definition.
// Note that while forward slash _can_ be escaped, it does
// not have to be, if JSON is not further embedded somewhere
// where forward slash is meaningful.
if ( c == '"' ) {
m_os << "\\\"";
} else if ( c == '\\' ) {
m_os << "\\\\";
} else if ( c == '\b' ) {
m_os << "\\b";
} else if ( c == '\f' ) {
m_os << "\\f";
} else if ( c == '\n' ) {
m_os << "\\n";
} else if ( c == '\r' ) {
m_os << "\\r";
} else if ( c == '\t' ) {
m_os << "\\t";
} else {
m_os << c;
}
}
if ( quote ) { m_os << '"'; }
}
} // namespace Catch

View File

@@ -0,0 +1,120 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
#define CATCH_JSONWRITER_HPP_INCLUDED
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <cstdint>
#include <sstream>
namespace Catch {
class JsonObjectWriter;
class JsonArrayWriter;
struct JsonUtils {
static void indent( std::ostream& os, std::uint64_t level );
static void appendCommaNewline( std::ostream& os,
bool& should_comma,
std::uint64_t level );
};
class JsonValueWriter {
public:
JsonValueWriter( std::ostream& os );
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
JsonObjectWriter writeObject() &&;
JsonArrayWriter writeArray() &&;
template <typename T>
void write( T const& value ) && {
writeImpl( value, !std::is_arithmetic<T>::value );
}
void write( StringRef value ) &&;
void write( bool value ) &&;
private:
void writeImpl( StringRef value, bool quote );
// Without this SFINAE, this overload is a better match
// for `std::string`, `char const*`, `char const[N]` args.
// While it would still work, it would cause code bloat
// and multiple iteration over the strings
template <typename T,
typename = typename std::enable_if_t<
!std::is_convertible<T, StringRef>::value>>
void writeImpl( T const& value, bool quote_value ) {
m_sstream << value;
writeImpl( m_sstream.str(), quote_value );
}
std::ostream& m_os;
std::stringstream m_sstream;
std::uint64_t m_indent_level;
};
class JsonObjectWriter {
public:
JsonObjectWriter( std::ostream& os );
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
JsonObjectWriter( JsonObjectWriter&& source );
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
~JsonObjectWriter();
JsonValueWriter write( StringRef key );
private:
std::ostream& m_os;
std::uint64_t m_indent_level;
bool m_should_comma = false;
bool m_active = true;
};
class JsonArrayWriter {
public:
JsonArrayWriter( std::ostream& os );
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
JsonArrayWriter( JsonArrayWriter&& source );
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
~JsonArrayWriter();
JsonObjectWriter writeObject();
JsonArrayWriter writeArray();
template <typename T>
JsonArrayWriter& write( T const& value ) {
return writeImpl( value );
}
JsonArrayWriter& write( bool value );
private:
template <typename T>
JsonArrayWriter& writeImpl( T const& value ) {
JsonUtils::appendCommaNewline(
m_os, m_should_comma, m_indent_level + 1 );
JsonValueWriter{ m_os }.write( value );
return *this;
}
std::ostream& m_os;
std::uint64_t m_indent_level;
bool m_should_comma = false;
bool m_active = true;
};
} // namespace Catch
#endif // CATCH_JSONWRITER_HPP_INCLUDED

View File

@@ -29,7 +29,7 @@ namespace Catch {
#else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv #else // ^^ Windows crt debug heap enabled // Windows crt debug heap disabled vv
Catch::LeakDetector::LeakDetector() {} Catch::LeakDetector::LeakDetector() = default;
#endif // CATCH_CONFIG_WINDOWS_CRTDBG #endif // CATCH_CONFIG_WINDOWS_CRTDBG

View File

@@ -31,4 +31,12 @@ namespace Catch {
} }
#endif #endif
#if !defined( CATCH_CONFIG_GLOBAL_NEXTAFTER )
float nextafter( float x, float y ) { return std::nextafter( x, y ); }
double nextafter( double x, double y ) { return std::nextafter( x, y ); }
#else
float nextafter( float x, float y ) { return ::nextafterf( x, y ); }
double nextafter( double x, double y ) { return ::nextafter( x, y ); }
#endif
} // end namespace Catch } // end namespace Catch

View File

@@ -9,8 +9,13 @@
#define CATCH_POLYFILLS_HPP_INCLUDED #define CATCH_POLYFILLS_HPP_INCLUDED
namespace Catch { namespace Catch {
bool isnan(float f); bool isnan(float f);
bool isnan(double d); bool isnan(double d);
float nextafter(float x, float y);
double nextafter(double x, double y);
} }
#endif // CATCH_POLYFILLS_HPP_INCLUDED #endif // CATCH_POLYFILLS_HPP_INCLUDED

View File

@@ -0,0 +1,94 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
#define CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED
#include <catch2/internal/catch_polyfills.hpp>
#include <cassert>
#include <cmath>
#include <cstdint>
#include <limits>
#include <type_traits>
namespace Catch {
namespace Detail {
/**
* Returns the largest magnitude of 1-ULP distance inside the [a, b] range.
*
* Assumes `a < b`.
*/
template <typename FloatType>
FloatType gamma(FloatType a, FloatType b) {
static_assert( std::is_floating_point<FloatType>::value,
"gamma returns the largest ULP magnitude within "
"floating point range [a, b]. This only makes sense "
"for floating point types" );
assert( a <= b );
const auto gamma_up = Catch::nextafter( a, std::numeric_limits<FloatType>::infinity() ) - a;
const auto gamma_down = b - Catch::nextafter( b, -std::numeric_limits<FloatType>::infinity() );
return gamma_up < gamma_down ? gamma_down : gamma_up;
}
template <typename FloatingPoint>
struct DistanceTypePicker;
template <>
struct DistanceTypePicker<float> {
using type = std::uint32_t;
};
template <>
struct DistanceTypePicker<double> {
using type = std::uint64_t;
};
template <typename T>
using DistanceType = typename DistanceTypePicker<T>::type;
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
/**
* Computes the number of equi-distant floats in [a, b]
*
* Since not every range can be split into equidistant floats
* exactly, we actually compute ceil(b/distance - a/distance),
* because in those cases we want to overcount.
*
* Uses modified Dekker's FastTwoSum algorithm to handle rounding.
*/
template <typename FloatType>
DistanceType<FloatType>
count_equidistant_floats( FloatType a, FloatType b, FloatType distance ) {
assert( a <= b );
// We get distance as gamma for our uniform float distribution,
// so this will round perfectly.
const auto ag = a / distance;
const auto bg = b / distance;
const auto s = bg - ag;
const auto err = ( std::fabs( a ) <= std::fabs( b ) )
? -ag - ( s - bg )
: bg - ( s + ag );
const auto ceil_s = static_cast<DistanceType<FloatType>>( std::ceil( s ) );
return ( ceil_s != s ) ? ceil_s : ceil_s + ( err > 0 );
}
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic pop
#endif
}
} // end namespace Catch
#endif // CATCH_RANDOM_FLOATING_POINT_HELPERS_HPP_INCLUDED

View File

@@ -0,0 +1,202 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
#define CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED
#include <climits>
#include <cstddef>
#include <cstdint>
#include <type_traits>
namespace Catch {
namespace Detail {
template <std::size_t>
struct SizedUnsignedType;
#define SizedUnsignedTypeHelper( TYPE ) \
template <> \
struct SizedUnsignedType<sizeof( TYPE )> { \
using type = TYPE; \
}
SizedUnsignedTypeHelper( std::uint8_t );
SizedUnsignedTypeHelper( std::uint16_t );
SizedUnsignedTypeHelper( std::uint32_t );
SizedUnsignedTypeHelper( std::uint64_t );
#undef SizedUnsignedTypeHelper
template <std::size_t sz>
using SizedUnsignedType_t = typename SizedUnsignedType<sz>::type;
template <typename T>
using DoubleWidthUnsignedType_t = SizedUnsignedType_t<2 * sizeof( T )>;
template <typename T>
struct ExtendedMultResult {
T upper;
T lower;
friend bool operator==( ExtendedMultResult const& lhs,
ExtendedMultResult const& rhs ) {
return lhs.upper == rhs.upper && lhs.lower == rhs.lower;
}
};
// Returns 128 bit result of multiplying lhs and rhs
constexpr ExtendedMultResult<std::uint64_t>
extendedMult( std::uint64_t lhs, std::uint64_t rhs ) {
// We use the simple long multiplication approach for
// correctness, we can use platform specific builtins
// for performance later.
// Split the lhs and rhs into two 32bit "digits", so that we can
// do 64 bit arithmetic to handle carry bits.
// 32b 32b 32b 32b
// lhs L1 L2
// * rhs R1 R2
// ------------------------
// | R2 * L2 |
// | R2 * L1 |
// | R1 * L2 |
// | R1 * L1 |
// -------------------------
// | a | b | c | d |
#define CarryBits( x ) ( x >> 32 )
#define Digits( x ) ( x & 0xFF'FF'FF'FF )
auto r2l2 = Digits( rhs ) * Digits( lhs );
auto r2l1 = Digits( rhs ) * CarryBits( lhs );
auto r1l2 = CarryBits( rhs ) * Digits( lhs );
auto r1l1 = CarryBits( rhs ) * CarryBits( lhs );
// Sum to columns first
auto d = Digits( r2l2 );
auto c = CarryBits( r2l2 ) + Digits( r2l1 ) + Digits( r1l2 );
auto b = CarryBits( r2l1 ) + CarryBits( r1l2 ) + Digits( r1l1 );
auto a = CarryBits( r1l1 );
// Propagate carries between columns
c += CarryBits( d );
b += CarryBits( c );
a += CarryBits( b );
// Remove the used carries
c = Digits( c );
b = Digits( b );
a = Digits( a );
#undef CarryBits
#undef Digits
return {
a << 32 | b, // upper 64 bits
c << 32 | d // lower 64 bits
};
}
template <typename UInt>
constexpr ExtendedMultResult<UInt> extendedMult( UInt lhs, UInt rhs ) {
static_assert( std::is_unsigned<UInt>::value,
"extendedMult can only handle unsigned integers" );
static_assert( sizeof( UInt ) < sizeof( std::uint64_t ),
"Generic extendedMult can only handle types smaller "
"than uint64_t" );
using WideType = DoubleWidthUnsignedType_t<UInt>;
auto result = WideType( lhs ) * WideType( rhs );
return {
static_cast<UInt>( result >> ( CHAR_BIT * sizeof( UInt ) ) ),
static_cast<UInt>( result & UInt( -1 ) ) };
}
template <typename TargetType,
typename Generator>
std::enable_if_t<sizeof(typename Generator::result_type) >= sizeof(TargetType),
TargetType> fillBitsFrom(Generator& gen) {
using gresult_type = typename Generator::result_type;
static_assert( std::is_unsigned<TargetType>::value, "Only unsigned integers are supported" );
static_assert( Generator::min() == 0 &&
Generator::max() == static_cast<gresult_type>( -1 ),
"Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
// We want to return the top bits from a generator, as they are
// usually considered higher quality.
constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
return static_cast<TargetType>( gen() >>
( generated_bits - return_bits) );
}
template <typename TargetType,
typename Generator>
std::enable_if_t<sizeof(typename Generator::result_type) < sizeof(TargetType),
TargetType> fillBitsFrom(Generator& gen) {
using gresult_type = typename Generator::result_type;
static_assert( std::is_unsigned<TargetType>::value,
"Only unsigned integers are supported" );
static_assert( Generator::min() == 0 &&
Generator::max() == static_cast<gresult_type>( -1 ),
"Generator must be able to output all numbers in its result type (effectively it must be a random bit generator)" );
constexpr auto generated_bits = sizeof( gresult_type ) * CHAR_BIT;
constexpr auto return_bits = sizeof( TargetType ) * CHAR_BIT;
std::size_t filled_bits = 0;
TargetType ret = 0;
do {
ret <<= generated_bits;
ret |= gen();
filled_bits += generated_bits;
} while ( filled_bits < return_bits );
return ret;
}
/*
* Transposes numbers into unsigned type while keeping their ordering
*
* This means that signed types are changed so that the ordering is
* [INT_MIN, ..., -1, 0, ..., INT_MAX], rather than order we would
* get by simple casting ([0, ..., INT_MAX, INT_MIN, ..., -1])
*/
template <typename OriginalType, typename UnsignedType>
std::enable_if_t<std::is_signed<OriginalType>::value, UnsignedType>
transposeToNaturalOrder( UnsignedType in ) {
static_assert(
sizeof( OriginalType ) == sizeof( UnsignedType ),
"reordering requires the same sized types on both sides" );
static_assert( std::is_unsigned<UnsignedType>::value,
"Input type must be unsigned" );
// Assuming 2s complement (standardized in current C++), the
// positive and negative numbers are already internally ordered,
// and their difference is in the top bit. Swapping it orders
// them the desired way.
constexpr auto highest_bit =
UnsignedType( 1 ) << ( sizeof( UnsignedType ) * CHAR_BIT - 1 );
return static_cast<UnsignedType>( in ^ highest_bit );
}
template <typename OriginalType,
typename UnsignedType>
std::enable_if_t<std::is_unsigned<OriginalType>::value, UnsignedType>
transposeToNaturalOrder(UnsignedType in) {
static_assert(
sizeof( OriginalType ) == sizeof( UnsignedType ),
"reordering requires the same sized types on both sides" );
static_assert( std::is_unsigned<UnsignedType>::value, "Input type must be unsigned" );
// No reordering is needed for unsigned -> unsigned
return in;
}
} // namespace Detail
} // namespace Catch
#endif // CATCH_RANDOM_INTEGER_HELPERS_HPP_INCLUDED

View File

@@ -9,6 +9,7 @@
#include <catch2/internal/catch_random_seed_generation.hpp> #include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_random_integer_helpers.hpp>
#include <ctime> #include <ctime>
#include <random> #include <random>
@@ -21,10 +22,10 @@ namespace Catch {
return static_cast<std::uint32_t>( std::time( nullptr ) ); return static_cast<std::uint32_t>( std::time( nullptr ) );
case GenerateFrom::Default: case GenerateFrom::Default:
case GenerateFrom::RandomDevice: case GenerateFrom::RandomDevice: {
// In theory, a platform could have random_device that returns just std::random_device rd;
// 16 bits. That is still some randomness, so we don't care too much return Detail::fillBitsFrom<std::uint32_t>( rd );
return static_cast<std::uint32_t>( std::random_device{}() ); }
default: default:
CATCH_ERROR("Unknown generation method"); CATCH_ERROR("Unknown generation method");

View File

@@ -6,13 +6,14 @@
// SPDX-License-Identifier: BSL-1.0 // SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp> #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_enforce.hpp> #include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_move_and_forward.hpp> #include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/reporters/catch_reporter_automake.hpp> #include <catch2/reporters/catch_reporter_automake.hpp>
#include <catch2/reporters/catch_reporter_compact.hpp> #include <catch2/reporters/catch_reporter_compact.hpp>
#include <catch2/reporters/catch_reporter_console.hpp> #include <catch2/reporters/catch_reporter_console.hpp>
#include <catch2/reporters/catch_reporter_json.hpp>
#include <catch2/reporters/catch_reporter_junit.hpp> #include <catch2/reporters/catch_reporter_junit.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp> #include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/reporters/catch_reporter_sonarqube.hpp> #include <catch2/reporters/catch_reporter_sonarqube.hpp>
@@ -47,6 +48,8 @@ namespace Catch {
Detail::make_unique<ReporterFactory<TeamCityReporter>>(); Detail::make_unique<ReporterFactory<TeamCityReporter>>();
m_impl->factories["XML"] = m_impl->factories["XML"] =
Detail::make_unique<ReporterFactory<XmlReporter>>(); Detail::make_unique<ReporterFactory<XmlReporter>>();
m_impl->factories["JSON"] =
Detail::make_unique<ReporterFactory<JsonReporter>>();
} }
ReporterRegistry::~ReporterRegistry() = default; ReporterRegistry::~ReporterRegistry() = default;

View File

@@ -20,6 +20,7 @@
#include <catch2/internal/catch_output_redirect.hpp> #include <catch2/internal/catch_output_redirect.hpp>
#include <catch2/internal/catch_assertion_handler.hpp> #include <catch2/internal/catch_assertion_handler.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp> #include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <cassert> #include <cassert>
#include <algorithm> #include <algorithm>
@@ -293,13 +294,14 @@ namespace Catch {
m_messageScopes.clear(); m_messageScopes.clear();
} }
// Reset working state // Reset working state. assertion info will be reset after
resetAssertionInfo(); // populateReaction is run if it is needed
m_lastResult = CATCH_MOVE( result ); m_lastResult = CATCH_MOVE( result );
} }
void RunContext::resetAssertionInfo() { void RunContext::resetAssertionInfo() {
m_lastAssertionInfo.macroName = StringRef(); m_lastAssertionInfo.macroName = StringRef();
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr; m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
m_lastAssertionInfo.resultDisposition = ResultDisposition::Normal;
} }
void RunContext::notifyAssertionStarted( AssertionInfo const& info ) { void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
@@ -447,6 +449,7 @@ namespace Catch {
AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult)); AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
assertionEnded(CATCH_MOVE(result) ); assertionEnded(CATCH_MOVE(result) );
resetAssertionInfo();
handleUnfinishedSections(); handleUnfinishedSections();
@@ -583,6 +586,7 @@ namespace Catch {
reportExpr(info, ResultWas::ExpressionFailed, &expr, negated ); reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
populateReaction( reaction ); populateReaction( reaction );
} }
resetAssertionInfo();
} }
void RunContext::reportExpr( void RunContext::reportExpr(
AssertionInfo const &info, AssertionInfo const &info,
@@ -621,6 +625,7 @@ namespace Catch {
// considered "OK" // considered "OK"
reaction.shouldSkip = true; reaction.shouldSkip = true;
} }
resetAssertionInfo();
} }
void RunContext::handleUnexpectedExceptionNotThrown( void RunContext::handleUnexpectedExceptionNotThrown(
AssertionInfo const& info, AssertionInfo const& info,
@@ -641,6 +646,7 @@ namespace Catch {
AssertionResult assertionResult{ info, CATCH_MOVE(data) }; AssertionResult assertionResult{ info, CATCH_MOVE(data) };
assertionEnded( CATCH_MOVE(assertionResult) ); assertionEnded( CATCH_MOVE(assertionResult) );
populateReaction( reaction ); populateReaction( reaction );
resetAssertionInfo();
} }
void RunContext::populateReaction( AssertionReaction& reaction ) { void RunContext::populateReaction( AssertionReaction& reaction ) {
@@ -658,6 +664,7 @@ namespace Catch {
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s; data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
AssertionResult assertionResult{ info, CATCH_MOVE( data ) }; AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( CATCH_MOVE(assertionResult) ); assertionEnded( CATCH_MOVE(assertionResult) );
resetAssertionInfo();
} }
void RunContext::handleNonExpr( void RunContext::handleNonExpr(
AssertionInfo const &info, AssertionInfo const &info,
@@ -672,6 +679,7 @@ namespace Catch {
const auto isOk = assertionResult.isOk(); const auto isOk = assertionResult.isOk();
assertionEnded( CATCH_MOVE(assertionResult) ); assertionEnded( CATCH_MOVE(assertionResult) );
if ( !isOk ) { populateReaction( reaction ); } if ( !isOk ) { populateReaction( reaction ); }
resetAssertionInfo();
} }

View File

@@ -78,7 +78,7 @@ namespace Catch {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \ catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \ catchInternalPreviousSectionHint == __LINE__ ) \
@@ -88,7 +88,7 @@ namespace Catch {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \ if ( [[maybe_unused]] const int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \ catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \ catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \ catchInternalPreviousSectionHint == __LINE__ ) \

View File

@@ -13,7 +13,7 @@
namespace Catch { namespace Catch {
TagAliasRegistry::~TagAliasRegistry() {} TagAliasRegistry::~TagAliasRegistry() = default;
TagAlias const* TagAliasRegistry::find( std::string const& alias ) const { TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
auto it = m_registry.find( alias ); auto it = m_registry.find( alias );

View File

@@ -34,7 +34,7 @@
#else // CATCH_CONFIG_FAST_COMPILE #else // CATCH_CONFIG_FAST_COMPILE
#define INTERNAL_CATCH_TRY try #define INTERNAL_CATCH_TRY try
#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); } #define INTERNAL_CATCH_CATCH( handler ) catch(...) { (handler).handleUnexpectedInflightException(); }
#endif #endif

View File

@@ -113,7 +113,7 @@ static int catchInternalSectionHint = 0;
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \ CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \ static const Catch::Detail::DummyUse INTERNAL_CATCH_UNIQUE_NAME( \
dummyUser )( &fname ); \ dummyUser )( &(fname) ); \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \ CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
static void fname( [[maybe_unused]] int catchInternalSectionHint ) \ static void fname( [[maybe_unused]] int catchInternalSectionHint ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION

View File

@@ -0,0 +1,131 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
#define CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED
#include <catch2/internal/catch_random_floating_point_helpers.hpp>
#include <catch2/internal/catch_uniform_integer_distribution.hpp>
#include <cmath>
#include <type_traits>
namespace Catch {
namespace Detail {
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
// The issue with overflow only happens with maximal ULP and HUGE
// distance, e.g. when generating numbers in [-inf, inf] for given
// type. So we only check for the largest possible ULP in the
// type, and return something that does not overflow to inf in 1 mult.
constexpr std::uint64_t calculate_max_steps_in_one_go(double gamma) {
if ( gamma == 1.99584030953472e+292 ) { return 9007199254740991; }
return static_cast<std::uint64_t>( -1 );
}
constexpr std::uint32_t calculate_max_steps_in_one_go(float gamma) {
if ( gamma == 2.028241e+31f ) { return 16777215; }
return static_cast<std::uint32_t>( -1 );
}
#if defined( __GNUC__ ) || defined( __clang__ )
# pragma GCC diagnostic pop
#endif
}
/**
* Implementation of uniform distribution on floating point numbers.
*
* Note that we support only `float` and `double` types, because these
* usually mean the same thing across different platform. `long double`
* varies wildly by platform and thus we cannot provide reproducible
* implementation. Also note that we don't implement all parts of
* distribution per standard: this distribution is not serializable, nor
* can the range be arbitrarily reset.
*
* The implementation also uses different approach than the one taken by
* `std::uniform_real_distribution`, where instead of generating a number
* between [0, 1) and then multiplying the range bounds with it, we first
* split the [a, b] range into a set of equidistributed floating point
* numbers, and then use uniform int distribution to pick which one to
* return.
*
* This has the advantage of guaranteeing uniformity (the multiplication
* method loses uniformity due to rounding when multiplying floats), except
* for small non-uniformity at one side of the interval, where we have
* to deal with the fact that not every interval is splittable into
* equidistributed floats.
*
* Based on "Drawing random floating-point numbers from an interval" by
* Frederic Goualard.
*/
template <typename FloatType>
class uniform_floating_point_distribution {
static_assert(std::is_floating_point<FloatType>::value, "...");
static_assert(!std::is_same<FloatType, long double>::value,
"We do not support long double due to inconsistent behaviour between platforms");
using WidthType = Detail::DistanceType<FloatType>;
FloatType m_a, m_b;
FloatType m_ulp_magnitude;
WidthType m_floats_in_range;
uniform_integer_distribution<WidthType> m_int_dist;
// In specific cases, we can overflow into `inf` when computing the
// `steps * g` offset. To avoid this, we don't offset by more than this
// in one multiply + addition.
WidthType m_max_steps_in_one_go;
// We don't want to do the magnitude check every call to `operator()`
bool m_a_has_leq_magnitude;
public:
using result_type = FloatType;
uniform_floating_point_distribution( FloatType a, FloatType b ):
m_a( a ),
m_b( b ),
m_ulp_magnitude( Detail::gamma( m_a, m_b ) ),
m_floats_in_range( Detail::count_equidistant_floats( m_a, m_b, m_ulp_magnitude ) ),
m_int_dist(0, m_floats_in_range),
m_max_steps_in_one_go( Detail::calculate_max_steps_in_one_go(m_ulp_magnitude)),
m_a_has_leq_magnitude(std::fabs(m_a) <= std::fabs(m_b))
{
assert( a <= b );
}
template <typename Generator>
result_type operator()( Generator& g ) {
WidthType steps = m_int_dist( g );
if ( m_a_has_leq_magnitude ) {
if ( steps == m_floats_in_range ) { return m_a; }
auto b = m_b;
while (steps > m_max_steps_in_one_go) {
b -= m_max_steps_in_one_go * m_ulp_magnitude;
steps -= m_max_steps_in_one_go;
}
return b - steps * m_ulp_magnitude;
} else {
if ( steps == m_floats_in_range ) { return m_b; }
auto a = m_a;
while (steps > m_max_steps_in_one_go) {
a += m_max_steps_in_one_go * m_ulp_magnitude;
steps -= m_max_steps_in_one_go;
}
return a + steps * m_ulp_magnitude;
}
}
result_type a() const { return m_a; }
result_type b() const { return m_b; }
};
} // end namespace Catch
#endif // CATCH_UNIFORM_FLOATING_POINT_DISTRIBUTION_HPP_INCLUDED

View File

@@ -0,0 +1,126 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
#define CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED
#include <catch2/internal/catch_random_integer_helpers.hpp>
namespace Catch {
namespace Detail {
// Indirection to enable make_unsigned<bool> behaviour.
template <typename T>
struct make_unsigned {
using type = std::make_unsigned_t<T>;
};
template <>
struct make_unsigned<bool> {
using type = uint8_t;
};
template <typename T>
using make_unsigned_t = typename make_unsigned<T>::type;
}
/**
* Implementation of uniform distribution on integers.
*
* Unlike `std::uniform_int_distribution`, this implementation supports
* various 1 byte integral types, including bool (but you should not
* actually use it for bools).
*
* The underlying algorithm is based on the one described in "Fast Random
* Integer Generation in an Interval" by Daniel Lemire, but has been
* optimized under the assumption of reuse of the same distribution object.
*/
template <typename IntegerType>
class uniform_integer_distribution {
static_assert(std::is_integral<IntegerType>::value, "...");
using UnsignedIntegerType = Detail::make_unsigned_t<IntegerType>;
// We store the left range bound converted to internal representation,
// because it will be used in computation in the () operator.
UnsignedIntegerType m_a;
// After initialization, right bound is only used for the b() getter,
// so we keep it in the original type.
IntegerType m_b;
// How many different values are there in [a, b]. a == b => 1, can be 0 for distribution over all values in the type.
UnsignedIntegerType m_ab_distance;
// We hoisted this out of the main generation function. Technically,
// this means that using this distribution will be slower than Lemire's
// algorithm if this distribution instance will be used only few times,
// but it will be faster if it is used many times. Since Catch2 uses
// distributions only to implement random generators, we assume that each
// distribution will be reused many times and this is an optimization.
UnsignedIntegerType m_rejection_threshold = 0;
// Assumes m_b and m_a are already filled
UnsignedIntegerType computeDistance() const {
// This overflows and returns 0 if ua == 0 and ub == TYPE_MAX.
// We handle that later when generating the number.
return transposeTo(m_b) - m_a + 1;
}
static UnsignedIntegerType computeRejectionThreshold(UnsignedIntegerType ab_distance) {
// distance == 0 means that we will return all possible values from
// the type's range, and that we shouldn't reject anything.
if ( ab_distance == 0 ) { return 0; }
return ( ~ab_distance + 1 ) % ab_distance;
}
static UnsignedIntegerType transposeTo(IntegerType in) {
return Detail::transposeToNaturalOrder<IntegerType>(
static_cast<UnsignedIntegerType>( in ) );
}
static IntegerType transposeBack(UnsignedIntegerType in) {
return static_cast<IntegerType>(
Detail::transposeToNaturalOrder<IntegerType>(in) );
}
public:
using result_type = IntegerType;
uniform_integer_distribution( IntegerType a, IntegerType b ):
m_a( transposeTo(a) ),
m_b( b ),
m_ab_distance( computeDistance() ),
m_rejection_threshold( computeRejectionThreshold(m_ab_distance) ) {
assert( a <= b );
}
template <typename Generator>
result_type operator()( Generator& g ) {
// All possible values of result_type are valid.
if ( m_ab_distance == 0 ) {
return transposeBack( Detail::fillBitsFrom<UnsignedIntegerType>( g ) );
}
auto random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
auto emul = Detail::extendedMult( random_number, m_ab_distance );
// Unlike Lemire's algorithm we skip the ab_distance check, since
// we precomputed the rejection threshold, which is always tighter.
while (emul.lower < m_rejection_threshold) {
random_number = Detail::fillBitsFrom<UnsignedIntegerType>( g );
emul = Detail::extendedMult( random_number, m_ab_distance );
}
return transposeBack(m_a + emul.upper);
}
result_type a() const { return transposeBack(m_a); }
result_type b() const { return m_b; }
};
} // end namespace Catch
#endif // CATCH_UNIFORM_INTEGER_DISTRIBUTION_HPP_INCLUDED

Some files were not shown because too many files have changed in this diff Show More