mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-10-30 19:57:10 +01:00 
			
		
		
		
	Feature: generic matchers (#1843)
This commit extends the Matchers feature with the ability to have type-independent (e.g. templated) matchers. This is done by adding a new base type that Matchers can extend, `MatcherGenericBase`, and overloads of operators `!`, `&&` and `||` that handle matchers extending `MatcherGenericBase` in a special manner. These new matchers can also take their arguments as values and non-const references. Closes #1307 Closes #1553 Closes #1554 Co-authored-by: Martin Hořeňovský <martin.horenovsky@gmail.com>
This commit is contained in:
		| @@ -77,6 +77,7 @@ set(INTERNAL_HEADERS | ||||
|     ${SOURCES_DIR}/catch_matchers_floating.h | ||||
|     ${SOURCES_DIR}/catch_matchers_generic.hpp | ||||
|     ${SOURCES_DIR}/catch_matchers_string.h | ||||
|     ${SOURCES_DIR}/catch_matchers_templates.hpp | ||||
|     ${SOURCES_DIR}/catch_matchers_vector.h | ||||
|     ${SOURCES_DIR}/catch_message.h | ||||
|     ${SOURCES_DIR}/catch_meta.hpp | ||||
| @@ -159,6 +160,7 @@ set(IMPL_SOURCES | ||||
|     ${SOURCES_DIR}/catch_matchers_floating.cpp | ||||
|     ${SOURCES_DIR}/catch_matchers_generic.cpp | ||||
|     ${SOURCES_DIR}/catch_matchers_string.cpp | ||||
|     ${SOURCES_DIR}/catch_matchers_templates.cpp | ||||
|     ${SOURCES_DIR}/catch_message.cpp | ||||
|     ${SOURCES_DIR}/catch_output_redirect.cpp | ||||
|     ${SOURCES_DIR}/catch_registry_hub.cpp | ||||
|   | ||||
| @@ -17,7 +17,7 @@ namespace Catch { | ||||
|     // the Equals matcher (so the header does not mention matchers) | ||||
|     void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) { | ||||
|         std::string exceptionMessage = Catch::translateActiveException(); | ||||
|         MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString ); | ||||
|         MatchExpr<std::string, StringMatcher const&> expr( std::move(exceptionMessage), matcher, matcherString ); | ||||
|         handler.handleExpr( expr ); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -16,13 +16,13 @@ namespace Catch { | ||||
|  | ||||
|     template<typename ArgT, typename MatcherT> | ||||
|     class MatchExpr : public ITransientExpression { | ||||
|         ArgT const& m_arg; | ||||
|         ArgT && m_arg; | ||||
|         MatcherT m_matcher; | ||||
|         StringRef m_matcherString; | ||||
|     public: | ||||
|         MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString ) | ||||
|         :   ITransientExpression{ true, matcher.match( arg ) }, | ||||
|             m_arg( arg ), | ||||
|         MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString ) | ||||
|         :   ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose | ||||
|             m_arg( std::forward<ArgT>(arg) ), | ||||
|             m_matcher( matcher ), | ||||
|             m_matcherString( matcherString ) | ||||
|         {} | ||||
| @@ -42,8 +42,8 @@ namespace Catch { | ||||
|     void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ); | ||||
|  | ||||
|     template<typename ArgT, typename MatcherT> | ||||
|     auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> { | ||||
|         return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString ); | ||||
|     auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> { | ||||
|         return MatchExpr<ArgT, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString ); | ||||
|     } | ||||
|  | ||||
| } // namespace Catch | ||||
|   | ||||
							
								
								
									
										32
									
								
								src/catch2/catch_matchers_templates.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/catch2/catch_matchers_templates.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #include <catch2/catch_matchers_templates.hpp> | ||||
|  | ||||
| namespace Catch { | ||||
| namespace Matchers { | ||||
|     namespace Impl { | ||||
|         MatcherGenericBase::~MatcherGenericBase() {} | ||||
|  | ||||
|         std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end) { | ||||
|             std::string description; | ||||
|             std::size_t combined_size = 4; | ||||
|             for ( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { | ||||
|                 combined_size += desc->size(); | ||||
|             } | ||||
|             combined_size += (descriptions_end - descriptions_begin - 1) * combine.size(); | ||||
|  | ||||
|             description.reserve(combined_size); | ||||
|  | ||||
|             description += "( "; | ||||
|             bool first = true; | ||||
|             for( auto desc = descriptions_begin; desc != descriptions_end; ++desc ) { | ||||
|                 if( first ) | ||||
|                     first = false; | ||||
|                 else | ||||
|                     description += combine; | ||||
|                 description += *desc; | ||||
|             } | ||||
|             description += " )"; | ||||
|             return description; | ||||
|         } | ||||
|     } | ||||
| } // namespace Matchers | ||||
| } // namespace Catch | ||||
							
								
								
									
										262
									
								
								src/catch2/catch_matchers_templates.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										262
									
								
								src/catch2/catch_matchers_templates.hpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,262 @@ | ||||
| #ifndef TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED | ||||
| #define TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED | ||||
|  | ||||
| #include <catch2/catch_common.h> | ||||
| #include <catch2/catch_matchers.h> | ||||
| #include <catch2/catch_stringref.h> | ||||
|  | ||||
| #include <array> | ||||
| #include <algorithm> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include <type_traits> | ||||
| #include <utility> | ||||
|  | ||||
| namespace Catch { | ||||
| namespace Matchers { | ||||
|     namespace Impl { | ||||
|  | ||||
|         template<typename... MatcherTs> struct MatchAllOfGeneric; | ||||
|         template<typename... MatcherTs> struct MatchAnyOfGeneric; | ||||
|         template<typename MatcherT> struct MatchNotOfGeneric; | ||||
|  | ||||
|         struct MatcherGenericBase : MatcherUntypedBase { | ||||
|             virtual ~MatcherGenericBase(); | ||||
|         }; | ||||
|  | ||||
|         template<std::size_t N, std::size_t M> | ||||
|         std::array<void const*, N + M> array_cat(std::array<void const*, N> && lhs, std::array<void const*, M> && rhs) { | ||||
|             std::array<void const*, N + M> arr{}; | ||||
|             std::copy_n(lhs.begin(), N, arr.begin()); | ||||
|             std::copy_n(rhs.begin(), M, arr.begin() + N); | ||||
|             return arr; | ||||
|         } | ||||
|  | ||||
|         template<std::size_t N> | ||||
|         std::array<void const*, N+1> array_cat(std::array<void const*, N> && lhs, void const* rhs) { | ||||
|             std::array<void const*, N+1> arr{}; | ||||
|             std::copy_n(lhs.begin(), N, arr.begin()); | ||||
|             arr[N] = rhs; | ||||
|             return arr; | ||||
|         } | ||||
|  | ||||
|         template<std::size_t N> | ||||
|         std::array<void const*, N+1> array_cat(void const* lhs, std::array<void const*, N> && rhs) { | ||||
|             std::array<void const*, N+1> arr{lhs}; | ||||
|             std::copy_n(rhs.begin(), N, arr.begin() + 1); | ||||
|             return arr; | ||||
|         } | ||||
|  | ||||
|         #ifdef CATCH_CPP17_OR_GREATER | ||||
|  | ||||
|         using std::conjunction; | ||||
|  | ||||
|         #else // CATCH_CPP17_OR_GREATER | ||||
|  | ||||
|         template<typename... Cond> | ||||
|         struct conjunction : std::true_type {}; | ||||
|  | ||||
|         template<typename Cond, typename... Rest> | ||||
|         struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {}; | ||||
|  | ||||
|         #endif // CATCH_CPP17_OR_GREATER | ||||
|  | ||||
|         template<typename T> | ||||
|         using is_generic_matcher = std::is_base_of< | ||||
|             Catch::Matchers::Impl::MatcherGenericBase, | ||||
|             typename std::remove_cv<typename std::remove_reference<T>::type>::type | ||||
|         >; | ||||
|  | ||||
|         template<typename... Ts> | ||||
|         using are_generic_matchers = conjunction<is_generic_matcher<Ts>...>; | ||||
|  | ||||
|         template<typename T> | ||||
|         using is_matcher = std::is_base_of< | ||||
|             Catch::Matchers::Impl::MatcherUntypedBase, | ||||
|             typename std::remove_cv<typename std::remove_reference<T>::type>::type | ||||
|         >; | ||||
|  | ||||
|  | ||||
|         template<std::size_t N, typename Arg> | ||||
|         bool match_all_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) { | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices> | ||||
|         bool match_all_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) { | ||||
|             return static_cast<T const*>(matchers[Idx])->match(arg) && match_all_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{}); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         template<std::size_t N, typename Arg> | ||||
|         bool match_any_of(Arg&&, std::array<void const*, N> const&, std::index_sequence<>) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         template<typename T, typename... MatcherTs, std::size_t N, typename Arg, std::size_t Idx, std::size_t... Indices> | ||||
|         bool match_any_of(Arg&& arg, std::array<void const*, N> const& matchers, std::index_sequence<Idx, Indices...>) { | ||||
|             return static_cast<T const*>(matchers[Idx])->match(arg) || match_any_of<MatcherTs...>(arg, matchers, std::index_sequence<Indices...>{}); | ||||
|         } | ||||
|  | ||||
|         std::string describe_multi_matcher(StringRef combine, std::string const* descriptions_begin, std::string const* descriptions_end); | ||||
|  | ||||
|         template<typename... MatcherTs, std::size_t... Idx> | ||||
|         std::string describe_multi_matcher(StringRef combine, std::array<void const*, sizeof...(MatcherTs)> const& matchers, std::index_sequence<Idx...>) { | ||||
|             std::array<std::string, sizeof...(MatcherTs)> descriptions {{ | ||||
|                 static_cast<MatcherTs const*>(matchers[Idx])->toString()... | ||||
|             }}; | ||||
|  | ||||
|             return describe_multi_matcher(combine, descriptions.data(), descriptions.data() + descriptions.size()); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         template<typename... MatcherTs> | ||||
|         struct MatchAllOfGeneric : MatcherGenericBase { | ||||
|             MatchAllOfGeneric(MatcherTs const&... matchers) : m_matchers{std::addressof(matchers)...} {} | ||||
|             explicit MatchAllOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {} | ||||
|  | ||||
|             template<typename Arg> | ||||
|             bool match(Arg&& arg) const { | ||||
|                 return match_all_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{}); | ||||
|             } | ||||
|  | ||||
|             std::string describe() const override { | ||||
|                 return describe_multi_matcher<MatcherTs...>(" and "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{}); | ||||
|             } | ||||
|  | ||||
|             std::array<void const*, sizeof...(MatcherTs)> m_matchers; | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         template<typename... MatcherTs> | ||||
|         struct MatchAnyOfGeneric : MatcherGenericBase { | ||||
|             MatchAnyOfGeneric(MatcherTs const&... matchers) : m_matchers{std::addressof(matchers)...} {} | ||||
|             explicit MatchAnyOfGeneric(std::array<void const*, sizeof...(MatcherTs)> matchers) : m_matchers{matchers} {} | ||||
|  | ||||
|             template<typename Arg> | ||||
|             bool match(Arg&& arg) const { | ||||
|                 return match_any_of<MatcherTs...>(arg, m_matchers, std::index_sequence_for<MatcherTs...>{}); | ||||
|             } | ||||
|  | ||||
|             std::string describe() const override { | ||||
|                 return describe_multi_matcher<MatcherTs...>(" or "_sr, m_matchers, std::index_sequence_for<MatcherTs...>{}); | ||||
|             } | ||||
|  | ||||
|             std::array<void const*, sizeof...(MatcherTs)> m_matchers; | ||||
|         }; | ||||
|  | ||||
|  | ||||
|         template<typename MatcherT> | ||||
|         struct MatchNotOfGeneric : MatcherGenericBase { | ||||
|             explicit MatchNotOfGeneric(MatcherT const& matcher) : m_matcher{matcher} {} | ||||
|  | ||||
|             template<typename Arg> | ||||
|             bool match(Arg&& arg) const { | ||||
|                 return !m_matcher.match(arg); | ||||
|             } | ||||
|  | ||||
|             std::string describe() const override { | ||||
|                 return "not " + m_matcher.toString(); | ||||
|             } | ||||
|  | ||||
|             MatcherT const& m_matcher; | ||||
|         }; | ||||
|  | ||||
|         // compose only generic matchers | ||||
|         template<typename MatcherLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherRHS>>::type | ||||
|         operator && (MatcherLHS const& lhs, MatcherRHS const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<are_generic_matchers<MatcherLHS, MatcherRHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherRHS>>::type | ||||
|         operator || (MatcherLHS const& lhs, MatcherRHS const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherT> | ||||
|         typename std::enable_if<is_generic_matcher<MatcherT>::value, MatchNotOfGeneric<MatcherT>>::type | ||||
|         operator ! (MatcherT const& matcher) { | ||||
|             return MatchNotOfGeneric<MatcherT>{matcher}; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // compose mixed generic and non-generic matchers | ||||
|         template<typename MatcherLHS, typename ArgRHS> | ||||
|         typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type | ||||
|         operator && (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|         template<typename ArgLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type | ||||
|         operator && (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherLHS, typename ArgRHS> | ||||
|         typename std::enable_if<is_generic_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatcherBase<ArgRHS>>>::type | ||||
|         operator || (MatcherLHS const& lhs, MatcherBase<ArgRHS> const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|         template<typename ArgLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<is_generic_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatcherBase<ArgLHS>, MatcherRHS>>::type | ||||
|         operator || (MatcherBase<ArgLHS> const& lhs, MatcherRHS const& rhs) { | ||||
|             return {lhs, rhs}; | ||||
|         } | ||||
|  | ||||
|  | ||||
|         // avoid deep nesting of matcher templates | ||||
|         template<typename... MatchersLHS, typename... MatchersRHS> | ||||
|         MatchAllOfGeneric<MatchersLHS..., MatchersRHS...> | ||||
|         operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) { | ||||
|             return MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; | ||||
|         } | ||||
|  | ||||
|         template<typename... MatchersLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAllOfGeneric<MatchersLHS..., MatcherRHS>>::type | ||||
|         operator && (MatchAllOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) { | ||||
|             return MatchAllOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherLHS, typename... MatchersRHS> | ||||
|         typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAllOfGeneric<MatcherLHS, MatchersRHS...>>::type | ||||
|         operator && (MatcherLHS const& lhs, MatchAllOfGeneric<MatchersRHS...> && rhs) { | ||||
|             return MatchAllOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))}; | ||||
|         } | ||||
|  | ||||
|         template<typename... MatchersLHS, typename... MatchersRHS> | ||||
|         MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...> | ||||
|         operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) { | ||||
|             return MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))}; | ||||
|         } | ||||
|  | ||||
|         template<typename... MatchersLHS, typename MatcherRHS> | ||||
|         typename std::enable_if<is_matcher<MatcherRHS>::value, MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>>::type | ||||
|         operator || (MatchAnyOfGeneric<MatchersLHS...> && lhs, MatcherRHS const& rhs) { | ||||
|             return MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherLHS, typename... MatchersRHS> | ||||
|         typename std::enable_if<is_matcher<MatcherLHS>::value, MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>>::type | ||||
|         operator || (MatcherLHS const& lhs, MatchAnyOfGeneric<MatchersRHS...> && rhs) { | ||||
|             return MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))}; | ||||
|         } | ||||
|  | ||||
|         template<typename MatcherT> | ||||
|         MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) { | ||||
|             return matcher.m_matcher; | ||||
|         } | ||||
|  | ||||
|     } // namespace Impl | ||||
|  | ||||
| } // namespace Matchers | ||||
|  | ||||
| using namespace Matchers; | ||||
| using Matchers::Impl::MatcherGenericBase; | ||||
|  | ||||
| } // namespace Catch | ||||
|  | ||||
| #endif //TWOBLUECUBES_CATCH_MATCHERS_TEMPLATES_HPP_INCLUDED | ||||
| @@ -85,6 +85,13 @@ Nor would this | ||||
| :test-result: PASS CAPTURE parses string and character constants | ||||
| :test-result: PASS Capture and info messages | ||||
| :test-result: PASS Character pretty printing | ||||
| :test-result: PASS Combining MatchAllOfGeneric does not nest | ||||
| :test-result: PASS Combining MatchAnyOfGeneric does not nest | ||||
| :test-result: PASS Combining MatchNotOfGeneric does not nest | ||||
| :test-result: PASS Combining concrete matchers does not use templated matchers | ||||
| :test-result: PASS Combining only templated matchers | ||||
| :test-result: PASS Combining templated and concrete matchers | ||||
| :test-result: PASS Combining templated matchers | ||||
| :test-result: PASS Commas in various macros are allowed | ||||
| :test-result: PASS Comparing function pointers | ||||
| :test-result: PASS Comparison ops | ||||
| @@ -147,6 +154,7 @@ Nor would this | ||||
| :test-result: PASS Ordering comparison checks that should succeed | ||||
| :test-result: PASS Our PCG implementation provides expected results for known seeds | ||||
| :test-result: FAIL Output from all sections is reported | ||||
| :test-result: PASS Overloaded comma or address-of operators are not used | ||||
| :test-result: PASS Parse test names and tags | ||||
| :test-result: PASS Pointers can be compared to null | ||||
| :test-result: PASS Precision of floating point stringification can be set | ||||
|   | ||||
| @@ -263,6 +263,37 @@ ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 2 == 2 | ||||
| ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 3 == 3 | ||||
| ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 4 == 4 | ||||
| ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 5 == 5 | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && MatcherB() && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || MatcherB() || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) | ||||
| Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>> >::value' | ||||
| Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) | ||||
| Matchers.tests.cpp:<line number>: passed: vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) ) | ||||
| Matchers.tests.cpp:<line number>: passed: str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } ) | ||||
| Matchers.tests.cpp:<line number>: passed: container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) | ||||
| Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} | ||||
| Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} | ||||
| Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3} | ||||
| @@ -883,6 +914,10 @@ RandomNumberGeneration.tests.cpp:<line number>: passed: rng() == 0x<hex digits> | ||||
| 4261393167 (0x<hex digits>) | ||||
| Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section one' | ||||
| Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section two' | ||||
| Matchers.tests.cpp:<line number>: passed: (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed | ||||
| Matchers.tests.cpp:<line number>: passed: &EvilMatcher(), EvilAddressOfOperatorUsed | ||||
| Matchers.tests.cpp:<line number>: passed: EvilMatcher() || EvilMatcher() && !EvilMatcher() | ||||
| Matchers.tests.cpp:<line number>: passed: (EvilMatcher() && EvilMatcher()) || !EvilMatcher() | ||||
| CmdLine.tests.cpp:<line number>: passed: spec.hasFilters() == false for: false == false | ||||
| CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcA ) == false for: false == false | ||||
| CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcB ) == false for: false == false | ||||
|   | ||||
| @@ -1380,6 +1380,6 @@ due to unexpected exception with message: | ||||
|   Why would you throw a std::string? | ||||
|  | ||||
| =============================================================================== | ||||
| test cases:  320 |  246 passed |  70 failed |  4 failed as expected | ||||
| assertions: 1792 | 1640 passed | 131 failed | 21 failed as expected | ||||
| test cases:  328 |  254 passed |  70 failed |  4 failed as expected | ||||
| assertions: 1827 | 1675 passed | 131 failed | 21 failed as expected | ||||
|  | ||||
|   | ||||
| @@ -2116,6 +2116,217 @@ ToStringGeneral.tests.cpp:<line number>: PASSED: | ||||
| with expansion: | ||||
|   5 == 5 | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining MatchAllOfGeneric does not nest | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch:: | ||||
|   Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) | ||||
|   1 ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), | ||||
|   Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, | ||||
|   MatcherD> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) | ||||
|   1 and equals: true ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining MatchAnyOfGeneric does not nest | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch:: | ||||
|   Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 | ||||
|   ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), | ||||
|   Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, | ||||
|   MatcherD> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 | ||||
|   or equals: true ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining MatchNotOfGeneric does not nest | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric | ||||
|   <MatcherA> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 0, !MatcherA() ) | ||||
| with expansion: | ||||
|   0 not equals: (int) 1 or (float) 1.0f | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(!!MatcherA()), MatcherA const& >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, !!MatcherA() ) | ||||
| with expansion: | ||||
|   1 equals: (int) 1 or (float) 1.0f | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl:: | ||||
|   MatchNotOfGeneric<MatcherA> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 0, !!!MatcherA() ) | ||||
| with expansion: | ||||
|   0 not equals: (int) 1 or (float) 1.0f | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, !!!!MatcherA() ) | ||||
| with expansion: | ||||
|   1 equals: (int) 1 or (float) 1.0f | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining concrete matchers does not use templated matchers | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith | ||||
|   ("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string> | ||||
|   >::value | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining only templated matchers | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl:: | ||||
|   MatchAnyOfGeneric<MatcherA, MatcherB> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() || MatcherB() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl:: | ||||
|   MatchAllOfGeneric<MatcherA, MatcherB> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() && MatcherB() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
| with message: | ||||
|   std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl:: | ||||
|   MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric | ||||
|   <MatcherB>> >::value | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( 1, MatcherA() || !MatcherB() ) | ||||
| with expansion: | ||||
|   1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining templated and concrete matchers | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) ) | ||||
| with expansion: | ||||
|   { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, | ||||
|   3, 1 } ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") ) | ||||
| with expansion: | ||||
|   "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } | ||||
|   and ends with: "bar" ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") ) | ||||
| with expansion: | ||||
|   "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' | ||||
|   } and ends with: "bar" ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") ) | ||||
| with expansion: | ||||
|   "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" | ||||
|   and ends with: "bar" ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") ) | ||||
| with expansion: | ||||
|   "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: | ||||
|   "foo" and ends with: "bar" ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) ) | ||||
| with expansion: | ||||
|   "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" | ||||
|   and ends with: "bar" ) ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) ) | ||||
| with expansion: | ||||
|   "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', | ||||
|   'f', 'b', 'a', 'r' } ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Combining templated matchers | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THAT( container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) ) | ||||
| with expansion: | ||||
|   { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 | ||||
|   } ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Commas in various macros are allowed | ||||
| ------------------------------------------------------------------------------- | ||||
| @@ -6414,6 +6625,24 @@ Message.tests.cpp:<line number>: FAILED: | ||||
| explicitly with message: | ||||
|   Message from section two | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Overloaded comma or address-of operators are not used | ||||
| ------------------------------------------------------------------------------- | ||||
| Matchers.tests.cpp:<line number> | ||||
| ............................................................................... | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THROWS_AS( (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_NOTHROW( EvilMatcher() || EvilMatcher() && !EvilMatcher() ) | ||||
|  | ||||
| Matchers.tests.cpp:<line number>: PASSED: | ||||
|   REQUIRE_NOTHROW( (EvilMatcher() && EvilMatcher()) || !EvilMatcher() ) | ||||
|  | ||||
| ------------------------------------------------------------------------------- | ||||
| Parse test names and tags | ||||
|   Empty test spec should have no filters | ||||
| @@ -14053,6 +14282,6 @@ Misc.tests.cpp:<line number> | ||||
| Misc.tests.cpp:<line number>: PASSED: | ||||
|  | ||||
| =============================================================================== | ||||
| test cases:  320 |  230 passed |  86 failed |  4 failed as expected | ||||
| assertions: 1809 | 1640 passed | 148 failed | 21 failed as expected | ||||
| test cases:  328 |  238 passed |  86 failed |  4 failed as expected | ||||
| assertions: 1844 | 1675 passed | 148 failed | 21 failed as expected | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <testsuitesloose text artifact | ||||
| > | ||||
|   <testsuite name="<exe-name>" errors="17" failures="132" tests="1810" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> | ||||
|   <testsuite name="<exe-name>" errors="17" failures="132" tests="1845" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}"> | ||||
|     <properties> | ||||
|       <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/> | ||||
|       <property name="random-seed" value="1"/> | ||||
| @@ -350,6 +350,13 @@ Exception.tests.cpp:<line number> | ||||
|     <testcase classname="<exe-name>.global" name="Character pretty printing/Specifically escaped" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Character pretty printing/General chars" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Character pretty printing/Low ASCII" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining MatchAllOfGeneric does not nest" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining MatchAnyOfGeneric does not nest" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining MatchNotOfGeneric does not nest" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining concrete matchers does not use templated matchers" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining only templated matchers" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining templated and concrete matchers" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Combining templated matchers" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Commas in various macros are allowed" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Comparing function pointers" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Comparison ops" time="{duration}"/> | ||||
| @@ -950,6 +957,7 @@ Message from section two | ||||
| Message.tests.cpp:<line number> | ||||
|       </failure> | ||||
|     </testcase> | ||||
|     <testcase classname="<exe-name>.global" name="Overloaded comma or address-of operators are not used" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Parse test names and tags/Empty test spec should have no filters" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Parse test names and tags/Test spec from empty string should have no filters" time="{duration}"/> | ||||
|     <testcase classname="<exe-name>.global" name="Parse test names and tags/Test spec from just a comma should have no filters" time="{duration}"/> | ||||
|   | ||||
| @@ -915,6 +915,13 @@ Exception.tests.cpp:<line number> | ||||
|   <file path="tests/<exe-name>/UsageTests/Matchers.tests.cpp"> | ||||
|     <testCase name="Arbitrary predicate matcher/Function pointer" duration="{duration}"/> | ||||
|     <testCase name="Arbitrary predicate matcher/Lambdas + different type" duration="{duration}"/> | ||||
|     <testCase name="Combining MatchAllOfGeneric does not nest" duration="{duration}"/> | ||||
|     <testCase name="Combining MatchAnyOfGeneric does not nest" duration="{duration}"/> | ||||
|     <testCase name="Combining MatchNotOfGeneric does not nest" duration="{duration}"/> | ||||
|     <testCase name="Combining concrete matchers does not use templated matchers" duration="{duration}"/> | ||||
|     <testCase name="Combining only templated matchers" duration="{duration}"/> | ||||
|     <testCase name="Combining templated and concrete matchers" duration="{duration}"/> | ||||
|     <testCase name="Combining templated matchers" duration="{duration}"/> | ||||
|     <testCase name="Contains string matcher" duration="{duration}"> | ||||
|       <failure message="CHECK_THAT(testStringForMatching(), Contains("not there", Catch::CaseSensitive::No))"> | ||||
| FAILED: | ||||
| @@ -1042,6 +1049,7 @@ with expansion: | ||||
| Matchers.tests.cpp:<line number> | ||||
|       </failure> | ||||
|     </testCase> | ||||
|     <testCase name="Overloaded comma or address-of operators are not used" duration="{duration}"/> | ||||
|     <testCase name="Predicate matcher can accept const char*" duration="{duration}"/> | ||||
|     <testCase name="Regex string matcher" duration="{duration}"> | ||||
|       <failure message="CHECK_THAT(testStringForMatching(), Matches("this STRING contains 'abc' as a substring"))"> | ||||
|   | ||||
| @@ -524,6 +524,68 @@ ok {test-number} - c == i for: 3 == 3 | ||||
| ok {test-number} - c == i for: 4 == 4 | ||||
| # Character pretty printing | ||||
| ok {test-number} - c == i for: 5 == 5 | ||||
| # Combining MatchAllOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value' | ||||
| # Combining MatchAllOfGeneric does not nest | ||||
| ok {test-number} - 1, MatcherA() && MatcherB() && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) | ||||
| # Combining MatchAllOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value' | ||||
| # Combining MatchAllOfGeneric does not nest | ||||
| ok {test-number} - 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) | ||||
| # Combining MatchAnyOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value' | ||||
| # Combining MatchAnyOfGeneric does not nest | ||||
| ok {test-number} - 1, MatcherA() || MatcherB() || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) | ||||
| # Combining MatchAnyOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value' | ||||
| # Combining MatchAnyOfGeneric does not nest | ||||
| ok {test-number} - 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value' | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value' | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> >::value' | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value' | ||||
| # Combining MatchNotOfGeneric does not nest | ||||
| ok {test-number} - 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f | ||||
| # Combining concrete matchers does not use templated matchers | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Impl::MatchAnyOf<std::string> >::value' | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB> >::value' | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB> >::value' | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>> >::value' | ||||
| # Combining only templated matchers | ||||
| ok {test-number} - 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) ) | ||||
| # Combining templated and concrete matchers | ||||
| ok {test-number} - str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } ) | ||||
| # Combining templated matchers | ||||
| ok {test-number} - container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) | ||||
| # Commas in various macros are allowed | ||||
| ok {test-number} - std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}} | ||||
| # Commas in various macros are allowed | ||||
| @@ -1688,6 +1750,14 @@ ok {test-number} - rng() == 0x<hex digits> for: 4261393167 (0x<hex digits>) == 4 | ||||
| not ok {test-number} - explicitly with 1 message: 'Message from section one' | ||||
| # Output from all sections is reported | ||||
| not ok {test-number} - explicitly with 1 message: 'Message from section two' | ||||
| # Overloaded comma or address-of operators are not used | ||||
| ok {test-number} - (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed | ||||
| # Overloaded comma or address-of operators are not used | ||||
| ok {test-number} - &EvilMatcher(), EvilAddressOfOperatorUsed | ||||
| # Overloaded comma or address-of operators are not used | ||||
| ok {test-number} - EvilMatcher() || EvilMatcher() && !EvilMatcher() | ||||
| # Overloaded comma or address-of operators are not used | ||||
| ok {test-number} - (EvilMatcher() && EvilMatcher()) || !EvilMatcher() | ||||
| # Parse test names and tags | ||||
| ok {test-number} - spec.hasFilters() == false for: false == false | ||||
| # Parse test names and tags | ||||
| @@ -3610,5 +3680,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0 | ||||
| ok {test-number} - | ||||
| # xmlentitycheck | ||||
| ok {test-number} - | ||||
| 1..1801 | ||||
| 1..1836 | ||||
|  | ||||
|   | ||||
| @@ -203,6 +203,20 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "unexpe | ||||
| ##teamcity[testFinished name='Capture and info messages' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Character pretty printing'] | ||||
| ##teamcity[testFinished name='Character pretty printing' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining MatchAllOfGeneric does not nest'] | ||||
| ##teamcity[testFinished name='Combining MatchAllOfGeneric does not nest' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining MatchAnyOfGeneric does not nest'] | ||||
| ##teamcity[testFinished name='Combining MatchAnyOfGeneric does not nest' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining MatchNotOfGeneric does not nest'] | ||||
| ##teamcity[testFinished name='Combining MatchNotOfGeneric does not nest' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining concrete matchers does not use templated matchers'] | ||||
| ##teamcity[testFinished name='Combining concrete matchers does not use templated matchers' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining only templated matchers'] | ||||
| ##teamcity[testFinished name='Combining only templated matchers' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining templated and concrete matchers'] | ||||
| ##teamcity[testFinished name='Combining templated and concrete matchers' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Combining templated matchers'] | ||||
| ##teamcity[testFinished name='Combining templated matchers' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Commas in various macros are allowed'] | ||||
| ##teamcity[testFinished name='Commas in various macros are allowed' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Comparing function pointers'] | ||||
| @@ -395,6 +409,8 @@ Condition.tests.cpp:<line number>|nexpression failed|n  CHECK( data.str_hello <= | ||||
| Message.tests.cpp:<line number>|nexplicit failure with message:|n  "Message from section one"'] | ||||
| Message.tests.cpp:<line number>|nexplicit failure with message:|n  "Message from section two"'] | ||||
| ##teamcity[testFinished name='Output from all sections is reported' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Overloaded comma or address-of operators are not used'] | ||||
| ##teamcity[testFinished name='Overloaded comma or address-of operators are not used' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Parse test names and tags'] | ||||
| ##teamcity[testFinished name='Parse test names and tags' duration="{duration}"] | ||||
| ##teamcity[testStarted name='Pointers can be compared to null'] | ||||
|   | ||||
| @@ -2417,6 +2417,179 @@ Nor would this | ||||
|       </Section> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining MatchAllOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() && MatcherB() && MatcherC() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() && MatcherB() && MatcherC() && MatcherD() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining MatchAnyOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() || MatcherB() || MatcherC() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() || MatcherB() || MatcherC() || MatcherD() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining MatchNotOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           0, !MatcherA() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           0 not equals: (int) 1 or (float) 1.0f | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, !!MatcherA() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 equals: (int) 1 or (float) 1.0f | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           0, !!!MatcherA() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           0 not equals: (int) 1 or (float) 1.0f | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, !!!!MatcherA() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 equals: (int) 1 or (float) 1.0f | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining concrete matchers does not use templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining only templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() || MatcherB() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() && MatcherB() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           1, MatcherA() || !MatcherB() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining templated and concrete matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           vec, Predicate<std::vector<int>>([](auto const& vec) { return std::all_of(vec.begin(), vec.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Combining templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } ) | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Commas in various macros are allowed" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" > | ||||
|         <Original> | ||||
| @@ -8025,6 +8198,41 @@ Nor would this | ||||
|       </Section> | ||||
|       <OverallResult success="false"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Overloaded comma or address-of operators are not used" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|       <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           &EvilMatcher(), EvilAddressOfOperatorUsed | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           &EvilMatcher(), EvilAddressOfOperatorUsed | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           EvilMatcher() || EvilMatcher() && !EvilMatcher() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           EvilMatcher() || EvilMatcher() && !EvilMatcher() | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" > | ||||
|         <Original> | ||||
|           (EvilMatcher() && EvilMatcher()) || !EvilMatcher() | ||||
|         </Original> | ||||
|         <Expanded> | ||||
|           (EvilMatcher() && EvilMatcher()) || !EvilMatcher() | ||||
|         </Expanded> | ||||
|       </Expression> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <TestCase name="Parse test names and tags" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > | ||||
|       <Section name="Empty test spec should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > | ||||
|         <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" > | ||||
| @@ -16909,7 +17117,7 @@ loose text artifact | ||||
|       </Section> | ||||
|       <OverallResult success="true"/> | ||||
|     </TestCase> | ||||
|     <OverallResults successes="1640" failures="149" expectedFailures="21"/> | ||||
|     <OverallResults successes="1675" failures="149" expectedFailures="21"/> | ||||
|   </Group> | ||||
|   <OverallResults successes="1640" failures="148" expectedFailures="21"/> | ||||
|   <OverallResults successes="1675" failures="148" expectedFailures="21"/> | ||||
| </Catch> | ||||
|   | ||||
| @@ -9,10 +9,12 @@ | ||||
| #include <catch2/catch_matchers_generic.hpp> | ||||
| #include <catch2/catch_matchers_string.h> | ||||
| #include <catch2/catch_matchers_vector.h> | ||||
| #include <catch2/catch_matchers_templates.hpp> | ||||
|  | ||||
| #include <sstream> | ||||
| #include <algorithm> | ||||
| #include <cmath> | ||||
| #include <list> | ||||
| #include <sstream> | ||||
|  | ||||
| #ifdef __clang__ | ||||
| #pragma clang diagnostic push | ||||
| @@ -554,6 +556,302 @@ namespace { namespace MatchersTests { | ||||
|             REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException,  Message("SpecialException::what")); | ||||
|         } | ||||
|  | ||||
|  | ||||
|     template<typename Range> | ||||
|     struct EqualsRangeMatcher : Catch::MatcherGenericBase { | ||||
|  | ||||
|         EqualsRangeMatcher(Range const& range) : range{ range } {} | ||||
|  | ||||
|         template<typename OtherRange> | ||||
|         bool match(OtherRange const& other) const { | ||||
|             using std::begin; | ||||
|             using std::end; | ||||
|  | ||||
|             return std::equal(begin(range), end(range), begin(other), end(other)); | ||||
|         } | ||||
|  | ||||
|         std::string describe() const override { | ||||
|             return "Equals: " + Catch::rangeToString(range); | ||||
|         } | ||||
|  | ||||
|     private: | ||||
|         Range const& range; | ||||
|     }; | ||||
|  | ||||
|     template<typename Range> | ||||
|     auto EqualsRange(const Range& range) -> EqualsRangeMatcher<Range> { | ||||
|         return EqualsRangeMatcher<Range>{range}; | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining templated matchers", "[matchers][templated]") { | ||||
|         std::array<int, 3> container{{ 1,2,3 }}; | ||||
|  | ||||
|         std::array<int, 3> a{{ 1,2,3 }}; | ||||
|         std::vector<int> b{ 0,1,2 }; | ||||
|         std::list<int> c{ 4,5,6 }; | ||||
|  | ||||
|         REQUIRE_THAT(container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c)); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining templated and concrete matchers", "[matchers][templated]") { | ||||
|         using namespace Catch::Matchers; | ||||
|         std::vector<int> vec{ 1, 3, 5 }; | ||||
|  | ||||
|         std::array<int, 3> a{{ 5, 3, 1 }}; | ||||
|  | ||||
|         REQUIRE_THAT(vec, | ||||
|                     Predicate<std::vector<int>>([](auto const& vec) { | ||||
|                         return std::all_of(vec.begin(), vec.end(), [](int elem) { | ||||
|                             return elem % 2 == 1; | ||||
|                         }); | ||||
|                     }, "All elements are odd") && | ||||
|                     !EqualsRange(a)); | ||||
|  | ||||
|         const std::string str = "foobar"; | ||||
|         const std::array<char, 6> arr{{ 'f', 'o', 'o', 'b', 'a', 'r' }}; | ||||
|         const std::array<char, 6> bad_arr{{ 'o', 'o', 'f', 'b', 'a', 'r' }}; | ||||
|  | ||||
|         using Catch::Matchers::StartsWith; | ||||
|         using Catch::Matchers::EndsWith; | ||||
|  | ||||
|         REQUIRE_THAT(str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar")); | ||||
|         REQUIRE_THAT(str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar")); | ||||
|  | ||||
|         REQUIRE_THAT(str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar")); | ||||
|         REQUIRE_THAT(str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar")); | ||||
|  | ||||
|         REQUIRE_THAT(str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar"))); | ||||
|         REQUIRE_THAT(str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr)); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining concrete matchers does not use templated matchers", "[matchers][templated]") { | ||||
|         using Catch::Matchers::StartsWith; | ||||
|         using Catch::Matchers::EndsWith; | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), | ||||
|             Catch::Matchers::Impl::MatchAnyOf<std::string> | ||||
|         >::value); | ||||
|     } | ||||
|  | ||||
|     struct MatcherA : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { return "equals: (int) 1 or (float) 1.0f"; } | ||||
|         bool match(int i) const { return i == 1; } | ||||
|         bool match(float f) const { return f == 1.0f; } | ||||
|     }; | ||||
|  | ||||
|     struct MatcherB : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { return "equals: (long long) 1"; } | ||||
|         bool match(long long l) const { return l == 1ll; } | ||||
|     }; | ||||
|  | ||||
|     struct MatcherC : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { return "equals: (T) 1"; } | ||||
|         template<typename T> | ||||
|         bool match(T t) const { return t == T{1}; } | ||||
|     }; | ||||
|  | ||||
|     struct MatcherD : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { return "equals: true"; } | ||||
|         bool match(bool b) const { return b == true; } | ||||
|     }; | ||||
|  | ||||
|     TEST_CASE("Combining only templated matchers", "[matchers][templated]") { | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() || MatcherB()), | ||||
|             Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() || MatcherB()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() && MatcherB()), | ||||
|             Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() && MatcherB()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() || !MatcherB()), | ||||
|             Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Impl::MatchNotOfGeneric<MatcherB>> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() || !MatcherB()); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining MatchAnyOfGeneric does not nest", "[matchers][templated]") { | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() || MatcherB() || MatcherC()), | ||||
|             Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()), | ||||
|             Catch::Matchers::Impl::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC() || MatcherD()); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining MatchAllOfGeneric does not nest", "[matchers][templated]") { | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() && MatcherB() && MatcherC()), | ||||
|             Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()), | ||||
|             Catch::Matchers::Impl::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC() && MatcherD()); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Combining MatchNotOfGeneric does not nest", "[matchers][templated]") { | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(!MatcherA()), | ||||
|             Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(0, !MatcherA()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(!!MatcherA()), | ||||
|             MatcherA const& | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, !!MatcherA()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(!!!MatcherA()), | ||||
|             Catch::Matchers::Impl::MatchNotOfGeneric<MatcherA> | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(0, !!!MatcherA()); | ||||
|  | ||||
|         STATIC_REQUIRE(std::is_same< | ||||
|             decltype(!!!!MatcherA()), | ||||
|             MatcherA const & | ||||
|         >::value); | ||||
|  | ||||
|         REQUIRE_THAT(1, !!!!MatcherA()); | ||||
|     } | ||||
|  | ||||
|     struct EvilAddressOfOperatorUsed : std::exception { | ||||
|         EvilAddressOfOperatorUsed() {} | ||||
|         const char* what() const noexcept override { | ||||
|             return "overloaded address-of operator of matcher was used instead of std::addressof"; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct EvilCommaOperatorUsed : std::exception { | ||||
|         EvilCommaOperatorUsed() {} | ||||
|         const char* what() const noexcept override { | ||||
|             return "overloaded comma operator of matcher was used"; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct EvilMatcher : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { | ||||
|             return "equals: 45"; | ||||
|         } | ||||
|  | ||||
|         bool match(int i) const { | ||||
|             return i == 45; | ||||
|         } | ||||
|  | ||||
|         EvilMatcher const* operator& () const { | ||||
|             throw EvilAddressOfOperatorUsed(); | ||||
|         } | ||||
|  | ||||
|         int operator,(EvilMatcher const&) const { | ||||
|             throw EvilCommaOperatorUsed(); | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TEST_CASE("Overloaded comma or address-of operators are not used", "[matchers][templated]") { | ||||
|         REQUIRE_THROWS_AS((EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed); | ||||
|         REQUIRE_THROWS_AS(&EvilMatcher(), EvilAddressOfOperatorUsed); | ||||
|         REQUIRE_NOTHROW(EvilMatcher() || EvilMatcher() && !EvilMatcher()); | ||||
|         REQUIRE_NOTHROW((EvilMatcher() && EvilMatcher()) || !EvilMatcher()); | ||||
|     } | ||||
|  | ||||
|     struct ImmovableMatcher : Catch::MatcherGenericBase { | ||||
|         ImmovableMatcher() = default; | ||||
|         ImmovableMatcher(ImmovableMatcher const&) = delete; | ||||
|         ImmovableMatcher(ImmovableMatcher &&) = delete; | ||||
|         ImmovableMatcher& operator=(ImmovableMatcher const&) = delete; | ||||
|         ImmovableMatcher& operator=(ImmovableMatcher &&) = delete; | ||||
|  | ||||
|         std::string describe() const override { | ||||
|             return "always false"; | ||||
|         } | ||||
|  | ||||
|         template<typename T> | ||||
|         bool match(T&&) const { | ||||
|             return false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct MatcherWasMovedOrCopied : std::exception { | ||||
|         MatcherWasMovedOrCopied() {} | ||||
|         const char* what() const noexcept override { | ||||
|             return "attempted to copy or move a matcher"; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     struct ThrowOnCopyOrMoveMatcher : Catch::MatcherGenericBase { | ||||
|         ThrowOnCopyOrMoveMatcher() = default; | ||||
|         [[noreturn]] | ||||
|         ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher const&) { | ||||
|             throw MatcherWasMovedOrCopied(); | ||||
|         } | ||||
|         [[noreturn]] | ||||
|         ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher &&) { | ||||
|             throw MatcherWasMovedOrCopied(); | ||||
|         } | ||||
|         ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher const&) { | ||||
|             throw MatcherWasMovedOrCopied(); | ||||
|         } | ||||
|         ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher &&) { | ||||
|             throw MatcherWasMovedOrCopied(); | ||||
|         } | ||||
|  | ||||
|         std::string describe() const override { | ||||
|             return "always false"; | ||||
|         } | ||||
|  | ||||
|         template<typename T> | ||||
|         bool match(T&&) const { | ||||
|             return false; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TEST_CASE("Matchers are not moved or copied", "[matchers][templated][approvals]") { | ||||
|         REQUIRE_NOTHROW((ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher()); | ||||
|     } | ||||
|  | ||||
|     TEST_CASE("Immovable matchers can be used", "[matchers][templated][approvals]") { | ||||
|         REQUIRE_THAT(123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher()); | ||||
|     } | ||||
|  | ||||
|     struct ReferencingMatcher : Catch::MatcherGenericBase { | ||||
|         std::string describe() const override { | ||||
|             return "takes reference"; | ||||
|         } | ||||
|         bool match(int& i) const { | ||||
|             return i == 22; | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     TEST_CASE("Matchers can take references", "[matchers][templated][approvals]") { | ||||
|         REQUIRE_THAT(22, ReferencingMatcher{}); | ||||
|     } | ||||
|  | ||||
| } } // namespace MatchersTests | ||||
|  | ||||
| #ifdef __clang__ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 melak47
					melak47