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:
```cpp
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
class BarPlusBazEqMatcher : public MatcherInterface<const Foo&> {
class BarPlusBazEqMatcher {
public:
explicit BarPlusBazEqMatcher(int expected_sum)
: expected_sum_(expected_sum) {}
bool MatchAndExplain(const Foo& foo,
MatchResultListener* /* listener */) const override {
std::ostream* /* listener */) const {
return (foo.bar() + foo.baz()) == expected_sum_;
}
void DescribeTo(std::ostream* os) const override {
*os << "bar() + baz() equals " << expected_sum_;
void DescribeTo(std::ostream& os) const {
os << "bar() + baz() equals " << expected_sum_;
}
void DescribeNegationTo(std::ostream* os) const override {
*os << "bar() + baz() does not equal " << expected_sum_;
void DescribeNegationTo(std::ostream& os) const {
os << "bar() + baz() does not equal " << expected_sum_;
}
private:
const 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) { ... }
```
While it's tempting to always use the `MATCHER*` macros when defining a new
matcher, you should also consider implementing `MatcherInterface` or using
`MakePolymorphicMatcher()` instead (see the recipes that follow), especially if
you need to use the matcher a lot. While these approaches require more work,
they give you more control on the types of the value being matched and the
matcher parameters, which in general leads to better compiler error messages
that pay off in the long run. They also allow overloading matchers based on
parameter types (as opposed to just based on the number of parameters).
matcher, you should also consider implementing the matcher interface directly
instead (see the recipes that follow), especially if you need to use the matcher
a lot. While these approaches require more work, they give you more control on
the types of the value being matched and the matcher parameters, which in
general leads to better compiler error messages that pay off in the long run.
They also allow overloading matchers based on parameter types (as opposed to
just based on the number of parameters).
### Writing New Monomorphic Matchers
A matcher of argument type `T` implements `::testing::MatcherInterface<T>` and
does two things: it tests whether a value of type `T` matches the matcher, and
can describe what kind of values it matches. The latter ability is used for
A matcher of argument type `T` implements the matcher interface for `T` and does
two things: it tests whether a value of type `T` matches the matcher, and can
describe what kind of values it matches. The latter ability is used for
generating readable error messages when expectations are violated.
The interface looks like this:
A matcher of `T` must declare a typedef like:
```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();
using is_gtest_matcher = void;
```
// 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;
and supports the following operations:
// Describes this matcher to an ostream.
virtual void DescribeTo(std::ostream* os) const = 0;
```cpp
// 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.
virtual void DescribeNegationTo(std::ostream* os) const;
};
matcher.DescribeTo(os);
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,
......@@ -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:
```cpp
using ::testing::MakeMatcher;
using ::testing::Matcher;
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
class DivisibleBy7Matcher : public MatcherInterface<int> {
class DivisibleBy7Matcher {
public:
bool MatchAndExplain(int n,
MatchResultListener* /* listener */) const override {
bool MatchAndExplain(int n, std::ostream*) const {
return (n % 7) == 0;
}
void DescribeTo(std::ostream* os) const override {
void DescribeTo(std::ostream* os) const {
*os << "is divisible by 7";
}
void DescribeNegationTo(std::ostream* os) const override {
void DescribeNegationTo(std::ostream* os) const {
*os << "is not divisible by 7";
}
};
Matcher<int> DivisibleBy7() {
return MakeMatcher(new DivisibleBy7Matcher);
return DivisibleBy7Matcher();
}
...
......@@ -3624,16 +3606,15 @@ Matcher<int> DivisibleBy7() {
```
You may improve the matcher message by streaming additional information to the
`listener` argument in `MatchAndExplain()`:
`os` argument in `MatchAndExplain()`:
```cpp
class DivisibleBy7Matcher : public MatcherInterface<int> {
class DivisibleBy7Matcher {
public:
bool MatchAndExplain(int n,
MatchResultListener* listener) const override {
bool MatchAndExplain(int n, std::ostream* os) const {
const int remainder = n % 7;
if (remainder != 0) {
*listener << "the remainder is " << remainder;
if (remainder != 0 && os != nullptr) {
*os << "the remainder is " << remainder;
}
return remainder == 0;
}
......@@ -3651,13 +3632,79 @@ Expected: is divisible by 7
### Writing New Polymorphic Matchers
You've learned how to write your own matchers in the previous recipe. Just one
problem: a matcher created using `MakeMatcher()` only works for one particular
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
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
it's a bit involved.
Expanding what we learned above to *polymorphic* matchers is now just as simple
as adding templates in the right place.
```cpp
class NotNullMatcher {
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
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) {
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
// type. This is a polymorphic matcher, so we need a template type
// conversion operator to make it appearing as a Matcher<T> for any
// type T.
class AnythingMatcher {
public:
using is_gtest_matcher = void;
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
......@@ -3443,7 +3437,9 @@ class UnorderedElementsAreMatcherImpl
: UnorderedElementsAreMatcherImplBase(matcher_flags) {
for (; first != last; ++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 _ = {};
// Creates a matcher that matches any value of the given type T.
template <typename T>
inline Matcher<T> A() {
return Matcher<T>(new internal::AnyMatcherImpl<T>());
return _;
}
// Creates a matcher that matches any value of the given type T.
template <typename T>
inline Matcher<T> An() { return A<T>(); }
inline Matcher<T> An() {
return _;
}
template <typename T, typename M>
Matcher<T> internal::MatcherCastImpl<T, M>::CastImpl(
......
......@@ -410,7 +410,7 @@ TEST(StringMatcherTest,
// MatcherInterface* without requiring the user to explicitly
// write the type.
TEST(MakeMatcherTest, ConstructsMatcherFromMatcherInterface) {
const MatcherInterface<int>* dummy_impl = nullptr;
const MatcherInterface<int>* dummy_impl = new EvenMatcherImpl;
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