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',