Fixes the explanation generated by many composite matchers (by Manuel Klimek); publishes the gmock value printer as testing::PrintToString() (by Zhanyong Wan).
This commit is contained in:
@@ -453,6 +453,38 @@ Matcher<T> A();
|
||||
// and MUST NOT BE USED IN USER CODE!!!
|
||||
namespace internal {
|
||||
|
||||
// If the explanation is not empty, prints it to the listener.
|
||||
// 'listener' must not be NULL.
|
||||
inline void PrintIfNotEmpty(
|
||||
const internal::string& explanation, MatchResultListener* listener) {
|
||||
if (explanation != "") {
|
||||
*listener << ", " << explanation;
|
||||
}
|
||||
}
|
||||
|
||||
// Matches the value against the given matcher, prints the value and explains
|
||||
// the match result to the listener. Returns the match result.
|
||||
// 'listener' must not be NULL.
|
||||
// Value cannot be passed by const reference, because some matchers take a
|
||||
// non-const argument.
|
||||
template <typename Value, typename T>
|
||||
bool MatchPrintAndExplain(Value& value, const Matcher<T>& matcher,
|
||||
MatchResultListener* listener) {
|
||||
if (!listener->IsInterested()) {
|
||||
// If the listener is not interested, we do not need to construct the
|
||||
// inner explanation.
|
||||
return matcher.Matches(value);
|
||||
}
|
||||
|
||||
StringMatchResultListener inner_listener;
|
||||
const bool match = matcher.MatchAndExplain(value, &inner_listener);
|
||||
|
||||
UniversalPrint(value, listener->stream());
|
||||
PrintIfNotEmpty(inner_listener.str(), listener);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
// If the given string is not empty and os is not NULL, wraps the
|
||||
// string inside a pair of parentheses and streams the result to os.
|
||||
inline void StreamInParensAsNeeded(const internal::string& str,
|
||||
@@ -1604,13 +1636,8 @@ class PointeeMatcher {
|
||||
if (GetRawPointer(pointer) == NULL)
|
||||
return false;
|
||||
|
||||
StringMatchResultListener inner_listener;
|
||||
const bool match = matcher_.MatchAndExplain(*pointer, &inner_listener);
|
||||
const internal::string s = inner_listener.str();
|
||||
if (s != "") {
|
||||
*listener << "points to a value that " << s;
|
||||
}
|
||||
return match;
|
||||
*listener << "which points to ";
|
||||
return MatchPrintAndExplain(*pointer, matcher_, listener);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -1634,12 +1661,12 @@ class FieldMatcher {
|
||||
: field_(field), matcher_(matcher) {}
|
||||
|
||||
void DescribeTo(::std::ostream* os) const {
|
||||
*os << "the given field ";
|
||||
*os << "is an object whose given field ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "the given field ";
|
||||
*os << "is an object whose given field ";
|
||||
matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
@@ -1657,13 +1684,8 @@ class FieldMatcher {
|
||||
// true_type iff the Field() matcher is used to match a pointer.
|
||||
bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
|
||||
MatchResultListener* listener) const {
|
||||
StringMatchResultListener inner_listener;
|
||||
const bool match = matcher_.MatchAndExplain(obj.*field_, &inner_listener);
|
||||
const internal::string s = inner_listener.str();
|
||||
if (s != "") {
|
||||
*listener << "the given field " << s;
|
||||
}
|
||||
return match;
|
||||
*listener << "whose given field is ";
|
||||
return MatchPrintAndExplain(obj.*field_, matcher_, listener);
|
||||
}
|
||||
|
||||
bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
|
||||
@@ -1671,6 +1693,7 @@ class FieldMatcher {
|
||||
if (p == NULL)
|
||||
return false;
|
||||
|
||||
*listener << "which points to an object ";
|
||||
// Since *p has a field, it must be a class/struct/union type and
|
||||
// thus cannot be a pointer. Therefore we pass false_type() as
|
||||
// the first argument.
|
||||
@@ -1699,12 +1722,12 @@ class PropertyMatcher {
|
||||
: property_(property), matcher_(matcher) {}
|
||||
|
||||
void DescribeTo(::std::ostream* os) const {
|
||||
*os << "the given property ";
|
||||
*os << "is an object whose given property ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
void DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "the given property ";
|
||||
*os << "is an object whose given property ";
|
||||
matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
@@ -1722,14 +1745,11 @@ class PropertyMatcher {
|
||||
// true_type iff the Property() matcher is used to match a pointer.
|
||||
bool MatchAndExplainImpl(false_type /* is_not_pointer */, const Class& obj,
|
||||
MatchResultListener* listener) const {
|
||||
StringMatchResultListener inner_listener;
|
||||
const bool match = matcher_.MatchAndExplain((obj.*property_)(),
|
||||
&inner_listener);
|
||||
const internal::string s = inner_listener.str();
|
||||
if (s != "") {
|
||||
*listener << "the given property " << s;
|
||||
}
|
||||
return match;
|
||||
*listener << "whose given property is ";
|
||||
// Cannot pass the return value (for example, int) to MatchPrintAndExplain,
|
||||
// which takes a non-const reference as argument.
|
||||
RefToConstProperty result = (obj.*property_)();
|
||||
return MatchPrintAndExplain(result, matcher_, listener);
|
||||
}
|
||||
|
||||
bool MatchAndExplainImpl(true_type /* is_pointer */, const Class* p,
|
||||
@@ -1737,6 +1757,7 @@ class PropertyMatcher {
|
||||
if (p == NULL)
|
||||
return false;
|
||||
|
||||
*listener << "which points to an object ";
|
||||
// Since *p has a property method, it must be a class/struct/union
|
||||
// type and thus cannot be a pointer. Therefore we pass
|
||||
// false_type() as the first argument.
|
||||
@@ -1806,26 +1827,22 @@ class ResultOfMatcher {
|
||||
: callable_(callable), matcher_(matcher) {}
|
||||
|
||||
virtual void DescribeTo(::std::ostream* os) const {
|
||||
*os << "result of the given callable ";
|
||||
*os << "is mapped by the given callable to a value that ";
|
||||
matcher_.DescribeTo(os);
|
||||
}
|
||||
|
||||
virtual void DescribeNegationTo(::std::ostream* os) const {
|
||||
*os << "result of the given callable ";
|
||||
*os << "is mapped by the given callable to a value that ";
|
||||
matcher_.DescribeNegationTo(os);
|
||||
}
|
||||
|
||||
virtual bool MatchAndExplain(T obj, MatchResultListener* listener) const {
|
||||
StringMatchResultListener inner_listener;
|
||||
const bool match = matcher_.MatchAndExplain(
|
||||
CallableTraits<Callable>::template Invoke<T>(callable_, obj),
|
||||
&inner_listener);
|
||||
|
||||
const internal::string s = inner_listener.str();
|
||||
if (s != "")
|
||||
*listener << "result of the given callable " << s;
|
||||
|
||||
return match;
|
||||
*listener << "which is mapped by the given callable to ";
|
||||
// Cannot pass the return value (for example, int) to
|
||||
// MatchPrintAndExplain, which takes a non-const reference as argument.
|
||||
ResultType result =
|
||||
CallableTraits<Callable>::template Invoke<T>(callable_, obj);
|
||||
return MatchPrintAndExplain(result, matcher_, listener);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -2098,39 +2115,50 @@ class PairMatcherImpl : public MatcherInterface<PairType> {
|
||||
// matches second_matcher.
|
||||
virtual bool MatchAndExplain(PairType a_pair,
|
||||
MatchResultListener* listener) const {
|
||||
StringMatchResultListener listener1;
|
||||
const bool match1 = first_matcher_.MatchAndExplain(a_pair.first,
|
||||
&listener1);
|
||||
internal::string s1 = listener1.str();
|
||||
if (s1 != "") {
|
||||
s1 = "the first field " + s1;
|
||||
if (!listener->IsInterested()) {
|
||||
// If the listener is not interested, we don't need to construct the
|
||||
// explanation.
|
||||
return first_matcher_.Matches(a_pair.first) &&
|
||||
second_matcher_.Matches(a_pair.second);
|
||||
}
|
||||
if (!match1) {
|
||||
*listener << s1;
|
||||
StringMatchResultListener first_inner_listener;
|
||||
if (!first_matcher_.MatchAndExplain(a_pair.first,
|
||||
&first_inner_listener)) {
|
||||
*listener << "whose first field does not match";
|
||||
PrintIfNotEmpty(first_inner_listener.str(), listener);
|
||||
return false;
|
||||
}
|
||||
|
||||
StringMatchResultListener listener2;
|
||||
const bool match2 = second_matcher_.MatchAndExplain(a_pair.second,
|
||||
&listener2);
|
||||
internal::string s2 = listener2.str();
|
||||
if (s2 != "") {
|
||||
s2 = "the second field " + s2;
|
||||
}
|
||||
if (!match2) {
|
||||
*listener << s2;
|
||||
StringMatchResultListener second_inner_listener;
|
||||
if (!second_matcher_.MatchAndExplain(a_pair.second,
|
||||
&second_inner_listener)) {
|
||||
*listener << "whose second field does not match";
|
||||
PrintIfNotEmpty(second_inner_listener.str(), listener);
|
||||
return false;
|
||||
}
|
||||
|
||||
*listener << s1;
|
||||
if (s1 != "" && s2 != "") {
|
||||
*listener << ", and ";
|
||||
}
|
||||
*listener << s2;
|
||||
ExplainSuccess(first_inner_listener.str(), second_inner_listener.str(),
|
||||
listener);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void ExplainSuccess(const internal::string& first_explanation,
|
||||
const internal::string& second_explanation,
|
||||
MatchResultListener* listener) const {
|
||||
*listener << "whose both fields match";
|
||||
if (first_explanation != "") {
|
||||
*listener << ", where the first field is a value " << first_explanation;
|
||||
}
|
||||
if (second_explanation != "") {
|
||||
*listener << ", ";
|
||||
if (first_explanation != "") {
|
||||
*listener << "and ";
|
||||
} else {
|
||||
*listener << "where ";
|
||||
}
|
||||
*listener << "the second field is a value " << second_explanation;
|
||||
}
|
||||
}
|
||||
|
||||
const Matcher<const FirstType&> first_matcher_;
|
||||
const Matcher<const SecondType&> second_matcher_;
|
||||
|
||||
|
||||
@@ -57,18 +57,20 @@
|
||||
//
|
||||
// We also provide some convenient wrappers:
|
||||
//
|
||||
// // Prints a value as the given type to a string.
|
||||
// string ::testing::internal::UniversalPrinter<T>::PrintToString(value);
|
||||
// // Prints a value to a string. For a (const or not) char
|
||||
// // pointer, the NUL-terminated string (but not the pointer) is
|
||||
// // printed.
|
||||
// std::string ::testing::PrintToString(const T& value);
|
||||
//
|
||||
// // Prints a value tersely: for a reference type, the referenced
|
||||
// // value (but not the address) is printed; for a (const) char
|
||||
// // value (but not the address) is printed; for a (const or not) char
|
||||
// // pointer, the NUL-terminated string (but not the pointer) is
|
||||
// // printed.
|
||||
// void ::testing::internal::UniversalTersePrint(const T& value, ostream*);
|
||||
//
|
||||
// // Prints value using the type inferred by the compiler. The difference
|
||||
// // from UniversalTersePrint() is that this function prints both the
|
||||
// // pointer and the NUL-terminated string for a (const) char pointer.
|
||||
// // pointer and the NUL-terminated string for a (const or not) char pointer.
|
||||
// void ::testing::internal::UniversalPrint(const T& value, ostream*);
|
||||
//
|
||||
// // Prints the fields of a tuple tersely to a string vector, one
|
||||
@@ -545,14 +547,6 @@ class UniversalPrinter {
|
||||
PrintTo(value, os);
|
||||
}
|
||||
|
||||
// A convenient wrapper for Print() that returns the print-out as a
|
||||
// string.
|
||||
static string PrintToString(const T& value) {
|
||||
::std::stringstream ss;
|
||||
Print(value, &ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop) // Restores the warning state.
|
||||
#endif // _MSC_VER
|
||||
@@ -585,14 +579,6 @@ void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) {
|
||||
// This overload prints a (const) char array compactly.
|
||||
void UniversalPrintArray(const char* begin, size_t len, ::std::ostream* os);
|
||||
|
||||
// Prints an array of 'len' elements, starting at address 'begin', to a string.
|
||||
template <typename T>
|
||||
string UniversalPrintArrayToString(const T* begin, size_t len) {
|
||||
::std::stringstream ss;
|
||||
UniversalPrintArray(begin, len, &ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Implements printing an array type T[N].
|
||||
template <typename T, size_t N>
|
||||
class UniversalPrinter<T[N]> {
|
||||
@@ -602,12 +588,6 @@ class UniversalPrinter<T[N]> {
|
||||
static void Print(const T (&a)[N], ::std::ostream* os) {
|
||||
UniversalPrintArray(a, N, os);
|
||||
}
|
||||
|
||||
// A convenient wrapper for Print() that returns the print-out as a
|
||||
// string.
|
||||
static string PrintToString(const T (&a)[N]) {
|
||||
return UniversalPrintArrayToString(a, N);
|
||||
}
|
||||
};
|
||||
|
||||
// Implements printing a reference type T&.
|
||||
@@ -630,14 +610,6 @@ class UniversalPrinter<T&> {
|
||||
UniversalPrinter<T>::Print(value, os);
|
||||
}
|
||||
|
||||
// A convenient wrapper for Print() that returns the print-out as a
|
||||
// string.
|
||||
static string PrintToString(const T& value) {
|
||||
::std::stringstream ss;
|
||||
Print(value, &ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop) // Restores the warning state.
|
||||
#endif // _MSC_VER
|
||||
@@ -740,6 +712,14 @@ Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) {
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template <typename T>
|
||||
::std::string PrintToString(const T& value) {
|
||||
::std::stringstream ss;
|
||||
internal::UniversalTersePrint(value, &ss);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
} // namespace testing
|
||||
|
||||
#endif // GMOCK_INCLUDE_GMOCK_GMOCK_PRINTERS_H_
|
||||
|
||||
Reference in New Issue
Block a user