Commit Graph
483 Commits
Author SHA1 Message Date
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
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ý 9a3d68315b Refactor CATCH_TRAP selection logic to prefer compiler-specific impls
Primarily this means that with Clang we use `__builtin_debugtrap`
if available.
2025-08-08 00:01:38 +02:00
Tim Lyon fbfd13501c Fix warning in catch_unique_ptr::bool() 2025-08-02 00:15:06 +02:00
Martin Hořeňovský ccabd4de89 Add enum types to what is captured by value by default
As it turns out, enums can be used to declare bitfields, and we
cannot form a reference to a bitfield in the cpature. Thus, we add
`std::is_enum` as a criteria to the default for `capture_by_value`,
so that enum-based bitfields are also captured by value and thus
decomposable.

Closes #3001
2025-07-30 20:20:38 +02:00
Martin Hořeňovský a1c7ee115f Don't follow __assume(false) with std::terminate in NDEBUG builds
Having `std::terminate` as the backstop after `__assume(false)`
would trigger W4702 (unreachable code) with MSVC. As we want to
keep `__assume(false)` for the optimization hint in NDEBUG builds,
we have to avoid mixing in `std::terminate` for those builds.

Fixes #3007
2025-07-28 11:03:46 +02:00
Martin Hořeňovský d547cae549 Fix bad error reporting for nested exceptions in default configuration
Bad handling of `TestFailureException` when translating unexpected
exceptions inside assertion macros led to the unexpected exceptions
handling erroring out through throwing the same exception again.

This was then backstopped by the machinery for handling uncaught
exceptions from assertions, which is normally used by the
`CATCH_CONFIG_FAST_COMPILE` machinery, where we assume that it can
only be invoked because the assertion macros are not configured to
catch assertions.

Closes #1292
2025-07-27 21:41:02 +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ý 8c3ffe05e1 Skeleton for applying deprecation warnings 2025-07-21 21:50:43 +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ý ce128f584c Remove assertionPassed fast path from the capture interface
As it is an internal helper for RunContext to speed-up handling of
succesfull assertions, I have no idea why it was added to the
interface back when it was first implemented.
2025-07-20 18:32:25 +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
Martin Hořeňovský f7968e9697 Constify various autoregistry globals to avoid clang-tidy complaints
Closes #2582
2025-07-15 15:23:32 +02:00
Martin Hořeňovský b9c018b38a Use template variables for our trait-likes in Clara 2025-07-15 11:49:39 +02:00
Martin Hořeňovský 6097bd6ee9 Mark code past FAIL and SKIP as unreachable
This helps the compiler see that the execution does not continue,
which in turn means that it is not an error if the user does not
return value from the function after FAIL/SKIP.

Closes #2941
2025-07-14 10:39:48 +02:00
Martin Hořeňovský 39c32b9662 Avoid needless string temporary in template product test registration 2025-07-09 12:29:36 +02:00
Clement CourbetandMartin Hořeňovský 2de12cb05f Allow using only types in TEMPLATE_TEST_CASE_SIG. (#2995)
Right now `TEMPLATE_TEST_CASE_SIG` fails to compile when the signature contains only types:

```
TEMPLATE_TEST_CASE_SIG(
  "TemplateTestSig: compiles with two type parameters",
  "[template][onlytypes]",
  ((typename U, typename V), U, V), (int,int)) {}
```

The trick is to resolve the ambiguity between the two overloads of
`get_wrapper` (`TypeList` and `Nttp`) by making one match more strongly.
We also need to allow `reg_test` to register more than one type.

Add unit tests.

Fixes #2680

---------

Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2025-07-08 14:26:25 +02:00
Omar Boukli-Hacene 8dc9f1a124 Move header from internal
Move `catch_test_run_info.hpp` from directory
`src/catch2/internal/` to be directly under `src/catch2/`.

Header `catch_test_run_info.hpp` defines `Catch::TestRunInfo` which is part of the public API, since it is required when overriding `Catch::EventListenerBase`.

Fixes issue caught by Clang Tidy misc-include-cleaner check.
2025-07-07 17:27:48 +02:00
Jean-Michaël CelerierandMartin Hořeňovský d134b0cae3 clang: do not issue bogus warnings about integer manipulation in hash functions with fsanitize=undefined/integer (#2965)
With -fsanitize=integer every over/under-flowing integer manipulation triggers a warning.
This is extremely useful as it allows to find some non-obvious bugs such as

    for(size_t i = 0; i < N - 1; i++) { ... }

But it comes with a lot of false positives, for instance with every hash function
doing shifting on unsigned integer. Random number generators are also often detected
with this sanitizer.

This marks a few of these functions as safe in this case.

Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
2025-07-02 13:30:36 +02:00
Eisuke Kawashima 416b075211 fix: make deleted member function public 2025-06-15 13:26:38 -06:00
Martin Hořeňovský 1e7b879fae Small include cleanup 2025-04-29 13:15:34 +02:00
Chris Thrasher 5a6d70eebb Remove unused headers 2025-04-27 11:08:41 -06:00
Chris Thrasher 4c93a595a1 Fix typos 2025-04-26 22:46:42 -06:00
abhishekbelgaonkar23 f51dc98dfc Fix: Clang 19 -Wc++20-extensions warning (#2910) 2025-04-07 15:45:34 -06:00
Martin Hořeňovský e260288807 Allow disabling use of __builtin_constant_p in internal macros
Turns out that even in GCC, the expression in `__builtin_cosntant_p`
can end up evaluated and side-effects executed. To allow users to
work around this bug, I added a configuration option to disable its
use in internal macros.

Related to #2925
2024-10-27 20:27:03 +01:00
Pino Toscano a6ee7e20cd Use isatty() when using GNU libc
While isatty() is a POSIX interface and theoretically could be used
more broadly than on Linux and macOS, use a conservative approach and
use it on any platform that uses GNU libc.
2024-10-19 20:36:19 +02:00
Sven Fischer 0b2af56271 Explicitly cast values of different types
In case the warning -Werror=conversion is active with GCC, the warnings
about "conversion from A to B may change value" lead to a compilation
error. This explicitly convert the values to address these warnings.
2024-10-14 21:37:35 +02:00
Martin Hořeňovský e200443b84 Fix compilation error from missing include in xmlwriter.hpp
Fixes #2907
2024-09-15 22:17:39 +02:00
Martin Hořeňovský 18df97df00 Sprinkle some constexpr around to make Jason happy
Most of these will not matter in practice due to C++14 imposing
significant limitations on what else we can make constexpr, and we cannot
have references outliving the constexpr context either way.
2024-09-13 16:40:11 +02:00
Martin Hořeňovský b2b7cbdc31 Remove pointless internal macro 2024-09-13 16:39:53 +02:00
Martin Hořeňovský 412cad546a Avoid needless copy of string in runContext::handleMessage 2024-09-13 16:39:51 +02:00
Mark Jansen 02d3304782 Fix bug in TokenStream parser
When presented with just '-' it would access the string at position [1]
2024-09-13 15:32:49 +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
Omar Boukli-Hacene 9721048a32 Move header from internal
Move `catch_case_sensitive.hpp` from directory
`src/catch2/internal/internal` to be directly under `src/catch2/`.

Header `catch_case_sensitive.hpp` defines `enum CaseSensitive` which is
part of the public API, since it is exposed by the string matcher
contracts.

Fixes issue caught by Clang Tidy misc-include-cleaner check.
2024-08-13 19:51:00 +02:00
Keith StockdaleandMartin Hořeňovský 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
Jeremy Rifkin 8ce2426e53 Handle ANSI escape sequences when performing column wrapping (#2849)
This PR adds functionality to skip around ANSI escape sequences in catch_textflow so they do not contribute to line length and line wrapping code does not split escape sequences in the middle. I've implemented this by creating a AnsiSkippingString abstraction that has a bidirectional iterator that can skip around escape sequences while iterating. Additionally I refactored Column::const_iterator to be iterator-based rather than index-based so this abstraction is a simple drop-in for std::string.

Currently only color sequences are handled, other escape sequences are left unaffected.

Motivation: Text with ANSI color sequences gets messed up when being output by Catch2 #2833.
2024-05-04 23:43:52 +02:00
Martin Hořeňovský fa5a53df17 Explicitly silence Wnon-virtual-dtor in Decomposer and MatchExpr
Closes #2854
2024-04-30 23:51:36 +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
AgostonSzepessyandAgoston Szepessy f2320724a7 Fix build on ARM64EC (#2858)
Remove `#pragma intrinsic(_umul128)` because it doesn't work on
ARM64EC and x64 works without it.

Co-authored-by: Agoston Szepessy <agos@microsoft.com>
2024-04-19 10:36:37 +02:00