From 3fdfa82f7b21473f0b8f0c0b2ead04d579e44a11 Mon Sep 17 00:00:00 2001 From: shrekshao Date: Tue, 31 Aug 2021 15:18:54 +0000 Subject: [PATCH] Callback reentrancy tests Some tests to make sure new requests inside user callback is okay. Bug: dawn:1091 Change-Id: I4c53d7fb6637f77e5af6fd0a78d879a2431d4ac8 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/63041 Reviewed-by: Corentin Wallez Commit-Queue: Shrek Shao --- .../unittests/wire/WireBufferMappingTests.cpp | 64 ++++++++++++++++ src/tests/unittests/wire/WireQueueTests.cpp | 43 ++++++++++- .../unittests/wire/WireShaderModuleTests.cpp | 75 +++++++++++++++++++ 3 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp index 22d8109b92..582cff8014 100644 --- a/src/tests/unittests/wire/WireBufferMappingTests.cpp +++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp @@ -751,3 +751,67 @@ TEST_F(WireBufferMappingTests, MapAfterDisconnect) { EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, this); } + +// Hack to pass in test context into user callback +struct TestData { + WireBufferMappingTests* pTest; + WGPUBuffer* pTestBuffer; + size_t numRequests; +}; + +static void ToMockBufferMapCallbackWithNewRequests(WGPUBufferMapAsyncStatus status, + void* userdata) { + TestData* testData = reinterpret_cast(userdata); + // Mimic the user callback is sending new requests + ASSERT_NE(testData, nullptr); + ASSERT_NE(testData->pTest, nullptr); + ASSERT_NE(testData->pTestBuffer, nullptr); + + mockBufferMapCallback->Call(status, testData->pTest); + + // Send the requests a number of times + for (size_t i = 0; i < testData->numRequests; i++) { + wgpuBufferMapAsync(*(testData->pTestBuffer), WGPUMapMode_Write, 0, sizeof(uint32_t), + ToMockBufferMapCallback, testData->pTest); + } +} + +// Test that requests inside user callbacks before disconnect are called +TEST_F(WireBufferMappingTests, MapInsideCallbackBeforeDisconnect) { + SetupBuffer(WGPUMapMode_Write); + TestData testData = {this, &buffer, 10}; + wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, + ToMockBufferMapCallbackWithNewRequests, &testData); + + EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)).Times(1); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)) + .Times(1 + testData.numRequests); + GetWireClient()->Disconnect(); +} + +// Test that requests inside user callbacks before object destruction are called +TEST_F(WireBufferMappingWriteTests, MapInsideCallbackBeforeDestruction) { + TestData testData = {this, &buffer, 10}; + wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, + ToMockBufferMapCallbackWithNewRequests, &testData); + + EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); + })); + EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)).Times(1); + + FlushClient(); + + EXPECT_CALL(*mockBufferMapCallback, + Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, this)) + .Times(1 + testData.numRequests); + wgpuBufferRelease(buffer); +} \ No newline at end of file diff --git a/src/tests/unittests/wire/WireQueueTests.cpp b/src/tests/unittests/wire/WireQueueTests.cpp index 0dd340cf68..6045e4fc08 100644 --- a/src/tests/unittests/wire/WireQueueTests.cpp +++ b/src/tests/unittests/wire/WireQueueTests.cpp @@ -37,8 +37,8 @@ class WireQueueTests : public WireTest { } void TearDown() override { - mockQueueWorkDoneCallback = nullptr; WireTest::TearDown(); + mockQueueWorkDoneCallback = nullptr; } void FlushServer() { @@ -97,3 +97,44 @@ TEST_F(WireQueueTests, OnSubmittedWorkDoneAfterDisconnect) { .Times(1); wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDone, this); } + +// Hack to pass in test context into user callback +struct TestData { + WireQueueTests* pTest; + WGPUQueue* pTestQueue; + size_t numRequests; +}; + +static void ToMockQueueWorkDoneWithNewRequests(WGPUQueueWorkDoneStatus status, void* userdata) { + TestData* testData = reinterpret_cast(userdata); + // Mimic the user callback is sending new requests + ASSERT_NE(testData, nullptr); + ASSERT_NE(testData->pTest, nullptr); + ASSERT_NE(testData->pTestQueue, nullptr); + mockQueueWorkDoneCallback->Call(status, testData->pTest); + + // Send the requests a number of times + for (size_t i = 0; i < testData->numRequests; i++) { + wgpuQueueOnSubmittedWorkDone(*(testData->pTestQueue), 0u, ToMockQueueWorkDone, + testData->pTest); + } +} + +// Test that requests inside user callbacks before disconnect are called +TEST_F(WireQueueTests, OnSubmittedWorkDoneInsideCallbackBeforeDisconnect) { + TestData testData = {this, &queue, 10}; + wgpuQueueOnSubmittedWorkDone(queue, 0u, ToMockQueueWorkDoneWithNewRequests, &testData); + EXPECT_CALL(api, OnQueueOnSubmittedWorkDone(apiQueue, 0u, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallQueueOnSubmittedWorkDoneCallback(apiQueue, WGPUQueueWorkDoneStatus_Error); + })); + FlushClient(); + + EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, this)) + .Times(1 + testData.numRequests); + GetWireClient()->Disconnect(); +} + +// Only one default queue is supported now so we cannot test ~Queue triggering ClearAllCallbacks +// since it is always destructed after the test TearDown, and we cannot create a new queue obj +// with wgpuDeviceGetQueue diff --git a/src/tests/unittests/wire/WireShaderModuleTests.cpp b/src/tests/unittests/wire/WireShaderModuleTests.cpp index 4b2db4dd2a..a999dffb95 100644 --- a/src/tests/unittests/wire/WireShaderModuleTests.cpp +++ b/src/tests/unittests/wire/WireShaderModuleTests.cpp @@ -149,3 +149,78 @@ TEST_F(WireShaderModuleTests, GetCompilationInfoAfterDisconnect) { Call(WGPUCompilationInfoRequestStatus_DeviceLost, nullptr, _)); wgpuShaderModuleGetCompilationInfo(shaderModule, ToMockGetCompilationInfoCallback, nullptr); } + +// Hack to pass in test context into user callback +struct TestData { + WireShaderModuleTests* pTest; + WGPUShaderModule* pTestShaderModule; + size_t numRequests; +}; + +static void ToMockBufferMapCallbackWithNewRequests(WGPUCompilationInfoRequestStatus status, + const WGPUCompilationInfo* info, + void* userdata) { + TestData* testData = reinterpret_cast(userdata); + // Mimic the user callback is sending new requests + ASSERT_NE(testData, nullptr); + ASSERT_NE(testData->pTest, nullptr); + ASSERT_NE(testData->pTestShaderModule, nullptr); + + mockCompilationInfoCallback->Call(status, info, testData->pTest); + + // Send the requests a number of times + for (size_t i = 0; i < testData->numRequests; i++) { + wgpuShaderModuleGetCompilationInfo(*(testData->pTestShaderModule), + ToMockGetCompilationInfoCallback, nullptr); + } +} + +// Test that requests inside user callbacks before disconnect are called +TEST_F(WireShaderModuleTests, GetCompilationInfoInsideCallbackBeforeDisconnect) { + TestData testData = {this, &shaderModule, 10}; + + wgpuShaderModuleGetCompilationInfo(shaderModule, ToMockBufferMapCallbackWithNewRequests, + &testData); + + WGPUCompilationMessage message = {"Test Message", WGPUCompilationMessageType_Info, 2, 4, 6, 8}; + WGPUCompilationInfo compilationInfo; + compilationInfo.messageCount = 1; + compilationInfo.messages = &message; + + EXPECT_CALL(api, OnShaderModuleGetCompilationInfo(apiShaderModule, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallShaderModuleGetCompilationInfoCallback( + apiShaderModule, WGPUCompilationInfoRequestStatus_Success, &compilationInfo); + })); + FlushClient(); + + EXPECT_CALL(*mockCompilationInfoCallback, + Call(WGPUCompilationInfoRequestStatus_DeviceLost, nullptr, _)) + .Times(1 + testData.numRequests); + GetWireClient()->Disconnect(); +} + +// Test that requests inside user callbacks before object destruction are called +TEST_F(WireShaderModuleTests, GetCompilationInfoInsideCallbackBeforeDestruction) { + TestData testData = {this, &shaderModule, 10}; + + wgpuShaderModuleGetCompilationInfo(shaderModule, ToMockBufferMapCallbackWithNewRequests, + &testData); + + WGPUCompilationMessage message = {"Test Message", WGPUCompilationMessageType_Info, 2, 4, 6, 8}; + WGPUCompilationInfo compilationInfo; + compilationInfo.messageCount = 1; + compilationInfo.messages = &message; + + EXPECT_CALL(api, OnShaderModuleGetCompilationInfo(apiShaderModule, _, _)) + .WillOnce(InvokeWithoutArgs([&]() { + api.CallShaderModuleGetCompilationInfoCallback( + apiShaderModule, WGPUCompilationInfoRequestStatus_Success, &compilationInfo); + })); + FlushClient(); + + EXPECT_CALL(*mockCompilationInfoCallback, + Call(WGPUCompilationInfoRequestStatus_Unknown, nullptr, _)) + .Times(1 + testData.numRequests); + wgpuShaderModuleRelease(shaderModule); +} \ No newline at end of file