| // Copyright 2020 The Dawn Authors | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #include "tests/unittests/wire/WireTest.h" | |
| #include "common/Assert.h" | |
| #include "dawn_wire/WireClient.h" | |
| #include "tests/MockCallback.h" | |
| using namespace testing; | |
| using namespace dawn_wire; | |
| namespace { | |
| class WireDisconnectTests : public WireTest {}; | |
| } // anonymous namespace | |
| // Test that commands are not received if the client disconnects. | |
| TEST_F(WireDisconnectTests, CommandsAfterDisconnect) { | |
| // Sanity check that commands work at all. | |
| wgpuDeviceCreateCommandEncoder(device, nullptr); | |
| WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); | |
| EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) | |
| .WillOnce(Return(apiCmdBufEncoder)); | |
| FlushClient(); | |
| // Disconnect. | |
| GetWireClient()->Disconnect(); | |
| // Command is not received because client disconnected. | |
| wgpuDeviceCreateCommandEncoder(device, nullptr); | |
| EXPECT_CALL(api, DeviceCreateCommandEncoder(_, _)).Times(Exactly(0)); | |
| FlushClient(); | |
| } | |
| // Test that commands that are serialized before a disconnect but flushed | |
| // after are received. | |
| TEST_F(WireDisconnectTests, FlushAfterDisconnect) { | |
| // Sanity check that commands work at all. | |
| wgpuDeviceCreateCommandEncoder(device, nullptr); | |
| // Disconnect. | |
| GetWireClient()->Disconnect(); | |
| // Already-serialized commmands are still received. | |
| WGPUCommandEncoder apiCmdBufEncoder = api.GetNewCommandEncoder(); | |
| EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) | |
| .WillOnce(Return(apiCmdBufEncoder)); | |
| FlushClient(); | |
| } | |
| // Check that disconnecting the wire client calls the device lost callback exacty once. | |
| TEST_F(WireDisconnectTests, CallsDeviceLostCallback) { | |
| MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; | |
| wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), | |
| mockDeviceLostCallback.MakeUserdata(this)); | |
| // Disconnect the wire client. We should receive device lost only once. | |
| EXPECT_CALL(mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this)) | |
| .Times(Exactly(1)); | |
| GetWireClient()->Disconnect(); | |
| GetWireClient()->Disconnect(); | |
| } | |
| // Check that disconnecting the wire client after a device loss does not trigger the callback again. | |
| TEST_F(WireDisconnectTests, ServerLostThenDisconnect) { | |
| MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; | |
| wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), | |
| mockDeviceLostCallback.MakeUserdata(this)); | |
| api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Undefined, | |
| "some reason"); | |
| // Flush the device lost return command. | |
| EXPECT_CALL(mockDeviceLostCallback, | |
| Call(WGPUDeviceLostReason_Undefined, StrEq("some reason"), this)) | |
| .Times(Exactly(1)); | |
| FlushServer(); | |
| // Disconnect the client. We shouldn't see the lost callback again. | |
| EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0)); | |
| GetWireClient()->Disconnect(); | |
| } | |
| // Check that disconnecting the wire client inside the device loss callback does not trigger the | |
| // callback again. | |
| TEST_F(WireDisconnectTests, ServerLostThenDisconnectInCallback) { | |
| MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; | |
| wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), | |
| mockDeviceLostCallback.MakeUserdata(this)); | |
| api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Undefined, | |
| "lost reason"); | |
| // Disconnect the client inside the lost callback. We should see the callback | |
| // only once. | |
| EXPECT_CALL(mockDeviceLostCallback, | |
| Call(WGPUDeviceLostReason_Undefined, StrEq("lost reason"), this)) | |
| .WillOnce(InvokeWithoutArgs([&]() { | |
| EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0)); | |
| GetWireClient()->Disconnect(); | |
| })); | |
| FlushServer(); | |
| } | |
| // Check that a device loss after a disconnect does not trigger the callback again. | |
| TEST_F(WireDisconnectTests, DisconnectThenServerLost) { | |
| MockCallback<WGPUDeviceLostCallback> mockDeviceLostCallback; | |
| wgpuDeviceSetDeviceLostCallback(device, mockDeviceLostCallback.Callback(), | |
| mockDeviceLostCallback.MakeUserdata(this)); | |
| // Disconnect the client. We should see the callback once. | |
| EXPECT_CALL(mockDeviceLostCallback, Call(WGPUDeviceLostReason_Undefined, _, this)) | |
| .Times(Exactly(1)); | |
| GetWireClient()->Disconnect(); | |
| // Lose the device on the server. The client callback shouldn't be | |
| // called again. | |
| api.CallDeviceSetDeviceLostCallbackCallback(apiDevice, WGPUDeviceLostReason_Undefined, | |
| "lost reason"); | |
| EXPECT_CALL(mockDeviceLostCallback, Call(_, _, _)).Times(Exactly(0)); | |
| FlushServer(); | |
| } | |
| // Test that client objects are all destroyed if the WireClient is destroyed. | |
| TEST_F(WireDisconnectTests, DeleteClientDestroysObjects) { | |
| WGPUSamplerDescriptor desc = {}; | |
| wgpuDeviceCreateCommandEncoder(device, nullptr); | |
| wgpuDeviceCreateSampler(device, &desc); | |
| WGPUCommandEncoder apiCommandEncoder = api.GetNewCommandEncoder(); | |
| EXPECT_CALL(api, DeviceCreateCommandEncoder(apiDevice, nullptr)) | |
| .WillOnce(Return(apiCommandEncoder)); | |
| WGPUSampler apiSampler = api.GetNewSampler(); | |
| EXPECT_CALL(api, DeviceCreateSampler(apiDevice, _)).WillOnce(Return(apiSampler)); | |
| FlushClient(); | |
| DeleteClient(); | |
| // Expect release on all objects created by the client. | |
| Sequence s1, s2, s3; | |
| EXPECT_CALL(api, QueueRelease(apiQueue)).Times(1).InSequence(s1); | |
| EXPECT_CALL(api, CommandEncoderRelease(apiCommandEncoder)).Times(1).InSequence(s2); | |
| EXPECT_CALL(api, SamplerRelease(apiSampler)).Times(1).InSequence(s3); | |
| EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)) | |
| .Times(1) | |
| .InSequence(s1, s2); | |
| EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)) | |
| .Times(1) | |
| .InSequence(s1, s2); | |
| EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)) | |
| .Times(1) | |
| .InSequence(s1, s2); | |
| EXPECT_CALL(api, DeviceRelease(apiDevice)).Times(1).InSequence(s1, s2, s3); | |
| FlushClient(); | |
| // Signal that we already released and cleared callbacks for |apiDevice| | |
| DefaultApiDeviceWasReleased(); | |
| } |