| // 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); |
| } |