Use matcher's description in AllOf if matcher has no explanation.

PiperOrigin-RevId: 652634229
Change-Id: I5fd7eaef4db3dea3d4142e1fb5fc97e46f654358
This commit is contained in:
Abseil Team 2024-07-15 16:39:22 -07:00 committed by Copybara-Service
parent 7321f950df
commit 417158b8bc
3 changed files with 39 additions and 23 deletions

View File

@ -1300,34 +1300,48 @@ class AllOfMatcherImpl : public MatcherInterface<const T&> {
bool MatchAndExplain(const T& x, bool MatchAndExplain(const T& x,
MatchResultListener* listener) const override { MatchResultListener* listener) const override {
// If either matcher1_ or matcher2_ doesn't match x, we only need // This method uses matcher's explanation when explaining the result.
// to explain why one of them fails. // However, if matcher doesn't provide one, this method uses matcher's
// description.
std::string all_match_result; std::string all_match_result;
for (const Matcher<T>& matcher : matchers_) {
for (size_t i = 0; i < matchers_.size(); ++i) {
StringMatchResultListener slistener; StringMatchResultListener slistener;
if (matchers_[i].MatchAndExplain(x, &slistener)) { // Return explanation for first failed matcher.
if (all_match_result.empty()) { if (!matcher.MatchAndExplain(x, &slistener)) {
all_match_result = slistener.str(); const std::string explanation = slistener.str();
if (!explanation.empty()) {
*listener << explanation;
} else { } else {
std::string result = slistener.str(); *listener << "which doesn't match (" << Describe(matcher) << ")";
if (!result.empty()) {
all_match_result += ", and ";
all_match_result += result;
}
} }
} else {
*listener << slistener.str();
return false; return false;
} }
// Keep track of explanations in case all matchers succeed.
std::string explanation = slistener.str();
if (explanation.empty()) {
explanation = Describe(matcher);
}
if (all_match_result.empty()) {
all_match_result = explanation;
} else {
if (!explanation.empty()) {
all_match_result += ", and ";
all_match_result += explanation;
}
}
} }
// Otherwise we need to explain why *both* of them match.
*listener << all_match_result; *listener << all_match_result;
return true; return true;
} }
private: private:
// Returns matcher description as a string.
std::string Describe(const Matcher<T>& matcher) const {
StringMatchResultListener listener;
matcher.DescribeTo(listener.stream());
return listener.str();
}
const std::vector<Matcher<T>> matchers_; const std::vector<Matcher<T>> matchers_;
}; };

View File

@ -559,10 +559,9 @@ TEST_P(AllOfTestP, ExplainsResult) {
Matcher<int> m; Matcher<int> m;
// Successful match. Both matchers need to explain. The second // Successful match. Both matchers need to explain. The second
// matcher doesn't give an explanation, so only the first matcher's // matcher doesn't give an explanation, so the matcher description is used.
// explanation is printed.
m = AllOf(GreaterThan(10), Lt(30)); m = AllOf(GreaterThan(10), Lt(30));
EXPECT_EQ("which is 15 more than 10", Explain(m, 25)); EXPECT_EQ("which is 15 more than 10, and is < 30", Explain(m, 25));
// Successful match. Both matchers need to explain. // Successful match. Both matchers need to explain.
m = AllOf(GreaterThan(10), GreaterThan(20)); m = AllOf(GreaterThan(10), GreaterThan(20));
@ -572,8 +571,9 @@ TEST_P(AllOfTestP, ExplainsResult) {
// Successful match. All matchers need to explain. The second // Successful match. All matchers need to explain. The second
// matcher doesn't given an explanation. // matcher doesn't given an explanation.
m = AllOf(GreaterThan(10), Lt(30), GreaterThan(20)); m = AllOf(GreaterThan(10), Lt(30), GreaterThan(20));
EXPECT_EQ("which is 15 more than 10, and which is 5 more than 20", EXPECT_EQ(
Explain(m, 25)); "which is 15 more than 10, and is < 30, and which is 5 more than 20",
Explain(m, 25));
// Successful match. All matchers need to explain. // Successful match. All matchers need to explain.
m = AllOf(GreaterThan(10), GreaterThan(20), GreaterThan(30)); m = AllOf(GreaterThan(10), GreaterThan(20), GreaterThan(30));
@ -588,10 +588,10 @@ TEST_P(AllOfTestP, ExplainsResult) {
EXPECT_EQ("which is 5 less than 10", Explain(m, 5)); EXPECT_EQ("which is 5 less than 10", Explain(m, 5));
// Failed match. The second matcher, which failed, needs to // Failed match. The second matcher, which failed, needs to
// explain. Since it doesn't given an explanation, nothing is // explain. Since it doesn't given an explanation, the matcher text is
// printed. // printed.
m = AllOf(GreaterThan(10), Lt(30)); m = AllOf(GreaterThan(10), Lt(30));
EXPECT_EQ("", Explain(m, 40)); EXPECT_EQ("which doesn't match (is < 30)", Explain(m, 40));
// Failed match. The second matcher, which failed, needs to // Failed match. The second matcher, which failed, needs to
// explain. // explain.

View File

@ -2334,9 +2334,11 @@ TEST(ExplainMatchResultTest, AllOf_True_True) {
EXPECT_EQ("which is 0 modulo 2, and which is 0 modulo 3", Explain(m, 6)); EXPECT_EQ("which is 0 modulo 2, and which is 0 modulo 3", Explain(m, 6));
} }
// Tests that when AllOf() succeeds, but matchers have no explanation,
// the matcher description is used.
TEST(ExplainMatchResultTest, AllOf_True_True_2) { TEST(ExplainMatchResultTest, AllOf_True_True_2) {
const Matcher<int> m = AllOf(Ge(2), Le(3)); const Matcher<int> m = AllOf(Ge(2), Le(3));
EXPECT_EQ("", Explain(m, 2)); EXPECT_EQ("is >= 2, and is <= 3", Explain(m, 2));
} }
INSTANTIATE_GTEST_MATCHER_TEST_P(ExplainmatcherResultTest); INSTANTIATE_GTEST_MATCHER_TEST_P(ExplainmatcherResultTest);