| // Copyright 2019 The Fuchsia Authors. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef LIB_MOCK_FUNCTION_MOCK_FUNCTION_H_ |
| #define LIB_MOCK_FUNCTION_MOCK_FUNCTION_H_ |
| |
| #include <memory> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include <gtest/gtest.h> |
| |
| namespace mock_function { |
| |
| // This class mocks a single function. The Expect*() functions are used by the test to set |
| // expectations, and Call() is used by the code under test. There are three variants: |
| // |
| // * ExpectCall(return_value, arg1, arg2, ...) sets the expectation that the call will be made with |
| // arguments `arg1`, `arg2`, etc., each compared using operator==. `return_value` will be returned |
| // unconditionally, or is omitted if the function returns void. |
| // * ExpectCallWithMatcher(matcher) uses a `matcher` validate the arguments. The matcher will be |
| // called with the arguments to the mocked function call, and the call will return the matcher's |
| // return value. |
| // * ExpectNoCall() expects that the function will not be called. |
| // |
| // Example: |
| // |
| // class SomeClassTest : SomeClass { |
| // public: |
| // zx_status_t CallsSomeMethod(); |
| // |
| // mock_function::MockFunction<zx_status_t, uint32_t, uint32_t>& mock_SomeMethod() { |
| // return mock_some_method_; |
| // } |
| // |
| // private: |
| // zx_status_t SomeMethod(uint32_t a, uint32_t b) override { |
| // return mock_some_method_.Call(a, b); |
| // } |
| // |
| // mock_function::MockFunction<zx_status_t, uint32_t, uint32_t> mock_some_method_; |
| // }; |
| // |
| // TEST(SomeDriver, SomeTest) { |
| // SomeClassTest test; |
| // test.mock_SomeMethod().ExpectCall(ZX_OK, 100, 30); |
| // test.mock_SomeMethod().ExpectCallWithMatcher([](uint32_t a, uint32_t b) { |
| // EXPECT_EQ(200, a); |
| // EXPECT_EQ(60, b); |
| // return ZX_OK; |
| // }); |
| // |
| // EXPECT_OK(test.CallsSomeMethod(100, 30)); |
| // EXPECT_OK(test.CallsSomeMethod(200, 60)); |
| // |
| // test.mock_SomeMethod().VerifyAndClear(); |
| // } |
| |
| template <typename R, typename... Ts> |
| class MockFunction { |
| public: |
| template <typename... As> |
| MockFunction& ExpectCall(R retval, As&&... args) { |
| static_assert(sizeof...(As) == sizeof...(Ts), "wrong number of arguments to ExpectCall"); |
| using ArgsTuple = std::tuple<typename std::decay<Ts>::type...>; |
| expectations_.emplace_back(MakeExpectation( |
| [return_value = std::move(retval), |
| expected_args = ArgsTuple(std::forward<As>(args)...)](const Ts&... actual_args) mutable { |
| EXPECT_EQ(expected_args, std::tie(actual_args...)); |
| return std::move(return_value); |
| })); |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| template <typename M> |
| MockFunction& ExpectCallWithMatcher(M matcher) { |
| expectations_.emplace_back(MakeExpectation<M>(std::move(matcher))); |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| MockFunction& ExpectNoCall() { |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| R Call(Ts... args) { |
| std::unique_ptr<Expectation> exp; |
| CallHelper(&exp); |
| return exp->Match(std::move(args)...); |
| } |
| |
| bool HasExpectations() const { return has_expectations_; } |
| |
| void VerifyAndClear() { |
| EXPECT_EQ(expectation_index_, expectations_.size()); |
| expectations_.clear(); |
| expectation_index_ = 0; |
| } |
| |
| private: |
| struct Expectation { |
| virtual ~Expectation() = default; |
| virtual R Match(Ts&&... actual_args) = 0; |
| }; |
| |
| template <typename M> |
| std::unique_ptr<Expectation> MakeExpectation(M matcher) { |
| struct ExpectationWithMatcher : public Expectation { |
| explicit ExpectationWithMatcher(M matcher) : matcher(std::move(matcher)) {} |
| R Match(Ts&&... actual_args) override { return matcher(std::forward<Ts>(actual_args)...); } |
| M matcher; |
| }; |
| |
| return std::make_unique<ExpectationWithMatcher>(std::move(matcher)); |
| } |
| |
| void CallHelper(std::unique_ptr<Expectation>* exp) { |
| const bool enough_expectations_were_set = expectation_index_ < expectations_.size(); |
| ZX_ASSERT(enough_expectations_were_set); |
| *exp = std::move(expectations_[expectation_index_++]); |
| } |
| |
| bool has_expectations_ = false; |
| std::vector<std::unique_ptr<Expectation>> expectations_; |
| size_t expectation_index_ = 0; |
| }; |
| |
| template <typename... Ts> |
| class MockFunction<void, Ts...> { |
| public: |
| template <typename... As> |
| MockFunction& ExpectCall(As&&... args) { |
| static_assert(sizeof...(As) == sizeof...(Ts), "wrong number of arguments to ExpectCall"); |
| using ArgsTuple = std::tuple<typename std::decay<Ts>::type...>; |
| expectations_.emplace_back(MakeExpectation( |
| [expected_args = ArgsTuple(std::forward<As>(args)...)](const Ts&... actual_args) { |
| EXPECT_EQ(expected_args, std::tie(actual_args...)); |
| })); |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| template <typename M> |
| MockFunction& ExpectCallWithMatcher(M matcher) { |
| expectations_.emplace_back(MakeExpectation<M>(std::move(matcher))); |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| MockFunction& ExpectNoCall() { |
| has_expectations_ = true; |
| return *this; |
| } |
| |
| void Call(Ts... args) { |
| std::unique_ptr<Expectation> exp; |
| CallHelper(&exp); |
| exp->Match(std::move(args)...); |
| } |
| |
| bool HasExpectations() const { return has_expectations_; } |
| |
| void VerifyAndClear() { |
| ASSERT_EQ(expectation_index_, expectations_.size()); |
| expectations_.clear(); |
| expectation_index_ = 0; |
| } |
| |
| private: |
| struct Expectation { |
| virtual ~Expectation() = default; |
| virtual void Match(Ts&&... actual_args) = 0; |
| }; |
| |
| template <typename M> |
| std::unique_ptr<Expectation> MakeExpectation(M matcher) { |
| struct ExpectationWithMatcher : public Expectation { |
| explicit ExpectationWithMatcher(M matcher) : matcher(std::move(matcher)) {} |
| void Match(Ts&&... actual_args) override { matcher(std::forward<Ts>(actual_args)...); } |
| M matcher; |
| }; |
| |
| return std::make_unique<ExpectationWithMatcher>(std::move(matcher)); |
| } |
| |
| void CallHelper(std::unique_ptr<Expectation>* exp) { |
| const bool enough_expectations_were_set = expectation_index_ < expectations_.size(); |
| ZX_ASSERT(enough_expectations_were_set); |
| *exp = std::move(expectations_[expectation_index_++]); |
| } |
| |
| bool has_expectations_ = false; |
| std::vector<std::unique_ptr<Expectation>> expectations_; |
| size_t expectation_index_ = 0; |
| }; |
| |
| } // namespace mock_function |
| |
| #endif // LIB_MOCK_FUNCTION_MOCK_FUNCTION_H_ |