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:
parent
695cf7c962
commit
06bb8d4d6d
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user