1 // Copyright 2020 The Dawn Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <gmock/gmock.h> 16 17 #include "common/Assert.h" 18 19 #include <memory> 20 #include <set> 21 22 namespace testing { 23 24 template <typename F> 25 class MockCallback; 26 27 // Helper class for mocking callbacks used for Dawn callbacks with |void* userdata| 28 // as the last callback argument. 29 // 30 // Example Usage: 31 // MockCallback<WGPUDeviceLostCallback> mock; 32 // 33 // void* foo = XYZ; // this is the callback userdata 34 // 35 // wgpuDeviceSetDeviceLostCallback(device, mock.Callback(), mock.MakeUserdata(foo)); 36 // EXPECT_CALL(mock, Call(_, foo)); 37 template <typename R, typename... Args> 38 class MockCallback<R (*)(Args...)> : public ::testing::MockFunction<R(Args...)> { 39 using CallbackType = R (*)(Args...); 40 41 public: 42 // Helper function makes it easier to get the callback using |foo.Callback()| 43 // unstead of MockCallback<CallbackType>::Callback. Callback()44 static CallbackType Callback() { 45 return CallUnboundCallback; 46 } 47 MakeUserdata(void * userdata)48 void* MakeUserdata(void* userdata) { 49 auto mockAndUserdata = 50 std::unique_ptr<MockAndUserdata>(new MockAndUserdata{this, userdata}); 51 52 // Add the userdata to a set of userdata for this mock. We never 53 // remove from this set even if a callback should only be called once so that 54 // repeated calls to the callback still forward the userdata correctly. 55 // Userdata will be destroyed when the mock is destroyed. 56 auto it = mUserdatas.insert(std::move(mockAndUserdata)); 57 ASSERT(it.second); 58 return it.first->get(); 59 } 60 61 private: 62 struct MockAndUserdata { 63 MockCallback* mock; 64 void* userdata; 65 }; 66 CallUnboundCallback(Args...args)67 static R CallUnboundCallback(Args... args) { 68 std::tuple<Args...> tuple = std::make_tuple(args...); 69 70 constexpr size_t ArgC = sizeof...(Args); 71 static_assert(ArgC >= 1, "Mock callback requires at least one argument (the userdata)"); 72 73 // Get the userdata. It should be the last argument. 74 auto userdata = std::get<ArgC - 1>(tuple); 75 static_assert(std::is_same<decltype(userdata), void*>::value, 76 "Last callback argument must be void* userdata"); 77 78 // Extract the mock. 79 ASSERT(userdata != nullptr); 80 auto* mockAndUserdata = reinterpret_cast<MockAndUserdata*>(userdata); 81 MockCallback* mock = mockAndUserdata->mock; 82 ASSERT(mock != nullptr); 83 84 // Replace the userdata 85 std::get<ArgC - 1>(tuple) = mockAndUserdata->userdata; 86 87 // Forward the callback to the mock. 88 return mock->CallImpl(std::make_index_sequence<ArgC>{}, std::move(tuple)); 89 } 90 91 // This helper cannot be inlined because we dependent on the templated index sequence 92 // to unpack the tuple arguments. 93 template <size_t... Is> CallImpl(const std::index_sequence<Is...> &,std::tuple<Args...> args)94 R CallImpl(const std::index_sequence<Is...>&, std::tuple<Args...> args) { 95 return this->Call(std::get<Is>(args)...); 96 } 97 98 std::set<std::unique_ptr<MockAndUserdata>> mUserdatas; 99 }; 100 101 } // namespace testing 102