1 // Copyright 2019 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 "tests/unittests/wire/WireTest.h"
16
17 #include "dawn_wire/WireClient.h"
18
19 using namespace testing;
20 using namespace dawn_wire;
21
22 namespace {
23
24 // Mock classes to add expectations on the wire calling callbacks
25 class MockDeviceErrorCallback {
26 public:
27 MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata));
28 };
29
30 std::unique_ptr<StrictMock<MockDeviceErrorCallback>> mockDeviceErrorCallback;
ToMockDeviceErrorCallback(WGPUErrorType type,const char * message,void * userdata)31 void ToMockDeviceErrorCallback(WGPUErrorType type, const char* message, void* userdata) {
32 mockDeviceErrorCallback->Call(type, message, userdata);
33 }
34
35 class MockDevicePopErrorScopeCallback {
36 public:
37 MOCK_METHOD(void, Call, (WGPUErrorType type, const char* message, void* userdata));
38 };
39
40 std::unique_ptr<StrictMock<MockDevicePopErrorScopeCallback>> mockDevicePopErrorScopeCallback;
ToMockDevicePopErrorScopeCallback(WGPUErrorType type,const char * message,void * userdata)41 void ToMockDevicePopErrorScopeCallback(WGPUErrorType type,
42 const char* message,
43 void* userdata) {
44 mockDevicePopErrorScopeCallback->Call(type, message, userdata);
45 }
46
47 class MockDeviceLoggingCallback {
48 public:
49 MOCK_METHOD(void, Call, (WGPULoggingType type, const char* message, void* userdata));
50 };
51
52 std::unique_ptr<StrictMock<MockDeviceLoggingCallback>> mockDeviceLoggingCallback;
ToMockDeviceLoggingCallback(WGPULoggingType type,const char * message,void * userdata)53 void ToMockDeviceLoggingCallback(WGPULoggingType type, const char* message, void* userdata) {
54 mockDeviceLoggingCallback->Call(type, message, userdata);
55 }
56
57 class MockDeviceLostCallback {
58 public:
59 MOCK_METHOD(void, Call, (WGPUDeviceLostReason reason, const char* message, void* userdata));
60 };
61
62 std::unique_ptr<StrictMock<MockDeviceLostCallback>> mockDeviceLostCallback;
ToMockDeviceLostCallback(WGPUDeviceLostReason reason,const char * message,void * userdata)63 void ToMockDeviceLostCallback(WGPUDeviceLostReason reason,
64 const char* message,
65 void* userdata) {
66 mockDeviceLostCallback->Call(reason, message, userdata);
67 }
68
69 } // anonymous namespace
70
71 class WireErrorCallbackTests : public WireTest {
72 public:
WireErrorCallbackTests()73 WireErrorCallbackTests() {
74 }
75 ~WireErrorCallbackTests() override = default;
76
SetUp()77 void SetUp() override {
78 WireTest::SetUp();
79
80 mockDeviceErrorCallback = std::make_unique<StrictMock<MockDeviceErrorCallback>>();
81 mockDeviceLoggingCallback = std::make_unique<StrictMock<MockDeviceLoggingCallback>>();
82 mockDevicePopErrorScopeCallback =
83 std::make_unique<StrictMock<MockDevicePopErrorScopeCallback>>();
84 mockDeviceLostCallback = std::make_unique<StrictMock<MockDeviceLostCallback>>();
85 }
86
TearDown()87 void TearDown() override {
88 WireTest::TearDown();
89
90 mockDeviceErrorCallback = nullptr;
91 mockDeviceLoggingCallback = nullptr;
92 mockDevicePopErrorScopeCallback = nullptr;
93 mockDeviceLostCallback = nullptr;
94 }
95
FlushServer()96 void FlushServer() {
97 WireTest::FlushServer();
98
99 Mock::VerifyAndClearExpectations(&mockDeviceErrorCallback);
100 Mock::VerifyAndClearExpectations(&mockDevicePopErrorScopeCallback);
101 }
102 };
103
104 // Test the return wire for device error callbacks
TEST_F(WireErrorCallbackTests,DeviceErrorCallback)105 TEST_F(WireErrorCallbackTests, DeviceErrorCallback) {
106 wgpuDeviceSetUncapturedErrorCallback(device, ToMockDeviceErrorCallback, this);
107
108 // Setting the error callback should stay on the client side and do nothing
109 FlushClient();
110
111 // Calling the callback on the server side will result in the callback being called on the
112 // client side
113 api.CallDeviceSetUncapturedErrorCallbackCallback(apiDevice, WGPUErrorType_Validation,
114 "Some error message");
115
116 EXPECT_CALL(*mockDeviceErrorCallback,
117 Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
118 .Times(1);
119
120 FlushServer();
121 }
122
123 // Test the return wire for device user warning callbacks
TEST_F(WireErrorCallbackTests,DeviceLoggingCallback)124 TEST_F(WireErrorCallbackTests, DeviceLoggingCallback) {
125 wgpuDeviceSetLoggingCallback(device, ToMockDeviceLoggingCallback, this);
126
127 // Setting the injected warning callback should stay on the client side and do nothing
128 FlushClient();
129
130 // Calling the callback on the server side will result in the callback being called on the
131 // client side
132 api.CallDeviceSetLoggingCallbackCallback(apiDevice, WGPULoggingType_Info, "Some message");
133
134 EXPECT_CALL(*mockDeviceLoggingCallback, Call(WGPULoggingType_Info, StrEq("Some message"), this))
135 .Times(1);
136
137 FlushServer();
138 }
139
140 // Test the return wire for error scopes.
TEST_F(WireErrorCallbackTests,PushPopErrorScopeCallback)141 TEST_F(WireErrorCallbackTests, PushPopErrorScopeCallback) {
142 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
143 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
144
145 FlushClient();
146
147 wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
148
149 WGPUErrorCallback callback;
150 void* userdata;
151 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _))
152 .WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true)));
153
154 FlushClient();
155
156 callback(WGPUErrorType_Validation, "Some error message", userdata);
157 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
158 Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
159 .Times(1);
160
161 FlushServer();
162 }
163
164 // Test the return wire for error scopes when callbacks return in a various orders.
TEST_F(WireErrorCallbackTests,PopErrorScopeCallbackOrdering)165 TEST_F(WireErrorCallbackTests, PopErrorScopeCallbackOrdering) {
166 // Two error scopes are popped, and the first one returns first.
167 {
168 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
169 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
170 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(2);
171
172 FlushClient();
173
174 wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
175 wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1);
176
177 WGPUErrorCallback callback1;
178 WGPUErrorCallback callback2;
179 void* userdata1;
180 void* userdata2;
181 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _))
182 .WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true)))
183 .WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true)));
184
185 FlushClient();
186
187 callback1(WGPUErrorType_Validation, "First error message", userdata1);
188 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
189 Call(WGPUErrorType_Validation, StrEq("First error message"), this))
190 .Times(1);
191 FlushServer();
192
193 callback2(WGPUErrorType_Validation, "Second error message", userdata2);
194 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
195 Call(WGPUErrorType_Validation, StrEq("Second error message"), this + 1))
196 .Times(1);
197 FlushServer();
198 }
199
200 // Two error scopes are popped, and the second one returns first.
201 {
202 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
203 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
204 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(2);
205
206 FlushClient();
207
208 wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this);
209 wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1);
210
211 WGPUErrorCallback callback1;
212 WGPUErrorCallback callback2;
213 void* userdata1;
214 void* userdata2;
215 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _))
216 .WillOnce(DoAll(SaveArg<1>(&callback1), SaveArg<2>(&userdata1), Return(true)))
217 .WillOnce(DoAll(SaveArg<1>(&callback2), SaveArg<2>(&userdata2), Return(true)));
218
219 FlushClient();
220
221 callback2(WGPUErrorType_Validation, "Second error message", userdata2);
222 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
223 Call(WGPUErrorType_Validation, StrEq("Second error message"), this + 1))
224 .Times(1);
225 FlushServer();
226
227 callback1(WGPUErrorType_Validation, "First error message", userdata1);
228 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
229 Call(WGPUErrorType_Validation, StrEq("First error message"), this))
230 .Times(1);
231 FlushServer();
232 }
233 }
234
235 // Test the return wire for error scopes in flight when the device is destroyed.
TEST_F(WireErrorCallbackTests,PopErrorScopeDeviceDestroyed)236 TEST_F(WireErrorCallbackTests, PopErrorScopeDeviceDestroyed) {
237 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
238 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
239
240 FlushClient();
241
242 EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
243
244 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _)).WillOnce(Return(true));
245 FlushClient();
246
247 // Incomplete callback called in Device destructor.
248 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
249 Call(WGPUErrorType_Unknown, ValidStringMessage(), this))
250 .Times(1);
251 }
252
253 // Test that registering a callback then wire disconnect calls the callback with
254 // DeviceLost.
TEST_F(WireErrorCallbackTests,PopErrorScopeThenDisconnect)255 TEST_F(WireErrorCallbackTests, PopErrorScopeThenDisconnect) {
256 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
257 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
258
259 EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
260 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _)).WillOnce(Return(true));
261
262 FlushClient();
263
264 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
265 Call(WGPUErrorType_DeviceLost, ValidStringMessage(), this))
266 .Times(1);
267 GetWireClient()->Disconnect();
268 }
269
270 // Test that registering a callback after wire disconnect calls the callback with
271 // DeviceLost.
TEST_F(WireErrorCallbackTests,PopErrorScopeAfterDisconnect)272 TEST_F(WireErrorCallbackTests, PopErrorScopeAfterDisconnect) {
273 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
274 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
275
276 FlushClient();
277
278 GetWireClient()->Disconnect();
279
280 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
281 Call(WGPUErrorType_DeviceLost, ValidStringMessage(), this))
282 .Times(1);
283 EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
284 }
285
286 // Test that PopErrorScope returns false if there are no error scopes.
TEST_F(WireErrorCallbackTests,PopErrorScopeEmptyStack)287 TEST_F(WireErrorCallbackTests, PopErrorScopeEmptyStack) {
288 // Empty stack
289 { EXPECT_FALSE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this)); }
290
291 // Pop too many times
292 {
293 wgpuDevicePushErrorScope(device, WGPUErrorFilter_Validation);
294 EXPECT_CALL(api, DevicePushErrorScope(apiDevice, WGPUErrorFilter_Validation)).Times(1);
295
296 EXPECT_TRUE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this));
297 EXPECT_FALSE(wgpuDevicePopErrorScope(device, ToMockDevicePopErrorScopeCallback, this + 1));
298
299 WGPUErrorCallback callback;
300 void* userdata;
301 EXPECT_CALL(api, OnDevicePopErrorScope(apiDevice, _, _))
302 .WillOnce(DoAll(SaveArg<1>(&callback), SaveArg<2>(&userdata), Return(true)));
303
304 FlushClient();
305
306 callback(WGPUErrorType_Validation, "Some error message", userdata);
307 EXPECT_CALL(*mockDevicePopErrorScopeCallback,
308 Call(WGPUErrorType_Validation, StrEq("Some error message"), this))
309 .Times(1);
310
311 FlushServer();
312 }
313 }
314
315 // Test the return wire for device lost callback
TEST_F(WireErrorCallbackTests,DeviceLostCallback)316 TEST_F(WireErrorCallbackTests, DeviceLostCallback) {
317 wgpuDeviceSetDeviceLostCallback(device, ToMockDeviceLostCallback, this);
318
319 // Setting the error callback should stay on the client side and do nothing
320 FlushClient();
321
322 // Calling the callback on the server side will result in the callback being called on the
323 // client side
324 api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Undefined,
325 "Some error message");
326
327 EXPECT_CALL(*mockDeviceLostCallback,
328 Call(WGPUDeviceLostReason_Undefined, StrEq("Some error message"), this))
329 .Times(1);
330
331 FlushServer();
332 }
333