diff options
author | Abseil Team <absl-team@google.com> | 2021-01-07 17:43:27 (GMT) |
---|---|---|
committer | Derek Mauro <dmauro@google.com> | 2021-01-14 01:59:20 (GMT) |
commit | c13c27a513ecd1cbf5700a45fe590e85e8ae6770 (patch) | |
tree | a96ac4bccbedcbe8de2effe77883af0296dc3629 /docs | |
parent | 489283524e3726b7adb9692763c2fb61b235d41a (diff) | |
download | googletest-c13c27a513ecd1cbf5700a45fe590e85e8ae6770.zip googletest-c13c27a513ecd1cbf5700a45fe590e85e8ae6770.tar.gz googletest-c13c27a513ecd1cbf5700a45fe590e85e8ae6770.tar.bz2 |
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.md | 179 |
1 files changed, 113 insertions, 66 deletions
diff --git a/docs/gmock_cook_book.md b/docs/gmock_cook_book.md index c88ded2..b7d9f44 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 |