Compare commits

..

114 Commits

Author SHA1 Message Date
Martin Hořeňovský 6ee0826dca v3.15.0 2026-05-12 13:16:46 +02:00
Martin Hořeňovský 47ea57d74f Constexpr matching support in the range equals matchers 2026-05-12 11:05:33 +02:00
Martin Hořeňovský d838f88b9c Constexpr matching support in generic Contains matchers 2026-05-12 11:05:31 +02:00
Martin Hořeňovský c267b6eb4d Constexpr matching support in the quantifier matchers 2026-05-12 11:05:31 +02:00
Martin Hořeňovský 3cdae5faf0 Constexpr matching support in IsEmpty and SizeIs matchers 2026-05-12 11:05:29 +02:00
Martin Hořeňovský 651247c7f4 Support for constexpr matchers in C++20 (P0784)
To make this all work, I had to remove the stringification cache
from matchers. In theory, this can cause performance penalty in
cases where single matcher instance is stringified multiple times,
but in practice this does not happen much, and the difference is
surprisingly small anyway, because the performance of stringification
is already horrible and full of allocating strings just to throw
them away.

The matcher combinators need P2738 from C++26 to be `constexpr`.

Closes #3091
2026-05-12 11:05:02 +02:00
Martin Hořeňovský 15b9393f0f Don't check if __cplusplus is defined 2026-05-10 20:48:58 +02:00
Martin Hořeňovský a18badd10f Workaround P3168 causing ambiguous overload issues with StringMaker
P3168 turned `std::optional` into a range type, so the partial specialization
of `StringMaker` for `std::optional<T>` conflicted with the partial
specialization for range types. Ideally we will fix this in the future
to support user-provided partial specializations for range-like types,
but for now we just disable the partial specialization for `std::optional<T>`
if P3168 is implemented.
2026-05-09 22:24:12 +02:00
Martin Hořeňovský 54af40652a Add more runtime benchmark recipe TODOs 2026-05-07 21:13:11 +02:00
Martin Hořeňovský 5f47160bb2 Add benchmark for catch_test_macros.hpp inclusion 2026-05-07 20:27:09 +02:00
Martin Hořeňovský e83528c6eb Add readme to benchmarks/ with some simple examples 2026-05-07 10:43:24 +02:00
Martin Hořeňovský fd31a62547 Add benchmark for REQUIRE_THAT into runtime_assertion_benches.cpp
I also changed the names of other benchmark `TEST_CASE`s so that
they are easier to use.
2026-05-07 10:18:21 +02:00
Martin Hořeňovský 4df8fee92d IConfig derives from NonCopyable privately 2026-05-07 10:12:28 +02:00
Ole Schmidt 300c5d3eed Fix Wconversion warning in catch_reporter_helpers.cpp 2026-05-06 09:58:01 +02:00
Martin Hořeňovský 11a96e186a Cleanups from static analysis 2026-04-24 11:21:04 +02:00
Martin Hořeňovský 10f62484bf AssertionHandler uses RunContext directly to avoid virtual dispatch 2026-04-13 22:56:55 +02:00
Martin Hořeňovský be2dfb45cc Update Doxyfile 2026-04-08 15:06:36 +02:00
Martin Hořeňovský 47200ddbee Rework internals of CATCH_REGISTER_ENUM
The old internals reached into the global hub to stash the allocation(s)
for enum value -> string value there, then kept around a potentially
invalid (in case the hub was cleaned up) reference into it, going through
whole bunch of virtual dispatch in the process.

The new internals just store the data in a static variable inside the
`StringMaker` specialization. This avoids potential lifetime issues,
avoids all virtual dispatch and (almost) reduces the include bloat in
the main header path.

The reason for (almost) there is that for full include correctness,
`EnumInfo` needs `<utility>` include for `std::pair`. However, this brings
in things like `std::relops`, because the std headers in C++ are dumb.
As this was not included before, and instead we relied on `std::pair`
existing in an internal stdlib header that we transitively included, the
full include size ends up bigger than before.
2026-04-08 12:57:09 +02:00
Martin Hořeňovský e51dcdcc59 Mark TestInvoker{AsMethod,Fixture} as final 2026-04-07 14:36:08 +02:00
Martin Hořeňovský 77eae8bd2a Turn IResultCapture::lastAssertionPassed into a free function
The thread-safety changes in assertions & messages turned whether
the last assertion passed or failed into thread-local state,
instead of being member of the `RunContext`. However, this change
was not reflected in the API `CHECKED_IF`/`CHECKED_ELSE` used,
which in turn required `catch_test_macro_impl.hpp` to include
`catch_interfaces_capture.hpp` for it.

Thanks to the combination of this commit and the previous similar
commit for the message stack handling, the main include path does
not need to include `catch_interfaces_capture.hpp` anymore.
2026-04-07 14:33:39 +02:00
Martin Hořeňovský 3ab0d7cef3 Turn IResultCapture::{push,pop}*Message into free functions
The thread-safety changes in assertions & messages turned the message
stacks into thread-local state, instead of it being member of `RunContext`.
The relevant {push,pop}(Un)ScopedMessage functions were turned from member
functions into static functions, but this left `catch_message.hpp` with
dependency on `IResultCapture`'s definition for the functions, and thus
it still had to include `catch_interfaces_capture.hpp`.

With this change, `catch_message.hpp` no longer needs
`catch_interfaces_capture.hpp`.
2026-04-07 14:28:11 +02:00
Martin Hořeňovský e83218c2df Move all use of IResultCapture in AssertionHandler into the cpp file
This has two advantages

1) Removing the include of `catch_interfaces_capture.hpp` from the
   header and potentially allowing further pruning later.
2) Providing small-but-measurable 1-2% speedup for assertions.
2026-04-07 09:32:07 +02:00
Martin Hořeňovský 548e14a8c8 Mark some extra classes final 2026-04-07 00:13:17 +02:00
Martin Hořeňovský b670de4fe1 v3.14.0 2026-04-05 15:05:38 +02:00
Martin Hořeňovský 465e63dad7 Do not include chrono_clock.hpp in benchmark_stats_fwd.hpp 2026-04-05 14:57:05 +02:00
Christoph Grüninger 34f4f81947 [ci] Update GitHub Actions to latest major release
Fix warning that Node.js 20 is disconinued in June
2026-04-04 22:28:48 +02:00
Martin Hořeňovský 57f738b380 JUnit reporter output single failed assertion node per test case
Ideally JUnit tools would handle multiple failures per test case,
but they don't, so here we are.

Closes #1919
2026-04-04 15:12:56 +02:00
Martin Hořeňovský 1df10d28ae Don't write semicolon after failed expression in separate call 2026-04-03 10:28:32 +02:00
Martin Hořeňovský 9f1b48a94f Inline SKIP/FAIL in RunContext to fix build failure with prefixed macros
Closes #3087
2026-04-03 10:19:05 +02:00
Sung, Po Han f4e83daa18 Fix catch_discover_tests PRE_TEST failure with zero discoverable tests
When using catch_discover_tests() with DISCOVERY_MODE PRE_TEST and a
multi-config generator (e.g. Ninja Multi-Config), if a test target has
zero discoverable tests (e.g. all tests tagged with [.]), ctest fails:

  CMake Error: include could not find requested file:
    .../test-hidden-b12d07c_tests-Release.cmake

The early return added in #2962 (76f70b14) correctly prevented a JSON
parsing crash for zero tests, but skipped writing the ctest file. The
PRE_TEST include script unconditionally includes this file, so the
missing file causes a hard error that aborts all test discovery.

Write an empty file before returning early so the include always
succeeds.
2026-04-02 10:24:46 +02:00
Jan Niklas Hasse 572f96b8fe Fix counting of UTF-8 codepoints in TextFlow
For line-wrapping bytes were counted instead of codepoints. This
resulted in line-breaks being inserted at the wrong position and also
breaking UTF-8 characters.

Fixes #1022.
2026-04-01 16:12:08 +02:00
Martin Hořeňovský 8492fd444e Ignore Wunreachable-code-return in Generators.tests.cpp 2026-04-01 11:02:56 +02:00
Martin Hořeňovský fe2a20ab55 Fix warning suppression block in Condition.tests straddling includes 2026-04-01 10:56:21 +02:00
Martin Hořeňovský 51b0532d1f Suppress __COUNTER__ warning from newest Clang versions
Ideally we could suppress the warning locally in the `UNIQUE_NAME` macro,
but that runs into at least 2 issues:

1) Clang actually does not consider the warning as coming from inside the
   `UNIQUE_NAME` macro, even though it correctly points to its expansion
   as the problem. This means that adding `_Pragma`s inside the macro
   around the __COUNTER__ usage does not actually silence the warning.

2) Adding the local suppressions anyway breaks the expansion of
   `MAKE_NAMESPACE` macro inside the templated test case macros. This can
   be fixed for the newest clang version by removing its use and using
   the uniqued `TestName` for the namespace name directly, but this breaks
   compilation on GCC, and older Clang versions.

Because of these issues, we introduce global warning suppression for
`-Wc2y-extensions` to be done with it. We should revisit this if Clang 23
fixes the local pragma based suppression, when it might be worth the effort
to rework the templated test case macros to support it.

Closes #3076
2026-04-01 10:34:58 +02:00
Joseph Edwards 2ec64d12b1 fix: add missing backslash in macro definition 2026-03-30 14:23:17 +02:00
Martin Hořeňovský ccc49ba664 Merge pull request #3078 from e-kwsm/cstdint
fix: add missed <cstdint> inclusion
2026-03-11 13:44:52 +01:00
Eisuke Kawashima 6f036244e9 fix: add missed <cstdint> inclusion 2026-03-08 19:34:59 +09:00
Vertexwahn 50e9dbfc4e Update bazel_skylib and rules_cc dependencies 2026-02-18 20:14:05 +01:00
Martin Hořeňovský a404f37cec Add quiet verbosity variant to the default listener and tag listing 2026-02-16 22:52:39 +01:00
Martin Hořeňovský 29c9844f68 v3.13.0 2026-02-15 22:55:48 +01:00
Martin Hořeňovský 56024c04e4 More tests for MapGenerator 2026-02-15 21:53:10 +01:00
Martin Hořeňovský edfed6c04e Efficient skipToNthElementImpl for Take and Map generators 2026-02-15 21:43:54 +01:00
Martin Hořeňovský 75bfcc3f30 Expose skipToNthElement in the GeneratorWrapper 2026-02-15 21:38:14 +01:00
Martin Hořeňovský 056e4fe88d MapGenerator only calls mapping function if the result is used
Previously the mapping was eager on construction and calls to
`next()`. This was fine when generators were always exhausted fully,
but with the new generator filtering, we might not want to call
the map function eagerly; rather we want to call it only once,
after the generator is moved to the target element.
2026-02-15 20:44:21 +01:00
Martin Hořeňovský 3a0cf7e75f Support filtering on both sections and generators (#3069)
Not being able to filter generators to specific element has been regularly
causing problems. It was possible to use a dynamic section to run tests
for specific element in a generator, at least if the element had a nice
string representation, but the test case would still run once per element
in the generator.

With this change, it is possible to have the generator return only one
specific element, and do so based on the index, rather than the string
representation of the element. This enables simple debugging of tests
that fail for specific generator element.
2026-02-15 20:27:15 +01:00
Martin Hořeňovský b6c7b217d4 Improve test for --warn InfiniteGenerators 2026-02-13 21:30:26 +01:00
Martin Hořeňovský 045ac7acce Simplify IGeneratorTracker
* Removed `IGeneratorTracker::hasGenerator`, as it was never used.
* Removed `IGeneratorTracker::setGenerator`, as actually using it
  after the tracker was used would lead to inconsistent behaviour.
  Instead, the tracker is given the generator it guards during
  construction.
2026-02-13 21:17:37 +01:00
Martin Hořeňovský 72671fdbdf Check for infinite generators before we construct generator's tracker 2026-02-13 21:17:06 +01:00
Martin Hořeňovský 120827d4d6 Fix formatting in catch_commandline.cpp 2026-02-13 21:16:03 +01:00
Martin Hořeňovský daadf42a0e Add --warn InfiniteGenerators 2026-02-10 09:37:19 +01:00
Martin Hořeňovský d079ee13ab Allow generators to declare themselves (infinite)
This will be useful later to implement warning on infinitely
running `GENERATE` expressions.
2026-02-10 09:37:15 +01:00
Martin Hořeňovský 0056cd4efb Remove Tuple sponsorship banner - sponsorship has ended 2026-02-09 10:23:43 +01:00
ThePhD de7e863013 🐛 Clang on Windows will define _MSC_VER and trip up these checks -- make it actually Visual C++-specific 2026-01-19 15:14:53 +01:00
Martin Hořeňovský 024aec9729 Add efficient skip forward to Values, Map and Take generators 2026-01-12 22:39:59 +01:00
Martin Hořeňovský 9eca713a1f Add option to skip forward to the generator interface 2026-01-12 16:58:15 +01:00
Martin Hořeňovský 44c597f074 Add Concat generator for combining multiple generators 2026-01-12 14:27:49 +01:00
Martin Hořeňovský 6aedc79870 Remove unused m_used_up member in the ChunkGenerator 2026-01-12 14:27:44 +01:00
dragon-archer fcbf006c78 Fix _NullFile for MinGW 2026-01-12 09:29:19 +01:00
Martin Hořeňovský 2580eadc42 Add UNSCOPED_CAPTURE
Closes #2954
Closes #3010
2026-01-11 14:53:08 +01:00
Martin Hořeňovský b59f4f3522 Rename DEPRECATED -> CATCH_DEPRECATED
Closes #3058
2026-01-06 20:26:08 +01:00
bigmoonbit cd4fc88e2a chore: fix some typos in comments (#3048)
Signed-off-by: bigmoonbit <bigmoonbit@outlook.com>
Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2026-01-03 16:05:18 +01:00
Martin Hořeňovský a50ac2f681 Add ASan and TSan builds on Mac 2026-01-03 14:30:07 +01:00
Martin Hořeňovský b81ef2aa2e Add OUTPUT_ON_FAILURE and NO_TESTS_ACTIONS=error to CTest GHA actions 2026-01-03 14:07:25 +01:00
Martin Hořeňovský ec4dcbf9cb Fix Wweak-vtables in benchmarks/assertion_listener.cpp 2026-01-03 13:43:27 +01:00
qerased b66b89374e Add validation for --benchmark-samples to prevent crash with zero value (#3056)
* Add validation for --benchmark-samples to prevent crash with zero samples
* Add automated test for benchmark samples validation
* Update baselines for benchmark-samples validation
* Add missing test for benchmark samples validation

---------

Co-authored-by: Chan Aung <chan@Thinkpad.localdomain>
2026-01-02 23:15:13 +01:00
OliverBeeckAVM b7e31c9ab3 Suppress static analysis warning 26426 for Windows/MSVC builds (#3057)
Suppress `Global initializer calls a non-constexpr function 'Catch::makeTestInvoker' (i.22).`,
which is issued when using macro `TEST_CASE` for windows/MSVC builds.


---------

Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2026-01-01 22:56:14 +01:00
Martin Hořeňovský 88abf9bf32 v3.12.0 2025-12-28 22:31:54 +01:00
Martin Hořeňovský 970ec144f5 Thread safety in assertions is no longer experimental 2025-12-28 21:02:50 +01:00
Martin Hořeňovský eb3811c555 Fix lifetime issues when using UNSCOPED_X message macros
The original implementation of `UNSCOPED_X` message macros used a
clever hack to make the original implementation simpler: construct
an instance of `ScopedMessage` to manage its lifetime, but store
it in a vector, so its lifetime is not actually scope-based, and
we can manage it through the vector instance.

This hack made it so that the lifetime of the vector that manages
the fake `ScopedMessage`s must be outlived by the vector with the
actual messages. Originally this wasn't a problem, because they both
lived inside the run context instance. However, since then these
vectors became globals and thread-local. When this happened, it
still wasn't a problem; the two globals were declared in the right
order, so they were destroyed in the right order as well.

Then, in f80956a43a, these globals
were turned into magic static globals to improve their behaviour
in MSVC's Debug build mode. This caused their lifetimes to be
runtime-dependent; if a specific test thread added its first scoped
message before it added first unscoped message, the lifetimes
would be correct. If it instead added first unscoped message
before adding first scoped message, then there **might** be
invalid reads during thread destruction.

The fix is simple: do things properly and manage the lifetime of
messages in `UNSCOPED_X` explicitly. Then we don't have to deal
with the destruction of fake `ScopedMessage`s while the thread is
being destroyed, and the lifetime of the two vectors is no longer
tied together.

I also threw them both into a new type, to encapsulate some of the
unscoped message logic.
2025-12-26 15:53:30 +01:00
Martin Hořeňovský 343cc059fe Move to newer MacOS GHA runners
Macos-13 runners were deprecated, and macos-15 family brings back
intel runner that is usable by OSS projects.
2025-12-23 16:12:23 +01:00
Masashi Fujita 97091636d0 Fix conditional compilation for FreeBSD to exclude PlayStation platform 2025-12-12 09:38:52 +01:00
Martin Hořeňovský f80956a43a Use magic statics for non-trivial thread-local globals
This avoids calling the global's constructor on threads that will
never interact with them. Calling the constructor can have surprising
overhead, as e.g. MSVC's Debug mode `std::vector` will allocate in
the default constructor.

Closes #3050
2025-12-02 14:19:21 +01:00
Martin Hořeňovský 32eac2d1bb Only use thread_local in builds with thread safety enabled
MSVC cannot dllexport thread_local variables, so we avoid making
globals thread local if we won't support multiple threads anyway.

Closes #3044
2025-12-02 14:01:55 +01:00
Martin Hořeňovský e849735e11 Add tests for assertion thread safety 2025-12-01 10:45:07 +01:00
Martin Hořeňovský d26f763180 Initialize ReusableStringStream cache before user threads can run
The initialization itself is thread unsafe, and as such we cannot
allow it to be delayed until multiple user-spawned threads need it.
2025-12-01 10:44:31 +01:00
Martin Hořeňovský 5e44382423 Fix initialization of AtomicCounts for older standards 2025-12-01 10:42:43 +01:00
Martin Hořeňovský 985a3f4460 Fix lazy removal of unscoped messages also removing still valid msgs 2025-11-30 14:30:19 +01:00
Stefan Haller a1faad9315 Fix the help text for the --order command line argument
It was changed to rand in v3.9.0.
2025-11-07 21:28:41 +01:00
Martin Hořeňovský 31ee3beb0a Small documentation fixes
This includes 2 small typos I found when working on generator skipping,
and 1 typo found by @sfraczek in #3039.

Closes #3039
2025-10-16 20:45:28 +02:00
Martin Hořeňovský 3b853aa9fb Add lifetime attributes to JSON/XML writers 2025-10-16 20:37:05 +02:00
Martin Hořeňovský 49d79e9e9c Fix section filtering to make sense
Specifically, this commit makes the `-c`/`--section` parameter
strictly ordered and hierarchical, unlike how it behaved before,
which was a huge mess -- see #3038 for details.

Closes #3038
2025-10-16 09:16:56 +02:00
ZXShady 33e6fd217a Remove recursion when stringifying std::tuple 2025-10-04 22:10:36 +02:00
ZXShady a58df2d7c5 Outline part of formatting system_clock's time_point into cpp file 2025-10-04 22:10:36 +02:00
ZXShady a9223b2bb3 Outline catch_strnlen's definition into catch_tostring.cpp 2025-10-04 22:10:36 +02:00
Martin Hořeňovský 363ca5af18 Add lifetime annotations to more places using StringRef 2025-10-04 16:38:07 +02:00
Martin Hořeňovský cb6d713774 Add lifetimebound annotation to StringRef 2025-10-04 16:12:17 +02:00
Martin Hořeňovský 8e4ab5dd8f Annotate matcher combinators with CATCH_ATTR_LIFETIMEBOUND
The matcher combinators do not take ownership of the matchers
being combined, which can catch the users off-guard, when code like
this

```cpp
using Catch::Matchers::EndsWith;
using Catch::Matchers::ContainsSubstring;

auto combinedMatcher = EndsWith("as a service")
                       && ContainsSubstring("web scale");

REQUIRE_THAT( getSomeString(), combinedMatcher );
```

leads to use-after-free, as the `combinedMatcher` refers to matcher
temporaries that no longer exists. With this commit, users of Clang,
MSVC or other compiler that understands the `lifetimebound` attribute,
should get a warning.
2025-10-03 22:27:29 +02:00
Martin Hořeňovský 8219ed79f2 Add CATCH_ATTR_LIFETIMEBOUND macro polyfill over lifetimebound attr 2025-10-03 22:15:27 +02:00
Martin Hořeňovský b3fb4b9fea v3.11.0 2025-09-30 10:54:31 +02:00
Martin Hořeňovský 6500dc8149 Add Tuple sponsorship banner to readme
Until December.
2025-09-30 10:34:21 +02:00
Martin Hořeňovský 4be9de6c54 Disable test for deprecated CATCH_CONFIG_BAZEL_SUPPORT option
Because it requires full rebuild of the base library, it adds about
20% to the build time of the test suite. Between the fact that the
option is deprecated, and that Bazel has added the `BAZEL_TEST`
env var _years_ ago, nobody should be using it, and the chance
of breakage is tiny, the test is not worth its compile-time cost.
2025-09-28 12:57:55 +02:00
Martin Hořeňovský 91b3b3bf40 Support Bazel's TEST_RANDOM_SEED
As with other Bazel env vars, it overrides the corresponding CLI
parameter if both are set.

Closes #3021
Closes #3024
2025-09-28 10:48:08 +02:00
Martin Hořeňovský a00d654437 Speed up processing mostly visible strings in convertIntoString
This commit causes small (~5%) slowdown when processing strings
that consist mostly of escaped characters, but improves throughput
by about 100% for strings that consist mostly of characters that
do not need escaping.
2025-09-27 22:43:14 +02:00
Martin Hořeňovský 756ae05d30 Inline the getResultCapture helper into header.
We still keep the error check in the function, but hide it in an
outlined function inside a .cpp file, to promote inlining of the
retrieval part.

In the future, we should explore two things

1) Skipping over the context retrieval here, allowing direct access.
   I currently do not see a way to do this while keeping the
   "greppability" of mutable vs immutable accesses that is there now,
   but it would help a lot when inlining is not enabled.
2) Removing the error check, to make the function trivially inlinable,
   and without branches.

**runtime difference**

| --------- | Debug | Release |
|:----------|------:|--------:|
| Slow path |  0.98 |    1.07 |
| Fast path |  1.04 |    1.08 |

We lost bit of performance on the assertion slow path in debug mode,
but together with the previous commit, it comes out at net zero.
For other combinations, we see 5-10% perf improvement across the
two commits.
2025-09-27 13:24:46 +02:00
Martin Hořeňovský a2e41916f2 Keep the main Context instance as static value, not pointer
This allows us to remove the lazy init checks, improving the inlining
potential when retrieving current context, thus slightly improving
the performance of assertions.

**runtime difference**

| --------- | Debug | Release |
|:----------|------:|--------:|
| Slow path |  1.01 |    0.98 |
| Fast path |  1.02 |    1.02 |

There is small slowdown in case of Release build + assertions taking
the slow path, but

1) going through the slow path is rare
2) Given the code change, I believe this to be artifact of the
   optimizer in the old GCC version I am using locally.
2025-09-27 13:24:44 +02:00
Duncan Horn 0e772cc0d2 Allow composability of unhandled exception filters (#3033)
The change is very simple. If a handler previously existed, Catch2 will invoke it after printing out its output. I've also updated the comment to better reflect that it's returning EXCEPTION_CONTINUE_SEARCH even in scenarios where the exception is one that the library cares about.
2025-09-27 11:09:34 +02:00
Mason Wilie 3bcd0a4e74 Changed GetOptions to use sequence 2025-09-25 21:06:10 +02:00
Duncan Horn f7e7fa0983 Fix ReusableStringStream to access the container under the lock (#3031)
Commit 582200a made `ReusableStringStream`'s index reservation thread safe, however it's still accessing the `m_streams` vector outside the lock. This makes it so that the `add` call returns the pointer in addition to the index so that the lock doesn't need to get acquired again until destruction.

The issue with accessing `m_streams` outside the lock is that `add` can call `push_back` on the vector, which might re-allocate. If this re-allocation occurs concurrently with anther thread trying to index into this array, you get UB (typically a null pointer read).
2025-09-25 13:31:49 +02:00
Martin Hořeňovský b626e4c7ae Add simple runtime benchmarks
For now we add just two binaries, one with assertions taking the
fast path, one with assertions taking the slow path, and the ability
to run 1 of `REQUIRE(true)`, `REQUIRE_NOTHROW`, `REQUIRE_THROWS`
in a loop.

I also split off a CMake preset which enables more tests than the
basic `simple-tests` preset, but does not enable the most expensive
tests which force recompilation of Catch2 multiple times.
2025-09-23 17:17:33 +02:00
Martin Hořeňovský 434bf55d47 Make message push/pop static
Since the change to make the message macros thread-safe, and thus
thread-local, there is no need to handle messages through instance
of `RunContext`.
2025-09-23 11:46:26 +02:00
Martin Hořeňovský 9048c24fa7 Improve QNX support
This cherry-picks the source changes from PR made by @pkleymonov-qnx,
without including the build path changes that are irrelevant unless
we can add a QNX target to our CI.

Close #2953
2025-09-22 15:36:46 +02:00
Martin Hořeňovský 8ed8c431c6 Support Bazel's TEST_PREMATURE_EXIT_FILE and add it to CLI as well
This tells Catch2 to create an empty file at specified path before
the tests start, and delete it after the tests finish. This allows
callers to catch cases where the test binary silently exits before
finishing (e.g. via call to `exit(0)` inside the code under test),
by looking whether the file still exists.

Closes #3020
2025-09-22 14:02:45 +02:00
Silent dc3a4ea41a Fix non-desktop Windows platforms not defining CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32 correctly
Fixes building on non-desktop GDK platforms.
2025-09-17 20:25:26 +02:00
Martin Hořeňovský cd93d202e0 Small refactoring in CAPTURE parsing 2025-08-26 12:40:13 +02:00
Martin Hořeňovský 227af796b4 Move MessageInfos into the run context
Together with the previous change, this means that `ScopedMessage`
only needs to keep around a single unsigned int.
2025-08-26 12:37:52 +02:00
Martin Hořeňovský 33adb4c779 Use only the ID of a message when removing it from context 2025-08-26 12:07:41 +02:00
Martin Hořeňovský 25319fd304 v3.10.0 2025-08-25 21:30:21 +02:00
Szapi 85c4bad86b Forbid deducing reference types for m_predicate in FilterGenerator (#3005)
Forbid deducing reference types for m_predicate in FilterGenerator to prevent dangling references.
This is needed for out-of-line predicates to work correctly instead of undefined behavior or crashes.

---------

Co-authored-by: Tek Mate <mate.tek@evosoft.com>
2025-08-23 21:55:38 +02:00
Martin Hořeňovský 582200a1f8 Make message macros (FAIL, WARN, INFO, etc) thread safe
This builds on the existing work to make assertion thread safe,
by adding an extra synchronization point in the holder of
`ReusableStringStream`'s stream instances, as those are used to
build the messages, and finishing the move of message scope holders
to be thread-local.
2025-08-23 11:08:18 +02:00
Martin Hořeňovský f4e05a67bb Improve performance of writing XML
As with the JSON writer, the old code was made to be simple and
for each char just decided whether it needs escaping, or should be
written as-is. The new code instead looks for characters that need
escaping and batches writes of characters that do not.

This provides 4-8x speedup (length dependent) for writing strings
that do not need escaping, and keeps roughly the same performance
for those that do need escaping.
2025-08-22 17:03:35 +02:00
Martin Hořeňovský fb2e4fbe41 Improve performance of writing JSON values
The old code was exceedingly simple, as it went char-by-char and
decided whether to write it to the output stream as-is, or escaped.
This caused a _lot_ of stream writes of individual characters.

The new code instead looks for characters that need escaping, and
bulk-writes the non-escaped characters in between them. This leads
to about the same performance for strings that comprise of only
escaped characters, and 3-10x improvement for strings without any
escaping needed.

In practice, we should expect the former rather than the latter,
but this is still nice improvement.
2025-08-22 17:00:40 +02:00
Martin Hořeňovský 78a9518a28 Don't add / to start of pkg-config file path when DESTDIR is unset
This broke installation on Windows, because the suddenly started
with a '/' instead of a drive letter.

Closes #3019
2025-08-21 20:58:55 +02:00
Shloka Jain 3e82ef9317 Fix color mode detection on FreeBSD by adding platform macro 2025-08-12 14:38:50 +02:00
Martin Hořeňovský 7cad6d7539 Handle DESTDIR env var when generating pkgconfig files
Having the ability to configure the installation path at config
time, and the ability to change the prefix at install time is not
enough, apparently people also use env var to redirect them instead.

Closes #3006
2025-08-09 23:05:18 +02:00
191 changed files with 11318 additions and 3398 deletions
+4 -1
View File
@@ -32,6 +32,7 @@ Checks: >-
-modernize-deprecated-headers,
,# There's a lot of these and most of them are probably not useful,
-modernize-pass-by-value,
-modernize-use-string-view, # We support C++14,
performance-*,
performance-enum-size,
@@ -58,6 +59,8 @@ Checks: >-
-readability-uppercase-literal-suffix,
-readability-use-anyofallof,
-readability-avoid-return-with-void-value,
,# We prefer if defined(FOO) form because it is easier to extend later,
-readability-use-concise-preprocessor-directives,
,# time hogs,
-bugprone-throw-keyword-missing,
@@ -78,5 +81,5 @@ WarningsAsErrors: >-
readability-duplicate-include,
HeaderFilterRegex: '.*\.(c|cxx|cpp)$'
FormatStyle: none
CheckOptions: {}
CheckOptions: []
...
+6 -2
View File
@@ -2,6 +2,10 @@ name: Linux Builds (Bazel)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build_and_test_ubuntu:
name: Linux Ubuntu 22.04 Bazel build <GCC 11.2.0>
@@ -12,10 +16,10 @@ jobs:
compilation_mode: [fastbuild, dbg, opt]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Mount Bazel cache
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: "/home/runner/.cache/bazel"
key: bazel-ubuntu22-gcc11
+5 -1
View File
@@ -2,6 +2,10 @@ name: Linux Builds (Meson)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: meson ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
@@ -19,7 +23,7 @@ jobs:
other_pkgs: clang-11
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Prepare environment
run: |
+7 -3
View File
@@ -5,6 +5,10 @@ name: Linux Builds (Complex)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.build_description}}, ${{matrix.cxx}}, C++${{matrix.std}} ${{matrix.build_type}}
@@ -71,7 +75,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Prepare environment
run: |
@@ -90,13 +94,13 @@ jobs:
run: cmake --build build
- name: Test
run: ctest --test-dir build -j --output-on-failure
run: ctest --test-dir build -j
clang-tidy:
name: clang-tidy
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Prepare environment
run: |
+6 -2
View File
@@ -2,6 +2,10 @@ name: Linux Builds (Basic)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
@@ -79,7 +83,7 @@ jobs:
other_pkgs: g++-11
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Add repositories for older compilers
run: |
@@ -102,4 +106,4 @@ jobs:
run: cmake --build build
- name: Test
run: ctest --test-dir build -j --output-on-failure
run: ctest --test-dir build -j
+7 -5
View File
@@ -2,20 +2,22 @@ name: Mac Builds
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
# From macos-14 forward, the baseline "macos-X" image is Arm based,
# and not Intel based.
runs-on: ${{matrix.image}}
strategy:
fail-fast: false
matrix:
image: [macos-13, macos-14, macos-15]
image: [macos-14, macos-15, macos-15-intel]
build_type: [Debug, Release]
std: [14, 17]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Configure
run: |
@@ -29,4 +31,4 @@ jobs:
run: cmake --build build
- name: Test
run: ctest --test-dir build -j --output-on-failure
run: ctest --test-dir build -j
@@ -0,0 +1,43 @@
name: Mac Sanitizer Builds
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
runs-on: ${{matrix.image}}
strategy:
fail-fast: false
matrix:
image: [macos-15, macos-15-intel]
build_type: [Debug]
std: [17]
sanitizer: [thread, address]
include:
- sanitizer: thread
preset: basic-tests
filter: -R ThreadSafetyTests
- sanitizer: address
preset: most-tests
filter:
steps:
- uses: actions/checkout@v6
- name: Configure
run: |
CFXXFLAGS=-fsanitize=${{matrix.sanitizer}},undefined
cmake --preset ${{matrix.preset}} -GNinja \
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-DCMAKE_CXX_STANDARD=${{matrix.std}} \
-DCATCH_BUILD_EXTRA_TESTS=ON
- name: Build
run: cmake --build build
- name: Test
run: ctest --test-dir build ${{matrix.filter}}
@@ -18,7 +18,7 @@ jobs:
profile_generate: 'false'
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Install conan
run: pip install conan==${{matrix.conan_version}}
+2 -2
View File
@@ -9,10 +9,10 @@ jobs:
steps:
- name: Checkout source code
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Setup Dependencies
uses: actions/setup-python@v2
uses: actions/setup-python@v6
with:
python-version: '3.7'
- name: Install checkguard
+6 -2
View File
@@ -2,6 +2,10 @@ name: Windows Builds (Basic)
on: [push, pull_request]
env:
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
jobs:
build:
name: ${{matrix.os}}, ${{matrix.std}}, ${{matrix.build_type}}, ${{matrix.platform}}
@@ -14,7 +18,7 @@ jobs:
build_type: [Debug, Release]
std: [14, 17]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v6
- name: Configure build
run: |
@@ -27,5 +31,5 @@ jobs:
shell: cmd
- name: Run tests
run: ctest --test-dir build -C ${{matrix.build_type}} -j %NUMBER_OF_PROCESSORS% --output-on-failure
run: ctest --test-dir build -C ${{matrix.build_type}} -j %NUMBER_OF_PROCESSORS%
shell: cmd
+2 -2
View File
@@ -76,8 +76,8 @@ expand_template(
"#cmakedefine CATCH_CONFIG_WINDOWS_SEH": "",
"#cmakedefine CATCH_CONFIG_USE_BUILTIN_CONSTANT_P": "",
"#cmakedefine CATCH_CONFIG_NO_USE_BUILTIN_CONSTANT_P": "",
"#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS": "",
"#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS": "",
},
template = "src/catch2/catch_user_config.hpp.in",
)
+1 -1
View File
@@ -45,7 +45,7 @@ set(_OverridableOptions
"EXPERIMENTAL_STATIC_ANALYSIS_SUPPORT"
"USE_BUILTIN_CONSTANT_P"
"DEPRECATION_ANNOTATIONS"
"EXPERIMENTAL_THREAD_SAFE_ASSERTIONS"
"THREAD_SAFE_ASSERTIONS"
)
foreach(OptionName ${_OverridableOptions})
+15 -3
View File
@@ -20,6 +20,7 @@ cmake_dependent_option(CATCH_BUILD_TESTING "Build the SelfTest project" ON "CATC
cmake_dependent_option(CATCH_BUILD_EXAMPLES "Build code examples" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_BUILD_BENCHMARKS "Build the benchmarks" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
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)
@@ -34,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.9.1 # CML version placeholder, don't delete
VERSION 3.15.0 # CML version placeholder, don't delete
LANGUAGES CXX
HOMEPAGE_URL "https://github.com/catchorg/Catch2"
DESCRIPTION "A modern, C++-native, unit test framework."
@@ -77,6 +78,11 @@ set(SELF_TEST_DIR ${CATCH_DIR}/tests/SelfTest)
# We need to bring-in the variables defined there to this scope
add_subdirectory(src)
if (CATCH_BUILD_BENCHMARKS)
set(CMAKE_FOLDER "benchmarks")
add_subdirectory(benchmarks)
endif()
# Build tests only if requested
if(BUILD_TESTING AND CATCH_BUILD_TESTING AND NOT_SUBPROJECT)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
@@ -195,17 +201,23 @@ if(NOT_SUBPROJECT)
"set(include_dir \"${CMAKE_INSTALL_INCLUDEDIR}\")"
"set(lib_dir \"${CMAKE_INSTALL_LIBDIR}\")"
[[
message(STATUS "DESTDIR: $ENV{DESTDIR}")
set(DESTDIR_PREFIX "")
if (DEFINED ENV{DESTDIR})
set(DESTDIR_PREFIX "$ENV{DESTDIR}")
endif ()
message(STATUS "PREFIX: ${DESTDIR_PREFIX}")
set(lib_name "$<TARGET_FILE_BASE_NAME:Catch2>")
configure_file(
"${impl_pc_file}"
"${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2.pc"
"${DESTDIR_PREFIX}${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2.pc"
@ONLY
)
set(lib_name "$<TARGET_FILE_BASE_NAME:Catch2WithMain>")
configure_file(
"${main_pc_file}"
"${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2-with-main.pc"
"${DESTDIR_PREFIX}${CMAKE_INSTALL_PREFIX}/${install_pkgconfdir}/catch2-with-main.pc"
@ONLY
)
]]
+11 -2
View File
@@ -15,14 +15,23 @@
}
},
{
"name": "all-tests",
"name": "most-tests",
"inherits": "basic-tests",
"displayName": "Full development build",
"description": "Enables development build with examples and ALL tests",
"description": "Enables development build with extended set of tests (still relatively cheap to build)",
"cacheVariables": {
"CATCH_BUILD_EXAMPLES": "ON",
"CATCH_BUILD_EXTRA_TESTS": "ON",
"CATCH_BUILD_SURROGATES": "ON",
"CATCH_BUILD_BENCHMARKS": "ON"
}
},
{
"name": "all-tests",
"inherits": "most-tests",
"displayName": "Full development build",
"description": "Enables development build with examples and ALL tests",
"cacheVariables": {
"CATCH_ENABLE_CONFIGURE_TESTS": "ON",
"CATCH_ENABLE_CMAKE_HELPER_TESTS": "ON"
}
+432 -266
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -1,5 +1,5 @@
module(name = "catch2")
bazel_dep(name = "bazel_skylib", version = "1.7.1")
bazel_dep(name = "rules_cc", version = "0.1.1")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "rules_cc", version = "0.2.16")
bazel_dep(name = "rules_license", version = "1.0.0")
+21
View File
@@ -0,0 +1,21 @@
include(CatchMiscFunctions)
add_executable(AssertionsFastPath
runtime_assertion_benches.cpp
)
add_executable(AssertionsSlowPath
runtime_assertion_benches.cpp
assertion_listener.cpp
)
add_executable(EmptyExecutable
only_include.cpp
)
target_link_libraries(AssertionsFastPath PRIVATE Catch2::Catch2WithMain)
target_link_libraries(AssertionsSlowPath PRIVATE Catch2::Catch2WithMain)
target_link_libraries(EmptyExecutable PRIVATE Catch2::Catch2WithMain)
list(APPEND CATCH_TEST_TARGETS AssertionsFastPath AssertionsSlowPath EmptyExecutable)
set(CATCH_TEST_TARGETS ${CATCH_TEST_TARGETS} PARENT_SCOPE)
+30
View File
@@ -0,0 +1,30 @@
// 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/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
/**
* Event listener that listens to all assertions, forcing assertion slow path
*/
namespace {
class AssertionSlowPathListener : public Catch::EventListenerBase {
public:
static std::string getDescription() {
return "Validates ordering of Catch2's listener events";
}
AssertionSlowPathListener(Catch::IConfig const* config) :
EventListenerBase(config) {
m_preferences.shouldReportAllAssertions = true;
m_preferences.shouldReportAllAssertionStarts = true;
}
};
CATCH_REGISTER_LISTENER( AssertionSlowPathListener )
}
+16
View File
@@ -0,0 +1,16 @@
// 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
* "empty" include of catch_test_macros.hpp
*
* This file does only one thing: include `catch2/catch_test_macros.hpp`,
* so that we can measure the compilation cost of the header.
*/
#include <catch2/catch_test_macros.hpp>
+81
View File
@@ -0,0 +1,81 @@
<a id="top"></a>
# Benchmarks of Catch2
This folder holds benchmarks for Catch2. It should not be built for the
small(er) test sets, as it is only relevant for running (mainly runtime)
benchmarks.
Below you will find some practical examples using
[hyperfine](https://github.com/sharkdp/hyperfine) to determine the
performance of various scenarios. They assume two parallel checkouts, one
named `Catch2-old` and the other `Catch2-new`. You will need to change
the paths to work on your own machine.
## Runtime benchmarks
The runtime benchmarks currently consist of few different `TEST_CASE`s,
each with a simple loop over different assertion type. They are compiled
into two binaries, one for assertion slow path and one for assertion fast
path.
### Assumptions and notes about real-world usage
The basic assertion macro, `REQUIRE` (and `CHECK`) is the most common one,
by far. Thus, it is the most important one to run quickly. It is probably
followed by the matcher macro, `REQUIRE_THAT`.
The most common result of an assertion is that it passes. Even if it is
stringified and reported, that is most likely due to some listener/reporter
that wants the string representation, not because it failed.
The performance of both Debug and Release builds are important; users
will run tests in both. LTO runtime performance is not too important,
but compile time perf is.
### Examples
**Compare performance of `REQUIRE` in slow path, debug build**
```text
hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-debug/benchmarks/AssertionsSlowPath -o /dev/null "REQUIRE"'
```
**Compare performance of `REQUIRE_THAT` in fast path, release build**
```text
hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-release/benchmarks/AssertionsFastPath -o /dev/null "REQUIRE_THAT"'
```
**Compare performance of `REQUIRE` with stringification enabled, release build**
```text
hyperfine --warmup 2 --shell none --parameter-list version old,new '/home/xarn/benches/Catch2-{version}/build-release/benchmarks/AssertionsFastPath -s -o /dev/null "REQUIRE"'
```
_Note that we redirect the output to `/dev/null` to reduce the overhead of the actual output printing, to see just the impact of stringification._
TODO:
* Start empty binary (set up cost base)
* Start binary with X (100/1k/10k) tests (test registration cost)
* Section tracking
## Compilation benchmarks
As tests are often iterated upon and relinked, the compilation cost of
Catch2 is also important.
### Examples
**Compare overhead of including `catch_test_macros.hpp`**
```text
hyperfine --warmup 2 --shell none --parameter-list version old,new '/usr/bin/c++ -I/home/xarn/benches/Catch2-{version}/src/catch2/.. -I/home/xarn/benches/Catch2-{version}/build-debug/generated-includes -g -o /dev/null -c /home/xarn/benches/Catch2-{version}/benchmarks/only_include.cpp'
```
**Compare build time of Catch2's `SelfTest` test suite, Debug build**
```text
hyperfine --warmup 2 --parameter-list version old,vas --prepare 'find ~/benches/Catch2-{version}/tests/SelfTest -type f -name "*.cpp" -exec touch {} +' 'ninja -j 1 -C ~/benches/Catch2-{version}/build-debug'
```
TODO:
* Link-only recipe
+54
View File
@@ -0,0 +1,54 @@
// 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_test_macros.hpp>
#include <catch2/matchers/catch_matchers_templated.hpp>
namespace {
class MatchAllMatcher final : public Catch::Matchers::MatcherGenericBase {
public:
template <typename Any>
bool match( Any&& ) const {
return true;
}
std::string describe() const override {
using namespace std::string_literals;
return "Long string that does not fit into SSO"s;
}
};
MatchAllMatcher MatchAll() { return MatchAllMatcher(); }
TEST_CASE( "REQUIRE", "[assertions]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE( true );
}
}
TEST_CASE( "REQUIRE_THAT", "[assertions][matchers]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_THAT( 1, MatchAll() );
}
}
TEST_CASE( "REQUIRE_NOTHROW", "[assertions][exceptions]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_NOTHROW( []() {}() );
}
}
TEST_CASE( "REQUIRE_THROWS", "[assertions][exceptions]" ) {
for ( size_t i = 0; i < 10'000'000; ++i ) {
REQUIRE_THROWS( []() { throw 1; }() );
}
}
} // namespace
+2 -1
View File
@@ -24,7 +24,8 @@ Once you're up and running consider the following reference material.
* [String Conversions](tostring.md#top)
**Running:**
* [Command line](command-line.md#top)
* [Command line reference](command-line.md#top)
* [Running specific section/generator](filtering-execution-path.md#top)
**Odds and ends:**
* [Frequently Asked Questions (FAQ)](faq.md#top)
+7
View File
@@ -66,17 +66,24 @@ test execution. Specifically it understands
* JUnit output path via `XML_OUTPUT_FILE`
* Test filtering via `TESTBRIDGE_TEST_ONLY`
* Test sharding via `TEST_SHARD_INDEX`, `TEST_TOTAL_SHARDS`, and `TEST_SHARD_STATUS_FILE`
* Creating a file to signal premature test exit via `TEST_PREMATURE_EXIT_FILE`
* Setting the RNG seed via `TEST_RANDOM_SEED`
> Support for `XML_OUTPUT_FILE` was [introduced](https://github.com/catchorg/Catch2/pull/2399) in Catch2 3.0.1
> Support for `TESTBRIDGE_TEST_ONLY` and sharding was introduced in Catch2 3.2.0
> Support for `TEST_PREMATURE_EXIT_FILE` and `TEST_RANDOM_SEED` was introduced in Catch2 3.11.0
This integration is enabled via either a [compile time configuration
option](configuration.md#bazel-support), or via `BAZEL_TEST` environment
variable set to "1".
> Support for `BAZEL_TEST` was [introduced](https://github.com/catchorg/Catch2/pull/2459) in Catch2 3.1.0
Note that if both the Bazel environment var and command line option for
something are used, the environment variable wins.
## Low-level tools
+33 -37
View File
@@ -32,6 +32,7 @@
[Test Sharding](#test-sharding)<br>
[Allow running the binary without tests](#allow-running-the-binary-without-tests)<br>
[Output verbosity](#output-verbosity)<br>
[Create file to guard against silent early termination](#create-file-to-guard-against-silent-early-termination)<br>
Catch works quite nicely without any command line options at all - but for those times when you want greater control the following options are available.
Click one of the following links to take you straight to that option - or scroll on to browse the available options.
@@ -51,6 +52,8 @@ Click one of the following links to take you straight to that option - or scroll
<a href="#reporting-timings"> ` -d, --durations`</a><br />
<a href="#input-file"> ` -f, --input-file`</a><br />
<a href="#run-section"> ` -c, --section`</a><br />
<a href="#path-filtering"> ` -g, --generator-index`</a><br />
<a href="#path-filtering"> ` -p, --path-filter`</a><br />
<a href="#filenames-as-tags"> ` -#, --filenames-as-tags`</a><br />
@@ -286,9 +289,10 @@ as follows:
| Option | `normal` (default) | `quiet` | `high` |
|--------------------|---------------------------------|---------------------|-----------------------------------------|
| `--list-tests` | Test names and tags | Test names only | Same as `normal`, plus source code line |
| `--list-tags` | Tags and counts | Same as `normal` | Same as `normal` |
| `--list-tags` | Tags and counts | Tags only | Same as `normal` |
| `--list-reporters` | Reporter names and descriptions | Reporter names only | Same as `normal` |
| `--list-listeners` | Listener names and descriptions | Same as `normal` | Same as `normal` |
| `--list-listeners` | Listener names and descriptions | Listener names only | Same as `normal` |
<a id="sending-output-to-a-file"></a>
## Sending output to a file
@@ -357,10 +361,13 @@ There are currently two warnings implemented:
// (e.g. `REQUIRE`) is encountered.
UnmatchedTestSpec // Fail test run if any of the CLI test specs did
// not match any tests.
InfiniteGenerators // Fail if GENERATE would run infinitely
```
> `UnmatchedTestSpec` was introduced in Catch2 3.0.1.
> `InfiniteGenerators` was introduced in Catch2 3.13.0
<a id="reporting-timings"></a>
## Reporting timings
@@ -528,45 +535,19 @@ Prints the command line arguments to stdout
<a id="run-section"></a>
## Specify the section to run
<a id="path-filtering"></a>
## Specify the section/generator element to run
<pre>-c, --section &lt;section name&gt;</pre>
<pre>-g, --generator-index &lt;index in generator&gt;</pre>
<pre>-p, --path-filter &lt;path filter spec&gt;</pre>
To limit execution to a specific section within a test case, use this option one or more times.
To narrow to sub-sections use multiple instances, where each subsequent instance specifies a deeper nesting level.
> The generator and generic path filtering was added in Catch2 3.13.0
E.g. if you have:
These arguments allow you to run specific section(s) in a test case, or
only get specific element from a generator. All the variants form a shared
stack of filters.
<pre>
TEST_CASE( "Test" ) {
SECTION( "sa" ) {
SECTION( "sb" ) {
/*...*/
}
SECTION( "sc" ) {
/*...*/
}
}
SECTION( "sd" ) {
/*...*/
}
}
</pre>
Then you can run `sb` with:
<pre>./MyExe Test -c sa -c sb</pre>
Or run just `sd` with:
<pre>./MyExe Test -c sd</pre>
To run all of `sa`, including `sb` and `sc` use:
<pre>./MyExe Test -c sa</pre>
There are some limitations of this feature to be aware of:
- Code outside of sections being skipped will still be executed - e.g. any set-up code in the TEST_CASE before the
start of the first section.</br>
- At time of writing, wildcards are not supported in section names.
- If you specify a section without narrowing to a test case first then all test cases will be executed
(but only matching sections within them).
[See the full documentation of path filtering for more details](filtering-execution-path.md#top)
<a id="filenames-as-tags"></a>
@@ -649,6 +630,21 @@ ignored.
Verbosity defaults to _normal_.
## Create file to guard against silent early termination
<pre>--premature-exit-guard-file &lt;path&gt;</pre>
> Introduced in Catch2 3.11.0
Tells Catch2 to create an empty file at specified path before the tests
start, and delete it after the tests finish. If the file is present after
the process stops, it can be assumed that the testing binary exited
prematurely, e.g. due to the OOM killer.
All directories in the path must already exist. If this option is used
and Catch2 cannot create the file (e.g. the location is not writable),
the test run will fail.
---
[Home](Readme.md#top)
+9 -5
View File
@@ -17,7 +17,7 @@
[Disabling deprecation warnings](#disabling-deprecation-warnings)<br>
[Overriding Catch's debug break (`-b`)](#overriding-catchs-debug-break--b)<br>
[Static analysis support](#static-analysis-support)<br>
[Experimental thread safety](#experimental-thread-safety)<br>
[Thread safety in assertions (and messages)](#thread-safety-in-assertions-and-messages)<br>
Catch2 is designed to "just work" as much as possible, and most of the
configuration options below are changed automatically during compilation,
@@ -29,7 +29,7 @@ with the same name.
## Prefixing Catch macros
CATCH_CONFIG_PREFIX_ALL // Prefix all macros with CATCH_
CATCH_CONFIG_PREFIX_MESSAGES // Prefix only INFO, UNSCOPED_INFO, WARN and CAPTURE
CATCH_CONFIG_PREFIX_MESSAGES // Prefix only message macros ((UNSCOPED_)INFO, WARN, (UNSCOPED_)CAPTURE)
To keep test code clean and uncluttered Catch uses short macro names (e.g. ```TEST_CASE``` and ```REQUIRE```). Occasionally these may conflict with identifiers from platform headers or the system under test. In this case the above identifier can be defined. This will cause all the Catch user macros to be prefixed with ```CATCH_``` (e.g. ```CATCH_TEST_CASE``` and ```CATCH_REQUIRE```).
@@ -316,17 +316,21 @@ no backwards compatibility guarantees._
are not meant to be runnable, only "scannable".
## Experimental thread safety
<a id="experimental-thread-safety"></a>
## Thread safety in assertions (and messages)
> Introduced in Catch2 3.9.0
> Made non-experimental in Catch2 3.12.0
Catch2 can optionally support thread-safe assertions, that means, multiple
user-spawned threads can use the assertion macros at the same time. Due
to the performance cost this imposes even on single-threaded usage, Catch2
defaults to non-thread-safe assertions.
CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
CATCH_CONFIG_THREAD_SAFE_ASSERTIONS // enables thread safe assertions
CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS // force-disables thread safe assertions
See [the documentation on thread safety in Catch2](thread-safety.md#top)
for details on which macros are safe and other notes.
+26
View File
@@ -48,6 +48,32 @@ If you are mutating the fixture instance from within the test case, and
want to keep doing so in the future, mark the mutated members as `mutable`.
### Section-only filtering with `-c/--section`
> Deprecated in Catch2 3.13.0
Currently, if you use only `-c/--section` parameters to decide which
sections to enter, the filtering ignores generators completely. In the
future, using only `-c/--section` will behave the same way as if you
specified the filters through the new `-p/--path-filter` parameter, which
means that generators are taken into account.
### Generator interfaces
#### Defaulted `UntypedGeneratorBase::isFinite()`
> Deprecated in Catch2 3.13.0
The `UntypedGeneratorBase` currently provides a default implementation
for `isFinite` that always returns `true`. This was done to keep backwards
compatibility with pre-existing generators, as infinite generators can
be diagnosed as errors in some cases.
In the future, all generators will be expected to override `isFinite`.
---
[Home](Readme.md#top)
+1 -1
View File
@@ -10,7 +10,7 @@ in-memory logs if they are not needed (the test case passed).
Unlike reporters, each registered event listener is always active. Event
listeners are always notified before reporter(s).
To write your own event listener, you should derive from `Catch::TestEventListenerBase`,
To write your own event listener, you should derive from `Catch::EventListenerBase`,
as it provides empty stubs for all reporter events, allowing you to
only override events you care for. Afterwards you have to register it
with Catch2 using `CATCH_REGISTER_LISTENER` macro, so that Catch2 knows
+222
View File
@@ -0,0 +1,222 @@
<a id="top"></a>
# How to run specific section/generator
> The generator and generic path filtering was added in Catch2 3.13.0
Catch2 supports picking specific path through a test case by filtering
sections and generator indices to run through. This is done by using one
of the three commandline parameters, one or more times.
```
-c, --section <section name>
-g, --generator-index <index in generator>
-p, --path-filter <path filter spec>
```
All the variants form a shared stack of filters, but if you use only
`-c`/`--section` form to specify section filters, you will get the old
behaviour, which does not affect generators at all. If you also use either
`-g`/`--generator-index`, or `-p`/`--path-filter`, you will get the new
behaviour, which can also filter generator elements.
Both the new and old filter behaviours include some potentially surprising
things:
* Code outside of sections being skipped will still be executed. E.g.
any setup code in the TEST_CASE that lives outside of sections.
* Path filters filter the prefix of the path. So if you specify single
filter, it affects only the top level sections/generator, with their
child sections/generators being unfiltered.
* Path filters are independent of test case selection, Catch2 will try
to follow the path filters in all selected test cases. This means
that if you specify path filters without a test case filter, Catch2
will try to apply the path filters inside every registered test case.
## Old behaviour
> The old behaviour was deprecated in Catch2 3.13.0
```
-c, --section <section name>
```
The argument to `-c`/`--section` can be any arbitrary string. When Catch2
is deciding whether to enter a section, it will check its trimmed name
against the appropriate trimmed section filter. If they are the same,
the section can be opened. If not, Catch2 will skip over that section.
### Examples
#### Simple section nesting
Given
```cpp
TEST_CASE( "foo" ) {
REQUIRE( true );
SECTION( "A" ) {
SECTION( "A1" ) { REQUIRE( true ); }
SECTION( "A2" ) { REQUIRE( true ); }
}
SECTION( "B" ) {
SECTION( "B1" ) { REQUIRE( true ); }
SECTION( "B2" ) { REQUIRE( true ); }
}
}
```
* `./tests foo -c A` runs section "A" and both of its subsections,
resulting in 4 assertions.
* `./tests foo -c A -c B` runs section "A", but none of its subsections,
resulting in 1 assertion (the one before "A").
* `./tests foo -c A -c A1` runs section "A" and only the "A1" subsection,
resulting in 2 assertions.
#### Sections with nested generators
Note that old behaviour completely _ignores_ generators. This means both
that they can't be filtered, but also that they aren't taken into account
for the filter depth. In other words, given
```cpp
TEST_CASE( "bar" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
SECTION( "B" ) {
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
}
```
* `./tests bar -c A` results in 2 assertions.
* `./tests bar -c B -c i=2` results in 4 assertions, because the whole
generator in section "B" has to be used up, but the dynamic section is
only entered when the generator returns 2 as the value for `i`.
* `./tests bar -c B -c i=4` results in 3 assertions, because the assertion
outside of section is executed every time the test case is entered, and
the generator forces the test case to rerun 3 times before it is used up,
even though the dynamic section will never be entered.
#### Section with sibling generators
For cases where sections have sibling generators, the filtering can get
even more surprising.
```cpp
TEST_CASE( "qux" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests qux -c A` results in **4** assertions, because section "A" is
entered once, but the sibling generator has to be exhausted, and
the first assertion is executed once per generator element.
* `./tests qux -c i=2` also results in 4 assertions. Once again,
the generator has to be exhausted and the dynamic section is entered
once.
## New behaviour
> The new behaviour was introduced in Catch2 3.13.0
```
-g, --generator-index <index in generator>
-p, --path-filter <path filter spec>
```
The argument to `-g`/`--generator-index` must be either a non-negative
number, which is interpreted as the index of the desired element from
the generator, or "\*", which allows all elements from the generator.
Providing index outside of the generator is an error.
The argument to `-p`/`--path-filter` must start with either "c:" for
a section filter, or with "g:" for a generator filter. Everything past
the colon is then parsed as either a section filter, or a generator filter.
Note that using `p`/`--path-filter` enables new filtering behaviour, even
if it is only used to add section filters.
There is another important difference between filtering out sections and
generators. A section can be left un-entered, but a generator always has
to be active. For this reason, if generator fails a filter
(e.g. there is a section filter at given depth instead), it has to stop
the execution of the test case. Currently, this is done via `SKIP()`
equivalent, causing the section to be considered skipped.
### Examples
#### Nested generators
```cpp
TEST_CASE( "waldo" ) {
auto i = GENERATE( 1, 10, 100 );
auto j = GENERATE( 2, 20, 200 );
CAPTURE( i, j );
REQUIRE( true );
}
```
* `./tests waldo -g 1` results in 3 assertions, with `i := 10`, because
the second nested generator is unfiltered.
* `./tests waldo -g 1 -g 2` results in 1 assertion, with `i := 10, j := 200`.
* `./tests waldo -g * -g 2` results in 3 assertions, all with `j := 200`.
* `./tests waldo -g 1 -g *` results in 3 assertions, all with `i := 10`.
* `./tests waldo -g 3` results in 1 **failed** assertion, because the first
generator does not have 3rd element.
* `./tests waldo -g * -g 3` results in 3 **failed** assertions, as the
second generator does not have 3rd element, but we have to exhaust the
first generator.
#### Generator with a nested dynamic section
```cpp
TEST_CASE( "grault" ) {
REQUIRE( true );
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests grault -p g:1` results in 2 assertions, as there is no filter
on the dynamic section.
* `./tests grault -p g:1 -p c:i=2` results in 2 assertions, as the filter
on the dynamic section matches the element given from the generator.
* `./tests grault -p g:1 -p c:i=3` results in 1 assertion, as the generator
is limited to only try `i := 2` and the dynamic section is filtered out.
#### Section with a sibling generator
Because generators have to stop test execution when they don't pass filter,
it is impossible to run only a section with sibling generator without
triggering a test case skip. Consider this test case from an earlier example:
```cpp
TEST_CASE( "qux" ) {
REQUIRE( true );
SECTION( "A" ) { REQUIRE( true ); }
auto i = GENERATE( 1, 2, 3 );
DYNAMIC_SECTION( "i=" << i ) {
REQUIRE( true );
}
}
```
* `./tests qux -p g:1` results in 2 assertions, as the dynamic section is
entered only once.
* `./tests qux -p g:1 -p c:i=1` results in 1 assertion, as the dynamic
section filter is incompatible with the generator filter.
* `./tests qux -p c:A` results in 2 assertions **and a skipped test case**.
This is because the generator is sibling to section "A", and thus reads
the same section filter. However, it is not a section and as thus cannot
proceed.
* `./tests qux -p c:i=2` results in 1 assertion **and a skipped test case**.
Once again, the first filter in the filter stack is a section filter,
and thus the generator cannot proceed.
Compare this with the old filter behaviour, where `./tests qux -c i=2`
would instead result in 4 assertions, because the generator would go
through all elements.
---
[Home](Readme.md#top)
+65 -2
View File
@@ -1,6 +1,12 @@
<a id="top"></a>
# Data Generators
**Contents**<br>
[Combining `GENERATE` and `SECTION`.](#combining-generate-and-section)<br>
[Provided generators](#provided-generators)<br>
[Generator interface](#generator-interface)<br>
[Other usage examples](#other-usage-examples)<br>
> Introduced in Catch2 2.6.0.
Data generators (also known as _data driven/parametrized test cases_)
@@ -106,7 +112,7 @@ a test case,
* 2 fundamental generators
* `SingleValueGenerator<T>` -- contains only single element
* `FixedValuesGenerator<T>` -- contains multiple elements
* 5 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`)
* 6 generic generators that modify other generators (defined in `catch2/generators/catch_generators_adapters.hpp`)
* `FilterGenerator<T, Predicate>` -- filters out elements from a generator
for which the predicate returns "false"
* `TakeGenerator<T>` -- takes first `n` elements from a generator
@@ -114,6 +120,7 @@ a test case,
* `MapGenerator<T, U, Func>` -- returns the result of applying `Func`
on elements from a different generator
* `ChunkGenerator<T>` -- returns chunks (inside `std::vector`) of n elements from a generator
* `ConcatGenerator<T>` -- returns elements from multiple generators as if they were one
* 2 random generators (defined in `catch2/generators/catch_generators_random.hpp`)
* `RandomIntegerGenerator<Integral>` -- generates random Integrals from range
* `RandomFloatGenerator<Float>` -- generates random Floats from range
@@ -125,6 +132,8 @@ a test case,
> `IteratorGenerator<T>` was introduced in Catch2 2.10.0.
> `ConcatGenerator<T>` was introduced in Catch2 3.13.0
The generators also have associated helper functions that infer their
type, making their usage much nicer. These are
@@ -142,6 +151,7 @@ type, making their usage much nicer. These are
* `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>`
* `cat(GeneratorWrapper<T>&&...)` for `ConcatGenerator<T>`
> `chunk()`, `random()` and both `range()` functions were introduced in Catch2 2.7.0.
@@ -149,6 +159,8 @@ type, making their usage much nicer. These are
> `range()` for floating point numbers has been introduced in Catch2 2.11.0
> `cat` has been introduced in Catch2 3.13.0
And can be used as shown in the example below to create a generator
that returns 100 odd random number:
@@ -252,9 +264,34 @@ struct IGenerator : GeneratorUntypedBase {
// Returns user-friendly string showing the current generator element
// Does not have to be overridden, IGenerator provides default implementation
virtual std::string stringifyImpl() const;
/**
* Customization point for `skipToNthElement`
*
* Does not have to be overridden, there is a default implementation.
* Can be overridden for better performance.
*
* If there are not enough elements, shall throw an error.
*
* Going backwards is not supported.
*/
virtual void skipToNthElementImpl( std::size_t n );
/**
* Returns true if calls to `next` will eventually return false
*
* Note that for backwards compatibility this is currently defaulted
* to return `true`, but in the future all generators will have to
* provide their own implementation.
*/
virtual bool isFinite() const = 0;
};
```
> `skipToNthElementImpl` was added in Catch2 3.13.0
> `isFinite` was added in Catch2 3.13.0
However, to be able to use your custom generator inside `GENERATE`, it
will need to be wrapped inside a `GeneratorWrapper<T>`.
`GeneratorWrapper<T>` is a value wrapper around a
@@ -275,9 +312,35 @@ 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.
* If empty generator **is not** an error, use the [`SKIP` macro](skipping-passing-failing.md#skipping-test-cases-at-runtime) in constructor.
## Other usage examples
### Adding a reproducer to random tests
If you use generators to generate random inputs for testing, you might
want to combine them with specific inputs, e.g. reproducers for previously
found issues.
Because `GENERATE` accepts multiple values/generators, the basic case is simple:
```cpp
const int input = GENERATE(1, 2, take(10, random(10, 10'000'000)));
```
This will set `input` first to "1", then to "2", and then to 10 random
integers.
But if you process the random inputs further (e.g. via `map`), you can't
rely on `GENERATE`'s support for multiple generators. In that case, you
have to use the `cat` generator combinator.
```cpp
const auto input = GENERATE(
map( foo,
cat( value( 4 ), take( 10, random( 10, 10'000'000 ) ) ) ) );
```
This will set `input` first to `foo(4)`, before transforming the 10 random
integers through `foo`.
---
+22 -11
View File
@@ -26,19 +26,22 @@ started" and "Section B", while the third one will only report "Test case
started" as the extra info.
## Logging without local scope
## Logging outside of current scope
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
> `UNSCOPED_INFO` was [introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
`UNSCOPED_INFO` is similar to `INFO` with two key differences:
> `UNSCOPED_CAPTURE` was introduced in Catch2 3.13.0.
- Lifetime of an unscoped message is not tied to its own scope.
The `UNSCOPED_X` macros are similar to their plain `X` macro counterparts,
with two key differences:
- The lifetime of an unscoped message is not tied to its own scope.
- An unscoped message can be reported by the first following assertion only, regardless of the result of that assertion.
In other words, lifetime of `UNSCOPED_INFO` is limited by the following assertion (or by the end of test case/section, whichever comes first) whereas lifetime of `INFO` is limited by its own scope.
These differences make this macro useful for reporting information from helper functions or inner scopes. An example:
In other words, the `UNSCOPED_X` macros are useful to add extra information
to the next assertion, e.g. from helper functions or inner scopes.
An example:
```cpp
void print_some_info() {
UNSCOPED_INFO("Info from helper");
@@ -83,9 +86,16 @@ Second info
Second unscoped info
```
Note that unscoped messages are not passed between test cases, even if
there were no assertions between them.
## Streaming macros
All these macros allow heterogeneous sequences of values to be streaming using the insertion operator (```<<```) in the same way that std::ostream, std::cout, etc support it.
Apart from `CAPTURE` (and its close sibling, `UNSCOPED_CAPTURE`), message
macros support gradual streaming of messages and values in the same way
that the standard streams do.
E.g.:
```c++
@@ -99,9 +109,6 @@ These macros come in three forms:
The message is logged to a buffer, but only reported with next assertions that are logged. This allows you to log contextual information in case of failures which is not shown during a successful test run (for the console reporter, without -s). Messages are removed from the buffer at the end of their scope, so may be used, for example, in loops.
_Note that in Catch2 2.x.x `INFO` can be used without a trailing semicolon as there is a trailing semicolon inside macro.
This semicolon will be removed with next major version. It is highly advised to use a trailing semicolon after `INFO` macro._
**UNSCOPED_INFO(** _message expression_ **)**
> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
@@ -128,6 +135,10 @@ AS `FAIL`, but does not abort the test
**CAPTURE(** _expression1_, _expression2_, ... **)**
**UNSCOPED_CAPTURE(** _expression1_, _expression2_, ... **)**
> `UNSCOPED_CAPTURE` was introduced in Catch2 3.13.0.
Sometimes you just want to log a value of variable, or expression. For
convenience, we provide the `CAPTURE` macro, that can take a variable,
or an expression, and prints out that variable/expression and its value
+37
View File
@@ -6,6 +6,7 @@
[Built-in matchers](#built-in-matchers)<br>
[Writing custom matchers (old style)](#writing-custom-matchers-old-style)<br>
[Writing custom matchers (new style)](#writing-custom-matchers-new-style)<br>
[Constexpr matchers](#constexpr-matchers)<br>
Matchers, as popularized by the [Hamcrest](https://en.wikipedia.org/wiki/Hamcrest)
framework are an alternative way to write assertions, useful for tests
@@ -471,6 +472,42 @@ and new style matchers arbitrarily.
> `MatcherGenericBase` lives in `catch2/matchers/catch_matchers_templated.hpp`
## Constexpr matchers
> Support for constexpr matchers was introduced in Catch2 3.15.0
When compiled for C++20, the new-style matchers (can) support `constexpr`
matching, albeit not `constexpr` stringification. The matcher combinators
require C++26 (or at least P2738) to be `constexpr` compatible.
This can be used together with the `STATIC_REQUIRE_THAT` macro to write
matcher-based static assertions like this:
```cpp
TEST_CASE("Constexpr support for matchers", "[constexpr][matchers]") {
STATIC_REQUIRE_THAT( 1, MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() || MatchAll() );
STATIC_REQUIRE_THAT( 1, !!MatchAll() );
}
```
### First party constexpr matchers
Some (but not all) of Catch2's generic matchers support `constexpr`
matching. Currently, this includes:
* `IsEmpty()`
* `SizeIs(size_t target_size)`, `SizeIs(Matcher size_matcher)`
* `AllMatch(Matcher element_matcher)`
* `AnyMatch(Matcher element_matcher)`
* `NoneMatch(Matcher element_matcher)`
* `AllTrue()`, `AnyTrue()`, `NoneTrue()`
* `Contains(T&& target_element, Comparator = std::equal_to<>{})`
* `Contains(Matcher element_matcher)`
* `RangeEquals(TargetRangeLike&&, Comparator = std::equal_to<>{})`
* `UnorderedRangeEquals(TargetRangeLike&&, Comparator = std::equal_to<>{})`
---
[Home](Readme.md#top)
+24
View File
@@ -91,6 +91,30 @@ TEST_CASE("STATIC_CHECK showcase", "[traits]") {
}
```
* `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT`
> `STATIC_REQUIRE_THAT` and `STATIC_CHECK_THAT` was introduced in Catch2 3.15.0
`STATIC_{REQUIRE,CHECK}_THAT` are analogous to `STATIC_{REQUIRE,CHECK}`,
but for matchers. They are always defined, even if the current compiler
does not support `constexpr` matchers, but in that case the compilation
will always fail.
Just like `STATIC_{REQUIRE,CHECK}`, `STATIC_{REQUIRE,CHECK}_THAT` can be
delayed into runtime through the `CATCH_CONFIG_RUNTIME_STATIC_REQUIRE`
configuration option.
Example:
```cpp
TEST_CASE("Constexpr support for matchers", "[constexpr][matchers]") {
STATIC_REQUIRE_THAT( 1, MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() && MatchAll() );
STATIC_REQUIRE_THAT( 1, MatchAll() || MatchAll() );
STATIC_REQUIRE_THAT( 1, !!MatchAll() );
}
```
## Test case related macros
* `REGISTER_TEST_CASE`
+1 -1
View File
@@ -65,7 +65,7 @@ int main( int argc, char* argv[] ) {
returnCode = session.run();
// returnCode encodes the type of error that occured. See the
// returnCode encodes the type of error that occurred. See the
// integer constants in catch_session.hpp for more information
// on what each return code means.
+123
View File
@@ -2,6 +2,12 @@
# Release notes
**Contents**<br>
[3.15.0](#3150)<br>
[3.14.0](#3140)<br>
[3.13.0](#3130)<br>
[3.12.0](#3120)<br>
[3.11.0](#3110)<br>
[3.10.0](#3100)<br>
[3.9.1](#391)<br>
[3.9.0](#390)<br>
[3.8.1](#381)<br>
@@ -69,6 +75,123 @@
[Even Older versions](#even-older-versions)<br>
## 3.15.0
### Fixes
* Fixed ambiguous overload issue with `std::optional<T>` in C++26. (#3095)
* Since `std::optional` was turned into a range, the partial specialization of `StringMaker` for range-like types and for `std::optional<T>` were in conflict.
### Improvements
* Simplified `CATCH_REGISTER_ENUM` internals for faster compilation and retrieval.
* Successful assertion are slightly (1-2%) faster.
* Generic (new-style) matchers support constexpr matching in C++20.
* Combining matchers requires C++26.
* Catch2-provided generic matchers are all constexpr enabled.
* Added `STATIC_REQUIRE_THAT` for compile-time matcher assertions.
* This requires the compiler to support enough `constexpr` for matchers, see above.
## 3.14.0
### Fixes
* Added missing `<cstdint>` includes. (#3078)
* Fixed suppression of empty variadic macro arguments warning on Clang <19. (#3085)
* Fixed `catch_discover_tests` failing during `PRE_TEST` discovery if a target does not have discoverable tests. (#3075)
* Fixed build of the main library failing with `CATCH_CONFIG_PREFIX_ALL` defined. (#3087)
* JUnit reporter outputs single failed (errored/skipped) assertion per test case. (#1919)
### Improvements
* The default implementation of `--list-tags` and `--list-listeners` has a quiet variant.
* Suppressed the new Clang warning about `__COUNTER__` usage. (#3076)
* Line-wrapping counts utf-8 codepoints instead of bytes. (#1022, #3086)
* Combining character sequences are still miscounted, but Catch2 does not aim to fully support Unicode.
## 3.13.0
### Fixes
* `--benchmark-samples 0` no longer hard crashes (#3056)
* The CLI validation fails instead.
* Fixed warning suppression macros being doubly defined when using Clang on Windows (#3060)
### Improvements
* Suppressed static analysis 26426 diagnostic for MSVC (#3057)
* Renamed the internal deprecation macro from `DEPRECATED` to `CATCH_DEPRECATED` to avoid conflicts (#3058)
* Added `UNSCOPED_CAPTURE` macro (#2954)
* Added `ConcatGenerator` to combine multiple separate generator into one
* The short form is `cat`
* Generators can now jump forward to nth element efficiently
* Custom generators that can jump forward efficiently should override `skipToNthElementImpl`
* Generators can declare themselves infinite
* The generator base defaults to declaring itself finite for backwards compatibility
* Custom generators should override `isFinite()` to return the proper value
* Added `--warn InfiniteGenerators` to error out on `GENERATE` being given an infinite generator
* Extended options for section filtering from CLI to include generators
* The user can specify which element from the generator to use in the test case
* See documentation for how the new filters work and how they can be specified
* `MapGenerator` only calls the mapping function if the output will be used
## 3.12.0
### Fixes
* Fixed unscoped messages after a passing fast-pathed assertion being lost.
* Fixed the help string for `--order` to mention random order as the default. (#3045)
* Fixed small documentation typos. (#3039)
* Fixed compilation with `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS` for older C++ standards.
* Fixed a thread-safety issue with message macros being used too early after the process starts.
* Fixed automatic configuration to properly handle PlayStation platform. (#3054)
* **Fixed the _weird_ behaviour of section filtering when specifying multiple filters.** (#3038)
* See #3038 for more details.
### Improvements
* Added `lifetimebound` attribute to various places.
* As an example, compiler that supports lifetime analysis will now diagnose invalid use of Matcher combinators.
* Minor compile-time improvements to stringification. (#3028)
* `std::tuple` printer does not recurse.
* Some implementation details were outlined into the cpp file.
* Global variables will only be marked with `thread_local` in thread-safe builds. (#3044)
### Miscellaneous
* The thread safety support is no longer experimental.
* The new CMake option and C++ define is now `CATCH_CONFIG_THREAD_SAFE_ASSERTIONS`.
## 3.11.0
### Fixes
* Fixed building on non-desktop GDK platforms (#3029)
* Fixed message macros being susceptible to race in specific scenario (#3031)
* Catch2's SEH filter will call the previously installed filter after reporting the error (#3033)
### Improvements
* Handling of scoped messages (e.g. `CAPTURE`) is a bit faster.
* Better out-of-the-box support for QNX (#2953)
* Improved performance of assertions by up-to 10%
* Release mode assertion fast-path sees the biggest improvement.
* Faster processing of non-escaped strings in `--invisibles` mode.
* Added support for Bazel's `TEST_RANDOM_SEED` env var (#3021)
* Added support for Bazel's `TEST_PREMATURE_EXIT_FILE` env var (#3020)
* This creates a file that is deleted if the tests exit normally, but stays around if the process dies unexpectedly.
* This functionality is also exposed through CLI as `--premature-exit-guard-file`
### Miscellaneous
* **[Tuple.app](https://tuple.app/catch2) has sponsored Catch2**
## 3.10.0
### Fixes
* pkg-config files will take `DESTDIR` env var into account when selecting install destination (#3006, #3019)
* Changed `filter` to store the provided predicate by value (#3002, #3005)
* This is done to avoid dangling-by-default behaviour when `filter` is used inside `GENERATE_COPY`/`GENERATE_REF`.
### Improvements
* Escaping XML and JSON output is faster when the strings do not need escaping.
* The improvement starts at about 3x throughput, up to 10x for long strings.
* Message macros (`INFO`, `CAPTURE`, `WARN`, `SUCCEED`, etc) are now thread safe.
## 3.9.1
### Fixes
+1 -1
View File
@@ -87,7 +87,7 @@ TEST_CASE("complex test case") {
```
This test case will report 5 passing assertions; one for each of the three
values in section `a1`, and then two in section `a2`, from values 2 and 4.
values in section `a1`, and then two in section `a2`, from values 2 and 6.
Note that as soon as one section is skipped, the entire test case will
be reported as _skipped_ (unless there is a failing assertion, in which
+118 -19
View File
@@ -2,7 +2,9 @@
# Thread safety in Catch2
**Contents**<br>
[Using assertion macros from multiple threads](#using-assertion-macros-from-multiple-threads)<br>
[Using assertion macros from spawned threads](#using-assertion-macros-from-spawned-threads)<br>
[Assertion-like message macros and spawned threads](#assertion-like-message-macros-and-spawned-threads)<br>
[Message macros and spawned threads](#message-macros-and-spawned-threads)<br>
[examples](#examples)<br>
[`STATIC_REQUIRE` and `STATIC_CHECK`](#static_require-and-static_check)<br>
[Fatal errors and multiple threads](#fatal-errors-and-multiple-threads)<br>
@@ -10,17 +12,18 @@
> Thread safe assertions were introduced in Catch2 3.9.0
Thread safety in Catch2 is currently limited to all the assertion macros.
Interacting with benchmark macros, message macros (e.g. `INFO` or `CAPTURE`),
sections macros, generator macros, or test case macros is not thread-safe.
The message macros are likely to be made thread-safe in the future, but
the way sections define test runs is incompatible with user being able
to spawn threads arbitrarily, thus that limitation is here to stay.
Thread safety in Catch2 is currently limited to all the assertion macros,
and to message or message-adjacent macros (e.g. `INFO` or `WARN`).
**Important: thread safety in Catch2 is [opt-in](configuration.md#experimental-thread-safety)**
Interacting with benchmark macros, sections macros, generator macros, or
test case macros is not thread-safe. The way sections define paths through
the test is incompatible with user spawning threads arbitrarily, so this
limitation is here to stay.
**Important: thread safety in Catch2 is [opt-in](configuration.md#thread-safety)**
## Using assertion macros from multiple threads
## Using assertion macros from spawned threads
The full set of Catch2's runtime assertion macros is thread-safe. However,
it is important to keep in mind that their semantics might not support
@@ -30,7 +33,7 @@ Specifically, the `REQUIRE` family of assertion macros have semantics
of stopping the test execution on failure. This is done by throwing
an exception, but since the user-spawned thread will not have the test-level
try-catch block ready to catch the test failure exception, failing a
`REQUIRE` assertion inside this thread will terminate the process.
`REQUIRE` assertion inside user-spawned thread will terminate the process.
The `CHECK` family of assertions does not have this issue, because it
does not try to stop the test execution.
@@ -38,16 +41,36 @@ does not try to stop the test execution.
Note that `CHECKED_IF` and `CHECKED_ELSE` are also thread safe (internally
they are assertion macro + an if).
**`SKIP()`, `FAIL()`, `SUCCEED()` are not assertion macros, and are not
thread-safe.**
## Assertion-like message macros and spawned threads
> Assertion-like messages were made thread safe in Catch2 3.10.0
Similarly to assertion macros, not all assertion-like message macros can
be used from spawned thread.
`SKIP` and `FAIL` macros stop the test execution. Just like with `REQUIRE`,
this means that they cannot be used inside user-spawned threads. `SUCCEED`,
`FAIL_CHECK` and `WARN` do not attempt to stop the test execution and
thus can be used from any thread.
## Message macros and spawned threads
> Message macros were made thread safe in Catch2 3.10.0
Macros that add extra messages to following assertion, such as `INFO`
or `CAPTURE`, are all thread safe and can be used in any thread. Note
that these messages are per-thread, and thus `INFO` inside a user-spawned
thread will not be seen by the main thread, and vice versa.
## examples
### `REQUIRE` from main thread, `CHECK` from spawned threads
### `REQUIRE` from the main thread, `CHECK` from spawned threads
```cpp
TEST_CASE( "Failed REQUIRE in main thread is fine" ) {
TEST_CASE( "Failed REQUIRE in the main thread is fine" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
@@ -85,7 +108,7 @@ TEST_CASE( "Successful REQUIRE in spawned thread is fine" ) {
This will also work as expected, because the `REQUIRE` is successful.
```cpp
TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) {
TEST_CASE( "Failed REQUIRE in spawned thread kills the process" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
@@ -99,12 +122,88 @@ TEST_CASE( "Failed REQUIRE in spawned thread is fine" ) {
This will fail catastrophically and terminate the process.
### INFO across threads
```cpp
TEST_CASE( "messages don't cross threads" ) {
std::jthread t1( [&]() {
for ( size_t i = 0; i < 100; ++i ) {
INFO( "spawned thread #1" );
CHECK( 1 == 1 );
}
} );
std::thread t2( [&]() {
for (size_t i = 0; i < 100; ++i) {
UNSCOPED_INFO( "spawned thread #2" );
}
} );
for (size_t i = 0; i < 100; ++i) {
CHECK( 1 == 2 );
}
}
```
None of the failed checks will show the "spawned thread #1" message, as
that message is for the `t1` thread. If the reporter shows passing
assertions (e.g. due to the tests being run with `-s`), you will see the
"spawned thread #1" message alongside the passing `CHECK( 1 == 1 )` assertion.
The message "spawned thread #2" will never be shown, because there are no
assertions in `t2`.
### FAIL/SKIP from the main thread
```cpp
TEST_CASE( "FAIL in the main thread is fine" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
for (size_t i = 0; i < 10; ++i) {
CHECK( true );
CHECK( false );
}
} );
}
FAIL();
}
```
This will work as expected, that is, the process will finish running
normally, the test case will fail and there will be 321 total assertions,
160 passing and 161 failing (`FAIL` counts as failed assertion).
However, when the main thread hits `FAIL`, it will wait for the other
threads to finish due to `std::jthread`'s destructor joining the spawned
thread. Due to this, using `SKIP` is not recommended once more threads
are spawned; while the main thread will bail from the test execution,
the spawned threads will keep running and may fail the test case.
### FAIL/SKIP from spawned threads
```cpp
TEST_CASE( "FAIL/SKIP in spawned thread kills the process" ) {
std::vector<std::jthread> threads;
for ( size_t t = 0; t < 16; ++t) {
threads.emplace_back( []() {
for (size_t i = 0; i < 10'000; ++i) {
FAIL();
}
} );
}
}
```
As with failing `REQUIRE`, both `FAIL` and `SKIP` in spawned threads
terminate the process.
## `STATIC_REQUIRE` and `STATIC_CHECK`
None of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and
`STATIC_CHECK_FALSE` are currently thread safe. This might be surprising
given that they are a compile-time checks, but they also rely on the
message macros to register the result with reporter at runtime.
All of `STATIC_REQUIRE`, `STATIC_REQUIRE_FALSE`, `STATIC_CHECK`, and
`STATIC_CHECK_FALSE` are thread safe in the delayed evaluation configuration.
## Fatal errors and multiple threads
+18
View File
@@ -39,6 +39,24 @@ public:
current_number = m_dist(m_rand);
return true;
}
bool isFinite() const override { return false; }
// Note: this improves the performance only a bit, but it is here
// to show how you can override the skip functionality.
void skipToNthElementImpl( std::size_t n ) override {
auto current_index = currentElementIndex();
assert(current_index <= n);
// We cannot jump forward the underlying generator directly,
// because we do not know how many bits each distributed number
// would consume to be generated.
for (; current_index < n; ++current_index) {
(void)m_dist(m_rand);
}
// We do not have to touch the current element index; it is handled
// by the base class.
}
};
// Avoids -Wweak-vtables
+2
View File
@@ -40,6 +40,8 @@ public:
bool next() override {
return !!std::getline(m_stream, m_line);
}
bool isFinite() const override { return true; }
};
std::string const& LineGenerator::get() const {
+1
View File
@@ -146,6 +146,7 @@ function(catch_discover_tests_impl)
# Exit early if no tests are detected
if(num_tests STREQUAL "0")
file(WRITE "${_CTEST_FILE}" "")
return()
endif()
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -9,6 +9,7 @@
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <cstdint>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
+1
View File
@@ -13,6 +13,7 @@
#include <string>
#include <string_view>
#include <cstdint>
template<class Callback>
+1 -1
View File
@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
version: '3.9.1', # CML version placeholder, don't delete
version: '3.15.0', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.54.1',
)
+7 -3
View File
@@ -87,7 +87,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_decomposer.hpp
${SOURCES_DIR}/internal/catch_deprecation_macro.hpp
${SOURCES_DIR}/internal/catch_enforce.hpp
${SOURCES_DIR}/internal/catch_enum_values_registry.hpp
${SOURCES_DIR}/internal/catch_enum_info.hpp
${SOURCES_DIR}/internal/catch_errno_guard.hpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
@@ -98,6 +98,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_jsonwriter.hpp
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_lifetimebound.hpp
${SOURCES_DIR}/internal/catch_list.hpp
${SOURCES_DIR}/internal/catch_logical_traits.hpp
${SOURCES_DIR}/internal/catch_message_info.hpp
@@ -107,6 +108,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_optional.hpp
${SOURCES_DIR}/internal/catch_output_redirect.hpp
${SOURCES_DIR}/internal/catch_parse_numbers.hpp
${SOURCES_DIR}/internal/catch_path_filter.hpp
${SOURCES_DIR}/internal/catch_platform.hpp
${SOURCES_DIR}/internal/catch_polyfills.hpp
${SOURCES_DIR}/internal/catch_preprocessor.hpp
@@ -139,6 +141,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_test_registry.hpp
${SOURCES_DIR}/internal/catch_test_spec_parser.hpp
${SOURCES_DIR}/internal/catch_textflow.hpp
${SOURCES_DIR}/internal/catch_thread_local.hpp
${SOURCES_DIR}/internal/catch_thread_support.hpp
${SOURCES_DIR}/internal/catch_to_string.hpp
${SOURCES_DIR}/internal/catch_uncaught_exceptions.hpp
@@ -178,7 +181,7 @@ set(IMPL_SOURCES
${SOURCES_DIR}/internal/catch_debugger.cpp
${SOURCES_DIR}/internal/catch_decomposer.cpp
${SOURCES_DIR}/internal/catch_enforce.cpp
${SOURCES_DIR}/internal/catch_enum_values_registry.cpp
${SOURCES_DIR}/internal/catch_enum_info.cpp
${SOURCES_DIR}/internal/catch_errno_guard.cpp
${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
@@ -224,7 +227,6 @@ set(INTERFACE_HEADERS
${SOURCES_DIR}/interfaces/catch_interfaces_all.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_capture.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_config.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_enum_values_registry.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_exception.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_generatortracker.hpp
${SOURCES_DIR}/interfaces/catch_interfaces_registry_hub.hpp
@@ -253,11 +255,13 @@ set(GENERATOR_HEADERS
${SOURCES_DIR}/generators/catch_generators_all.hpp
${SOURCES_DIR}/generators/catch_generators_random.hpp
${SOURCES_DIR}/generators/catch_generators_range.hpp
${SOURCES_DIR}/generators/catch_generators_throw.hpp
)
set(GENERATOR_SOURCES
${SOURCES_DIR}/generators/catch_generator_exception.cpp
${SOURCES_DIR}/generators/catch_generators.cpp
${SOURCES_DIR}/generators/catch_generators_random.cpp
${SOURCES_DIR}/generators/catch_generators_throw.cpp
)
set(GENERATOR_FILES ${GENERATOR_HEADERS} ${GENERATOR_SOURCES})
@@ -36,11 +36,11 @@ namespace Catch {
samples.data(), samples.data() + samples.size() );
auto wrap_estimate = [](Estimate<double> e) {
return Estimate<FDuration> {
FDuration(e.point),
FDuration(e.lower_bound),
FDuration(e.upper_bound),
e.confidence_interval,
return Estimate<FDuration>{
FDuration( e.point ),
FDuration( e.lower_bound ),
FDuration( e.upper_bound ),
e.confidence_interval,
};
};
std::vector<FDuration> samples2;
@@ -41,7 +41,7 @@ namespace Catch {
callable& operator=(callable&&) = default;
};
template <typename Fun>
struct model : public callable {
struct model final : public callable {
model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
model(Fun const& fun_) : fun(fun_) {}
@@ -8,6 +8,7 @@
#ifndef CATCH_BENCHMARK_STATS_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.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
@@ -8,14 +8,16 @@
#ifndef CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#define CATCH_BENCHMARK_STATS_FWD_HPP_INCLUDED
#include <catch2/benchmark/catch_clock.hpp>
namespace Catch {
namespace Detail {
struct DummyTemplateArgPlaceholder;
}
// 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 = Benchmark::FDuration>
// we can prevent multiple declarations in dependencies
template <typename Duration = Detail::DummyTemplateArgPlaceholder>
struct BenchmarkStats;
} // end namespace Catch
@@ -14,7 +14,7 @@
namespace Catch {
namespace Benchmark {
namespace Detail {
struct optimized_away_error : std::exception {
struct optimized_away_error final : std::exception {
const char* what() const noexcept override;
};
+4 -1
View File
@@ -68,7 +68,7 @@
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/internal/catch_deprecation_macro.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_errno_guard.hpp>
#include <catch2/internal/catch_exception_translator_registry.hpp>
#include <catch2/internal/catch_fatal_condition_handler.hpp>
@@ -79,6 +79,7 @@
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <catch2/internal/catch_message_info.hpp>
@@ -88,6 +89,7 @@
#include <catch2/internal/catch_optional.hpp>
#include <catch2/internal/catch_output_redirect.hpp>
#include <catch2/internal/catch_parse_numbers.hpp>
#include <catch2/internal/catch_path_filter.hpp>
#include <catch2/internal/catch_platform.hpp>
#include <catch2/internal/catch_polyfills.hpp>
#include <catch2/internal/catch_preprocessor.hpp>
@@ -121,6 +123,7 @@
#include <catch2/internal/catch_test_registry.hpp>
#include <catch2/internal/catch_test_spec_parser.hpp>
#include <catch2/internal/catch_textflow.hpp>
#include <catch2/internal/catch_thread_local.hpp>
#include <catch2/internal/catch_thread_support.hpp>
#include <catch2/internal/catch_to_string.hpp>
#include <catch2/internal/catch_uncaught_exceptions.hpp>
+33 -4
View File
@@ -92,6 +92,10 @@ namespace Catch {
lhs.customOptions == rhs.customOptions;
}
bool operator==( PathFilter const& lhs, PathFilter const& rhs ) {
return lhs.type == rhs.type && lhs.filter == rhs.filter;
}
Config::Config( ConfigData const& data ):
m_data( data ) {
// We need to trim filter specs to avoid trouble with superfluous
@@ -101,9 +105,6 @@ namespace Catch {
for (auto& elem : m_data.testsOrTags) {
elem = trim(elem);
}
for (auto& elem : m_data.sectionsToRun) {
elem = trim(elem);
}
// Insert the default reporter if user hasn't asked for a specific one
if ( m_data.reporterSpecifications.empty() ) {
@@ -119,6 +120,8 @@ namespace Catch {
m_data.reporterSpecifications.push_back( std::move( *parsed ) );
}
// Reading bazel env vars can change some parts of the config data,
// so we have to process the bazel env before acting on the config.
if ( enableBazelEnvSupport() ) {
readBazelEnvVars();
}
@@ -167,7 +170,8 @@ namespace Catch {
bool Config::listListeners() const { return m_data.listListeners; }
std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
std::vector<PathFilter> const& Config::getPathFilters() const { return m_data.pathFilters; }
bool Config::useNewFilterBehaviour() const { return m_data.useNewPathFilteringBehaviour; }
std::vector<ReporterSpec> const& Config::getReporterSpecs() const {
return m_data.reporterSpecifications;
@@ -183,6 +187,8 @@ namespace Catch {
bool Config::showHelp() const { return m_data.showHelp; }
std::string const& Config::getExitGuardFilePath() const { return m_data.prematureExitGuardFilePath; }
// IConfig interface
bool Config::allowThrows() const { return !m_data.noThrow; }
StringRef Config::name() const { return m_data.name.empty() ? m_data.processName : m_data.name; }
@@ -193,6 +199,9 @@ namespace Catch {
bool Config::warnAboutUnmatchedTestSpecs() const {
return !!( m_data.warnings & WarnAbout::UnmatchedTestSpec );
}
bool Config::warnAboutInfiniteGenerators() const {
return !!( m_data.warnings & WarnAbout::InfiniteGenerator );
}
bool Config::zeroTestsCountAsSuccess() const { return m_data.allowZeroTests; }
ShowDurations Config::showDurations() const { return m_data.showDurations; }
double Config::minDuration() const { return m_data.minDuration; }
@@ -244,6 +253,26 @@ namespace Catch {
m_data.shardCount = bazelShardOptions->shardCount;
}
}
const auto bazelExitGuardFile = Detail::getEnv( "TEST_PREMATURE_EXIT_FILE" );
if (bazelExitGuardFile) {
m_data.prematureExitGuardFilePath = bazelExitGuardFile;
}
const auto bazelRandomSeed = Detail::getEnv( "TEST_RANDOM_SEED" );
if ( bazelRandomSeed ) {
auto parsedSeed = parseUInt( bazelRandomSeed, 0 );
if ( !parsedSeed ) {
// Currently we handle issues with parsing other Bazel Env
// options by warning and ignoring the issue. So we do the
// same for random seed option.
Catch::cerr()
<< "Warning: could not parse 'TEST_RANDOM_SEED' ('"
<< bazelRandomSeed << "') as proper seed.\n";
} else {
m_data.rngSeed = *parsedSeed;
}
}
}
} // end namespace Catch
+10 -2
View File
@@ -12,6 +12,7 @@
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <catch2/internal/catch_path_filter.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_random_seed_generation.hpp>
#include <catch2/internal/catch_reporter_spec_parser.hpp>
@@ -86,7 +87,10 @@ namespace Catch {
std::vector<ReporterSpec> reporterSpecifications;
std::vector<std::string> testsOrTags;
std::vector<std::string> sectionsToRun;
std::vector<PathFilter> pathFilters;
bool useNewPathFilteringBehaviour = false;
std::string prematureExitGuardFilePath;
};
@@ -107,19 +111,23 @@ namespace Catch {
getProcessedReporterSpecs() const;
std::vector<std::string> const& getTestsOrTags() const override;
std::vector<std::string> const& getSectionsToRun() const override;
std::vector<PathFilter> const& getPathFilters() const override;
bool useNewFilterBehaviour() const override;
TestSpec const& testSpec() const override;
bool hasTestFilters() const override;
bool showHelp() const;
std::string const& getExitGuardFilePath() const;
// IConfig interface
bool allowThrows() const override;
StringRef name() const override;
bool includeSuccessfulResults() const override;
bool warnAboutMissingAssertions() const override;
bool warnAboutUnmatchedTestSpecs() const override;
bool warnAboutInfiniteGenerators() const override;
bool zeroTestsCountAsSuccess() const override;
ShowDurations showDurations() const override;
double minDuration() const override;
+24 -17
View File
@@ -5,8 +5,8 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/catch_message.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
@@ -19,28 +19,28 @@ namespace Catch {
ScopedMessage::ScopedMessage( MessageBuilder&& builder ):
m_info( CATCH_MOVE(builder.m_info) ) {
m_info.message = builder.m_stream.str();
getResultCapture().pushScopedMessage( m_info );
m_messageId( builder.m_info.sequence ) {
MessageInfo info( CATCH_MOVE( builder.m_info ) );
info.message = builder.m_stream.str();
Detail::pushScopedMessage( CATCH_MOVE( info ) );
}
ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
m_info( CATCH_MOVE( old.m_info ) ) {
m_messageId( old.m_messageId ) {
old.m_moved = true;
}
ScopedMessage::~ScopedMessage() {
if ( !m_moved ){
getResultCapture().popScopedMessage(m_info);
}
if ( !m_moved ) { Detail::popScopedMessage( m_messageId ); }
}
Capturer::Capturer( StringRef macroName,
SourceLineInfo const& lineInfo,
ResultWas::OfType resultType,
StringRef names ):
m_resultCapture( getResultCapture() ) {
StringRef names,
bool isScoped):
m_isScoped(isScoped) {
auto trimmed = [&] (size_t start, size_t end) {
while (names[start] == ',' || isspace(static_cast<unsigned char>(names[start]))) {
++start;
@@ -86,8 +86,8 @@ namespace Catch {
case ',':
if (start != pos && openings.empty()) {
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, pos));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, pos);
m_messages.back().message += " := "_sr;
start = pos;
}
break;
@@ -96,19 +96,26 @@ namespace Catch {
}
assert(openings.empty() && "Mismatched openings");
m_messages.emplace_back(macroName, lineInfo, resultType);
m_messages.back().message = static_cast<std::string>(trimmed(start, names.size() - 1));
m_messages.back().message += " := ";
m_messages.back().message += trimmed(start, names.size() - 1);
m_messages.back().message += " := "_sr;
}
Capturer::~Capturer() {
assert( m_captured == m_messages.size() );
for ( size_t i = 0; i < m_captured; ++i )
m_resultCapture.popScopedMessage( m_messages[i] );
if ( m_isScoped ) {
for ( auto const& message : m_messages ) {
Detail::popScopedMessage( message.sequence );
}
}
}
void Capturer::captureValue( size_t index, std::string const& value ) {
assert( index < m_messages.size() );
m_messages[index].message += value;
m_resultCapture.pushScopedMessage( m_messages[index] );
if ( m_isScoped ) {
Detail::pushScopedMessage( CATCH_MOVE( m_messages[index] ) );
} else {
Detail::addUnscopedMessage( CATCH_MOVE( m_messages[index] ) );
}
m_captured++;
}
+38 -21
View File
@@ -13,16 +13,28 @@
#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/internal/catch_move_and_forward.hpp>
#include <catch2/catch_tostring.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string>
#include <vector>
namespace Catch {
struct MessageInfo;
struct MessageBuilder;
namespace Detail {
// The message state affecting functions have to be defined in
// the TU where the thread-local message holders are defined.
// Currently this is catch_run_context.cpp
void pushScopedMessage( MessageInfo&& message );
void popScopedMessage( unsigned int messageId );
void addUnscopedMessage( MessageInfo&& message );
void emplaceUnscopedMessage( MessageBuilder&& builder );
} // namespace Detail
struct SourceLineInfo;
class IResultCapture;
struct MessageStream {
@@ -57,16 +69,16 @@ namespace Catch {
ScopedMessage( ScopedMessage&& old ) noexcept;
~ScopedMessage();
MessageInfo m_info;
unsigned int m_messageId;
bool m_moved = false;
};
class Capturer {
std::vector<MessageInfo> m_messages;
IResultCapture& m_resultCapture;
size_t m_captured = 0;
bool m_isScoped = false;
public:
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names, bool isScoped );
Capturer(Capturer const&) = delete;
Capturer& operator=(Capturer const&) = delete;
@@ -98,11 +110,12 @@ namespace Catch {
} while( false )
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr ); \
#define INTERNAL_CATCH_CAPTURE( varName, macroName, scopedCapture, ... ) \
Catch::Capturer varName( macroName##_catch_sr, \
CATCH_INTERNAL_LINEINFO, \
Catch::ResultWas::Info, \
#__VA_ARGS__##_catch_sr, \
scopedCapture ); \
varName.captureValues( 0, __VA_ARGS__ )
///////////////////////////////////////////////////////////////////////////////
@@ -111,7 +124,7 @@ namespace Catch {
///////////////////////////////////////////////////////////////////////////////
#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
Catch::Detail::emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
#if defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
@@ -119,28 +132,32 @@ namespace Catch {
#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", __VA_ARGS__ )
#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE", true, __VA_ARGS__ )
#define CATCH_UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_UNSCOPED_CAPTURE", false, __VA_ARGS__ )
#elif defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define CATCH_INFO( msg ) (void)(0)
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
#define CATCH_WARN( msg ) (void)(0)
#define CATCH_CAPTURE( ... ) (void)(0)
#define CATCH_INFO( msg ) (void)(0)
#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
#define CATCH_WARN( msg ) (void)(0)
#define CATCH_CAPTURE( ... ) (void)(0)
#define CATCH_UNSCOPED_CAPTURE( ... ) (void)(0)
#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && !defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", __VA_ARGS__ )
#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE", true, __VA_ARGS__ )
#define UNSCOPED_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "UNSCOPED_CAPTURE", false, __VA_ARGS__ )
#elif !defined(CATCH_CONFIG_PREFIX_MESSAGES) && defined(CATCH_CONFIG_DISABLE)
#define INFO( msg ) (void)(0)
#define UNSCOPED_INFO( msg ) (void)(0)
#define WARN( msg ) (void)(0)
#define CAPTURE( ... ) (void)(0)
#define INFO( msg ) (void)(0)
#define UNSCOPED_INFO( msg ) (void)(0)
#define WARN( msg ) (void)(0)
#define CAPTURE( ... ) (void)(0)
#define UNSCOPED_CAPTURE( ... ) (void)(0)
#endif // end of user facing macro declarations
-6
View File
@@ -15,7 +15,6 @@
#include <catch2/internal/catch_tag_alias_registry.hpp>
#include <catch2/internal/catch_startup_exception_registry.hpp>
#include <catch2/internal/catch_singletons.hpp>
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
@@ -72,9 +71,6 @@ namespace Catch {
CATCH_INTERNAL_ERROR("Attempted to register active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
#endif
}
IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
return m_enumValuesRegistry;
}
private:
TestRegistry m_testCaseRegistry;
@@ -82,7 +78,6 @@ namespace Catch {
ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
TagAliasRegistry m_tagAliasRegistry;
StartupExceptionRegistry m_exceptionRegistry;
Detail::EnumValuesRegistry m_enumValuesRegistry;
};
}
@@ -96,7 +91,6 @@ namespace Catch {
}
void cleanUp() {
cleanupSingletons();
cleanUpContext();
}
std::string translateActiveException() {
return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+56 -1
View File
@@ -21,11 +21,14 @@
#include <catch2/reporters/catch_reporter_multi.hpp>
#include <catch2/internal/catch_reporter_registry.hpp>
#include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_stdstreams.hpp>
#include <catch2/internal/catch_istream.hpp>
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <exception>
#include <iomanip>
#include <set>
@@ -140,6 +143,50 @@ namespace Catch {
}
}
// Creates empty file at path. The path must be writable, we do not
// try to create directories in path because that's hard in C++14.
void setUpGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
#if defined( _MSC_VER )
std::FILE* file = nullptr;
if ( fopen_s( &file, guardFilePath.c_str(), "w" ) ) {
char msgBuffer[100];
const auto err = errno;
std::string errMsg;
if ( !strerror_s( msgBuffer, err ) ) {
errMsg = msgBuffer;
} else {
errMsg = "Could not translate errno to a string";
}
#else
std::FILE* file = std::fopen( guardFilePath.c_str(), "w" );
if ( !file ) {
const auto err = errno;
const char* errMsg = std::strerror( err );
#endif
CATCH_RUNTIME_ERROR( "Could not open the exit guard file '"
<< guardFilePath << "' because '"
<< errMsg << "' (" << err << ')' );
}
const int ret = std::fclose( file );
CATCH_ENFORCE(
ret == 0,
"Error when closing the exit guard file: " << ret );
}
}
// Removes file at path. Assuming we created it in setUpGuardFile.
void tearDownGuardFile( std::string const& guardFilePath ) {
if ( !guardFilePath.empty() ) {
const int ret = std::remove( guardFilePath.c_str() );
CATCH_ENFORCE(
ret == 0,
"Error when removing the exit guard file: " << ret );
}
}
} // anon namespace
Session::Session() {
@@ -258,6 +305,7 @@ namespace Catch {
static_cast<void>(std::getchar());
}
int exitCode = runInternal();
if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
static_cast<void>(std::getchar());
@@ -298,6 +346,10 @@ namespace Catch {
CATCH_TRY {
config(); // Force config to be constructed
// We need to retrieve potential Bazel config with the full Config
// constructor, so we have to create the guard file after it is created.
setUpGuardFile( m_config->getExitGuardFilePath() );
seedRng( *m_config );
if (m_configData.filenamesAsTags) {
@@ -327,9 +379,12 @@ namespace Catch {
TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
auto const totals = tests.execute();
// If we got here, running the tests finished normally-enough.
// They might've failed, but that would've been reported elsewhere.
tearDownGuardFile( m_config->getExitGuardFilePath() );
if ( tests.hadUnmatchedTestSpecs()
&& m_config->warnAboutUnmatchedTestSpecs() ) {
// UnmatchedTestSpecExitCode
return UnmatchedTestSpecExitCode;
}
+49 -19
View File
@@ -57,39 +57,69 @@ namespace Detail {
}
} // end unnamed namespace
std::size_t catch_strnlen( const char* str, std::size_t n ) {
auto ret = std::char_traits<char>::find( str, n, '\0' );
if ( ret != nullptr ) { return static_cast<std::size_t>( ret - str ); }
return n;
}
std::string formatTimeT(std::time_t time) {
#ifdef _MSC_VER
std::tm timeInfo = {};
const auto err = gmtime_s( &timeInfo, &time );
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime( &time );
#endif
auto const timeStampSize = sizeof( "2017-01-16T17:06:45Z" );
char timeStamp[timeStampSize];
const char* const fmt = "%Y-%m-%dT%H:%M:%SZ";
#ifdef _MSC_VER
std::strftime( timeStamp, timeStampSize, fmt, &timeInfo );
#else
std::strftime( timeStamp, timeStampSize, fmt, timeInfo );
#endif
return std::string( timeStamp, timeStampSize - 1 );
}
std::string convertIntoString(StringRef string, bool escapeInvisibles) {
std::string ret;
// This is enough for the "don't escape invisibles" case, and a good
// lower bound on the "escape invisibles" case.
ret.reserve(string.size() + 2);
ret.reserve( string.size() + 2 );
if (!escapeInvisibles) {
if ( !escapeInvisibles ) {
ret += '"';
ret += string;
ret += '"';
return ret;
}
size_t last_start = 0;
auto write_to = [&]( size_t idx ) {
if ( last_start < idx ) {
ret += string.substr( last_start, idx - last_start );
}
last_start = idx + 1;
};
ret += '"';
for (char c : string) {
switch (c) {
case '\r':
ret.append("\\r");
break;
case '\n':
ret.append("\\n");
break;
case '\t':
ret.append("\\t");
break;
case '\f':
ret.append("\\f");
break;
default:
ret.push_back(c);
break;
for ( size_t i = 0; i < string.size(); ++i ) {
const char c = string[i];
if ( c == '\r' || c == '\n' || c == '\t' || c == '\f' ) {
write_to( i );
if ( c == '\r' ) { ret.append( "\\r" ); }
if ( c == '\n' ) { ret.append( "\\n" ); }
if ( c == '\t' ) { ret.append( "\\t" ); }
if ( c == '\f' ) { ret.append( "\\f" ); }
}
}
write_to( string.size() );
ret += '"';
return ret;
+47 -70
View File
@@ -8,7 +8,7 @@
#ifndef CATCH_TOSTRING_HPP_INCLUDED
#define CATCH_TOSTRING_HPP_INCLUDED
#include <ctime>
#include <vector>
#include <cstddef>
#include <type_traits>
@@ -18,7 +18,8 @@
#include <catch2/internal/catch_config_wchar.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_void_type.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
#include <string_view>
@@ -40,13 +41,9 @@ namespace Catch {
namespace Detail {
inline std::size_t catch_strnlen(const char *str, std::size_t n) {
auto ret = std::char_traits<char>::find(str, n, '\0');
if (ret != nullptr) {
return static_cast<std::size_t>(ret - str);
}
return n;
}
std::size_t catch_strnlen(const char *str, std::size_t n);
std::string formatTimeT( std::time_t time );
constexpr StringRef unprintableString = "{?}"_sr;
@@ -387,7 +384,10 @@ namespace Catch {
}
#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
#if defined( CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER ) && \
defined( CATCH_CONFIG_CPP17_OPTIONAL ) && \
/* P3168 turned optional into a range, making this ambigous with the range support */ \
!defined( __cpp_lib_optional_range_support )
#include <optional>
namespace Catch {
template<typename T>
@@ -411,44 +411,38 @@ namespace Catch {
// Separate std::tuple specialization
#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
#include <tuple>
# include <tuple>
# include <utility>
namespace Catch {
namespace Detail {
template<
typename Tuple,
std::size_t N = 0,
bool = (N < std::tuple_size<Tuple>::value)
>
struct TupleElementPrinter {
static void print(const Tuple& tuple, std::ostream& os) {
os << (N ? ", " : " ")
<< ::Catch::Detail::stringify(std::get<N>(tuple));
TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
}
};
template <typename Tuple, std::size_t... Is>
void PrintTuple( const Tuple& tuple,
std::ostream& os,
std::index_sequence<Is...> ) {
// 1 + Account for when the tuple is empty
char a[1 + sizeof...( Is )] = {
( ( os << ( Is ? ", " : " " )
<< ::Catch::Detail::stringify( std::get<Is>( tuple ) ) ),
'\0' )... };
(void)a;
}
template<
typename Tuple,
std::size_t N
>
struct TupleElementPrinter<Tuple, N, false> {
static void print(const Tuple&, std::ostream&) {}
};
} // namespace Detail
}
template<typename ...Types>
template <typename... Types>
struct StringMaker<std::tuple<Types...>> {
static std::string convert(const std::tuple<Types...>& tuple) {
static std::string convert( const std::tuple<Types...>& tuple ) {
ReusableStringStream rss;
rss << '{';
Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
Detail::PrintTuple(
tuple,
rss.get(),
std::make_index_sequence<sizeof...( Types )>{} );
rss << " }";
return rss.str();
}
};
}
} // namespace Catch
#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
@@ -635,43 +629,26 @@ struct ratio_string<std::milli> {
const auto systemish = std::chrono::time_point_cast<
std::chrono::system_clock::duration>( time_point );
const auto as_time_t = std::chrono::system_clock::to_time_t( systemish );
#ifdef _MSC_VER
std::tm timeInfo = {};
const auto err = gmtime_s( &timeInfo, &as_time_t );
if ( err ) {
return "gmtime from provided timepoint has failed. This "
"happens e.g. with pre-1970 dates using Microsoft libc";
}
#else
std::tm* timeInfo = std::gmtime( &as_time_t );
#endif
auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
char timeStamp[timeStampSize];
const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
#ifdef _MSC_VER
std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
#else
std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
#endif
return std::string(timeStamp, timeStampSize - 1);
return ::Catch::Detail::formatTimeT( as_time_t );
}
};
}
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
namespace Catch { \
template<> struct StringMaker<enumName> { \
static std::string convert( enumName value ) { \
static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
return static_cast<std::string>(enumInfo.lookup( static_cast<int>( value ) )); \
} \
}; \
}
#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
namespace Catch { \
template <> \
struct StringMaker<enumName> { \
static std::string convert( enumName value ) { \
CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \
CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
static const auto enumInfo = ::Catch::Detail::makeEnumInfo( \
#enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
return static_cast<std::string>( \
enumInfo.lookup( static_cast<int64_t>( value ) ) ); \
} \
}; \
}
#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
+1 -1
View File
@@ -22,7 +22,7 @@ namespace Catch {
class ExceptionTranslatorRegistrar {
template<typename T>
class ExceptionTranslator : public IExceptionTranslator {
class ExceptionTranslator final : public IExceptionTranslator {
public:
constexpr ExceptionTranslator( std::string(*translateFunction)( T const& ) )
+5 -5
View File
@@ -196,12 +196,12 @@
#endif
#cmakedefine CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_THREAD_SAFE_ASSERTIONS
#cmakedefine CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS
#if defined( CATCH_CONFIG_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS ) && \
defined( CATCH_CONFIG_NO_EXPERIMENTAL_THREAD_SAFE_ASSERTIONS )
# error Cannot force EXPERIMENTAL_THREAD_SAFE_ASSERTIONS to both ON and OFF
#if defined( CATCH_CONFIG_THREAD_SAFE_ASSERTIONS ) && \
defined( CATCH_CONFIG_NO_THREAD_SAFE_ASSERTIONS )
# error Cannot force THREAD_SAFE_ASSERTIONS to both ON and OFF
#endif
+1 -1
View File
@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 9, 1, "", 0 );
static Version version( 3, 15, 0, "", 0 );
return version;
}
+2 -2
View File
@@ -9,7 +9,7 @@
#define CATCH_VERSION_MACROS_HPP_INCLUDED
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 9
#define CATCH_VERSION_PATCH 1
#define CATCH_VERSION_MINOR 15
#define CATCH_VERSION_PATCH 0
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -7,8 +7,6 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/generators/catch_generator_exception.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
namespace Catch {
@@ -17,14 +15,6 @@ namespace Catch {
namespace Generators {
namespace Detail {
[[noreturn]]
void throw_generator_exception(char const* msg) {
Catch::throw_exception(GeneratorException{ msg });
}
} // end namespace Detail
GeneratorUntypedBase::~GeneratorUntypedBase() = default;
IGeneratorTracker* acquireGeneratorTracker(StringRef generatorName, SourceLineInfo const& lineInfo ) {
+25 -8
View File
@@ -9,6 +9,7 @@
#define CATCH_GENERATORS_HPP_INCLUDED
#include <catch2/catch_tostring.hpp>
#include <catch2/generators/catch_generators_throw.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/internal/catch_source_line_info.hpp>
#include <catch2/internal/catch_stringref.hpp>
@@ -22,14 +23,6 @@ namespace Catch {
namespace Generators {
namespace Detail {
//! Throws GeneratorException with the provided message
[[noreturn]]
void throw_generator_exception(char const * msg);
} // end namespace detail
template<typename T>
class IGenerator : public GeneratorUntypedBase {
std::string stringifyImpl() const override {
@@ -64,6 +57,9 @@ namespace Detail {
bool next() {
return m_generator->countedNext();
}
bool isFinite() const { return m_generator->isFinite(); }
void skipToNthElement( size_t n ) { m_generator->skipToNthElement(n); }
};
@@ -84,6 +80,8 @@ namespace Detail {
bool next() override {
return false;
}
bool isFinite() const override { return true; }
};
template<typename T>
@@ -93,6 +91,15 @@ namespace Detail {
"specialization, use SingleValue Generator instead.");
std::vector<T> m_values;
size_t m_idx = 0;
void skipToNthElementImpl( std::size_t n ) override {
if ( n >= m_values.size() ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
}
m_idx = n;
}
public:
FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
@@ -103,6 +110,8 @@ namespace Detail {
++m_idx;
return m_idx < m_values.size();
}
bool isFinite() const override { return true; }
};
template <typename T, typename DecayedT = std::decay_t<T>>
@@ -167,6 +176,14 @@ namespace Detail {
}
return m_current < m_generators.size();
}
bool isFinite() const override {
for (auto const& gen : m_generators) {
if (!gen.isFinite()) { return false;
}
}
return true;
}
};
@@ -11,6 +11,7 @@
#include <catch2/generators/catch_generators.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_optional.hpp>
#include <cassert>
@@ -22,6 +23,17 @@ namespace Generators {
GeneratorWrapper<T> m_generator;
size_t m_returned = 0;
size_t m_target;
void skipToNthElementImpl( std::size_t n ) override {
if ( n >= m_target ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
}
m_generator.skipToNthElement( n );
m_returned = n;
}
public:
TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
@@ -46,6 +58,8 @@ namespace Generators {
}
return success;
}
bool isFinite() const override { return true; }
};
template <typename T>
@@ -58,8 +72,9 @@ namespace Generators {
class FilterGenerator final : public IGenerator<T> {
GeneratorWrapper<T> m_generator;
Predicate m_predicate;
static_assert(!std::is_reference<Predicate>::value, "This would most likely result in a dangling reference");
public:
template <typename P = Predicate>
template <typename P>
FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
m_generator(CATCH_MOVE(generator)),
m_predicate(CATCH_FORWARD(pred))
@@ -86,12 +101,14 @@ namespace Generators {
while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
return success;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename T, typename Predicate>
GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, typename std::remove_reference<Predicate>::type>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
}
template <typename T>
@@ -110,6 +127,9 @@ namespace Generators {
m_target_repeats(repeats)
{
assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
if (!m_generator.isFinite()) {
Detail::throw_generator_exception( "Cannot repeat infinite generator" );
}
}
T const& get() const override {
@@ -143,6 +163,8 @@ namespace Generators {
}
return m_current_repeat < m_target_repeats;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename T>
@@ -156,25 +178,30 @@ namespace Generators {
GeneratorWrapper<U> m_generator;
Func m_function;
// To avoid returning dangling reference, we have to save the values
T m_cache;
mutable Optional<T> m_cache;
void skipToNthElementImpl( std::size_t n ) override {
m_generator.skipToNthElement( n );
m_cache.reset();
}
public:
template <typename F2 = Func>
MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
m_generator(CATCH_MOVE(generator)),
m_function(CATCH_FORWARD(function)),
m_cache(m_function(m_generator.get()))
m_function(CATCH_FORWARD(function))
{}
T const& get() const override {
return m_cache;
if ( !m_cache ) { m_cache = m_function( m_generator.get() ); }
return *m_cache;
}
bool next() override {
const auto success = m_generator.next();
if (success) {
m_cache = m_function(m_generator.get());
}
return success;
m_cache.reset();
return m_generator.next();
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
@@ -196,7 +223,6 @@ namespace Generators {
std::vector<T> m_chunk;
size_t m_chunk_size;
GeneratorWrapper<T> m_generator;
bool m_used_up = false;
public:
ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
m_chunk_size(size), m_generator(CATCH_MOVE(generator))
@@ -225,6 +251,8 @@ namespace Generators {
}
return true;
}
bool isFinite() const override { return m_generator.isFinite(); }
};
template <typename T>
@@ -234,6 +262,56 @@ namespace Generators {
);
}
template <typename T>
class ConcatGenerator final : public IGenerator<T> {
std::vector<GeneratorWrapper<T>> m_generators;
size_t m_current_generator = 0;
void InsertGenerators( GeneratorWrapper<T>&& gen ) {
m_generators.push_back( CATCH_MOVE( gen ) );
}
template <typename... Generators>
void InsertGenerators( GeneratorWrapper<T>&& gen, Generators&&... gens ) {
m_generators.push_back( CATCH_MOVE( gen ) );
InsertGenerators( CATCH_MOVE( gens )... );
}
public:
template <typename... Generators>
ConcatGenerator( Generators&&... generators ) {
InsertGenerators( CATCH_MOVE( generators )... );
}
T const& get() const override {
return m_generators[m_current_generator].get();
}
bool next() override {
const bool success = m_generators[m_current_generator].next();
if ( success ) { return true; }
// If current generator is used up, we have to move to the next one
++m_current_generator;
return m_current_generator < m_generators.size();
}
bool isFinite() const override {
for ( auto const& gen : m_generators ) {
if ( !gen.isFinite() ) { return false; }
}
return true;
}
};
template <typename T, typename... Generators>
GeneratorWrapper<T> cat( GeneratorWrapper<T>&& generator,
Generators&&... generators ) {
return GeneratorWrapper<T>(
Catch::Detail::make_unique<ConcatGenerator<T>>(
CATCH_MOVE( generator ), CATCH_MOVE( generators )... ) );
}
} // namespace Generators
} // namespace Catch
@@ -26,5 +26,6 @@
#include <catch2/generators/catch_generators_adapters.hpp>
#include <catch2/generators/catch_generators_random.hpp>
#include <catch2/generators/catch_generators_range.hpp>
#include <catch2/generators/catch_generators_throw.hpp>
#endif // CATCH_GENERATORS_ALL_HPP_INCLUDED
@@ -37,5 +37,10 @@ namespace Catch {
m_current_number = m_pimpl->dist( m_pimpl->rng );
return true;
}
bool RandomFloatingGenerator<long double>::isFinite() const {
return false;
}
} // namespace Generators
} // namespace Catch
@@ -42,6 +42,7 @@ public:
m_current_number = m_dist(m_rng);
return true;
}
bool isFinite() const override { return false; }
};
template <>
@@ -59,6 +60,7 @@ public:
bool next() override;
~RandomFloatingGenerator() override; // = default
bool isFinite() const override;
};
template <typename Integer>
@@ -80,6 +82,7 @@ public:
m_current_number = m_dist(m_rng);
return true;
}
bool isFinite() const override { return false; }
};
template <typename T>
@@ -48,6 +48,8 @@ public:
m_current += m_step;
return (m_positive) ? (m_current < m_end) : (m_current > m_end);
}
bool isFinite() const override { return true; }
};
template <typename T>
@@ -87,6 +89,8 @@ public:
++m_current;
return m_current != m_elems.size();
}
bool isFinite() const override { return true; }
};
template <typename InputIterator,
@@ -0,0 +1,24 @@
// 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/generators/catch_generator_exception.hpp>
#include <catch2/generators/catch_generators_throw.hpp>
#include <catch2/internal/catch_enforce.hpp>
namespace Catch {
namespace Generators {
namespace Detail {
[[noreturn]]
void throw_generator_exception( char const* msg ) {
Catch::throw_exception( GeneratorException{ msg } );
}
} // namespace Detail
} // namespace Generators
} // namespace Catch
@@ -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_GENERATORS_THROW_HPP_INCLUDED
#define CATCH_GENERATORS_THROW_HPP_INCLUDED
namespace Catch {
namespace Generators {
namespace Detail {
//! Throws GeneratorException with the provided message
[[noreturn]]
void throw_generator_exception( char const* msg );
} // namespace Detail
} // namespace Generators
} // namespace Catch
#endif // CATCH_GENERATORS_THROW_HPP_INCLUDED
@@ -24,7 +24,6 @@
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/interfaces/catch_interfaces_config.hpp>
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/interfaces/catch_interfaces_exception.hpp>
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
@@ -7,7 +7,14 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <catch2/internal/catch_enforce.hpp>
namespace Catch {
namespace Detail {
void missingCaptureInstance() {
CATCH_INTERNAL_ERROR( "No result capture instance" );
}
} // namespace Detail
IResultCapture::~IResultCapture() = default;
}
} // namespace Catch
@@ -10,6 +10,7 @@
#include <string>
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_result_type.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
@@ -21,8 +22,6 @@ namespace Catch {
struct AssertionInfo;
struct SectionInfo;
struct SectionEndInfo;
struct MessageInfo;
struct MessageBuilder;
struct Counts;
struct AssertionReaction;
struct SourceLineInfo;
@@ -62,11 +61,6 @@ namespace Catch {
virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
virtual void benchmarkFailed( StringRef error ) = 0;
virtual void pushScopedMessage( MessageInfo const& message ) = 0;
virtual void popScopedMessage( MessageInfo const& message ) = 0;
virtual void emplaceUnscopedMessage( MessageBuilder&& builder ) = 0;
virtual void handleFatalErrorCondition( StringRef message ) = 0;
virtual void handleExpr
@@ -92,16 +86,24 @@ namespace Catch {
ResultWas::OfType resultType,
AssertionReaction &reaction ) = 0;
virtual bool lastAssertionPassed() = 0;
// Deprecated, do not use:
virtual std::string getCurrentTestName() const = 0;
virtual const AssertionResult* getLastResult() const = 0;
virtual void exceptionEarlyReported() = 0;
};
IResultCapture& getResultCapture();
namespace Detail {
[[noreturn]]
void missingCaptureInstance();
}
inline IResultCapture& getResultCapture() {
if (auto* capture = getCurrentContext().getResultCapture()) {
return *capture;
} else {
Detail::missingCaptureInstance();
}
}
}
#endif // CATCH_INTERFACES_CAPTURE_HPP_INCLUDED
@@ -29,6 +29,8 @@ namespace Catch {
NoAssertions = 0x01,
//! A command line test spec matched no test cases
UnmatchedTestSpec = 0x02,
//! The resulting generator in GENERATE is infinite
InfiniteGenerator = 0x04,
}; };
enum class ShowDurations {
@@ -60,8 +62,9 @@ namespace Catch {
class TestSpec;
class IStream;
struct PathFilter;
class IConfig : public Detail::NonCopyable {
class IConfig : Detail::NonCopyable {
public:
virtual ~IConfig();
@@ -71,6 +74,7 @@ namespace Catch {
virtual bool shouldDebugBreak() const = 0;
virtual bool warnAboutMissingAssertions() const = 0;
virtual bool warnAboutUnmatchedTestSpecs() const = 0;
virtual bool warnAboutInfiniteGenerators() const = 0;
virtual bool zeroTestsCountAsSuccess() const = 0;
virtual int abortAfter() const = 0;
virtual bool showInvisibles() const = 0;
@@ -84,7 +88,9 @@ namespace Catch {
virtual unsigned int shardCount() const = 0;
virtual unsigned int shardIndex() const = 0;
virtual ColourMode defaultColourMode() const = 0;
virtual std::vector<std::string> const& getSectionsToRun() const = 0;
virtual std::vector<PathFilter> const& getPathFilters() const = 0;
virtual bool useNewFilterBehaviour() const = 0;
virtual Verbosity verbosity() const = 0;
virtual bool skipBenchmarks() const = 0;
@@ -1,47 +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_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <vector>
namespace Catch {
namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int, StringRef>> m_values;
~EnumInfo();
StringRef lookup( int value ) const;
};
} // namespace Detail
class IMutableEnumValuesRegistry {
public:
virtual ~IMutableEnumValuesRegistry(); // = default;
virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
template<typename E>
Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
static_assert(sizeof(int) >= sizeof(E), "Cannot serialize enum to int");
std::vector<int> intValues;
intValues.reserve( values.size() );
for( auto enumValue : values )
intValues.push_back( static_cast<int>( enumValue ) );
return registerEnum( enumName, allEnums, intValues );
}
};
} // Catch
#endif // CATCH_INTERFACES_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -7,6 +7,8 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
#include <catch2/generators/catch_generators.hpp>
#include <string>
namespace Catch {
@@ -21,6 +23,31 @@ namespace Catch {
return ret;
}
void GeneratorUntypedBase::skipToNthElementImpl( std::size_t n ) {
for ( size_t i = m_currentElementIndex; i < n; ++i ) {
bool isValid = next();
if ( !isValid ) {
Detail::throw_generator_exception(
"Coud not jump to Nth element: not enough elements" );
}
}
}
void GeneratorUntypedBase::skipToNthElement( std::size_t n ) {
if ( n < m_currentElementIndex ) {
Detail::throw_generator_exception(
"Tried to jump generator backwards" );
}
if ( n == m_currentElementIndex ) { return; }
skipToNthElementImpl(n);
// Fixup tracking after moving the generator forward
// * Ensure that the correct element index is set after skipping
// * Invalidate cache
m_currentElementIndex = n;
m_stringReprCache.clear();
}
StringRef GeneratorUntypedBase::currentElementAsString() const {
if ( m_stringReprCache.empty() ) {
m_stringReprCache = stringifyImpl();
@@ -28,5 +55,7 @@ namespace Catch {
return m_stringReprCache;
}
bool GeneratorUntypedBase::isFinite() const { return true; }
} // namespace Generators
} // namespace Catch
@@ -35,6 +35,15 @@ namespace Catch {
//! Customization point for `currentElementAsString`
virtual std::string stringifyImpl() const = 0;
/**
* Customization point for skipping to the n-th element
*
* Defaults to successively calling `countedNext`. If there
* are not enough elements to reach the nth one, will throw
* an error.
*/
virtual void skipToNthElementImpl( std::size_t n );
public:
GeneratorUntypedBase() = default;
// Generation of copy ops is deprecated (and Clang will complain)
@@ -58,6 +67,13 @@ namespace Catch {
std::size_t currentElementIndex() const { return m_currentElementIndex; }
/**
* Moves the generator forward **to** the n-th element
*
* Cannot move backwards. Can stay in place.
*/
void skipToNthElement( std::size_t n );
/**
* Returns generator's current element as user-friendly string.
*
@@ -72,6 +88,15 @@ namespace Catch {
* comes first.
*/
StringRef currentElementAsString() const;
/**
* Returns true if calls to `next` will eventually return false
*
* Note that for backwards compatibility this is currently defaulted
* to return `true`, but in the future all generators will have to
* provide their own implementation.
*/
virtual bool isFinite() const;
};
using GeneratorBasePtr = Catch::Detail::unique_ptr<GeneratorUntypedBase>;
@@ -80,9 +105,7 @@ namespace Catch {
class IGeneratorTracker {
public:
virtual ~IGeneratorTracker(); // = default;
virtual auto hasGenerator() const -> bool = 0;
virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
};
} // namespace Catch
@@ -53,7 +53,6 @@ namespace Catch {
virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
virtual void registerStartupException() noexcept = 0;
virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
};
IRegistryHub const& getRegistryHub();
@@ -6,21 +6,28 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_assertion_handler.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/internal/catch_context.hpp>
#include <catch2/internal/catch_debugger.hpp>
#include <catch2/internal/catch_run_context.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/matchers/catch_matchers_string.hpp>
namespace Catch {
void AssertionHandler::finishIncomplete() {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
AssertionHandler::AssertionHandler
( StringRef macroName,
SourceLineInfo const& lineInfo,
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition )
: m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
m_resultCapture( getResultCapture() )
m_resultCapture( static_cast<RunContext&>(getResultCapture()) )
{
m_resultCapture.notifyAssertionStarted( m_assertionInfo );
}
@@ -10,12 +10,13 @@
#include <catch2/catch_assertion_info.hpp>
#include <catch2/internal/catch_decomposer.hpp>
#include <catch2/interfaces/catch_interfaces_capture.hpp>
#include <string>
namespace Catch {
class RunContext;
struct AssertionReaction {
bool shouldDebugBreak = false;
bool shouldThrow = false;
@@ -26,7 +27,12 @@ namespace Catch {
AssertionInfo m_assertionInfo;
AssertionReaction m_reaction;
bool m_completed = false;
IResultCapture& m_resultCapture;
// Since all uses are hidden in the .cpp file, we can directly use
// the final type and avoid going through virtual dispatch, without
// massive compilation time overhead.
RunContext& m_resultCapture;
void finishIncomplete();
public:
AssertionHandler
@@ -35,9 +41,9 @@ namespace Catch {
StringRef capturedExpression,
ResultDisposition::Flags resultDisposition );
~AssertionHandler() {
if ( !m_completed ) {
m_resultCapture.handleIncomplete( m_assertionInfo );
}
// We want the common fast path inlinable, and the virtual
// dispatch in a function in single TU.
if ( !m_completed ) { finishIncomplete(); }
}
+65 -3
View File
@@ -32,6 +32,9 @@ namespace Catch {
} else if ( warning == "UnmatchedTestSpec" ) {
config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::UnmatchedTestSpec);
return ParserResult::ok( ParseResultType::Matched );
} else if ( warning == "InfiniteGenerators" ) {
config.warnings = static_cast<WarnAbout::What>(config.warnings | WarnAbout::InfiniteGenerator);
return ParserResult::ok( ParseResultType::Matched );
}
return ParserResult ::runtimeError(
@@ -189,6 +192,19 @@ namespace Catch {
config.shardCount = *parsedCount;
return ParserResult::ok( ParseResultType::Matched );
};
auto const setBenchmarkSamples = [&]( std::string const& samples ) {
auto parsedSamples = parseUInt( samples );
if ( !parsedSamples ) {
return ParserResult::runtimeError(
"Could not parse '" + samples + "' as benchmark samples" );
}
if ( *parsedSamples == 0 ) {
return ParserResult::runtimeError(
"Benchmark samples must be greater than 0" );
}
config.benchmarkSamples = *parsedSamples;
return ParserResult::ok( ParseResultType::Matched );
};
auto const setShardIndex = [&](std::string const& shardIndex) {
auto parsedIndex = parseUInt( shardIndex );
@@ -200,6 +216,43 @@ namespace Catch {
return ParserResult::ok( ParseResultType::Matched );
};
auto const setSectionFilter = [&]( std::string const& sectionFilter ) {
config.pathFilters.emplace_back( PathFilter::For::Section, trim(sectionFilter) );
return ParserResult::ok( ParseResultType::Matched );
};
auto const setGeneratorFilter = [&]( std::string const& generatorFilter ) {
if (generatorFilter != "*") {
// TODO: avoid re-parsing the index?
auto parsedIndex = parseUInt( generatorFilter );
if ( !parsedIndex ) {
return ParserResult::runtimeError( "Could not parse '" +
generatorFilter +
"' as generator index" );
}
}
config.useNewPathFilteringBehaviour = true;
config.pathFilters.emplace_back( PathFilter::For::Generator, trim(generatorFilter) );
return ParserResult::ok( ParseResultType::Matched );
};
// Copy-capturing other `setFoo` functions enables calling them later,
// as the config ref remains valid, but the local lambda vars won't.
auto const setPathFilter = [=, &config]( std::string const& pathFilter ) {
config.useNewPathFilteringBehaviour = true;
if ( pathFilter.size() < 3 ) {
return ParserResult::runtimeError(
"Path filter '" + pathFilter + "' is too short" );
}
if ( startsWith( pathFilter, "g:" ) ) {
return setGeneratorFilter( pathFilter.substr( 2 ) );
}
if ( startsWith( pathFilter, "c:" ) ) {
return setSectionFilter( pathFilter.substr( 2 ) );
}
return ParserResult::runtimeError( "Path filter '" + pathFilter +
"' has unknown type prefix" );
};
auto cli
= ExeName( config.processName )
| Help( config.showHelp )
@@ -245,9 +298,15 @@ namespace Catch {
| Opt( config.filenamesAsTags )
["-#"]["--filenames-as-tags"]
( "adds a tag for the filename" )
| Opt( config.sectionsToRun, "section name" )
| Opt( accept_many, setSectionFilter, "section name" )
["-c"]["--section"]
( "specify section to run" )
| Opt( accept_many, setGeneratorFilter, "index spec" )
["-g"]["--generator-index"]
( "specify generator elements to try" )
| Opt( accept_many, setPathFilter, "path filter spec" )
["-p"]["--path-filter"]
( "qualified path filter" )
| Opt( setVerbosity, "quiet|normal|high" )
["-v"]["--verbosity"]
( "set output verbosity" )
@@ -265,7 +324,7 @@ namespace Catch {
( "list all listeners" )
| Opt( setTestOrder, "decl|lex|rand" )
["--order"]
( "test case order (defaults to decl)" )
( "test case order (defaults to rand)" )
| Opt( setRngSeed, "'time'|'random-device'|number" )
["--rng-seed"]
( "set a specific seed for random numbers" )
@@ -281,7 +340,7 @@ namespace Catch {
| Opt( config.skipBenchmarks)
["--skip-benchmarks"]
( "disable running benchmarks")
| Opt( config.benchmarkSamples, "samples" )
| Opt( setBenchmarkSamples, "samples" )
["--benchmark-samples"]
( "number of samples to collect (default: 100)" )
| Opt( config.benchmarkResamples, "resamples" )
@@ -305,6 +364,9 @@ namespace Catch {
| Opt( config.allowZeroTests )
["--allow-running-no-tests"]
( "Treat 'No tests run' as a success" )
| Opt( config.prematureExitGuardFilePath, "path" )
["--premature-exit-guard-file"]
( "create a file before running tests and delete it during clean exit" )
| Arg( config.testsOrTags, "test name|pattern|tags" )
( "which test or tests to use" );
@@ -27,16 +27,24 @@
#include <catch2/internal/catch_platform.hpp>
#include <catch2/catch_user_config.hpp>
#ifdef __cplusplus
#if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define CATCH_CPP17_OR_GREATER
#endif
# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
# define CATCH_CPP17_OR_GREATER
# endif
# if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
# define CATCH_CPP20_OR_GREATER
# endif
#if (__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)
# define CATCH_CPP20_OR_GREATER
#endif
// Matchers are only constexpr-able in C++20
#if defined( CATCH_CPP20_OR_GREATER ) && \
defined( __cpp_constexpr_dynamic_alloc ) && \
__cpp_constexpr_dynamic_alloc >= 201907L && \
/* GCC < 13 define the feature macro, but compiler bugs stop us from using it */ \
( !defined( __GNUC__ ) || __GNUC__ >= 13 || defined(__clang__) )
# define CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED
# define CATCH_DESTRUCTOR_CONSTEXPR constexpr
#else
# define CATCH_DESTRUCTOR_CONSTEXPR
#endif
// Only GCC compiler should be used in this block, so other compilers trying to
@@ -110,10 +118,15 @@
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wc++20-extensions\"" )
# else
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
# endif
# if ( __clang_major__ >= 22 )
# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wc2y-extensions\"" )
# endif
# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \
_Pragma( "clang diagnostic ignored \"-Wunused-template\"" )
@@ -208,7 +221,7 @@
////////////////////////////////////////////////////////////////////////////////
// Visual C++
#if defined(_MSC_VER)
#if defined(_MSC_VER) && !defined(__clang__)
// We want to defer to nvcc-specific warning suppression if we are compiled
// with nvcc masquerading for MSVC.
@@ -219,14 +232,23 @@
__pragma( warning( pop ) )
# endif
// Suppress MSVC C++ Core Guidelines checker warning 26426:
// "Global initializer calls a non-constexpr function (i.22)"
# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
__pragma( warning( disable : 26426 ) )
// Universal Windows platform does not support SEH
// Or console colours (or console at all...)
# if defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# else
# if !defined(CATCH_PLATFORM_WINDOWS_UWP)
# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
# endif
// Only some Windows platform families support the console
# if defined(WINAPI_FAMILY_PARTITION)
# if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP | WINAPI_PARTITION_SYSTEM)
# define CATCH_INTERNAL_CONFIG_NO_COLOUR_WIN32
# endif
# endif
// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
@@ -420,6 +442,9 @@
#if !defined( CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS )
# define CATCH_INTERNAL_SUPPRESS_SHADOW_WARNINGS
#endif
#if !defined( CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS )
# define CATCH_INTERNAL_SUPPRESS_COUNTER_WARNINGS
#endif
#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10)
# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS
+7 -1
View File
@@ -161,7 +161,13 @@ namespace {
#endif // Windows/ ANSI/ None
#if defined( CATCH_PLATFORM_LINUX ) || defined( CATCH_PLATFORM_MAC ) || defined( __GLIBC__ )
#if defined( CATCH_PLATFORM_LINUX ) \
|| defined( CATCH_PLATFORM_MAC ) \
|| defined( __GLIBC__ ) \
|| (defined( __FreeBSD__ ) \
/* PlayStation platform does not have `isatty()` */ \
&& !defined(CATCH_PLATFORM_PLAYSTATION)) \
|| defined( CATCH_PLATFORM_QNX )
# define CATCH_INTERNAL_HAS_ISATTY
# include <unistd.h>
#endif
+2 -13
View File
@@ -6,25 +6,14 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_context.hpp>
#include <catch2/internal/catch_noncopyable.hpp>
#include <catch2/internal/catch_random_number_generator.hpp>
namespace Catch {
Context* Context::currentContext = nullptr;
void cleanUpContext() {
delete Context::currentContext;
Context::currentContext = nullptr;
}
void Context::createContext() {
currentContext = new Context();
}
Context Context::currentContext;
Context& getCurrentMutableContext() {
if ( !Context::currentContext ) { Context::createContext(); }
// NOLINTNEXTLINE(clang-analyzer-core.uninitialized.UndefReturn)
return *Context::currentContext;
return Context::currentContext;
}
SimplePcg32& sharedRng() {
+2 -11
View File
@@ -19,11 +19,9 @@ namespace Catch {
IConfig const* m_config = nullptr;
IResultCapture* m_resultCapture = nullptr;
CATCH_EXPORT static Context* currentContext;
CATCH_EXPORT static Context currentContext;
friend Context& getCurrentMutableContext();
friend Context const& getCurrentContext();
static void createContext();
friend void cleanUpContext();
public:
constexpr IResultCapture* getResultCapture() const {
@@ -34,21 +32,14 @@ namespace Catch {
m_resultCapture = resultCapture;
}
constexpr void setConfig( IConfig const* config ) { m_config = config; }
};
Context& 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;
return Context::currentContext;
}
void cleanUpContext();
class SimplePcg32;
SimplePcg32& sharedRng();
}
+1 -1
View File
@@ -69,7 +69,7 @@
#endif
} // namespace Catch
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
#include <fstream>
#include <string>
+1 -1
View File
@@ -49,7 +49,7 @@ namespace Catch {
#define CATCH_TRAP() __asm__(".inst 0xde01")
#endif
#elif defined(CATCH_PLATFORM_LINUX)
#elif defined(CATCH_PLATFORM_LINUX) || defined(CATCH_PLATFORM_QNX)
// If we can use inline assembler, do it because this allows us to break
// directly at the location of the failing check instead of breaking inside
// raise() called from it, i.e. one stack frame below.
+2 -2
View File
@@ -193,7 +193,7 @@ namespace Catch {
void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
template<typename LhsT, typename RhsT>
class BinaryExpr : public ITransientExpression {
class BinaryExpr final : public ITransientExpression {
LhsT m_lhs;
StringRef m_op;
RhsT m_rhs;
@@ -269,7 +269,7 @@ namespace Catch {
};
template<typename LhsT>
class UnaryExpr : public ITransientExpression {
class UnaryExpr final : public ITransientExpression {
LhsT m_lhs;
void streamReconstructedExpression( std::ostream &os ) const override {
@@ -11,9 +11,9 @@
#include <catch2/catch_user_config.hpp>
#if !defined( CATCH_CONFIG_NO_DEPRECATION_ANNOTATIONS )
# define DEPRECATED( msg ) [[deprecated( msg )]]
# define CATCH_DEPRECATED( msg ) [[deprecated( msg )]]
#else
# define DEPRECATED( msg )
# define CATCH_DEPRECATED( msg )
#endif
#endif // CATCH_DEPRECATION_MACRO_HPP_INCLUDED
@@ -5,15 +5,14 @@
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enum_values_registry.hpp>
#include <catch2/internal/catch_enum_info.hpp>
#include <catch2/internal/catch_string_manip.hpp>
#include <cassert>
namespace Catch {
IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
namespace Detail {
namespace {
@@ -39,9 +38,7 @@ namespace Catch {
return parsed;
}
EnumInfo::~EnumInfo() = default;
StringRef EnumInfo::lookup( int value ) const {
StringRef EnumInfo::lookup( int64_t value ) const {
for( auto const& valueToName : m_values ) {
if( valueToName.first == value )
return valueToName.second;
@@ -49,25 +46,20 @@ namespace Catch {
return "{** unexpected enum value **}"_sr;
}
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
auto enumInfo = Catch::Detail::make_unique<EnumInfo>();
enumInfo->m_name = enumName;
enumInfo->m_values.reserve( values.size() );
EnumInfo makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int64_t> const& values ) {
EnumInfo enumInfo;
enumInfo.m_name = enumName;
enumInfo.m_values.reserve( values.size() );
const auto valueNames = Catch::Detail::parseEnums( allValueNames );
assert( valueNames.size() == values.size() );
std::size_t i = 0;
for( auto value : values )
enumInfo->m_values.emplace_back(value, valueNames[i++]);
for (size_t i = 0; i < values.size(); ++i) {
enumInfo.m_values.emplace_back( values[i], valueNames[i] );
}
return enumInfo;
}
EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
m_enumInfos.push_back(makeEnumInfo(enumName, allValueNames, values));
return *m_enumInfos.back();
}
} // Detail
} // Catch
+48
View File
@@ -0,0 +1,48 @@
// 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_ENUM_INFO_HPP_INCLUDED
#define CATCH_ENUM_INFO_HPP_INCLUDED
#include <catch2/internal/catch_stringref.hpp>
#include <cstdint>
#include <utility>
#include <vector>
namespace Catch {
namespace Detail {
struct EnumInfo {
StringRef m_name;
std::vector<std::pair<int64_t, StringRef>> m_values;
StringRef lookup( int64_t value ) const;
};
EnumInfo makeEnumInfo( StringRef enumName,
StringRef allValueNames,
std::vector<int64_t> const& values );
template <typename E>
Detail::EnumInfo makeEnumInfo( StringRef enumName,
StringRef allEnums,
std::initializer_list<E> values ) {
static_assert( sizeof( int64_t ) >= sizeof( E ),
"Cannot serialize enum to int64_t" );
std::vector<int64_t> intValues;
intValues.reserve( values.size() );
for ( auto enumValue : values )
intValues.push_back( static_cast<int64_t>( enumValue ) );
return makeEnumInfo( enumName, allEnums, intValues );
}
std::vector<StringRef> parseEnums( StringRef enums );
} // namespace Detail
} // namespace Catch
#endif // CATCH_ENUM_INFO_HPP_INCLUDED
@@ -1,36 +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_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#define CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
#include <catch2/interfaces/catch_interfaces_enum_values_registry.hpp>
#include <catch2/internal/catch_unique_ptr.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <vector>
namespace Catch {
namespace Detail {
Catch::Detail::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
class EnumValuesRegistry : public IMutableEnumValuesRegistry {
std::vector<Catch::Detail::unique_ptr<EnumInfo>> m_enumInfos;
EnumInfo const& registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values) override;
};
std::vector<StringRef> parseEnums( StringRef enums );
} // Detail
} // Catch
#endif // CATCH_ENUM_VALUES_REGISTRY_HPP_INCLUDED
@@ -15,7 +15,7 @@
namespace Catch {
class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
class ExceptionTranslatorRegistry final : public IExceptionTranslatorRegistry {
public:
~ExceptionTranslatorRegistry() override;
void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
@@ -86,23 +86,27 @@ namespace Catch {
{ EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
};
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
static LONG CALLBACK topLevelExceptionFilter(PEXCEPTION_POINTERS ExceptionInfo) {
for (auto const& def : signalDefs) {
if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
reportFatal(def.name);
}
}
// If its not an exception we care about, pass it along.
// If a filter was previously registered, invoke it
if (previousTopLevelExceptionFilter) {
return previousTopLevelExceptionFilter(ExceptionInfo);
}
// Otherwise, pass along all exceptions.
// This stops us from eating debugger breaks etc.
return EXCEPTION_CONTINUE_SEARCH;
}
// Since we do not support multiple instantiations, we put these
// into global variables and rely on cleaning them up in outlined
// constructors/destructors
static LPTOP_LEVEL_EXCEPTION_FILTER previousTopLevelExceptionFilter = nullptr;
// For MSVC, we reserve part of the stack memory for handling
// memory overflow structured exception.
FatalConditionHandler::FatalConditionHandler() {
+39 -22
View File
@@ -7,8 +7,36 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_enforce.hpp>
#include <catch2/internal/catch_jsonwriter.hpp>
#include <catch2/internal/catch_unreachable.hpp>
namespace Catch {
namespace {
static bool needsEscape( char c ) {
return c == '"' || c == '\\' || c == '\b' || c == '\f' ||
c == '\n' || c == '\r' || c == '\t';
}
static Catch::StringRef makeEscapeStringRef( char c ) {
if ( c == '"' ) {
return "\\\""_sr;
} else if ( c == '\\' ) {
return "\\\\"_sr;
} else if ( c == '\b' ) {
return "\\b"_sr;
} else if ( c == '\f' ) {
return "\\f"_sr;
} else if ( c == '\n' ) {
return "\\n"_sr;
} else if ( c == '\r' ) {
return "\\r"_sr;
} else if ( c == '\t' ) {
return "\\t"_sr;
}
Catch::Detail::Unreachable();
}
} // namespace
void JsonUtils::indent( std::ostream& os, std::uint64_t level ) {
for ( std::uint64_t i = 0; i < level; ++i ) {
os << " ";
@@ -118,30 +146,19 @@ namespace Catch {
void JsonValueWriter::writeImpl( Catch::StringRef value, bool quote ) {
if ( quote ) { m_os << '"'; }
for (char c : value) {
// Escape list taken from https://www.json.org/json-en.html,
// string definition.
// Note that while forward slash _can_ be escaped, it does
// not have to be, if JSON is not further embedded somewhere
// where forward slash is meaningful.
if ( c == '"' ) {
m_os << "\\\"";
} else if ( c == '\\' ) {
m_os << "\\\\";
} else if ( c == '\b' ) {
m_os << "\\b";
} else if ( c == '\f' ) {
m_os << "\\f";
} else if ( c == '\n' ) {
m_os << "\\n";
} else if ( c == '\r' ) {
m_os << "\\r";
} else if ( c == '\t' ) {
m_os << "\\t";
} else {
m_os << c;
size_t current_start = 0;
for ( size_t i = 0; i < value.size(); ++i ) {
if ( needsEscape( value[i] ) ) {
if ( current_start < i ) {
m_os << value.substr( current_start, i - current_start );
}
m_os << makeEscapeStringRef( value[i] );
current_start = i + 1;
}
}
if ( current_start < value.size() ) {
m_os << value.substr( current_start, value.size() - current_start );
}
if ( quote ) { m_os << '"'; }
}
+7 -6
View File
@@ -8,6 +8,7 @@
#ifndef CATCH_JSONWRITER_HPP_INCLUDED
#define CATCH_JSONWRITER_HPP_INCLUDED
#include <catch2/internal/catch_lifetimebound.hpp>
#include <catch2/internal/catch_reusable_string_stream.hpp>
#include <catch2/internal/catch_stringref.hpp>
@@ -27,8 +28,8 @@ namespace Catch {
class JsonValueWriter {
public:
JsonValueWriter( std::ostream& os );
JsonValueWriter( std::ostream& os, std::uint64_t indent_level );
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonValueWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonObjectWriter writeObject() &&;
JsonArrayWriter writeArray() &&;
@@ -62,8 +63,8 @@ namespace Catch {
class JsonObjectWriter {
public:
JsonObjectWriter( std::ostream& os );
JsonObjectWriter( std::ostream& os, std::uint64_t indent_level );
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonObjectWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonObjectWriter( JsonObjectWriter&& source ) noexcept;
JsonObjectWriter& operator=( JsonObjectWriter&& source ) = delete;
@@ -81,8 +82,8 @@ namespace Catch {
class JsonArrayWriter {
public:
JsonArrayWriter( std::ostream& os );
JsonArrayWriter( std::ostream& os, std::uint64_t indent_level );
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND );
JsonArrayWriter( std::ostream& os CATCH_ATTR_LIFETIMEBOUND, std::uint64_t indent_level );
JsonArrayWriter( JsonArrayWriter&& source ) noexcept;
JsonArrayWriter& operator=( JsonArrayWriter&& source ) = delete;
@@ -0,0 +1,24 @@
// 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_LIFETIMEBOUND_HPP_INCLUDED
#define CATCH_LIFETIMEBOUND_HPP_INCLUDED
#if !defined( __has_cpp_attribute )
# define CATCH_ATTR_LIFETIMEBOUND
#elif __has_cpp_attribute( msvc::lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[msvc::lifetimebound]]
#elif __has_cpp_attribute( clang::lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#elif __has_cpp_attribute( lifetimebound )
# define CATCH_ATTR_LIFETIMEBOUND [[lifetimebound]]
#else
# define CATCH_ATTR_LIFETIMEBOUND
#endif
#endif // CATCH_LIFETIMEBOUND_HPP_INCLUDED
+12 -7
View File
@@ -7,19 +7,24 @@
// SPDX-License-Identifier: BSL-1.0
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_thread_local.hpp>
namespace Catch {
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
namespace {
// Messages are owned by their individual threads, so the counter should
// be thread-local as well. Alternative consideration: atomic counter,
// so threads don't share IDs and things are easier to debug.
static CATCH_INTERNAL_THREAD_LOCAL unsigned int messageIDCounter = 0;
}
MessageInfo::MessageInfo( StringRef _macroName,
SourceLineInfo const& _lineInfo,
ResultWas::OfType _type )
: macroName( _macroName ),
lineInfo( _lineInfo ),
type( _type ),
sequence( ++globalCount )
sequence( ++messageIDCounter )
{}
// This may need protecting if threading support is added
unsigned int MessageInfo::globalCount = 0;
} // end namespace Catch

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