1
0
mirror of https://github.com/catchorg/Catch2.git synced 2025-01-15 22:58:02 +00:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Martin Hořeňovský
a25d83d8c4
Matcher type flattening overloads of && and || are now hidden friends 2020-03-29 14:03:29 +02:00
Martin Hořeňovský
f7d7aa9eb2
Fix and extend tests for composing generic matchers 2020-03-29 14:03:12 +02:00
Martin Hořeňovský
ca5af2e85b
Cleanup vector matchers
Once again, added doxygen comments and removed the inner-most namespace.
2020-03-28 15:17:12 +01:00
Martin Hořeňovský
904c47a634
Cleanup string matchers
Removed nested `StdString` namespace and added Doxygen comments.
Also renamed some matchers to avoid colisions now that there are
less separate namespaces for matchers to go to. Since this is a
breaking release anyway, it shouldn't matter, and the factory
functions that the users should use remain the same anyway.
2020-03-28 15:15:31 +01:00
Martin Hořeňovský
afc8b28c07
Cleanup for floating point matchers
Removed the `Floating` nested namespace and added Doxygen comments
for the factory methods.
2020-03-28 12:48:19 +01:00
Martin Hořeňovský
a6baa6dda6
Cleanup in exception matchers
No more nested namespace for the matcher, and there is now a doxygen
explanatory comment on the factory function.
2020-03-28 11:06:14 +01:00
Martin Hořeňovský
5c9367d4f1
Small cleanup for PredicateMatcher
Removed the `generic` nested namespace, so PredicateMatcher now
lives in `Catch::Matchers` namespace, just like other matchers.
Also cleaned up and doxygenized comments on the `Predicate` factory
function for `PredicateMatcher`.
2020-03-27 14:56:33 +01:00
17 changed files with 535 additions and 455 deletions

View File

@ -10,7 +10,6 @@
namespace Catch {
namespace Matchers {
namespace Exception {
bool ExceptionMessageMatcher::match(std::exception const& ex) const {
return ex.what() == m_message;
@ -20,11 +19,9 @@ std::string ExceptionMessageMatcher::describe() const {
return "exception message matches \"" + m_message + "\"";
}
}
Exception::ExceptionMessageMatcher Message(std::string const& message) {
return Exception::ExceptionMessageMatcher(message);
ExceptionMessageMatcher Message(std::string const& message) {
return ExceptionMessageMatcher(message);
}
// namespace Exception
} // namespace Matchers
} // namespace Catch

View File

@ -11,7 +11,6 @@
namespace Catch {
namespace Matchers {
namespace Exception {
class ExceptionMessageMatcher final : public MatcherBase<std::exception> {
std::string m_message;
@ -26,9 +25,8 @@ public:
std::string describe() const override;
};
} // namespace Exception
Exception::ExceptionMessageMatcher Message(std::string const& message);
//! Creates a matcher that checks whether a std derived exception has the provided message
ExceptionMessageMatcher Message(std::string const& message);
} // namespace Matchers
} // namespace Catch

View File

@ -99,13 +99,15 @@ void write(std::ostream& out, FloatingPoint num) {
} // end anonymous namespace
namespace Matchers {
namespace Floating {
namespace Detail {
enum class FloatingPointKind : uint8_t {
Float,
Double
};
} // end namespace Detail
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
:m_target{ target }, m_margin{ margin } {
@ -124,9 +126,9 @@ namespace Floating {
}
WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
CATCH_ENFORCE(m_type == FloatingPointKind::Double
CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double
|| m_ulps < (std::numeric_limits<uint32_t>::max)(),
"Provided ULP is impossibly large for a float comparison.");
}
@ -139,12 +141,12 @@ namespace Floating {
bool WithinUlpsMatcher::match(double const& matchee) const {
switch (m_type) {
case FloatingPointKind::Float:
case Detail::FloatingPointKind::Float:
return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
case FloatingPointKind::Double:
case Detail::FloatingPointKind::Double:
return almostEqualUlps<double>(matchee, m_target, m_ulps);
default:
CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
CATCH_INTERNAL_ERROR( "Unknown Detail::FloatingPointKind value" );
}
}
@ -157,7 +159,7 @@ namespace Floating {
ret << "is within " << m_ulps << " ULPs of ";
if (m_type == FloatingPointKind::Float) {
if (m_type == Detail::FloatingPointKind::Float) {
write(ret, static_cast<float>(m_target));
ret << 'f';
} else {
@ -165,7 +167,7 @@ namespace Floating {
}
ret << " ([";
if (m_type == FloatingPointKind::Double) {
if (m_type == Detail::FloatingPointKind::Double) {
write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));
ret << ", ";
write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));
@ -199,39 +201,35 @@ namespace Floating {
return sstr.str();
}
}// namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Double);
}
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
return WithinUlpsMatcher(target, maxUlpDiff, Detail::FloatingPointKind::Float);
}
Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
return Floating::WithinAbsMatcher(target, margin);
WithinAbsMatcher WithinAbs(double target, double margin) {
return WithinAbsMatcher(target, margin);
}
Floating::WithinRelMatcher WithinRel(double target, double eps) {
return Floating::WithinRelMatcher(target, eps);
WithinRelMatcher WithinRel(double target, double eps) {
return WithinRelMatcher(target, eps);
}
Floating::WithinRelMatcher WithinRel(double target) {
return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
WithinRelMatcher WithinRel(double target) {
return WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);
}
Floating::WithinRelMatcher WithinRel(float target, float eps) {
return Floating::WithinRelMatcher(target, eps);
WithinRelMatcher WithinRel(float target, float eps) {
return WithinRelMatcher(target, eps);
}
Floating::WithinRelMatcher WithinRel(float target) {
return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
WithinRelMatcher WithinRel(float target) {
return WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);
}
} // namespace Matchers
} // namespace Catch

View File

@ -12,57 +12,59 @@
namespace Catch {
namespace Matchers {
namespace Floating {
namespace Detail {
enum class FloatingPointKind : uint8_t;
}
struct WithinAbsMatcher final : MatcherBase<double> {
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_margin;
};
struct WithinAbsMatcher final : MatcherBase<double> {
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_margin;
};
struct WithinUlpsMatcher final : MatcherBase<double> {
WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
uint64_t m_ulps;
FloatingPointKind m_type;
};
struct WithinUlpsMatcher final : MatcherBase<double> {
WithinUlpsMatcher(double target, uint64_t ulps, Detail::FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
uint64_t m_ulps;
Detail::FloatingPointKind m_type;
};
// Given IEEE-754 format for floats and doubles, we can assume
// that float -> double promotion is lossless. Given this, we can
// assume that if we do the standard relative comparison of
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
// the same result if we do this for floats, as if we do this for
// doubles that were promoted from floats.
struct WithinRelMatcher final : MatcherBase<double> {
WithinRelMatcher(double target, double epsilon);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_epsilon;
};
// Given IEEE-754 format for floats and doubles, we can assume
// that float -> double promotion is lossless. Given this, we can
// assume that if we do the standard relative comparison of
// |lhs - rhs| <= epsilon * max(fabs(lhs), fabs(rhs)), then we get
// the same result if we do this for floats, as if we do this for
// doubles that were promoted from floats.
struct WithinRelMatcher final : MatcherBase<double> {
WithinRelMatcher(double target, double epsilon);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_epsilon;
};
} // namespace Floating
//! Creates a matcher that accepts doubles within certain ULP range of target
WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts floats within certain ULP range of target
WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
//! Creates a matcher that accepts numbers within certain range of target
WithinAbsMatcher WithinAbs(double target, double margin);
// The following functions create the actual matcher objects.
// This allows the types to be inferred
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
Floating::WithinRelMatcher WithinRel(double target, double eps);
// defaults epsilon to 100*numeric_limits<double>::epsilon()
Floating::WithinRelMatcher WithinRel(double target);
Floating::WithinRelMatcher WithinRel(float target, float eps);
// defaults epsilon to 100*numeric_limits<float>::epsilon()
Floating::WithinRelMatcher WithinRel(float target);
//! Creates a matcher that accepts doubles within certain relative range of target
WithinRelMatcher WithinRel(double target, double eps);
//! Creates a matcher that accepts doubles within 100*DBL_EPS relative range of target
WithinRelMatcher WithinRel(double target);
//! Creates a matcher that accepts doubles within certain relative range of target
WithinRelMatcher WithinRel(float target, float eps);
//! Creates a matcher that accepts floats within 100*FLT_EPS relative range of target
WithinRelMatcher WithinRel(float target);
} // namespace Matchers
} // namespace Catch

View File

@ -1,6 +1,6 @@
#include <catch2/matchers/catch_matchers_predicate.hpp>
std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
std::string Catch::Matchers::Detail::finalizeDescription(const std::string& desc) {
if (desc.empty()) {
return "matches undescribed predicate";
} else {

View File

@ -16,11 +16,10 @@
namespace Catch {
namespace Matchers {
namespace Generic {
namespace Detail {
std::string finalizeDescription(const std::string& desc);
}
} // namespace Detail
template <typename T, typename Predicate>
class PredicateMatcher final : public MatcherBase<T> {
@ -42,17 +41,16 @@ public:
}
};
} // namespace Generic
// The following functions create the actual matcher objects.
// The user has to explicitly specify type to the function, because
// inferring std::function<bool(T const&)> is hard (but possible) and
// requires a lot of TMP.
/**
* Creates a matcher that calls delegates `match` to the provided predicate.
*
* The user has to explicitly specify the argument type to the matcher
*/
template<typename T, typename Pred>
Generic::PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
static_assert(is_callable<Pred(T)>::value, "Predicate not callable with argument T");
static_assert(std::is_same<bool, FunctionReturnType<Pred, T>>::value, "Predicate does not return bool");
return Generic::PredicateMatcher<T, Pred>(std::forward<Pred>(predicate), description);
return PredicateMatcher<T, Pred>(std::forward<Pred>(predicate), description);
}
} // namespace Matchers

View File

@ -12,103 +12,99 @@
namespace Catch {
namespace Matchers {
namespace StdString {
CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
m_str( adjustString( str ) )
{}
std::string CasedString::adjustString( std::string const& str ) const {
return m_caseSensitivity == CaseSensitive::No
? toLower( str )
: str;
}
std::string CasedString::caseSensitivitySuffix() const {
return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)"
: std::string();
}
StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
: m_comparator( comparator ),
m_operation( operation ) {
}
std::string StringMatcherBase::describe() const {
std::string description;
description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
m_comparator.caseSensitivitySuffix().size());
description += m_operation;
description += ": \"";
description += m_comparator.m_str;
description += "\"";
description += m_comparator.caseSensitivitySuffix();
return description;
}
EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
bool EqualsMatcher::match( std::string const& source ) const {
return m_comparator.adjustString( source ) == m_comparator.m_str;
}
ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
bool ContainsMatcher::match( std::string const& source ) const {
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
}
StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
bool StartsWithMatcher::match( std::string const& source ) const {
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
bool EndsWithMatcher::match( std::string const& source ) const {
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
bool RegexMatcher::match(std::string const& matchee) const {
auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
if (m_caseSensitivity == CaseSensitive::Choice::No) {
flags |= std::regex::icase;
}
auto reg = std::regex(m_regex, flags);
return std::regex_match(matchee, reg);
}
std::string RegexMatcher::describe() const {
return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
}
} // namespace StdString
StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
: m_caseSensitivity( caseSensitivity ),
m_str( adjustString( str ) )
{}
std::string CasedString::adjustString( std::string const& str ) const {
return m_caseSensitivity == CaseSensitive::No
? toLower( str )
: str;
}
StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
}
StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
std::string CasedString::caseSensitivitySuffix() const {
return m_caseSensitivity == CaseSensitive::No
? " (case insensitive)"
: std::string();
}
StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
return StdString::RegexMatcher(regex, caseSensitivity);
StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
: m_comparator( comparator ),
m_operation( operation ) {
}
std::string StringMatcherBase::describe() const {
std::string description;
description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
m_comparator.caseSensitivitySuffix().size());
description += m_operation;
description += ": \"";
description += m_comparator.m_str;
description += "\"";
description += m_comparator.caseSensitivitySuffix();
return description;
}
StringEqualsMatcher::StringEqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
bool StringEqualsMatcher::match( std::string const& source ) const {
return m_comparator.adjustString( source ) == m_comparator.m_str;
}
StringContainsMatcher::StringContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
bool StringContainsMatcher::match( std::string const& source ) const {
return contains( m_comparator.adjustString( source ), m_comparator.m_str );
}
StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
bool StartsWithMatcher::match( std::string const& source ) const {
return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
bool EndsWithMatcher::match( std::string const& source ) const {
return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
}
RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
bool RegexMatcher::match(std::string const& matchee) const {
auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
if (m_caseSensitivity == CaseSensitive::Choice::No) {
flags |= std::regex::icase;
}
auto reg = std::regex(m_regex, flags);
return std::regex_match(matchee, reg);
}
std::string RegexMatcher::describe() const {
return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
}
StringEqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StringEqualsMatcher( CasedString( str, caseSensitivity) );
}
StringContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StringContainsMatcher( CasedString( str, caseSensitivity) );
}
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return EndsWithMatcher( CasedString( str, caseSensitivity) );
}
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
return StartsWithMatcher( CasedString( str, caseSensitivity) );
}
RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
return RegexMatcher(regex, caseSensitivity);
}
} // namespace Matchers

View File

@ -15,64 +15,60 @@
namespace Catch {
namespace Matchers {
namespace StdString {
struct CasedString {
CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
std::string adjustString( std::string const& str ) const;
std::string caseSensitivitySuffix() const;
struct CasedString
{
CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
std::string adjustString( std::string const& str ) const;
std::string caseSensitivitySuffix() const;
CaseSensitive::Choice m_caseSensitivity;
std::string m_str;
};
CaseSensitive::Choice m_caseSensitivity;
std::string m_str;
};
struct StringMatcherBase : MatcherBase<std::string> {
StringMatcherBase( std::string const& operation, CasedString const& comparator );
std::string describe() const override;
struct StringMatcherBase : MatcherBase<std::string> {
StringMatcherBase( std::string const& operation, CasedString const& comparator );
std::string describe() const override;
CasedString m_comparator;
std::string m_operation;
};
CasedString m_comparator;
std::string m_operation;
};
struct StringEqualsMatcher final : StringMatcherBase {
StringEqualsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct StringContainsMatcher final : StringMatcherBase {
StringContainsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct StartsWithMatcher final : StringMatcherBase {
StartsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct EndsWithMatcher final : StringMatcherBase {
EndsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct EqualsMatcher final : StringMatcherBase {
EqualsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct ContainsMatcher final : StringMatcherBase {
ContainsMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct StartsWithMatcher final : StringMatcherBase {
StartsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct EndsWithMatcher final : StringMatcherBase {
EndsWithMatcher( CasedString const& comparator );
bool match( std::string const& source ) const override;
};
struct RegexMatcher final : MatcherBase<std::string> {
RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
bool match( std::string const& matchee ) const override;
std::string describe() const override;
struct RegexMatcher final : MatcherBase<std::string> {
RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
bool match( std::string const& matchee ) const override;
std::string describe() const override;
private:
std::string m_regex;
CaseSensitive::Choice m_caseSensitivity;
};
private:
std::string m_regex;
CaseSensitive::Choice m_caseSensitivity;
};
} // namespace StdString
// The following functions create the actual matcher objects.
// This allows the types to be inferred
StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that are exactly equal to `str`
StringEqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that contain `str`
StringContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that _end_ with `str`
EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings that _start_ with `str`
StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
//! Creates matcher that accepts strings matching `regex`
RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
} // namespace Matchers
} // namespace Catch

View File

@ -41,7 +41,7 @@ namespace Matchers {
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::array<void const*, N + 1> arr{ {lhs} };
std::copy_n(rhs.begin(), N, arr.begin() + 1);
return arr;
}
@ -129,6 +129,33 @@ namespace Matchers {
}
std::array<void const*, sizeof...(MatcherTs)> m_matchers;
//! Avoids type nesting for `GenericAllOf && GenericAllOf` case
template<typename... MatchersRHS>
friend
MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatchAllOfGeneric<MatchersRHS...>&& rhs) {
return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
//! Avoids type nesting for `GenericAllOf && some matcher` case
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher<MatcherRHS>::value,
MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
MatchAllOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
//! Avoids type nesting for `some matcher && GenericAllOf` case
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher<MatcherLHS>::value,
MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
MatcherLHS const& lhs,
MatchAllOfGeneric<MatcherTs...>&& rhs) {
return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
};
@ -152,6 +179,32 @@ namespace Matchers {
}
std::array<void const*, sizeof...(MatcherTs)> m_matchers;
//! Avoids type nesting for `GenericAnyOf || GenericAnyOf` case
template<typename... MatchersRHS>
friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
//! Avoids type nesting for `GenericAnyOf || some matcher` case
template<typename MatcherRHS>
friend std::enable_if_t<is_matcher<MatcherRHS>::value,
MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
MatchAnyOfGeneric<MatcherTs...>&& lhs,
MatcherRHS const& rhs) {
return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
//! Avoids type nesting for `some matcher || GenericAnyOf` case
template<typename MatcherLHS>
friend std::enable_if_t<is_matcher<MatcherLHS>::value,
MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
MatcherLHS const& lhs,
MatchAnyOfGeneric<MatcherTs...>&& rhs) {
return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
};
@ -174,7 +227,7 @@ namespace Matchers {
}
//! Negating negation can just unwrap and return underlying matcher
friend MatcherT const& operator ! (Detail::MatchNotOfGeneric<MatcherT> const& matcher) {
friend MatcherT const& operator ! (MatchNotOfGeneric<MatcherT> const& matcher) {
return matcher.m_matcher;
}
private:
@ -229,44 +282,6 @@ namespace Matchers {
return { lhs, rhs };
}
// avoid deep nesting of matcher templates
template<typename... MatchersLHS, typename... MatchersRHS>
Detail::MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>
operator && (Detail::MatchAllOfGeneric<MatchersLHS...>&& lhs, Detail::MatchAllOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAllOfGeneric<MatchersLHS..., MatchersRHS...>{Detail::array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_matcher<MatcherRHS>::value, Detail::MatchAllOfGeneric<MatchersLHS..., MatcherRHS>>
operator && (Detail::MatchAllOfGeneric<MatchersLHS...>&& lhs, MatcherRHS const& rhs) {
return Detail::MatchAllOfGeneric<MatchersLHS..., MatcherRHS>{Detail::array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
}
template<typename MatcherLHS, typename... MatchersRHS>
std::enable_if_t<Detail::is_matcher<MatcherLHS>::value, Detail::MatchAllOfGeneric<MatcherLHS, MatchersRHS...>>
operator && (MatcherLHS const& lhs, Detail::MatchAllOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAllOfGeneric<MatcherLHS, MatchersRHS...>{Detail::array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename... MatchersRHS>
Detail::MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>
operator || (Detail::MatchAnyOfGeneric<MatchersLHS...>&& lhs, Detail::MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAnyOfGeneric<MatchersLHS..., MatchersRHS...>{Detail::array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
}
template<typename... MatchersLHS, typename MatcherRHS>
std::enable_if_t<Detail::is_matcher<MatcherRHS>::value, Detail::MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>>
operator || (Detail::MatchAnyOfGeneric<MatchersLHS...>&& lhs, MatcherRHS const& rhs) {
return Detail::MatchAnyOfGeneric<MatchersLHS..., MatcherRHS>{Detail::array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
}
template<typename MatcherLHS, typename... MatchersRHS>
std::enable_if_t<Detail::is_matcher<MatcherLHS>::value, Detail::MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>>
operator || (MatcherLHS const& lhs, Detail::MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
return Detail::MatchAnyOfGeneric<MatcherLHS, MatchersRHS...>{Detail::array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
}
} // namespace Matchers
} // namespace Catch

View File

@ -16,164 +16,172 @@
namespace Catch {
namespace Matchers {
namespace Vector {
template<typename T>
struct ContainsElementMatcher final : MatcherBase<std::vector<T>> {
template<typename T>
struct ContainsElementMatcher final : MatcherBase<std::vector<T>> {
ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
ContainsElementMatcher(T const& comparator):
m_comparator(comparator)
{}
bool match(std::vector<T> const &v) const override {
for (auto const& el : v) {
if (el == m_comparator) {
return true;
bool match(std::vector<T> const& v) const override {
for (auto const& el : v) {
if (el == m_comparator) {
return true;
}
}
return false;
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
T const& m_comparator;
};
template<typename T>
struct ContainsMatcher final : MatcherBase<std::vector<T>> {
ContainsMatcher(std::vector<T> const& comparator):
m_comparator(comparator)
{}
bool match(std::vector<T> const& v) const override {
// !TBD: see note in EqualsMatcher
if (m_comparator.size() > v.size())
return false;
for (auto const& comparator : m_comparator) {
auto present = false;
for (const auto& el : v) {
if (el == comparator) {
present = true;
break;
}
}
if (!present) {
return false;
}
}
return true;
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct EqualsMatcher final : MatcherBase<std::vector<T>> {
EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
// !TBD: This currently works if all elements can be compared using !=
// - a more general approach would be via a compare template that defaults
// to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly
if (m_comparator.size() != v.size())
return false;
for (std::size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != v[i])
return false;
return true;
}
std::string describe() const override {
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct ApproxMatcher final : MatcherBase<std::vector<T>> {
ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
if (m_comparator.size() != v.size())
return false;
for (std::size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != approx(v[i]))
return false;
return true;
}
std::string describe() const override {
return "is approx: " + ::Catch::Detail::stringify( m_comparator );
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& epsilon( T const& newEpsilon ) {
approx.epsilon(newEpsilon);
return *this;
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& margin( T const& newMargin ) {
approx.margin(newMargin);
return *this;
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& scale( T const& newScale ) {
approx.scale(newScale);
return *this;
}
std::vector<T> const& m_comparator;
mutable Catch::Approx approx = Catch::Approx::custom();
};
template<typename T>
struct UnorderedEqualsMatcher final : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
bool match(std::vector<T> const& vec) const override {
// Note: This is a reimplementation of std::is_permutation,
// because I don't want to include <algorithm> inside the common path
if (m_target.size() != vec.size()) {
return false;
}
return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
std::vector<T> const& m_target;
};
T const& m_comparator;
};
template<typename T>
struct ContainsMatcher final : MatcherBase<std::vector<T>> {
ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
// !TBD: see note in EqualsMatcher
if (m_comparator.size() > v.size())
return false;
for (auto const& comparator : m_comparator) {
auto present = false;
for (const auto& el : v) {
if (el == comparator) {
present = true;
break;
}
}
if (!present) {
return false;
}
}
return true;
}
std::string describe() const override {
return "Contains: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct EqualsMatcher final : MatcherBase<std::vector<T>> {
EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
// !TBD: This currently works if all elements can be compared using !=
// - a more general approach would be via a compare template that defaults
// to using !=. but could be specialised for, e.g. std::vector<T> etc
// - then just call that directly
if (m_comparator.size() != v.size())
return false;
for (std::size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != v[i])
return false;
return true;
}
std::string describe() const override {
return "Equals: " + ::Catch::Detail::stringify( m_comparator );
}
std::vector<T> const& m_comparator;
};
template<typename T>
struct ApproxMatcher final : MatcherBase<std::vector<T>> {
ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {}
bool match(std::vector<T> const &v) const override {
if (m_comparator.size() != v.size())
return false;
for (std::size_t i = 0; i < v.size(); ++i)
if (m_comparator[i] != approx(v[i]))
return false;
return true;
}
std::string describe() const override {
return "is approx: " + ::Catch::Detail::stringify( m_comparator );
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& epsilon( T const& newEpsilon ) {
approx.epsilon(newEpsilon);
return *this;
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& margin( T const& newMargin ) {
approx.margin(newMargin);
return *this;
}
template <typename = std::enable_if_t<std::is_constructible<double, T>::value>>
ApproxMatcher& scale( T const& newScale ) {
approx.scale(newScale);
return *this;
}
std::vector<T> const& m_comparator;
mutable Catch::Approx approx = Catch::Approx::custom();
};
template<typename T>
struct UnorderedEqualsMatcher final : MatcherBase<std::vector<T>> {
UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
bool match(std::vector<T> const& vec) const override {
// Note: This is a reimplementation of std::is_permutation,
// because I don't want to include <algorithm> inside the common path
if (m_target.size() != vec.size()) {
return false;
}
return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
}
std::string describe() const override {
return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
}
private:
std::vector<T> const& m_target;
};
} // namespace Vector
// The following functions create the actual matcher objects.
// This allows the types to be inferred
//! Creates a matcher that matches vectors that contain all elements in `comparator`
template<typename T>
Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
return Vector::ContainsMatcher<T>( comparator );
ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
return ContainsMatcher<T>( comparator );
}
//! Creates a matcher that matches vectors that contain `comparator` as an element
template<typename T>
Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
return Vector::ContainsElementMatcher<T>( comparator );
ContainsElementMatcher<T> VectorContains( T const& comparator ) {
return ContainsElementMatcher<T>( comparator );
}
//! Creates a matcher that matches vectors that are exactly equal to `comparator`
template<typename T>
Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
return Vector::EqualsMatcher<T>( comparator );
EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
return EqualsMatcher<T>( comparator );
}
//! Creates a matcher that matches vectors that `comparator` as an element
template<typename T>
Vector::ApproxMatcher<T> Approx( std::vector<T> const& comparator ) {
return Vector::ApproxMatcher<T>( comparator );
ApproxMatcher<T> Approx( std::vector<T> const& comparator ) {
return ApproxMatcher<T>( comparator );
}
//! Creates a matcher that matches vectors that is equal to `target` modulo permutation
template<typename T>
Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
return Vector::UnorderedEqualsMatcher<T>(target);
UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
return UnorderedEqualsMatcher<T>(target);
}
} // namespace Matchers

View File

@ -283,14 +283,18 @@ 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::Detail::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::Detail::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::Detail::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::Detail::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() && MatcherB()) && MatcherC()), Catch::Matchers::Detail::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())), Catch::Matchers::Detail::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::Detail::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::Detail::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())), Catch::Matchers::Detail::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::Detail::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::Detail::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'

View File

@ -1381,5 +1381,5 @@ due to unexpected exception with message:
===============================================================================
test cases: 331 | 257 passed | 70 failed | 4 failed as expected
assertions: 1868 | 1716 passed | 131 failed | 21 failed as expected
assertions: 1872 | 1720 passed | 131 failed | 21 failed as expected

View File

@ -2287,23 +2287,34 @@ Matchers.tests.cpp:<line number>
Matchers.tests.cpp:<line number>: PASSED:
with message:
std::is_same< decltype(MatcherA() && MatcherB() && MatcherC()), Catch::
std::is_same< decltype((MatcherA() && MatcherB()) && MatcherC()), Catch::
Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() )
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::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC,
MatcherD> >::value
std::is_same< decltype(MatcherA() && (MatcherB() && MatcherC())), Catch::
Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( 1, MatcherA() && MatcherB() && MatcherC() && MatcherD() )
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::Detail::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 )
@ -2316,23 +2327,34 @@ Matchers.tests.cpp:<line number>
Matchers.tests.cpp:<line number>: PASSED:
with message:
std::is_same< decltype(MatcherA() || MatcherB() || MatcherC()), Catch::
std::is_same< decltype((MatcherA() || MatcherB()) || MatcherC()), Catch::
Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() )
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::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC,
MatcherD> >::value
std::is_same< decltype(MatcherA() || (MatcherB() || MatcherC())), Catch::
Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value
Matchers.tests.cpp:<line number>: PASSED:
REQUIRE_THAT( 1, MatcherA() || MatcherB() || MatcherC() || MatcherD() )
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::Detail::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 )
@ -14626,5 +14648,5 @@ Misc.tests.cpp:<line number>: PASSED:
===============================================================================
test cases: 331 | 241 passed | 86 failed | 4 failed as expected
assertions: 1885 | 1716 passed | 148 failed | 21 failed as expected
assertions: 1889 | 1720 passed | 148 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="17" failures="132" tests="1886" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="17" failures="132" tests="1890" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<properties>
<property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/>
<property name="random-seed" value="1"/>

View File

@ -565,21 +565,29 @@ 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::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() && MatcherB()) && MatcherC()), Catch::Matchers::Detail::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 )
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::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && (MatcherB() && MatcherC())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::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 )
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::Detail::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::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() || MatcherB()) || MatcherC()), Catch::Matchers::Detail::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 )
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::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || (MatcherB() || MatcherC())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::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 )
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::Detail::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::Detail::MatchNotOfGeneric<MatcherA> >::value'
# Combining MatchNotOfGeneric does not nest
@ -3762,5 +3770,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
ok {test-number} -
# xmlentitycheck
ok {test-number} -
1..1877
1..1881

View File

@ -2613,7 +2613,7 @@ Nor would this
<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() &amp;&amp; MatcherB() &amp;&amp; MatcherC()
1, (MatcherA() &amp;&amp; MatcherB()) &amp;&amp; MatcherC()
</Original>
<Expanded>
1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
@ -2621,7 +2621,15 @@ Nor would this
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
1, MatcherA() &amp;&amp; MatcherB() &amp;&amp; MatcherC() &amp;&amp; MatcherD()
1, MatcherA() &amp;&amp; (MatcherB() &amp;&amp; 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() &amp;&amp; MatcherB()) &amp;&amp; (MatcherC() &amp;&amp; MatcherD())
</Original>
<Expanded>
1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
@ -2632,7 +2640,7 @@ Nor would this
<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()
1, (MatcherA() || MatcherB()) || MatcherC()
</Original>
<Expanded>
1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
@ -2640,7 +2648,15 @@ Nor would this
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
<Original>
1, MatcherA() || MatcherB() || MatcherC() || MatcherD()
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 )
@ -17541,7 +17557,7 @@ loose text artifact
</Section>
<OverallResult success="true"/>
</TestCase>
<OverallResults successes="1716" failures="149" expectedFailures="21"/>
<OverallResults successes="1720" failures="149" expectedFailures="21"/>
</Group>
<OverallResults successes="1716" failures="148" expectedFailures="21"/>
<OverallResults successes="1720" failures="148" expectedFailures="21"/>
</Catch>

View File

@ -678,35 +678,57 @@ namespace { namespace MatchersTests {
}
TEST_CASE("Combining MatchAnyOfGeneric does not nest", "[matchers][templated]") {
// MatchAnyOfGeneric LHS + some matcher RHS
STATIC_REQUIRE(std::is_same<
decltype(MatcherA() || MatcherB() || MatcherC()),
decltype((MatcherA() || MatcherB()) || MatcherC()),
Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>
>::value);
REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC());
REQUIRE_THAT(1, (MatcherA() || MatcherB()) || MatcherC());
// some matcher LHS + MatchAnyOfGeneric RHS
STATIC_REQUIRE(std::is_same<
decltype(MatcherA() || MatcherB() || MatcherC() || MatcherD()),
decltype(MatcherA() || (MatcherB() || MatcherC())),
Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>
>::value);
REQUIRE_THAT(1, MatcherA() || (MatcherB() || MatcherC()));
// MatchAnyOfGeneric LHS + MatchAnyOfGeneric RHS
STATIC_REQUIRE(std::is_same<
decltype((MatcherA() || MatcherB()) || (MatcherC() || MatcherD())),
Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
>::value);
REQUIRE_THAT(1, MatcherA() || MatcherB() || MatcherC() || MatcherD());
REQUIRE_THAT(1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD()));
}
TEST_CASE("Combining MatchAllOfGeneric does not nest", "[matchers][templated]") {
// MatchAllOfGeneric lhs + some matcher RHS
STATIC_REQUIRE(std::is_same<
decltype(MatcherA() && MatcherB() && MatcherC()),
decltype((MatcherA() && MatcherB()) && MatcherC()),
Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>
>::value);
REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC());
REQUIRE_THAT(1, (MatcherA() && MatcherB()) && MatcherC());
// some matcher LHS + MatchAllOfGeneric RSH
STATIC_REQUIRE(std::is_same<
decltype(MatcherA() && MatcherB() && MatcherC() && MatcherD()),
decltype(MatcherA() && (MatcherB() && MatcherC())),
Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>
>::value);
REQUIRE_THAT(1, MatcherA() && (MatcherB() && MatcherC()));
// MatchAllOfGeneric LHS + MatchAllOfGeneric RHS
STATIC_REQUIRE(std::is_same<
decltype((MatcherA() && MatcherB()) && (MatcherC() && MatcherD())),
Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
>::value);
REQUIRE_THAT(1, MatcherA() && MatcherB() && MatcherC() && MatcherD());
REQUIRE_THAT(1, (MatcherA() && MatcherB()) && (MatcherC() && MatcherD()));
}
TEST_CASE("Combining MatchNotOfGeneric does not nest", "[matchers][templated]") {