diff --git a/BUILD.gn b/BUILD.gn index d7581f87b9..933c915ce6 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -939,7 +939,6 @@ test("dawn_unittests") { "src/tests/unittests/SerialMapTests.cpp", "src/tests/unittests/SerialQueueTests.cpp", "src/tests/unittests/ToBackendTests.cpp", - "src/tests/unittests/WireTests.cpp", "src/tests/unittests/validation/BindGroupValidationTests.cpp", "src/tests/unittests/validation/BufferValidationTests.cpp", "src/tests/unittests/validation/CommandBufferValidationTests.cpp", @@ -958,6 +957,14 @@ test("dawn_unittests") { "src/tests/unittests/validation/ValidationTest.cpp", "src/tests/unittests/validation/ValidationTest.h", "src/tests/unittests/validation/VertexBufferValidationTests.cpp", + "src/tests/unittests/wire/WireArgumentTests.cpp", + "src/tests/unittests/wire/WireBasicTests.cpp", + "src/tests/unittests/wire/WireBufferMappingTests.cpp", + "src/tests/unittests/wire/WireCallbackTests.cpp", + "src/tests/unittests/wire/WireFenceTests.cpp", + "src/tests/unittests/wire/WireOptionalTests.cpp", + "src/tests/unittests/wire/WireTest.cpp", + "src/tests/unittests/wire/WireTest.h", ] if (dawn_enable_d3d12) { diff --git a/src/tests/unittests/WireTests.cpp b/src/tests/unittests/WireTests.cpp deleted file mode 100644 index 27af06f34d..0000000000 --- a/src/tests/unittests/WireTests.cpp +++ /dev/null @@ -1,1659 +0,0 @@ -// Copyright 2017 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 "gtest/gtest.h" -#include "mock/mock_dawn.h" - -#include "common/Assert.h" -#include "common/Constants.h" -#include "dawn_wire/WireClient.h" -#include "dawn_wire/WireServer.h" -#include "utils/TerribleCommandBuffer.h" - -#include - -using namespace testing; - -// Definition of a "Lambda predicate matcher" for GMock to allow checking deep structures -// are passed correctly by the wire. - -// Helper templates to extract the argument type of a lambda. -template -struct MatcherMethodArgument; - -template -struct MatcherMethodArgument { - using Type = Arg; -}; - -template -using MatcherLambdaArgument = typename MatcherMethodArgument::Type; - -// The matcher itself, unfortunately it isn't able to return detailed information like other -// matchers do. -template -class LambdaMatcherImpl : public MatcherInterface { - public: - explicit LambdaMatcherImpl(Lambda lambda) : mLambda(lambda) {} - - void DescribeTo(std::ostream* os) const override { - *os << "with a custom matcher"; - } - - bool MatchAndExplain(Arg value, MatchResultListener* listener) const override { - if (!mLambda(value)) { - *listener << "which doesn't satisfy the custom predicate"; - return false; - } - return true; - } - - private: - Lambda mLambda; -}; - -// Use the MatchesLambda as follows: -// -// EXPECT_CALL(foo, Bar(MatchesLambda([](ArgType arg) -> bool { -// return CheckPredicateOnArg(arg); -// }))); -template -inline Matcher> MatchesLambda(Lambda lambda) { - return MakeMatcher(new LambdaMatcherImpl>(lambda)); -} - -// Mock classes to add expectations on the wire calling callbacks -class MockDeviceErrorCallback { - public: - MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata)); -}; - -static std::unique_ptr mockDeviceErrorCallback; -static void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) { - mockDeviceErrorCallback->Call(message, userdata); -} - -class MockBuilderErrorCallback { - public: - MOCK_METHOD4(Call, void(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2)); -}; - -static std::unique_ptr mockBuilderErrorCallback; -static void ToMockBuilderErrorCallback(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) { - mockBuilderErrorCallback->Call(status, message, userdata1, userdata2); -} - -class MockBufferMapReadCallback { - public: - MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, const uint32_t* ptr, dawnCallbackUserdata userdata)); -}; - -static std::unique_ptr mockBufferMapReadCallback; -static void ToMockBufferMapReadCallback(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata) { - // Assume the data is uint32_t to make writing matchers easier - mockBufferMapReadCallback->Call(status, static_cast(ptr), userdata); -} - -class MockBufferMapWriteCallback { - public: - MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, uint32_t* ptr, dawnCallbackUserdata userdata)); -}; - -static std::unique_ptr mockBufferMapWriteCallback; -uint32_t* lastMapWritePointer = nullptr; -static void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata) { - // Assume the data is uint32_t to make writing matchers easier - lastMapWritePointer = static_cast(ptr); - mockBufferMapWriteCallback->Call(status, lastMapWritePointer, userdata); -} - -class MockFenceOnCompletionCallback { - public: - MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata)); -}; - -static std::unique_ptr mockFenceOnCompletionCallback; -static void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status, - dawnCallbackUserdata userdata) { - mockFenceOnCompletionCallback->Call(status, userdata); -} - -class WireTestsBase : public Test { - protected: - WireTestsBase(bool ignoreSetCallbackCalls) - : mIgnoreSetCallbackCalls(ignoreSetCallbackCalls) { - } - - void SetUp() override { - mockDeviceErrorCallback = std::make_unique(); - mockBuilderErrorCallback = std::make_unique(); - mockBufferMapReadCallback = std::make_unique(); - mockBufferMapWriteCallback = std::make_unique(); - mockFenceOnCompletionCallback = std::make_unique(); - - dawnProcTable mockProcs; - dawnDevice mockDevice; - api.GetProcTableAndDevice(&mockProcs, &mockDevice); - - // This SetCallback call cannot be ignored because it is done as soon as we start the server - EXPECT_CALL(api, OnDeviceSetErrorCallback(_, _, _)).Times(Exactly(1)); - if (mIgnoreSetCallbackCalls) { - EXPECT_CALL(api, OnBuilderSetErrorCallback(_, _, _, _)).Times(AnyNumber()); - } - EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber()); - - mS2cBuf = std::make_unique(); - mC2sBuf = std::make_unique(mWireServer.get()); - - mWireServer.reset(new dawn_wire::WireServer(mockDevice, mockProcs, mS2cBuf.get())); - mC2sBuf->SetHandler(mWireServer.get()); - - mWireClient.reset(new dawn_wire::WireClient(mC2sBuf.get())); - dawnProcTable clientProcs = mWireClient->GetProcs(); - device = mWireClient->GetDevice(); - dawnSetProcs(&clientProcs); - mS2cBuf->SetHandler(mWireClient.get()); - - apiDevice = mockDevice; - } - - void TearDown() override { - dawnSetProcs(nullptr); - - // Reset client before mocks are deleted. - // Incomplete callbacks will be called on deletion, so the mocks cannot be null. - mWireClient = nullptr; - - // Delete mocks so that expectations are checked - mockDeviceErrorCallback = nullptr; - mockBuilderErrorCallback = nullptr; - mockBufferMapReadCallback = nullptr; - mockBufferMapWriteCallback = nullptr; - mockFenceOnCompletionCallback = nullptr; - } - - void FlushClient() { - ASSERT_TRUE(mC2sBuf->Flush()); - } - - void FlushServer() { - ASSERT_TRUE(mS2cBuf->Flush()); - } - - MockProcTable api; - dawnDevice apiDevice; - dawnDevice device; - - private: - bool mIgnoreSetCallbackCalls = false; - - std::unique_ptr mWireServer; - std::unique_ptr mWireClient; - std::unique_ptr mS2cBuf; - std::unique_ptr mC2sBuf; -}; - -class WireTests : public WireTestsBase { - public: - WireTests() : WireTestsBase(true) { - } -}; - -// One call gets forwarded correctly. -TEST_F(WireTests, CallForwarded) { - dawnDeviceCreateCommandBufferBuilder(device); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - FlushClient(); -} - -// Test that calling methods on a new object works as expected. -TEST_F(WireTests, CreateThenCall) { - dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); - dawnCommandBufferBuilderGetResult(builder); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - - dawnCommandBuffer apiCmdBuf = api.GetNewCommandBuffer(); - EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder)) - .WillOnce(Return(apiCmdBuf)); - - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - EXPECT_CALL(api, CommandBufferRelease(apiCmdBuf)); - FlushClient(); -} - -// Test that client reference/release do not call the backend API. -TEST_F(WireTests, RefCountKeptInClient) { - dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); - - dawnCommandBufferBuilderReference(builder); - dawnCommandBufferBuilderRelease(builder); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - - FlushClient(); -} - -// Test that client reference/release do not call the backend API. -TEST_F(WireTests, ReleaseCalledOnRefCount0) { - dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); - - dawnCommandBufferBuilderRelease(builder); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - - FlushClient(); -} - -// Test that the wire is able to send numerical values -TEST_F(WireTests, ValueArgument) { - dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); - dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder); - dawnComputePassEncoderDispatch(pass, 1, 2, 3); - - dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiBuilder)); - - dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); - EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)) - .WillOnce(Return(apiPass)); - - EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)) - .Times(1); - - EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder)); - EXPECT_CALL(api, ComputePassEncoderRelease(apiPass)); - FlushClient(); -} - -// Test that the wire is able to send arrays of numerical values -static constexpr uint32_t testPushConstantValues[4] = { - 0, - 42, - 0xDEADBEEFu, - 0xFFFFFFFFu -}; - -bool CheckPushConstantValues(const uint32_t* values) { - for (int i = 0; i < 4; ++i) { - if (values[i] != testPushConstantValues[i]) { - return false; - } - } - return true; -} - -TEST_F(WireTests, ValueArrayArgument) { - dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); - dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder); - dawnComputePassEncoderSetPushConstants(pass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, testPushConstantValues); - - dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiBuilder)); - - dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); - EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)) - .WillOnce(Return(apiPass)); - - EXPECT_CALL(api, ComputePassEncoderSetPushConstants(apiPass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, ResultOf(CheckPushConstantValues, Eq(true)))); - EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder)); - EXPECT_CALL(api, ComputePassEncoderRelease(apiPass)); - - FlushClient(); -} - -// Test that the wire is able to send C strings -TEST_F(WireTests, CStringArgument) { - // Create shader module - dawnShaderModuleDescriptor vertexDescriptor; - vertexDescriptor.nextInChain = nullptr; - vertexDescriptor.codeSize = 0; - dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); - dawnShaderModule apiVsModule = api.GetNewShaderModule(); - EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)) - .WillOnce(Return(apiVsModule)); - - // Create the blend state descriptor - dawnBlendDescriptor blendDescriptor; - blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; - blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; - blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; - dawnBlendStateDescriptor blendStateDescriptor; - blendStateDescriptor.nextInChain = nullptr; - blendStateDescriptor.alphaBlend = blendDescriptor; - blendStateDescriptor.colorBlend = blendDescriptor; - blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL; - - // Create the input state - dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device); - dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder(); - EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice)) - .WillOnce(Return(apiInputStateBuilder)); - - dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder); - dawnInputState apiInputState = api.GetNewInputState(); - EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder)) - .WillOnce(Return(apiInputState)); - - // Create the depth-stencil state - dawnStencilStateFaceDescriptor stencilFace; - stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; - stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; - - dawnDepthStencilStateDescriptor depthStencilState; - depthStencilState.nextInChain = nullptr; - depthStencilState.depthWriteEnabled = false; - depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; - depthStencilState.stencilBack = stencilFace; - depthStencilState.stencilFront = stencilFace; - depthStencilState.stencilReadMask = 0xff; - depthStencilState.stencilWriteMask = 0xff; - - // Create the pipeline layout - dawnPipelineLayoutDescriptor layoutDescriptor; - layoutDescriptor.nextInChain = nullptr; - layoutDescriptor.numBindGroupLayouts = 0; - layoutDescriptor.bindGroupLayouts = nullptr; - dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); - dawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); - EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)) - .WillOnce(Return(apiLayout)); - - // Create pipeline - dawnRenderPipelineDescriptor pipelineDescriptor; - pipelineDescriptor.nextInChain = nullptr; - - dawnPipelineStageDescriptor vertexStage; - vertexStage.nextInChain = nullptr; - vertexStage.module = vsModule; - vertexStage.entryPoint = "main"; - pipelineDescriptor.vertexStage = &vertexStage; - - dawnPipelineStageDescriptor fragmentStage; - fragmentStage.nextInChain = nullptr; - fragmentStage.module = vsModule; - fragmentStage.entryPoint = "main"; - pipelineDescriptor.fragmentStage = &fragmentStage; - - dawnAttachmentsStateDescriptor attachmentsState; - attachmentsState.nextInChain = nullptr; - attachmentsState.numColorAttachments = 1; - dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM}; - dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment}; - attachmentsState.colorAttachments = colorAttachmentPtr; - attachmentsState.hasDepthStencilAttachment = false; - // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid - // data because we don't have optional substructures yet. - attachmentsState.depthStencilAttachment = &colorAttachment; - pipelineDescriptor.attachmentsState = &attachmentsState; - - pipelineDescriptor.numBlendStates = 1; - pipelineDescriptor.blendStates = &blendStateDescriptor; - - pipelineDescriptor.sampleCount = 1; - pipelineDescriptor.layout = layout; - pipelineDescriptor.inputState = inputState; - pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32; - pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - pipelineDescriptor.depthStencilState = &depthStencilState; - - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); - EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { - return desc->vertexStage->entryPoint == std::string("main"); - }))) - .WillOnce(Return(nullptr)); - EXPECT_CALL(api, ShaderModuleRelease(apiVsModule)); - EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder)); - EXPECT_CALL(api, InputStateRelease(apiInputState)); - EXPECT_CALL(api, PipelineLayoutRelease(apiLayout)); - - FlushClient(); -} - -// Test that the wire is able to send optional pointers to structures -TEST_F(WireTests, OptionalStructPointer) { - // Create shader module - dawnShaderModuleDescriptor vertexDescriptor; - vertexDescriptor.nextInChain = nullptr; - vertexDescriptor.codeSize = 0; - dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); - dawnShaderModule apiVsModule = api.GetNewShaderModule(); - EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)) - .WillOnce(Return(apiVsModule)); - - // Create the blend state descriptor - dawnBlendDescriptor blendDescriptor; - blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; - blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; - blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; - dawnBlendStateDescriptor blendStateDescriptor; - blendStateDescriptor.nextInChain = nullptr; - blendStateDescriptor.alphaBlend = blendDescriptor; - blendStateDescriptor.colorBlend = blendDescriptor; - blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL; - - // Create the input state - dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device); - dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder(); - EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice)) - .WillOnce(Return(apiInputStateBuilder)); - - dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder); - dawnInputState apiInputState = api.GetNewInputState(); - EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder)) - .WillOnce(Return(apiInputState)); - - // Create the depth-stencil state - dawnStencilStateFaceDescriptor stencilFace; - stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; - stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; - stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; - - dawnDepthStencilStateDescriptor depthStencilState; - depthStencilState.nextInChain = nullptr; - depthStencilState.depthWriteEnabled = false; - depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; - depthStencilState.stencilBack = stencilFace; - depthStencilState.stencilFront = stencilFace; - depthStencilState.stencilReadMask = 0xff; - depthStencilState.stencilWriteMask = 0xff; - - // Create the pipeline layout - dawnPipelineLayoutDescriptor layoutDescriptor; - layoutDescriptor.nextInChain = nullptr; - layoutDescriptor.numBindGroupLayouts = 0; - layoutDescriptor.bindGroupLayouts = nullptr; - dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); - dawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); - EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)) - .WillOnce(Return(apiLayout)); - - // Create pipeline - dawnRenderPipelineDescriptor pipelineDescriptor; - pipelineDescriptor.nextInChain = nullptr; - - dawnPipelineStageDescriptor vertexStage; - vertexStage.nextInChain = nullptr; - vertexStage.module = vsModule; - vertexStage.entryPoint = "main"; - pipelineDescriptor.vertexStage = &vertexStage; - - dawnPipelineStageDescriptor fragmentStage; - fragmentStage.nextInChain = nullptr; - fragmentStage.module = vsModule; - fragmentStage.entryPoint = "main"; - pipelineDescriptor.fragmentStage = &fragmentStage; - - dawnAttachmentsStateDescriptor attachmentsState; - attachmentsState.nextInChain = nullptr; - attachmentsState.numColorAttachments = 1; - dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM}; - dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment}; - attachmentsState.colorAttachments = colorAttachmentPtr; - attachmentsState.hasDepthStencilAttachment = false; - // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid - // data because we don't have optional substructures yet. - attachmentsState.depthStencilAttachment = &colorAttachment; - pipelineDescriptor.attachmentsState = &attachmentsState; - - pipelineDescriptor.numBlendStates = 1; - pipelineDescriptor.blendStates = &blendStateDescriptor; - - pipelineDescriptor.sampleCount = 1; - pipelineDescriptor.layout = layout; - pipelineDescriptor.inputState = inputState; - pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32; - pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; - - // First case: depthStencilState is not null. - pipelineDescriptor.depthStencilState = &depthStencilState; - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); - EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { - return desc->depthStencilState != nullptr && - desc->depthStencilState->nextInChain == nullptr && - desc->depthStencilState->depthWriteEnabled == false && - desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilBack.compare == DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilBack.depthFailOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilFront.compare == DAWN_COMPARE_FUNCTION_ALWAYS && - desc->depthStencilState->stencilFront.failOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilFront.depthFailOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilFront.passOp == DAWN_STENCIL_OPERATION_KEEP && - desc->depthStencilState->stencilReadMask == 0xff && - desc->depthStencilState->stencilWriteMask == 0xff; - }))) - .WillOnce(Return(nullptr)); - - FlushClient(); - - // Second case: depthStencilState is null. - pipelineDescriptor.depthStencilState = nullptr; - dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); - EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { - return desc->depthStencilState == nullptr; - }))) - .WillOnce(Return(nullptr)); - - EXPECT_CALL(api, ShaderModuleRelease(apiVsModule)); - EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder)); - EXPECT_CALL(api, InputStateRelease(apiInputState)); - EXPECT_CALL(api, PipelineLayoutRelease(apiLayout)); - - FlushClient(); -} - -// Test that the wire is able to send objects as value arguments -TEST_F(WireTests, ObjectAsValueArgument) { - // Create a RenderPassDescriptor - dawnRenderPassDescriptorBuilder renderPassBuilder = dawnDeviceCreateRenderPassDescriptorBuilder(device); - dawnRenderPassDescriptor renderPass = dawnRenderPassDescriptorBuilderGetResult(renderPassBuilder); - - dawnRenderPassDescriptorBuilder apiRenderPassBuilder = api.GetNewRenderPassDescriptorBuilder(); - EXPECT_CALL(api, DeviceCreateRenderPassDescriptorBuilder(apiDevice)) - .WillOnce(Return(apiRenderPassBuilder)); - dawnRenderPassDescriptor apiRenderPass = api.GetNewRenderPassDescriptor(); - EXPECT_CALL(api, RenderPassDescriptorBuilderGetResult(apiRenderPassBuilder)) - .WillOnce(Return(apiRenderPass)); - - // Create command buffer builder, setting render pass descriptor - dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); - dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, renderPass); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - - EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, apiRenderPass)) - .Times(1); - - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - EXPECT_CALL(api, RenderPassDescriptorBuilderRelease(apiRenderPassBuilder)); - EXPECT_CALL(api, RenderPassDescriptorRelease(apiRenderPass)); - FlushClient(); -} - -// Test that the wire is able to send array of objects -TEST_F(WireTests, ObjectsAsPointerArgument) { - dawnCommandBuffer cmdBufs[2]; - dawnCommandBuffer apiCmdBufs[2]; - - // Create two command buffers we need to use a GMock sequence otherwise the order of the - // CreateCommandBufferBuilder might be swapped since they are equivalent in term of matchers - Sequence s; - for (int i = 0; i < 2; ++i) { - dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); - cmdBufs[i] = dawnCommandBufferBuilderGetResult(cmdBufBuilder); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .InSequence(s) - .WillOnce(Return(apiCmdBufBuilder)); - - apiCmdBufs[i] = api.GetNewCommandBuffer(); - EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder)) - .WillOnce(Return(apiCmdBufs[i])); - EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); - EXPECT_CALL(api, CommandBufferRelease(apiCmdBufs[i])); - } - - // Create queue - dawnQueue queue = dawnDeviceCreateQueue(device); - dawnQueue apiQueue = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)) - .WillOnce(Return(apiQueue)); - - // Submit command buffer and check we got a call with both API-side command buffers - dawnQueueSubmit(queue, 2, cmdBufs); - - EXPECT_CALL(api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const dawnCommandBuffer* cmdBufs) -> bool { - return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1]; - }))); - - EXPECT_CALL(api, QueueRelease(apiQueue)); - FlushClient(); -} - -// Test that the wire is able to send structures that contain pure values (non-objects) -TEST_F(WireTests, StructureOfValuesArgument) { - dawnSamplerDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.magFilter = DAWN_FILTER_MODE_LINEAR; - descriptor.minFilter = DAWN_FILTER_MODE_NEAREST; - descriptor.mipmapFilter = DAWN_FILTER_MODE_LINEAR; - descriptor.addressModeU = DAWN_ADDRESS_MODE_CLAMP_TO_EDGE; - descriptor.addressModeV = DAWN_ADDRESS_MODE_REPEAT; - descriptor.addressModeW = DAWN_ADDRESS_MODE_MIRRORED_REPEAT; - descriptor.lodMinClamp = kLodMin; - descriptor.lodMaxClamp = kLodMax; - descriptor.compareFunction = DAWN_COMPARE_FUNCTION_NEVER; - descriptor.borderColor = DAWN_BORDER_COLOR_TRANSPARENT_BLACK; - - dawnDeviceCreateSampler(device, &descriptor); - EXPECT_CALL(api, DeviceCreateSampler(apiDevice, MatchesLambda([](const dawnSamplerDescriptor* desc) -> bool { - return desc->nextInChain == nullptr && - desc->magFilter == DAWN_FILTER_MODE_LINEAR && - desc->minFilter == DAWN_FILTER_MODE_NEAREST && - desc->mipmapFilter == DAWN_FILTER_MODE_LINEAR && - desc->addressModeU == DAWN_ADDRESS_MODE_CLAMP_TO_EDGE && - desc->addressModeV == DAWN_ADDRESS_MODE_REPEAT && - desc->addressModeW == DAWN_ADDRESS_MODE_MIRRORED_REPEAT && - desc->compareFunction == DAWN_COMPARE_FUNCTION_NEVER && - desc->borderColor == DAWN_BORDER_COLOR_TRANSPARENT_BLACK && - desc->lodMinClamp == kLodMin && - desc->lodMaxClamp == kLodMax; - }))) - .WillOnce(Return(nullptr)); - - FlushClient(); -} - -// Test that the wire is able to send structures that contain objects -TEST_F(WireTests, StructureOfObjectArrayArgument) { - dawnBindGroupLayoutDescriptor bglDescriptor; - bglDescriptor.numBindings = 0; - bglDescriptor.bindings = nullptr; - - dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); - dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); - EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl)); - - dawnPipelineLayoutDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.numBindGroupLayouts = 1; - descriptor.bindGroupLayouts = &bgl; - - dawnDeviceCreatePipelineLayout(device, &descriptor); - EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, MatchesLambda([apiBgl](const dawnPipelineLayoutDescriptor* desc) -> bool { - return desc->nextInChain == nullptr && - desc->numBindGroupLayouts == 1 && - desc->bindGroupLayouts[0] == apiBgl; - }))) - .WillOnce(Return(nullptr)); - - EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl)); - FlushClient(); -} - -// Test that the wire is able to send structures that contain objects -TEST_F(WireTests, StructureOfStructureArrayArgument) { - static constexpr int NUM_BINDINGS = 3; - dawnBindGroupLayoutBinding bindings[NUM_BINDINGS]{ - {0, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLER}, - {1, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLED_TEXTURE}, - {2, - static_cast(DAWN_SHADER_STAGE_BIT_VERTEX | - DAWN_SHADER_STAGE_BIT_FRAGMENT), - DAWN_BINDING_TYPE_UNIFORM_BUFFER}, - }; - dawnBindGroupLayoutDescriptor bglDescriptor; - bglDescriptor.numBindings = NUM_BINDINGS; - bglDescriptor.bindings = bindings; - - dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); - dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); - EXPECT_CALL( - api, - DeviceCreateBindGroupLayout( - apiDevice, MatchesLambda([bindings](const dawnBindGroupLayoutDescriptor* desc) -> bool { - for (int i = 0; i < NUM_BINDINGS; ++i) { - const auto& a = desc->bindings[i]; - const auto& b = bindings[i]; - if (a.binding != b.binding || a.visibility != b.visibility || - a.type != b.type) { - return false; - } - } - return desc->nextInChain == nullptr && desc->numBindings == 3; - }))) - .WillOnce(Return(apiBgl)); - - EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl)); - FlushClient(); -} - -// Test passing nullptr instead of objects - object as value version -TEST_F(WireTests, OptionalObjectValue) { - dawnBindGroupLayoutDescriptor bglDesc; - bglDesc.nextInChain = nullptr; - bglDesc.numBindings = 0; - dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDesc); - - dawnBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout(); - EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)) - .WillOnce(Return(apiBindGroupLayout)); - - // The `sampler`, `textureView` and `buffer` members of a binding are optional. - dawnBindGroupBinding binding; - binding.binding = 0; - binding.sampler = nullptr; - binding.textureView = nullptr; - binding.buffer = nullptr; - - dawnBindGroupDescriptor bgDesc; - bgDesc.nextInChain = nullptr; - bgDesc.layout = bgl; - bgDesc.numBindings = 1; - bgDesc.bindings = &binding; - - dawnDeviceCreateBindGroup(device, &bgDesc); - EXPECT_CALL(api, DeviceCreateBindGroup(apiDevice, MatchesLambda([](const dawnBindGroupDescriptor* desc) -> bool { - return desc->nextInChain == nullptr && - desc->numBindings == 1 && - desc->bindings[0].binding == 0 && - desc->bindings[0].sampler == nullptr && - desc->bindings[0].buffer == nullptr && - desc->bindings[0].textureView == nullptr; - }))) - .WillOnce(Return(nullptr)); - - EXPECT_CALL(api, BindGroupLayoutRelease(apiBindGroupLayout)); - FlushClient(); -} - -// Test passing nullptr instead of objects - array of objects version -TEST_F(WireTests, DISABLED_NullptrInArray) { - dawnBindGroupLayout nullBGL = nullptr; - - dawnPipelineLayoutDescriptor descriptor; - descriptor.nextInChain = nullptr; - descriptor.numBindGroupLayouts = 1; - descriptor.bindGroupLayouts = &nullBGL; - - dawnDeviceCreatePipelineLayout(device, &descriptor); - EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, MatchesLambda([](const dawnPipelineLayoutDescriptor* desc) -> bool { - return desc->nextInChain == nullptr && - desc->numBindGroupLayouts == 1 && - desc->bindGroupLayouts[0] == nullptr; - }))) - .WillOnce(Return(nullptr)); - - FlushClient(); -} - -// Test that the server doesn't forward calls to error objects or with error objects -// Also test that when GetResult is called on an error builder, the error callback is fired -// TODO(cwallez@chromium.org): This test is disabled because the introduction of encoders breaks -// the assumptions of the "builder error" handling that a builder is self-contained. We need to -// revisit this once the new error handling is in place. -TEST_F(WireTests, DISABLED_CallsSkippedAfterBuilderError) { - dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); - dawnCommandBufferBuilderSetErrorCallback(cmdBufBuilder, ToMockBuilderErrorCallback, 1, 2); - - dawnRenderPassEncoder pass = dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, nullptr); - - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4); - dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder); // Hey look an error! - - // These calls will be skipped because of the error - dawnBufferSetSubData(buffer, 0, 0, nullptr); - dawnRenderPassEncoderSetIndexBuffer(pass, buffer, 0); - dawnRenderPassEncoderEndPass(pass); - dawnCommandBufferBuilderGetResult(cmdBufBuilder); - - dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); - EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) - .WillOnce(Return(apiCmdBufBuilder)); - - dawnRenderPassEncoder apiPass = api.GetNewRenderPassEncoder(); - EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, _)) - .WillOnce(Return(apiPass)); - - dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); - EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) - .WillOnce(Return(apiBufferBuilder)); - - // Hey look an error! - EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) - .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { - api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, "Error"); - return nullptr; - })); - - EXPECT_CALL(api, BufferSetSubData(_, _, _, _)).Times(0); - EXPECT_CALL(api, RenderPassEncoderSetIndexBuffer(_, _, _)).Times(0); - EXPECT_CALL(api, CommandBufferBuilderGetResult(_)).Times(0); - - FlushClient(); - - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 1, 2)).Times(1); - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 3, 4)).Times(1); - - FlushServer(); -} - -// Test that we get a success builder error status when no error happens -TEST_F(WireTests, SuccessCallbackOnBuilderSuccess) { - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2); - dawnBufferBuilderGetResult(bufferBuilder); - - dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); - EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) - .WillOnce(Return(apiBufferBuilder)); - - dawnBuffer apiBuffer = api.GetNewBuffer(); - EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) - .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { - api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "I like cheese"); - return apiBuffer; - })); - - EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); - EXPECT_CALL(api, BufferRelease(apiBuffer)); - FlushClient(); - - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, _ , 1 ,2)); - - FlushServer(); -} - -// Test that the client calls the builder callback with unknown when it HAS to fire the callback but can't -// know the status yet. -TEST_F(WireTests, UnknownBuilderErrorStatusCallback) { - // The builder is destroyed before the object is built - { - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2); - - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 1 ,2)).Times(1); - - dawnBufferBuilderRelease(bufferBuilder); - } - - // If the builder has been consumed, it doesn't fire the callback with unknown - { - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4); - dawnBufferBuilderGetResult(bufferBuilder); - - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 3, 4)).Times(0); - - dawnBufferBuilderRelease(bufferBuilder); - } - - // If the builder has been consumed, and the object is destroyed before the result comes from the server, - // then the callback is fired with unknown - { - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 5, 6); - dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder); - - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 5, 6)).Times(1); - - dawnBufferRelease(buffer); - } -} - -// Test that a builder success status doesn't get forwarded to the device -TEST_F(WireTests, SuccessCallbackNotForwardedToDevice) { - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, 0); - - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderGetResult(bufferBuilder); - - dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); - EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) - .WillOnce(Return(apiBufferBuilder)); - - dawnBuffer apiBuffer = api.GetNewBuffer(); - EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) - .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { - api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "I like cheese"); - return apiBuffer; - })); - - EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); - EXPECT_CALL(api, BufferRelease(apiBuffer)); - FlushClient(); - FlushServer(); -} - -// Test that a builder error status gets forwarded to the device -TEST_F(WireTests, ErrorCallbackForwardedToDevice) { - uint64_t userdata = 30495; - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); - - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - dawnBufferBuilderGetResult(bufferBuilder); - - dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); - EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) - .WillOnce(Return(apiBufferBuilder)); - - EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) - .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { - api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, "Error :("); - return nullptr; - })); - - EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); - FlushClient(); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); - - FlushServer(); -} - -class WireSetCallbackTests : public WireTestsBase { - public: - WireSetCallbackTests() : WireTestsBase(false) { - } -}; - -// Test the return wire for device error callbacks -TEST_F(WireSetCallbackTests, DeviceErrorCallback) { - uint64_t userdata = 3049785; - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); - - // Setting the error callback should stay on the client side and do nothing - FlushClient(); - - // Calling the callback on the server side will result in the callback being called on the client side - api.CallDeviceErrorCallback(apiDevice, "Some error message"); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), userdata)) - .Times(1); - - FlushServer(); -} - -// Test the return wire for device error callbacks -TEST_F(WireSetCallbackTests, BuilderErrorCallback) { - uint64_t userdata1 = 982734; - uint64_t userdata2 = 982734239028; - - // Create the buffer builder, the callback is set immediately on the server side - dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); - - dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); - EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) - .WillOnce(Return(apiBufferBuilder)); - - EXPECT_CALL(api, OnBuilderSetErrorCallback(apiBufferBuilder, _, _, _)) - .Times(1); - - FlushClient(); - - // Setting the callback on the client side doesn't do anything on the server side - dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, userdata1, userdata2); - FlushClient(); - - // Create an object so that it is a valid case to call the error callback - dawnBufferBuilderGetResult(bufferBuilder); - - dawnBuffer apiBuffer = api.GetNewBuffer(); - EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) - .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { - api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "Success!"); - return apiBuffer; - })); - - EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); - EXPECT_CALL(api, BufferRelease(apiBuffer)); - FlushClient(); - - // The error callback gets called on the client side - EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, StrEq("Success!"), userdata1, userdata2)) - .Times(1); - - FlushServer(); -} - -class WireBufferMappingTests : public WireTestsBase { - public: - WireBufferMappingTests() : WireTestsBase(true) { - } - - void SetUp() override { - WireTestsBase::SetUp(); - - { - dawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - - apiBuffer = api.GetNewBuffer(); - buffer = dawnDeviceCreateBuffer(device, &descriptor); - - EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) - .WillOnce(Return(apiBuffer)) - .RetiresOnSaturation(); - EXPECT_CALL(api, BufferRelease(apiBuffer)); - FlushClient(); - } - { - dawnBufferDescriptor descriptor; - descriptor.nextInChain = nullptr; - - errorBuffer = dawnDeviceCreateBuffer(device, &descriptor); - - EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) - .WillOnce(Return(nullptr)) - .RetiresOnSaturation(); - FlushClient(); - } - } - - protected: - // A successfully created buffer - dawnBuffer buffer; - dawnBuffer apiBuffer; - - // An buffer that wasn't created on the server side - dawnBuffer errorBuffer; -}; - -// MapRead-specific tests - -// Check mapping for reading a succesfully created buffer -TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) { - dawnCallbackUserdata userdata = 8653; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) - .Times(1); - - FlushServer(); - - dawnBufferUnmap(buffer); - EXPECT_CALL(api, BufferUnmap(apiBuffer)) - .Times(1); - - FlushClient(); -} - -// Check that things work correctly when a validation error happens when mapping the buffer for reading -TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) { - dawnCallbackUserdata userdata = 8654; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); -} - -// Check mapping for reading a buffer that didn't get created on the server side -TEST_F(WireBufferMappingTests, MappingForReadErrorBuffer) { - dawnCallbackUserdata userdata = 8655; - dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); - - dawnBufferUnmap(errorBuffer); - - FlushClient(); -} - -// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the request is finished -TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) { - dawnCallbackUserdata userdata = 8656; - dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) - .Times(1); - - dawnBufferRelease(errorBuffer); -} - -// Check the map read callback is called with UNKNOWN when the map request would have worked, but Unmap was called -TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) { - dawnCallbackUserdata userdata = 8657; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - // Oh no! We are calling Unmap too early! - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) - .Times(1); - dawnBufferUnmap(buffer); - - // The callback shouldn't get called, even when the request succeeded on the server side - FlushServer(); -} - -// Check that an error map read callback gets nullptr while a buffer is already mapped -TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) { - // Successful map - dawnCallbackUserdata userdata = 34098; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })) - .RetiresOnSaturation(); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) - .Times(1); - - FlushServer(); - - // Map failure while the buffer is already mapped - userdata ++; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); -} - -// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback -TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) { - dawnCallbackUserdata userdata = 2039; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) - .WillOnce(InvokeWithoutArgs([&]() { - dawnBufferUnmap(buffer); - })); - - FlushServer(); - - EXPECT_CALL(api, BufferUnmap(apiBuffer)) - .Times(1); - - FlushClient(); -} - -// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the callback -TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) { - dawnCallbackUserdata userdata = 2039; - dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) - .WillOnce(InvokeWithoutArgs([&]() { - dawnBufferRelease(buffer); - })); - - FlushServer(); - - FlushClient(); -} - -// MapWrite-specific tests - -// Check mapping for writing a succesfully created buffer -TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) { - dawnCallbackUserdata userdata = 8653; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - uint32_t serverBufferContent = 31337; - uint32_t updatedContent = 4242; - uint32_t zero = 0; - - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &serverBufferContent); - })); - - FlushClient(); - - // The map write callback always gets a buffer full of zeroes. - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) - .Times(1); - - FlushServer(); - - // Write something to the mapped pointer - *lastMapWritePointer = updatedContent; - - dawnBufferUnmap(buffer); - EXPECT_CALL(api, BufferUnmap(apiBuffer)) - .Times(1); - - FlushClient(); - - // After the buffer is unmapped, the content of the buffer is updated on the server - ASSERT_EQ(serverBufferContent, updatedContent); -} - -// Check that things work correctly when a validation error happens when mapping the buffer for writing -TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) { - dawnCallbackUserdata userdata = 8654; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); -} - -// Check mapping for writing a buffer that didn't get created on the server side -TEST_F(WireBufferMappingTests, MappingForWriteErrorBuffer) { - dawnCallbackUserdata userdata = 8655; - dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); - - dawnBufferUnmap(errorBuffer); - - FlushClient(); -} - -// Check that the map write callback is called with UNKNOWN when the buffer is destroyed before the request is finished -TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) { - dawnCallbackUserdata userdata = 8656; - dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) - .Times(1); - - dawnBufferRelease(errorBuffer); -} - -// Check the map read callback is called with UNKNOWN when the map request would have worked, but Unmap was called -TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) { - dawnCallbackUserdata userdata = 8657; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - uint32_t bufferContent = 31337; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - // Oh no! We are calling Unmap too early! - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) - .Times(1); - dawnBufferUnmap(buffer); - - // The callback shouldn't get called, even when the request succeeded on the server side - FlushServer(); -} - -// Check that an error map read callback gets nullptr while a buffer is already mapped -TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) { - // Successful map - dawnCallbackUserdata userdata = 34098; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - uint32_t bufferContent = 31337; - uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })) - .RetiresOnSaturation(); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) - .Times(1); - - FlushServer(); - - // Map failure while the buffer is already mapped - userdata ++; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) - .Times(1); - - FlushServer(); -} - -// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback -TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) { - dawnCallbackUserdata userdata = 2039; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - uint32_t bufferContent = 31337; - uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) - .WillOnce(InvokeWithoutArgs([&]() { - dawnBufferUnmap(buffer); - })); - - FlushServer(); - - EXPECT_CALL(api, BufferUnmap(apiBuffer)) - .Times(1); - - FlushClient(); -} - -// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the callback -TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) { - dawnCallbackUserdata userdata = 2039; - dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); - - uint32_t bufferContent = 31337; - uint32_t zero = 0; - EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) - .WillOnce(InvokeWithoutArgs([&]() { - api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent); - })); - - FlushClient(); - - EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) - .WillOnce(InvokeWithoutArgs([&]() { - dawnBufferRelease(buffer); - })); - - FlushServer(); - - FlushClient(); -} - -class WireFenceTests : public WireTestsBase { - public: - WireFenceTests() : WireTestsBase(true) { - } - - void SetUp() override { - WireTestsBase::SetUp(); - - { - dawnFenceDescriptor descriptor; - descriptor.initialValue = 1; - descriptor.nextInChain = nullptr; - - apiFence = api.GetNewFence(); - fence = dawnDeviceCreateFence(device, &descriptor); - - EXPECT_CALL(api, DeviceCreateFence(apiDevice, _)).WillOnce(Return(apiFence)); - EXPECT_CALL(api, FenceRelease(apiFence)); - FlushClient(); - } - { - queue = dawnDeviceCreateQueue(device); - apiQueue = api.GetNewQueue(); - EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); - EXPECT_CALL(api, QueueRelease(apiQueue)); - FlushClient(); - } - } - - protected: - void DoQueueSignal(uint64_t signalValue) { - dawnQueueSignal(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, DAWN_FENCE_COMPLETION_STATUS_SUCCESS); - })); - } - - // A successfully created fence - dawnFence fence; - dawnFence apiFence; - - dawnQueue queue; - dawnQueue apiQueue; -}; - -// Check that signaling a fence succeeds -TEST_F(WireFenceTests, QueueSignalSuccess) { - DoQueueSignal(2u); - DoQueueSignal(3u); - FlushClient(); - FlushServer(); -} - -// Without any flushes, it is valid to signal a value greater than the current -// signaled value -TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) { - dawnCallbackUserdata userdata = 9157; - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0); - - dawnQueueSignal(queue, fence, 2u); - dawnQueueSignal(queue, fence, 4u); - dawnQueueSignal(queue, fence, 5u); -} - -// Without any flushes, errors should be generated when signaling a value less -// than or equal to the current signaled value -TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) { - dawnCallbackUserdata userdata = 3157; - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); - dawnQueueSignal(queue, fence, 0u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); - dawnQueueSignal(queue, fence, 1u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0); - dawnQueueSignal(queue, fence, 4u); // Success - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); - - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); - dawnQueueSignal(queue, fence, 3u); // Error - EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); -} - -// 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 - { - dawnCallbackUserdata userdata = 9847; - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata)) - .Times(1); - dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, userdata); - } - - // Can call on value == (initial) signaled value happens immediately - { - dawnCallbackUserdata userdata = 4347; - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata)) - .Times(1); - dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, userdata); - } -} - -// Check that all passed client completion callbacks are called -TEST_F(WireFenceTests, OnCompletionMultiple) { - DoQueueSignal(3u); - DoQueueSignal(6u); - - dawnCallbackUserdata userdata0 = 2134; - dawnCallbackUserdata userdata1 = 7134; - dawnCallbackUserdata userdata2 = 3144; - dawnCallbackUserdata userdata3 = 1130; - - // 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. - dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, userdata0); - dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata1); - dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, userdata2); - dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata3); - - Sequence s1, s2; - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1)) - .Times(1) - .InSequence(s1); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3)) - .Times(1) - .InSequence(s2); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2)) - .Times(1) - .InSequence(s1, s2); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0)) - .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) { - dawnQueueSignal(queue, fence, 4u); - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0); - dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0); - dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0); -} - -// Without any flushes, errors should be generated when waiting on a value greater -// than the last signaled value -TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) { - dawnCallbackUserdata userdata1 = 3817; - dawnCallbackUserdata userdata2 = 3857; - dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata2); - - EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata1)) - .Times(1); - EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata2)).Times(1); - - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata1); -} - -// Check that the fence completed value is initialized -TEST_F(WireFenceTests, GetCompletedValueInitialization) { - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u); -} - -// Check that the fence completed value updates after signaling the fence -TEST_F(WireFenceTests, GetCompletedValueUpdate) { - DoQueueSignal(3u); - FlushClient(); - FlushServer(); - - EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u); -} - -// Check that the fence completed value does not update without a flush -TEST_F(WireFenceTests, GetCompletedValueNoUpdate) { - dawnQueueSignal(queue, fence, 3u); - EXPECT_EQ(dawnFenceGetCompletedValue(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) { - dawnCallbackUserdata userdata = 8616; - dawnQueueSignal(queue, fence, 3u); - dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata); - EXPECT_CALL(*mockFenceOnCompletionCallback, - Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata)) - .Times(1); - - dawnFenceRelease(fence); -} diff --git a/src/tests/unittests/wire/WireArgumentTests.cpp b/src/tests/unittests/wire/WireArgumentTests.cpp new file mode 100644 index 0000000000..613c0c1fd7 --- /dev/null +++ b/src/tests/unittests/wire/WireArgumentTests.cpp @@ -0,0 +1,382 @@ +// 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" + +#include "common/Constants.h" + +using namespace testing; +using namespace dawn_wire; + +class WireArgumentTests : public WireTest { + public: + WireArgumentTests() : WireTest(true) { + } + ~WireArgumentTests() override = default; +}; + +// Test that the wire is able to send numerical values +TEST_F(WireArgumentTests, ValueArgument) { + dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); + dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder); + dawnComputePassEncoderDispatch(pass, 1, 2, 3); + + dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)).WillOnce(Return(apiBuilder)); + + dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); + EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)).WillOnce(Return(apiPass)); + + EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)).Times(1); + + EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder)); + EXPECT_CALL(api, ComputePassEncoderRelease(apiPass)); + FlushClient(); +} + +// Test that the wire is able to send arrays of numerical values +static constexpr uint32_t testPushConstantValues[4] = {0, 42, 0xDEADBEEFu, 0xFFFFFFFFu}; + +bool CheckPushConstantValues(const uint32_t* values) { + for (int i = 0; i < 4; ++i) { + if (values[i] != testPushConstantValues[i]) { + return false; + } + } + return true; +} + +TEST_F(WireArgumentTests, ValueArrayArgument) { + dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); + dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder); + dawnComputePassEncoderSetPushConstants(pass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, + testPushConstantValues); + + dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)).WillOnce(Return(apiBuilder)); + + dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder(); + EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)).WillOnce(Return(apiPass)); + + EXPECT_CALL(api, + ComputePassEncoderSetPushConstants(apiPass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, + ResultOf(CheckPushConstantValues, Eq(true)))); + EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder)); + EXPECT_CALL(api, ComputePassEncoderRelease(apiPass)); + + FlushClient(); +} + +// Test that the wire is able to send C strings +TEST_F(WireArgumentTests, CStringArgument) { + // Create shader module + dawnShaderModuleDescriptor vertexDescriptor; + vertexDescriptor.nextInChain = nullptr; + vertexDescriptor.codeSize = 0; + dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); + dawnShaderModule apiVsModule = api.GetNewShaderModule(); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); + + // Create the blend state descriptor + dawnBlendDescriptor blendDescriptor; + blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; + blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; + blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; + dawnBlendStateDescriptor blendStateDescriptor; + blendStateDescriptor.nextInChain = nullptr; + blendStateDescriptor.alphaBlend = blendDescriptor; + blendStateDescriptor.colorBlend = blendDescriptor; + blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL; + + // Create the input state + dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device); + dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder(); + EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice)) + .WillOnce(Return(apiInputStateBuilder)); + + dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder); + dawnInputState apiInputState = api.GetNewInputState(); + EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder)) + .WillOnce(Return(apiInputState)); + + // Create the depth-stencil state + dawnStencilStateFaceDescriptor stencilFace; + stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; + stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; + stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; + stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; + + dawnDepthStencilStateDescriptor depthStencilState; + depthStencilState.nextInChain = nullptr; + depthStencilState.depthWriteEnabled = false; + depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; + depthStencilState.stencilBack = stencilFace; + depthStencilState.stencilFront = stencilFace; + depthStencilState.stencilReadMask = 0xff; + depthStencilState.stencilWriteMask = 0xff; + + // Create the pipeline layout + dawnPipelineLayoutDescriptor layoutDescriptor; + layoutDescriptor.nextInChain = nullptr; + layoutDescriptor.numBindGroupLayouts = 0; + layoutDescriptor.bindGroupLayouts = nullptr; + dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); + dawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); + EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout)); + + // Create pipeline + dawnRenderPipelineDescriptor pipelineDescriptor; + pipelineDescriptor.nextInChain = nullptr; + + dawnPipelineStageDescriptor vertexStage; + vertexStage.nextInChain = nullptr; + vertexStage.module = vsModule; + vertexStage.entryPoint = "main"; + pipelineDescriptor.vertexStage = &vertexStage; + + dawnPipelineStageDescriptor fragmentStage; + fragmentStage.nextInChain = nullptr; + fragmentStage.module = vsModule; + fragmentStage.entryPoint = "main"; + pipelineDescriptor.fragmentStage = &fragmentStage; + + dawnAttachmentsStateDescriptor attachmentsState; + attachmentsState.nextInChain = nullptr; + attachmentsState.numColorAttachments = 1; + dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM}; + dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment}; + attachmentsState.colorAttachments = colorAttachmentPtr; + attachmentsState.hasDepthStencilAttachment = false; + // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid + // data because we don't have optional substructures yet. + attachmentsState.depthStencilAttachment = &colorAttachment; + pipelineDescriptor.attachmentsState = &attachmentsState; + + pipelineDescriptor.numBlendStates = 1; + pipelineDescriptor.blendStates = &blendStateDescriptor; + + pipelineDescriptor.sampleCount = 1; + pipelineDescriptor.layout = layout; + pipelineDescriptor.inputState = inputState; + pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32; + pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + pipelineDescriptor.depthStencilState = &depthStencilState; + + dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + EXPECT_CALL(api, + DeviceCreateRenderPipeline( + apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { + return desc->vertexStage->entryPoint == std::string("main"); + }))) + .WillOnce(Return(nullptr)); + EXPECT_CALL(api, ShaderModuleRelease(apiVsModule)); + EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder)); + EXPECT_CALL(api, InputStateRelease(apiInputState)); + EXPECT_CALL(api, PipelineLayoutRelease(apiLayout)); + + FlushClient(); +} + +// Test that the wire is able to send objects as value arguments +TEST_F(WireArgumentTests, ObjectAsValueArgument) { + // Create a RenderPassDescriptor + dawnRenderPassDescriptorBuilder renderPassBuilder = + dawnDeviceCreateRenderPassDescriptorBuilder(device); + dawnRenderPassDescriptor renderPass = + dawnRenderPassDescriptorBuilderGetResult(renderPassBuilder); + + dawnRenderPassDescriptorBuilder apiRenderPassBuilder = api.GetNewRenderPassDescriptorBuilder(); + EXPECT_CALL(api, DeviceCreateRenderPassDescriptorBuilder(apiDevice)) + .WillOnce(Return(apiRenderPassBuilder)); + dawnRenderPassDescriptor apiRenderPass = api.GetNewRenderPassDescriptor(); + EXPECT_CALL(api, RenderPassDescriptorBuilderGetResult(apiRenderPassBuilder)) + .WillOnce(Return(apiRenderPass)); + + // Create command buffer builder, setting render pass descriptor + dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); + dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, renderPass); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + + EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, apiRenderPass)).Times(1); + + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + EXPECT_CALL(api, RenderPassDescriptorBuilderRelease(apiRenderPassBuilder)); + EXPECT_CALL(api, RenderPassDescriptorRelease(apiRenderPass)); + FlushClient(); +} + +// Test that the wire is able to send array of objects +TEST_F(WireArgumentTests, ObjectsAsPointerArgument) { + dawnCommandBuffer cmdBufs[2]; + dawnCommandBuffer apiCmdBufs[2]; + + // Create two command buffers we need to use a GMock sequence otherwise the order of the + // CreateCommandBufferBuilder might be swapped since they are equivalent in term of matchers + Sequence s; + for (int i = 0; i < 2; ++i) { + dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); + cmdBufs[i] = dawnCommandBufferBuilderGetResult(cmdBufBuilder); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .InSequence(s) + .WillOnce(Return(apiCmdBufBuilder)); + + apiCmdBufs[i] = api.GetNewCommandBuffer(); + EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder)) + .WillOnce(Return(apiCmdBufs[i])); + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + EXPECT_CALL(api, CommandBufferRelease(apiCmdBufs[i])); + } + + // Create queue + dawnQueue queue = dawnDeviceCreateQueue(device); + dawnQueue apiQueue = api.GetNewQueue(); + EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); + + // Submit command buffer and check we got a call with both API-side command buffers + dawnQueueSubmit(queue, 2, cmdBufs); + + EXPECT_CALL( + api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const dawnCommandBuffer* cmdBufs) -> bool { + return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1]; + }))); + + EXPECT_CALL(api, QueueRelease(apiQueue)); + FlushClient(); +} + +// Test that the wire is able to send structures that contain pure values (non-objects) +TEST_F(WireArgumentTests, StructureOfValuesArgument) { + dawnSamplerDescriptor descriptor; + descriptor.nextInChain = nullptr; + descriptor.magFilter = DAWN_FILTER_MODE_LINEAR; + descriptor.minFilter = DAWN_FILTER_MODE_NEAREST; + descriptor.mipmapFilter = DAWN_FILTER_MODE_LINEAR; + descriptor.addressModeU = DAWN_ADDRESS_MODE_CLAMP_TO_EDGE; + descriptor.addressModeV = DAWN_ADDRESS_MODE_REPEAT; + descriptor.addressModeW = DAWN_ADDRESS_MODE_MIRRORED_REPEAT; + descriptor.lodMinClamp = kLodMin; + descriptor.lodMaxClamp = kLodMax; + descriptor.compareFunction = DAWN_COMPARE_FUNCTION_NEVER; + descriptor.borderColor = DAWN_BORDER_COLOR_TRANSPARENT_BLACK; + + dawnDeviceCreateSampler(device, &descriptor); + EXPECT_CALL(api, DeviceCreateSampler( + apiDevice, MatchesLambda([](const dawnSamplerDescriptor* desc) -> bool { + return desc->nextInChain == nullptr && + desc->magFilter == DAWN_FILTER_MODE_LINEAR && + desc->minFilter == DAWN_FILTER_MODE_NEAREST && + desc->mipmapFilter == DAWN_FILTER_MODE_LINEAR && + desc->addressModeU == DAWN_ADDRESS_MODE_CLAMP_TO_EDGE && + desc->addressModeV == DAWN_ADDRESS_MODE_REPEAT && + desc->addressModeW == DAWN_ADDRESS_MODE_MIRRORED_REPEAT && + desc->compareFunction == DAWN_COMPARE_FUNCTION_NEVER && + desc->borderColor == DAWN_BORDER_COLOR_TRANSPARENT_BLACK && + desc->lodMinClamp == kLodMin && desc->lodMaxClamp == kLodMax; + }))) + .WillOnce(Return(nullptr)); + + FlushClient(); +} + +// Test that the wire is able to send structures that contain objects +TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) { + dawnBindGroupLayoutDescriptor bglDescriptor; + bglDescriptor.numBindings = 0; + bglDescriptor.bindings = nullptr; + + dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); + dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); + EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl)); + + dawnPipelineLayoutDescriptor descriptor; + descriptor.nextInChain = nullptr; + descriptor.numBindGroupLayouts = 1; + descriptor.bindGroupLayouts = &bgl; + + dawnDeviceCreatePipelineLayout(device, &descriptor); + EXPECT_CALL(api, DeviceCreatePipelineLayout( + apiDevice, + MatchesLambda([apiBgl](const dawnPipelineLayoutDescriptor* desc) -> bool { + return desc->nextInChain == nullptr && + desc->numBindGroupLayouts == 1 && + desc->bindGroupLayouts[0] == apiBgl; + }))) + .WillOnce(Return(nullptr)); + + EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl)); + FlushClient(); +} + +// Test that the wire is able to send structures that contain objects +TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) { + static constexpr int NUM_BINDINGS = 3; + dawnBindGroupLayoutBinding bindings[NUM_BINDINGS]{ + {0, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLER}, + {1, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLED_TEXTURE}, + {2, + static_cast(DAWN_SHADER_STAGE_BIT_VERTEX | + DAWN_SHADER_STAGE_BIT_FRAGMENT), + DAWN_BINDING_TYPE_UNIFORM_BUFFER}, + }; + dawnBindGroupLayoutDescriptor bglDescriptor; + bglDescriptor.numBindings = NUM_BINDINGS; + bglDescriptor.bindings = bindings; + + dawnDeviceCreateBindGroupLayout(device, &bglDescriptor); + dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout(); + EXPECT_CALL( + api, + DeviceCreateBindGroupLayout( + apiDevice, MatchesLambda([bindings](const dawnBindGroupLayoutDescriptor* desc) -> bool { + for (int i = 0; i < NUM_BINDINGS; ++i) { + const auto& a = desc->bindings[i]; + const auto& b = bindings[i]; + if (a.binding != b.binding || a.visibility != b.visibility || + a.type != b.type) { + return false; + } + } + return desc->nextInChain == nullptr && desc->numBindings == 3; + }))) + .WillOnce(Return(apiBgl)); + + EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl)); + FlushClient(); +} + +// Test passing nullptr instead of objects - array of objects version +TEST_F(WireArgumentTests, DISABLED_NullptrInArray) { + dawnBindGroupLayout nullBGL = nullptr; + + dawnPipelineLayoutDescriptor descriptor; + descriptor.nextInChain = nullptr; + descriptor.numBindGroupLayouts = 1; + descriptor.bindGroupLayouts = &nullBGL; + + dawnDeviceCreatePipelineLayout(device, &descriptor); + EXPECT_CALL(api, + DeviceCreatePipelineLayout( + apiDevice, MatchesLambda([](const dawnPipelineLayoutDescriptor* desc) -> bool { + return desc->nextInChain == nullptr && desc->numBindGroupLayouts == 1 && + desc->bindGroupLayouts[0] == nullptr; + }))) + .WillOnce(Return(nullptr)); + + FlushClient(); +} diff --git a/src/tests/unittests/wire/WireBasicTests.cpp b/src/tests/unittests/wire/WireBasicTests.cpp new file mode 100644 index 0000000000..bb214b3184 --- /dev/null +++ b/src/tests/unittests/wire/WireBasicTests.cpp @@ -0,0 +1,84 @@ +// 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; + +class WireBasicTests : public WireTest { + public: + WireBasicTests() : WireTest(true) { + } + ~WireBasicTests() override = default; +}; + +// One call gets forwarded correctly. +TEST_F(WireBasicTests, CallForwarded) { + dawnDeviceCreateCommandBufferBuilder(device); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + FlushClient(); +} + +// Test that calling methods on a new object works as expected. +TEST_F(WireBasicTests, CreateThenCall) { + dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); + dawnCommandBufferBuilderGetResult(builder); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + + dawnCommandBuffer apiCmdBuf = api.GetNewCommandBuffer(); + EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder)).WillOnce(Return(apiCmdBuf)); + + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + EXPECT_CALL(api, CommandBufferRelease(apiCmdBuf)); + FlushClient(); +} + +// Test that client reference/release do not call the backend API. +TEST_F(WireBasicTests, RefCountKeptInClient) { + dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); + + dawnCommandBufferBuilderReference(builder); + dawnCommandBufferBuilderRelease(builder); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + + FlushClient(); +} + +// Test that client reference/release do not call the backend API. +TEST_F(WireBasicTests, ReleaseCalledOnRefCount0) { + dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device); + + dawnCommandBufferBuilderRelease(builder); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + + EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder)); + + FlushClient(); +} diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp new file mode 100644 index 0000000000..d4dcf02efc --- /dev/null +++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp @@ -0,0 +1,513 @@ +// 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 { + + // Mock classes to add expectations on the wire calling callbacks + class MockBufferMapReadCallback { + public: + MOCK_METHOD3(Call, + void(dawnBufferMapAsyncStatus status, + const uint32_t* ptr, + dawnCallbackUserdata userdata)); + }; + + std::unique_ptr mockBufferMapReadCallback; + void ToMockBufferMapReadCallback(dawnBufferMapAsyncStatus status, + const void* ptr, + dawnCallbackUserdata userdata) { + // Assume the data is uint32_t to make writing matchers easier + mockBufferMapReadCallback->Call(status, static_cast(ptr), userdata); + } + + class MockBufferMapWriteCallback { + public: + MOCK_METHOD3(Call, + void(dawnBufferMapAsyncStatus status, + uint32_t* ptr, + dawnCallbackUserdata userdata)); + }; + + std::unique_ptr mockBufferMapWriteCallback; + uint32_t* lastMapWritePointer = nullptr; + void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status, + void* ptr, + dawnCallbackUserdata userdata) { + // Assume the data is uint32_t to make writing matchers easier + lastMapWritePointer = static_cast(ptr); + mockBufferMapWriteCallback->Call(status, lastMapWritePointer, userdata); + } + +} // anonymous namespace + +class WireBufferMappingTests : public WireTest { + public: + WireBufferMappingTests() : WireTest(true) { + } + ~WireBufferMappingTests() override = default; + + void SetUp() override { + WireTest::SetUp(); + + mockBufferMapReadCallback = std::make_unique(); + mockBufferMapWriteCallback = std::make_unique(); + + { + dawnBufferDescriptor descriptor; + descriptor.nextInChain = nullptr; + + apiBuffer = api.GetNewBuffer(); + buffer = dawnDeviceCreateBuffer(device, &descriptor); + + EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) + .WillOnce(Return(apiBuffer)) + .RetiresOnSaturation(); + EXPECT_CALL(api, BufferRelease(apiBuffer)); + FlushClient(); + } + { + dawnBufferDescriptor descriptor; + descriptor.nextInChain = nullptr; + + errorBuffer = dawnDeviceCreateBuffer(device, &descriptor); + + EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) + .WillOnce(Return(nullptr)) + .RetiresOnSaturation(); + FlushClient(); + } + } + + void TearDown() override { + WireTest::TearDown(); + + // Delete mocks so that expectations are checked + mockBufferMapReadCallback = nullptr; + mockBufferMapWriteCallback = nullptr; + } + + protected: + // A successfully created buffer + dawnBuffer buffer; + dawnBuffer apiBuffer; + + // An buffer that wasn't created on the server side + dawnBuffer errorBuffer; +}; + +// MapRead-specific tests + +// Check mapping for reading a succesfully created buffer +TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) { + dawnCallbackUserdata userdata = 8653; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) + .Times(1); + + FlushServer(); + + dawnBufferUnmap(buffer); + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Check that things work correctly when a validation error happens when mapping the buffer for +// reading +TEST_F(WireBufferMappingTests, ErrorWhileMappingForRead) { + dawnCallbackUserdata userdata = 8654; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); +} + +// Check mapping for reading a buffer that didn't get created on the server side +TEST_F(WireBufferMappingTests, MappingForReadErrorBuffer) { + dawnCallbackUserdata userdata = 8655; + dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, + userdata); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); + + dawnBufferUnmap(errorBuffer); + + FlushClient(); +} + +// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the +// request is finished +TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) { + dawnCallbackUserdata userdata = 8656; + dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, + userdata); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) + .Times(1); + + dawnBufferRelease(errorBuffer); +} + +// Check the map read callback is called with UNKNOWN when the map request would have worked, but +// Unmap was called +TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) { + dawnCallbackUserdata userdata = 8657; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + // Oh no! We are calling Unmap too early! + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) + .Times(1); + dawnBufferUnmap(buffer); + + // The callback shouldn't get called, even when the request succeeded on the server side + FlushServer(); +} + +// Check that an error map read callback gets nullptr while a buffer is already mapped +TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) { + // Successful map + dawnCallbackUserdata userdata = 34098; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })) + .RetiresOnSaturation(); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) + .Times(1); + + FlushServer(); + + // Map failure while the buffer is already mapped + userdata++; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); +} + +// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback +TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) { + dawnCallbackUserdata userdata = 2039; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) + .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(buffer); })); + + FlushServer(); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the +// callback +TEST_F(WireBufferMappingTests, DestroyInsideMapReadCallback) { + dawnCallbackUserdata userdata = 2039; + dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapReadCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata)) + .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); })); + + FlushServer(); + + FlushClient(); +} + +// MapWrite-specific tests + +// Check mapping for writing a succesfully created buffer +TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) { + dawnCallbackUserdata userdata = 8653; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + uint32_t serverBufferContent = 31337; + uint32_t updatedContent = 4242; + uint32_t zero = 0; + + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &serverBufferContent); + })); + + FlushClient(); + + // The map write callback always gets a buffer full of zeroes. + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) + .Times(1); + + FlushServer(); + + // Write something to the mapped pointer + *lastMapWritePointer = updatedContent; + + dawnBufferUnmap(buffer); + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); + + // After the buffer is unmapped, the content of the buffer is updated on the server + ASSERT_EQ(serverBufferContent, updatedContent); +} + +// Check that things work correctly when a validation error happens when mapping the buffer for +// writing +TEST_F(WireBufferMappingTests, ErrorWhileMappingForWrite) { + dawnCallbackUserdata userdata = 8654; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); +} + +// Check mapping for writing a buffer that didn't get created on the server side +TEST_F(WireBufferMappingTests, MappingForWriteErrorBuffer) { + dawnCallbackUserdata userdata = 8655; + dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, + userdata); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); + + dawnBufferUnmap(errorBuffer); + + FlushClient(); +} + +// Check that the map write callback is called with UNKNOWN when the buffer is destroyed before the +// request is finished +TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) { + dawnCallbackUserdata userdata = 8656; + dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, + userdata); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) + .Times(1); + + dawnBufferRelease(errorBuffer); +} + +// Check the map read callback is called with UNKNOWN when the map request would have worked, but +// Unmap was called +TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) { + dawnCallbackUserdata userdata = 8657; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + uint32_t bufferContent = 31337; + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + // Oh no! We are calling Unmap too early! + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata)) + .Times(1); + dawnBufferUnmap(buffer); + + // The callback shouldn't get called, even when the request succeeded on the server side + FlushServer(); +} + +// Check that an error map read callback gets nullptr while a buffer is already mapped +TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) { + // Successful map + dawnCallbackUserdata userdata = 34098; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + uint32_t bufferContent = 31337; + uint32_t zero = 0; + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })) + .RetiresOnSaturation(); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) + .Times(1); + + FlushServer(); + + // Map failure while the buffer is already mapped + userdata++; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata)) + .Times(1); + + FlushServer(); +} + +// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback +TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) { + dawnCallbackUserdata userdata = 2039; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + uint32_t bufferContent = 31337; + uint32_t zero = 0; + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) + .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(buffer); })); + + FlushServer(); + + EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); + + FlushClient(); +} + +// Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the +// callback +TEST_F(WireBufferMappingTests, DestroyInsideMapWriteCallback) { + dawnCallbackUserdata userdata = 2039; + dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata); + + uint32_t bufferContent = 31337; + uint32_t zero = 0; + EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, + &bufferContent); + })); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapWriteCallback, + Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata)) + .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); })); + + FlushServer(); + + FlushClient(); +} diff --git a/src/tests/unittests/wire/WireCallbackTests.cpp b/src/tests/unittests/wire/WireCallbackTests.cpp new file mode 100644 index 0000000000..64b68ac10e --- /dev/null +++ b/src/tests/unittests/wire/WireCallbackTests.cpp @@ -0,0 +1,305 @@ +// 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 { + + // Mock classes to add expectations on the wire calling callbacks + class MockDeviceErrorCallback { + public: + MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata)); + }; + + std::unique_ptr mockDeviceErrorCallback; + void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) { + mockDeviceErrorCallback->Call(message, userdata); + } + + class MockBuilderErrorCallback { + public: + MOCK_METHOD4(Call, + void(dawnBuilderErrorStatus status, + const char* message, + dawnCallbackUserdata userdata1, + dawnCallbackUserdata userdata2)); + }; + + std::unique_ptr mockBuilderErrorCallback; + void ToMockBuilderErrorCallback(dawnBuilderErrorStatus status, + const char* message, + dawnCallbackUserdata userdata1, + dawnCallbackUserdata userdata2) { + mockBuilderErrorCallback->Call(status, message, userdata1, userdata2); + } + +} // anonymous namespace + +class WireCallbackTests : public WireTest { + public: + WireCallbackTests() : WireTest(true) { + } + ~WireCallbackTests() override = default; + + void SetUp() override { + WireTest::SetUp(); + + mockDeviceErrorCallback = std::make_unique(); + mockBuilderErrorCallback = std::make_unique(); + } + + void TearDown() override { + WireTest::TearDown(); + + // Delete mocks so that expectations are checked + mockDeviceErrorCallback = nullptr; + mockBuilderErrorCallback = nullptr; + } +}; + +// Test that we get a success builder error status when no error happens +TEST_F(WireCallbackTests, SuccessCallbackOnBuilderSuccess) { + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2); + dawnBufferBuilderGetResult(bufferBuilder); + + dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); + EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) + .WillOnce(Return(apiBufferBuilder)); + + dawnBuffer apiBuffer = api.GetNewBuffer(); + EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) + .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { + api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, + "I like cheese"); + return apiBuffer; + })); + + EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); + EXPECT_CALL(api, BufferRelease(apiBuffer)); + FlushClient(); + + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, _, 1, 2)); + + FlushServer(); +} + +// Test that the client calls the builder callback with unknown when it HAS to fire the callback but +// can't know the status yet. +TEST_F(WireCallbackTests, UnknownBuilderErrorStatusCallback) { + // The builder is destroyed before the object is built + { + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2); + + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 1, 2)) + .Times(1); + + dawnBufferBuilderRelease(bufferBuilder); + } + + // If the builder has been consumed, it doesn't fire the callback with unknown + { + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4); + dawnBufferBuilderGetResult(bufferBuilder); + + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 3, 4)) + .Times(0); + + dawnBufferBuilderRelease(bufferBuilder); + } + + // If the builder has been consumed, and the object is destroyed before the result comes from + // the server, then the callback is fired with unknown + { + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 5, 6); + dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder); + + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 5, 6)) + .Times(1); + + dawnBufferRelease(buffer); + } +} + +// Test that a builder success status doesn't get forwarded to the device +TEST_F(WireCallbackTests, SuccessCallbackNotForwardedToDevice) { + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, 0); + + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderGetResult(bufferBuilder); + + dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); + EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) + .WillOnce(Return(apiBufferBuilder)); + + dawnBuffer apiBuffer = api.GetNewBuffer(); + EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) + .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { + api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, + "I like cheese"); + return apiBuffer; + })); + + EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); + EXPECT_CALL(api, BufferRelease(apiBuffer)); + FlushClient(); + FlushServer(); +} + +// Test that a builder error status gets forwarded to the device +TEST_F(WireCallbackTests, ErrorCallbackForwardedToDevice) { + uint64_t userdata = 30495; + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); + + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderGetResult(bufferBuilder); + + dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); + EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) + .WillOnce(Return(apiBufferBuilder)); + + EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) + .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { + api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, + "Error :("); + return nullptr; + })); + + EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); + FlushClient(); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); + + FlushServer(); +} + +// Test the return wire for device error callbacks +TEST_F(WireCallbackTests, DeviceErrorCallback) { + uint64_t userdata = 3049785; + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); + + // Setting the error callback should stay on the client side and do nothing + FlushClient(); + + // Calling the callback on the server side will result in the callback being called on the + // client side + api.CallDeviceErrorCallback(apiDevice, "Some error message"); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), userdata)).Times(1); + + FlushServer(); +} + +// Test the return wire for device error callbacks +TEST_F(WireCallbackTests, BuilderErrorCallback) { + uint64_t userdata1 = 982734; + uint64_t userdata2 = 982734239028; + + // Create the buffer builder, the callback is set immediately on the server side + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + + dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); + EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) + .WillOnce(Return(apiBufferBuilder)); + + EXPECT_CALL(api, OnBuilderSetErrorCallback(apiBufferBuilder, _, _, _)).Times(1); + + FlushClient(); + + // Setting the callback on the client side doesn't do anything on the server side + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, userdata1, + userdata2); + FlushClient(); + + // Create an object so that it is a valid case to call the error callback + dawnBufferBuilderGetResult(bufferBuilder); + + dawnBuffer apiBuffer = api.GetNewBuffer(); + EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) + .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { + api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, + "Success!"); + return apiBuffer; + })); + + EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder)); + EXPECT_CALL(api, BufferRelease(apiBuffer)); + FlushClient(); + + // The error callback gets called on the client side + EXPECT_CALL(*mockBuilderErrorCallback, + Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, StrEq("Success!"), userdata1, userdata2)) + .Times(1); + + FlushServer(); +} + +// Test that the server doesn't forward calls to error objects or with error objects +// Also test that when GetResult is called on an error builder, the error callback is fired +// TODO(cwallez@chromium.org): This test is disabled because the introduction of encoders breaks +// the assumptions of the "builder error" handling that a builder is self-contained. We need to +// revisit this once the new error handling is in place. +TEST_F(WireCallbackTests, DISABLED_CallsSkippedAfterBuilderError) { + dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device); + dawnCommandBufferBuilderSetErrorCallback(cmdBufBuilder, ToMockBuilderErrorCallback, 1, 2); + + dawnRenderPassEncoder pass = dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, nullptr); + + dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device); + dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4); + dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder); // Hey look an error! + + // These calls will be skipped because of the error + dawnBufferSetSubData(buffer, 0, 0, nullptr); + dawnRenderPassEncoderSetIndexBuffer(pass, buffer, 0); + dawnRenderPassEncoderEndPass(pass); + dawnCommandBufferBuilderGetResult(cmdBufBuilder); + + dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder(); + EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)) + .WillOnce(Return(apiCmdBufBuilder)); + + dawnRenderPassEncoder apiPass = api.GetNewRenderPassEncoder(); + EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, _)) + .WillOnce(Return(apiPass)); + + dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder(); + EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice)) + .WillOnce(Return(apiBufferBuilder)); + + // Hey look an error! + EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder)) + .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer { + api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, + "Error"); + return nullptr; + })); + + EXPECT_CALL(api, BufferSetSubData(_, _, _, _)).Times(0); + EXPECT_CALL(api, RenderPassEncoderSetIndexBuffer(_, _, _)).Times(0); + EXPECT_CALL(api, CommandBufferBuilderGetResult(_)).Times(0); + + FlushClient(); + + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 1, 2)).Times(1); + EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 3, 4)).Times(1); + + FlushServer(); +} diff --git a/src/tests/unittests/wire/WireFenceTests.cpp b/src/tests/unittests/wire/WireFenceTests.cpp new file mode 100644 index 0000000000..5c8fb88a76 --- /dev/null +++ b/src/tests/unittests/wire/WireFenceTests.cpp @@ -0,0 +1,266 @@ +// 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 { + + // Mock classes to add expectations on the wire calling callbacks + class MockDeviceErrorCallback { + public: + MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata)); + }; + + std::unique_ptr mockDeviceErrorCallback; + void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) { + mockDeviceErrorCallback->Call(message, userdata); + } + + class MockFenceOnCompletionCallback { + public: + MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata)); + }; + + std::unique_ptr mockFenceOnCompletionCallback; + void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status, + dawnCallbackUserdata userdata) { + mockFenceOnCompletionCallback->Call(status, userdata); + } + +} // anonymous namespace + +class WireFenceTests : public WireTest { + public: + WireFenceTests() : WireTest(true) { + } + ~WireFenceTests() override = default; + + void SetUp() override { + WireTest::SetUp(); + + mockDeviceErrorCallback = std::make_unique(); + mockFenceOnCompletionCallback = std::make_unique(); + + { + dawnFenceDescriptor descriptor; + descriptor.initialValue = 1; + descriptor.nextInChain = nullptr; + + apiFence = api.GetNewFence(); + fence = dawnDeviceCreateFence(device, &descriptor); + + EXPECT_CALL(api, DeviceCreateFence(apiDevice, _)).WillOnce(Return(apiFence)); + EXPECT_CALL(api, FenceRelease(apiFence)); + FlushClient(); + } + { + queue = dawnDeviceCreateQueue(device); + apiQueue = api.GetNewQueue(); + EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue)); + EXPECT_CALL(api, QueueRelease(apiQueue)); + FlushClient(); + } + } + + void TearDown() override { + WireTest::TearDown(); + + // Delete mocks so that expectations are checked + mockDeviceErrorCallback = nullptr; + mockFenceOnCompletionCallback = nullptr; + } + + protected: + void DoQueueSignal(uint64_t signalValue) { + dawnQueueSignal(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, DAWN_FENCE_COMPLETION_STATUS_SUCCESS); + })); + } + + // A successfully created fence + dawnFence fence; + dawnFence apiFence; + + dawnQueue queue; + dawnQueue apiQueue; +}; + +// Check that signaling a fence succeeds +TEST_F(WireFenceTests, QueueSignalSuccess) { + DoQueueSignal(2u); + DoQueueSignal(3u); + FlushClient(); + FlushServer(); +} + +// Without any flushes, it is valid to signal a value greater than the current +// signaled value +TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) { + dawnCallbackUserdata userdata = 9157; + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0); + + dawnQueueSignal(queue, fence, 2u); + dawnQueueSignal(queue, fence, 4u); + dawnQueueSignal(queue, fence, 5u); +} + +// Without any flushes, errors should be generated when signaling a value less +// than or equal to the current signaled value +TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) { + dawnCallbackUserdata userdata = 3157; + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); + dawnQueueSignal(queue, fence, 0u); // Error + EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); + dawnQueueSignal(queue, fence, 1u); // Error + EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0); + dawnQueueSignal(queue, fence, 4u); // Success + EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); + + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1); + dawnQueueSignal(queue, fence, 3u); // Error + EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get())); +} + +// 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 + { + dawnCallbackUserdata userdata = 9847; + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata)) + .Times(1); + dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, userdata); + } + + // Can call on value == (initial) signaled value happens immediately + { + dawnCallbackUserdata userdata = 4347; + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata)) + .Times(1); + dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, userdata); + } +} + +// Check that all passed client completion callbacks are called +TEST_F(WireFenceTests, OnCompletionMultiple) { + DoQueueSignal(3u); + DoQueueSignal(6u); + + dawnCallbackUserdata userdata0 = 2134; + dawnCallbackUserdata userdata1 = 7134; + dawnCallbackUserdata userdata2 = 3144; + dawnCallbackUserdata userdata3 = 1130; + + // 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. + dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, userdata0); + dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata1); + dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, userdata2); + dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata3); + + Sequence s1, s2; + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1)) + .Times(1) + .InSequence(s1); + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3)) + .Times(1) + .InSequence(s2); + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2)) + .Times(1) + .InSequence(s1, s2); + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0)) + .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) { + dawnQueueSignal(queue, fence, 4u); + dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0); + dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0); + dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0); +} + +// Without any flushes, errors should be generated when waiting on a value greater +// than the last signaled value +TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) { + dawnCallbackUserdata userdata1 = 3817; + dawnCallbackUserdata userdata2 = 3857; + dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata2); + + EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata1)) + .Times(1); + EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata2)).Times(1); + + dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata1); +} + +// Check that the fence completed value is initialized +TEST_F(WireFenceTests, GetCompletedValueInitialization) { + EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u); +} + +// Check that the fence completed value updates after signaling the fence +TEST_F(WireFenceTests, GetCompletedValueUpdate) { + DoQueueSignal(3u); + FlushClient(); + FlushServer(); + + EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u); +} + +// Check that the fence completed value does not update without a flush +TEST_F(WireFenceTests, GetCompletedValueNoUpdate) { + dawnQueueSignal(queue, fence, 3u); + EXPECT_EQ(dawnFenceGetCompletedValue(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) { + dawnCallbackUserdata userdata = 8616; + dawnQueueSignal(queue, fence, 3u); + dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata); + EXPECT_CALL(*mockFenceOnCompletionCallback, + Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata)) + .Times(1); + + dawnFenceRelease(fence); +} diff --git a/src/tests/unittests/wire/WireOptionalTests.cpp b/src/tests/unittests/wire/WireOptionalTests.cpp new file mode 100644 index 0000000000..9243b00354 --- /dev/null +++ b/src/tests/unittests/wire/WireOptionalTests.cpp @@ -0,0 +1,208 @@ +// 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; + +class WireOptionalTests : public WireTest { + public: + WireOptionalTests() : WireTest(true) { + } + ~WireOptionalTests() override = default; +}; + +// Test passing nullptr instead of objects - object as value version +TEST_F(WireOptionalTests, OptionalObjectValue) { + dawnBindGroupLayoutDescriptor bglDesc; + bglDesc.nextInChain = nullptr; + bglDesc.numBindings = 0; + dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDesc); + + dawnBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout(); + EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)) + .WillOnce(Return(apiBindGroupLayout)); + + // The `sampler`, `textureView` and `buffer` members of a binding are optional. + dawnBindGroupBinding binding; + binding.binding = 0; + binding.sampler = nullptr; + binding.textureView = nullptr; + binding.buffer = nullptr; + + dawnBindGroupDescriptor bgDesc; + bgDesc.nextInChain = nullptr; + bgDesc.layout = bgl; + bgDesc.numBindings = 1; + bgDesc.bindings = &binding; + + dawnDeviceCreateBindGroup(device, &bgDesc); + EXPECT_CALL(api, DeviceCreateBindGroup( + apiDevice, MatchesLambda([](const dawnBindGroupDescriptor* desc) -> bool { + return desc->nextInChain == nullptr && desc->numBindings == 1 && + desc->bindings[0].binding == 0 && + desc->bindings[0].sampler == nullptr && + desc->bindings[0].buffer == nullptr && + desc->bindings[0].textureView == nullptr; + }))) + .WillOnce(Return(nullptr)); + + EXPECT_CALL(api, BindGroupLayoutRelease(apiBindGroupLayout)); + FlushClient(); +} + +// Test that the wire is able to send optional pointers to structures +TEST_F(WireOptionalTests, OptionalStructPointer) { + // Create shader module + dawnShaderModuleDescriptor vertexDescriptor; + vertexDescriptor.nextInChain = nullptr; + vertexDescriptor.codeSize = 0; + dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor); + dawnShaderModule apiVsModule = api.GetNewShaderModule(); + EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule)); + + // Create the blend state descriptor + dawnBlendDescriptor blendDescriptor; + blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD; + blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE; + blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE; + dawnBlendStateDescriptor blendStateDescriptor; + blendStateDescriptor.nextInChain = nullptr; + blendStateDescriptor.alphaBlend = blendDescriptor; + blendStateDescriptor.colorBlend = blendDescriptor; + blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL; + + // Create the input state + dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device); + dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder(); + EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice)) + .WillOnce(Return(apiInputStateBuilder)); + + dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder); + dawnInputState apiInputState = api.GetNewInputState(); + EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder)) + .WillOnce(Return(apiInputState)); + + // Create the depth-stencil state + dawnStencilStateFaceDescriptor stencilFace; + stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS; + stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP; + stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP; + stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP; + + dawnDepthStencilStateDescriptor depthStencilState; + depthStencilState.nextInChain = nullptr; + depthStencilState.depthWriteEnabled = false; + depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS; + depthStencilState.stencilBack = stencilFace; + depthStencilState.stencilFront = stencilFace; + depthStencilState.stencilReadMask = 0xff; + depthStencilState.stencilWriteMask = 0xff; + + // Create the pipeline layout + dawnPipelineLayoutDescriptor layoutDescriptor; + layoutDescriptor.nextInChain = nullptr; + layoutDescriptor.numBindGroupLayouts = 0; + layoutDescriptor.bindGroupLayouts = nullptr; + dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor); + dawnPipelineLayout apiLayout = api.GetNewPipelineLayout(); + EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout)); + + // Create pipeline + dawnRenderPipelineDescriptor pipelineDescriptor; + pipelineDescriptor.nextInChain = nullptr; + + dawnPipelineStageDescriptor vertexStage; + vertexStage.nextInChain = nullptr; + vertexStage.module = vsModule; + vertexStage.entryPoint = "main"; + pipelineDescriptor.vertexStage = &vertexStage; + + dawnPipelineStageDescriptor fragmentStage; + fragmentStage.nextInChain = nullptr; + fragmentStage.module = vsModule; + fragmentStage.entryPoint = "main"; + pipelineDescriptor.fragmentStage = &fragmentStage; + + dawnAttachmentsStateDescriptor attachmentsState; + attachmentsState.nextInChain = nullptr; + attachmentsState.numColorAttachments = 1; + dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM}; + dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment}; + attachmentsState.colorAttachments = colorAttachmentPtr; + attachmentsState.hasDepthStencilAttachment = false; + // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid + // data because we don't have optional substructures yet. + attachmentsState.depthStencilAttachment = &colorAttachment; + pipelineDescriptor.attachmentsState = &attachmentsState; + + pipelineDescriptor.numBlendStates = 1; + pipelineDescriptor.blendStates = &blendStateDescriptor; + + pipelineDescriptor.sampleCount = 1; + pipelineDescriptor.layout = layout; + pipelineDescriptor.inputState = inputState; + pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32; + pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + // First case: depthStencilState is not null. + pipelineDescriptor.depthStencilState = &depthStencilState; + dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + EXPECT_CALL( + api, + DeviceCreateRenderPipeline( + apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { + return desc->depthStencilState != nullptr && + desc->depthStencilState->nextInChain == nullptr && + desc->depthStencilState->depthWriteEnabled == false && + desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS && + desc->depthStencilState->stencilBack.compare == + DAWN_COMPARE_FUNCTION_ALWAYS && + desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilBack.depthFailOp == + DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilFront.compare == + DAWN_COMPARE_FUNCTION_ALWAYS && + desc->depthStencilState->stencilFront.failOp == + DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilFront.depthFailOp == + DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilFront.passOp == + DAWN_STENCIL_OPERATION_KEEP && + desc->depthStencilState->stencilReadMask == 0xff && + desc->depthStencilState->stencilWriteMask == 0xff; + }))) + .WillOnce(Return(nullptr)); + + FlushClient(); + + // Second case: depthStencilState is null. + pipelineDescriptor.depthStencilState = nullptr; + dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor); + EXPECT_CALL(api, + DeviceCreateRenderPipeline( + apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool { + return desc->depthStencilState == nullptr; + }))) + .WillOnce(Return(nullptr)); + + EXPECT_CALL(api, ShaderModuleRelease(apiVsModule)); + EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder)); + EXPECT_CALL(api, InputStateRelease(apiInputState)); + EXPECT_CALL(api, PipelineLayoutRelease(apiLayout)); + + FlushClient(); +} diff --git a/src/tests/unittests/wire/WireTest.cpp b/src/tests/unittests/wire/WireTest.cpp new file mode 100644 index 0000000000..cfc5cf577f --- /dev/null +++ b/src/tests/unittests/wire/WireTest.cpp @@ -0,0 +1,74 @@ +// 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" + +#include "dawn_wire/WireClient.h" +#include "dawn_wire/WireServer.h" +#include "utils/TerribleCommandBuffer.h" + +using namespace testing; +using namespace dawn_wire; + +WireTest::WireTest(bool ignoreSetCallbackCalls) : mIgnoreSetCallbackCalls(ignoreSetCallbackCalls) { +} + +WireTest::~WireTest() { +} + +void WireTest::SetUp() { + dawnProcTable mockProcs; + dawnDevice mockDevice; + api.GetProcTableAndDevice(&mockProcs, &mockDevice); + + // This SetCallback call cannot be ignored because it is done as soon as we start the server + EXPECT_CALL(api, OnDeviceSetErrorCallback(_, _, _)).Times(Exactly(1)); + if (mIgnoreSetCallbackCalls) { + EXPECT_CALL(api, OnBuilderSetErrorCallback(_, _, _, _)).Times(AnyNumber()); + } + EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber()); + + mS2cBuf = std::make_unique(); + mC2sBuf = std::make_unique(mWireServer.get()); + + mWireServer.reset(new WireServer(mockDevice, mockProcs, mS2cBuf.get())); + mC2sBuf->SetHandler(mWireServer.get()); + + mWireClient.reset(new WireClient(mC2sBuf.get())); + mS2cBuf->SetHandler(mWireClient.get()); + + device = mWireClient->GetDevice(); + dawnProcTable clientProcs = mWireClient->GetProcs(); + dawnSetProcs(&clientProcs); + + apiDevice = mockDevice; +} + +void WireTest::TearDown() { + dawnSetProcs(nullptr); + + // Derived classes should call the base TearDown() first. The client must + // be reset before any mocks are deleted. + // Incomplete client callbacks will be called on deletion, so the mocks + // cannot be null. + mWireClient = nullptr; +} + +void WireTest::FlushClient() { + ASSERT_TRUE(mC2sBuf->Flush()); +} + +void WireTest::FlushServer() { + ASSERT_TRUE(mS2cBuf->Flush()); +} diff --git a/src/tests/unittests/wire/WireTest.h b/src/tests/unittests/wire/WireTest.h new file mode 100644 index 0000000000..295de0a2c9 --- /dev/null +++ b/src/tests/unittests/wire/WireTest.h @@ -0,0 +1,99 @@ +// 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 "gtest/gtest.h" +#include "mock/mock_dawn.h" + +#include + +// Definition of a "Lambda predicate matcher" for GMock to allow checking deep structures +// are passed correctly by the wire. + +// Helper templates to extract the argument type of a lambda. +template +struct MatcherMethodArgument; + +template +struct MatcherMethodArgument { + using Type = Arg; +}; + +template +using MatcherLambdaArgument = typename MatcherMethodArgument::Type; + +// The matcher itself, unfortunately it isn't able to return detailed information like other +// matchers do. +template +class LambdaMatcherImpl : public testing::MatcherInterface { + public: + explicit LambdaMatcherImpl(Lambda lambda) : mLambda(lambda) { + } + + void DescribeTo(std::ostream* os) const override { + *os << "with a custom matcher"; + } + + bool MatchAndExplain(Arg value, testing::MatchResultListener* listener) const override { + if (!mLambda(value)) { + *listener << "which doesn't satisfy the custom predicate"; + return false; + } + return true; + } + + private: + Lambda mLambda; +}; + +// Use the MatchesLambda as follows: +// +// EXPECT_CALL(foo, Bar(MatchesLambda([](ArgType arg) -> bool { +// return CheckPredicateOnArg(arg); +// }))); +template +inline testing::Matcher> MatchesLambda(Lambda lambda) { + return MakeMatcher(new LambdaMatcherImpl>(lambda)); +} + +namespace dawn_wire { + class WireClient; + class WireServer; +} // namespace dawn_wire + +namespace utils { + class TerribleCommandBuffer; +} + +class WireTest : public testing::Test { + protected: + WireTest(bool ignoreSetCallbackCalls); + ~WireTest() override; + + void SetUp() override; + void TearDown() override; + void FlushClient(); + void FlushServer(); + + MockProcTable api; + dawnDevice apiDevice; + dawnDevice device; + + private: + bool mIgnoreSetCallbackCalls = false; + + std::unique_ptr mWireServer; + std::unique_ptr mWireClient; + std::unique_ptr mS2cBuf; + std::unique_ptr mC2sBuf; +};