| // Copyright 2018 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/validation/ValidationTest.h" |
| |
| #include <gmock/gmock.h> |
| |
| using namespace testing; |
| |
| class MockFenceOnCompletionCallback { |
| public: |
| MOCK_METHOD(void, Call, (WGPUFenceCompletionStatus status, void* userdata)); |
| }; |
| |
| struct FenceOnCompletionExpectation { |
| wgpu::Fence fence; |
| uint64_t value; |
| WGPUFenceCompletionStatus status; |
| }; |
| |
| static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback; |
| static void ToMockFenceOnCompletion(WGPUFenceCompletionStatus status, void* userdata) { |
| mockFenceOnCompletionCallback->Call(status, userdata); |
| } |
| |
| class FenceValidationTest : public ValidationTest { |
| protected: |
| void TestOnCompletion(wgpu::Fence fence, uint64_t value, WGPUFenceCompletionStatus status) { |
| FenceOnCompletionExpectation* expectation = new FenceOnCompletionExpectation; |
| expectation->fence = fence; |
| expectation->value = value; |
| expectation->status = status; |
| |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(status, expectation)).Times(1); |
| fence.OnCompletion(value, ToMockFenceOnCompletion, expectation); |
| } |
| |
| wgpu::Queue queue; |
| |
| private: |
| void SetUp() override { |
| ValidationTest::SetUp(); |
| |
| mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>(); |
| queue = device.GetDefaultQueue(); |
| } |
| |
| void TearDown() override { |
| // Delete mocks so that expectations are checked |
| mockFenceOnCompletionCallback = nullptr; |
| |
| ValidationTest::TearDown(); |
| } |
| }; |
| |
| // Test cases where creation should succeed |
| TEST_F(FenceValidationTest, CreationSuccess) { |
| // Success |
| { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 0; |
| queue.CreateFence(&descriptor); |
| } |
| } |
| |
| // Creation succeeds if no descriptor is provided |
| TEST_F(FenceValidationTest, DefaultDescriptor) { |
| wgpu::Fence fence = queue.CreateFence(); |
| EXPECT_EQ(fence.GetCompletedValue(), 0u); |
| } |
| |
| TEST_F(FenceValidationTest, GetCompletedValue) { |
| // Starts at initial value |
| { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| EXPECT_EQ(fence.GetCompletedValue(), 1u); |
| } |
| } |
| |
| // Test that OnCompletion handlers are called immediately for |
| // already completed fence values |
| TEST_F(FenceValidationTest, OnCompletionImmediate) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 0)) |
| .Times(1); |
| fence.OnCompletion(0u, ToMockFenceOnCompletion, this + 0); |
| |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, this + 1)) |
| .Times(1); |
| fence.OnCompletion(1u, ToMockFenceOnCompletion, this + 1); |
| } |
| |
| // Test setting OnCompletion handlers for values > signaled value |
| TEST_F(FenceValidationTest, OnCompletionLargerThanSignaled) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| // Cannot signal for values > signaled value |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Error, nullptr)) |
| .Times(1); |
| ASSERT_DEVICE_ERROR(fence.OnCompletion(2u, ToMockFenceOnCompletion, nullptr)); |
| |
| // Can set handler after signaling |
| queue.Signal(fence, 2); |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) |
| .Times(1); |
| fence.OnCompletion(2u, ToMockFenceOnCompletion, nullptr); |
| |
| WaitForAllOperations(device); |
| } |
| |
| TEST_F(FenceValidationTest, GetCompletedValueInsideCallback) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| queue.Signal(fence, 3); |
| fence.OnCompletion(2u, ToMockFenceOnCompletion, nullptr); |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) |
| .WillOnce(Invoke([&](WGPUFenceCompletionStatus status, void* userdata) { |
| EXPECT_EQ(fence.GetCompletedValue(), 3u); |
| })); |
| |
| WaitForAllOperations(device); |
| } |
| |
| TEST_F(FenceValidationTest, GetCompletedValueAfterCallback) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| queue.Signal(fence, 2); |
| fence.OnCompletion(2u, ToMockFenceOnCompletion, nullptr); |
| EXPECT_CALL(*mockFenceOnCompletionCallback, Call(WGPUFenceCompletionStatus_Success, nullptr)) |
| .Times(1); |
| |
| WaitForAllOperations(device); |
| EXPECT_EQ(fence.GetCompletedValue(), 2u); |
| } |
| |
| TEST_F(FenceValidationTest, SignalError) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| // value < fence signaled value |
| ASSERT_DEVICE_ERROR(queue.Signal(fence, 0)); |
| |
| // value == fence signaled value |
| ASSERT_DEVICE_ERROR(queue.Signal(fence, 1)); |
| } |
| |
| TEST_F(FenceValidationTest, SignalSuccess) { |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| // Success |
| queue.Signal(fence, 2); |
| WaitForAllOperations(device); |
| EXPECT_EQ(fence.GetCompletedValue(), 2u); |
| |
| // Success increasing fence value by more than 1 |
| queue.Signal(fence, 6); |
| WaitForAllOperations(device); |
| EXPECT_EQ(fence.GetCompletedValue(), 6u); |
| } |
| |
| // Test it is invalid to signal a fence on a different queue than it was created on |
| // DISABLED until we have support for multiple queues |
| TEST_F(FenceValidationTest, DISABLED_SignalWrongQueue) { |
| wgpu::Queue queue2 = device.GetDefaultQueue(); |
| |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| ASSERT_DEVICE_ERROR(queue2.Signal(fence, 2)); |
| } |
| |
| // Test that signaling a fence on a wrong queue does not update fence signaled value |
| // DISABLED until we have support for multiple queues |
| TEST_F(FenceValidationTest, DISABLED_SignalWrongQueueDoesNotUpdateValue) { |
| wgpu::Queue queue2 = device.GetDefaultQueue(); |
| |
| wgpu::FenceDescriptor descriptor; |
| descriptor.initialValue = 1; |
| wgpu::Fence fence = queue.CreateFence(&descriptor); |
| |
| ASSERT_DEVICE_ERROR(queue2.Signal(fence, 2)); |
| |
| // Fence value should be unchanged. |
| WaitForAllOperations(device); |
| EXPECT_EQ(fence.GetCompletedValue(), 1u); |
| |
| // Signaling with 2 on the correct queue should succeed |
| queue.Signal(fence, 2); |
| WaitForAllOperations(device); |
| EXPECT_EQ(fence.GetCompletedValue(), 2u); |
| } |