Compare commits

...

111 Commits

Author SHA1 Message Date
Martin Hořeňovský
6e79e682b7 v3.4.0 2023-07-13 13:37:30 +02:00
Martin Hořeňovský
683c85772f Clean up explanation in tests 2023-07-12 11:49:43 +02:00
Martin Hořeňovský
1b049bdba4 2 more TEST_CASEs to DiscoverTests/register-tests.cpp 2023-07-04 00:06:24 +02:00
Martin Hořeňovský
e4b16053a6 Escape Catch2 test names in catch_discover_tests tests 2023-06-15 14:19:39 +02:00
Robin Christ
42ee66b5e6 Fix handling of semicolon and backslash characters in CMake test discovery (#2676)
This PR fixes the handling of semicolon and backslash characters in test names in the CMake test discovery

Closes #2674
2023-06-14 23:40:10 +02:00
Martin Hořeňovský
a0c6a28460 Fix possible FP in catch_discover_tests tests 2023-06-14 23:31:41 +02:00
Martin Hořeňovský
c8363143e7 Add test scaffolding for catch_discover_tests 2023-06-14 21:14:33 +02:00
Martin Hořeňovský
7a52dfa77b Fix typo in cross-docs links 2023-06-11 19:37:15 +02:00
Vertexwahn
9131736630 Bazel support: Update skylib 2023-06-08 13:30:50 +02:00
Martin Hořeňovský
0631b607ee Test & document SKIP in generator constructor
Closes #1593
2023-05-31 15:12:43 +02:00
Martin Hořeňovský
dff7513b28 Static analysis cleanup in tests 2023-05-29 21:45:30 +02:00
Martin Hořeňovský
bf5aa7b383 Experimental static analysis support in TEST_CASE and SECTION
Closes #2681
2023-05-29 21:45:28 +02:00
Martin Hořeňovský
dba9197ec7 Add new config option: STATIC_ANALYSIS_SUPPORT 2023-05-29 00:55:20 +02:00
Martin Hořeňovský
f60c15364b Add macro for suppressing Wshadow 2023-05-28 21:07:31 +02:00
Martin Hořeňovský
b3cf1bfb5d Avoid unused variable warning in GeneratorsImpl tests 2023-05-28 21:05:01 +02:00
Martin Hořeňovský
73b93ce6bc Include catch_user_config.hpp in all catch_config_* files 2023-05-28 21:04:16 +02:00
Martin Hořeňovský
8008625d7e Merge pull request #2693 from Ali-Amir/u/ali/optional-meson-unit-tests
Add option to disable building unit tests in Meson build file.
2023-05-27 12:00:52 +02:00
Ali-Amir Aldan
ce7b153021 Add option to disable building unit tests in Meson build file. 2023-05-26 10:05:06 -07:00
Cristian Morales Vega
535205e2ac Suppress -Wunused-result warning in gcc
See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66425.
2023-05-23 23:31:55 +02:00
Martin Hořeňovský
689fdcd7dc Fix some tests never being run 2023-05-20 22:03:48 +02:00
Martin Hořeňovský
a153fce724 Improve error messages for TEST_CASE tag parsing errors
Also removes a duplicated test case checking for empty tag error.

Related to #2650
2023-05-20 21:13:48 +02:00
Martin Hořeňovský
06c0e1cfab Merge pull request #2689 from ThePhD/fix/includes/header-exception
🛠💚 Add <exception> header where strictly necessary
2023-05-16 18:41:19 +02:00
ThePhD
05d7eb5a00 🛠 Add <exception> header where strictly necessary 2023-05-16 12:18:57 -04:00
Valeri
f53bb3ae7b meson: require version >=0.54.1
See discussion in https://github.com/mesonbuild/wrapdb/pull/1016.
2023-05-15 17:41:56 +02:00
Martin Hořeňovský
ce8a7b3390 Merge pull request #2687 from ChrisThrasher/sfml
Add SFML to the list of open source users
2023-05-15 13:44:59 +02:00
Chris Thrasher
6dce539fad Add SFML to the list of open source users 2023-05-13 14:34:34 -06:00
Yaroslav
5a40b2275c Update CatchConfigOptions.cmake
Fix CMake warning

CMake Warning (dev) at catch2-src/CMake/CatchConfigOptions.cmake:71 (set):
  uninitialized variable 'BUILD_SHARED_LIBS'
2023-05-12 13:52:45 +02:00
Martin Hořeňovský
598895d048 Fix Wredundant-decls
Closes #2682
2023-05-12 09:51:13 +02:00
Martin Hořeňovský
0dc82e08df Move CATCH_INTERNAL_STRINGIFY macro into its own header 2023-05-07 20:58:54 +02:00
Martin Hořeňovský
8ca504cbc9 Move AssertionResult when passing it inside RunContext 2023-05-06 23:58:48 +02:00
Martin Hořeňovský
c57b5cdf43 Move-enable Catch::optional
This avoids copies in couple places through Catch2, e.g. reporter
spec handling, and moving around `AssertionResult` in `RunContext`.
2023-05-06 15:22:02 +02:00
Martin Hořeňovský
d84777c9cb Fix assertionStarting events being sent after the expr is evaluated
Closes #2678
2023-05-06 11:48:41 +02:00
Martin Hořeňovský
51fdbedd13 Internal linkage for outlier_variance 2023-05-01 13:21:47 +02:00
Martin Hořeňovský
10f0a58643 Some template instantiation reductions 2023-05-01 13:21:45 +02:00
Martin Hořeňovský
fe64c28925 Reduce compilation costs of benchmarks
We replaced some simple std::algorithm usage by loops, and reduced
header inclusion.
2023-05-01 13:21:41 +02:00
Martin Hořeňovský
7d07efc92b Clean up iterator usage in benchmarks
Specifically we turned `mean`, `classify_outliers`, `jackknife`,
into concrete functions that take only `const_iterator` from vecs,
instead of generic iterators over anything.

I also changed `resample` to take `const_iterator` instead of
plain `iterator`, and similar for `standard_deviation`, and
`analyse_samples`.
2023-05-01 13:21:36 +02:00
Martin Hořeňovský
f3c678c0ab Constexprify constants in estimate_clock.hpp 2023-05-01 13:21:33 +02:00
Vertexwahn
46539b6d9b Fix spelling 2023-04-29 12:55:51 +02:00
Holger Kaelberer
10596b2278 Fix unreachable-code-return warnings 2023-04-20 14:53:03 +02:00
Holger Kaelberer
897fe2a01b cmake: Improve unreachable-code warnings
Enable CI to report -Wunreachable-code-aggressive warnings in clang
builds which covers all,  -Wunreachable-code, -code-break,
-code-return.
2023-04-20 14:53:03 +02:00
Holger Kaelberer
aad926baf8 Catch.cmake: Add new DISCOVERY_MODE option to catch_discover_tests
Introducing a new DISCOVERY_MODE mode option, which provides greater
control over when catch_discover_tests perforsm test discovery.

It has two supported modes:
* POST_BUILD: The default behavior, which adds a POST_BUILD command
  to perform test discovery after the test has been built as was
  always done so far.

* PRE_TEST: New mode, which delays test discovery until test execution.
  The generated include file generates the appropriate CTest files at
  runtime and regenerates the CTest files when the executable is
  updated.
  This mode can be used in build-environments that don't allow for
  executing the linked binaries at build-time (like in a
  cross-compilation environment).

DISCOVERY_MODE can be controlled in two ways:
1. Setting the DISCOVERY_MODE when calling catch_discover_tests.

2. Setting the global CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE prior
   to calling gtest_discover_tests.

Closes #2493
2023-04-19 23:51:42 +02:00
Holger Kaelberer
4e8399d835 CatchAddTests.cmake: Refactor into callable method
Move test discovery logic into new catch_discover_tests_impl method
and make CatchAddTests aware of whether it is being launched in
CMake's script mode.

When launched in script mode, catch_discover_tests_impl is called
passing arguments obtained from the definitions passed into the call
to cmake. This preserves the existing behavior assumed by Catch.cmake.

Looking ahead, it also allows CatchAddTests to be included in
generated files and call catch_discover_tests_impl to perform test
discovery at test runtime with the new PRE_TEST discovery mode
introduced later.
2023-04-19 23:51:42 +02:00
Martin Hořeňovský
9a2a4eadc0 Bump xml-format-version in XML reporter 2023-04-10 21:59:50 +02:00
Martin Hořeňovský
fb806da76f Add lineinfo to XML reporter output for INFO/WARN
Closes #1251
2023-04-10 16:32:07 +02:00
Arne Mertz
50bf00e266 Fix reporter detection in catch_discover_tests
Closes #2668
Instead of grepping for the reporter, use it in the process and check for the error code
2023-04-05 10:12:50 +02:00
Martin Hořeňovský
9f08097f55 Cleanup internal includes by splitting out some event structs
* Split out BenchmarkInfo and BenchmarkStats to their own header
* Outline BenchmarkStats<> declaration to separate header
* Split out TestRunInfo into its own header

These changes let us remove the large `interfaces_reporter.hpp`
include from `benchmark.hpp`, and replace it with
`interfaces_capture.hpp` in `run_context.hpp`.

I also cleaned out `interfaces_repoter.hpp` from reporter headers
that depend on `reporter_common_base.hpp`. This will not change
anything in the actual inclusion set, but makes it logically
more consistent.
2023-03-31 19:31:51 +02:00
Martin Hořeňovský
1f881ab464 Split ITestInvoker into its own header 2023-03-23 16:50:11 +01:00
Martin Hořeňovský
c487b27d9d Reduce misc includes all around 2023-03-23 16:50:07 +01:00
Martin Hořeňovský
3230760db2 Cleanup in translating exceptions to messages 2023-03-23 12:26:44 +01:00
Martin Hořeňovský
b3ebce715e Cleanup benchmarking includes 2023-03-23 11:59:40 +01:00
Martin Hořeňovský
d0f70fdfd6 Unify IReporterRegistry and ReporterRegistry
To keep the compilation firewall effect, the implementations
are hidden behind a PIMPL. In this case it is probably not
worth it, but we can inline it later if needed.
2023-03-22 23:58:44 +01:00
Martin Hořeňovský
4f4ad8ada9 Sprinkle some constexpr around 2023-03-22 19:12:32 +01:00
Martin Hořeňovský
5b665be643 Cut out catch_interfaces_capture.hpp include from the main include 2023-03-22 19:12:03 +01:00
Martin Hořeňovský
2598116aa6 Mark various anonymous classes final 2023-03-20 22:56:43 +01:00
Martin Hořeňovský
173aa3f1f4 Devirtualize Context 2023-03-20 20:46:41 +01:00
Martin Hořeňovský
28437e1214 Remove pointless member variable from RunContext 2023-03-20 20:34:58 +01:00
Martin Hořeňovský
3c8fb6bbb2 Internal linkage for generator trackers 2023-03-20 19:37:58 +01:00
Martin Hořeňovský
72f3ce4db5 Outline the actual registering of listener factories to cpp file 2023-03-20 19:37:27 +01:00
Martin Hořeňovský
62167d756e Reduce internal includes 2023-03-20 19:24:52 +01:00
Bob Miller
6783411349 Fixed extras installation and shard impl location 2023-03-14 18:31:38 +01:00
Martin Hořeňovský
7b4dd326c0 Remove obsolete comment in multireporter 2023-03-12 13:27:07 +01:00
Martin Hořeňovský
1dfaa8abe7 Outline throwing of TestSkipException 2023-03-12 00:45:31 +01:00
Martin Hořeňovský
ba94278bdd Inline trivial function in AssertionHandler 2023-03-12 00:41:25 +01:00
Martin Hořeňovský
8e5a4b6f70 Remove superfluous pointer copy in AssertionStats constructor 2023-03-12 00:39:18 +01:00
Martin Hořeňovský
9b884d8107 Fix refactoring 2023-03-11 23:59:47 +01:00
Vadim Zeitlin
8a1b3b81cb Add wxWidgets as another Open Source project using Catch
wxWidgets uses Catch (v2 currently) for all of its tests, even though
some of them still use CppUnit-like macros for compatibility.
2023-03-11 21:29:51 +01:00
Vadim Zeitlin
e5aabb6714 Add xmlwrapp to the list of Open Source projects using Catch
This library uses Catch (v2 currently) for its unit tests.
2023-03-11 21:29:51 +01:00
Martin Hořeňovský
3a1ef14097 Use hasMessage() instead of getMessage().empty() 2023-03-11 21:27:11 +01:00
Martin Hořeňovský
13fae1e2ff Move exception's translation into AssertionResultData message 2023-03-11 16:14:06 +01:00
jushar
3220ae6d4a Add support for the IAR compiler 2023-03-08 20:55:41 +01:00
Martin Hořeňovský
0a0ebf5003 Support elements without op!= in VectorEquals
Closes #2648
2023-03-05 00:11:38 +01:00
Vertexwahn
69f35a5ac8 Bazel support: Update skylib version 2023-03-01 18:55:25 +01:00
Martin Hořeňovský
3f0283de7a v3.3.2 2023-02-27 15:12:49 +01:00
Martin Hořeňovský
6fbb3f0723 Add IsNaN matcher 2023-02-26 00:14:32 +01:00
Martin Hořeňovský
9ff3cde87b Simplify test name creation for list-templated test cases 2023-02-23 15:12:14 +01:00
Martin Hořeňovský
4d802ca58f Use StringRef UDL in more preprocessor-generated strings 2023-02-23 13:25:08 +01:00
Martin Hořeňovský
13711be7cf Use StringRef UDL for generated generator names 2023-02-23 13:25:07 +01:00
Martin Hořeňovský
27ba26f743 Merge pull request #2643 from kisielk/patch-1
cmake-integration.md: Use "tests" as test target name in all examples.
2023-02-22 20:59:12 +01:00
Martin Hořeňovský
a209bcfb54 Update build instructions in contributing.md
We now show the more modern `-S {source}` instead of the old and
undocumented `-H{source}` CMake flag, and also show the available
presets, instead of individually specifying the different testing
options.

Closes #2593
2023-02-22 20:11:45 +01:00
Martin Hořeňovský
584973a485 Early evaluate line loc in NameAndLoc::operator==
I do not know if checking the tracker name or the tracker's file
part of the location first would provide better results, but
in the common case, the line part of the location check should be
rather unique, because different `SECTION`s will have different
source lines where they are defined.

I also propagated this same check into `ITracker::findChild`,
because this significantly improves performance of section tracking
in Debug builds -> 10% in macro benchmark heavily focused on section
tracking. In Release build there is usually no difference, because
the inliner will inline `NameAndLoc::operator==` into `findChild`,
and then eliminate the redundant check. (If the inliner decides
against, then this still improves the performance on average).
2023-02-20 15:19:57 +01:00
Martin Hořeňovský
4f7c8cb28a Avoid copying NameAndLocationRef when passed as argument
`NameAndLocationRef` is pretty large type, so even in release build,
it is unlikely to be passed in registers. In addition to the fact
that some platforms currently do not allow passing even small types
in register (Windows ABI!!), it is better to pass it as a ref,
effectively passing around a pointer.
2023-02-20 15:17:35 +01:00
Martin Hořeňovský
e1dbad4c9e Inline StringRef::operator==
This enables its inlining even without LTO, which in turns enables
callers to determine that two StringRefs are unequals with simple
comparison of two numbers, without any function calls.
2023-02-20 15:05:09 +01:00
Martin Hořeňovský
2befd98da2 Inline some non-virtual functions in ITracker and TrackerContext 2023-02-20 15:02:50 +01:00
Martin Hořeňovský
00f259aeb2 Move captured output into TestCaseStats when sending testCaseEnded 2023-02-20 14:48:39 +01:00
Martin Hořeňovský
fed1436246 Avoid allocating trimmed name for SectionTracker 2023-02-20 14:32:46 +01:00
Martin Hořeňovský
0477326ad9 Directly construct empty string for invalid SectionInfo 2023-02-20 14:32:14 +01:00
Martin Hořeňovský
f04c93462b Small refactoring in AssertionResult 2023-02-20 14:32:12 +01:00
Martin Hořeňovský
1af351cea1 Remove unused TrackerContext::endRun function 2023-02-20 14:32:10 +01:00
Martin Hořeňovský
dcc9fa3f38 Use StringRef UDL for more string literals when expanding macros
* for the name of the listener when registering listener
* for the original expression in assertion macros
2023-02-20 14:31:26 +01:00
Martin Hořeňovský
bf6a15a69a Rewrite -# docs 2023-02-17 15:55:21 +01:00
Martin Hořeňovský
6135a78c31 Don't insert the foo part of [.foo] tag twice when parsing test spec 2023-02-13 22:16:53 +01:00
Martin Hořeňovský
e8ba329b6c Add support for iterator+sentinel pairs in Contains matcher 2023-02-10 23:25:45 +01:00
Martin Hořeňovský
4aa88299af Preconstruct error message in RunContext::handleIncomplete 2023-02-10 21:36:04 +01:00
Kamil Kisiel
4ff9be3bc5 cmake-integration.md: Use "tests" as test target name in all examples. 2023-02-10 11:09:26 -08:00
Martin Hořeňovský
76cdaa3b51 Merge pull request #2637 from jbadwaik/nvhpc_unused_warning
Suppress declared_but_not_referenced warning for NVHPC
2023-02-08 19:51:39 +01:00
Jayesh Badwaik (FZ Juelich)
644294df60 Suppress declared_but_not_referenced warning for NVHPC
Catch2 suppresses unused variable and equivalent warnings in a couple
  of places, but most importantly, in the declaration of autoRegistrar
  in test registry. This warning gets triggered by NVHPC compiler. The
  current patch adds three macros, namely:

      CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
      CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
      CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION

  for the NVHPC Compiler which in particular prevents that warning from
  occurring. The compiler is detected completely separately from the
  other compilers in this patch, because from what I found out, NVHPC
  defines __GNUC__ as well for some reason. (I suspect because it
  advertises itself as GNU compatible.)

  We also add a condition to make sure that the `__GNUC__` path is not
  taken by the NVHPC compiler.
2023-02-08 12:40:55 +01:00
Martin Hořeňovský
cefa8fcf32 Enable use of UnorderedRangeEquals with iterator+sentinel pairs 2023-02-06 15:34:38 +01:00
Martin Hořeňovský
772fa3f790 Add Catch::Detail::is_permutation that supports sentinels
Also split out helpers for testing matcher ranges (types whose
begin/end/empty/etc require ADL lookup, types whose iteration
uses iterator + sentinel pair, etc) into their own file.
2023-02-06 15:29:01 +01:00
Martin Hořeňovský
f3c0a3cd09 Fix RangeEquals matcher to handle iterator + sentinel ranges
Also added tests for types that require ADL lookup for their
`begin` and `end`.
2023-02-03 18:22:41 +01:00
Martin Hořeňovský
42d9d4533e Add test for empty result of filter generator 2023-02-01 18:27:41 +01:00
Martin Hořeňovský
618d44c448 Update docs about thread safe assertions 2023-02-01 15:24:47 +01:00
Martin Hořeňovský
388f7e1737 Cleanup unneeded allocations from reporters
The CompactReporter changes save 21 (430764 -> 430743) allocations
when running the SelfTest binary in default configuration. They
save about 500 allocations when running the binary with `-s`.
2023-01-30 15:30:36 +01:00
Martin Hořeňovský
2ab20a0e00 v3.3.1 2023-01-29 23:18:57 +01:00
Martin Hořeňovský
60264b8807 Avoid copying strings in sonarqube when sorting tests by file 2023-01-29 20:45:13 +01:00
Martin Hořeňovský
65ffee5189 Don't take ownership of SECTION's name for inactive sections
This eliminates 1945 (432709 -> 430764) allocations from running
`./tests/SelfTest -o /dev/null`. In general terms, this saves
an allocation every time an unvisited `SECTION` is passed, which
means that the saved allocations are quadratic in number of sibling
(same level) `SECTION`s in a test case.
2023-01-29 10:44:20 +01:00
Martin Hořeňovský
43f02027e4 Avoid allocations when looking for trackers
Now we delay allocating owning `NameAndLocation` instances until
we construct a new tracker (because a tracker's lifetime can be
significantly different from the underlying tracked-thing's name).

This saves 4239 allocations (436948 -> 432709) when running
`./tests/SelfTest -o /dev/null`, at some cost to code clarity
due to introducing a new ref type, `NameAndLocationRef`.
2023-01-29 10:14:20 +01:00
Martin Hořeňovský
906552f8c8 Clean up extraneous copies in Messages
This removes 109 allocations from running `tests/SelfTest`
(437057 -> 436948).
2023-01-28 22:14:37 +01:00
Martin Hořeňovský
356dfc1439 Move name and sample analysis in benchmarks into BenchmarkStats
This always saves 1 allocation per benchmark, and another two
allocations if the benchmark name is longer than the SSO buffer.
2023-01-28 21:40:59 +01:00
Martin Hořeňovský
e5d1eb757f Move AssertionResultData into AssertionResult in RunContext
When running `./tests/SelfTest -o /dev/null`, this saves 109
allocations (437167 -> 437058).
2023-01-28 19:57:38 +01:00
Martin Hořeňovský
2403f5620e Move SectionEndInfo into sectionEnded call in SECTION's destructor
When running `./tests/SelfTest -o /dev/null`, this saves 1272
allocations (437439 -> 437167). In general, this saves multiple
allocations per end of an entered `SECTION`, if the section name
was too long for SSO, because `RunContext::sectionEnded` can then
move the section's name further down the callstack.
2023-01-28 13:00:30 +01:00
Martin Hořeňovský
d58491c85a Move sectionInfo into sectionEndInfo when SECTION ends
When running `./tests/SelfTest -o /dev/null`, this saves 468
allocations (438907 -> 437439). In general, this saves 1 allocation
every time an entered `SECTION` ends and the section name was long
enough to move out of the SSO buffer.
2023-01-28 12:56:29 +01:00
178 changed files with 6655 additions and 3927 deletions

View File

@@ -29,13 +29,13 @@ jobs:
build_type: Debug
std: 14
other_pkgs: g++-7
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
- cxx: g++-7
build_description: Extras + Examples
build_type: Release
std: 14
other_pkgs: g++-7
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
# Extras and examples with Clang-10
- cxx: clang++-10
@@ -43,13 +43,13 @@ jobs:
build_type: Debug
std: 17
other_pkgs: clang-10
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
- cxx: clang++-10
build_description: Extras + Examples
build_type: Release
std: 17
other_pkgs: clang-10
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_ENABLE_CMAKE_HELPER_TESTS=ON
# Configure tests with Clang-10
- cxx: clang++-10

View File

@@ -43,12 +43,14 @@ expand_template(
"#cmakedefine CATCH_CONFIG_NO_GLOBAL_NEXTAFTER": "",
"#cmakedefine CATCH_CONFIG_NO_POSIX_SIGNALS": "",
"#cmakedefine CATCH_CONFIG_NO_USE_ASYNC": "",
"#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "",
"#cmakedefine CATCH_CONFIG_NO_WCHAR": "",
"#cmakedefine CATCH_CONFIG_NO_WINDOWS_SEH": "",
"#cmakedefine CATCH_CONFIG_NOSTDOUT": "",
"#cmakedefine CATCH_CONFIG_POSIX_SIGNALS": "",
"#cmakedefine CATCH_CONFIG_PREFIX_ALL": "",
"#cmakedefine CATCH_CONFIG_SHARED_LIBRARY": "",
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT": "",
"#cmakedefine CATCH_CONFIG_USE_ASYNC": "",
"#cmakedefine CATCH_CONFIG_WCHAR": "",
"#cmakedefine CATCH_CONFIG_WINDOWS_CRTDBG": "",

View File

@@ -41,6 +41,7 @@ set(_OverridableOptions
"WCHAR"
"WINDOWS_SEH"
"GETENV"
"EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT"
)
foreach(OptionName ${_OverridableOptions})
@@ -68,7 +69,11 @@ set(_OtherConfigOptions
foreach(OptionName ${_OtherConfigOptions})
AddConfigOption(${OptionName})
endforeach()
set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
if(DEFINED BUILD_SHARED_LIBS)
set(CATCH_CONFIG_SHARED_LIBRARY ${BUILD_SHARED_LIBS})
else()
set(CATCH_CONFIG_SHARED_LIBRARY "")
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_CONSOLE_WIDTH "80" CACHE STRING "Read docs/configuration.md for details. Must form a valid integer literal.")

View File

@@ -74,6 +74,7 @@ function(add_warnings_to_targets targets)
"-Woverloaded-virtual"
"-Wparentheses"
"-Wpedantic"
"-Wredundant-decls"
"-Wreorder"
"-Wreturn-std-move"
"-Wshadow"
@@ -83,7 +84,7 @@ function(add_warnings_to_targets targets)
"-Wundef"
"-Wuninitialized"
"-Wunneeded-internal-declaration"
"-Wunreachable-code"
"-Wunreachable-code-aggressive"
"-Wunused"
"-Wunused-function"
"-Wunused-parameter"

View File

@@ -21,6 +21,7 @@ cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io"
cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_CONFIGURE_TESTS "Enable CMake configuration tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_CMAKE_HELPER_TESTS "Enable CMake helper tests. WARNING: VERY EXPENSIVE" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
# Catch2's build breaks if done in-tree. You probably should not build
@@ -31,7 +32,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.3.0 # CML version placeholder, don't delete
VERSION 3.4.0 # CML version placeholder, don't delete
LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet.
@@ -148,6 +149,8 @@ if (NOT_SUBPROJECT)
"extras/ParseAndAddCatchTests.cmake"
"extras/Catch.cmake"
"extras/CatchAddTests.cmake"
"extras/CatchShardTests.cmake"
"extras/CatchShardTestsImpl.cmake"
DESTINATION
${CATCH_CMAKE_CONFIG_DESTINATION}
)

View File

@@ -18,7 +18,8 @@
"CATCH_BUILD_EXAMPLES": "ON",
"CATCH_BUILD_EXTRA_TESTS": "ON",
"CATCH_BUILD_SURROGATES": "ON",
"CATCH_ENABLE_CONFIGURE_TESTS": "ON"
"CATCH_ENABLE_CONFIGURE_TESTS": "ON",
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
}
}
]

View File

@@ -4,12 +4,13 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
name = "bazel_skylib",
sha256 = "66ffd9315665bfaafc96b52278f57c7e2dd09f5ede279ea6d39b2be471e7e3aa",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.4.2/bazel-skylib-1.4.2.tar.gz",
],
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
)
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()

View File

@@ -90,12 +90,12 @@ cmake_minimum_required(VERSION 3.5)
project(baz LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED)
add_executable(foo test.cpp)
target_link_libraries(foo PRIVATE Catch2::Catch2)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2)
include(CTest)
include(Catch)
catch_discover_tests(foo)
catch_discover_tests(tests)
```
When using `FetchContent`, `include(Catch)` will fail unless
@@ -108,7 +108,7 @@ directory.
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(CTest)
include(Catch)
catch_discover_tests()
catch_discover_tests(tests)
```
#### Customization
@@ -126,6 +126,7 @@ catch_discover_tests(target
[OUTPUT_DIR dir]
[OUTPUT_PREFIX prefix]
[OUTPUT_SUFFIX suffix]
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
)
```
@@ -198,6 +199,16 @@ If specified, `suffix` is added to each output file name, like so
`--out dir/<test_name>suffix`. This can be used to add a file extension to
the output file name e.g. ".xml".
* `DISCOVERY_MODE mode`
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 a value of `PRE_TEST` test discovery is delayed until just prior to test
execution (useful e.g. in cross-compilation environments).
``DISCOVERY_MODE`` defaults to the value of the
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
calling ``catch_discover_tests``. This provides a mechanism for globally
selecting a preferred test discovery behavior.
### `ParseAndAddCatchTests.cmake`
@@ -222,12 +233,12 @@ cmake_minimum_required(VERSION 3.5)
project(baz LANGUAGES CXX VERSION 0.0.1)
find_package(Catch2 REQUIRED)
add_executable(foo test.cpp)
target_link_libraries(foo PRIVATE Catch2::Catch2)
add_executable(tests test.cpp)
target_link_libraries(tests PRIVATE Catch2::Catch2)
include(CTest)
include(ParseAndAddCatchTests)
ParseAndAddCatchTests(foo)
ParseAndAddCatchTests(tests)
```

View File

@@ -507,10 +507,13 @@ start of the first section.</br>
## Filenames as tags
<pre>-#, --filenames-as-tags</pre>
When this option is used then every test is given an additional tag which is formed of the unqualified
filename it is found in, with any extension stripped, prefixed with the `#` character.
This option adds an extra tag to all test cases. The tag is `#` followed
by the unqualified filename the test case is defined in, with the _last_
extension stripped out.
For example, tests within the file `tests\SelfTest\UsageTests\BDD.tests.cpp`
will be given the `[#BDD.tests]` tag.
So, for example, tests within the file `~\Dev\MyProject\Ferrets.cpp` would be tagged `[#Ferrets]`.
<a id="colour-mode"></a>
## Override output colouring

View File

@@ -15,6 +15,7 @@
[Enabling stringification](#enabling-stringification)<br>
[Disabling exceptions](#disabling-exceptions)<br>
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
[Static analysis support](#static-analysis-support)<br>
Catch2 is designed to "just work" as much as possible, and most of the
configuration options below are changed automatically during compilation,
@@ -264,6 +265,31 @@ The macro will be used as is, that is, `CATCH_BREAK_INTO_DEBUGGER();`
must compile and must break into debugger.
## Static analysis support
> Introduced in Catch2 3.4.0.
Some parts of Catch2, e.g. `SECTION`s, can be hard for static analysis
tools to reason about. Catch2 can change its internals to help static
analysis tools reason about the tests.
Catch2 automatically detects some static analysis tools (initial
implementation checks for clang-tidy and Coverity), but you can override
its detection (in either direction) via
```
CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force enables static analysis help
CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT // force disables static analysis help
```
_As the name suggests, this is currently experimental, and thus we provide
no backwards compatibility guarantees._
**DO NOT ENABLE THIS FOR BUILDS YOU INTEND TO RUN.** The changed internals
are not meant to be runnable, only "scannable".
---
[Home](Readme.md#top)

View File

@@ -55,6 +55,15 @@ tests from `SelfTest` through a specific reporter and then compare the
generated output with a known good output ("Baseline"). By default, new
tests should be placed here.
To configure a Catch2 build with just the basic tests, use the `basic-tests`
preset, like so:
```
# Assuming you are in Catch2's root folder
cmake -B basic-test-build -S . -DCMAKE_BUILD_TYPE=Debug --preset basic-tests
```
However, not all tests can be written as plain unit tests. For example,
checking that Catch2 orders tests randomly when asked to, and that this
random ordering is subset-invariant, is better done as an integration
@@ -76,21 +85,23 @@ configuration and require separate compilation.
Finally, CMake config tests test that you set Catch2's compile-time
configuration options through CMake, using CMake options of the same name.
None of these tests are enabled by default. To enable them, add
These test categories can be enabled one by one, by passing
`-DCATCH_BUILD_EXAMPLES=ON`, `-DCATCH_BUILD_EXTRA_TESTS=ON`, and
`-DCATCH_ENABLE_CONFIGURE_TESTS=ON` when configuration the CMake build.
`-DCATCH_ENABLE_CONFIGURE_TESTS=ON` when configuring the build.
Bringing this all together, the steps below should configure, build,
and run all tests in the `Debug` compilation.
Catch2 also provides a preset that promises to enable _all_ test types,
`all-tests`.
The snippet below will build & run all tests, in `Debug` compilation mode.
<!-- snippet: catch2-build-and-test -->
<a id='snippet-catch2-build-and-test'></a>
```sh
# 1. Regenerate the amalgamated distribution
# 1. Regenerate the amalgamated distribution (some tests are built against it)
./tools/scripts/generateAmalgamatedFiles.py
# 2. Configure the full test build
cmake -Bdebug-build -H. -DCMAKE_BUILD_TYPE=Debug -DCATCH_DEVELOPMENT_BUILD=ON -DCATCH_BUILD_EXAMPLES=ON -DCATCH_BUILD_EXTRA_TESTS=ON
cmake -B debug-build -S . -DCMAKE_BUILD_TYPE=Debug --preset all-tests
# 3. Run the actual build
cmake --build debug-build

View File

@@ -28,7 +28,7 @@ depending on how often the cleanup needs to happen.
## Why cannot I derive from the built-in reporters?
They are not made to be overridden, in that we do not attempt to maintain
a consistent internal state if a member function is overriden, and by
a consistent internal state if a member function is overridden, and by
forbidding users from using them as a base class, we can refactor them
as needed later.

View File

@@ -134,7 +134,7 @@ type, making their usage much nicer. These are
* `map<T>(func, GeneratorWrapper<U>&&)` for `MapGenerator<T, U, Func>` (map `U` to `T`)
* `chunk(chunk-size, GeneratorWrapper<T>&&)` for `ChunkGenerator<T>`
* `random(IntegerOrFloat a, IntegerOrFloat b)` for `RandomIntegerGenerator` or `RandomFloatGenerator`
* `range(Arithemtic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
* `range(Arithmetic start, Arithmetic end)` for `RangeGenerator<Arithmetic>` with a step size of `1`
* `range(Arithmetic start, Arithmetic end, Arithmetic step)` for `RangeGenerator<Arithmetic>` with a custom step size
* `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
* `from_range(Container const&)` for `IteratorGenerator<T>`
@@ -221,3 +221,21 @@ For full example of implementing your own generator, look into Catch2's
examples, specifically
[Generators: Create your own generator](../examples/300-Gen-OwnGenerator.cpp).
### Handling empty generators
The generator interface assumes that a generator always has at least one
element. This is not always true, e.g. if the generator depends on an external
datafile, the file might be missing.
There are two ways to handle this, depending on whether you want this
to be an error or not.
* If empty generator **is** an error, throw an exception in constructor.
* If empty generator **is not** an error, use the [`SKIP`](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
---
[Home](Readme.md#top)

View File

@@ -88,8 +88,8 @@ because only one thread passes the `REQUIRE` macro and this is not
REQUIRE(cnt == 16);
```
Because C++11 provides the necessary tools to do this, we are planning
to remove this limitation in the future.
We currently do not plan to support thread-safe assertions.
### Process isolation in a test
Catch does not support running tests in isolated (forked) processes. While this might in the future, the fact that Windows does not support forking and only allows full-on process creation and the desire to keep code as similar as possible across platforms, mean that this is likely to take significant development time, that is not currently available.

View File

@@ -141,18 +141,27 @@ are a permutation of the ones in `some_vec`.
### Floating point matchers
Catch2 provides 3 matchers that target floating point numbers. These
Catch2 provides 4 matchers that target floating point numbers. These
are:
* `WithinAbs(double target, double margin)`,
* `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`, and
* `WithinRel(FloatingPoint target, FloatingPoint eps)`.
* `IsNaN()`
> `WithinRel` matcher was introduced in Catch2 2.10.0
For more details, read [the docs on comparing floating point
> `IsNaN` matcher was introduced in Catch2 3.3.2.
The first three serve to compare two floating pointe numbers. For more
details about how they work, read [the docs on comparing floating point
numbers](comparing-floating-point-numbers.md#floating-point-matchers).
`IsNaN` then does exactly what it says on the tin. It matches the input
if it is a NaN (Not a Number). The advantage of using it over just plain
`REQUIRE(std::isnan(x))`, is that if the check fails, with `REQUIRE` you
won't see the value of `x`, but with `REQUIRE_THAT(x, IsNaN())`, you will.
### Miscellaneous matchers

View File

@@ -95,6 +95,9 @@ A C++ client library for Consul. Consul is a distributed tool for discovering an
### [Reactive-Extensions/ RxCpp](https://github.com/Reactive-Extensions/RxCpp)
A library of algorithms for values-distributed-in-time.
### [SFML](https://github.com/SFML/SFML)
Simple and Fast Multimedia Library.
### [SOCI](https://github.com/SOCI/soci)
The C++ Database Access Library.
@@ -110,6 +113,12 @@ A header-only TOML parser and serializer for modern C++.
### [Trompeloeil](https://github.com/rollbear/trompeloeil)
A thread-safe header-only mocking framework for C++14.
### [wxWidgets](https://www.wxwidgets.org/)
Cross-Platform C++ GUI Library.
### [xmlwrapp](https://github.com/vslavik/xmlwrapp)
C++ XML parsing library using libxml2.
## Applications & Tools
### [App Mesh](https://github.com/laoshanxi/app-mesh)
@@ -137,7 +146,7 @@ Newsbeuter is an open-source RSS/Atom feed reader for text terminals.
A 2D, Zombie, RPG game which is being made on our own engine.
### [raspigcd](https://github.com/pantadeusz/raspigcd)
Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrolers (just RPi + Stepsticks).
Low level CLI app and library for execution of GCODE on Raspberry Pi without any additional microcontrollers (just RPi + Stepsticks).
### [SpECTRE](https://github.com/sxs-collaboration/spectre)
SpECTRE is a code for multi-scale, multi-physics problems in astrophysics and gravitational physics.

View File

@@ -2,6 +2,8 @@
# Release notes
**Contents**<br>
[3.3.2](#332)<br>
[3.3.1](#331)<br>
[3.3.0](#330)<br>
[3.2.1](#321)<br>
[3.2.0](#320)<br>
@@ -55,6 +57,66 @@
## 3.4.0
### Improvements
* `VectorEquals` supports elements that provide only `==` and not `!=` (#2648)
* Catch2 supports compiling with IAR compiler (#2651)
* Various small internal performance improvements
* Various small internal compilation time improvements
* XMLReporter now reports location info for INFO and WARN (#1251)
* This bumps up the xml format version to 3
* Documented that `SKIP` in generator constructor can be used to handle empty generator (#1593)
* Added experimental static analysis support to `TEST_CASE` and `SECTION` macros (#2681)
* The two macros are redefined in a way that helps the SA tools reason about the possible paths through a test case with sections.
* The support is controlled by the `CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT` option and autodetects clang-tidy and Coverity.
* `*_THROWS`, `*_THROWS_AS`, etc now suppress warning coming from `__attribute__((warn_unused_result))` on GCC (#2691)
* Unlike plain `[[nodiscard]]`, this warning is not silenced by void cast. WTF GCC?
### Fixes
* Fixed `assertionStarting` events being sent after the expr is evaluated (#2678)
* Errors in `TEST_CASE` tags are now reported nicely (#2650)
### Miscellaneous
* Bunch of improvements to `catch_discover_tests`
* Added DISCOVERY_MODE option, so the discovery can happen either post build or pre-run.
* Fixed handling of semicolons and backslashes in test names (#2674, #2676)
* meson build can disable building tests (#2693)
* meson build properly sets meson version 0.54.1 as the minimal supported version (#2688)
## 3.3.2
### Improvements
* Further reduced allocations
* The compact, console, TAP and XML reporters perform less allocations in various cases
* Removed 1 allocation per entered `SECTION`/`TEST_CASE`.
* Removed 2 allocations per test case exit, if stdout/stderr is captured
* Improved performance
* Section tracking is 10%-25% faster than in v3.3.0
* Assertion handling is 5%-10% faster than in v3.3.0
* Test case registration is 1%-2% faster than in v3.3.0
* Tiny speedup for registering listeners
* Tiny speedup for `CAPTURE`, `TEST_CASE_METHOD`, `METHOD_AS_TEST_CASE`, and `TEMPLATE_LIST_TEST_*` macros.
* `Contains`, `RangeEquals` and `UnorderedRangeEquals` matchers now support ranges with iterator + sentinel pair
* Added `IsNaN` matcher
* Unlike `REQUIRE(isnan(x))`, `REQUIRE_THAT(x, IsNaN())` shows you the value of `x`.
* Suppressed `declared_but_not_referenced` warning for NVHPC (#2637)
### Fixes
* Fixed performance regression in section tracking introduced in v3.3.1
* Extreme cases would cause the tracking to run about 4x slower than in 3.3.0
## 3.3.1
### Improvements
* Reduced allocations and improved performance
* The exact improvements are dependent on your usage of Catch2.
* For example running Catch2's SelfTest binary performs 8k less allocations.
* The main improvement comes from smarter handling of `SECTION`s, especially sibling `SECTION`s
## 3.3.0
### Improvements
@@ -115,7 +177,7 @@
### Fixes
* Cleaned out some warnings and static analysis issues
* Suppressed `-Wcomma` warning rarely occuring in templated test cases (#2543)
* Suppressed `-Wcomma` warning rarely occurring in templated test cases (#2543)
* Constified implementation details in `INFO` (#2564)
* Made `MatcherGenericBase` copy constructor const (#2566)
* Fixed serialization of test filters so the output roundtrips
@@ -483,7 +545,7 @@ v3 releases.
* The `SECTION`(s) before the `GENERATE` will not be run multiple times, the following ones will.
* Added `-D`/`--min-duration` command line flag (#1910)
* If a test takes longer to finish than the provided value, its name and duration will be printed.
* This flag is overriden by setting `-d`/`--duration`.
* This flag is overridden by setting `-d`/`--duration`.
### Fixes
* `TAPReporter` no longer skips successful assertions (#1983)
@@ -551,7 +613,7 @@ v3 releases.
### Fixes
* Fixed computation of benchmarking column widths in ConsoleReporter (#1885, #1886)
* Suppressed clang-tidy's `cppcoreguidelines-pro-type-vararg` in assertions (#1901)
* It was a false positive trigered by the new warning support workaround
* It was a false positive triggered by the new warning support workaround
* Fixed bug in test specification parser handling of OR'd patterns using escaping (#1905)
### Miscellaneous
@@ -888,7 +950,7 @@ v3 releases.
### Contrib
* `ParseAndAddCatchTests` has learned how to use `DISABLED` CTest property (#1452)
* `ParseAndAddCatchTests` now works when there is a whitspace before the test name (#1493)
* `ParseAndAddCatchTests` now works when there is a whitespace before the test name (#1493)
### Miscellaneous

View File

@@ -96,12 +96,12 @@ void assertionStarting( AssertionInfo const& assertionInfo );
void assertionEnded( AssertionStats const& assertionStats );
```
`assertionStarting` is called after the expression is captured, but before
the assertion expression is evaluated. This might seem like a minor
distinction, but what it means is that if you have assertion like
`REQUIRE( a + b == c + d )`, then what happens is that `a + b` and `c + d`
are evaluated before `assertionStarting` is emitted, while the `==` is
evaluated after the event.
The `assertionStarting` event is emitted before the expression in the
assertion is captured or evaluated and `assertionEnded` is emitted
afterwards. This means that given assertion like `REQUIRE(a + b == c + d)`,
Catch2 first emits `assertionStarting` event, then `a + b` and `c + d`
are evaluated, then their results are captured, the comparison is evaluated,
and then `assertionEnded` event is emitted.
## Benchmarking events

View File

@@ -52,7 +52,7 @@ its machine-readable XML output to file `result-junit.xml`, and the
uses ANSI colour codes for colouring the output.
Using multiple reporters (or one reporter and one-or-more [event
listeners](event-listener.md#top)) can have surprisingly complex semantics
listeners](event-listeners.md#top)) can have surprisingly complex semantics
when using customization points provided to reporters by Catch2, namely
capturing stdout/stderr from test cases.

View File

@@ -84,6 +84,12 @@ exit code, same as it does if no test cases have run. This behaviour can
be overridden using the [--allow-running-no-tests](command-line.md#no-tests-override)
flag.
### `SKIP` inside generators
You can also use the `SKIP` macro inside generator's constructor to handle
cases where the generator is empty, but you do not want to fail the test
case.
## Passing and failing test cases

View File

@@ -69,7 +69,8 @@ All tag names beginning with non-alphanumeric characters are reserved by Catch.
* `[!nonportable]` - Indicates that behaviour may vary between platforms or compilers.
* `[#<filename>]` - running with `-#` or `--filenames-as-tags` causes Catch to add the filename, prefixed with `#` (and with any extension stripped), as a tag to all contained tests, e.g. tests in testfile.cpp would all be tagged `[#testfile]`.
* `[#<filename>]` - these tags are added to test cases when you run Catch2
with [`-#` or `--filenames-as-tags`](command-line.md#filenames-as-tags).
* `[@<alias>]` - tag aliases all begin with `@` (see below).

View File

@@ -44,11 +44,11 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
/* Possible simplifications where less legacy toolchain support is needed:
*
* - With libstdc++6 or newer, the make_tuple() calls can be ommitted
* - With libstdc++6 or newer, the make_tuple() calls can be omitted
* (technically C++17 but does not require -std in GCC/Clang). See
* https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
*
* - In C++17 mode std::tie() and the preceding variable delcarations can be
* - In C++17 mode std::tie() and the preceding variable declarations can be
* replaced by structured bindings: auto [test_input, expected] = GENERATE(
* table<std::string, size_t>({ ...
*/

View File

@@ -35,8 +35,9 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
[TEST_LIST var]
[REPORTER reporter]
[OUTPUT_DIR dir]
[OUTPUT_PREFIX prefix}
[OUTPUT_PREFIX prefix]
[OUTPUT_SUFFIX suffix]
[DISCOVERY_MODE <POST_BUILD|PRE_TEST>]
)
``catch_discover_tests`` sets up a post-build command on the test executable
@@ -123,14 +124,28 @@ same as the Catch name; see also ``TEST_PREFIX`` and ``TEST_SUFFIX``.
test executable and when the tests are executed themselves. This requires
cmake/ctest >= 3.22.
`DISCOVERY_MODE mode``
Provides control over when ``catch_discover_tests`` performs test discovery.
By default, ``POST_BUILD`` sets up a post-build command to perform test discovery
at build time. In certain scenarios, like cross-compiling, this ``POST_BUILD``
behavior is not desirable. By contrast, ``PRE_TEST`` delays test discovery until
just prior to test execution. This way test discovery occurs in the target environment
where the test has a better chance at finding appropriate runtime dependencies.
``DISCOVERY_MODE`` defaults to the value of the
``CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE`` variable if it is not passed when
calling ``catch_discover_tests``. This provides a mechanism for globally selecting
a preferred test discovery behavior without having to modify each call site.
#]=======================================================================]
#------------------------------------------------------------------------------
function(catch_discover_tests TARGET)
cmake_parse_arguments(
""
""
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX"
"TEST_PREFIX;TEST_SUFFIX;WORKING_DIRECTORY;TEST_LIST;REPORTER;OUTPUT_DIR;OUTPUT_PREFIX;OUTPUT_SUFFIX;DISCOVERY_MODE"
"TEST_SPEC;EXTRA_ARGS;PROPERTIES;DL_PATHS"
${ARGN}
)
@@ -141,12 +156,20 @@ function(catch_discover_tests TARGET)
if(NOT _TEST_LIST)
set(_TEST_LIST ${TARGET}_TESTS)
endif()
if (_DL_PATHS)
if(${CMAKE_VERSION} VERSION_LESS "3.22.0")
message(FATAL_ERROR "The DL_PATHS option requires at least cmake 3.22")
endif()
endif()
if(NOT _DISCOVERY_MODE)
if(NOT CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE)
set(CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE "POST_BUILD")
endif()
set(_DISCOVERY_MODE ${CMAKE_CATCH_DISCOVER_TESTS_DISCOVERY_MODE})
endif()
if (NOT _DISCOVERY_MODE MATCHES "^(POST_BUILD|PRE_TEST)$")
message(FATAL_ERROR "Unknown DISCOVERY_MODE: ${_DISCOVERY_MODE}")
endif()
## Generate a unique name based on the extra arguments
string(SHA1 args_hash "${_TEST_SPEC} ${_EXTRA_ARGS} ${_REPORTER} ${_OUTPUT_DIR} ${_OUTPUT_PREFIX} ${_OUTPUT_SUFFIX}")
@@ -159,39 +182,77 @@ function(catch_discover_tests TARGET)
TARGET ${TARGET}
PROPERTY CROSSCOMPILING_EMULATOR
)
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_REPORTER=${_REPORTER}"
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
-D "TEST_DL_PATHS=${_DL_PATHS}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
if(_DISCOVERY_MODE STREQUAL "POST_BUILD")
add_custom_command(
TARGET ${TARGET} POST_BUILD
BYPRODUCTS "${ctest_tests_file}"
COMMAND "${CMAKE_COMMAND}"
-D "TEST_TARGET=${TARGET}"
-D "TEST_EXECUTABLE=$<TARGET_FILE:${TARGET}>"
-D "TEST_EXECUTOR=${crosscompiling_emulator}"
-D "TEST_WORKING_DIR=${_WORKING_DIRECTORY}"
-D "TEST_SPEC=${_TEST_SPEC}"
-D "TEST_EXTRA_ARGS=${_EXTRA_ARGS}"
-D "TEST_PROPERTIES=${_PROPERTIES}"
-D "TEST_PREFIX=${_TEST_PREFIX}"
-D "TEST_SUFFIX=${_TEST_SUFFIX}"
-D "TEST_LIST=${_TEST_LIST}"
-D "TEST_REPORTER=${_REPORTER}"
-D "TEST_OUTPUT_DIR=${_OUTPUT_DIR}"
-D "TEST_OUTPUT_PREFIX=${_OUTPUT_PREFIX}"
-D "TEST_OUTPUT_SUFFIX=${_OUTPUT_SUFFIX}"
-D "TEST_DL_PATHS=${_DL_PATHS}"
-D "CTEST_FILE=${ctest_tests_file}"
-P "${_CATCH_DISCOVER_TESTS_SCRIPT}"
VERBATIM
)
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
file(WRITE "${ctest_include_file}"
"if(EXISTS \"${ctest_tests_file}\")\n"
" include(\"${ctest_tests_file}\")\n"
"else()\n"
" add_test(${TARGET}_NOT_BUILT-${args_hash} ${TARGET}_NOT_BUILT-${args_hash})\n"
"endif()\n"
)
elseif(_DISCOVERY_MODE STREQUAL "PRE_TEST")
string(CONCAT ctest_include_content
"if(EXISTS \"$<TARGET_FILE:${TARGET}>\")" "\n"
" if(NOT EXISTS \"${ctest_tests_file}\" OR" "\n"
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"$<TARGET_FILE:${TARGET}>\" OR\n"
" NOT \"${ctest_tests_file}\" IS_NEWER_THAN \"\${CMAKE_CURRENT_LIST_FILE}\")\n"
" include(\"${_CATCH_DISCOVER_TESTS_SCRIPT}\")" "\n"
" catch_discover_tests_impl(" "\n"
" TEST_EXECUTABLE" " [==[" "$<TARGET_FILE:${TARGET}>" "]==]" "\n"
" TEST_EXECUTOR" " [==[" "${crosscompiling_emulator}" "]==]" "\n"
" TEST_WORKING_DIR" " [==[" "${_WORKING_DIRECTORY}" "]==]" "\n"
" TEST_SPEC" " [==[" "${_TEST_SPEC}" "]==]" "\n"
" TEST_EXTRA_ARGS" " [==[" "${_EXTRA_ARGS}" "]==]" "\n"
" TEST_PROPERTIES" " [==[" "${_PROPERTIES}" "]==]" "\n"
" TEST_PREFIX" " [==[" "${_TEST_PREFIX}" "]==]" "\n"
" TEST_SUFFIX" " [==[" "${_TEST_SUFFIX}" "]==]" "\n"
" TEST_LIST" " [==[" "${_TEST_LIST}" "]==]" "\n"
" TEST_REPORTER" " [==[" "${_REPORTER}" "]==]" "\n"
" TEST_OUTPUT_DIR" " [==[" "${_OUTPUT_DIR}" "]==]" "\n"
" TEST_OUTPUT_PREFIX" " [==[" "${_OUTPUT_PREFIX}" "]==]" "\n"
" TEST_OUTPUT_SUFFIX" " [==[" "${_OUTPUT_SUFFIX}" "]==]" "\n"
" CTEST_FILE" " [==[" "${ctest_tests_file}" "]==]" "\n"
" TEST_DL_PATHS" " [==[" "${_DL_PATHS}" "]==]" "\n"
" CTEST_FILE" " [==[" "${CTEST_FILE}" "]==]" "\n"
" )" "\n"
" endif()" "\n"
" include(\"${ctest_tests_file}\")" "\n"
"else()" "\n"
" add_test(${TARGET}_NOT_BUILT ${TARGET}_NOT_BUILT)" "\n"
"endif()" "\n"
)
file(GENERATE OUTPUT "${ctest_include_file}" CONTENT "${ctest_include_content}")
endif()
if(NOT ${CMAKE_VERSION} VERSION_LESS "3.10.0")
# Add discovered tests to directory TEST_INCLUDE_FILES
set_property(DIRECTORY
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
@@ -204,9 +265,7 @@ function(catch_discover_tests TARGET)
PROPERTY TEST_INCLUDE_FILE "${ctest_include_file}"
)
else()
message(FATAL_ERROR
"Cannot set more than one TEST_INCLUDE_FILE"
)
message(FATAL_ERROR "Cannot set more than one TEST_INCLUDE_FILE")
endif()
endif()

View File

@@ -1,28 +1,6 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
set(prefix "${TEST_PREFIX}")
set(suffix "${TEST_SUFFIX}")
set(spec ${TEST_SPEC})
set(extra_args ${TEST_EXTRA_ARGS})
set(properties ${TEST_PROPERTIES})
set(reporter ${TEST_REPORTER})
set(output_dir ${TEST_OUTPUT_DIR})
set(output_prefix ${TEST_OUTPUT_PREFIX})
set(output_suffix ${TEST_OUTPUT_SUFFIX})
set(dl_paths ${TEST_DL_PATHS})
set(script)
set(suite)
set(tests)
if(WIN32)
set(dl_paths_variable_name PATH)
elseif(APPLE)
set(dl_paths_variable_name DYLD_LIBRARY_PATH)
else()
set(dl_paths_variable_name LD_LIBRARY_PATH)
endif()
function(add_command NAME)
set(_args "")
# use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
@@ -38,119 +16,177 @@ function(add_command NAME)
set(script "${script}${NAME}(${_args})\n" PARENT_SCOPE)
endfunction()
# Run test executable to get list of available tests
if(NOT EXISTS "${TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${TEST_EXECUTABLE}' does not exist"
function(catch_discover_tests_impl)
cmake_parse_arguments(
""
""
"TEST_EXECUTABLE;TEST_WORKING_DIR;TEST_DL_PATHS;TEST_OUTPUT_DIR;TEST_OUTPUT_PREFIX;TEST_OUTPUT_SUFFIX;TEST_PREFIX;TEST_REPORTER;TEST_SPEC;TEST_SUFFIX;TEST_LIST;CTEST_FILE"
"TEST_EXTRA_ARGS;TEST_PROPERTIES;TEST_EXECUTOR"
${ARGN}
)
endif()
if(dl_paths)
cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths)
set(ENV{${dl_paths_variable_name}} "${paths}")
endif()
set(prefix "${_TEST_PREFIX}")
set(suffix "${_TEST_SUFFIX}")
set(spec ${_TEST_SPEC})
set(extra_args ${_TEST_EXTRA_ARGS})
set(properties ${_TEST_PROPERTIES})
set(reporter ${_TEST_REPORTER})
set(output_dir ${_TEST_OUTPUT_DIR})
set(output_prefix ${_TEST_OUTPUT_PREFIX})
set(output_suffix ${_TEST_OUTPUT_SUFFIX})
set(dl_paths ${_TEST_DL_PATHS})
set(script)
set(suite)
set(tests)
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet
OUTPUT_VARIABLE output
RESULT_VARIABLE result
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
string(REPLACE "\n" ";" output "${output}")
# Run test executable to get list of available reporters
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} --list-reporters
OUTPUT_VARIABLE reporters_output
RESULT_VARIABLE reporters_result
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(NOT ${reporters_result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${reporters_result}\n"
" Output: ${reporters_output}\n"
)
endif()
string(FIND "${reporters_output}" "${reporter}" reporter_is_valid)
if(reporter AND ${reporter_is_valid} EQUAL -1)
message(FATAL_ERROR
"\"${reporter}\" is not a valid reporter!\n"
)
endif()
# Prepare reporter
if(reporter)
set(reporter_arg "--reporter ${reporter}")
endif()
# Prepare output dir
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
set(output_dir "${TEST_WORKING_DIR}/${output_dir}")
if(NOT EXISTS ${output_dir})
file(MAKE_DIRECTORY ${output_dir})
if(WIN32)
set(dl_paths_variable_name PATH)
elseif(APPLE)
set(dl_paths_variable_name DYLD_LIBRARY_PATH)
else()
set(dl_paths_variable_name LD_LIBRARY_PATH)
endif()
endif()
if(dl_paths)
foreach(path ${dl_paths})
cmake_path(NATIVE_PATH path native_path)
list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
# Run test executable to get list of available tests
if(NOT EXISTS "${_TEST_EXECUTABLE}")
message(FATAL_ERROR
"Specified test executable '${_TEST_EXECUTABLE}' does not exist"
)
endif()
if(dl_paths)
cmake_path(CONVERT "${dl_paths}" TO_NATIVE_PATH_LIST paths)
set(ENV{${dl_paths_variable_name}} "${paths}")
endif()
execute_process(
COMMAND ${_TEST_EXECUTOR} "${_TEST_EXECUTABLE}" ${spec} --list-tests --verbosity quiet
OUTPUT_VARIABLE output
RESULT_VARIABLE result
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
)
if(NOT ${result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${_TEST_EXECUTABLE}':\n"
" Result: ${result}\n"
" Output: ${output}\n"
)
endif()
# Make sure to escape ; (semicolons) in test names first, because
# that'd break the foreach loop for "Parse output" later and create
# wrongly splitted and thus failing test cases (false positives)
string(REPLACE ";" "\;" output "${output}")
string(REPLACE "\n" ";" output "${output}")
# Prepare reporter
if(reporter)
set(reporter_arg "--reporter ${reporter}")
# Run test executable to check whether reporter is available
# note that the use of --list-reporters is not the important part,
# we only want to check whether the execution succeeds with ${reporter_arg}
execute_process(
COMMAND ${TEST_EXECUTOR} "${TEST_EXECUTABLE}" ${spec} ${reporter_arg} --list-reporters
OUTPUT_VARIABLE reporter_check_output
RESULT_VARIABLE reporter_check_result
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
)
if(${reporter_check_result} EQUAL 255)
message(FATAL_ERROR
"\"${reporter}\" is not a valid reporter!\n"
)
elseif(NOT ${reporter_check_result} EQUAL 0)
message(FATAL_ERROR
"Error running test executable '${TEST_EXECUTABLE}':\n"
" Result: ${reporter_check_result}\n"
" Output: ${reporter_check_output}\n"
)
endif()
endif()
# Prepare output dir
if(output_dir AND NOT IS_ABSOLUTE ${output_dir})
set(output_dir "${_TEST_WORKING_DIR}/${output_dir}")
if(NOT EXISTS ${output_dir})
file(MAKE_DIRECTORY ${output_dir})
endif()
endif()
if(dl_paths)
foreach(path ${dl_paths})
cmake_path(NATIVE_PATH path native_path)
list(APPEND environment_modifications "${dl_paths_variable_name}=path_list_prepend:${native_path}")
endforeach()
endif()
# Parse output
foreach(line ${output})
set(test "${line}")
# Escape characters in test case names that would be parsed by Catch2
# Note that the \ escaping must happen FIRST! Do not change the order.
set(test_name "${test}")
foreach(char \\ , [ ])
string(REPLACE ${char} "\\${char}" test_name "${test_name}")
endforeach(char)
# ...add output dir
if(output_dir)
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean "${test_name}")
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
endif()
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${_TEST_EXECUTOR}
"${_TEST_EXECUTABLE}"
"${test_name}"
${extra_args}
"${reporter_arg}"
"${output_dir_arg}"
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${_TEST_WORKING_DIR}"
${properties}
)
if(environment_modifications)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}")
endif()
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${_TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${_CTEST_FILE}" "${script}")
endfunction()
if(CMAKE_SCRIPT_MODE_FILE)
catch_discover_tests_impl(
TEST_EXECUTABLE ${TEST_EXECUTABLE}
TEST_EXECUTOR ${TEST_EXECUTOR}
TEST_WORKING_DIR ${TEST_WORKING_DIR}
TEST_SPEC ${TEST_SPEC}
TEST_EXTRA_ARGS ${TEST_EXTRA_ARGS}
TEST_PROPERTIES ${TEST_PROPERTIES}
TEST_PREFIX ${TEST_PREFIX}
TEST_SUFFIX ${TEST_SUFFIX}
TEST_LIST ${TEST_LIST}
TEST_REPORTER ${TEST_REPORTER}
TEST_OUTPUT_DIR ${TEST_OUTPUT_DIR}
TEST_OUTPUT_PREFIX ${TEST_OUTPUT_PREFIX}
TEST_OUTPUT_SUFFIX ${TEST_OUTPUT_SUFFIX}
TEST_DL_PATHS ${TEST_DL_PATHS}
CTEST_FILE ${CTEST_FILE}
)
endif()
# Parse output
foreach(line ${output})
set(test ${line})
# Escape characters in test case names that would be parsed by Catch2
set(test_name ${test})
foreach(char , [ ])
string(REPLACE ${char} "\\${char}" test_name ${test_name})
endforeach(char)
# ...add output dir
if(output_dir)
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" test_name_clean ${test_name})
set(output_dir_arg "--out ${output_dir}/${output_prefix}${test_name_clean}${output_suffix}")
endif()
# ...and add to script
add_command(add_test
"${prefix}${test}${suffix}"
${TEST_EXECUTOR}
"${TEST_EXECUTABLE}"
"${test_name}"
${extra_args}
"${reporter_arg}"
"${output_dir_arg}"
)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
WORKING_DIRECTORY "${TEST_WORKING_DIR}"
${properties}
)
if(environment_modifications)
add_command(set_tests_properties
"${prefix}${test}${suffix}"
PROPERTIES
ENVIRONMENT_MODIFICATION "${environment_modifications}")
endif()
list(APPEND tests "${prefix}${test}${suffix}")
endforeach()
# Create a list of all discovered tests, which users may use to e.g. set
# properties on the tests
add_command(set ${TEST_LIST} ${tests})
# Write CTest script
file(WRITE "${CTEST_FILE}" "${script}")

View File

@@ -46,7 +46,7 @@ function(catch_add_sharded_tests TARGET)
APPEND PROPERTY TEST_INCLUDE_FILES "${ctest_include_file}"
)
set(shard_impl_script_file "${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake")
set(shard_impl_script_file "${_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT}")
add_custom_command(
TARGET ${TARGET} POST_BUILD
@@ -64,3 +64,11 @@ function(catch_add_sharded_tests TARGET)
endfunction()
###############################################################################
set(_CATCH_DISCOVER_SHARD_TESTS_IMPL_SCRIPT
${CMAKE_CURRENT_LIST_DIR}/CatchShardTestsImpl.cmake
CACHE INTERNAL "Catch2 full path to CatchShardTestsImpl.cmake helper file"
)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,12 @@
project(
'catch2',
'cpp',
version: '3.3.0', # CML version placeholder, don't delete
version: '3.4.0', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.50.0',
meson_version: '>=0.54.1',
)
subdir('src/catch2')
subdir('tests')
if get_option('tests')
subdir('tests')
endif

1
meson_options.txt Normal file
View File

@@ -0,0 +1 @@
option('tests', type: 'boolean', value: true, description: 'Build the unit tests')

View File

@@ -21,6 +21,8 @@ set(BENCHMARK_HEADERS
${SOURCES_DIR}/benchmark/catch_sample_analysis.hpp
${SOURCES_DIR}/benchmark/detail/catch_analyse.hpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_function.hpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats.hpp
${SOURCES_DIR}/benchmark/detail/catch_benchmark_stats_fwd.hpp
${SOURCES_DIR}/benchmark/detail/catch_complete_invoke.hpp
${SOURCES_DIR}/benchmark/detail/catch_estimate_clock.hpp
${SOURCES_DIR}/benchmark/detail/catch_measure.hpp
@@ -71,6 +73,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_compiler_capabilities.hpp
${SOURCES_DIR}/internal/catch_config_android_logwrite.hpp
${SOURCES_DIR}/internal/catch_config_counter.hpp
${SOURCES_DIR}/internal/catch_config_static_analysis_support.hpp
${SOURCES_DIR}/internal/catch_config_uncaught_exceptions.hpp
${SOURCES_DIR}/internal/catch_config_wchar.hpp
${SOURCES_DIR}/internal/catch_console_colour.hpp
@@ -88,6 +91,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
${SOURCES_DIR}/internal/catch_getenv.hpp
${SOURCES_DIR}/internal/catch_istream.hpp
${SOURCES_DIR}/internal/catch_is_permutation.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_list.hpp
@@ -127,6 +131,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_test_failure_exception.hpp
${SOURCES_DIR}/internal/catch_test_macro_impl.hpp
${SOURCES_DIR}/internal/catch_test_registry.hpp
${SOURCES_DIR}/internal/catch_test_run_info.hpp
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
${SOURCES_DIR}/internal/catch_textflow.hpp
${SOURCES_DIR}/internal/catch_to_string.hpp
@@ -152,6 +157,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/catch_timer.cpp
${SOURCES_DIR}/catch_tostring.cpp
${SOURCES_DIR}/catch_totals.cpp
${SOURCES_DIR}/catch_translate_exception.cpp
${SOURCES_DIR}/catch_version.cpp
${SOURCES_DIR}/internal/catch_assertion_handler.cpp
${SOURCES_DIR}/internal/catch_case_insensitive_comparisons.cpp
@@ -215,8 +221,8 @@ set(INTERFACE_HEADERS
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_tag_alias_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_test_invoker.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp
)
set(INTERFACE_SOURCES
@@ -227,7 +233,6 @@ set(INTERFACE_SOURCES
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.cpp
${SOURCES_DIR}/interfaces/catch_interfaces_testcase.cpp
)
set(INTERFACE_FILES ${INTERFACE_HEADERS} ${INTERFACE_SOURCES})

View File

@@ -10,23 +10,27 @@
#ifndef CATCH_BENCHMARK_HPP_INCLUDED
#define CATCH_BENCHMARK_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/catch_user_config.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/benchmark/catch_chronometer.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_unique_name.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_environment.hpp>
#include <catch2/benchmark/catch_execution_plan.hpp>
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_analyse.hpp>
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
#include <algorithm>
#include <chrono>
#include <exception>
#include <functional>
#include <string>
#include <vector>
@@ -64,7 +68,7 @@ namespace Catch {
});
BenchmarkInfo info {
name,
CATCH_MOVE(name),
plan.estimated_duration.count(),
plan.iterations_per_sample,
cfg->benchmarkSamples(),
@@ -80,9 +84,9 @@ namespace Catch {
});
auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
BenchmarkStats<FloatDuration<Clock>> stats{ CATCH_MOVE(info), CATCH_MOVE(analysis.samples), analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
getResultCapture().benchmarkEnded(stats);
} CATCH_CATCH_ANON (TestFailureException) {
} CATCH_CATCH_ANON (TestFailureException const&) {
getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
} CATCH_CATCH_ALL{
getResultCapture().benchmarkFailed(translateActiveException());

View File

@@ -33,6 +33,8 @@
#include <catch2/benchmark/catch_sample_analysis.hpp>
#include <catch2/benchmark/detail/catch_analyse.hpp>
#include <catch2/benchmark/detail/catch_benchmark_function.hpp>
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_estimate_clock.hpp>
#include <catch2/benchmark/detail/catch_measure.hpp>

View File

@@ -12,7 +12,6 @@
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_optimizer.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>

View File

@@ -17,8 +17,7 @@
#include <catch2/benchmark/detail/catch_repeat.hpp>
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
#include <algorithm>
#include <iterator>
#include <vector>
namespace Catch {
namespace Benchmark {
@@ -41,14 +40,17 @@ namespace Catch {
Detail::run_for_at_least<Clock>(std::chrono::duration_cast<ClockDuration<Clock>>(warmup_time), warmup_iterations, Detail::repeat(now<Clock>{}));
std::vector<FloatDuration<Clock>> times;
times.reserve(cfg.benchmarkSamples());
std::generate_n(std::back_inserter(times), cfg.benchmarkSamples(), [this, env] {
const auto num_samples = cfg.benchmarkSamples();
times.reserve( num_samples );
for ( size_t i = 0; i < num_samples; ++i ) {
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;
if (sample_time < FloatDuration<Clock>::zero()) sample_time = FloatDuration<Clock>::zero();
return sample_time / iterations_per_sample;
});
if ( sample_time < FloatDuration<Clock>::zero() ) {
sample_time = FloatDuration<Clock>::zero();
}
times.push_back(sample_time / iterations_per_sample);
}
return times;
}
};

View File

@@ -10,7 +10,7 @@
#ifndef CATCH_OPTIMIZER_HPP_INCLUDED
#define CATCH_OPTIMIZER_HPP_INCLUDED
#if defined(_MSC_VER)
#if defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
# include <atomic> // atomic_thread_fence
#endif
@@ -32,16 +32,23 @@ namespace Catch {
namespace Detail {
inline void optimizer_barrier() { keep_memory(); }
} // namespace Detail
#elif defined(_MSC_VER)
#elif defined(_MSC_VER) || defined(__IAR_SYSTEMS_ICC__)
#if defined(_MSVC_VER)
#pragma optimize("", off)
#elif defined(__IAR_SYSTEMS_ICC__)
// For IAR the pragma only affects the following function
#pragma optimize=disable
#endif
template <typename T>
inline void keep_memory(T* p) {
// thanks @milleniumbug
*reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p);
}
// TODO equivalent keep_memory()
#if defined(_MSVC_VER)
#pragma optimize("", on)
#endif
namespace Detail {
inline void optimizer_barrier() {

View File

@@ -10,14 +10,11 @@
#ifndef CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
#define CATCH_SAMPLE_ANALYSIS_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <algorithm>
#include <vector>
#include <iterator>
namespace Catch {
namespace Benchmark {
@@ -33,7 +30,9 @@ namespace Catch {
operator SampleAnalysis<Duration2>() const {
std::vector<Duration2> samples2;
samples2.reserve(samples.size());
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
for (auto const& d : samples) {
samples2.push_back(Duration2(d));
}
return {
CATCH_MOVE(samples2),
mean,

View File

@@ -10,15 +10,12 @@
#ifndef CATCH_ANALYSE_HPP_INCLUDED
#define CATCH_ANALYSE_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/catch_environment.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 <algorithm>
#include <iterator>
#include <vector>
namespace Catch {
@@ -29,7 +26,9 @@ namespace Catch {
if (!cfg.benchmarkNoAnalysis()) {
std::vector<double> samples;
samples.reserve(static_cast<size_t>(last - first));
std::transform(first, last, std::back_inserter(samples), [](Duration d) { return d.count(); });
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());
@@ -44,7 +43,10 @@ namespace Catch {
};
std::vector<Duration> samples2;
samples2.reserve(samples.size());
std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
for (auto s : samples) {
samples2.push_back( Duration( s ) );
}
return {
CATCH_MOVE(samples2),
wrap_estimate(analysis.mean),

View File

@@ -11,7 +11,6 @@
#define CATCH_BENCHMARK_FUNCTION_HPP_INCLUDED
#include <catch2/benchmark/catch_chronometer.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>

View File

@@ -0,0 +1,64 @@
// 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_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_outlier_classification.hpp>
// The fwd decl & default specialization needs to be seen by VS2017 before
// BenchmarkStats itself, or VS2017 will report compilation error.
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
#include <string>
#include <vector>
namespace Catch {
struct BenchmarkInfo {
std::string name;
double estimatedDuration;
int iterations;
unsigned int samples;
unsigned int resamples;
double clockResolution;
double clockCost;
};
template <class Duration>
struct BenchmarkStats {
BenchmarkInfo info;
std::vector<Duration> samples;
Benchmark::Estimate<Duration> mean;
Benchmark::Estimate<Duration> standardDeviation;
Benchmark::OutlierClassification outliers;
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,
};
}
};
} // end namespace Catch
#endif // CATCH_BENCHMARK_STATS_HPP_INCLUDED

View File

@@ -0,0 +1,23 @@
// 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_BENCHMARK_STATS_FWD_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#include <chrono>
namespace Catch {
// We cannot forward declare the type with default template argument
// multiple times, so it is split out into a separate header so that
// we can prevent multiple declarations in dependees
template <typename Duration = std::chrono::duration<double, std::nano>>
struct BenchmarkStats;
} // end namespace Catch
#endif // CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED

View File

@@ -10,14 +10,9 @@
#ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
#define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <type_traits>
namespace Catch {
namespace Benchmark {
namespace Detail {

View File

@@ -19,7 +19,6 @@
#include <catch2/internal/catch_unique_ptr.hpp>
#include <algorithm>
#include <iterator>
#include <vector>
#include <cmath>
@@ -30,26 +29,29 @@ namespace Catch {
std::vector<double> resolution(int k) {
std::vector<TimePoint<Clock>> times;
times.reserve(static_cast<size_t>(k + 1));
std::generate_n(std::back_inserter(times), k + 1, now<Clock>{});
for ( int i = 0; i < k + 1; ++i ) {
times.push_back( Clock::now() );
}
std::vector<double> deltas;
deltas.reserve(static_cast<size_t>(k));
std::transform(std::next(times.begin()), times.end(), times.begin(),
std::back_inserter(deltas),
[](TimePoint<Clock> a, TimePoint<Clock> b) { return static_cast<double>((a - b).count()); });
for ( size_t idx = 1; idx < times.size(); ++idx ) {
deltas.push_back( static_cast<double>(
( times[idx] - times[idx - 1] ).count() ) );
}
return deltas;
}
const auto warmup_iterations = 10000;
const auto warmup_time = std::chrono::milliseconds(100);
const auto minimum_ticks = 1000;
const auto warmup_seed = 10000;
const auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
const auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
const auto clock_cost_estimation_tick_limit = 100000;
const auto clock_cost_estimation_time = std::chrono::milliseconds(10);
const auto clock_cost_estimation_iterations = 10000;
constexpr auto warmup_iterations = 10000;
constexpr auto warmup_time = std::chrono::milliseconds(100);
constexpr auto minimum_ticks = 1000;
constexpr auto warmup_seed = 10000;
constexpr auto clock_resolution_estimation_time = std::chrono::milliseconds(500);
constexpr auto clock_cost_estimation_time_limit = std::chrono::seconds(1);
constexpr auto clock_cost_estimation_tick_limit = 100000;
constexpr auto clock_cost_estimation_time = std::chrono::milliseconds(10);
constexpr auto clock_cost_estimation_iterations = 10000;
template <typename Clock>
int warmup() {
@@ -84,9 +86,11 @@ namespace Catch {
std::vector<double> times;
int nsamples = static_cast<int>(std::ceil(time_limit / r.elapsed));
times.reserve(static_cast<size_t>(nsamples));
std::generate_n(std::back_inserter(times), nsamples, [time_clock, &r] {
return static_cast<double>((time_clock(r.iterations) / r.iterations).count());
});
for ( int s = 0; s < nsamples; ++s ) {
times.push_back( static_cast<double>(
( time_clock( r.iterations ) / r.iterations )
.count() ) );
}
return {
FloatDuration<Clock>(mean(times.begin(), times.end())),
classify_outliers(times.begin(), times.end()),

View File

@@ -10,7 +10,6 @@
#ifndef CATCH_MEASURE_HPP_INCLUDED
#define CATCH_MEASURE_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
#include <catch2/benchmark/detail/catch_timing.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>

View File

@@ -7,9 +7,10 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
#include <exception>
#include <catch2/internal/catch_enforce.hpp>
#include <exception>
namespace Catch {
namespace Benchmark {
namespace Detail {

View File

@@ -13,7 +13,7 @@
#include <cassert>
#include <cstddef>
#include <iterator>
#include <numeric>
#include <random>
@@ -21,117 +21,172 @@
#include <future>
#endif
namespace {
namespace Catch {
namespace Benchmark {
namespace Detail {
namespace {
using Catch::Benchmark::Detail::sample;
template <typename URng, typename Estimator>
static sample
resample( URng& rng,
unsigned int resamples,
std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last,
Estimator& estimator ) {
auto n = static_cast<size_t>( last - first );
std::uniform_int_distribution<decltype( n )> dist( 0,
n - 1 );
template <typename URng, typename Estimator>
sample resample(URng& rng, unsigned int resamples, std::vector<double>::iterator first, std::vector<double>::iterator last, Estimator& estimator) {
auto n = static_cast<size_t>(last - first);
std::uniform_int_distribution<decltype(n)> dist(0, n - 1);
sample out;
out.reserve( resamples );
// We allocate the vector outside the loop to avoid realloc
// per resample
std::vector<double> resampled;
resampled.reserve( n );
for ( size_t i = 0; i < resamples; ++i ) {
resampled.clear();
for ( size_t s = 0; s < n; ++s ) {
resampled.push_back(
first[static_cast<std::ptrdiff_t>(
dist( rng ) )] );
}
const auto estimate =
estimator( resampled.begin(), resampled.end() );
out.push_back( estimate );
}
std::sort( out.begin(), out.end() );
return out;
}
sample out;
out.reserve(resamples);
std::generate_n(std::back_inserter(out), resamples, [n, first, &estimator, &dist, &rng] {
std::vector<double> resampled;
resampled.reserve(n);
std::generate_n(std::back_inserter(resampled), n, [first, &dist, &rng] { return first[static_cast<std::ptrdiff_t>(dist(rng))]; });
return estimator(resampled.begin(), resampled.end());
});
std::sort(out.begin(), out.end());
return out;
}
static double outlier_variance( Estimate<double> mean,
Estimate<double> stddev,
int n ) {
double sb = stddev.point;
double mn = mean.point / n;
double mg_min = mn / 2.;
double sg = (std::min)( mg_min / 4., sb / std::sqrt( n ) );
double sg2 = sg * sg;
double sb2 = sb * sb;
auto c_max = [n, mn, sb2, sg2]( double x ) -> double {
double k = mn - x;
double d = k * k;
double nd = n * d;
double k0 = -n * nd;
double k1 = sb2 - n * sg2 + nd;
double det = k1 * k1 - 4 * sg2 * k0;
return static_cast<int>( -2. * k0 /
( k1 + std::sqrt( det ) ) );
};
double erf_inv(double x) {
// Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2
double w, p;
auto var_out = [n, sb2, sg2]( double c ) {
double nc = n - c;
return ( nc / n ) * ( sb2 - nc * sg2 );
};
w = -log((1.0 - x) * (1.0 + x));
return (std::min)( var_out( 1 ),
var_out(
(std::min)( c_max( 0. ),
c_max( mg_min ) ) ) ) /
sb2;
}
if (w < 6.250000) {
w = w - 3.125000;
p = -3.6444120640178196996e-21;
p = -1.685059138182016589e-19 + p * w;
p = 1.2858480715256400167e-18 + p * w;
p = 1.115787767802518096e-17 + p * w;
p = -1.333171662854620906e-16 + p * w;
p = 2.0972767875968561637e-17 + p * w;
p = 6.6376381343583238325e-15 + p * w;
p = -4.0545662729752068639e-14 + p * w;
p = -8.1519341976054721522e-14 + p * w;
p = 2.6335093153082322977e-12 + p * w;
p = -1.2975133253453532498e-11 + p * w;
p = -5.4154120542946279317e-11 + p * w;
p = 1.051212273321532285e-09 + p * w;
p = -4.1126339803469836976e-09 + p * w;
p = -2.9070369957882005086e-08 + p * w;
p = 4.2347877827932403518e-07 + p * w;
p = -1.3654692000834678645e-06 + p * w;
p = -1.3882523362786468719e-05 + p * w;
p = 0.0001867342080340571352 + p * w;
p = -0.00074070253416626697512 + p * w;
p = -0.0060336708714301490533 + p * w;
p = 0.24015818242558961693 + p * w;
p = 1.6536545626831027356 + p * w;
} else if (w < 16.000000) {
w = sqrt(w) - 3.250000;
p = 2.2137376921775787049e-09;
p = 9.0756561938885390979e-08 + p * w;
p = -2.7517406297064545428e-07 + p * w;
p = 1.8239629214389227755e-08 + p * w;
p = 1.5027403968909827627e-06 + p * w;
p = -4.013867526981545969e-06 + p * w;
p = 2.9234449089955446044e-06 + p * w;
p = 1.2475304481671778723e-05 + p * w;
p = -4.7318229009055733981e-05 + p * w;
p = 6.8284851459573175448e-05 + p * w;
p = 2.4031110387097893999e-05 + p * w;
p = -0.0003550375203628474796 + p * w;
p = 0.00095328937973738049703 + p * w;
p = -0.0016882755560235047313 + p * w;
p = 0.0024914420961078508066 + p * w;
p = -0.0037512085075692412107 + p * w;
p = 0.005370914553590063617 + p * w;
p = 1.0052589676941592334 + p * w;
p = 3.0838856104922207635 + p * w;
} else {
w = sqrt(w) - 5.000000;
p = -2.7109920616438573243e-11;
p = -2.5556418169965252055e-10 + p * w;
p = 1.5076572693500548083e-09 + p * w;
p = -3.7894654401267369937e-09 + p * w;
p = 7.6157012080783393804e-09 + p * w;
p = -1.4960026627149240478e-08 + p * w;
p = 2.9147953450901080826e-08 + p * w;
p = -6.7711997758452339498e-08 + p * w;
p = 2.2900482228026654717e-07 + p * w;
p = -9.9298272942317002539e-07 + p * w;
p = 4.5260625972231537039e-06 + p * w;
p = -1.9681778105531670567e-05 + p * w;
p = 7.5995277030017761139e-05 + p * w;
p = -0.00021503011930044477347 + p * w;
p = -0.00013871931833623122026 + p * w;
p = 1.0103004648645343977 + p * w;
p = 4.8499064014085844221 + p * w;
}
return p * x;
}
static double erf_inv( double x ) {
// Code accompanying the article "Approximating the erfinv
// function" in GPU Computing Gems, Volume 2
double w, p;
double standard_deviation(std::vector<double>::iterator first, std::vector<double>::iterator last) {
auto m = Catch::Benchmark::Detail::mean(first, last);
double variance = std::accumulate( first,
last,
0.,
[m]( double a, double b ) {
double diff = b - m;
return a + diff * diff;
} ) /
( last - first );
return std::sqrt( variance );
}
w = -log( ( 1.0 - x ) * ( 1.0 + x ) );
}
if ( w < 6.250000 ) {
w = w - 3.125000;
p = -3.6444120640178196996e-21;
p = -1.685059138182016589e-19 + p * w;
p = 1.2858480715256400167e-18 + p * w;
p = 1.115787767802518096e-17 + p * w;
p = -1.333171662854620906e-16 + p * w;
p = 2.0972767875968561637e-17 + p * w;
p = 6.6376381343583238325e-15 + p * w;
p = -4.0545662729752068639e-14 + p * w;
p = -8.1519341976054721522e-14 + p * w;
p = 2.6335093153082322977e-12 + p * w;
p = -1.2975133253453532498e-11 + p * w;
p = -5.4154120542946279317e-11 + p * w;
p = 1.051212273321532285e-09 + p * w;
p = -4.1126339803469836976e-09 + p * w;
p = -2.9070369957882005086e-08 + p * w;
p = 4.2347877827932403518e-07 + p * w;
p = -1.3654692000834678645e-06 + p * w;
p = -1.3882523362786468719e-05 + p * w;
p = 0.0001867342080340571352 + p * w;
p = -0.00074070253416626697512 + p * w;
p = -0.0060336708714301490533 + p * w;
p = 0.24015818242558961693 + p * w;
p = 1.6536545626831027356 + p * w;
} else if ( w < 16.000000 ) {
w = sqrt( w ) - 3.250000;
p = 2.2137376921775787049e-09;
p = 9.0756561938885390979e-08 + p * w;
p = -2.7517406297064545428e-07 + p * w;
p = 1.8239629214389227755e-08 + p * w;
p = 1.5027403968909827627e-06 + p * w;
p = -4.013867526981545969e-06 + p * w;
p = 2.9234449089955446044e-06 + p * w;
p = 1.2475304481671778723e-05 + p * w;
p = -4.7318229009055733981e-05 + p * w;
p = 6.8284851459573175448e-05 + p * w;
p = 2.4031110387097893999e-05 + p * w;
p = -0.0003550375203628474796 + p * w;
p = 0.00095328937973738049703 + p * w;
p = -0.0016882755560235047313 + p * w;
p = 0.0024914420961078508066 + p * w;
p = -0.0037512085075692412107 + p * w;
p = 0.005370914553590063617 + p * w;
p = 1.0052589676941592334 + p * w;
p = 3.0838856104922207635 + p * w;
} else {
w = sqrt( w ) - 5.000000;
p = -2.7109920616438573243e-11;
p = -2.5556418169965252055e-10 + p * w;
p = 1.5076572693500548083e-09 + p * w;
p = -3.7894654401267369937e-09 + p * w;
p = 7.6157012080783393804e-09 + p * w;
p = -1.4960026627149240478e-08 + p * w;
p = 2.9147953450901080826e-08 + p * w;
p = -6.7711997758452339498e-08 + p * w;
p = 2.2900482228026654717e-07 + p * w;
p = -9.9298272942317002539e-07 + p * w;
p = 4.5260625972231537039e-06 + p * w;
p = -1.9681778105531670567e-05 + p * w;
p = 7.5995277030017761139e-05 + p * w;
p = -0.00021503011930044477347 + p * w;
p = -0.00013871931833623122026 + p * w;
p = 1.0103004648645343977 + p * w;
p = 4.8499064014085844221 + p * w;
}
return p * x;
}
static double
standard_deviation( std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last ) {
auto m = Catch::Benchmark::Detail::mean( first, last );
double variance =
std::accumulate( first,
last,
0.,
[m]( double a, double b ) {
double diff = b - m;
return a + diff * diff;
} ) /
( last - first );
return std::sqrt( variance );
}
} // namespace
} // namespace Detail
} // namespace Benchmark
} // namespace Catch
namespace Catch {
namespace Benchmark {
@@ -161,6 +216,47 @@ namespace Catch {
return xj + g * (xj1 - xj);
}
OutlierClassification
classify_outliers( std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last ) {
std::vector<double> copy( first, last );
auto q1 = weighted_average_quantile( 1, 4, copy.begin(), copy.end() );
auto q3 = weighted_average_quantile( 3, 4, copy.begin(), copy.end() );
auto iqr = q3 - q1;
auto los = q1 - ( iqr * 3. );
auto lom = q1 - ( iqr * 1.5 );
auto him = q3 + ( iqr * 1.5 );
auto his = q3 + ( iqr * 3. );
OutlierClassification o;
for ( ; first != last; ++first ) {
const double t = *first;
if ( t < los ) {
++o.low_severe;
} else if ( t < lom ) {
++o.low_mild;
} else if ( t > his ) {
++o.high_severe;
} else if ( t > him ) {
++o.high_mild;
}
++o.samples_seen;
}
return o;
}
double mean( std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last ) {
auto count = last - first;
double sum = 0.;
while (first != last) {
sum += *first;
++first;
}
return sum / static_cast<double>(count);
}
double erfc_inv(double x) {
return erf_inv(1.0 - x);
@@ -182,35 +278,10 @@ namespace Catch {
return result;
}
double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n) {
double sb = stddev.point;
double mn = mean.point / n;
double mg_min = mn / 2.;
double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
double sg2 = sg * sg;
double sb2 = sb * sb;
auto c_max = [n, mn, sb2, sg2](double x) -> double {
double k = mn - x;
double d = k * k;
double nd = n * d;
double k0 = -n * nd;
double k1 = sb2 - n * sg2 + nd;
double det = k1 * k1 - 4 * sg2 * k0;
return static_cast<int>(-2. * k0 / (k1 + std::sqrt(det)));
};
auto var_out = [n, sb2, sg2](double c) {
double nc = n - c;
return (nc / n) * (sb2 - nc * sg2);
};
return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
}
bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last) {
bootstrap_analysis analyse_samples(double confidence_level,
unsigned int n_resamples,
std::vector<double>::iterator first,
std::vector<double>::iterator last) {
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
static std::random_device entropy;
@@ -218,11 +289,12 @@ namespace Catch {
auto n = static_cast<int>(last - first); // seriously, one can't use integral types without hell in C++
auto mean = &Detail::mean<std::vector<double>::iterator>;
auto mean = &Detail::mean;
auto stddev = &standard_deviation;
#if defined(CATCH_CONFIG_USE_ASYNC)
auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator,
std::vector<double>::const_iterator)) {
auto seed = entropy();
return std::async(std::launch::async, [=] {
std::mt19937 rng(seed);
@@ -237,7 +309,8 @@ namespace Catch {
auto mean_estimate = mean_future.get();
auto stddev_estimate = stddev_future.get();
#else
auto Estimate = [=](double(*f)(std::vector<double>::iterator, std::vector<double>::iterator)) {
auto Estimate = [=](double(*f)(std::vector<double>::const_iterator,
std::vector<double>::const_iterator)) {
auto seed = entropy();
std::mt19937 rng(seed);
auto resampled = resample(rng, n_resamples, first, last, f);

View File

@@ -15,8 +15,6 @@
#include <algorithm>
#include <vector>
#include <numeric>
#include <tuple>
#include <cmath>
namespace Catch {
@@ -30,39 +28,17 @@ namespace Catch {
double weighted_average_quantile(int k, int q, std::vector<double>::iterator first, std::vector<double>::iterator last);
template <typename Iterator>
OutlierClassification classify_outliers(Iterator first, Iterator last) {
std::vector<double> copy(first, last);
OutlierClassification
classify_outliers( std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last );
auto q1 = weighted_average_quantile(1, 4, copy.begin(), copy.end());
auto q3 = weighted_average_quantile(3, 4, copy.begin(), copy.end());
auto iqr = q3 - q1;
auto los = q1 - (iqr * 3.);
auto lom = q1 - (iqr * 1.5);
auto him = q3 + (iqr * 1.5);
auto his = q3 + (iqr * 3.);
double mean( std::vector<double>::const_iterator first,
std::vector<double>::const_iterator last );
OutlierClassification o;
for (; first != last; ++first) {
auto&& t = *first;
if (t < los) ++o.low_severe;
else if (t < lom) ++o.low_mild;
else if (t > his) ++o.high_severe;
else if (t > him) ++o.high_mild;
++o.samples_seen;
}
return o;
}
template <typename Iterator>
double mean(Iterator first, Iterator last) {
auto count = last - first;
double sum = std::accumulate(first, last, 0.);
return sum / static_cast<double>(count);
}
template <typename Estimator, typename Iterator>
sample jackknife(Estimator&& estimator, Iterator first, Iterator last) {
template <typename Estimator>
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;
@@ -85,8 +61,12 @@ namespace Catch {
double normal_quantile(double p);
template <typename Iterator, typename Estimator>
Estimate<double> bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) {
template <typename Estimator>
Estimate<double> bootstrap( double confidence_level,
std::vector<double>::iterator first,
std::vector<double>::iterator last,
sample const& resample,
Estimator&& estimator ) {
auto n_samples = last - first;
double point = estimator(first, last);
@@ -95,13 +75,13 @@ namespace Catch {
sample jack = jackknife(estimator, first, last);
double jack_mean = mean(jack.begin(), jack.end());
double sum_squares, sum_cubes;
std::tie(sum_squares, sum_cubes) = std::accumulate(jack.begin(), jack.end(), std::make_pair(0., 0.), [jack_mean](std::pair<double, double> sqcb, double x) -> std::pair<double, double> {
auto d = jack_mean - x;
auto d2 = d * d;
auto d3 = d2 * d;
return { sqcb.first + d2, sqcb.second + d3 };
});
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());
@@ -128,15 +108,16 @@ namespace Catch {
return { point, resample[lo], resample[hi], confidence_level };
}
double outlier_variance(Estimate<double> mean, Estimate<double> stddev, int n);
struct bootstrap_analysis {
Estimate<double> mean;
Estimate<double> standard_deviation;
double outlier_variance;
};
bootstrap_analysis analyse_samples(double confidence_level, unsigned int n_resamples, std::vector<double>::iterator first, std::vector<double>::iterator last);
bootstrap_analysis analyse_samples(double confidence_level,
unsigned int n_resamples,
std::vector<double>::iterator first,
std::vector<double>::iterator last);
} // namespace Detail
} // namespace Benchmark
} // namespace Catch

View File

@@ -54,6 +54,7 @@
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_android_logwrite.hpp>
#include <catch2/internal/catch_config_counter.hpp>
#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_config_uncaught_exceptions.hpp>
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_console_colour.hpp>
@@ -70,6 +71,7 @@
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/internal/catch_floating_point_helpers.hpp>
#include <catch2/internal/catch_getenv.hpp>
#include <catch2/internal/catch_is_permutation.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
@@ -85,6 +87,7 @@
#include <catch2/internal/catch_platform.hpp>
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
#include <catch2/internal/catch_preprocessor_internal_stringify.hpp>
#include <catch2/internal/catch_preprocessor_remove_parens.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp>
@@ -110,6 +113,7 @@
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_test_macro_impl.hpp>
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_to_string.hpp>

View File

@@ -7,6 +7,7 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
namespace Catch {
@@ -26,9 +27,9 @@ namespace Catch {
return reconstructedExpression;
}
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData&& data )
: m_info( info ),
m_resultData( data )
m_resultData( CATCH_MOVE(data) )
{}
// Result was a success
@@ -67,16 +68,15 @@ namespace Catch {
}
std::string AssertionResult::getExpressionInMacro() const {
std::string expr;
if( m_info.macroName.empty() )
expr = static_cast<std::string>(m_info.capturedExpression);
else {
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName;
expr += "( ";
expr += m_info.capturedExpression;
expr += " )";
if ( m_info.macroName.empty() ) {
return static_cast<std::string>( m_info.capturedExpression );
}
std::string expr;
expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
expr += m_info.macroName;
expr += "( ";
expr += m_info.capturedExpression;
expr += " )";
return expr;
}

View File

@@ -35,7 +35,7 @@ namespace Catch {
class AssertionResult {
public:
AssertionResult() = delete;
AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
AssertionResult( AssertionInfo const& info, AssertionResultData&& data );
bool isOk() const;
bool succeeded() const;

View File

@@ -105,7 +105,7 @@ namespace Catch {
elem = trim(elem);
}
// Insert the default reporter if user hasn't asked for a specfic one
// Insert the default reporter if user hasn't asked for a specific one
if ( m_data.reporterSpecifications.empty() ) {
m_data.reporterSpecifications.push_back( {
#if defined( CATCH_CONFIG_DEFAULT_REPORTER )

View File

@@ -19,8 +19,8 @@ namespace Catch {
////////////////////////////////////////////////////////////////////////////
ScopedMessage::ScopedMessage( MessageBuilder const& builder ):
m_info( builder.m_info ) {
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
}
@@ -37,7 +37,11 @@ namespace Catch {
}
Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
Capturer::Capturer( StringRef macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names ):
m_resultCapture( getResultCapture() ) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;

View File

@@ -12,7 +12,6 @@
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stream_end_stop.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/catch_tostring.hpp>
#include <string>
@@ -21,6 +20,7 @@
namespace Catch {
struct SourceLineInfo;
class IResultCapture;
struct MessageStream {
@@ -39,11 +39,10 @@ namespace Catch {
ResultWas::OfType type ):
m_info(macroName, lineInfo, type) {}
template<typename T>
MessageBuilder& operator << ( T const& value ) {
MessageBuilder&& operator << ( T const& value ) && {
m_stream << value;
return *this;
return CATCH_MOVE(*this);
}
MessageInfo m_info;
@@ -51,7 +50,7 @@ namespace Catch {
class ScopedMessage {
public:
explicit ScopedMessage( MessageBuilder const& builder );
explicit ScopedMessage( MessageBuilder&& builder );
ScopedMessage( ScopedMessage& duplicate ) = delete;
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
@@ -62,7 +61,7 @@ namespace Catch {
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture = getResultCapture();
IResultCapture& m_resultCapture;
size_t m_captured = 0;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
@@ -98,7 +97,10 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
Catch::Capturer varName( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr ); \
varName.captureValues( 0, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////

View File

@@ -20,6 +20,9 @@
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <exception>
namespace Catch {
@@ -31,7 +34,7 @@ namespace Catch {
public: // IRegistryHub
RegistryHub() = default;
IReporterRegistry const& getReporterRegistry() const override {
ReporterRegistry const& getReporterRegistry() const override {
return m_reporterRegistry;
}
ITestCaseRegistry const& getTestCaseRegistry() const override {

View File

@@ -13,13 +13,13 @@
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/catch_test_spec.hpp>
#include <catch2/catch_version.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_sharding.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_windows_h_proxy.hpp>
#include <catch2/reporters/catch_reporter_multi.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
@@ -27,6 +27,7 @@
#include <algorithm>
#include <cassert>
#include <exception>
#include <iomanip>
#include <set>

View File

@@ -139,12 +139,20 @@ namespace Catch {
for (size_t idx = 0; idx < originalTags.size(); ++idx) {
auto c = originalTags[idx];
if (c == '[') {
assert(!inTag);
CATCH_ENFORCE(
!inTag,
"Found '[' inside a tag while registering test case '"
<< _nameAndTags.name << "' at " << _lineInfo );
inTag = true;
tagStart = idx;
}
if (c == ']') {
assert(inTag);
CATCH_ENFORCE(
inTag,
"Found unmatched ']' while registering test case '"
<< _nameAndTags.name << "' at " << _lineInfo );
inTag = false;
tagEnd = idx;
assert(tagStart < tagEnd);
@@ -153,7 +161,11 @@ namespace Catch {
// it over to backing storage and actually reference the
// backing storage in the saved tags
StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
CATCH_ENFORCE( !tagStr.empty(),
"Found an empty tag while registering test case '"
<< _nameAndTags.name << "' at "
<< _lineInfo );
enforceNotReservedTag(tagStr, lineInfo);
properties |= parseSpecialTag(tagStr);
// When copying a tag to the backing storage, we need to
@@ -167,8 +179,12 @@ namespace Catch {
// the tags.
internalAppendTag(tagStr);
}
(void)inTag; // Silence "set-but-unused" warning in release mode.
}
CATCH_ENFORCE( !inTag,
"Found an unclosed tag while registering test case '"
<< _nameAndTags.name << "' at " << _lineInfo );
// Add [.] if relevant
if (isHidden()) {
internalAppendTag("."_sr);

View File

@@ -6,6 +6,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_test_spec.hpp>
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/catch_test_case_info.hpp>
@@ -106,16 +108,18 @@ namespace Catch {
return std::any_of( m_filters.begin(), m_filters.end(), [&]( Filter const& f ){ return f.matches( testCase ); } );
}
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const
{
Matches matches( m_filters.size() );
std::transform( m_filters.begin(), m_filters.end(), matches.begin(), [&]( Filter const& filter ){
TestSpec::Matches TestSpec::matchesByFilter( std::vector<TestCaseHandle> const& testCases, IConfig const& config ) const {
Matches matches;
matches.reserve( m_filters.size() );
for ( auto const& filter : m_filters ) {
std::vector<TestCaseHandle const*> currentMatches;
for( auto const& test : testCases )
if( isThrowSafe( test, config ) && filter.matches( test.getTestCaseInfo() ) )
for ( auto const& test : testCases )
if ( isThrowSafe( test, config ) &&
filter.matches( test.getTestCaseInfo() ) )
currentMatches.emplace_back( &test );
return FilterMatch{ extractFilterName(filter), currentMatches };
} );
matches.push_back(
FilterMatch{ extractFilterName( filter ), currentMatches } );
}
return matches;
}

View File

@@ -116,7 +116,6 @@ namespace Catch {
} // namespace Detail
// If we decide for C++14, change these to enable_if_ts
template <typename T, typename = void>
struct StringMaker {
template <typename Fake = T>

View File

@@ -0,0 +1,20 @@
// 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/catch_translate_exception.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
namespace Catch {
namespace Detail {
void registerTranslatorImpl(
Detail::unique_ptr<IExceptionTranslator>&& translator ) {
getMutableRegistryHub().registerTranslator(
CATCH_MOVE( translator ) );
}
} // namespace Detail
} // namespace Catch

View File

@@ -15,6 +15,10 @@
#include <exception>
namespace Catch {
namespace Detail {
void registerTranslatorImpl(
Detail::unique_ptr<IExceptionTranslator>&& translator );
}
class ExceptionTranslatorRegistrar {
template<typename T>
@@ -48,9 +52,9 @@ namespace Catch {
public:
template<typename T>
ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
getMutableRegistryHub().registerTranslator(
Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
);
Detail::registerTranslatorImpl(
Detail::make_unique<ExceptionTranslator<T>>(
translateFunction ) );
}
};

View File

@@ -169,9 +169,18 @@
#endif
#cmakedefine CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
#if defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
# error Cannot force STATIC_ANALYSIS_SUPPORT to both ON and OFF
#endif
// ------
// Simple toggle defines
// their value is never used and they cannot be overriden
// their value is never used and they cannot be overridden
// ------

View File

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

View File

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

View File

@@ -14,7 +14,6 @@
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_unique_name.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
#include <vector>
#include <tuple>
@@ -232,16 +231,19 @@ namespace Detail {
} // namespace Generators
} // namespace Catch
#define CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL( ... ) #__VA_ARGS__##_catch_sr
#define CATCH_INTERNAL_GENERATOR_STRINGIZE(...) CATCH_INTERNAL_GENERATOR_STRINGIZE_IMPL(__VA_ARGS__)
#define GENERATE( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_COPY( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)
#define GENERATE_REF( ... ) \
Catch::Generators::generate( INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
Catch::Generators::generate( CATCH_INTERNAL_GENERATOR_STRINGIZE(INTERNAL_CATCH_UNIQUE_NAME(generator)), \
CATCH_INTERNAL_LINEINFO, \
[&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } ) //NOLINT(google-build-using-namespace)

View File

@@ -30,8 +30,8 @@
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
#include <catch2/interfaces/catch_interfaces_test_invoker.hpp>
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#endif // CATCH_INTERFACES_ALL_HPP_INCLUDED

View File

@@ -14,6 +14,7 @@
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/benchmark/detail/catch_benchmark_stats_fwd.hpp>
namespace Catch {
@@ -31,8 +32,6 @@ namespace Catch {
class IGeneratorTracker;
struct BenchmarkInfo;
template <typename Duration = std::chrono::duration<double, std::nano>>
struct BenchmarkStats;
namespace Generators {
class GeneratorUntypedBase;
@@ -44,10 +43,12 @@ namespace Catch {
public:
virtual ~IResultCapture();
virtual bool sectionStarted( SectionInfo const& sectionInfo,
Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
virtual void notifyAssertionStarted( AssertionInfo const& info ) = 0;
virtual bool sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions ) = 0;
virtual void sectionEnded( SectionEndInfo&& endInfo ) = 0;
virtual void sectionEndedEarly( SectionEndInfo&& endInfo ) = 0;
virtual IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName,
@@ -65,7 +66,7 @@ namespace Catch {
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0;
@@ -83,7 +84,7 @@ namespace Catch {
AssertionReaction& reaction ) = 0;
virtual void handleUnexpectedInflightException
( AssertionInfo const& info,
std::string const& message,
std::string&& message,
AssertionReaction& reaction ) = 0;
virtual void handleIncomplete
( AssertionInfo const& info ) = 0;

View File

@@ -8,7 +8,6 @@
#ifndef CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
#define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <string>

View File

@@ -19,7 +19,7 @@ namespace Catch {
class ITestCaseRegistry;
class IExceptionTranslatorRegistry;
class IExceptionTranslator;
class IReporterRegistry;
class ReporterRegistry;
class IReporterFactory;
class ITagAliasRegistry;
class ITestInvoker;
@@ -35,7 +35,7 @@ namespace Catch {
public:
virtual ~IRegistryHub(); // = default
virtual IReporterRegistry const& getReporterRegistry() const = 0;
virtual ReporterRegistry const& getReporterRegistry() const = 0;
virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;

View File

@@ -54,24 +54,21 @@ namespace Catch {
infoMessages( _infoMessages ),
totals( _totals )
{
assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
if( assertionResult.hasMessage() ) {
// Copy message into messages list.
// !TBD This should have been done earlier, somewhere
MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
builder << assertionResult.getMessage();
builder.m_info.message = builder.m_stream.str();
builder.m_info.message = static_cast<std::string>(assertionResult.getMessage());
infoMessages.push_back( builder.m_info );
infoMessages.push_back( CATCH_MOVE(builder.m_info) );
}
}
SectionStats::SectionStats( SectionInfo const& _sectionInfo,
SectionStats::SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions,
double _durationInSeconds,
bool _missingAssertions )
: sectionInfo( _sectionInfo ),
: sectionInfo( CATCH_MOVE(_sectionInfo) ),
assertions( _assertions ),
durationInSeconds( _durationInSeconds ),
missingAssertions( _missingAssertions )
@@ -80,13 +77,13 @@ namespace Catch {
TestCaseStats::TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals,
std::string const& _stdOut,
std::string const& _stdErr,
std::string&& _stdOut,
std::string&& _stdErr,
bool _aborting )
: testInfo( &_testInfo ),
totals( _totals ),
stdOut( _stdOut ),
stdErr( _stdErr ),
stdOut( CATCH_MOVE(_stdOut) ),
stdErr( CATCH_MOVE(_stdErr) ),
aborting( _aborting )
{}

View File

@@ -13,11 +13,10 @@
#include <catch2/catch_assertion_result.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/benchmark/catch_estimate.hpp>
#include <catch2/benchmark/catch_outlier_classification.hpp>
#include <catch2/benchmark/detail/catch_benchmark_stats.hpp>
#include <map>
#include <string>
@@ -57,11 +56,6 @@ namespace Catch {
std::map<std::string, std::string> m_customOptions;
};
struct TestRunInfo {
constexpr TestRunInfo(StringRef _name) : name(_name) {}
StringRef name;
};
struct AssertionStats {
AssertionStats( AssertionResult const& _assertionResult,
std::vector<MessageInfo> const& _infoMessages,
@@ -78,7 +72,7 @@ namespace Catch {
};
struct SectionStats {
SectionStats( SectionInfo const& _sectionInfo,
SectionStats( SectionInfo&& _sectionInfo,
Counts const& _assertions,
double _durationInSeconds,
bool _missingAssertions );
@@ -92,8 +86,8 @@ namespace Catch {
struct TestCaseStats {
TestCaseStats( TestCaseInfo const& _testInfo,
Totals const& _totals,
std::string const& _stdOut,
std::string const& _stdErr,
std::string&& _stdOut,
std::string&& _stdErr,
bool _aborting );
TestCaseInfo const * testInfo;
@@ -113,45 +107,6 @@ namespace Catch {
bool aborting;
};
struct BenchmarkInfo {
std::string name;
double estimatedDuration;
int iterations;
unsigned int samples;
unsigned int resamples;
double clockResolution;
double clockCost;
};
template <class Duration>
struct BenchmarkStats {
BenchmarkInfo info;
std::vector<Duration> samples;
Benchmark::Estimate<Duration> mean;
Benchmark::Estimate<Duration> standardDeviation;
Benchmark::OutlierClassification outliers;
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,
};
}
};
//! By setting up its preferences, a reporter can modify Catch2's behaviour
//! in some regards, e.g. it can request Catch2 to capture writes to
//! stdout/stderr during test execution, and pass them to the reporter.
@@ -250,7 +205,7 @@ namespace Catch {
*/
virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
//! Called if a fatal error (signal/structured exception) occured
//! Called if a fatal error (signal/structured exception) occurred
virtual void fatalErrorEncountered( StringRef error ) = 0;
//! Writes out information about provided reporters using reporter-specific format

View File

@@ -1,13 +0,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
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
namespace Catch {
IReporterRegistry::~IReporterRegistry() = default;
}

View File

@@ -1,42 +0,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
#ifndef CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <string>
#include <vector>
#include <map>
namespace Catch {
class IConfig;
class IEventListener;
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
class IReporterFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
struct ReporterConfig;
class EventListenerFactory;
class IReporterRegistry {
public:
using FactoryMap = std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>;
using Listeners = std::vector<Detail::unique_ptr<EventListenerFactory>>;
virtual ~IReporterRegistry(); // = default
virtual IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const = 0;
virtual FactoryMap const& getFactories() const = 0;
virtual Listeners const& getListeners() const = 0;
};
} // end namespace Catch
#endif // CATCH_INTERFACES_REPORTER_REGISTRY_HPP_INCLUDED

View File

@@ -0,0 +1,21 @@
// 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_INTERFACES_TEST_INVOKER_HPP_INCLUDED
#define CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED
namespace Catch {
class ITestInvoker {
public:
virtual void invoke() const = 0;
virtual ~ITestInvoker(); // = default
};
} // namespace Catch
#endif // CATCH_INTERFACES_TEST_INVOKER_HPP_INCLUDED

View File

@@ -9,6 +9,5 @@
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
namespace Catch {
ITestInvoker::~ITestInvoker() = default;
ITestCaseRegistry::~ITestCaseRegistry() = default;
}

View File

@@ -12,15 +12,7 @@
namespace Catch {
class TestSpec;
struct TestCaseInfo;
class ITestInvoker {
public:
virtual void invoke () const = 0;
virtual ~ITestInvoker(); // = default
};
class TestCaseHandle;
class IConfig;
@@ -33,11 +25,6 @@ namespace Catch {
virtual std::vector<TestCaseHandle> const& getAllTestsSorted( IConfig const& config ) const = 0;
};
bool isThrowSafe( TestCaseHandle const& testCase, IConfig const& config );
bool matchTest( TestCaseHandle const& testCase, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> filterTests( std::vector<TestCaseHandle> const& testCases, TestSpec const& testSpec, IConfig const& config );
std::vector<TestCaseHandle> const& getAllTestCasesSorted( IConfig const& config );
}
#endif // CATCH_INTERFACES_TESTCASE_HPP_INCLUDED

View File

@@ -12,7 +12,6 @@
#include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
namespace Catch {
@@ -24,7 +23,9 @@ namespace Catch {
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
{}
{
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
}
void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
@@ -38,7 +39,7 @@ namespace Catch {
}
void AssertionHandler::complete() {
setCompleted();
m_completed = true;
if( m_reaction.shouldDebugBreak ) {
// If you find your debugger stopping you here then go one level up on the
@@ -51,16 +52,9 @@ namespace Catch {
throw_test_failure_exception();
}
if ( m_reaction.shouldSkip ) {
#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS )
throw Catch::TestSkipException();
#else
CATCH_ERROR( "Explicitly skipping tests during runtime requires exceptions" );
#endif
throw_test_skip_exception();
}
}
void AssertionHandler::setCompleted() {
m_completed = true;
}
void AssertionHandler::handleUnexpectedInflightException() {
m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );

View File

@@ -11,14 +11,11 @@
#include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <string>
namespace Catch {
class IResultCapture;
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
@@ -59,7 +56,6 @@ namespace Catch {
void handleUnexpectedInflightException();
void complete();
void setCompleted();
// query
auto allowThrows() const -> bool;

View File

@@ -10,7 +10,7 @@
#include <catch2/catch_config.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_console_colour.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp>
@@ -144,7 +144,7 @@ namespace Catch {
auto const& reporterSpec = *parsed;
IReporterRegistry::FactoryMap const& factories =
auto const& factories =
getRegistryHub().getReporterRegistry().getFactories();
auto result = factories.find( reporterSpec.name() );

View File

@@ -41,7 +41,7 @@
// Only GCC compiler should be used in this block, so other compilers trying to
// mask themselves as GCC should be ignored.
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" )
@@ -50,16 +50,28 @@
# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT \
_Pragma( "GCC diagnostic ignored \"-Wunused-result\"" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wunused-variable\"" )
# define CATCH_INTERNAL_SUPPRESS_USELESS_CAST_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wuseless-cast\"" )
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
_Pragma( "GCC diagnostic ignored \"-Wshadow\"" )
# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__)
#endif
#if defined(__NVCOMPILER)
# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "diag push" )
# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "diag pop" )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS _Pragma( "diag_suppress declared_but_not_referenced" )
#endif
#if defined(__CUDACC__) && !defined(__clang__)
# ifdef __NVCC_DIAG_PRAGMA_SUPPORT__
// New pragmas introduced in CUDA 11.5+
@@ -122,6 +134,9 @@
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wcomma\"" )
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wshadow\"" )
#endif // __clang__
@@ -359,6 +374,9 @@
#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_RESULT
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS
#endif
@@ -368,6 +386,16 @@
#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
#endif
#if !defined( CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS )
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if !defined( CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS )
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
#endif
#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS )
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS
#endif
// The goal of this macro is to avoid evaluation of the arguments, but
// still have the compiler warn on problems inside...
@@ -381,13 +409,6 @@
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
#endif
#if !defined(CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS)
# define CATCH_INTERNAL_SUPPRESS_COMMA_WARNINGS
#endif
#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
#define CATCH_TRY if ((true))

View File

@@ -18,6 +18,8 @@
#ifndef CATCH_CONFIG_COUNTER_HPP_INCLUDED
#define CATCH_CONFIG_COUNTER_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
#define CATCH_INTERNAL_CONFIG_COUNTER
#endif

View File

@@ -0,0 +1,34 @@
// 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 STATIC_ANALYSIS_SUPPORT configuration option
*
* Some of Catch2's macros can be defined differently to work better with
* static analysis tools, like clang-tidy or coverity.
* Currently the main use case is to show that `SECTION`s are executed
* exclusively, and not all in one run of a `TEST_CASE`.
*/
#ifndef CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
#define CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
#if defined(__clang_analyzer__) || defined(__COVERITY__)
#define CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT
#endif
#if defined( CATCH_INTERNAL_CONFIG_STATIC_ANALYSIS_SUPPORT ) && \
!defined( CATCH_CONFIG_NO_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT ) && \
!defined( CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT )
# define CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT
#endif
#endif // CATCH_CONFIG_STATIC_ANALYSIS_SUPPORT_HPP_INCLUDED

View File

@@ -17,6 +17,8 @@
#ifndef CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#define CATCH_CONFIG_UNCAUGHT_EXCEPTIONS_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
#if defined(_MSC_VER)
# if _MSC_VER >= 1900 // Visual Studio 2015 or newer
# define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS

View File

@@ -17,6 +17,8 @@
#ifndef CATCH_CONFIG_WCHAR_HPP_INCLUDED
#define CATCH_CONFIG_WCHAR_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
// We assume that WCHAR should be enabled by default, and only disabled
// for a shortlist (so far only DJGPP) of compilers.

View File

@@ -85,7 +85,7 @@ namespace Catch {
namespace {
//! A do-nothing implementation of colour, used as fallback for unknown
//! platforms, and when the user asks to deactivate all colours.
class NoColourImpl : public ColourImpl {
class NoColourImpl final : public ColourImpl {
public:
NoColourImpl( IStream* stream ): ColourImpl( stream ) {}
@@ -103,7 +103,7 @@ namespace Catch {
namespace Catch {
namespace {
class Win32ColourImpl : public ColourImpl {
class Win32ColourImpl final : public ColourImpl {
public:
Win32ColourImpl(IStream* stream):
ColourImpl(stream) {
@@ -169,7 +169,7 @@ namespace {
namespace Catch {
namespace {
class ANSIColourImpl : public ColourImpl {
class ANSIColourImpl final : public ColourImpl {
public:
ANSIColourImpl( IStream* stream ): ColourImpl( stream ) {}

View File

@@ -11,49 +11,27 @@
namespace Catch {
class Context : public IMutableContext, private Detail::NonCopyable {
Context* Context::currentContext = nullptr;
public: // IContext
IResultCapture* getResultCapture() override {
return m_resultCapture;
}
IConfig const* getConfig() const override {
return m_config;
}
~Context() override;
public: // IMutableContext
void setResultCapture( IResultCapture* resultCapture ) override {
m_resultCapture = resultCapture;
}
void setConfig( IConfig const* config ) override {
m_config = config;
}
friend IMutableContext& getCurrentMutableContext();
private:
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
};
IMutableContext *IMutableContext::currentContext = nullptr;
void IMutableContext::createContext()
{
void cleanUpContext() {
delete Context::currentContext;
Context::currentContext = nullptr;
}
void Context::createContext() {
currentContext = new Context();
}
void cleanUpContext() {
delete IMutableContext::currentContext;
IMutableContext::currentContext = nullptr;
Context& getCurrentMutableContext() {
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
}
IContext::~IContext() = default;
IMutableContext::~IMutableContext() = default;
Context::~Context() = default;
void Context::setResultCapture( IResultCapture* resultCapture ) {
m_resultCapture = resultCapture;
}
void Context::setConfig( IConfig const* config ) { m_config = config; }
SimplePcg32& sharedRng() {
static SimplePcg32 s_rng;

View File

@@ -15,38 +15,31 @@ namespace Catch {
class IResultCapture;
class IConfig;
class IContext {
public:
virtual ~IContext(); // = default
class Context {
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
virtual IResultCapture* getResultCapture() = 0;
virtual IConfig const* getConfig() const = 0;
};
class IMutableContext : public IContext {
public:
~IMutableContext() override; // = default
virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
virtual void setConfig( IConfig const* config ) = 0;
private:
CATCH_EXPORT static IMutableContext* currentContext;
friend IMutableContext& getCurrentMutableContext();
friend void cleanUpContext();
CATCH_EXPORT static Context* currentContext;
friend Context& getCurrentMutableContext();
friend Context const& getCurrentContext();
static void createContext();
friend void cleanUpContext();
public:
IResultCapture* getResultCapture() const { return m_resultCapture; }
IConfig const* getConfig() const { return m_config; }
void setResultCapture( IResultCapture* resultCapture );
void setConfig( IConfig const* config );
};
inline IMutableContext& getCurrentMutableContext()
{
if( !IMutableContext::currentContext )
IMutableContext::createContext();
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *IMutableContext::currentContext;
}
Context& getCurrentMutableContext();
inline IContext& getCurrentContext()
{
return getCurrentMutableContext();
inline Context const& getCurrentContext() {
// We duplicate the logic from `getCurrentMutableContext` here,
// to avoid paying the call overhead in debug mode.
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
}
void cleanUpContext();

View File

@@ -11,8 +11,24 @@
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <exception>
namespace Catch {
namespace {
static std::string tryTranslators(
std::vector<
Detail::unique_ptr<IExceptionTranslator const>> const& translators ) {
if ( translators.empty() ) {
std::rethrow_exception( std::current_exception() );
} else {
return translators[0]->translate( translators.begin() + 1,
translators.end() );
}
}
}
ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
}
@@ -37,7 +53,7 @@ namespace Catch {
// First we try user-registered translators. If none of them can
// handle the exception, it will be rethrown handled by our defaults.
try {
return tryTranslators();
return tryTranslators(m_translators);
}
// To avoid having to handle TFE explicitly everywhere, we just
// rethrow it so that it goes back up the caller.
@@ -61,23 +77,10 @@ namespace Catch {
}
}
std::string ExceptionTranslatorRegistry::tryTranslators() const {
if (m_translators.empty()) {
std::rethrow_exception(std::current_exception());
} else {
return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
}
}
#else // ^^ Exceptions are enabled // Exceptions are disabled vv
std::string ExceptionTranslatorRegistry::translateActiveException() const {
CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
std::string ExceptionTranslatorRegistry::tryTranslators() const {
CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
}
#endif
}

View File

@@ -21,7 +21,6 @@ namespace Catch {
~ExceptionTranslatorRegistry() override;
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
std::string translateActiveException() const override;
std::string tryTranslators() const;
private:
ExceptionTranslators m_translators;

View File

@@ -0,0 +1,138 @@
// 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_IS_PERMUTATION_HPP_INCLUDED
#define CATCH_IS_PERMUTATION_HPP_INCLUDED
#include <algorithm>
#include <iterator>
namespace Catch {
namespace Detail {
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
ForwardIter find_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { break; }
++start;
}
return start;
}
template <typename ForwardIter,
typename Sentinel,
typename T,
typename Comparator>
std::ptrdiff_t count_sentinel( ForwardIter start,
Sentinel sentinel,
T const& value,
Comparator cmp ) {
std::ptrdiff_t count = 0;
while ( start != sentinel ) {
if ( cmp( *start, value ) ) { ++count; }
++start;
}
return count;
}
template <typename ForwardIter, typename Sentinel>
std::enable_if_t<!std::is_same<ForwardIter, Sentinel>::value,
std::ptrdiff_t>
sentinel_distance( ForwardIter iter, const Sentinel sentinel ) {
std::ptrdiff_t dist = 0;
while ( iter != sentinel ) {
++iter;
++dist;
}
return dist;
}
template <typename ForwardIter>
std::ptrdiff_t sentinel_distance( ForwardIter first,
ForwardIter last ) {
return std::distance( first, last );
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool check_element_counts( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
auto cursor = first_1;
while ( cursor != end_1 ) {
if ( find_sentinel( first_1, cursor, *cursor, cmp ) ==
cursor ) {
// we haven't checked this element yet
const auto count_in_range_2 =
count_sentinel( first_2, end_2, *cursor, cmp );
// Not a single instance in 2nd range, so it cannot be a
// permutation of 1st range
if ( count_in_range_2 == 0 ) { return false; }
const auto count_in_range_1 =
count_sentinel( cursor, end_1, *cursor, cmp );
if ( count_in_range_1 != count_in_range_2 ) {
return false;
}
}
++cursor;
}
return true;
}
template <typename ForwardIter1,
typename Sentinel1,
typename ForwardIter2,
typename Sentinel2,
typename Comparator>
bool is_permutation( ForwardIter1 first_1,
const Sentinel1 end_1,
ForwardIter2 first_2,
const Sentinel2 end_2,
Comparator cmp ) {
// TODO: no optimization for stronger iterators, because we would also have to constrain on sentinel vs not sentinel types
// TODO: Comparator has to be "both sides", e.g. a == b => b == a
// This skips shared prefix of the two ranges
while (first_1 != end_1 && first_2 != end_2 && cmp(*first_1, *first_2)) {
++first_1;
++first_2;
}
// We need to handle case where at least one of the ranges has no more elements
if (first_1 == end_1 || first_2 == end_2) {
return first_1 == end_1 && first_2 == end_2;
}
// pair counting is n**2, so we pay linear walk to compare the sizes first
auto dist_1 = sentinel_distance( first_1, end_1 );
auto dist_2 = sentinel_distance( first_2, end_2 );
if (dist_1 != dist_2) { return false; }
// Since we do not try to handle stronger iterators pair (e.g.
// bidir) optimally, the only thing left to do is to check counts in
// the remaining ranges.
return check_element_counts( first_1, end_1, first_2, end_2, cmp );
}
} // namespace Detail
} // namespace Catch
#endif // CATCH_IS_PERMUTATION_HPP_INCLUDED

View File

@@ -24,7 +24,7 @@ namespace Catch {
namespace Detail {
namespace {
template<typename WriterF, std::size_t bufferSize=256>
class StreamBufImpl : public std::streambuf {
class StreamBufImpl final : public std::streambuf {
char data[bufferSize];
WriterF m_writer;
@@ -72,7 +72,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
class FileStream : public IStream {
class FileStream final : public IStream {
std::ofstream m_ofs;
public:
FileStream( std::string const& filename ) {
@@ -89,7 +89,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
class CoutStream : public IStream {
class CoutStream final : public IStream {
std::ostream m_os;
public:
// Store the streambuf from cout up-front because
@@ -118,7 +118,7 @@ namespace Detail {
///////////////////////////////////////////////////////////////////////////
class DebugOutStream : public IStream {
class DebugOutStream final : public IStream {
Detail::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
std::ostream m_os;
public:

View File

@@ -9,9 +9,9 @@
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_testcase.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_test_case_registry_impl.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
@@ -54,7 +54,7 @@ namespace Catch {
void listReporters(IEventListener& reporter) {
std::vector<ReporterDescription> descriptions;
IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
auto const& factories = getRegistryHub().getReporterRegistry().getFactories();
descriptions.reserve(factories.size());
for (auto const& fac : factories) {
descriptions.push_back({ fac.first, fac.second->getDescription() });

View File

@@ -10,7 +10,7 @@
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <string>

View File

@@ -8,6 +8,8 @@
#ifndef CATCH_OPTIONAL_HPP_INCLUDED
#define CATCH_OPTIONAL_HPP_INCLUDED
#include <catch2/internal/catch_move_and_forward.hpp>
#include <cassert>
namespace Catch {
@@ -16,35 +18,50 @@ namespace Catch {
template<typename T>
class Optional {
public:
Optional() : nullableValue( nullptr ) {}
Optional( T const& _value )
: nullableValue( new( storage ) T( _value ) )
{}
Optional( Optional const& _other )
: nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
{}
Optional(): nullableValue( nullptr ) {}
~Optional() { reset(); }
~Optional() {
Optional( T const& _value ):
nullableValue( new ( storage ) T( _value ) ) {}
Optional( T&& _value ):
nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {}
Optional& operator=( T const& _value ) {
reset();
nullableValue = new ( storage ) T( _value );
return *this;
}
Optional& operator=( T&& _value ) {
reset();
nullableValue = new ( storage ) T( CATCH_MOVE( _value ) );
return *this;
}
Optional& operator= ( Optional const& _other ) {
if( &_other != this ) {
Optional( Optional const& _other ):
nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {}
Optional( Optional&& _other ):
nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) )
: nullptr ) {}
Optional& operator=( Optional const& _other ) {
if ( &_other != this ) {
reset();
if( _other )
nullableValue = new( storage ) T( *_other );
if ( _other ) { nullableValue = new ( storage ) T( *_other ); }
}
return *this;
}
Optional& operator = ( T const& _value ) {
reset();
nullableValue = new( storage ) T( _value );
Optional& operator=( Optional&& _other ) {
if ( &_other != this ) {
reset();
if ( _other ) {
nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) );
}
}
return *this;
}
void reset() {
if( nullableValue )
nullableValue->~T();
if ( nullableValue ) { nullableValue->~T(); }
nullableValue = nullptr;
}
@@ -91,7 +108,7 @@ namespace Catch {
}
private:
T *nullableValue;
T* nullableValue;
alignas(alignof(T)) char storage[sizeof(T)];
};

View File

@@ -0,0 +1,19 @@
// 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_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
#define CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED
#include <catch2/catch_user_config.hpp>
#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
#define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__##_catch_sr
#else
#define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"_catch_sr
#endif
#endif // CATCH_PREPROCESSOR_INTERNAL_STRINGIFY_HPP_INCLUDED

View File

@@ -5,61 +5,84 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/reporters/catch_reporter_automake.hpp>
#include <catch2/reporters/catch_reporter_compact.hpp>
#include <catch2/reporters/catch_reporter_console.hpp>
#include <catch2/reporters/catch_reporter_junit.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/reporters/catch_reporter_sonarqube.hpp>
#include <catch2/reporters/catch_reporter_tap.hpp>
#include <catch2/reporters/catch_reporter_teamcity.hpp>
#include <catch2/reporters/catch_reporter_xml.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_enforce.hpp>
namespace Catch {
struct ReporterRegistry::ReporterRegistryImpl {
std::vector<Detail::unique_ptr<EventListenerFactory>> listeners;
std::map<std::string, IReporterFactoryPtr, Detail::CaseInsensitiveLess>
factories;
};
ReporterRegistry::ReporterRegistry() {
ReporterRegistry::ReporterRegistry():
m_impl( Detail::make_unique<ReporterRegistryImpl>() ) {
// Because it is impossible to move out of initializer list,
// we have to add the elements manually
m_factories["Automake"] = Detail::make_unique<ReporterFactory<AutomakeReporter>>();
m_factories["compact"] = Detail::make_unique<ReporterFactory<CompactReporter>>();
m_factories["console"] = Detail::make_unique<ReporterFactory<ConsoleReporter>>();
m_factories["JUnit"] = Detail::make_unique<ReporterFactory<JunitReporter>>();
m_factories["SonarQube"] = Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
m_factories["TAP"] = Detail::make_unique<ReporterFactory<TAPReporter>>();
m_factories["TeamCity"] = Detail::make_unique<ReporterFactory<TeamCityReporter>>();
m_factories["XML"] = Detail::make_unique<ReporterFactory<XmlReporter>>();
m_impl->factories["Automake"] =
Detail::make_unique<ReporterFactory<AutomakeReporter>>();
m_impl->factories["compact"] =
Detail::make_unique<ReporterFactory<CompactReporter>>();
m_impl->factories["console"] =
Detail::make_unique<ReporterFactory<ConsoleReporter>>();
m_impl->factories["JUnit"] =
Detail::make_unique<ReporterFactory<JunitReporter>>();
m_impl->factories["SonarQube"] =
Detail::make_unique<ReporterFactory<SonarQubeReporter>>();
m_impl->factories["TAP"] =
Detail::make_unique<ReporterFactory<TAPReporter>>();
m_impl->factories["TeamCity"] =
Detail::make_unique<ReporterFactory<TeamCityReporter>>();
m_impl->factories["XML"] =
Detail::make_unique<ReporterFactory<XmlReporter>>();
}
ReporterRegistry::~ReporterRegistry() = default;
IEventListenerPtr ReporterRegistry::create( std::string const& name, ReporterConfig&& config ) const {
auto it = m_factories.find( name );
if( it == m_factories.end() )
return nullptr;
return it->second->create( CATCH_MOVE(config) );
IEventListenerPtr
ReporterRegistry::create( std::string const& name,
ReporterConfig&& config ) const {
auto it = m_impl->factories.find( name );
if ( it == m_impl->factories.end() ) return nullptr;
return it->second->create( CATCH_MOVE( config ) );
}
void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
void ReporterRegistry::registerReporter( std::string const& name,
IReporterFactoryPtr factory ) {
CATCH_ENFORCE( name.find( "::" ) == name.npos,
"'::' is not allowed in reporter name: '" + name + '\'' );
auto ret = m_factories.emplace(name, CATCH_MOVE(factory));
CATCH_ENFORCE( ret.second, "reporter using '" + name + "' as name was already registered" );
"'::' is not allowed in reporter name: '" + name +
'\'' );
auto ret = m_impl->factories.emplace( name, CATCH_MOVE( factory ) );
CATCH_ENFORCE( ret.second,
"reporter using '" + name +
"' as name was already registered" );
}
void ReporterRegistry::registerListener(
Detail::unique_ptr<EventListenerFactory> factory ) {
m_listeners.push_back( CATCH_MOVE(factory) );
m_impl->listeners.push_back( CATCH_MOVE( factory ) );
}
IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
return m_factories;
}
IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
return m_listeners;
std::map<std::string,
IReporterFactoryPtr,
Detail::CaseInsensitiveLess> const&
ReporterRegistry::getFactories() const {
return m_impl->factories;
}
}
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
ReporterRegistry::getListeners() const {
return m_impl->listeners;
}
} // namespace Catch

View File

@@ -8,31 +8,48 @@
#ifndef CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#define CATCH_REPORTER_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
#include <catch2/internal/catch_case_insensitive_comparisons.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <map>
#include <string>
#include <vector>
namespace Catch {
class ReporterRegistry : public IReporterRegistry {
class IEventListener;
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
class IReporterFactory;
using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
struct ReporterConfig;
class EventListenerFactory;
class ReporterRegistry {
struct ReporterRegistryImpl;
Detail::unique_ptr<ReporterRegistryImpl> m_impl;
public:
ReporterRegistry();
~ReporterRegistry() override; // = default, out of line to allow fwd decl
~ReporterRegistry(); // = default;
IEventListenerPtr create( std::string const& name, ReporterConfig&& config ) const override;
IEventListenerPtr create( std::string const& name,
ReporterConfig&& config ) const;
void registerReporter( std::string const& name, IReporterFactoryPtr factory );
void registerListener( Detail::unique_ptr<EventListenerFactory> factory );
void registerReporter( std::string const& name,
IReporterFactoryPtr factory );
FactoryMap const& getFactories() const override;
Listeners const& getListeners() const override;
void
registerListener( Detail::unique_ptr<EventListenerFactory> factory );
private:
FactoryMap m_factories;
Listeners m_listeners;
std::map<std::string,
IReporterFactoryPtr,
Detail::CaseInsensitiveLess> const&
getFactories() const;
std::vector<Detail::unique_ptr<EventListenerFactory>> const&
getListeners() const;
};
}
} // end namespace Catch
#endif // CATCH_REPORTER_REGISTRY_HPP_INCLUDED

View File

@@ -21,9 +21,9 @@ namespace Catch {
};
kvPair splitKVPair(StringRef kvString) {
auto splitPos = static_cast<size_t>( std::distance(
kvString.begin(),
std::find( kvString.begin(), kvString.end(), '=' ) ) );
auto splitPos = static_cast<size_t>(
std::find( kvString.begin(), kvString.end(), '=' ) -
kvString.begin() );
return { kvString.substr( 0, splitPos ),
kvString.substr( splitPos + 1, kvString.size() ) };

View File

@@ -8,8 +8,9 @@
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/catch_user_config.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_enforce.hpp>
@@ -26,146 +27,152 @@
namespace Catch {
namespace Generators {
struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
GeneratorBasePtr m_generator;
namespace {
struct GeneratorTracker final : TestCaseTracking::TrackerBase,
IGeneratorTracker {
GeneratorBasePtr m_generator;
GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
: TrackerBase( nameAndLocation, ctx, parent )
{}
~GeneratorTracker() override;
GeneratorTracker(
TestCaseTracking::NameAndLocation&& nameAndLocation,
TrackerContext& ctx,
ITracker* parent ):
TrackerBase( CATCH_MOVE( nameAndLocation ), ctx, parent ) {}
~GeneratorTracker() override = default;
static GeneratorTracker* acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
GeneratorTracker* tracker;
static GeneratorTracker*
acquire( TrackerContext& ctx,
TestCaseTracking::NameAndLocationRef const&
nameAndLocation ) {
GeneratorTracker* tracker;
ITracker& currentTracker = ctx.currentTracker();
// Under specific circumstances, the generator we want
// to acquire is also the current tracker. If this is
// the case, we have to avoid looking through current
// tracker's children, and instead return the current
// tracker.
// A case where this check is important is e.g.
// for (int i = 0; i < 5; ++i) {
// int n = GENERATE(1, 2);
// }
//
// without it, the code above creates 5 nested generators.
if ( currentTracker.nameAndLocation() == nameAndLocation ) {
auto thisTracker =
currentTracker.parent()->findChild( nameAndLocation );
assert( thisTracker );
assert( thisTracker->isGeneratorTracker() );
tracker = static_cast<GeneratorTracker*>( thisTracker );
} else if ( ITracker* childTracker =
currentTracker.findChild( nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isGeneratorTracker() );
tracker = static_cast<GeneratorTracker*>( childTracker );
} else {
return nullptr;
ITracker& currentTracker = ctx.currentTracker();
// Under specific circumstances, the generator we want
// to acquire is also the current tracker. If this is
// the case, we have to avoid looking through current
// tracker's children, and instead return the current
// tracker.
// A case where this check is important is e.g.
// for (int i = 0; i < 5; ++i) {
// int n = GENERATE(1, 2);
// }
//
// without it, the code above creates 5 nested generators.
if ( currentTracker.nameAndLocation() == nameAndLocation ) {
auto thisTracker = currentTracker.parent()->findChild(
nameAndLocation );
assert( thisTracker );
assert( thisTracker->isGeneratorTracker() );
tracker = static_cast<GeneratorTracker*>( thisTracker );
} else if ( ITracker* childTracker =
currentTracker.findChild(
nameAndLocation ) ) {
assert( childTracker );
assert( childTracker->isGeneratorTracker() );
tracker =
static_cast<GeneratorTracker*>( childTracker );
} else {
return nullptr;
}
if ( !tracker->isComplete() ) { tracker->open(); }
return tracker;
}
if( !tracker->isComplete() ) {
tracker->open();
// TrackerBase interface
bool isGeneratorTracker() const override { return true; }
auto hasGenerator() const -> bool override {
return !!m_generator;
}
return tracker;
}
// TrackerBase interface
bool isGeneratorTracker() const override { return true; }
auto hasGenerator() const -> bool override {
return !!m_generator;
}
void close() override {
TrackerBase::close();
// If a generator has a child (it is followed by a section)
// and none of its children have started, then we must wait
// until later to start consuming its values.
// This catches cases where `GENERATE` is placed between two
// `SECTION`s.
// **The check for m_children.empty cannot be removed**.
// doing so would break `GENERATE` _not_ followed by `SECTION`s.
const bool should_wait_for_child = [&]() {
// No children -> nobody to wait for
if ( m_children.empty() ) {
return false;
}
// If at least one child started executing, don't wait
if ( std::find_if(
m_children.begin(),
m_children.end(),
[]( TestCaseTracking::ITrackerPtr const& tracker ) {
return tracker->hasStarted();
} ) != m_children.end() ) {
return false;
}
// No children have started. We need to check if they _can_
// start, and thus we should wait for them, or they cannot
// start (due to filters), and we shouldn't wait for them
ITracker* parent = m_parent;
// This is safe: there is always at least one section
// tracker in a test case tracking tree
while ( !parent->isSectionTracker() ) {
parent = parent->parent();
}
assert( parent &&
"Missing root (test case) level section" );
auto const& parentSection =
static_cast<SectionTracker const&>( *parent );
auto const& filters = parentSection.getFilters();
// No filters -> no restrictions on running sections
if ( filters.empty() ) {
return true;
}
for ( auto const& child : m_children ) {
if ( child->isSectionTracker() &&
std::find(
filters.begin(),
filters.end(),
static_cast<SectionTracker const&>( *child )
.trimmedName() ) != filters.end() ) {
return true;
void close() override {
TrackerBase::close();
// If a generator has a child (it is followed by a section)
// and none of its children have started, then we must wait
// until later to start consuming its values.
// This catches cases where `GENERATE` is placed between two
// `SECTION`s.
// **The check for m_children.empty cannot be removed**.
// doing so would break `GENERATE` _not_ followed by
// `SECTION`s.
const bool should_wait_for_child = [&]() {
// No children -> nobody to wait for
if ( m_children.empty() ) { return false; }
// If at least one child started executing, don't wait
if ( std::find_if(
m_children.begin(),
m_children.end(),
[]( TestCaseTracking::ITrackerPtr const&
tracker ) {
return tracker->hasStarted();
} ) != m_children.end() ) {
return false;
}
// No children have started. We need to check if they
// _can_ start, and thus we should wait for them, or
// they cannot start (due to filters), and we shouldn't
// wait for them
ITracker* parent = m_parent;
// This is safe: there is always at least one section
// tracker in a test case tracking tree
while ( !parent->isSectionTracker() ) {
parent = parent->parent();
}
assert( parent &&
"Missing root (test case) level section" );
auto const& parentSection =
static_cast<SectionTracker const&>( *parent );
auto const& filters = parentSection.getFilters();
// No filters -> no restrictions on running sections
if ( filters.empty() ) { return true; }
for ( auto const& child : m_children ) {
if ( child->isSectionTracker() &&
std::find( filters.begin(),
filters.end(),
static_cast<SectionTracker const&>(
*child )
.trimmedName() ) !=
filters.end() ) {
return true;
}
}
return false;
}();
// This check is a bit tricky, because m_generator->next()
// has a side-effect, where it consumes generator's current
// value, but we do not want to invoke the side-effect if
// this generator is still waiting for any child to start.
assert( m_generator && "Tracker without generator" );
if ( should_wait_for_child ||
( m_runState == CompletedSuccessfully &&
m_generator->countedNext() ) ) {
m_children.clear();
m_runState = Executing;
}
return false;
}();
// This check is a bit tricky, because m_generator->next()
// has a side-effect, where it consumes generator's current
// value, but we do not want to invoke the side-effect if
// this generator is still waiting for any child to start.
assert( m_generator && "Tracker without generator" );
if ( should_wait_for_child ||
( m_runState == CompletedSuccessfully &&
m_generator->countedNext() ) ) {
m_children.clear();
m_runState = Executing;
}
}
// IGeneratorTracker interface
auto getGenerator() const -> GeneratorBasePtr const& override {
return m_generator;
}
void setGenerator( GeneratorBasePtr&& generator ) override {
m_generator = CATCH_MOVE( generator );
}
};
GeneratorTracker::~GeneratorTracker() = default;
// IGeneratorTracker interface
auto getGenerator() const -> GeneratorBasePtr const& override {
return m_generator;
}
void setGenerator( GeneratorBasePtr&& generator ) override {
m_generator = CATCH_MOVE( generator );
}
};
} // namespace
}
RunContext::RunContext(IConfig const* _config, IEventListenerPtr&& reporter)
: m_runInfo(_config->name()),
m_context(getCurrentMutableContext()),
m_config(_config),
m_reporter(CATCH_MOVE(reporter)),
m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
{
m_context.setResultCapture(this);
getCurrentMutableContext().setResultCapture( this );
m_reporter->testRunStarting(m_runInfo);
}
@@ -176,13 +183,8 @@ namespace Catch {
Totals RunContext::runTest(TestCaseHandle const& testCase) {
const Totals prevTotals = m_totals;
std::string redirectedCout;
std::string redirectedCerr;
auto const& testInfo = testCase.getTestCaseInfo();
m_reporter->testCaseStarting(testInfo);
m_activeTestCase = &testCase;
@@ -224,9 +226,11 @@ namespace Catch {
seedRng( *m_config );
uint64_t testRuns = 0;
std::string redirectedCout;
std::string redirectedCerr;
do {
m_trackerContext.startCycle();
m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocationRef(testInfo.name, testInfo.lineInfo));
m_reporter->testCasePartialStarting(testInfo, testRuns);
@@ -237,7 +241,7 @@ namespace Catch {
redirectedCerr += oneRunCerr;
const auto singleRunTotals = m_totals.delta(beforeRunTotals);
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, oneRunCout, oneRunCerr, aborting());
auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, CATCH_MOVE(oneRunCout), CATCH_MOVE(oneRunCerr), aborting());
m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
++testRuns;
@@ -252,8 +256,8 @@ namespace Catch {
m_totals.testCases += deltaTotals.testCases;
m_reporter->testCaseEnded(TestCaseStats(testInfo,
deltaTotals,
redirectedCout,
redirectedCerr,
CATCH_MOVE(redirectedCout),
CATCH_MOVE(redirectedCerr),
aborting()));
m_activeTestCase = nullptr;
@@ -263,7 +267,7 @@ namespace Catch {
}
void RunContext::assertionEnded(AssertionResult const & result) {
void RunContext::assertionEnded(AssertionResult&& result) {
if (result.getResultType() == ResultWas::Ok) {
m_totals.assertions.passed++;
m_lastAssertionPassed = true;
@@ -285,24 +289,36 @@ namespace Catch {
m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
if (result.getResultType() != ResultWas::Warning)
if ( result.getResultType() != ResultWas::Warning ) {
m_messageScopes.clear();
}
// Reset working state
resetAssertionInfo();
m_lastResult = result;
m_lastResult = CATCH_MOVE( result );
}
void RunContext::resetAssertionInfo() {
m_lastAssertionInfo.macroName = StringRef();
m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
}
bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
void RunContext::notifyAssertionStarted( AssertionInfo const& info ) {
m_reporter->assertionStarting( info );
}
bool RunContext::sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions ) {
ITracker& sectionTracker =
SectionTracker::acquire( m_trackerContext,
TestCaseTracking::NameAndLocationRef(
sectionName, sectionLineInfo ) );
if (!sectionTracker.isOpen())
return false;
m_activeSections.push_back(&sectionTracker);
SectionInfo sectionInfo( sectionLineInfo, static_cast<std::string>(sectionName) );
m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
m_reporter->sectionStarting(sectionInfo);
@@ -317,8 +333,8 @@ namespace Catch {
using namespace Generators;
GeneratorTracker* tracker = GeneratorTracker::acquire(
m_trackerContext,
TestCaseTracking::NameAndLocation(
static_cast<std::string>( generatorName ), lineInfo ) );
TestCaseTracking::NameAndLocationRef(
generatorName, lineInfo ) );
m_lastAssertionInfo.lineInfo = lineInfo;
return tracker;
}
@@ -335,7 +351,7 @@ namespace Catch {
"Trying to create tracker for a genreator that already has one" );
auto newTracker = Catch::Detail::make_unique<Generators::GeneratorTracker>(
nameAndLoc, m_trackerContext, &currentTracker );
CATCH_MOVE(nameAndLoc), m_trackerContext, &currentTracker );
auto ret = newTracker.get();
currentTracker.addChild( CATCH_MOVE( newTracker ) );
@@ -356,7 +372,7 @@ namespace Catch {
return true;
}
void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
void RunContext::sectionEnded(SectionEndInfo&& endInfo) {
Counts assertions = m_totals.assertions - endInfo.prevAssertions;
bool missingAssertions = testForMissingAssertions(assertions);
@@ -365,19 +381,20 @@ namespace Catch {
m_activeSections.pop_back();
}
m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
m_reporter->sectionEnded(SectionStats(CATCH_MOVE(endInfo.sectionInfo), assertions, endInfo.durationInSeconds, missingAssertions));
m_messages.clear();
m_messageScopes.clear();
}
void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
if (m_unfinishedSections.empty())
void RunContext::sectionEndedEarly(SectionEndInfo&& endInfo) {
if ( m_unfinishedSections.empty() ) {
m_activeSections.back()->fail();
else
} else {
m_activeSections.back()->close();
}
m_activeSections.pop_back();
m_unfinishedSections.push_back(endInfo);
m_unfinishedSections.push_back(CATCH_MOVE(endInfo));
}
void RunContext::benchmarkPreparing( StringRef name ) {
@@ -401,8 +418,8 @@ namespace Catch {
m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
}
void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
m_messageScopes.emplace_back( builder );
void RunContext::emplaceUnscopedMessage( MessageBuilder&& builder ) {
m_messageScopes.emplace_back( CATCH_MOVE(builder) );
}
std::string RunContext::getCurrentTestName() const {
@@ -427,9 +444,9 @@ namespace Catch {
// Instead, fake a result data.
AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
tempResult.message = static_cast<std::string>(message);
AssertionResult result(m_lastAssertionInfo, tempResult);
AssertionResult result(m_lastAssertionInfo, CATCH_MOVE(tempResult));
assertionEnded(result);
assertionEnded(CATCH_MOVE(result) );
handleUnfinishedSections();
@@ -439,7 +456,7 @@ namespace Catch {
Counts assertions;
assertions.failed = 1;
SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, 0, false);
m_reporter->sectionEnded(testCaseSectionStats);
auto const& testInfo = m_activeTestCase->getTestCaseInfo();
@@ -518,7 +535,7 @@ namespace Catch {
m_messages.clear();
m_messageScopes.clear();
SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
SectionStats testCaseSectionStats(CATCH_MOVE(testCaseSection), assertions, duration, missingAssertions);
m_reporter->sectionEnded(testCaseSectionStats);
}
@@ -542,7 +559,7 @@ namespace Catch {
itEnd = m_unfinishedSections.rend();
it != itEnd;
++it)
sectionEnded(*it);
sectionEnded(CATCH_MOVE(*it));
m_unfinishedSections.clear();
}
@@ -551,8 +568,6 @@ namespace Catch {
ITransientExpression const& expr,
AssertionReaction& reaction
) {
m_reporter->assertionStarting( info );
bool negated = isFalseTest( info.resultDisposition );
bool result = expr.getResult() != negated;
@@ -578,10 +593,10 @@ namespace Catch {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( negated ) );
AssertionResult assertionResult{ info, data };
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
assertionEnded( assertionResult );
assertionEnded( CATCH_MOVE(assertionResult) );
}
void RunContext::handleMessage(
@@ -590,15 +605,16 @@ namespace Catch {
StringRef message,
AssertionReaction& reaction
) {
m_reporter->assertionStarting( info );
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
data.message = static_cast<std::string>(message);
AssertionResult assertionResult{ m_lastAssertionInfo, data };
assertionEnded( assertionResult );
if ( !assertionResult.isOk() ) {
AssertionResult assertionResult{ m_lastAssertionInfo,
CATCH_MOVE( data ) };
const auto isOk = assertionResult.isOk();
assertionEnded( CATCH_MOVE(assertionResult) );
if ( !isOk ) {
populateReaction( reaction );
} else if ( resultType == ResultWas::ExplicitSkip ) {
// TODO: Need to handle this explicitly, as ExplicitSkip is
@@ -615,15 +631,15 @@ namespace Catch {
void RunContext::handleUnexpectedInflightException(
AssertionInfo const& info,
std::string const& message,
std::string&& message,
AssertionReaction& reaction
) {
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = message;
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
data.message = CATCH_MOVE(message);
AssertionResult assertionResult{ info, CATCH_MOVE(data) };
assertionEnded( CATCH_MOVE(assertionResult) );
populateReaction( reaction );
}
@@ -635,12 +651,13 @@ namespace Catch {
void RunContext::handleIncomplete(
AssertionInfo const& info
) {
using namespace std::string_literals;
m_lastAssertionInfo = info;
AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE"s;
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
assertionEnded( CATCH_MOVE(assertionResult) );
}
void RunContext::handleNonExpr(
AssertionInfo const &info,
@@ -650,11 +667,11 @@ namespace Catch {
m_lastAssertionInfo = info;
AssertionResultData data( resultType, LazyExpression( false ) );
AssertionResult assertionResult{ info, data };
assertionEnded( assertionResult );
AssertionResult assertionResult{ info, CATCH_MOVE( data ) };
if( !assertionResult.isOk() )
populateReaction( reaction );
const auto isOk = assertionResult.isOk();
assertionEnded( CATCH_MOVE(assertionResult) );
if ( !isOk ) { populateReaction( reaction ); }
}

View File

@@ -8,8 +8,9 @@
#ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
#define CATCH_RUN_CONTEXT_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_reporter.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_test_run_info.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/catch_message.hpp>
@@ -24,13 +25,14 @@
namespace Catch {
class IMutableContext;
class IGeneratorTracker;
class IConfig;
class IEventListener;
using IEventListenerPtr = Detail::unique_ptr<IEventListener>;
///////////////////////////////////////////////////////////////////////////
class RunContext : public IResultCapture {
class RunContext final : public IResultCapture {
public:
RunContext( RunContext const& ) = delete;
@@ -59,7 +61,7 @@ namespace Catch {
AssertionReaction& reaction ) override;
void handleUnexpectedInflightException
( AssertionInfo const& info,
std::string const& message,
std::string&& message,
AssertionReaction& reaction ) override;
void handleIncomplete
( AssertionInfo const& info ) override;
@@ -68,10 +70,13 @@ namespace Catch {
ResultWas::OfType resultType,
AssertionReaction &reaction ) override;
bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
void notifyAssertionStarted( AssertionInfo const& info ) override;
bool sectionStarted( StringRef sectionName,
SourceLineInfo const& sectionLineInfo,
Counts& assertions ) override;
void sectionEnded( SectionEndInfo const& endInfo ) override;
void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
void sectionEnded( SectionEndInfo&& endInfo ) override;
void sectionEndedEarly( SectionEndInfo&& endInfo ) override;
IGeneratorTracker*
acquireGeneratorTracker( StringRef generatorName,
@@ -90,7 +95,7 @@ namespace Catch {
void pushScopedMessage( MessageInfo const& message ) override;
void popScopedMessage( MessageInfo const& message ) override;
void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
void emplaceUnscopedMessage( MessageBuilder&& builder ) override;
std::string getCurrentTestName() const override;
@@ -116,7 +121,7 @@ namespace Catch {
void resetAssertionInfo();
bool testForMissingAssertions( Counts& assertions );
void assertionEnded( AssertionResult const& result );
void assertionEnded( AssertionResult&& result );
void reportExpr
( AssertionInfo const &info,
ResultWas::OfType resultType,
@@ -130,7 +135,6 @@ namespace Catch {
void handleUnfinishedSections();
TestRunInfo m_runInfo;
IMutableContext& m_context;
TestCaseHandle const* m_activeTestCase = nullptr;
ITracker* m_testCaseTracker = nullptr;
Optional<AssertionResult> m_lastResult;

View File

@@ -15,7 +15,7 @@ namespace Catch {
Section::Section( SectionInfo&& info ):
m_info( CATCH_MOVE( info ) ),
m_sectionIncluded(
getResultCapture().sectionStarted( m_info, m_assertions ) ) {
getResultCapture().sectionStarted( m_info.name, m_info.lineInfo, m_assertions ) ) {
// Non-"included" sections will not use the timing information
// anyway, so don't bother with the potential syscall.
if (m_sectionIncluded) {
@@ -23,13 +23,31 @@ namespace Catch {
}
}
Section::Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const ):
m_info( { "invalid", static_cast<std::size_t>( -1 ) }, std::string{} ),
m_sectionIncluded(
getResultCapture().sectionStarted( _name, _lineInfo, m_assertions ) ) {
// We delay initialization the SectionInfo member until we know
// this section needs it, so we avoid allocating std::string for name.
// We also delay timer start to avoid the potential syscall unless we
// will actually use the result.
if ( m_sectionIncluded ) {
m_info.name = static_cast<std::string>( _name );
m_info.lineInfo = _lineInfo;
m_timer.start();
}
}
Section::~Section() {
if( m_sectionIncluded ) {
SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
if( uncaught_exceptions() )
getResultCapture().sectionEndedEarly( endInfo );
else
getResultCapture().sectionEnded( endInfo );
SectionEndInfo endInfo{ CATCH_MOVE(m_info), m_assertions, m_timer.getElapsedSeconds() };
if ( uncaught_exceptions() ) {
getResultCapture().sectionEndedEarly( CATCH_MOVE(endInfo) );
} else {
getResultCapture().sectionEnded( CATCH_MOVE( endInfo ) );
}
}
}

View File

@@ -9,6 +9,7 @@
#define CATCH_SECTION_HPP_INCLUDED
#include <catch2/internal/catch_compiler_capabilities.hpp>
#include <catch2/internal/catch_config_static_analysis_support.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/catch_section_info.hpp>
#include <catch2/catch_timer.hpp>
@@ -20,6 +21,9 @@ namespace Catch {
class Section : Detail::NonCopyable {
public:
Section( SectionInfo&& info );
Section( SourceLineInfo const& _lineInfo,
StringRef _name,
const char* const = nullptr );
~Section();
// This indicates whether the section should be executed or not
@@ -35,16 +39,62 @@ namespace Catch {
} // end namespace Catch
#define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#if !defined(CATCH_CONFIG_EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT)
# define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
catch_internal_Section ) = \
Catch::Section( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if ( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( \
catch_internal_Section ) = \
Catch::SectionInfo( \
CATCH_INTERNAL_LINEINFO, \
( Catch::ReusableStringStream() << __VA_ARGS__ ) \
.str() ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#else
// These section definitions imply that at most one section at one level
// will be intered (because only one section's __LINE__ can be equal to
// the dummy `catchInternalSectionHint` variable from `TEST_CASE`).
namespace Catch {
namespace Detail {
// Intentionally without linkage, as it should only be used as a dummy
// symbol for static analysis.
int GetNewSectionHint();
} // namespace Detail
} // namespace Catch
# define INTERNAL_CATCH_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
# define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS \
if ( [[maybe_unused]] int catchInternalPreviousSectionHint = \
catchInternalSectionHint, \
catchInternalSectionHint = Catch::Detail::GetNewSectionHint(); \
catchInternalPreviousSectionHint == __LINE__ ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#endif
#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_UNUSED_VARIABLE_WARNINGS \
if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
#endif // CATCH_SECTION_HPP_INCLUDED

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