• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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