Googletest export

The gmock matchers have a concept of MatchAndExpain; where the details of the
matching are written to a "result listener". A matcher can avoid creating
expensive debug info by checking result_listener->IsInterested(); but,
unfortunately, the default matcher code (called from EXPECT_THAT) is always
"interested".

This change implements EXPECT_THAT matching to first run the matcher in a "not
interested" mode; and then run it a second time ("interested") only if the
match fails.

PiperOrigin-RevId: 224929783
This commit is contained in:
Abseil Team 2018-12-10 22:46:47 -05:00 committed by Gennadiy Civil
parent 695cf7c962
commit 06bb8d4d6d
2 changed files with 96 additions and 7 deletions

View File

@ -1296,14 +1296,24 @@ class PredicateFormatterFromMatcher {
// We don't write MatcherCast<const T&> either, as that allows // We don't write MatcherCast<const T&> either, as that allows
// potentially unsafe downcasting of the matcher argument. // potentially unsafe downcasting of the matcher argument.
const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_); const Matcher<const T&> matcher = SafeMatcherCast<const T&>(matcher_);
StringMatchResultListener listener;
if (MatchPrintAndExplain(x, matcher, &listener)) // The expected path here is that the matcher should match (i.e. that most
// tests pass) so optimize for this case.
if (matcher.Matches(x)) {
return AssertionSuccess(); return AssertionSuccess();
}
::std::stringstream ss; ::std::stringstream ss;
ss << "Value of: " << value_text << "\n" ss << "Value of: " << value_text << "\n"
<< "Expected: "; << "Expected: ";
matcher.DescribeTo(&ss); matcher.DescribeTo(&ss);
// Rerun the matcher to "PrintAndExain" the failure.
StringMatchResultListener listener;
if (MatchPrintAndExplain(x, matcher, &listener)) {
ss << "\n The matcher failed on the initial attempt; but passed when "
"rerun to generate the explanation.";
}
ss << "\n Actual: " << listener.str(); ss << "\n Actual: " << listener.str();
return AssertionFailure() << ss.str(); return AssertionFailure() << ss.str();
} }

View File

@ -85,6 +85,7 @@ using std::pair;
using std::set; using std::set;
using std::stringstream; using std::stringstream;
using std::vector; using std::vector;
using testing::_;
using testing::A; using testing::A;
using testing::AllArgs; using testing::AllArgs;
using testing::AllOf; using testing::AllOf;
@ -110,12 +111,12 @@ using testing::Le;
using testing::Lt; using testing::Lt;
using testing::MakeMatcher; using testing::MakeMatcher;
using testing::MakePolymorphicMatcher; using testing::MakePolymorphicMatcher;
using testing::MatchResultListener;
using testing::Matcher; using testing::Matcher;
using testing::MatcherCast; using testing::MatcherCast;
using testing::MatcherInterface; using testing::MatcherInterface;
using testing::Matches; using testing::Matches;
using testing::MatchesRegex; using testing::MatchesRegex;
using testing::MatchResultListener;
using testing::NanSensitiveDoubleEq; using testing::NanSensitiveDoubleEq;
using testing::NanSensitiveDoubleNear; using testing::NanSensitiveDoubleNear;
using testing::NanSensitiveFloatEq; using testing::NanSensitiveFloatEq;
@ -135,15 +136,14 @@ using testing::StartsWith;
using testing::StrCaseEq; using testing::StrCaseEq;
using testing::StrCaseNe; using testing::StrCaseNe;
using testing::StrEq; using testing::StrEq;
using testing::StrNe;
using testing::StringMatchResultListener; using testing::StringMatchResultListener;
using testing::StrNe;
using testing::Truly; using testing::Truly;
using testing::TypedEq; using testing::TypedEq;
using testing::UnorderedPointwise; using testing::UnorderedPointwise;
using testing::Value; using testing::Value;
using testing::WhenSorted; using testing::WhenSorted;
using testing::WhenSortedBy; using testing::WhenSortedBy;
using testing::_;
using testing::internal::DummyMatchResultListener; using testing::internal::DummyMatchResultListener;
using testing::internal::ElementMatcherPair; using testing::internal::ElementMatcherPair;
using testing::internal::ElementMatcherPairs; using testing::internal::ElementMatcherPairs;
@ -152,10 +152,11 @@ using testing::internal::FloatingEqMatcher;
using testing::internal::FormatMatcherDescription; using testing::internal::FormatMatcherDescription;
using testing::internal::IsReadableTypeName; using testing::internal::IsReadableTypeName;
using testing::internal::MatchMatrix; using testing::internal::MatchMatrix;
using testing::internal::PredicateFormatterFromMatcher;
using testing::internal::RE; using testing::internal::RE;
using testing::internal::StreamMatchResultListener; using testing::internal::StreamMatchResultListener;
using testing::internal::Strings;
using testing::internal::string; using testing::internal::string;
using testing::internal::Strings;
// For testing ExplainMatchResultTo(). // For testing ExplainMatchResultTo().
class GreaterThanMatcher : public MatcherInterface<int> { class GreaterThanMatcher : public MatcherInterface<int> {
@ -4932,7 +4933,7 @@ TYPED_TEST(ContainerEqTest, DuplicateDifference) {
} }
#endif // GTEST_HAS_TYPED_TEST #endif // GTEST_HAS_TYPED_TEST
// Tests that mutliple missing values are reported. // Tests that multiple missing values are reported.
// Using just vector here, so order is predictable. // Using just vector here, so order is predictable.
TEST(ContainerEqExtraTest, MultipleValuesMissing) { TEST(ContainerEqExtraTest, MultipleValuesMissing) {
static const int vals[] = {1, 1, 2, 3, 5, 8}; static const int vals[] = {1, 1, 2, 3, 5, 8};
@ -6910,6 +6911,84 @@ TEST(ArgsTest, ExplainsMatchResultWithInnerExplanation) {
Explain(m, std::make_tuple('\0', 42, 43))); Explain(m, std::make_tuple('\0', 42, 43)));
} }
class PredicateFormatterFromMatcherTest : public ::testing::Test {
protected:
enum Behavior { kInitialSuccess, kAlwaysFail, kFlaky };
// A matcher that can return different results when used multiple times on the
// same input. No real matcher should do this; but this lets us test that we
// detect such behavior and fail appropriately.
class MockMatcher : public MatcherInterface<Behavior> {
public:
bool MatchAndExplain(Behavior behavior,
MatchResultListener* listener) const override {
*listener << "[MatchAndExplain]";
switch (behavior) {
case kInitialSuccess:
// The first call to MatchAndExplain should use a "not interested"
// listener; so this is expected to return |true|. There should be no
// subsequent calls.
return !listener->IsInterested();
case kAlwaysFail:
return false;
case kFlaky:
// The first call to MatchAndExplain should use a "not interested"
// listener; so this will return |false|. Subsequent calls should have
// an "interested" listener; so this will return |true|, thus
// simulating a flaky matcher.
return listener->IsInterested();
}
}
void DescribeTo(ostream* os) const override { *os << "[DescribeTo]"; }
void DescribeNegationTo(ostream* os) const override {
*os << "[DescribeNegationTo]";
}
};
AssertionResult RunPredicateFormatter(Behavior behavior) {
auto matcher = MakeMatcher(new MockMatcher);
PredicateFormatterFromMatcher<Matcher<Behavior>> predicate_formatter(
matcher);
return predicate_formatter("dummy-name", behavior);
}
const std::string kMatcherType =
"testing::gmock_matchers_test::PredicateFormatterFromMatcherTest::"
"Behavior";
};
TEST_F(PredicateFormatterFromMatcherTest, ShortCircuitOnSuccess) {
AssertionResult result = RunPredicateFormatter(kInitialSuccess);
EXPECT_TRUE(result); // Implicit cast to bool.
EXPECT_EQ("", result.message());
}
TEST_F(PredicateFormatterFromMatcherTest, NoShortCircuitOnFailure) {
AssertionResult result = RunPredicateFormatter(kAlwaysFail);
EXPECT_FALSE(result); // Implicit cast to bool.
std::string expect =
"Value of: dummy-name\nExpected: [DescribeTo]\n"
" Actual: 1" +
OfType(kMatcherType) + ", [MatchAndExplain]";
EXPECT_EQ(expect, result.message());
}
TEST_F(PredicateFormatterFromMatcherTest, DetectsFlakyShortCircuit) {
AssertionResult result = RunPredicateFormatter(kFlaky);
EXPECT_FALSE(result); // Implicit cast to bool.
std::string expect =
"Value of: dummy-name\nExpected: [DescribeTo]\n"
" The matcher failed on the initial attempt; but passed when rerun to "
"generate the explanation.\n"
" Actual: 2" +
OfType(kMatcherType) + ", [MatchAndExplain]";
EXPECT_EQ(expect, result.message());
}
} // namespace gmock_matchers_test } // namespace gmock_matchers_test
} // namespace testing } // namespace testing