2
0
mirror of https://github.com/catchorg/Catch2 synced 2026-02-23 16:22:10 +00:00
Commit Graph

89 Commits

Author SHA1 Message Date
Martin Hořeňovský
daadf42a0e Add --warn InfiniteGenerators 2026-02-10 09:37: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ý
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ý
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ý
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ý
985a3f4460 Fix lazy removal of unscoped messages also removing still valid msgs 2025-11-30 14:30:19 +01: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
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ý
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ý
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ý
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ý
2a8a8a7210 Add configuration option to make assertions thread-safe
All the previous refactoring to make the assertion fast paths
smaller and faster also allows us to implement the fast paths
just with thread-local and atomic variables, without full mutexes.

However, the performance overhead of thread-safe assertions is
still significant for single threaded usage:

|  slowdown |  Debug  | Release |
|-----------|--------:|--------:|
| fast path |   1.04x |   1.43x |
| slow path |   1.16x |   1.22x |

Thus, we don't make the assertions thread-safe by default, and instead
provide a build-time configuration option that the users can set to get
thread-safe assertions.

This commit is functional, but it still needs some follow-up work:
 * We do not need full seq_cst increments for the atomic counters,
   and using weaker ones can be faster.
 * We brute-force updating the reporter-friendly totals from internal
   atomic counters by doing it everywhere. We should properly trace
   where this is needed instead.
 * Message macros (`INFO`, `UNSCOPED_INFO`, `CAPTURE`, etc) are not
   made thread safe in this commit, but they can be made thread safe
   in the future, by building on top of this work.
 * Add more tests, including with thread-sanitizer, and compiled
   examples to the repository. Right now, these changes have been
   compiled with tsan manually, but these tests are not added to CI.

Closes #2948
2025-07-24 10:10:00 +02:00
Martin Hořeňovský
5aa8d11321 Clear unscoped messages lazily
This improves the fast path performance for successful assertions
by about 7%, at the cost of potentially keeping around the message
allocation longer.
2025-07-21 21:50:51 +02:00
Martin Hořeňovský
9c5c21df82 Simplify removal of messages from RunContext
Instead of doing range-based removals, we only erase specific message,
because there can never be more than 1 message with the specific ID.
2025-07-21 21:50:49 +02:00
Martin Hořeňovský
10aef62f21 Regularize scoped message lifetime to only consider the object's scope
This means that:
1) Scoped messages are always removed at the end of their scope,
   even if the scope ended due to an exception.
2) Scoped messages outlive section end, if that section's scope is
   enclosed in their own.

Previously neither of these were true, which has led to a number
of surprising behaviour, where e.g. this:
```cpp
TEST_CASE() {
    try {
        INFO( "some info" );
        throw std::runtime_error( "ex" );
    } catch (std::exception const&) {}

    REQUIRE( false );
}
```
would print "some info" as the message for the assertion, while this:
```cpp
TEST_CASE() {
    INFO("Hello");
    SECTION("dummy") {}
    REQUIRE(false);
}
```
would not print out "Hello" as the message for the assertion.

This had an underlying reason, in that it was trying to helpfully
keep the messages around in case of unexpected exceptions, so that
code like this:
```cpp
TEST_CASE() {
    auto [input, expected] = GENERATE(...);
    CAPTURE(input);
    auto result = transform(input); // throws
    REQUIRE(result == expected);
}
```
would report the value of `input` when `transform` throws. However,
it was surprising in practice and was causing various issues around
handling of messages in other cases.

Closes #1759
Closes #2019
Closes #2959
2025-07-21 21:50:47 +02:00
Martin Hořeňovský
98b4bbb35e Deprecate comparison operators on MessageInfo 2025-07-21 21:50:45 +02:00
Martin Hořeňovský
715558fd97 Use the fast path for passing assertions in RunContext::handleNonExpr
`handleNonExpr` is responsible for handling assertions that do not
result in a decomposable expression, e.g. `REQUIRE_THROWS`, or
`REQUIRE_NOTHROW`.

Running benchmark on these two macros specifically, with
`REQUIRE_THROWS([](){ throw 1; }())`, and `REQUIRE_NOTHROW([](){}())`,
we get these speedups:

|         |  Debug | Release |
|---------|--------|---------|
| THROWS  |  3.69x |   2.10x |
| NOTHROW |  1.18x |   1.05x |

Obviously the actual performance improvement is dependent on
how expensive the expression under test is.
2025-07-20 21:01:10 +02:00
Martin Hořeňovský
55b14e1b34 Update last seen line info in the assertionEnded fast path
This costs us about 1% perf in Debug build and 3% in Release build,
but it is worth it for more precise information during unexpected
exceptions or fatal errors.

Given a simple test case like this
```cpp
TEST_CASE("Hard fail") {
    REQUIRE( 1 == 1 );
    REQUIRE( 2 == 2 );
    throw 1;
    REQUIRE( 3 == 3 );
}
```

Catch2 before this change would report the line info from the
`TEST_CASE` macro as the last seen expression before error. With
this change, it will correctly report the line info from the
`REQUIRE(2 == 2)` assertion as the last seen expression before error.
2025-07-20 18:34:21 +02:00
Martin Hořeňovský
c22096846c Let reporters opt into fast path in RunContext::assertionStarting
The fast path allows `RunContext` to skip disabling output redirect,
and notifying the reporters, turning `RunContext::notifyAssertionStarted`
into a no-op. This improves the overall performance of assertion handling
significantly, and also prepares ground for future changes around
assertion handling and thread safety.

For simple 10M assertion run, this improves the running time by ~30%
in Debug build and ~40% in Release build.

For backwards-compatibility reasons, the fast path is disabled
by default. However, none of the first party reporters use the
`assertionStarting` event, so all first party reporters are opted-in.
2025-07-17 13:47:44 +02:00
Martin Hořeňovský
0e8112a762 Only track the last line info in RunContext
There were only two places where we used the full `AssertionInfo`
instance in `m_lastAssertionInfo`:
1) when reporting unexpected exception from running a test case
2) when reporting fatal error

because in those two places we do not have access to a real
instance of `AssertionInfo`, but we still need to send one to the
reporters. As a bonus, in both of these places we were already
constructing a fake-ish assertion info, by using the last encountered
source location, but dummying out the other information.

Instead, we only keep track of the last encountered source location,
and construct the dummy `AssertionInfo` on-demand.

This finishes the set of refactoring around `m_lastAssertionInfo`
in `RunContext` and improves the performance of running assertions
by ~5% in both Debug and Release mode.

--------------

Note that this change also causes small difference in output. It
could be avoided by having an invalidation flag and tracking where
the information would be invalidated before, but the difference
includes more precise line location for unexpected errors (both
exceptions and fatals), so I prefer the new output.
2025-07-17 11:27:03 +02:00
Martin Hořeňovský
9be81c0e05 Move resetAssertionInfo into RunContext::reportExpr
Because `RunContext::populateReaction` no longer implicitly depends
on the value of `RunContext::m_lastAssertionInfo`, we don't have to
delay the reset until `RunContext::reportExpr` is finished.
2025-07-16 21:58:42 +02:00
Martin Hořeňovský
efcb76874e Store only lineinfo if m_lastAssertionInfo is going to be reset
`RunContext::resetAssertionInfo` overwrites everything in
`m_lastAssertionInfo` except for the lineInfo, so if the reset
is called at the end of a copying function, we can save work
by copying only the lineInfo.
2025-07-16 21:52:05 +02:00
Martin Hořeňovský
895b8af6bd Avoid needless copy in handling fatal errors 2025-07-16 21:50:20 +02:00
Martin Hořeňovský
066f00acf5 Pass result disposition into RunContext::populateReaction directly
This avoids implicit dataflow through RunContext::m_lastAssertionInfo,
which will be useful in later refactoring.
2025-07-16 21:43:00 +02:00
Martin Hořeňovský
1b72e45354 Cache config::abortAfter and config::shouldDebugBreak in RunContext 2025-07-16 21:29:53 +02:00
Chris Thrasher
4c93a595a1 Fix typos 2025-04-26 22:46:42 -06:00
Martin Hořeňovský
412cad546a Avoid needless copy of string in runContext::handleMessage 2024-09-13 16:39:51 +02:00
Martin Hořeňovský
f24569a1b4 Large output redirect refactor
This rework changes two important things

1) the output redirect is deactivated while control is given to the reporters.
   This means that combining reporters that write to stdout with capturing
   reporters, e.g. `./tests -s -r console -r junit::out=junit.xml`, no
   longer leads to the capturing reporter seeing all the output from
   the other reporter captured.

Trying this with the `SelfTest` binary would previously lead to JUnit
spending **hours** trying to escape all of ConsoleReporter's output and
write it to the output file. I actually ended up killing the process
after 3 hours, during which the JUnit reporter wrote something like 50 MBs
of output to a file.

2) The redirect object's lifetime is tied to the `RunContext`, instead
  of being constructed for every partial test case run separately.

This has no effect on the basic StreamRedirect, but improves the FileRedirect
significantly. Previously, running many tests in single process with this
redirect (e.g. running `SelfTest -r junit`) would cause later tests to
always fail before starting, due to exceeding the limit of temporary files.

For the current `SelfTest` binary, the old implementation would lead to
**295** test failures from not being able to initiate the redirect. The
new implementation completely eliminates them.

----

There is one downside to the new implementation of FileRedirect, specific
to Linux. Running the `SelfTest` binary on Linux causes 3-4 tests to have
no captured stdout/stderr, even though the tests were supposed to be
writing there (there was no output to the actual stdout/stderr either,
the output was just completely lost).

Since this never happen for smaller test case sets, nor does it reproduce
on other platforms, this implementation is still strictly better than
the old one, and thus it can get reasonably merged.
2024-08-13 23:32:24 +02:00
Keith Stockdale
f7cd0ba051 TEST_CASE_PERSISTENT_FIXTURE: A new fixture macro for allowing persistent fixtures throughout a TEST_CASE (#2885)
This PR introduces a new `TEST_CASE` macro called `TEST_CASE_PERSISTENT_FIXTURE`. `TEST_CASE_PERSISTENT_FIXTURE` offers the same functionality as `TEST_CASE_METHOD` except for one difference. The object on which the test method is invoked is only created once for all invocations of the test case. The object is created just after the `testCaseStarting` event is broadcast and the object is destroyed just before the `testCaseEnding` event is broadcast.

The main motivation for this new functionality is to allow `TEST_CASE`s to do expensive setup and teardown once per `TEST_CASE`, without having to resort to abusing event listeners or static function variables with manual initialization.


Implements #1602

---------

Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2024-08-05 17:01:41 +02:00
Altan Birler
42fe78d0ba Handle active Sections for fatal errors
Closes #1210

When a signal is caught, the destructors of Sections will not be called.
Thus, we must call `sectionEndedEarly` manually for those Sections.

Example test case:
```
TEST_CASE("broken") {
   SECTION("section") {
      /// Use illegal cpu instruction
      __asm__ __volatile__("ud2" : : : "memory");
   }
}
```
2024-04-21 21:52:33 +02:00
Chris Thrasher
05786fa7ec Remove redundant destructors
Classes will automatically inherit the virtual-ness of their base
class destructors. If the base class already has a virtual
destructor and the derived class needs default destructor semantics
then the derived class can omit defining the destructor in favor of
the compiler automatically defining it.

This has an additional benefit of reenabling move semantics. The
presence of a user-specified destructor automatically disables move
operations.
2024-01-14 23:33:51 +01:00
rosstang
cd60a0301c Assert Info reset need to also reset result disposition to normal to handle uncaught exception correctly (#2723)
* AssertionEnd does not reset the assertion info yet. That is done after populateReaction. And reset assertion info would also reset the result disposition to normal, so that any uncaught exception would be reported as failure

* Approving test output changes due to added unit tests

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

* Add a unit test to test incomplete assertion handler

---------

Co-authored-by: Ross <ross.tang@gfo-x.com>
2023-08-07 22:07:31 +02:00
Martin Hořeňovský
8ca504cbc9 Move AssertionResult when passing it inside RunContext 2023-05-06 23:58:48 +02:00
Martin Hořeňovský
d84777c9cb Fix assertionStarting events being sent after the expr is evaluated
Closes #2678
2023-05-06 11:48:41 +02:00
Martin Hořeňovský
9f08097f55 Cleanup internal includes by splitting out some event structs
* Split out BenchmarkInfo and BenchmarkStats to their own header
* Outline BenchmarkStats<> declaration to separate header
* Split out TestRunInfo into its own header

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

I also cleaned out `interfaces_repoter.hpp` from reporter headers
that depend on `reporter_common_base.hpp`. This will not change
anything in the actual inclusion set, but makes it logically
more consistent.
2023-03-31 19:31:51 +02:00
Martin Hořeňovský
2598116aa6 Mark various anonymous classes final 2023-03-20 22:56:43 +01:00
Martin Hořeňovský
28437e1214 Remove pointless member variable from RunContext 2023-03-20 20:34:58 +01:00
Martin Hořeňovský
3c8fb6bbb2 Internal linkage for generator trackers 2023-03-20 19:37:58 +01:00
Martin Hořeňovský
13fae1e2ff Move exception's translation into AssertionResultData message 2023-03-11 16:14:06 +01:00
Martin Hořeňovský
4f7c8cb28a Avoid copying NameAndLocationRef when passed as argument
`NameAndLocationRef` is pretty large type, so even in release build,
it is unlikely to be passed in registers. In addition to the fact
that some platforms currently do not allow passing even small types
in register (Windows ABI!!), it is better to pass it as a ref,
effectively passing around a pointer.
2023-02-20 15:17:35 +01:00
Martin Hořeňovský
00f259aeb2 Move captured output into TestCaseStats when sending testCaseEnded 2023-02-20 14:48:39 +01:00
Martin Hořeňovský
4aa88299af Preconstruct error message in RunContext::handleIncomplete 2023-02-10 21:36:04 +01:00
Martin Hořeňovský
65ffee5189 Don't take ownership of SECTION's name for inactive sections
This eliminates 1945 (432709 -> 430764) allocations from running
`./tests/SelfTest -o /dev/null`. In general terms, this saves
an allocation every time an unvisited `SECTION` is passed, which
means that the saved allocations are quadratic in number of sibling
(same level) `SECTION`s in a test case.
2023-01-29 10:44:20 +01:00
Martin Hořeňovský
43f02027e4 Avoid allocations when looking for trackers
Now we delay allocating owning `NameAndLocation` instances until
we construct a new tracker (because a tracker's lifetime can be
significantly different from the underlying tracked-thing's name).

This saves 4239 allocations (436948 -> 432709) when running
`./tests/SelfTest -o /dev/null`, at some cost to code clarity
due to introducing a new ref type, `NameAndLocationRef`.
2023-01-29 10:14:20 +01:00
Martin Hořeňovský
906552f8c8 Clean up extraneous copies in Messages
This removes 109 allocations from running `tests/SelfTest`
(437057 -> 436948).
2023-01-28 22:14:37 +01:00
Martin Hořeňovský
e5d1eb757f Move AssertionResultData into AssertionResult in RunContext
When running `./tests/SelfTest -o /dev/null`, this saves 109
allocations (437167 -> 437058).
2023-01-28 19:57:38 +01:00
Martin Hořeňovský
2403f5620e Move SectionEndInfo into sectionEnded call in SECTION's destructor
When running `./tests/SelfTest -o /dev/null`, this saves 1272
allocations (437439 -> 437167). In general, this saves multiple
allocations per end of an entered `SECTION`, if the section name
was too long for SSO, because `RunContext::sectionEnded` can then
move the section's name further down the callstack.
2023-01-28 13:00:30 +01:00
Martin Hořeňovský
8359a6b244 Stop exceptions in generator constructors from aborting the binary
Fixes #2615
2023-01-22 16:04:16 +01:00