|  | // Copyright 2019 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" | 
|  |  | 
|  | using namespace testing; | 
|  | using namespace dawn_wire; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class MockFenceOnCompletionCallback { | 
|  | public: | 
|  | MOCK_METHOD2(Call, void(WGPUFenceCompletionStatus status, void* userdata)); | 
|  | }; | 
|  |  | 
|  | std::unique_ptr<StrictMock<MockFenceOnCompletionCallback>> mockFenceOnCompletionCallback; | 
|  | void ToMockFenceOnCompletionCallback(WGPUFenceCompletionStatus status, void* userdata) { | 
|  | mockFenceOnCompletionCallback->Call(status, userdata); | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | class WireFenceTests : public WireTest { | 
|  | public: | 
|  | WireFenceTests() { | 
|  | } | 
|  | ~WireFenceTests() override = default; | 
|  |  | 
|  | void SetUp() override { | 
|  | WireTest::SetUp(); | 
|  |  | 
|  | mockFenceOnCompletionCallback = | 
|  | std::make_unique<StrictMock<MockFenceOnCompletionCallback>>(); | 
|  |  | 
|  | { | 
|  | queue = wgpuDeviceCreateQueue(device); | 
|  | apiQueue = api.GetNewQueue(); | 
|  | EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); | 
|  | FlushClient(); | 
|  | } | 
|  | { | 
|  | WGPUFenceDescriptor descriptor; | 
|  | descriptor.nextInChain = nullptr; | 
|  | descriptor.label = nullptr; | 
|  | descriptor.initialValue = 1; | 
|  |  | 
|  | apiFence = api.GetNewFence(); | 
|  | fence = wgpuQueueCreateFence(queue, &descriptor); | 
|  |  | 
|  | EXPECT_CALL(api, QueueCreateFence(apiQueue, _)).WillOnce(Return(apiFence)); | 
|  | FlushClient(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | WireTest::TearDown(); | 
|  |  | 
|  | mockFenceOnCompletionCallback = nullptr; | 
|  | } | 
|  |  | 
|  | void FlushServer() { | 
|  | WireTest::FlushServer(); | 
|  |  | 
|  | Mock::VerifyAndClearExpectations(&mockFenceOnCompletionCallback); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | void DoQueueSignal(uint64_t signalValue) { | 
|  | wgpuQueueSignal(queue, fence, signalValue); | 
|  | EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1); | 
|  |  | 
|  | // This callback is generated to update the completedValue of the fence | 
|  | // on the client | 
|  | EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _)) | 
|  | .WillOnce(InvokeWithoutArgs([&]() { | 
|  | api.CallFenceOnCompletionCallback(apiFence, WGPUFenceCompletionStatus_Success); | 
|  | })); | 
|  | } | 
|  |  | 
|  | // A successfully created fence | 
|  | WGPUFence fence; | 
|  | WGPUFence apiFence; | 
|  |  | 
|  | WGPUQueue queue; | 
|  | WGPUQueue apiQueue; | 
|  | }; | 
|  |  | 
|  | // Check that signaling a fence succeeds | 
|  | TEST_F(WireFenceTests, QueueSignalSuccess) { | 
|  | DoQueueSignal(2u); | 
|  | DoQueueSignal(3u); | 
|  | FlushClient(); | 
|  | FlushServer(); | 
|  | } | 
|  |  | 
|  | // Errors should be generated when signaling a value less | 
|  | // than or equal to the current signaled value | 
|  | TEST_F(WireFenceTests, QueueSignalValidationError) { | 
|  | wgpuQueueSignal(queue, fence, 0u);  // Error | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  |  | 
|  | wgpuQueueSignal(queue, fence, 1u);  // Error | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  |  | 
|  | DoQueueSignal(4u);  // Success | 
|  | FlushClient(); | 
|  |  | 
|  | wgpuQueueSignal(queue, fence, 3u);  // Error | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  | } | 
|  |  | 
|  | // Check that callbacks are immediately called if the fence is already finished | 
|  | TEST_F(WireFenceTests, OnCompletionImmediate) { | 
|  | // Can call on value < (initial) signaled value happens immediately | 
|  | { | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _)) | 
|  | .Times(1); | 
|  | wgpuFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, nullptr); | 
|  | } | 
|  |  | 
|  | // Can call on value == (initial) signaled value happens immediately | 
|  | { | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, _)) | 
|  | .Times(1); | 
|  | wgpuFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, nullptr); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check that all passed client completion callbacks are called | 
|  | TEST_F(WireFenceTests, OnCompletionMultiple) { | 
|  | DoQueueSignal(3u); | 
|  | DoQueueSignal(6u); | 
|  |  | 
|  | // Add callbacks in a non-monotonic order. They should still be called | 
|  | // in order of increasing fence value. | 
|  | // Add multiple callbacks for the same value. | 
|  | wgpuFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, this + 0); | 
|  | wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 1); | 
|  | wgpuFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, this + 2); | 
|  | wgpuFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, this + 3); | 
|  |  | 
|  | Sequence s1, s2; | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1)) | 
|  | .Times(1) | 
|  | .InSequence(s1); | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 3)) | 
|  | .Times(1) | 
|  | .InSequence(s2); | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 2)) | 
|  | .Times(1) | 
|  | .InSequence(s1, s2); | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0)) | 
|  | .Times(1) | 
|  | .InSequence(s1, s2); | 
|  |  | 
|  | FlushClient(); | 
|  | FlushServer(); | 
|  | } | 
|  |  | 
|  | // Without any flushes, it is valid to wait on a value less than or equal to | 
|  | // the last signaled value | 
|  | TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) { | 
|  | wgpuQueueSignal(queue, fence, 4u); | 
|  | wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0); | 
|  | wgpuFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0); | 
|  | wgpuFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0); | 
|  |  | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _)) | 
|  | .Times(3); | 
|  | } | 
|  |  | 
|  | // Errors should be generated when waiting on a value greater | 
|  | // than the last signaled value | 
|  | TEST_F(WireFenceTests, OnCompletionValidationError) { | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Error, this + 0)) | 
|  | .Times(1); | 
|  |  | 
|  | wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, this + 0); | 
|  |  | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  | } | 
|  |  | 
|  | // Check that the fence completed value is initialized | 
|  | TEST_F(WireFenceTests, GetCompletedValueInitialization) { | 
|  | EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); | 
|  | } | 
|  |  | 
|  | // Check that the fence completed value updates after signaling the fence | 
|  | TEST_F(WireFenceTests, GetCompletedValueUpdate) { | 
|  | DoQueueSignal(3u); | 
|  | FlushClient(); | 
|  | FlushServer(); | 
|  |  | 
|  | EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 3u); | 
|  | } | 
|  |  | 
|  | // Check that the fence completed value does not update without a flush | 
|  | TEST_F(WireFenceTests, GetCompletedValueNoUpdate) { | 
|  | wgpuQueueSignal(queue, fence, 3u); | 
|  | EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); | 
|  | } | 
|  |  | 
|  | // Check that the callback is called with UNKNOWN when the fence is destroyed | 
|  | // before the completed value is updated | 
|  | TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) { | 
|  | wgpuQueueSignal(queue, fence, 3u); | 
|  | wgpuFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, nullptr); | 
|  | EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Unknown, _)) | 
|  | .Times(1); | 
|  | } | 
|  |  | 
|  | // Test that signaling a fence on a wrong queue is invalid | 
|  | TEST_F(WireFenceTests, SignalWrongQueue) { | 
|  | WGPUQueue queue2 = wgpuDeviceCreateQueue(device); | 
|  | WGPUQueue apiQueue2 = api.GetNewQueue(); | 
|  | EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2)); | 
|  | FlushClient(); | 
|  |  | 
|  | wgpuQueueSignal(queue2, fence, 2u);  // error | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  | } | 
|  |  | 
|  | // Test that signaling a fence on a wrong queue does not update fence signaled value | 
|  | TEST_F(WireFenceTests, SignalWrongQueueDoesNotUpdateValue) { | 
|  | WGPUQueue queue2 = wgpuDeviceCreateQueue(device); | 
|  | WGPUQueue apiQueue2 = api.GetNewQueue(); | 
|  | EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue2)); | 
|  | FlushClient(); | 
|  |  | 
|  | wgpuQueueSignal(queue2, fence, 2u);  // error | 
|  | EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_Validation, ValidStringMessage())) | 
|  | .Times(1); | 
|  | FlushClient(); | 
|  |  | 
|  | // Fence value should be unchanged. | 
|  | FlushClient(); | 
|  | FlushServer(); | 
|  | EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 1u); | 
|  |  | 
|  | // Signaling with 2 on the correct queue should succeed | 
|  | DoQueueSignal(2u);  // success | 
|  | FlushClient(); | 
|  | FlushServer(); | 
|  | EXPECT_EQ(wgpuFenceGetCompletedValue(fence), 2u); | 
|  | } |