From a02a591605dfef9addde49634bf010dbe8f95c50 Mon Sep 17 00:00:00 2001 From: Abseil Team Date: Mon, 7 Dec 2020 16:17:26 -0500 Subject: [PATCH] Googletest export Add a `Pointer` matcher as an analog to `Pointee`. Similar to `Pointee`, `Pointer` works with either raw or smart pointers and allows creating a matcher like Pointer(Eq(foo)) for smart pointers. PiperOrigin-RevId: 346164768 --- googlemock/docs/cheat_sheet.md | 1 + googlemock/include/gmock/gmock-matchers.h | 71 ++++++++++++++++++- .../gmock/internal/gmock-internal-utils.h | 17 +---- googlemock/test/gmock-internal-utils_test.cc | 14 ---- googlemock/test/gmock-matchers_test.cc | 59 +++++++++++++++ 5 files changed, 131 insertions(+), 31 deletions(-) diff --git a/googlemock/docs/cheat_sheet.md b/googlemock/docs/cheat_sheet.md index bcb4ce94..fcb9201a 100644 --- a/googlemock/docs/cheat_sheet.md +++ b/googlemock/docs/cheat_sheet.md @@ -421,6 +421,7 @@ messages, you can use: | Matcher | Description | | :------------------------ | :---------------------------------------------- | | `Pointee(m)` | `argument` (either a smart pointer or a raw pointer) points to a value that matches matcher `m`. | +| `Pointer(m)` | `argument` (either a smart pointer or a raw pointer) contains a pointer that matches `m`. `m` will match against the raw pointer regardless of the type of `argument`. | | `WhenDynamicCastTo(m)` | when `argument` is passed through `dynamic_cast()`, it matches matcher `m`. | diff --git a/googlemock/include/gmock/gmock-matchers.h b/googlemock/include/gmock/gmock-matchers.h index d12d7bf7..ae064b5d 100644 --- a/googlemock/include/gmock/gmock-matchers.h +++ b/googlemock/include/gmock/gmock-matchers.h @@ -1841,8 +1841,9 @@ class PointeeMatcher { template class Impl : public MatcherInterface { public: - typedef typename PointeeOf::type - Pointee; + using Pointee = + typename std::pointer_traits::element_type; explicit Impl(const InnerMatcher& matcher) : matcher_(MatcherCast(matcher)) {} @@ -1872,6 +1873,64 @@ class PointeeMatcher { const InnerMatcher matcher_; }; +// Implements the Pointer(m) matcher +// Implements the Pointer(m) matcher for matching a pointer that matches matcher +// m. The pointer can be either raw or smart, and will match `m` against the +// raw pointer. +template +class PointerMatcher { + public: + explicit PointerMatcher(const InnerMatcher& matcher) : matcher_(matcher) {} + + // This type conversion operator template allows Pointer(m) to be + // used as a matcher for any pointer type whose pointer type is + // compatible with the inner matcher, where type PointerType can be + // either a raw pointer or a smart pointer. + // + // The reason we do this instead of relying on + // MakePolymorphicMatcher() is that the latter is not flexible + // enough for implementing the DescribeTo() method of Pointer(). + template + operator Matcher() const { // NOLINT + return Matcher(new Impl(matcher_)); + } + + private: + // The monomorphic implementation that works for a particular pointer type. + template + class Impl : public MatcherInterface { + public: + using Pointer = + const typename std::pointer_traits::element_type*; + + explicit Impl(const InnerMatcher& matcher) + : matcher_(MatcherCast(matcher)) {} + + void DescribeTo(::std::ostream* os) const override { + *os << "is a pointer that "; + matcher_.DescribeTo(os); + } + + void DescribeNegationTo(::std::ostream* os) const override { + *os << "is not a pointer that "; + matcher_.DescribeTo(os); + } + + bool MatchAndExplain(PointerType pointer, + MatchResultListener* listener) const override { + *listener << "which is a pointer that "; + Pointer p = GetRawPointer(pointer); + return MatchPrintAndExplain(p, matcher_, listener); + } + + private: + Matcher matcher_; + }; + + const InnerMatcher matcher_; +}; + #if GTEST_HAS_RTTI // Implements the WhenDynamicCastTo(m) matcher that matches a pointer or // reference that matches inner_matcher when dynamic_cast is applied. @@ -4720,6 +4779,14 @@ internal::FieldsAreMatcher::type...> FieldsAre( return internal::FieldsAreMatcher::type...>( std::forward(matchers)...); } + +// Creates a matcher that matches a pointer (raw or smart) that matches +// inner_matcher. +template +inline internal::PointerMatcher Pointer( + const InnerMatcher& inner_matcher) { + return internal::PointerMatcher(inner_matcher); +} } // namespace no_adl // Returns a predicate that is satisfied by anything that matches the diff --git a/googlemock/include/gmock/internal/gmock-internal-utils.h b/googlemock/include/gmock/internal/gmock-internal-utils.h index 5580dcb3..200c30e4 100644 --- a/googlemock/include/gmock/internal/gmock-internal-utils.h +++ b/googlemock/include/gmock/internal/gmock-internal-utils.h @@ -71,20 +71,6 @@ GTEST_API_ std::string JoinAsTuple(const Strings& fields); // "foo_bar_123" are converted to "foo bar 123". GTEST_API_ std::string ConvertIdentifierNameToWords(const char* id_name); -// PointeeOf::type is the type of a value pointed to by a -// Pointer, which can be either a smart pointer or a raw pointer. The -// following default implementation is for the case where Pointer is a -// smart pointer. -template -struct PointeeOf { - // Smart pointer classes define type element_type as the type of - // their pointees. - typedef typename Pointer::element_type type; -}; -// This specialization is for the raw pointer case. -template -struct PointeeOf { typedef T type; }; // NOLINT - // GetRawPointer(p) returns the raw pointer underlying p when p is a // smart pointer, or returns p itself when p is already a raw pointer. // The following default implementation is for the smart pointer case. @@ -378,7 +364,8 @@ template class StlContainerView< ::std::tuple > { public: typedef typename std::remove_const< - typename internal::PointeeOf::type>::type RawElement; + typename std::pointer_traits::element_type>::type + RawElement; typedef internal::NativeArray type; typedef const type const_reference; diff --git a/googlemock/test/gmock-internal-utils_test.cc b/googlemock/test/gmock-internal-utils_test.cc index 8019f4a3..0d15e8f4 100644 --- a/googlemock/test/gmock-internal-utils_test.cc +++ b/googlemock/test/gmock-internal-utils_test.cc @@ -124,20 +124,6 @@ TEST(ConvertIdentifierNameToWordsTest, WorksWhenNameIsMixture) { ConvertIdentifierNameToWords("_Chapter11Section_1_")); } -TEST(PointeeOfTest, WorksForSmartPointers) { - EXPECT_TRUE( - (std::is_same>::type>::value)); - EXPECT_TRUE( - (std::is_same>::type>::value)); -} - -TEST(PointeeOfTest, WorksForRawPointers) { - EXPECT_TRUE((std::is_same::type>::value)); - EXPECT_TRUE((std::is_same::type>::value)); - EXPECT_TRUE((std::is_void::type>::value)); -} - TEST(GetRawPointerTest, WorksForSmartPointers) { const char* const raw_p1 = new const char('a'); // NOLINT const std::unique_ptr p1(raw_p1); diff --git a/googlemock/test/gmock-matchers_test.cc b/googlemock/test/gmock-matchers_test.cc index e6923195..8084e29f 100644 --- a/googlemock/test/gmock-matchers_test.cc +++ b/googlemock/test/gmock-matchers_test.cc @@ -3728,6 +3728,65 @@ TEST(PointeeTest, ReferenceToNonConstRawPointer) { EXPECT_FALSE(m.Matches(p)); } +TEST(PointeeTest, SmartPointer) { + const Matcher> m = Pointee(Ge(0)); + + std::unique_ptr n(new int(1)); + EXPECT_TRUE(m.Matches(n)); +} + +TEST(PointeeTest, SmartPointerToConst) { + const Matcher> m = Pointee(Ge(0)); + + // There's no implicit conversion from unique_ptr to const + // unique_ptr, so we must pass a unique_ptr into the + // matcher. + std::unique_ptr n(new int(1)); + EXPECT_TRUE(m.Matches(n)); +} + +TEST(PointerTest, RawPointer) { + int n = 1; + const Matcher m = Pointer(Eq(&n)); + + EXPECT_TRUE(m.Matches(&n)); + + int* p = nullptr; + EXPECT_FALSE(m.Matches(p)); + EXPECT_FALSE(m.Matches(nullptr)); +} + +TEST(PointerTest, RawPointerToConst) { + int n = 1; + const Matcher m = Pointer(Eq(&n)); + + EXPECT_TRUE(m.Matches(&n)); + + int* p = nullptr; + EXPECT_FALSE(m.Matches(p)); + EXPECT_FALSE(m.Matches(nullptr)); +} + +TEST(PointerTest, SmartPointer) { + std::unique_ptr n(new int(10)); + int* raw_n = n.get(); + const Matcher> m = Pointer(Eq(raw_n)); + + EXPECT_TRUE(m.Matches(n)); +} + +TEST(PointerTest, SmartPointerToConst) { + std::unique_ptr n(new int(10)); + const int* raw_n = n.get(); + const Matcher> m = Pointer(Eq(raw_n)); + + // There's no implicit conversion from unique_ptr to const + // unique_ptr, so we must pass a unique_ptr into the + // matcher. + std::unique_ptr p(new int(10)); + EXPECT_FALSE(m.Matches(p)); +} + MATCHER_P(FieldIIs, inner_matcher, "") { return ExplainMatchResult(inner_matcher, arg.i, result_listener); }