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: 225036073
			
			
This commit is contained in:
		
							parent
							
								
									84e8c236f8
								
							
						
					
					
						commit
						97bce1c9cc
					
				@ -1296,14 +1296,24 @@ class PredicateFormatterFromMatcher {
 | 
			
		||||
    // We don't write MatcherCast<const T&> either, as that allows
 | 
			
		||||
    // potentially unsafe downcasting of the matcher argument.
 | 
			
		||||
    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();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ::std::stringstream ss;
 | 
			
		||||
    ss << "Value of: " << value_text << "\n"
 | 
			
		||||
       << "Expected: ";
 | 
			
		||||
    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();
 | 
			
		||||
    return AssertionFailure() << ss.str();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -85,6 +85,7 @@ using std::pair;
 | 
			
		||||
using std::set;
 | 
			
		||||
using std::stringstream;
 | 
			
		||||
using std::vector;
 | 
			
		||||
using testing::_;
 | 
			
		||||
using testing::A;
 | 
			
		||||
using testing::AllArgs;
 | 
			
		||||
using testing::AllOf;
 | 
			
		||||
@ -110,12 +111,12 @@ using testing::Le;
 | 
			
		||||
using testing::Lt;
 | 
			
		||||
using testing::MakeMatcher;
 | 
			
		||||
using testing::MakePolymorphicMatcher;
 | 
			
		||||
using testing::MatchResultListener;
 | 
			
		||||
using testing::Matcher;
 | 
			
		||||
using testing::MatcherCast;
 | 
			
		||||
using testing::MatcherInterface;
 | 
			
		||||
using testing::Matches;
 | 
			
		||||
using testing::MatchesRegex;
 | 
			
		||||
using testing::MatchResultListener;
 | 
			
		||||
using testing::NanSensitiveDoubleEq;
 | 
			
		||||
using testing::NanSensitiveDoubleNear;
 | 
			
		||||
using testing::NanSensitiveFloatEq;
 | 
			
		||||
@ -135,15 +136,14 @@ using testing::StartsWith;
 | 
			
		||||
using testing::StrCaseEq;
 | 
			
		||||
using testing::StrCaseNe;
 | 
			
		||||
using testing::StrEq;
 | 
			
		||||
using testing::StrNe;
 | 
			
		||||
using testing::StringMatchResultListener;
 | 
			
		||||
using testing::StrNe;
 | 
			
		||||
using testing::Truly;
 | 
			
		||||
using testing::TypedEq;
 | 
			
		||||
using testing::UnorderedPointwise;
 | 
			
		||||
using testing::Value;
 | 
			
		||||
using testing::WhenSorted;
 | 
			
		||||
using testing::WhenSortedBy;
 | 
			
		||||
using testing::_;
 | 
			
		||||
using testing::internal::DummyMatchResultListener;
 | 
			
		||||
using testing::internal::ElementMatcherPair;
 | 
			
		||||
using testing::internal::ElementMatcherPairs;
 | 
			
		||||
@ -152,10 +152,11 @@ using testing::internal::FloatingEqMatcher;
 | 
			
		||||
using testing::internal::FormatMatcherDescription;
 | 
			
		||||
using testing::internal::IsReadableTypeName;
 | 
			
		||||
using testing::internal::MatchMatrix;
 | 
			
		||||
using testing::internal::PredicateFormatterFromMatcher;
 | 
			
		||||
using testing::internal::RE;
 | 
			
		||||
using testing::internal::StreamMatchResultListener;
 | 
			
		||||
using testing::internal::Strings;
 | 
			
		||||
using testing::internal::string;
 | 
			
		||||
using testing::internal::Strings;
 | 
			
		||||
 | 
			
		||||
// For testing ExplainMatchResultTo().
 | 
			
		||||
class GreaterThanMatcher : public MatcherInterface<int> {
 | 
			
		||||
@ -4932,7 +4933,7 @@ TYPED_TEST(ContainerEqTest, DuplicateDifference) {
 | 
			
		||||
}
 | 
			
		||||
#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.
 | 
			
		||||
TEST(ContainerEqExtraTest, MultipleValuesMissing) {
 | 
			
		||||
  static const int vals[] = {1, 1, 2, 3, 5, 8};
 | 
			
		||||
@ -6910,6 +6911,88 @@ TEST(ArgsTest, ExplainsMatchResultWithInnerExplanation) {
 | 
			
		||||
            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();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      GTEST_LOG_(FATAL) << "This should never be reached";
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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.
 | 
			
		||||
  std::string expect;
 | 
			
		||||
  EXPECT_EQ(expect, 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 testing
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user