diff options
| author | Leitwolf | 2026-10-11 10:09:19 +0300 |
|---|---|---|
| committer | GitHub | 2026-10-11 10:09:19 +0300 |
| commit | ce6fa1996a8e358efdcff53a7628584077d9855c (patch) | |
| tree | d76f5945311828511e3ea55712487f21fbf8f70f | |
| parent | 5633c5c98ec073ae938253bcfc72430d5d9aee89 (diff) | |
| parent | 1f78d8903c2fb18afea8c3055f8df83d8c60d9b5 (diff) | |
| download | catch3-ce6fa1996a8e358efdcff53a7628584077d9855c.tar.gz | |
Merge pull request #4 from Progmasoft/backport/upstream-3.16.1
Backport Catch2 3.16.1
42 files changed, 840 insertions, 455 deletions
diff --git a/.github/workflows/mac-builds.yml b/.github/workflows/mac-builds.yml index 7ba8dac5..7162646b 100644 --- a/.github/workflows/mac-builds.yml +++ b/.github/workflows/mac-builds.yml @@ -15,7 +15,7 @@ jobs: strategy: fail-fast: false matrix: - image: [macos-14, macos-15, macos-15-intel] + image: [macos-15, macos-15-intel] build_type: [Debug, Release] std: [14, 17] diff --git a/CMakeLists.txt b/CMakeLists.txt index 291af7c5..a8f1da6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,7 +35,7 @@ if(CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) endif() project(Catch2 - VERSION 3.16.0 # CML version placeholder, don't delete + VERSION 3.16.1 # CML version placeholder, don't delete LANGUAGES CXX HOMEPAGE_URL "https://github.com/catchorg/Catch2" DESCRIPTION "A modern, C++-native, unit test framework." @@ -10,7 +10,7 @@ owned C++20 library for property checks, snapshots, structured results, and XML serialization. > Catch3 is not an official Catch2 release and is not endorsed by Catch2's -> maintainers. Its current compatibility engine is based on Catch2 3.16.0; +> maintainers. Its current compatibility engine is based on Catch2 3.16.1; > upstream-originated files keep their upstream license. Progmasoft-authored > additions have their own licensing terms. See [the project scope and > compatibility notes](docs/why-catch3.md). diff --git a/docs/deprecations.md b/docs/deprecations.md index 3915ddbc..82f044cc 100644 --- a/docs/deprecations.md +++ b/docs/deprecations.md @@ -62,6 +62,16 @@ specified the filters through the new `-p/--path-filter` parameter, which means that generators are taken into account. +### Reporter event handling functions without noexcept + +> Deprecated in Catch2 3.16.1 + +Catch2 does not try to support reporters throwing from arbitrary +`fooStarting` or `fooEnded` test running event. For legacy reasons, the +event listener interface is declared without `noexcept`, but in the future +it will be changed to use `noexcept` to make this limitation explicit. + + ### Generator interfaces #### Defaulted `UntypedGeneratorBase::isFinite()` diff --git a/docs/release-notes.md b/docs/release-notes.md index 3a2c7734..2ba3b663 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -5,6 +5,7 @@ # Release notes **Contents**<br> +[3.16.1](#3161)<br> [3.16.0](#3160)<br> [3.15.3](#3153)<br> [3.15.2](#3152)<br> @@ -82,6 +83,40 @@ [Even Older versions](#even-older-versions)<br> +## 3.16.1 + +### Fixes +* Fixed optimizations being disabled after including benchmarking headers under MSVC. (#3212) + * This was introduced in 3.16.0's fix for the deopt implementation for MSVC never being used. +* Fixed Console reporter replacing `\0xFF`s in user-provided strings. +* Added missing `<cassert>` include to `catch_generators_range.hpp`. (#3205) + +### Improvements +* Workaround for GCC's quadratic scaling for release builds with number of test cases. + * In practice the improvement is only noticeable with unity builds and/or template product tests. +* Changed how decomposer operators are defined for better compilation times. + * For Catch2's `SelfTest` binary, this provides ~2% build time improvement. +* Changed how decomposer handles stringification for smaller binary sizes. + * For the `SelfTest` binary this is about ~8%. +* Improved compilation costs of multiple assertions in one test cases for release builds. +* Optimized initial test registration at process start: + * Fewer allocations for test cases with many tags. + * Faster processing for test cases with <=2 tags. + * Faster tag validation. + * Faster checking for duplicated test cases. +* Optimized test selection and filtering: +* Optimized filtering tests by test name. + * Running `SelfTest` with `abc*` as the filter pattern is ~10% faster. + +### Miscellaneous +* `catch_discover_tests` registers individual tests with `--order decl` for faster startup. + * For `SelfTest`, this provides 20% improvement in Debug and 5% in Release builds. +* `CMakeConfig::` family of tests use the main build's toolchain file. (#3202) +* Deprecated throwing exceptions from the reporter interface functions. + * Previously, throwing from e.g. `assertionEnded` might, or might not, have worked depending on which assertion has ended. As it is not practical for Catch2 to guard against the reporters failing randomly, and marking so with `noexcept` can provide compilation throughput improvements, I decided to fully forbid it. + * Reporter constructors are allowed to throw and the test binary will report the error nicely before exiting. + * Due to C++ limitations, this deprecation cannot be marked inside the code, only in docs. + ## 3.16.0 diff --git a/docs/upstream-status.md b/docs/upstream-status.md index 99182ad7..ac87b654 100644 --- a/docs/upstream-status.md +++ b/docs/upstream-status.md @@ -13,6 +13,33 @@ licenses or compiler options: - `222e233903d287cda4713fb08e1a959e8d71fd6d`: isolate Clang's pointer-to-bool diagnostic at the upstream reusable-string-stream compatibility boundary. +The branch was reviewed again through +`08092139210881f01a34e60c5f2fd6afa0ea4024`, the commit of Catch2 3.16.1, on +2026-10-11. The eleven changes since the previous reviewed revision were all +backported, in upstream order, and the compatibility engine is now 3.16.1: + +- `d7683484`: a better message when a reporter cannot be initialized. +- `ea5020f0`: every first-party reporter validates its custom arguments. +- `aa01559e`: the throwing-reporter test also throws in `assertionEnded`. +- `45cd3987`: `handleUnexpectedInflightException` is `noexcept`. +- `6adbd539`: MSVC optimizations are enabled again after the benchmark + headers (upstream issue 3212). +- `e87f2882`: test registration with non-type template parameters no longer + scales quadratically. +- `73d546cd`: `REQUIRE` expands its arguments fewer times under GCC and + Clang. +- `b710039a`: the decomposer operators are plain functions and no longer + hidden friends. +- `f899467b`: streaming of reconstructed expressions is type-erased. +- `8f0e70ca`: the retired `macos-14` runner is dropped from the macOS builds. +- `08092139`: version 3.16.1, with its release notes. + +Each applied without a conflict in the sources. The amalgamated distribution +under `extras/` did conflict, because it is generated; it was generated again +from the sources of this repository with `generateAmalgamatedFiles.py`, which +also brought it up to date with fork changes it had not yet picked up. No +fork-owned file under `src/Progmasoft` needed a change. + These are compatibility fixes, not a claim that every future upstream change is automatically incorporated. The existing Windows/macOS build and test workflows remain the verification gate for the fork. diff --git a/docs/why-catch3.md b/docs/why-catch3.md index 7fbeef44..8c900ac7 100644 --- a/docs/why-catch3.md +++ b/docs/why-catch3.md @@ -7,7 +7,7 @@ namespace, build-target, and licensing boundary. The name does **not** mean that this is an official Catch2 v3 release, a new upstream version, or a project endorsed by Catch2's maintainers. The current -compatibility engine is based on Catch2's 3.16.0 development line. The project +compatibility engine is based on Catch2's 3.16.1 development line. The project is still early-stage and does not yet replace or independently reimplement the Catch2 runner. @@ -35,7 +35,7 @@ Catch2's `Catch` namespace, established headers, macros, command-line behavior, reporters, and coding style remain the compatibility surface. Progmasoft features should be added under `Progmasoft::Catch3` and must not require users to rename existing Catch2 tests. The first compatibility goal is source-level -continuity with the included Catch2 3.16.0 base; this is not a promise that every +continuity with the included Catch2 3.16.1 base; this is not a promise that every future Catch2 upstream change will be adopted unchanged or that all downstream projects have already been validated. diff --git a/extras/catch_amalgamated.cpp b/extras/catch_amalgamated.cpp index c6a8de36..230849f1 100644 --- a/extras/catch_amalgamated.cpp +++ b/extras/catch_amalgamated.cpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.16.0 -// Generated: 2026-09-28 00:12:28.744397 +// Catch v3.16.1 +// Generated: 2026-10-11 02:09:25.622664 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -1458,7 +1458,9 @@ namespace Catch { int err = 0; std::FILE* const file = openGuardFile( guardFilePath, err ); if ( file == nullptr ) { +#if defined( _MSC_VER ) char msgBuffer[100] = {}; +#endif std::string errMsg; #if defined( _MSC_VER ) if ( !strerror_s( msgBuffer, err ) ) { @@ -1677,7 +1679,17 @@ namespace Catch { getCurrentMutableContext().setConfig(m_config.get()); // Create reporter(s) so we can route listings through them - auto reporter = prepareReporters(m_config.get()); + IEventListenerPtr reporter; + CATCH_TRY { + auto reporter_ = prepareReporters( m_config.get() ); + swap( reporter, reporter_ ); + } +#if !defined( CATCH_CONFIG_DISABLE_EXCEPTIONS ) + catch ( std::exception& ex ) { + Catch::cerr() << "Error when creating reporter: " << ex.what() << '\n' << std::flush; + return ReporterConstructionErrorExitCode; + } +#endif auto const& invalidSpecs = m_config->testSpec().getInvalidSpecs(); if ( !invalidSpecs.empty() ) { @@ -2586,7 +2598,7 @@ namespace Catch { } Version const& libraryVersion() { - static Version version( 3, 16, 0, "", 0 ); + static Version version( 3, 16, 1, "", 0 ); return version; } @@ -2922,7 +2934,7 @@ namespace Catch { } } - void AssertionHandler::handleUnexpectedInflightException() { + void AssertionHandler::handleUnexpectedInflightException() noexcept { m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction ); } @@ -4234,13 +4246,6 @@ namespace Catch { namespace Catch { - void ITransientExpression::streamReconstructedExpression( - std::ostream& os ) const { - // We can't make this function pure virtual to keep ITransientExpression - // constexpr, so we write error message instead - os << "Some class derived from ITransientExpression without overriding streamReconstructedExpression"; - } - void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) { if( lhs.size() + rhs.size() < 40 && lhs.find('\n') == std::string::npos && @@ -6056,6 +6061,7 @@ ReporterSpec::ReporterSpec( #include <cstdio> +#include <list> #include <sstream> #include <tuple> #include <vector> @@ -6065,7 +6071,10 @@ namespace Catch { // This class encapsulates the idea of a pool of ostringstreams that can be reused. struct StringStreams { std::vector<Detail::unique_ptr<std::ostringstream>> m_streams; - std::vector<std::size_t> m_unused; + // The library can be linked into ASan-instrumented clients without + // instrumenting Catch3 itself. A list avoids mixed-mode libc++ vector + // container annotations for the reusable-index pool. + std::list<std::size_t> m_unused; std::ostringstream m_referenceStream; // Used for copy state/ flags from Detail::Mutex m_mutex; @@ -9530,12 +9539,20 @@ namespace Catch { + #include <ostream> namespace Catch { AutomakeReporter::~AutomakeReporter() = default; + AutomakeReporter::AutomakeReporter( ReporterConfig&& _config ): + StreamingReporterBase( CATCH_MOVE( _config ) ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + + m_preferences.shouldReportAllAssertionStarts = false; + } + void AutomakeReporter::testCaseEnded(TestCaseStats const& _testCaseStats) { // Possible values to emit are PASS, XFAIL, SKIP, FAIL, XPASS and ERROR. m_stream << ":test-result: "; @@ -9837,6 +9854,13 @@ private: } CompactReporter::~CompactReporter() = default; + CompactReporter::CompactReporter( ReporterConfig&& _config ): + StreamingReporterBase( CATCH_MOVE( _config ) ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + + m_preferences.shouldReportAllAssertionStarts = false; + } + } // end namespace Catch @@ -10224,6 +10248,7 @@ ConsoleReporter::ConsoleReporter(ReporterConfig&& config): }; } }())) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); m_preferences.shouldReportAllAssertionStarts = false; } @@ -11037,6 +11062,18 @@ namespace Catch { printSummaryRow( stream, streamColour, "assertions"_sr, columns, 1 ); } + void rejectSuperfluousConfigKeys( + std::map<std::string, std::string> const& options, + std::initializer_list<StringRef> knownKeys ) { + for ( auto const& option : options ) { + StringRef key( option.first ); + CATCH_ENFORCE( std::find( knownKeys.begin(), + knownKeys.end(), + key ) != knownKeys.end(), + "Unknown reporter argument: " << key ); + } + } + } // namespace Catch @@ -11075,6 +11112,7 @@ namespace Catch { JsonReporter::JsonReporter( ReporterConfig&& config ): StreamingReporterBase{ CATCH_MOVE( config ) } { + rejectSuperfluousConfigKeys( m_customOptions, {} ); m_preferences.shouldRedirectStdOut = true; // TBD: Do we want to report all assertions? XML reporter does @@ -11480,11 +11518,13 @@ namespace Catch { : CumulativeReporterBase( CATCH_MOVE(_config) ), xml( m_stream ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + m_preferences.shouldRedirectStdOut = true; m_preferences.shouldReportAllAssertions = false; m_preferences.shouldReportAllAssertionStarts = false; m_shouldStoreSuccesfulAssertions = false; - } + } std::string JunitReporter::getDescription() { return "Reports test results in an XML format that looks like Ant's junitreport target"; @@ -11947,6 +11987,16 @@ namespace Catch { } } + SonarQubeReporter::SonarQubeReporter( ReporterConfig&& config ): + CumulativeReporterBase( CATCH_MOVE( config ) ), xml( m_stream ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertions = false; + m_preferences.shouldReportAllAssertionStarts = false; + m_shouldStoreSuccesfulAssertions = false; + } + void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) { CumulativeReporterBase::testRunStarting(testRunInfo); @@ -12275,6 +12325,14 @@ namespace Catch { } // End anonymous namespace + TAPReporter::TAPReporter( ReporterConfig&& config ): + StreamingReporterBase( CATCH_MOVE( config ) ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + + m_preferences.shouldReportAllAssertions = true; + m_preferences.shouldReportAllAssertionStarts = false; + } + void TAPReporter::testRunStarting( TestRunInfo const& ) { if ( m_config->testSpec().hasFilters() ) { m_stream << "# filters: " << m_config->testSpec() << '\n'; @@ -12346,6 +12404,14 @@ namespace Catch { } // end anonymous namespace + TeamCityReporter::TeamCityReporter( ReporterConfig&& _config ): + StreamingReporterBase( CATCH_MOVE( _config ) ) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + + m_preferences.shouldRedirectStdOut = true; + m_preferences.shouldReportAllAssertionStarts = false; + } + TeamCityReporter::~TeamCityReporter() = default; void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) { @@ -12492,6 +12558,8 @@ namespace Catch { : StreamingReporterBase( CATCH_MOVE(_config) ), m_xml(m_stream) { + rejectSuperfluousConfigKeys( m_customOptions, {} ); + m_preferences.shouldRedirectStdOut = true; m_preferences.shouldReportAllAssertions = true; m_preferences.shouldReportAllAssertionStarts = false; diff --git a/extras/catch_amalgamated.hpp b/extras/catch_amalgamated.hpp index 29279da0..25207b85 100644 --- a/extras/catch_amalgamated.hpp +++ b/extras/catch_amalgamated.hpp @@ -6,8 +6,8 @@ // SPDX-License-Identifier: BSL-1.0 -// Catch v3.16.0 -// Generated: 2026-09-28 00:12:28.337654 +// Catch v3.16.1 +// Generated: 2026-10-11 02:09:25.452537 // ---------------------------------------------------------- // This file is an amalgamation of multiple different files. // You probably shouldn't edit it directly. @@ -1520,7 +1520,7 @@ namespace Catch { *reinterpret_cast<char volatile*>(p) = *reinterpret_cast<char const volatile*>(p); } // TODO equivalent keep_memory() -#if defined(_MSVC_VER) +#if defined(_MSC_VER) #pragma optimize("", on) #endif @@ -2513,6 +2513,9 @@ namespace Catch { // be null... #pragma GCC diagnostic ignored "-Waddress" #pragma GCC diagnostic ignored "-Wnonnull-compare" +#elif defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpointer-bool-conversion" #endif template<typename T> @@ -2523,6 +2526,8 @@ namespace Catch { #if defined(__GNUC__) && !defined(__clang__) #pragma GCC diagnostic pop +#elif defined(__clang__) +#pragma clang diagnostic pop #endif auto get() -> std::ostream& { return *m_oss; } }; @@ -5130,6 +5135,7 @@ namespace Catch { constexpr int UnmatchedTestSpecExitCode = 3; constexpr int AllTestsSkippedExitCode = 4; constexpr int InvalidTestSpecExitCode = 5; + constexpr int ReporterConstructionErrorExitCode = 6; constexpr int TestFailureExitCode = 42; class Session : Detail::NonCopyable { @@ -5531,28 +5537,27 @@ namespace Catch { struct always_false : std::false_type {}; class ITransientExpression { + //! Type-erased streaming function for the derived expression + using StreamFn = void ( * )( void const*, std::ostream& ); + + StreamFn m_streamFn; bool m_isBinaryExpression; bool m_result; - protected: - ~ITransientExpression() = default; - public: constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; } constexpr auto getResult() const -> bool { return m_result; } - //! This function **has** to be overridden by the derived class. - virtual void streamReconstructedExpression( std::ostream& os ) const; - constexpr ITransientExpression( bool isBinaryExpression, bool result ) - : m_isBinaryExpression( isBinaryExpression ), - m_result( result ) - {} - - constexpr ITransientExpression( ITransientExpression const& ) = default; - constexpr ITransientExpression& operator=( ITransientExpression const& ) = default; + constexpr ITransientExpression( StreamFn streamFn, + bool isBinaryExpression, + bool result ): + m_streamFn( streamFn ), + m_isBinaryExpression( isBinaryExpression ), + m_result( result ) {} - friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) { - expr.streamReconstructedExpression(out); + friend std::ostream& operator<<( std::ostream& out, + ITransientExpression const& expr ) { + expr.m_streamFn( &expr, out ); return out; } }; @@ -5565,87 +5570,67 @@ namespace Catch { StringRef m_op; RhsT m_rhs; - void streamReconstructedExpression( std::ostream &os ) const override { - formatReconstructedExpression - ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) ); + static void streamFn( void const* self, std::ostream& out ) { + auto const& expr = *static_cast<BinaryExpr const*>( + static_cast<ITransientExpression const*>( self ) ); + formatReconstructedExpression( + out, + Catch::Detail::stringify( expr.m_lhs ), + expr.m_op, + Catch::Detail::stringify( expr.m_rhs ) ); } public: constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs ) - : ITransientExpression{ true, comparisonResult }, + : ITransientExpression{ &streamFn, true, comparisonResult }, m_lhs( lhs ), m_op( op ), m_rhs( rhs ) {} + }; - template<typename T> - auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } - - template<typename T> - auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const { - static_assert(always_false<T>::value, - "chained comparisons are not supported inside assertions, " - "wrap the expression inside parentheses, or decompose it"); - } +#define CATCH_INTERNAL_DEFINE_BINARY_EXPR_OPERATOR( op ) \ + template <typename LhsT, typename RhsT, typename T> \ + auto operator op( BinaryExpr<LhsT, RhsT> const&, T ) \ + ->BinaryExpr<LhsT, RhsT const&> const { \ + static_assert( always_false<T>::value, |