diff --git a/docs/matchers.md b/docs/matchers.md index 4b9445ae..a013245c 100644 --- a/docs/matchers.md +++ b/docs/matchers.md @@ -6,6 +6,7 @@ [Built-in matchers](#built-in-matchers)
[Writing custom matchers (old style)](#writing-custom-matchers-old-style)
[Writing custom matchers (new style)](#writing-custom-matchers-new-style)
+[Constexpr matchers](#constexpr-matchers)
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,26 @@ and new style matchers arbitrarily. > `MatcherGenericBase` lives in `catch2/matchers/catch_matchers_templated.hpp` +## Constexpr matchers + +> Support for constexpr matchers was introduced in Catch2 vX.Y.Z + +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() ); +} +``` + + --- [Home](Readme.md#top) diff --git a/docs/other-macros.md b/docs/other-macros.md index 79990a6a..de7e4bc6 100644 --- a/docs/other-macros.md +++ b/docs/other-macros.md @@ -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 X.Y.Z + +`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` diff --git a/src/catch2/internal/catch_compiler_capabilities.hpp b/src/catch2/internal/catch_compiler_capabilities.hpp index d0c79243..2d6d0c75 100644 --- a/src/catch2/internal/catch_compiler_capabilities.hpp +++ b/src/catch2/internal/catch_compiler_capabilities.hpp @@ -35,6 +35,18 @@ # 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 // mask themselves as GCC should be ignored. #if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) && !defined(__NVCOMPILER) diff --git a/src/catch2/matchers/catch_matchers.cpp b/src/catch2/matchers/catch_matchers.cpp index 123b3041..d1add581 100644 --- a/src/catch2/matchers/catch_matchers.cpp +++ b/src/catch2/matchers/catch_matchers.cpp @@ -13,13 +13,13 @@ namespace Catch { namespace Matchers { std::string MatcherUntypedBase::toString() const { - if (m_cachedToString.empty()) { - m_cachedToString = describe(); - } - return m_cachedToString; + return describe(); } - MatcherUntypedBase::~MatcherUntypedBase() = default; + std::string MatcherUntypedBase::describe() const { + using namespace std::string_literals; + return "Undescribed matcher"s; + } } // namespace Matchers } // namespace Catch diff --git a/src/catch2/matchers/catch_matchers.hpp b/src/catch2/matchers/catch_matchers.hpp index 90ed3338..6d30c7f0 100644 --- a/src/catch2/matchers/catch_matchers.hpp +++ b/src/catch2/matchers/catch_matchers.hpp @@ -20,10 +20,10 @@ namespace Matchers { class MatcherUntypedBase { public: - MatcherUntypedBase() = default; + constexpr MatcherUntypedBase() = default; - MatcherUntypedBase(MatcherUntypedBase const&) = default; - MatcherUntypedBase(MatcherUntypedBase&&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase const&) = default; + constexpr MatcherUntypedBase(MatcherUntypedBase&&) = default; MatcherUntypedBase& operator = (MatcherUntypedBase const&) = delete; MatcherUntypedBase& operator = (MatcherUntypedBase&&) = delete; @@ -31,9 +31,9 @@ namespace Matchers { std::string toString() const; protected: - virtual ~MatcherUntypedBase(); // = default; - virtual std::string describe() const = 0; - mutable std::string m_cachedToString; + CATCH_DESTRUCTOR_CONSTEXPR virtual ~MatcherUntypedBase() = default; + //! Should be overridden, but we provide default "undescribed" impl + virtual std::string describe() const; }; @@ -215,6 +215,19 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + CATCH_SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) CATCH_REQUIRE_THAT( arg, matcher ) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) CATCH_CHECK_THAT( arg, matcher ) + #endif + + #elif defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -226,6 +239,9 @@ namespace Matchers { #define CATCH_CHECK_THAT( arg, matcher ) (void)(0) #define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define CATCH_STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && !defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr ) @@ -237,6 +253,19 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg ) #define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg ) + #if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE) + #define STATIC_REQUIRE_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #define STATIC_CHECK_THAT( arg, matcher ) \ + static_assert( ( matcher ).match( arg ), #matcher ".match( " #arg " )"); \ + SUCCEED( #matcher ".match( " #arg " )" ) + #else + #define STATIC_REQUIRE_THAT( arg, matcher ) REQUIRE_THAT( arg, matcher ) + #define STATIC_CHECK_THAT( arg, matcher ) CHECK_THAT( arg, matcher ) + #endif + + #elif !defined(CATCH_CONFIG_PREFIX_ALL) && defined(CATCH_CONFIG_DISABLE) #define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0) @@ -248,6 +277,9 @@ namespace Matchers { #define CHECK_THAT( arg, matcher ) (void)(0) #define REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_REQUIRE_THAT( arg, matcher ) (void)(0) + #define STATIC_CHECK_THAT( arg, matcher ) (void)(0) + #endif // end of user facing macro declarations #endif // CATCH_MATCHERS_HPP_INCLUDED diff --git a/src/catch2/matchers/catch_matchers_templated.cpp b/src/catch2/matchers/catch_matchers_templated.cpp index 2fc529d2..e755b479 100644 --- a/src/catch2/matchers/catch_matchers_templated.cpp +++ b/src/catch2/matchers/catch_matchers_templated.cpp @@ -9,7 +9,11 @@ namespace Catch { namespace Matchers { - MatcherGenericBase::~MatcherGenericBase() = default; + + std::string MatcherGenericBase::describe() const { + using namespace std::string_literals; + return "Undescribed generic matcher"s; + } namespace Detail { diff --git a/src/catch2/matchers/catch_matchers_templated.hpp b/src/catch2/matchers/catch_matchers_templated.hpp index 0cd40163..2c6247f8 100644 --- a/src/catch2/matchers/catch_matchers_templated.hpp +++ b/src/catch2/matchers/catch_matchers_templated.hpp @@ -22,12 +22,12 @@ namespace Catch { namespace Matchers { class MatcherGenericBase : public MatcherUntypedBase { + std::string describe() const override; public: - MatcherGenericBase() = default; - ~MatcherGenericBase() override; // = default; + constexpr MatcherGenericBase() = default; - MatcherGenericBase(MatcherGenericBase const&) = default; - MatcherGenericBase(MatcherGenericBase&&) = default; + constexpr MatcherGenericBase(MatcherGenericBase const&) = default; + constexpr MatcherGenericBase(MatcherGenericBase&&) = default; MatcherGenericBase& operator=(MatcherGenericBase const&) = delete; MatcherGenericBase& operator=(MatcherGenericBase&&) = delete; @@ -36,7 +36,9 @@ namespace Matchers { namespace Detail { template - std::array array_cat(std::array && lhs, std::array && rhs) { + constexpr std::array + array_cat( std::array&& lhs, + std::array&& rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); std::copy_n(rhs.begin(), M, arr.begin() + N); @@ -44,7 +46,8 @@ namespace Matchers { } template - std::array array_cat(std::array && lhs, void const* rhs) { + constexpr std::array + array_cat( std::array&& lhs, void const* rhs ) { std::array arr{}; std::copy_n(lhs.begin(), N, arr.begin()); arr[N] = rhs; @@ -52,7 +55,8 @@ namespace Matchers { } template - std::array array_cat(void const* lhs, std::array && rhs) { + constexpr std::array + array_cat( void const* lhs, std::array&& rhs ) { std::array arr{ {lhs} }; std::copy_n(rhs.begin(), N, arr.begin() + 1); return arr; @@ -75,23 +79,31 @@ namespace Matchers { template - bool match_all_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_all_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return true; } template - bool match_all_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_all_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) && match_all_of(arg, matchers, std::index_sequence{}); } template - bool match_any_of(Arg&&, std::array const&, std::index_sequence<>) { + constexpr bool match_any_of( Arg&&, + std::array const&, + std::index_sequence<> ) { return false; } template - bool match_any_of(Arg&& arg, std::array const& matchers, std::index_sequence) { + constexpr bool match_any_of( Arg&& arg, + std::array const& matchers, + std::index_sequence ) { return static_cast(matchers[Idx])->match(arg) || match_any_of(arg, matchers, std::index_sequence{}); } @@ -112,15 +124,18 @@ namespace Matchers { public: MatchAllOfGeneric(MatchAllOfGeneric const&) = delete; MatchAllOfGeneric& operator=(MatchAllOfGeneric const&) = delete; - MatchAllOfGeneric(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; + constexpr MatchAllOfGeneric( MatchAllOfGeneric&& ) = default; + constexpr MatchAllOfGeneric& operator=(MatchAllOfGeneric&&) = default; - MatchAllOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + constexpr MatchAllOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAllOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr explicit MatchAllOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_all_of(arg, m_matchers, std::index_sequence_for{}); } @@ -136,7 +151,7 @@ namespace Matchers { //! Avoids type nesting for `GenericAllOf && GenericAllOf` case template - friend + constexpr friend MatchAllOfGeneric operator && ( MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { @@ -145,7 +160,8 @@ namespace Matchers { //! Avoids type nesting for `GenericAllOf && some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( MatchAllOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { @@ -154,7 +170,8 @@ namespace Matchers { //! Avoids type nesting for `some matcher && GenericAllOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAllOfGeneric> operator && ( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAllOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { @@ -168,15 +185,18 @@ namespace Matchers { public: MatchAnyOfGeneric(MatchAnyOfGeneric const&) = delete; MatchAnyOfGeneric& operator=(MatchAnyOfGeneric const&) = delete; - MatchAnyOfGeneric(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; + constexpr MatchAnyOfGeneric( MatchAnyOfGeneric&& ) = default; + constexpr MatchAnyOfGeneric& operator=(MatchAnyOfGeneric&&) = default; - MatchAnyOfGeneric(MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND) + constexpr MatchAnyOfGeneric( + MatcherTs const&... matchers CATCH_ATTR_LIFETIMEBOUND ) : m_matchers{ {std::addressof(matchers)...} } {} - explicit MatchAnyOfGeneric(std::array matchers) : m_matchers{matchers} {} + constexpr explicit MatchAnyOfGeneric( + std::array matchers ): + m_matchers{ matchers } {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return match_any_of(arg, m_matchers, std::index_sequence_for{}); } @@ -192,7 +212,8 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case template - friend MatchAnyOfGeneric operator || ( + constexpr friend MatchAnyOfGeneric + operator||( MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND ) { return MatchAnyOfGeneric{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))}; @@ -200,7 +221,8 @@ namespace Matchers { //! Avoids type nesting for `GenericAnyOf || some matcher` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( MatchAnyOfGeneric&& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { @@ -209,7 +231,8 @@ namespace Matchers { //! Avoids type nesting for `some matcher || GenericAnyOf` case template - friend std::enable_if_t, + constexpr friend std::enable_if_t< + is_matcher_v, MatchAnyOfGeneric> operator || ( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatchAnyOfGeneric&& rhs CATCH_ATTR_LIFETIMEBOUND) { @@ -225,14 +248,15 @@ namespace Matchers { public: MatchNotOfGeneric(MatchNotOfGeneric const&) = delete; MatchNotOfGeneric& operator=(MatchNotOfGeneric const&) = delete; - MatchNotOfGeneric(MatchNotOfGeneric&&) = default; - MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; + constexpr MatchNotOfGeneric( MatchNotOfGeneric&& ) = default; + constexpr MatchNotOfGeneric& operator=(MatchNotOfGeneric&&) = default; - explicit MatchNotOfGeneric(MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND) + constexpr explicit MatchNotOfGeneric( + MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) : m_matcher{matcher} {} template - bool match(Arg&& arg) const { + constexpr bool match( Arg&& arg ) const { return !m_matcher.match(arg); } @@ -241,7 +265,7 @@ namespace Matchers { } //! Negating negation can just unwrap and return underlying matcher - friend MatcherT const& + constexpr friend MatcherT const& operator!( MatchNotOfGeneric const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return matcher.m_matcher; @@ -252,14 +276,18 @@ namespace Matchers { // compose only generic matchers template - std::enable_if_t, Detail::MatchAllOfGeneric> + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAllOfGeneric> operator&&( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; } template - std::enable_if_t, Detail::MatchAnyOfGeneric> + constexpr std::enable_if_t< + Detail::are_generic_matchers_v, + Detail::MatchAnyOfGeneric> operator||( MatcherLHS const& lhs CATCH_ATTR_LIFETIMEBOUND, MatcherRHS const& rhs CATCH_ATTR_LIFETIMEBOUND ) { return { lhs, rhs }; @@ -267,7 +295,8 @@ namespace Matchers { //! Wrap provided generic matcher in generic negator template - std::enable_if_t, Detail::MatchNotOfGeneric> + constexpr std::enable_if_t, + Detail::MatchNotOfGeneric> operator!( MatcherT const& matcher CATCH_ATTR_LIFETIMEBOUND ) { return Detail::MatchNotOfGeneric{matcher}; } diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 58c05205..1e192a1c 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -62,6 +62,7 @@ cc_test( "SelfTest/UsageTests/Exception.tests.cpp", "SelfTest/UsageTests/Generators.tests.cpp", "SelfTest/UsageTests/Matchers.tests.cpp", + "SelfTest/UsageTests/MatchersConstexpr.tests.cpp", "SelfTest/UsageTests/MatchersRanges.tests.cpp", "SelfTest/UsageTests/Message.tests.cpp", "SelfTest/UsageTests/Misc.tests.cpp", diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3329e90f..7429734f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -124,8 +124,9 @@ set(TEST_SOURCES ${SELF_TEST_DIR}/UsageTests/ToStringWhich.tests.cpp ${SELF_TEST_DIR}/UsageTests/Tricky.tests.cpp ${SELF_TEST_DIR}/UsageTests/VariadicMacros.tests.cpp - ${SELF_TEST_DIR}/UsageTests/MatchersRanges.tests.cpp ${SELF_TEST_DIR}/UsageTests/Matchers.tests.cpp + ${SELF_TEST_DIR}/UsageTests/MatchersConstexpr.tests.cpp + ${SELF_TEST_DIR}/UsageTests/MatchersRanges.tests.cpp ) set(TEST_HEADERS diff --git a/tests/ExtraTests/CMakeLists.txt b/tests/ExtraTests/CMakeLists.txt index 886c059e..731bcf1c 100644 --- a/tests/ExtraTests/CMakeLists.txt +++ b/tests/ExtraTests/CMakeLists.txt @@ -174,7 +174,7 @@ target_compile_definitions(DeferredStaticChecks PRIVATE "CATCH_CONFIG_RUNTIME_ST add_test(NAME DeferredStaticChecks COMMAND DeferredStaticChecks -r compact) set_tests_properties(DeferredStaticChecks PROPERTIES - PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed\nassertions: 3 \\| 3 failed" + PASS_REGULAR_EXPRESSION "test cases: 1 \\| 1 failed\nassertions: 3 \\| 3 failed;test cases: 1 \\| 1 failed\nassertions: 4 \\| 4 failed" ) add_executable(MixingClearedAndUnclearedMessages ${TESTS_DIR}/X06-MixingClearedAndUnclearedMessages.cpp) diff --git a/tests/ExtraTests/X02-DisabledMacros.cpp b/tests/ExtraTests/X02-DisabledMacros.cpp index 0c051acc..3449ab73 100644 --- a/tests/ExtraTests/X02-DisabledMacros.cpp +++ b/tests/ExtraTests/X02-DisabledMacros.cpp @@ -53,6 +53,8 @@ TEST_CASE( "Disabled Macros" ) { REQUIRE_THAT( 1, Catch::Matchers::Predicate( []( int ) { return false; } ) ); BENCHMARK( "Disabled benchmark" ) { REQUIRE( 1 == 2 ); }; + + STATIC_REQUIRE_THAT( 1, Catch::Matchers::Predicate( []( int ) { return false; } ) ); } struct DisabledFixture {}; diff --git a/tests/ExtraTests/X05-DeferredStaticChecks.cpp b/tests/ExtraTests/X05-DeferredStaticChecks.cpp index 8005dbcf..79ff35cc 100644 --- a/tests/ExtraTests/X05-DeferredStaticChecks.cpp +++ b/tests/ExtraTests/X05-DeferredStaticChecks.cpp @@ -12,10 +12,36 @@ */ #include +#include + +#if defined( CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED ) + +namespace { + struct MatchNoneMatcher final : public Catch::Matchers::MatcherGenericBase { + public: + template + constexpr bool match( Any&& ) const { + return false; + } + + std::string describe() const override { + using namespace std::string_literals; + return "Matches anything"s; + } + }; + + constexpr MatchNoneMatcher MatchNone() { return MatchNoneMatcher(); } + +} // namespace + +#endif TEST_CASE("Deferred static checks") { STATIC_CHECK(1 == 2); STATIC_CHECK_FALSE(1 != 2); +#if defined(CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED) + STATIC_CHECK_THAT(1, MatchNone()); +#endif // This last assertion must be executed too CHECK(1 == 2); } diff --git a/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp b/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp new file mode 100644 index 00000000..edd03519 --- /dev/null +++ b/tests/SelfTest/UsageTests/MatchersConstexpr.tests.cpp @@ -0,0 +1,52 @@ + +// 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 +#include + +#if defined( CATCH_INTERNAL_CONSTEXPR_MATCHERS_ENABLED ) + +namespace { + struct MatchAllMatcher final : public Catch::Matchers::MatcherGenericBase { + public: + template + constexpr bool match( Any&& ) const { + return true; + } + + std::string describe() const override { + using namespace std::string_literals; + return "Matches anything"s; + } + }; + + constexpr MatchAllMatcher MatchAll() { return MatchAllMatcher(); } + +} // namespace + +TEST_CASE( "Constexpr support for matchers", "[constexpr][matchers][approvals]" ) { + STATIC_REQUIRE( MatchAll().match( 1 ) ); + STATIC_REQUIRE_THAT( 1, MatchAll() ); +} + +// Combining matchers needs C++26 and P2738, so they are in separate preprocessor block +# if __cpp_constexpr >= 202306L + +TEST_CASE("Constexpr support for combining matchers", + "[constexpr][matchers][approvals]") { + STATIC_REQUIRE( ( MatchAll() && MatchAll() ).match( 1 ) ); + STATIC_REQUIRE( ( MatchAll() || MatchAll() ).match( 1 ) ); + STATIC_REQUIRE( ( !!MatchAll() ).match( 1 ) ); + STATIC_REQUIRE_THAT( 1, MatchAll() && MatchAll() ); + STATIC_REQUIRE_THAT( 1, MatchAll() || MatchAll() ); + STATIC_REQUIRE_THAT( 1, !!MatchAll() ); +} + +#endif // __cpp_constexpr >= 202306L + +#endif diff --git a/tests/meson.build b/tests/meson.build index 58302b7a..0e44eaab 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -49,6 +49,7 @@ self_test_sources = files( 'SelfTest/UsageTests/Exception.tests.cpp', 'SelfTest/UsageTests/Generators.tests.cpp', 'SelfTest/UsageTests/Matchers.tests.cpp', + 'SelfTest/UsageTests/MatchersConstexpr.tests.cpp', 'SelfTest/UsageTests/MatchersRanges.tests.cpp', 'SelfTest/UsageTests/Message.tests.cpp', 'SelfTest/UsageTests/Misc.tests.cpp',