summaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorAbseil Team <absl-team@google.com>2021-01-07 12:43:27 -0500
committerDerek Mauro <dmauro@google.com>2021-01-13 20:59:20 -0500
commitc13c27a513ecd1cbf5700a45fe590e85e8ae6770 (patch)
treea96ac4bccbedcbe8de2effe77883af0296dc3629 /docs
parent489283524e3726b7adb9692763c2fb61b235d41a (diff)
downloadgoogletest-c13c27a513ecd1cbf5700a45fe590e85e8ae6770.tar.gz
googletest-c13c27a513ecd1cbf5700a45fe590e85e8ae6770.zip
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
Diffstat (limited to 'docs')
-rw-r--r--docs/gmock_cook_book.md179
1 files changed, 113 insertions, 66 deletions
diff --git a/docs/gmock_cook_book.md b/docs/gmock_cook_book.md
index c88ded27..b7d9f440 100644
--- a/docs/gmock_cook_book.md
+++ b/docs/gmock_cook_book.md
@@ -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
OpenPOWER on IntegriCloud