Commit c13c27a5 by Abseil Team Committed by Derek Mauro

Googletest export

Change Matcher<T> to allow binding an implementation by value directly: - Drop the requirement of MatcherInterface. Doing manual type erasure avoid extra layers in many cases. - Avoid the adaptor for `MatcherInterface<T>` and `MatcherInterface<const T&>` mismatch. - Use a small object optimization when possible. This makes things like `_` and `Eq(1)` really cheap and do not require memory allocations. - Migrate some matchers to the new model to speed them up and to test the new framework. More matchers to come in future changes. PiperOrigin-RevId: 350580998
parent 48928352
...@@ -1315,32 +1315,30 @@ how you can define a matcher to do it: ...@@ -1315,32 +1315,30 @@ how you can define a matcher to do it:
```cpp ```cpp
using ::testing::Matcher; using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
class BarPlusBazEqMatcher : public MatcherInterface<const Foo&> { class BarPlusBazEqMatcher {
public: public:
explicit BarPlusBazEqMatcher(int expected_sum) explicit BarPlusBazEqMatcher(int expected_sum)
: expected_sum_(expected_sum) {} : expected_sum_(expected_sum) {}
bool MatchAndExplain(const Foo& foo, bool MatchAndExplain(const Foo& foo,
MatchResultListener* /* listener */) const override { std::ostream* /* listener */) const {
return (foo.bar() + foo.baz()) == expected_sum_; return (foo.bar() + foo.baz()) == expected_sum_;
} }
void DescribeTo(std::ostream* os) const override { void DescribeTo(std::ostream& os) const {
*os << "bar() + baz() equals " << expected_sum_; os << "bar() + baz() equals " << expected_sum_;
} }
void DescribeNegationTo(std::ostream* os) const override { void DescribeNegationTo(std::ostream& os) const {
*os << "bar() + baz() does not equal " << expected_sum_; os << "bar() + baz() does not equal " << expected_sum_;
} }
private: private:
const int expected_sum_; const int expected_sum_;
}; };
Matcher<const Foo&> BarPlusBazEq(int expected_sum) { Matcher<const Foo&> BarPlusBazEq(int expected_sum) {
return MakeMatcher(new BarPlusBazEqMatcher(expected_sum)); return BarPlusBazEqMatcher(expected_sum);
} }
... ...
...@@ -3535,51 +3533,39 @@ MATCHER_P2(Blah, a, b, description_string_2) { ... } ...@@ -3535,51 +3533,39 @@ MATCHER_P2(Blah, a, b, description_string_2) { ... }
``` ```
While it's tempting to always use the `MATCHER*` macros when defining a new While it's tempting to always use the `MATCHER*` macros when defining a new
matcher, you should also consider implementing `MatcherInterface` or using matcher, you should also consider implementing the matcher interface directly
`MakePolymorphicMatcher()` instead (see the recipes that follow), especially if instead (see the recipes that follow), especially if you need to use the matcher
you need to use the matcher a lot. While these approaches require more work, a lot. While these approaches require more work, they give you more control on
they give you more control on the types of the value being matched and the the types of the value being matched and the matcher parameters, which in
matcher parameters, which in general leads to better compiler error messages general leads to better compiler error messages that pay off in the long run.
that pay off in the long run. They also allow overloading matchers based on They also allow overloading matchers based on parameter types (as opposed to
parameter types (as opposed to just based on the number of parameters). just based on the number of parameters).
### Writing New Monomorphic Matchers ### Writing New Monomorphic Matchers
A matcher of argument type `T` implements `::testing::MatcherInterface<T>` and A matcher of argument type `T` implements the matcher interface for `T` and does
does two things: it tests whether a value of type `T` matches the matcher, and two things: it tests whether a value of type `T` matches the matcher, and can
can describe what kind of values it matches. The latter ability is used for describe what kind of values it matches. The latter ability is used for
generating readable error messages when expectations are violated. generating readable error messages when expectations are violated.
The interface looks like this: A matcher of `T` must declare a typedef like:
```cpp ```cpp
class MatchResultListener { using is_gtest_matcher = void;
public: ```
...
// Streams x to the underlying ostream; does nothing if the ostream
// is NULL.
template <typename T>
MatchResultListener& operator<<(const T& x);
// Returns the underlying ostream.
std::ostream* stream();
};
template <typename T>
class MatcherInterface {
public:
virtual ~MatcherInterface();
// Returns true if and only if the matcher matches x; also explains the match and supports the following operations:
// result to 'listener'.
virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
// Describes this matcher to an ostream. ```cpp
virtual void DescribeTo(std::ostream* os) const = 0; // Match a value and optionally explain into an ostream.
bool matched = matcher.MatchAndExplain(value, maybe_os);
// where `value` is of type `T` and
// `maybe_os` is of type `std::ostream*`, where it can be null if the caller
// is not interested in there textual explanation.
// Describes the negation of this matcher to an ostream. matcher.DescribeTo(os);
virtual void DescribeNegationTo(std::ostream* os) const; matcher.DescribeNegationTo(os);
}; // where `os` is of type `std::ostream*`.
``` ```
If you need a custom matcher but `Truly()` is not a good option (for example, If you need a custom matcher but `Truly()` is not a good option (for example,
...@@ -3594,29 +3580,25 @@ For example, you can define a matcher to test whether an `int` is divisible by 7 ...@@ -3594,29 +3580,25 @@ For example, you can define a matcher to test whether an `int` is divisible by 7
and then use it like this: and then use it like this:
```cpp ```cpp
using ::testing::MakeMatcher;
using ::testing::Matcher; using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
class DivisibleBy7Matcher : public MatcherInterface<int> { class DivisibleBy7Matcher {
public: public:
bool MatchAndExplain(int n, bool MatchAndExplain(int n, std::ostream*) const {
MatchResultListener* /* listener */) const override {
return (n % 7) == 0; return (n % 7) == 0;
} }
void DescribeTo(std::ostream* os) const override { void DescribeTo(std::ostream* os) const {
*os << "is divisible by 7"; *os << "is divisible by 7";
} }
void DescribeNegationTo(std::ostream* os) const override { void DescribeNegationTo(std::ostream* os) const {
*os << "is not divisible by 7"; *os << "is not divisible by 7";
} }
}; };
Matcher<int> DivisibleBy7() { Matcher<int> DivisibleBy7() {
return MakeMatcher(new DivisibleBy7Matcher); return DivisibleBy7Matcher();
} }
... ...
...@@ -3624,16 +3606,15 @@ Matcher<int> DivisibleBy7() { ...@@ -3624,16 +3606,15 @@ Matcher<int> DivisibleBy7() {
``` ```
You may improve the matcher message by streaming additional information to the You may improve the matcher message by streaming additional information to the
`listener` argument in `MatchAndExplain()`: `os` argument in `MatchAndExplain()`:
```cpp ```cpp
class DivisibleBy7Matcher : public MatcherInterface<int> { class DivisibleBy7Matcher {
public: public:
bool MatchAndExplain(int n, bool MatchAndExplain(int n, std::ostream* os) const {
MatchResultListener* listener) const override {
const int remainder = n % 7; const int remainder = n % 7;
if (remainder != 0) { if (remainder != 0 && os != nullptr) {
*listener << "the remainder is " << remainder; *os << "the remainder is " << remainder;
} }
return remainder == 0; return remainder == 0;
} }
...@@ -3651,13 +3632,79 @@ Expected: is divisible by 7 ...@@ -3651,13 +3632,79 @@ Expected: is divisible by 7
### Writing New Polymorphic Matchers ### Writing New Polymorphic Matchers
You've learned how to write your own matchers in the previous recipe. Just one Expanding what we learned above to *polymorphic* matchers is now just as simple
problem: a matcher created using `MakeMatcher()` only works for one particular as adding templates in the right place.
type of arguments. If you want a *polymorphic* matcher that works with arguments
of several types (for instance, `Eq(x)` can be used to match a *`value`* as long ```cpp
as `value == x` compiles -- *`value`* and `x` don't have to share the same
type), you can learn the trick from `testing/base/public/gmock-matchers.h` but class NotNullMatcher {
it's a bit involved. public:
// To implement a polymorphic matcher, we just need to make MatchAndExplain a
// template on its first argument.
// In this example, we want to use NotNull() with any pointer, so
// MatchAndExplain() accepts a pointer of any type as its first argument.
// In general, you can define MatchAndExplain() as an ordinary method or
// a method template, or even overload it.
template <typename T>
bool MatchAndExplain(T* p, std::ostream*) const {
return p != nullptr;
}
// Describes the property of a value matching this matcher.
void DescribeTo(std::ostream& os) const { *os << "is not NULL"; }
// Describes the property of a value NOT matching this matcher.
void DescribeNegationTo(std::ostream* os) const { *os << "is NULL"; }
};
NotNullMatcher NotNull() {
return NotNullMatcher();
}
...
EXPECT_CALL(foo, Bar(NotNull())); // The argument must be a non-NULL pointer.
```
### Legacy Matcher Implementation
Defining matchers used to be somewhat more complicated, in which it required
several supporting classes and virtual functions. To implement a matcher for
type `T` using the legacy API you have to derive from `MatcherInterface<T>` and
call `MakeMatcher` to construct the object.
The interface looks like this:
```cpp
class MatchResultListener {
public:
...
// Streams x to the underlying ostream; does nothing if the ostream
// is NULL.
template <typename T>
MatchResultListener& operator<<(const T& x);
// Returns the underlying ostream.
std::ostream* stream();
};
template <typename T>
class MatcherInterface {
public:
virtual ~MatcherInterface();
// Returns true if and only if the matcher matches x; also explains the match
// result to 'listener'.
virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
// Describes this matcher to an ostream.
virtual void DescribeTo(std::ostream* os) const = 0;
// Describes the negation of this matcher to an ostream.
virtual void DescribeNegationTo(std::ostream* os) const;
};
```
Fortunately, most of the time you can define a polymorphic matcher easily with Fortunately, most of the time you can define a polymorphic matcher easily with
the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as the help of `MakePolymorphicMatcher()`. Here's how you can define `NotNull()` as
......
...@@ -735,31 +735,25 @@ OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) { ...@@ -735,31 +735,25 @@ OutIter TransformTupleValues(Func f, const Tuple& t, OutIter out) {
return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out); return TransformTupleValuesHelper<Tuple, Func, OutIter>::Run(f, t, out);
} }
// Implements A<T>().
template <typename T>
class AnyMatcherImpl : public MatcherInterface<const T&> {
public:
bool MatchAndExplain(const T& /* x */,
MatchResultListener* /* listener */) const override {
return true;
}
void DescribeTo(::std::ostream* os) const override { *os << "is anything"; }
void DescribeNegationTo(::std::ostream* os) const override {
// This is mostly for completeness' safe, as it's not very useful
// to write Not(A<bool>()). However we cannot completely rule out
// such a possibility, and it doesn't hurt to be prepared.
*os << "never matches";
}
};
// Implements _, a matcher that matches any value of any // Implements _, a matcher that matches any value of any
// type. This is a polymorphic matcher, so we need a template type // type. This is a polymorphic matcher, so we need a template type
// conversion operator to make it appearing as a Matcher<T> for any // conversion operator to make it appearing as a Matcher<T> for any
// type T. // type T.
class AnythingMatcher { class AnythingMatcher {
public: public:
using is_gtest_matcher = void;
template <typename T> template <typename T>
operator Matcher<T>() const { return A<T>(); } bool MatchAndExplain(const T& /* x */, std::ostream* /* listener */) const {
return true;
}
void DescribeTo(std::ostream* os) const { *os << "is anything"; }
void DescribeNegationTo(::std::ostream* os) const {
// This is mostly for completeness' sake, as it's not very useful
// to write Not(A<bool>()). However we cannot completely rule out
// such a possibility, and it doesn't hurt to be prepared.
*os << "never matches";
}
}; };
// Implements the polymorphic IsNull() matcher, which matches any raw or smart // Implements the polymorphic IsNull() matcher, which matches any raw or smart
...@@ -3443,7 +3437,9 @@ class UnorderedElementsAreMatcherImpl ...@@ -3443,7 +3437,9 @@ class UnorderedElementsAreMatcherImpl
: UnorderedElementsAreMatcherImplBase(matcher_flags) { : UnorderedElementsAreMatcherImplBase(matcher_flags) {
for (; first != last; ++first) { for (; first != last; ++first) {
matchers_.push_back(MatcherCast<const Element&>(*first)); matchers_.push_back(MatcherCast<const Element&>(*first));
matcher_describers().push_back(matchers_.back().GetDescriber()); }
for (const auto& m : matchers_) {
matcher_describers().push_back(m.GetDescriber());
} }
} }
...@@ -4068,12 +4064,14 @@ const internal::AnythingMatcher _ = {}; ...@@ -4068,12 +4064,14 @@ const internal::AnythingMatcher _ = {};
// Creates a matcher that matches any value of the given type T. // Creates a matcher that matches any value of the given type T.
template <typename T> template <typename T>
inline Matcher<T> A() { inline Matcher<T> A() {
return Matcher<T>(new internal::AnyMatcherImpl<T>()); return _;
} }
// Creates a matcher that matches any value of the given type T. // Creates a matcher that matches any value of the given type T.
template <typename T> template <typename T>
inline Matcher<T> An() { return A<T>(); } inline Matcher<T> An() {
return _;
}
template <typename T, typename M> template <typename T, typename M>
Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl( Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
......
...@@ -410,7 +410,7 @@ TEST(StringMatcherTest, ...@@ -410,7 +410,7 @@ TEST(StringMatcherTest,
// MatcherInterface* without requiring the user to explicitly // MatcherInterface* without requiring the user to explicitly
// write the type. // write the type.
TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) { TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) {
const MatcherInterface<int>* dummy_impl = nullptr; const MatcherInterface<int>* dummy_impl = new EvenMatcherImpl;
Matcher<int> m = MakeMatcher(dummy_impl); Matcher<int> m = MakeMatcher(dummy_impl);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment