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 <cwallez@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp
index 22d8109..582cff8 100644
--- a/src/tests/unittests/wire/WireBufferMappingTests.cpp
+++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -751,3 +751,67 @@
     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<TestData*>(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 0dd340c..6045e4f 100644
--- a/src/tests/unittests/wire/WireQueueTests.cpp
+++ b/src/tests/unittests/wire/WireQueueTests.cpp
@@ -37,8 +37,8 @@
     }
 
     void TearDown() override {
-        mockQueueWorkDoneCallback = nullptr;
         WireTest::TearDown();
+        mockQueueWorkDoneCallback = nullptr;
     }
 
     void FlushServer() {
@@ -97,3 +97,44 @@
         .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<TestData*>(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 4b2db4d..a999dff 100644
--- a/src/tests/unittests/wire/WireShaderModuleTests.cpp
+++ b/src/tests/unittests/wire/WireShaderModuleTests.cpp
@@ -149,3 +149,78 @@
                 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<TestData*>(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