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
This commit is contained in:
parent
489283524e
commit
c13c27a513
@ -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,21 +3533,147 @@ 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.
|
||||||
|
|
||||||
|
A matcher of `T` must declare a typedef like:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
using is_gtest_matcher = void;
|
||||||
|
```
|
||||||
|
|
||||||
|
and supports the following operations:
|
||||||
|
|
||||||
|
```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.
|
||||||
|
|
||||||
|
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,
|
||||||
|
you may not be happy with the way `Truly(predicate)` describes itself, or you
|
||||||
|
may want your matcher to be polymorphic as `Eq(value)` is), you can define a
|
||||||
|
matcher to do whatever you want in two steps: first implement the matcher
|
||||||
|
interface, and then define a factory function to create a matcher instance. The
|
||||||
|
second step is not strictly needed but it makes the syntax of using the matcher
|
||||||
|
nicer.
|
||||||
|
|
||||||
|
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::Matcher;
|
||||||
|
|
||||||
|
class DivisibleBy7Matcher {
|
||||||
|
public:
|
||||||
|
bool MatchAndExplain(int n, std::ostream*) const {
|
||||||
|
return (n % 7) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeTo(std::ostream* os) const {
|
||||||
|
*os << "is divisible by 7";
|
||||||
|
}
|
||||||
|
|
||||||
|
void DescribeNegationTo(std::ostream* os) const {
|
||||||
|
*os << "is not divisible by 7";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Matcher<int> DivisibleBy7() {
|
||||||
|
return DivisibleBy7Matcher();
|
||||||
|
}
|
||||||
|
|
||||||
|
...
|
||||||
|
EXPECT_CALL(foo, Bar(DivisibleBy7()));
|
||||||
|
```
|
||||||
|
|
||||||
|
You may improve the matcher message by streaming additional information to the
|
||||||
|
`os` argument in `MatchAndExplain()`:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
class DivisibleBy7Matcher {
|
||||||
|
public:
|
||||||
|
bool MatchAndExplain(int n, std::ostream* os) const {
|
||||||
|
const int remainder = n % 7;
|
||||||
|
if (remainder != 0 && os != nullptr) {
|
||||||
|
*os << "the remainder is " << remainder;
|
||||||
|
}
|
||||||
|
return remainder == 0;
|
||||||
|
}
|
||||||
|
...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, `EXPECT_THAT(x, DivisibleBy7());` may generate a message like this:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
Value of: x
|
||||||
|
Expected: is divisible by 7
|
||||||
|
Actual: 23 (the remainder is 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Writing New Polymorphic Matchers
|
||||||
|
|
||||||
|
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:
|
The interface looks like this:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
@ -3582,83 +3706,6 @@ class MatcherInterface {
|
|||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
If you need a custom matcher but `Truly()` is not a good option (for example,
|
|
||||||
you may not be happy with the way `Truly(predicate)` describes itself, or you
|
|
||||||
may want your matcher to be polymorphic as `Eq(value)` is), you can define a
|
|
||||||
matcher to do whatever you want in two steps: first implement the matcher
|
|
||||||
interface, and then define a factory function to create a matcher instance. The
|
|
||||||
second step is not strictly needed but it makes the syntax of using the matcher
|
|
||||||
nicer.
|
|
||||||
|
|
||||||
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> {
|
|
||||||
public:
|
|
||||||
bool MatchAndExplain(int n,
|
|
||||||
MatchResultListener* /* listener */) const override {
|
|
||||||
return (n % 7) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DescribeTo(std::ostream* os) const override {
|
|
||||||
*os << "is divisible by 7";
|
|
||||||
}
|
|
||||||
|
|
||||||
void DescribeNegationTo(std::ostream* os) const override {
|
|
||||||
*os << "is not divisible by 7";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Matcher<int> DivisibleBy7() {
|
|
||||||
return MakeMatcher(new DivisibleBy7Matcher);
|
|
||||||
}
|
|
||||||
|
|
||||||
...
|
|
||||||
EXPECT_CALL(foo, Bar(DivisibleBy7()));
|
|
||||||
```
|
|
||||||
|
|
||||||
You may improve the matcher message by streaming additional information to the
|
|
||||||
`listener` argument in `MatchAndExplain()`:
|
|
||||||
|
|
||||||
```cpp
|
|
||||||
class DivisibleBy7Matcher : public MatcherInterface<int> {
|
|
||||||
public:
|
|
||||||
bool MatchAndExplain(int n,
|
|
||||||
MatchResultListener* listener) const override {
|
|
||||||
const int remainder = n % 7;
|
|
||||||
if (remainder != 0) {
|
|
||||||
*listener << "the remainder is " << remainder;
|
|
||||||
}
|
|
||||||
return remainder == 0;
|
|
||||||
}
|
|
||||||
...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
Then, `EXPECT_THAT(x, DivisibleBy7());` may generate a message like this:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
Value of: x
|
|
||||||
Expected: is divisible by 7
|
|
||||||
Actual: 23 (the remainder is 2)
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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.
|
|
||||||
|
|
||||||
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
|
||||||
an example:
|
an example:
|
||||||
|
@ -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
|
|||||||
: 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 _ = {};
|
|||||||
// 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,
|
|||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
#ifndef GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
#ifndef GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||||
#define GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
#define GTEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -63,20 +64,15 @@ GTEST_DISABLE_MSC_WARNINGS_PUSH_(
|
|||||||
namespace testing {
|
namespace testing {
|
||||||
|
|
||||||
// To implement a matcher Foo for type T, define:
|
// To implement a matcher Foo for type T, define:
|
||||||
// 1. a class FooMatcherImpl that implements the
|
// 1. a class FooMatcherMatcher that implements the matcher interface:
|
||||||
// MatcherInterface<T> interface, and
|
// using is_gtest_matcher = void;
|
||||||
|
// bool MatchAndExplain(const T&, std::ostream*);
|
||||||
|
// void DescribeTo(std::ostream*);
|
||||||
|
// void DescribeNegationTo(std::ostream*);
|
||||||
|
//
|
||||||
// 2. a factory function that creates a Matcher<T> object from a
|
// 2. a factory function that creates a Matcher<T> object from a
|
||||||
// FooMatcherImpl*.
|
// FooMatcherMatcher.
|
||||||
//
|
|
||||||
// The two-level delegation design makes it possible to allow a user
|
|
||||||
// to write "v" instead of "Eq(v)" where a Matcher is expected, which
|
|
||||||
// is impossible if we pass matchers by pointers. It also eases
|
|
||||||
// ownership management as Matcher objects can now be copied like
|
|
||||||
// plain values.
|
|
||||||
|
|
||||||
// MatchResultListener is an abstract class. Its << operator can be
|
|
||||||
// used by a matcher to explain why a value matches or doesn't match.
|
|
||||||
//
|
|
||||||
class MatchResultListener {
|
class MatchResultListener {
|
||||||
public:
|
public:
|
||||||
// Creates a listener object with the given underlying ostream. The
|
// Creates a listener object with the given underlying ostream. The
|
||||||
@ -181,31 +177,6 @@ class MatcherInterface : public MatcherDescriberInterface {
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
// Converts a MatcherInterface<T> to a MatcherInterface<const T&>.
|
|
||||||
template <typename T>
|
|
||||||
class MatcherInterfaceAdapter : public MatcherInterface<const T&> {
|
|
||||||
public:
|
|
||||||
explicit MatcherInterfaceAdapter(const MatcherInterface<T>* impl)
|
|
||||||
: impl_(impl) {}
|
|
||||||
~MatcherInterfaceAdapter() override { delete impl_; }
|
|
||||||
|
|
||||||
void DescribeTo(::std::ostream* os) const override { impl_->DescribeTo(os); }
|
|
||||||
|
|
||||||
void DescribeNegationTo(::std::ostream* os) const override {
|
|
||||||
impl_->DescribeNegationTo(os);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MatchAndExplain(const T& x,
|
|
||||||
MatchResultListener* listener) const override {
|
|
||||||
return impl_->MatchAndExplain(x, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
const MatcherInterface<T>* const impl_;
|
|
||||||
|
|
||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(MatcherInterfaceAdapter);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AnyEq {
|
struct AnyEq {
|
||||||
template <typename A, typename B>
|
template <typename A, typename B>
|
||||||
bool operator()(const A& a, const B& b) const { return a == b; }
|
bool operator()(const A& a, const B& b) const { return a == b; }
|
||||||
@ -252,16 +223,35 @@ class StreamMatchResultListener : public MatchResultListener {
|
|||||||
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
|
GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamMatchResultListener);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SharedPayloadBase {
|
||||||
|
std::atomic<int> ref{1};
|
||||||
|
void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
|
||||||
|
bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct SharedPayload : SharedPayloadBase {
|
||||||
|
explicit SharedPayload(const T& v) : value(v) {}
|
||||||
|
explicit SharedPayload(T&& v) : value(std::move(v)) {}
|
||||||
|
|
||||||
|
static void Destroy(SharedPayloadBase* shared) {
|
||||||
|
delete static_cast<SharedPayload*>(shared);
|
||||||
|
}
|
||||||
|
|
||||||
|
T value;
|
||||||
|
};
|
||||||
|
|
||||||
// An internal class for implementing Matcher<T>, which will derive
|
// An internal class for implementing Matcher<T>, which will derive
|
||||||
// from it. We put functionalities common to all Matcher<T>
|
// from it. We put functionalities common to all Matcher<T>
|
||||||
// specializations here to avoid code duplication.
|
// specializations here to avoid code duplication.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class MatcherBase {
|
class MatcherBase : private MatcherDescriberInterface {
|
||||||
public:
|
public:
|
||||||
// Returns true if and only if the matcher matches x; also explains the
|
// Returns true if and only if the matcher matches x; also explains the
|
||||||
// match result to 'listener'.
|
// match result to 'listener'.
|
||||||
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
|
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
|
||||||
return impl_->MatchAndExplain(x, listener);
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
return vtable_->match_and_explain(*this, x, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns true if and only if this matcher matches x.
|
// Returns true if and only if this matcher matches x.
|
||||||
@ -271,11 +261,15 @@ class MatcherBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Describes this matcher to an ostream.
|
// Describes this matcher to an ostream.
|
||||||
void DescribeTo(::std::ostream* os) const { impl_->DescribeTo(os); }
|
void DescribeTo(::std::ostream* os) const final {
|
||||||
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
vtable_->describe(*this, os, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Describes the negation of this matcher to an ostream.
|
// Describes the negation of this matcher to an ostream.
|
||||||
void DescribeNegationTo(::std::ostream* os) const {
|
void DescribeNegationTo(::std::ostream* os) const final {
|
||||||
impl_->DescribeNegationTo(os);
|
GTEST_CHECK_(vtable_ != nullptr);
|
||||||
|
vtable_->describe(*this, os, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Explains why x matches, or doesn't match, the matcher.
|
// Explains why x matches, or doesn't match, the matcher.
|
||||||
@ -288,31 +282,190 @@ class MatcherBase {
|
|||||||
// of the describer, which is only guaranteed to be alive when
|
// of the describer, which is only guaranteed to be alive when
|
||||||
// this matcher object is alive.
|
// this matcher object is alive.
|
||||||
const MatcherDescriberInterface* GetDescriber() const {
|
const MatcherDescriberInterface* GetDescriber() const {
|
||||||
return impl_.get();
|
if (vtable_ == nullptr) return nullptr;
|
||||||
|
return vtable_->get_describer(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
MatcherBase() {}
|
MatcherBase() : vtable_(nullptr) {}
|
||||||
|
|
||||||
// Constructs a matcher from its implementation.
|
// Constructs a matcher from its implementation.
|
||||||
explicit MatcherBase(const MatcherInterface<const T&>* impl) : impl_(impl) {}
|
|
||||||
|
|
||||||
template <typename U>
|
template <typename U>
|
||||||
explicit MatcherBase(
|
explicit MatcherBase(const MatcherInterface<U>* impl) {
|
||||||
const MatcherInterface<U>* impl,
|
Init(impl);
|
||||||
typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
|
}
|
||||||
nullptr)
|
|
||||||
: impl_(new internal::MatcherInterfaceAdapter<U>(impl)) {}
|
|
||||||
|
|
||||||
MatcherBase(const MatcherBase&) = default;
|
template <typename M, typename = typename std::remove_reference<
|
||||||
MatcherBase& operator=(const MatcherBase&) = default;
|
M>::type::is_gtest_matcher>
|
||||||
MatcherBase(MatcherBase&&) = default;
|
MatcherBase(M&& m) { // NOLINT
|
||||||
MatcherBase& operator=(MatcherBase&&) = default;
|
Init(std::forward<M>(m));
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~MatcherBase() {}
|
MatcherBase(const MatcherBase& other)
|
||||||
|
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||||
|
if (IsShared()) buffer_.shared->Ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase& operator=(const MatcherBase& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
Destroy();
|
||||||
|
vtable_ = other.vtable_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
if (IsShared()) buffer_.shared->Ref();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase(MatcherBase&& other)
|
||||||
|
: vtable_(other.vtable_), buffer_(other.buffer_) {
|
||||||
|
other.vtable_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MatcherBase& operator=(MatcherBase&& other) {
|
||||||
|
if (this == &other) return *this;
|
||||||
|
Destroy();
|
||||||
|
vtable_ = other.vtable_;
|
||||||
|
buffer_ = other.buffer_;
|
||||||
|
other.vtable_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~MatcherBase() override { Destroy(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<const MatcherInterface<const T&>> impl_;
|
struct VTable {
|
||||||
|
bool (*match_and_explain)(const MatcherBase&, const T&,
|
||||||
|
MatchResultListener*);
|
||||||
|
void (*describe)(const MatcherBase&, std::ostream*, bool negation);
|
||||||
|
// Returns the captured object if it implements the interface, otherwise
|
||||||
|
// returns the MatcherBase itself.
|
||||||
|
const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
|
||||||
|
// Called on shared instances when the reference count reaches 0.
|
||||||
|
void (*shared_destroy)(SharedPayloadBase*);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool IsShared() const {
|
||||||
|
return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the implementation uses a listener, call that.
|
||||||
|
template <typename P>
|
||||||
|
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
|
||||||
|
MatchResultListener* listener)
|
||||||
|
-> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
|
||||||
|
return P::Get(m).MatchAndExplain(value, listener->stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
|
||||||
|
MatchResultListener* listener)
|
||||||
|
-> decltype(P::Get(m).MatchAndExplain(value, listener)) {
|
||||||
|
return P::Get(m).MatchAndExplain(value, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static void DescribeImpl(const MatcherBase& m, std::ostream* os,
|
||||||
|
bool negation) {
|
||||||
|
if (negation) {
|
||||||
|
P::Get(m).DescribeNegationTo(os);
|
||||||
|
} else {
|
||||||
|
P::Get(m).DescribeTo(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
static const MatcherDescriberInterface* GetDescriberImpl(
|
||||||
|
const MatcherBase& m) {
|
||||||
|
// If the impl is a MatcherDescriberInterface, then return it.
|
||||||
|
// Otherwise use MatcherBase itself.
|
||||||
|
// This allows us to implement the GetDescriber() function without support
|
||||||
|
// from the impl, but some users really want to get their impl back when
|
||||||
|
// they call GetDescriber().
|
||||||
|
// We use std::get on a tuple as a workaround of not having `if constexpr`.
|
||||||
|
return std::get<(
|
||||||
|
std::is_convertible<decltype(&P::Get(m)),
|
||||||
|
const MatcherDescriberInterface*>::value
|
||||||
|
? 1
|
||||||
|
: 0)>(std::make_tuple(&m, &P::Get(m)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
const VTable* GetVTable() {
|
||||||
|
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
|
||||||
|
&DescribeImpl<P>, &GetDescriberImpl<P>,
|
||||||
|
P::shared_destroy};
|
||||||
|
return &kVTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
union Buffer {
|
||||||
|
// Add some types to give Buffer some common alignment/size use cases.
|
||||||
|
void* ptr;
|
||||||
|
double d;
|
||||||
|
int64_t i;
|
||||||
|
// And add one for the out-of-line cases.
|
||||||
|
SharedPayloadBase* shared;
|
||||||
|
};
|
||||||
|
|
||||||
|
void Destroy() {
|
||||||
|
if (IsShared() && buffer_.shared->Unref()) {
|
||||||
|
vtable_->shared_destroy(buffer_.shared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
static constexpr bool IsInlined() {
|
||||||
|
return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
|
||||||
|
std::is_trivially_copy_constructible<M>::value &&
|
||||||
|
std::is_trivially_destructible<M>::value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename M, bool = IsInlined<M>()>
|
||||||
|
struct ValuePolicy {
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
return reinterpret_cast<const M&>(m.buffer_);
|
||||||
|
}
|
||||||
|
static void Init(MatcherBase& m, M impl) {
|
||||||
|
::new (static_cast<void*>(&m.buffer_)) M(impl);
|
||||||
|
}
|
||||||
|
static constexpr auto shared_destroy = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
struct ValuePolicy<M, false> {
|
||||||
|
using Shared = SharedPayload<M>;
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
return static_cast<Shared*>(m.buffer_.shared)->value;
|
||||||
|
}
|
||||||
|
template <typename Arg>
|
||||||
|
static void Init(MatcherBase& m, Arg&& arg) {
|
||||||
|
m.buffer_.shared = new Shared(std::forward<Arg>(arg));
|
||||||
|
}
|
||||||
|
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename U, bool B>
|
||||||
|
struct ValuePolicy<const MatcherInterface<U>*, B> {
|
||||||
|
using M = const MatcherInterface<U>;
|
||||||
|
using Shared = SharedPayload<std::unique_ptr<M>>;
|
||||||
|
static const M& Get(const MatcherBase& m) {
|
||||||
|
return *static_cast<Shared*>(m.buffer_.shared)->value;
|
||||||
|
}
|
||||||
|
static void Init(MatcherBase& m, M* impl) {
|
||||||
|
m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto shared_destroy = &Shared::Destroy;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename M>
|
||||||
|
void Init(M&& m) {
|
||||||
|
using MM = typename std::decay<M>::type;
|
||||||
|
using Policy = ValuePolicy<MM>;
|
||||||
|
vtable_ = GetVTable<Policy>();
|
||||||
|
Policy::Init(*this, std::forward<M>(m));
|
||||||
|
}
|
||||||
|
|
||||||
|
const VTable* vtable_;
|
||||||
|
Buffer buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
@ -340,6 +493,10 @@ class Matcher : public internal::MatcherBase<T> {
|
|||||||
nullptr)
|
nullptr)
|
||||||
: internal::MatcherBase<T>(impl) {}
|
: internal::MatcherBase<T>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT
|
||||||
|
|
||||||
// Implicit constructor here allows people to write
|
// Implicit constructor here allows people to write
|
||||||
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
|
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
|
||||||
Matcher(T value); // NOLINT
|
Matcher(T value); // NOLINT
|
||||||
@ -357,6 +514,11 @@ class GTEST_API_ Matcher<const std::string&>
|
|||||||
explicit Matcher(const MatcherInterface<const std::string&>* impl)
|
explicit Matcher(const MatcherInterface<const std::string&>* impl)
|
||||||
: internal::MatcherBase<const std::string&>(impl) {}
|
: internal::MatcherBase<const std::string&>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
// str is a std::string object.
|
// str is a std::string object.
|
||||||
Matcher(const std::string& s); // NOLINT
|
Matcher(const std::string& s); // NOLINT
|
||||||
@ -376,6 +538,11 @@ class GTEST_API_ Matcher<std::string>
|
|||||||
explicit Matcher(const MatcherInterface<std::string>* impl)
|
explicit Matcher(const MatcherInterface<std::string>* impl)
|
||||||
: internal::MatcherBase<std::string>(impl) {}
|
: internal::MatcherBase<std::string>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<std::string>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
// str is a string object.
|
// str is a string object.
|
||||||
Matcher(const std::string& s); // NOLINT
|
Matcher(const std::string& s); // NOLINT
|
||||||
@ -397,6 +564,12 @@ class GTEST_API_ Matcher<const internal::StringView&>
|
|||||||
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
|
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
|
||||||
: internal::MatcherBase<const internal::StringView&>(impl) {}
|
: internal::MatcherBase<const internal::StringView&>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
|
||||||
|
}
|
||||||
|
|
||||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
// str is a std::string object.
|
// str is a std::string object.
|
||||||
Matcher(const std::string& s); // NOLINT
|
Matcher(const std::string& s); // NOLINT
|
||||||
@ -419,6 +592,11 @@ class GTEST_API_ Matcher<internal::StringView>
|
|||||||
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
|
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
|
||||||
: internal::MatcherBase<internal::StringView>(impl) {}
|
: internal::MatcherBase<internal::StringView>(impl) {}
|
||||||
|
|
||||||
|
template <typename M, typename = typename std::remove_reference<
|
||||||
|
M>::type::is_gtest_matcher>
|
||||||
|
Matcher(M&& m) // NOLINT
|
||||||
|
: internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
|
||||||
|
|
||||||
// Allows the user to write str instead of Eq(str) sometimes, where
|
// Allows the user to write str instead of Eq(str) sometimes, where
|
||||||
// str is a std::string object.
|
// str is a std::string object.
|
||||||
Matcher(const std::string& s); // NOLINT
|
Matcher(const std::string& s); // NOLINT
|
||||||
@ -529,37 +707,32 @@ template <typename D, typename Rhs, typename Op>
|
|||||||
class ComparisonBase {
|
class ComparisonBase {
|
||||||
public:
|
public:
|
||||||
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
|
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
|
||||||
|
|
||||||
|
using is_gtest_matcher = void;
|
||||||
|
|
||||||
template <typename Lhs>
|
template <typename Lhs>
|
||||||
operator Matcher<Lhs>() const {
|
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
|
||||||
return Matcher<Lhs>(new Impl<const Lhs&>(rhs_));
|
return Op()(lhs, Unwrap(rhs_));
|
||||||
|
}
|
||||||
|
void DescribeTo(std::ostream* os) const {
|
||||||
|
*os << D::Desc() << " ";
|
||||||
|
UniversalPrint(Unwrap(rhs_), os);
|
||||||
|
}
|
||||||
|
void DescribeNegationTo(std::ostream* os) const {
|
||||||
|
*os << D::NegatedDesc() << " ";
|
||||||
|
UniversalPrint(Unwrap(rhs_), os);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static const T& Unwrap(const T& v) { return v; }
|
static const T& Unwrap(const T& v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static const T& Unwrap(std::reference_wrapper<T> v) { return v; }
|
static const T& Unwrap(std::reference_wrapper<T> v) {
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Lhs, typename = Rhs>
|
|
||||||
class Impl : public MatcherInterface<Lhs> {
|
|
||||||
public:
|
|
||||||
explicit Impl(const Rhs& rhs) : rhs_(rhs) {}
|
|
||||||
bool MatchAndExplain(Lhs lhs,
|
|
||||||
MatchResultListener* /* listener */) const override {
|
|
||||||
return Op()(lhs, Unwrap(rhs_));
|
|
||||||
}
|
|
||||||
void DescribeTo(::std::ostream* os) const override {
|
|
||||||
*os << D::Desc() << " ";
|
|
||||||
UniversalPrint(Unwrap(rhs_), os);
|
|
||||||
}
|
|
||||||
void DescribeNegationTo(::std::ostream* os) const override {
|
|
||||||
*os << D::NegatedDesc() << " ";
|
|
||||||
UniversalPrint(Unwrap(rhs_), os);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Rhs rhs_;
|
|
||||||
};
|
|
||||||
Rhs rhs_;
|
Rhs rhs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user