diff --git a/include/gmock/gmock-matchers.h b/include/gmock/gmock-matchers.h index 8dba440a..9a1bab24 100644 --- a/include/gmock/gmock-matchers.h +++ b/include/gmock/gmock-matchers.h @@ -453,6 +453,38 @@ Matcher 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 +bool MatchPrintAndExplain(Value& value, const Matcher& 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::template Invoke(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::template Invoke(callable_, obj); + return MatchPrintAndExplain(result, matcher_, listener); } private: @@ -2098,39 +2115,50 @@ class PairMatcherImpl : public MatcherInterface { // 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 first_matcher_; const Matcher second_matcher_; diff --git a/include/gmock/gmock-printers.h b/include/gmock/gmock-printers.h index cda3545a..d1cd03ca 100644 --- a/include/gmock/gmock-printers.h +++ b/include/gmock/gmock-printers.h @@ -57,18 +57,20 @@ // // We also provide some convenient wrappers: // -// // Prints a value as the given type to a string. -// string ::testing::internal::UniversalPrinter::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 -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 class UniversalPrinter { @@ -602,12 +588,6 @@ class UniversalPrinter { 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 { UniversalPrinter::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 +::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_ diff --git a/test/gmock-generated-matchers_test.cc b/test/gmock-generated-matchers_test.cc index 5e14c42a..db2ffb2f 100644 --- a/test/gmock-generated-matchers_test.cc +++ b/test/gmock-generated-matchers_test.cc @@ -178,9 +178,7 @@ TEST(ArgsTest, CanMatchTupleByReference) { // Validates that arg is printed as str. MATCHER_P(PrintsAs, str, "") { - typedef GMOCK_REMOVE_CONST_(GMOCK_REMOVE_REFERENCE_(arg_type)) RawTuple; - return - testing::internal::UniversalPrinter::PrintToString(arg) == str; + return testing::PrintToString(arg) == str; } TEST(ArgsTest, AcceptsTenTemplateArgs) { diff --git a/test/gmock-matchers_test.cc b/test/gmock-matchers_test.cc index 1ba4c8d9..5557983c 100644 --- a/test/gmock-matchers_test.cc +++ b/test/gmock-matchers_test.cc @@ -148,11 +148,11 @@ class GreaterThanMatcher : public MatcherInterface { MatchResultListener* listener) const { const int diff = lhs - rhs_; if (diff > 0) { - *listener << "is " << diff << " more than " << rhs_; + *listener << "which is " << diff << " more than " << rhs_; } else if (diff == 0) { - *listener << "is the same as " << rhs_; + *listener << "which is the same as " << rhs_; } else { - *listener << "is " << -diff << " less than " << rhs_; + *listener << "which is " << -diff << " less than " << rhs_; } return lhs > rhs_; @@ -320,11 +320,11 @@ TEST(MatcherTest, MatchAndExplain) { Matcher m = GreaterThan(0); StringMatchResultListener listener1; EXPECT_TRUE(m.MatchAndExplain(42, &listener1)); - EXPECT_EQ("is 42 more than 0", listener1.str()); + EXPECT_EQ("which is 42 more than 0", listener1.str()); StringMatchResultListener listener2; EXPECT_FALSE(m.MatchAndExplain(-9, &listener2)); - EXPECT_EQ("is 9 less than 0", listener2.str()); + EXPECT_EQ("which is 9 less than 0", listener2.str()); } // Tests that a C-string literal can be implicitly converted to a @@ -1180,23 +1180,38 @@ TEST(PairTest, CanExplainMatchResultTo) { // If neither field matches, Pair() should explain about the first // field. const Matcher > m = Pair(GreaterThan(0), GreaterThan(0)); - EXPECT_EQ("the first field is 1 less than 0", + EXPECT_EQ("whose first field does not match, which is 1 less than 0", Explain(m, std::make_pair(-1, -2))); // If the first field matches but the second doesn't, Pair() should // explain about the second field. - EXPECT_EQ("the second field is 2 less than 0", + EXPECT_EQ("whose second field does not match, which is 2 less than 0", Explain(m, std::make_pair(1, -2))); // If the first field doesn't match but the second does, Pair() // should explain about the first field. - EXPECT_EQ("the first field is 1 less than 0", + EXPECT_EQ("whose first field does not match, which is 1 less than 0", Explain(m, std::make_pair(-1, 2))); // If both fields match, Pair() should explain about them both. - EXPECT_EQ("the first field is 1 more than 0" - ", and the second field is 2 more than 0", + EXPECT_EQ("whose both fields match, where the first field is a value " + "which is 1 more than 0, and the second field is a value " + "which is 2 more than 0", Explain(m, std::make_pair(1, 2))); + + // If only the first match has an explanation, only this explanation should + // be printed. + const Matcher > explain_first = Pair(GreaterThan(0), 0); + EXPECT_EQ("whose both fields match, where the first field is a value " + "which is 1 more than 0", + Explain(explain_first, std::make_pair(1, 0))); + + // If only the second match has an explanation, only this explanation should + // be printed. + const Matcher > explain_second = Pair(0, GreaterThan(0)); + EXPECT_EQ("whose both fields match, where the second field is a value " + "which is 1 more than 0", + Explain(explain_second, std::make_pair(0, 1))); } TEST(PairTest, MatchesCorrectly) { @@ -2544,7 +2559,14 @@ TEST(PointeeTest, CanExplainMatchResult) { const Matcher m2 = Pointee(GreaterThan(1)); int n = 3; - EXPECT_EQ("points to a value that is 2 more than 1", Explain(m2, &n)); + EXPECT_EQ("which points to 3, which is 2 more than 1", + Explain(m2, &n)); +} + +TEST(PointeeTest, AlwaysExplainsPointee) { + const Matcher m = Pointee(0); + int n = 42; + EXPECT_EQ("which points to 42", Explain(m, &n)); } // An uncopyable class. @@ -2671,8 +2693,9 @@ TEST(FieldTest, WorksForCompatibleMatcherType) { TEST(FieldTest, CanDescribeSelf) { Matcher m = Field(&AStruct::x, Ge(0)); - EXPECT_EQ("the given field is greater than or equal to 0", Describe(m)); - EXPECT_EQ("the given field is not greater than or equal to 0", + EXPECT_EQ("is an object whose given field is greater than or equal to 0", + Describe(m)); + EXPECT_EQ("is an object whose given field is not greater than or equal to 0", DescribeNegation(m)); } @@ -2682,10 +2705,10 @@ TEST(FieldTest, CanExplainMatchResult) { AStruct a; a.x = 1; - EXPECT_EQ("", Explain(m, a)); + EXPECT_EQ("whose given field is 1", Explain(m, a)); m = Field(&AStruct::x, GreaterThan(0)); - EXPECT_EQ("the given field is 1 more than 0", Explain(m, a)); + EXPECT_EQ("whose given field is 1, which is 1 more than 0", Explain(m, a)); } // Tests that Field() works when the argument is a pointer to const. @@ -2741,8 +2764,9 @@ TEST(FieldForPointerTest, WorksForArgumentOfSubType) { TEST(FieldForPointerTest, CanDescribeSelf) { Matcher m = Field(&AStruct::x, Ge(0)); - EXPECT_EQ("the given field is greater than or equal to 0", Describe(m)); - EXPECT_EQ("the given field is not greater than or equal to 0", + EXPECT_EQ("is an object whose given field is greater than or equal to 0", + Describe(m)); + EXPECT_EQ("is an object whose given field is not greater than or equal to 0", DescribeNegation(m)); } @@ -2753,10 +2777,11 @@ TEST(FieldForPointerTest, CanExplainMatchResult) { AStruct a; a.x = 1; EXPECT_EQ("", Explain(m, static_cast(NULL))); - EXPECT_EQ("", Explain(m, &a)); + EXPECT_EQ("which points to an object whose given field is 1", Explain(m, &a)); m = Field(&AStruct::x, GreaterThan(0)); - EXPECT_EQ("the given field is 1 more than 0", Explain(m, &a)); + EXPECT_EQ("which points to an object whose given field is 1, " + "which is 1 more than 0", Explain(m, &a)); } // A user-defined class for testing Property(). @@ -2875,9 +2900,10 @@ TEST(PropertyTest, WorksForCompatibleMatcherType) { TEST(PropertyTest, CanDescribeSelf) { Matcher m = Property(&AClass::n, Ge(0)); - EXPECT_EQ("the given property is greater than or equal to 0", Describe(m)); - EXPECT_EQ("the given property is not greater than or equal to 0", - DescribeNegation(m)); + EXPECT_EQ("is an object whose given property is greater than or equal to 0", + Describe(m)); + EXPECT_EQ("is an object whose given property " + "is not greater than or equal to 0", DescribeNegation(m)); } // Tests that Property() can explain the match result. @@ -2886,10 +2912,10 @@ TEST(PropertyTest, CanExplainMatchResult) { AClass a; a.set_n(1); - EXPECT_EQ("", Explain(m, a)); + EXPECT_EQ("whose given property is 1", Explain(m, a)); m = Property(&AClass::n, GreaterThan(0)); - EXPECT_EQ("the given property is 1 more than 0", Explain(m, a)); + EXPECT_EQ("whose given property is 1, which is 1 more than 0", Explain(m, a)); } // Tests that Property() works when the argument is a pointer to const. @@ -2954,9 +2980,10 @@ TEST(PropertyForPointerTest, WorksForArgumentOfSubType) { TEST(PropertyForPointerTest, CanDescribeSelf) { Matcher m = Property(&AClass::n, Ge(0)); - EXPECT_EQ("the given property is greater than or equal to 0", Describe(m)); - EXPECT_EQ("the given property is not greater than or equal to 0", - DescribeNegation(m)); + EXPECT_EQ("is an object whose given property is greater than or equal to 0", + Describe(m)); + EXPECT_EQ("is an object whose given property " + "is not greater than or equal to 0", DescribeNegation(m)); } // Tests that Property() can explain the result of matching a pointer. @@ -2966,10 +2993,12 @@ TEST(PropertyForPointerTest, CanExplainMatchResult) { AClass a; a.set_n(1); EXPECT_EQ("", Explain(m, static_cast(NULL))); - EXPECT_EQ("", Explain(m, &a)); + EXPECT_EQ("which points to an object whose given property is 1", + Explain(m, &a)); m = Property(&AClass::n, GreaterThan(0)); - EXPECT_EQ("the given property is 1 more than 0", Explain(m, &a)); + EXPECT_EQ("which points to an object whose given property is 1, " + "which is 1 more than 0", Explain(m, &a)); } // Tests ResultOf. @@ -2989,10 +3018,10 @@ TEST(ResultOfTest, WorksForFunctionPointers) { TEST(ResultOfTest, CanDescribeItself) { Matcher matcher = ResultOf(&IntToStringFunction, StrEq("foo")); - EXPECT_EQ("result of the given callable is equal to \"foo\"", - Describe(matcher)); - EXPECT_EQ("result of the given callable is not equal to \"foo\"", - DescribeNegation(matcher)); + EXPECT_EQ("is mapped by the given callable to a value that " + "is equal to \"foo\"", Describe(matcher)); + EXPECT_EQ("is mapped by the given callable to a value that " + "is not equal to \"foo\"", DescribeNegation(matcher)); } // Tests that ResultOf() can explain the match result. @@ -3000,11 +3029,12 @@ int IntFunction(int input) { return input == 42 ? 80 : 90; } TEST(ResultOfTest, CanExplainMatchResult) { Matcher matcher = ResultOf(&IntFunction, Ge(85)); - EXPECT_EQ("", Explain(matcher, 36)); + EXPECT_EQ("which is mapped by the given callable to 90", + Explain(matcher, 36)); matcher = ResultOf(&IntFunction, GreaterThan(85)); - EXPECT_EQ("result of the given callable is 5 more than 85", - Explain(matcher, 36)); + EXPECT_EQ("which is mapped by the given callable to 90, " + "which is 5 more than 85", Explain(matcher, 36)); } // Tests that ResultOf(f, ...) compiles and works as expected when f(x) @@ -3202,7 +3232,7 @@ TEST(ExplainMatchResultTest, AllOf_True_True_2) { TEST(ExplainmatcherResultTest, MonomorphicMatcher) { const Matcher m = GreaterThan(5); - EXPECT_EQ("is 1 more than 5", Explain(m, 6)); + EXPECT_EQ("which is 1 more than 5", Explain(m, 6)); } // The following two tests verify that values without a public copy diff --git a/test/gmock-printers_test.cc b/test/gmock-printers_test.cc index 0553e9ce..92c8413c 100644 --- a/test/gmock-printers_test.cc +++ b/test/gmock-printers_test.cc @@ -153,6 +153,7 @@ using ::std::tr1::make_tuple; using ::std::tr1::tuple; using ::std::vector; using ::testing::ElementsAre; +using ::testing::PrintToString; using ::testing::StartsWith; using ::testing::internal::NativeArray; using ::testing::internal::Strings; @@ -1010,19 +1011,24 @@ TEST(PrintReferenceTest, HandlesMemberVariablePointer) { + " " + Print(sizeof(p)) + "-byte object ")); } -TEST(PrintToStringTest, WorksForNonReference) { - EXPECT_EQ("123", UniversalPrinter::PrintToString(123)); +TEST(PrintToStringTest, WorksForScalar) { + EXPECT_EQ("123", PrintToString(123)); } -TEST(PrintToStringTest, WorksForReference) { - int n = 123; - EXPECT_EQ("@" + PrintPointer(&n) + " 123", - UniversalPrinter::PrintToString(n)); +TEST(PrintToStringTest, WorksForPointerToConstChar) { + const char* p = "hello"; + EXPECT_EQ("\"hello\"", PrintToString(p)); +} + +TEST(PrintToStringTest, WorksForPointerToNonConstChar) { + char s[] = "hello"; + char* p = s; + EXPECT_EQ("\"hello\"", PrintToString(p)); } TEST(PrintToStringTest, WorksForArray) { int n[3] = { 1, 2, 3 }; - EXPECT_EQ("{ 1, 2, 3 }", UniversalPrinter::PrintToString(n)); + EXPECT_EQ("{ 1, 2, 3 }", PrintToString(n)); } TEST(UniversalTersePrintTest, WorksForNonReference) {