[dawn][test] Update buffer related tests to use new callback types.

- Removes any remaining old references to old callback types.
- Directly mock read/write handles on the wire instead of using
  the unnecessary proxy.
- Adds an additional macro to help produce parameterized structs
  for testing.
- Revamp all the WireMemoryTransferServiceTests to be
  parameterized and reduce code duplication.

Bug: 369445924
Change-Id: I8cd7d1563107385f1442be3c5db03f799b8efe8b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/216618
Reviewed-by: Shrek Shao <shrekshao@google.com>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/tests/ParamGenerator.h b/src/dawn/tests/ParamGenerator.h
index 52fcefb..669b636 100644
--- a/src/dawn/tests/ParamGenerator.h
+++ b/src/dawn/tests/ParamGenerator.h
@@ -110,10 +110,48 @@
     }                                                                                              \
     static_assert(true, "require semicolon")
 
+// Usage: DAWN_TEST_PARAM_STRUCT_TYPES(Foo, TypeA, TypeB, ...)
+// Generate a test param struct called Foo which extends generated struct _Dawn_Foo. _Dawn_Foo has
+// members of types TypeA, TypeB, etc. which are named mTypeA, mTypeB, etc. in the order they are
+// placed in the macro argument list. Struct Foo should be constructed with a list of values to
+// initialize the base _Dawn_Foo struct.
+// It is recommended to use alias declarations so that stringified types are more readable.
+// Example:
+//   using MyParam = unsigned int;
+//   DAWN_TEST_PARAM_STRUCT(FooParams, MyParam);
+struct Placeholder {};
+#define DAWN_TEST_PARAM_STRUCT_TYPES(StructName, ...)                                              \
+    struct DAWN_PP_CONCATENATE(_Dawn_, StructName) {                                               \
+        DAWN_PP_EXPAND(DAWN_PP_EXPAND(DAWN_PP_FOR_EACH)(DAWN_TEST_PARAM_STRUCT_DECL_STRUCT_FIELD,  \
+                                                        __VA_ARGS__))                              \
+    };                                                                                             \
+    inline std::ostream& operator<<(std::ostream& o,                                               \
+                                    const DAWN_PP_CONCATENATE(_Dawn_, StructName) & param) {       \
+        DAWN_PP_EXPAND(DAWN_PP_EXPAND(DAWN_PP_FOR_EACH)(DAWN_TEST_PARAM_STRUCT_PRINT_STRUCT_FIELD, \
+                                                        __VA_ARGS__))                              \
+        return o;                                                                                  \
+    }                                                                                              \
+    struct StructName : DAWN_PP_CONCATENATE(_Dawn_, StructName) {                                  \
+        template <typename... Args>                                                                \
+        StructName(Args&&... args) : DAWN_PP_CONCATENATE(_Dawn_, StructName) {                     \
+            std::forward<Args>(args)...                                                            \
+        }                                                                                          \
+        {}                                                                                         \
+    };                                                                                             \
+    inline std::ostream& operator<<(std::ostream& o, const StructName& param) {                    \
+        o << static_cast<const DAWN_PP_CONCATENATE(_Dawn_, StructName)&>(param);                   \
+        return o;                                                                                  \
+    }                                                                                              \
+    static_assert(true, "require semicolon")
+
 template <typename ParamStruct>
 std::string TestParamToString(const testing::TestParamInfo<ParamStruct>& info) {
     std::ostringstream output;
     output << info.param;
+    auto result = output.str();
+    if (result[0] == '_') {
+        return result.substr(1);
+    }
     return output.str();
 }
 
diff --git a/src/dawn/tests/end2end/BufferTests.cpp b/src/dawn/tests/end2end/BufferTests.cpp
index 00b3f50..1877348 100644
--- a/src/dawn/tests/end2end/BufferTests.cpp
+++ b/src/dawn/tests/end2end/BufferTests.cpp
@@ -46,10 +46,10 @@
 
 using testing::_;
 using testing::MockCppCallback;
+using testing::StrictMock;
 
-using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, wgpu::StringView)>;
-
-using FutureCallbackMode = std::optional<wgpu::CallbackMode>;
+using MockMapAsyncCallback = MockCppCallback<wgpu::BufferMapCallback2<void>*>;
+using FutureCallbackMode = wgpu::CallbackMode;
 
 DAWN_TEST_PARAM_STRUCT(BufferMappingTestParams, FutureCallbackMode);
 
@@ -58,65 +58,33 @@
     void SetUp() override {
         DawnTestWithParams<BufferMappingTestParams>::SetUp();
         // Wire only supports polling / spontaneous futures.
-        DAWN_TEST_UNSUPPORTED_IF(UsesWire() && GetParam().mFutureCallbackMode &&
-                                 *GetParam().mFutureCallbackMode ==
-                                     wgpu::CallbackMode::WaitAnyOnly);
+        DAWN_TEST_UNSUPPORTED_IF(UsesWire() &&
+                                 GetParam().mFutureCallbackMode == wgpu::CallbackMode::WaitAnyOnly);
     }
 
     void MapAsyncAndWait(const wgpu::Buffer& buffer,
                          wgpu::MapMode mode,
                          size_t offset,
                          size_t size,
-                         wgpu::BufferMapCallback cb = nullptr,
-                         void* ud = nullptr) {
-        // Legacy MapAsync
-        if (!GetParam().mFutureCallbackMode) {
-            struct Userdata {
-                wgpu::BufferMapCallback cb;
-                raw_ptr<void> ud;
-                bool done = false;
-            };
-            Userdata userdata = Userdata{cb, ud};
-            auto callback = [](WGPUBufferMapAsyncStatus status, void* rawUserdata) {
-                auto* userdata = static_cast<Userdata*>(rawUserdata);
-                userdata->done = true;
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                auto cb = userdata->cb;
-                auto ud = userdata->ud;
-                if (cb) {
-                    cb(status, ud);
-                }
-            };
+                         wgpu::BufferMapCallback2<> cb = nullptr) {
+        wgpu::Future future;
 
-            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(mode, offset, size, callback, &userdata));
-            while (!userdata.done) {
-                // Flush wire and call instance process events.
-                WaitABit();
-            }
-            return;
+        if (cb) {
+            future = buffer.MapAsync(mode, offset, size, GetParam().mFutureCallbackMode, cb);
+        } else {
+            EXPECT_CALL(mMockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+            future = buffer.MapAsync(mode, offset, size, GetParam().mFutureCallbackMode,
+                                     mMockCb.Callback());
         }
 
-        bool done = false;
-        wgpu::Future future =
-            buffer.MapAsync(mode, offset, size, *GetParam().mFutureCallbackMode,
-                            [cb, ud, &done](wgpu::MapAsyncStatus status, wgpu::StringView) {
-                                done = true;
-                                ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
-                                if (cb) {
-                                    cb(WGPUBufferMapAsyncStatus_Success, ud);
-                                }
-                            });
-        switch (*GetParam().mFutureCallbackMode) {
+        switch (GetParam().mFutureCallbackMode) {
             case wgpu::CallbackMode::WaitAnyOnly: {
-                wgpu::FutureWaitInfo waitInfo = {future};
-                GetInstance().WaitAny(1, &waitInfo, UINT64_MAX);
-                ASSERT_TRUE(waitInfo.completed);
-                ASSERT_TRUE(done);
+                ASSERT_EQ(GetInstance().WaitAny(future, UINT64_MAX), wgpu::WaitStatus::Success);
                 break;
             }
             case wgpu::CallbackMode::AllowProcessEvents:
             case wgpu::CallbackMode::AllowSpontaneous:
-                while (!done) {
+                while (GetInstance().WaitAny(future, 0) == wgpu::WaitStatus::TimedOut) {
                     // Flush wire and call instance process events.
                     WaitABit();
                 }
@@ -144,6 +112,8 @@
         descriptor.usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
         return device.CreateBuffer(&descriptor);
     }
+
+    StrictMock<MockMapAsyncCallback> mMockCb;
 };
 
 void CheckMapping(const void* actual, const void* expected, size_t size) {
@@ -266,25 +236,17 @@
     static constexpr size_t kSize = sizeof(myData);
     queue.WriteBuffer(buffer, 0, &myData, kSize);
 
-    struct UserData {
-        wgpu::Buffer buffer;
-        raw_ptr<void> expected;
-    };
-    UserData user{buffer, &myData};
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kBufferSize,
+                    [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                        ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
 
-    MapAsyncAndWait(
-        buffer, wgpu::MapMode::Read, 0, kBufferSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            UserData* user = static_cast<UserData*>(userdata);
-            CheckMapping(user->buffer.GetConstMappedRange(), user->expected, kSize);
-            CheckMapping(user->buffer.GetConstMappedRange(0, kSize), user->expected, kSize);
+                        CheckMapping(buffer.GetConstMappedRange(), &myData, kSize);
+                        CheckMapping(buffer.GetConstMappedRange(0, kSize), &myData, kSize);
+                        CheckMapping(buffer.GetConstMappedRange(8, 4), &myData[2],
+                                     sizeof(uint32_t));
 
-            CheckMapping(user->buffer.GetConstMappedRange(8, 4),
-                         static_cast<const uint32_t*>(user->expected) + 2, sizeof(uint32_t));
-
-            user->buffer.Unmap();
-        },
-        &user);
+                        buffer.Unmap();
+                    });
 }
 
 // Test that the simplest map write works.
@@ -461,35 +423,17 @@
         buffers[i] = device.CreateBuffer(&descriptor);
     }
 
-    // Legacy MapAsync
-    if (!GetParam().mFutureCallbackMode) {
-        // Map all the buffers.
-        for (uint32_t i = 0; i < kBuffers; ++i) {
-            EXPECT_DEPRECATION_WARNING(buffers[i].MapAsync(
-                wgpu::MapMode::Write, 0, descriptor.size,
-                [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                    ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                    (*static_cast<uint32_t*>(userdata))++;
-                },
-                &mapCompletedCount));
-        }
-
-        // Wait for all mappings to complete
-        while (mapCompletedCount != kBuffers) {
-            WaitABit();
-        }
-    } else {
         std::array<wgpu::Future, kBuffers> futures;
         for (uint32_t i = 0; i < kBuffers; ++i) {
             futures[i] = buffers[i].MapAsync(
-                wgpu::MapMode::Write, 0, descriptor.size, *GetParam().mFutureCallbackMode,
+                wgpu::MapMode::Write, 0, descriptor.size, GetParam().mFutureCallbackMode,
                 [&mapCompletedCount](wgpu::MapAsyncStatus status, wgpu::StringView) {
                     ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
                     mapCompletedCount++;
                 });
         }
 
-        switch (*GetParam().mFutureCallbackMode) {
+        switch (GetParam().mFutureCallbackMode) {
             case wgpu::CallbackMode::WaitAnyOnly: {
                 std::array<wgpu::FutureWaitInfo, kBuffers> waitInfos;
                 for (uint32_t i = 0; i < kBuffers; ++i) {
@@ -516,7 +460,6 @@
                 }
                 break;
         }
-    }
 
     // All buffers are mapped, write into them and unmap them all.
     for (uint32_t i = 0; i < kBuffers; ++i) {
@@ -536,80 +479,38 @@
     wgpu::Buffer buffer = CreateMapReadBuffer(sizeof(data));
     queue.WriteBuffer(buffer, 0, data, sizeof(data));
 
-    // Legacy MapAsync
-    if (!GetParam().mFutureCallbackMode) {
-        bool done1 = false;
-        bool done2 = false;
-        auto cb1 = [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        };
-        // Calling MapAsync another time, will reject the callback with error status
-        // and mMapOffset is not updated because the buffer is already being mapped and it doesn't
-        // allow multiple MapAsync requests.
-        auto cb2 = [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_MappingAlreadyPending, status);
-            *static_cast<bool*>(userdata) = true;
-        };
-        // Calling MapAsync when the buffer is already mapped (as opposed to pending mapping) will
-        // cause a validation error.
-        auto cb2Mapped = [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_ValidationError, status);
-            *static_cast<bool*>(userdata) = true;
-        };
+    EXPECT_CALL(mMockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    EXPECT_CALL(mMockCb, Call(wgpu::MapAsyncStatus::Error, _)).Times(1);
 
-        // Map the buffer but do not wait on the result yet.
-        EXPECT_DEPRECATION_WARNING(buffer.MapAsync(wgpu::MapMode::Read, 8, 4, cb1, &done1));
+    // Map the buffer but do not wait on the result yet.
+    wgpu::FutureWaitInfo f1 = {buffer.MapAsync(wgpu::MapMode::Read, 8, 4,
+                                               GetParam().mFutureCallbackMode, mMockCb.Callback())};
 
-        // Call MapAsync another time, the callback will be rejected with error status
-        // and mMapOffset is not updated because the buffer is already being mapped and it doesn't
-        // allow multiple MapAsync requests.
-        if (buffer.GetMapState() == wgpu::BufferMapState::Mapped) {
-            ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(
-                buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2Mapped, &done2)));
-        } else {
-            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2, &done2));
-        }
-
-        while (!done1 || !done2) {
-            WaitABit();
-        }
+    // Call MapAsync another time, the callback will be rejected with error status
+    // and mMapOffset is not updated because the buffer is already being mapped and it doesn't
+    // allow multiple MapAsync requests.
+    wgpu::FutureWaitInfo f2;
+    if (!UsesWire()) {
+        ASSERT_DEVICE_ERROR(
+            f2 = {buffer.MapAsync(wgpu::MapMode::Read, 0, 4, GetParam().mFutureCallbackMode,
+                                  mMockCb.Callback())});
     } else {
-        MockMapAsyncCallback cb1;
-        MockMapAsyncCallback cb2;
-        EXPECT_CALL(cb1, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
-        EXPECT_CALL(cb2, Call(wgpu::MapAsyncStatus::Error, _)).Times(1);
+        f2 = {buffer.MapAsync(wgpu::MapMode::Read, 0, 4, GetParam().mFutureCallbackMode,
+                              mMockCb.Callback())};
+    }
 
-        // Map the buffer but do not wait on the result yet.
-        wgpu::FutureWaitInfo f1 = {buffer.MapAsync(
-            wgpu::MapMode::Read, 8, 4, *GetParam().mFutureCallbackMode, cb1.Callback())};
-
-        // Call MapAsync another time, the callback will be rejected with error status
-        // and mMapOffset is not updated because the buffer is already being mapped and it doesn't
-        // allow multiple MapAsync requests.
-        wgpu::FutureWaitInfo f2;
-        if (!UsesWire()) {
-            ASSERT_DEVICE_ERROR(
-                f2 = {buffer.MapAsync(wgpu::MapMode::Read, 0, 4, *GetParam().mFutureCallbackMode,
-                                      cb2.Callback())});
-        } else {
-            f2 = {buffer.MapAsync(wgpu::MapMode::Read, 0, 4, *GetParam().mFutureCallbackMode,
-                                  cb2.Callback())};
+    switch (GetParam().mFutureCallbackMode) {
+        case wgpu::CallbackMode::WaitAnyOnly: {
+            ASSERT_EQ(GetInstance().WaitAny(1, &f1, UINT64_MAX), wgpu::WaitStatus::Success);
+            ASSERT_EQ(GetInstance().WaitAny(1, &f2, UINT64_MAX), wgpu::WaitStatus::Success);
+            EXPECT_TRUE(f1.completed);
+            EXPECT_TRUE(f2.completed);
+            break;
         }
-
-        switch (*GetParam().mFutureCallbackMode) {
-            case wgpu::CallbackMode::WaitAnyOnly: {
-                ASSERT_EQ(GetInstance().WaitAny(1, &f1, UINT64_MAX), wgpu::WaitStatus::Success);
-                ASSERT_EQ(GetInstance().WaitAny(1, &f2, UINT64_MAX), wgpu::WaitStatus::Success);
-                EXPECT_TRUE(f1.completed);
-                EXPECT_TRUE(f2.completed);
-                break;
-            }
-            case wgpu::CallbackMode::AllowProcessEvents:
-            case wgpu::CallbackMode::AllowSpontaneous:
-                WaitForAllOperations();
-                break;
-        }
+        case wgpu::CallbackMode::AllowProcessEvents:
+        case wgpu::CallbackMode::AllowSpontaneous:
+            WaitForAllOperations();
+            break;
     }
 
     // mMapOffset has not been updated so it should still be 4, which is data[1]
@@ -623,21 +524,17 @@
     static constexpr uint32_t myData = 2934875;
     static constexpr size_t kSize = sizeof(myData);
 
-    MapAsyncAndWait(
-        buffer, wgpu::MapMode::Write, 0, kSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            wgpu::Buffer* buffer = static_cast<wgpu::Buffer*>(userdata);
-
-            EXPECT_NE(nullptr, buffer->GetConstMappedRange());
-            void* ptr = buffer->GetMappedRange();
-            EXPECT_NE(nullptr, ptr);
-            if (ptr != nullptr) {
-                uint32_t data = myData;
-                memcpy(ptr, &data, kSize);
-            }
-            buffer->Unmap();
-        },
-        &buffer);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, kSize,
+                    [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                        EXPECT_NE(nullptr, buffer.GetConstMappedRange());
+                        void* ptr = buffer.GetMappedRange();
+                        EXPECT_NE(nullptr, ptr);
+                        if (ptr != nullptr) {
+                            uint32_t data = myData;
+                            memcpy(ptr, &data, kSize);
+                        }
+                        buffer.Unmap();
+                    });
 
     EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
 }
@@ -649,21 +546,17 @@
     static constexpr uint32_t myData = 2934875;
     static constexpr size_t kSize = sizeof(myData);
 
-    MapAsyncAndWait(
-        buffer, wgpu::MapMode::Write, 0, kSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            wgpu::Buffer* buffer = static_cast<wgpu::Buffer*>(userdata);
-
-            EXPECT_NE(nullptr, buffer->GetConstMappedRange(0, kSize));
-            void* ptr = buffer->GetMappedRange(0, kSize);
-            EXPECT_NE(nullptr, ptr);
-            if (ptr != nullptr) {
-                uint32_t data = myData;
-                memcpy(ptr, &data, kSize);
-            }
-            buffer->Unmap();
-        },
-        &buffer);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, kSize,
+                    [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                        EXPECT_NE(nullptr, buffer.GetConstMappedRange(0, kSize));
+                        void* ptr = buffer.GetMappedRange(0, kSize);
+                        EXPECT_NE(nullptr, ptr);
+                        if (ptr != nullptr) {
+                            uint32_t data = myData;
+                            memcpy(ptr, &data, kSize);
+                        }
+                        buffer.Unmap();
+                    });
 
     EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
 }
@@ -697,64 +590,15 @@
     device.Tick();
 }
 
-DAWN_INSTANTIATE_PREFIXED_TEST_P(Legacy,
-                                 BufferMappingTests,
-                                 {D3D11Backend(), D3D12Backend(), MetalBackend(), OpenGLBackend(),
-                                  OpenGLESBackend(), VulkanBackend()},
-                                 {std::nullopt});
-
-DAWN_INSTANTIATE_PREFIXED_TEST_P(Future,
-                                 BufferMappingTests,
-                                 {D3D11Backend(), D3D12Backend(), MetalBackend(), VulkanBackend(),
-                                  OpenGLBackend(), OpenGLESBackend()},
-                                 std::initializer_list<std::optional<wgpu::CallbackMode>>{
-                                     wgpu::CallbackMode::WaitAnyOnly,
-                                     wgpu::CallbackMode::AllowProcessEvents,
-                                     wgpu::CallbackMode::AllowSpontaneous});
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BufferMappingTests);
+DAWN_INSTANTIATE_TEST_P(BufferMappingTests,
+                        {D3D11Backend(), D3D12Backend(), MetalBackend(), VulkanBackend(),
+                         OpenGLBackend(), OpenGLESBackend()},
+                        std::initializer_list<wgpu::CallbackMode>{
+                            wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+                            wgpu::CallbackMode::AllowSpontaneous});
 
 class BufferMappingCallbackTests : public BufferMappingTests {
   protected:
-    wgpu::Future DoMapAsync(wgpu::Buffer& buffer,
-                            wgpu::MapMode mapMode,
-                            size_t offset,
-                            size_t size,
-                            wgpu::BufferMapCallback callback,
-                            void* userdata) {
-        if (!GetParam().mFutureCallbackMode) {
-            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(mapMode, offset, size, callback, userdata));
-            return {0};
-        } else {
-            return buffer.MapAsync(
-                mapMode, offset, size, *GetParam().mFutureCallbackMode,
-                [callback, userdata](wgpu::MapAsyncStatus status, wgpu::StringView) {
-                    // Note that technically this cast should eventually be
-                    // removed once we update all tests to use the new callback
-                    // status. This currently works only because this is only
-                    // used for success cases which cast to the same underlying
-                    // value.
-                    ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
-                    callback(WGPUBufferMapAsyncStatus_Success, userdata);
-                });
-        }
-    }
-
-    wgpu::Future DoOnSubmittedWorkDone(wgpu::Queue& queueObj,
-                                       wgpu::QueueWorkDoneCallback callback,
-                                       void* userdata) {
-        if (!GetParam().mFutureCallbackMode) {
-            EXPECT_DEPRECATION_WARNING(queueObj.OnSubmittedWorkDone(callback, userdata));
-            return {0};
-        } else {
-            return queueObj.OnSubmittedWorkDone(
-                *GetParam().mFutureCallbackMode,
-                [callback, userdata](wgpu::QueueWorkDoneStatus status) {
-                    callback(static_cast<WGPUQueueWorkDoneStatus>(status), userdata);
-                });
-        }
-    }
-
     void SubmitCommandBuffer(wgpu::Buffer buffer) {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
 
@@ -784,8 +628,7 @@
     }
 
     void WaitAll(std::vector<bool>& done, std::vector<wgpu::Future> futures) {
-        if (GetParam().mFutureCallbackMode &&
-            *GetParam().mFutureCallbackMode == wgpu::CallbackMode::WaitAnyOnly) {
+        if (GetParam().mFutureCallbackMode == wgpu::CallbackMode::WaitAnyOnly) {
             std::vector<wgpu::FutureWaitInfo> waitInfos;
             waitInfos.reserve(futures.size());
             for (wgpu::Future f : futures) {
@@ -815,30 +658,28 @@
 
     // 1. submission without using buffer.
     SubmitCommandBuffer({});
-    wgpu::Future f1 = DoOnSubmittedWorkDone(
-        queue,
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
+    wgpu::Future f1 = queue.OnSubmittedWorkDone(
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+            ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[0] = true;
+
             // Step 2 callback should be called first, this is the second.
             const std::vector<bool> kExpected = {true, true};
             EXPECT_EQ(done, kExpected);
-        },
-        &done);
+        });
 
     // 2.
-    wgpu::Future f2 = DoMapAsync(
-        buffer, wgpu::MapMode::Write, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
-            done[1] = true;
-            // The buffer is not used by step 1, so this callback is called first.
-            const std::vector<bool> kExpected = {false, true};
-            EXPECT_EQ(done, kExpected);
-        },
-        &done);
+    wgpu::Future f2 = buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize,
+                                      GetParam().mFutureCallbackMode,
+                                      [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                                          ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                                          done[1] = true;
+
+                                          // The buffer is not used by step 1, so this callback is
+                                          // called first.
+                                          const std::vector<bool> kExpected = {false, true};
+                                          EXPECT_EQ(done, kExpected);
+                                      });
 
     WaitAll(done, {f1, f2});
 }
@@ -856,30 +697,27 @@
     SubmitCommandBuffer(buffer);
 
     // 1. Map the buffer.
-    wgpu::Future f1 = DoMapAsync(
-        buffer, wgpu::MapMode::Write, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
-            done[0] = true;
-            // This callback must be called first.
-            const std::vector<bool> kExpected = {true, false};
-            EXPECT_EQ(done, kExpected);
-        },
-        &done);
+    wgpu::Future f1 = buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize,
+                                      GetParam().mFutureCallbackMode,
+                                      [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                                          ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                                          done[0] = true;
+
+                                          // This callback must be called first.
+                                          const std::vector<bool> kExpected = {true, false};
+                                          EXPECT_EQ(done, kExpected);
+                                      });
 
     // 2. Wait for command completion.
-    wgpu::Future f2 = DoOnSubmittedWorkDone(
-        queue,
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
+    wgpu::Future f2 = queue.OnSubmittedWorkDone(
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+            ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[1] = true;
+
             // The buffer mapping callback must have been called before this one.
             const std::vector<bool> kExpected = {true, true};
             EXPECT_EQ(done, kExpected);
-        },
-        &done);
+        });
 
     WaitAll(done, {f1, f2});
 
@@ -895,61 +733,44 @@
 
     // 1. submission without using buffer.
     SubmitCommandBuffer({});
-    wgpu::Future f1 = DoOnSubmittedWorkDone(
-        queue,
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
+    wgpu::Future f1 = queue.OnSubmittedWorkDone(
+        GetParam().mFutureCallbackMode, [&](wgpu::QueueWorkDoneStatus status) {
+            ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
             done[0] = true;
+
             // Step 2 callback should be called first, this is the second.
             const std::vector<bool> kExpected = {true, false};
             EXPECT_EQ(done, kExpected);
-        },
-        &done);
+        });
 
     int32_t data = 0x12345678;
     queue.WriteBuffer(buffer, 0, &data, sizeof(data));
 
     // 2.
-    wgpu::Future f2 = DoMapAsync(
-        buffer, wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-            auto& done = *static_cast<std::vector<bool>*>(userdata);
-            done[1] = true;
-            // The buffer is not used by step 1, so this callback is called first.
-            const std::vector<bool> kExpected = {true, true};
-            EXPECT_EQ(done, kExpected);
-        },
-        &done);
+    wgpu::Future f2 =
+        buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize, GetParam().mFutureCallbackMode,
+                        [&](wgpu::MapAsyncStatus status, wgpu::StringView) {
+                            ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                            done[1] = true;
+
+                            // The buffer is not used by step 1, so this callback is called first.
+                            const std::vector<bool> kExpected = {true, true};
+                            EXPECT_EQ(done, kExpected);
+                        });
 
     WaitAll(done, {f1, f2});
 
     buffer.Unmap();
 }
 
-DAWN_INSTANTIATE_PREFIXED_TEST_P(Legacy,
-                                 BufferMappingCallbackTests,
-                                 {D3D11Backend(), D3D12Backend(), MetalBackend(), VulkanBackend()},
-                                 {std::nullopt});
-
-DAWN_INSTANTIATE_PREFIXED_TEST_P(Future,
-                                 BufferMappingCallbackTests,
-                                 {D3D11Backend(), D3D12Backend(), MetalBackend(), VulkanBackend()},
-                                 std::initializer_list<std::optional<wgpu::CallbackMode>>{
-                                     wgpu::CallbackMode::WaitAnyOnly,
-                                     wgpu::CallbackMode::AllowProcessEvents,
-                                     wgpu::CallbackMode::AllowSpontaneous});
-
-GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BufferMappingCallbackTests);
+DAWN_INSTANTIATE_TEST_P(BufferMappingCallbackTests,
+                        {D3D11Backend(), D3D12Backend(), MetalBackend(), VulkanBackend()},
+                        std::initializer_list<wgpu::CallbackMode>{
+                            wgpu::CallbackMode::WaitAnyOnly, wgpu::CallbackMode::AllowProcessEvents,
+                            wgpu::CallbackMode::AllowSpontaneous});
 
 class BufferMappedAtCreationTests : public DawnTest {
   protected:
-    static void MapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
-        EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-        *static_cast<bool*>(userdata) = true;
-    }
-
     const void* MapAsyncAndWait(const wgpu::Buffer& buffer, wgpu::MapMode mode, size_t size) {
         bool done = false;
         buffer.MapAsync(mode, 0, size, wgpu::CallbackMode::AllowProcessEvents,
diff --git a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
index 5103184..d80b89b 100644
--- a/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireMemoryTransferServiceTests.cpp
@@ -26,1126 +26,815 @@
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <memory>
+#include <tuple>
 #include <utility>
 
+#include "dawn/common/StringViewUtils.h"
+#include "dawn/tests/MockCallback.h"
+#include "dawn/tests/ParamGenerator.h"
+#include "dawn/tests/StringViewMatchers.h"
 #include "dawn/tests/unittests/wire/WireTest.h"
 #include "dawn/wire/client/ClientMemoryTransferService_mock.h"
 #include "dawn/wire/server/ServerMemoryTransferService_mock.h"
 
+namespace wgpu {
+// Define a stream operator for wgpu::MapMode so that it can be found on resolution for test name
+// generation.
+// TODO(dawn:2205) Remove this in favor of custom serializer.
+static std::ostream& operator<<(std::ostream& os, const MapMode& param) {
+    switch (param) {
+        case wgpu::MapMode::Read:
+            os << "Read";
+            break;
+        case wgpu::MapMode::Write:
+            os << "Write";
+            break;
+        default:
+            DAWN_UNREACHABLE();
+    }
+    return os;
+}
+}  // namespace wgpu
+
 namespace dawn::wire {
 namespace {
 
 using testing::_;
-using testing::Eq;
 using testing::InvokeWithoutArgs;
-using testing::Mock;
-using testing::Pointee;
+using testing::MockCppCallback;
+using testing::Ne;
+using testing::NotNull;
 using testing::Return;
+using testing::SizedString;
 using testing::StrictMock;
 using testing::WithArg;
 
-// Mock class to add expectations on the wire calling callbacks
-class MockBufferMapCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
-};
+using MapMode = wgpu::MapMode;
+using MappedAtCreation = bool;
+DAWN_TEST_PARAM_STRUCT_TYPES(MapModeParam, MapMode, MappedAtCreation);
 
-std::unique_ptr<StrictMock<MockBufferMapCallback>> mockBufferMapCallback;
-void ToMockBufferMapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
-    mockBufferMapCallback->Call(status, userdata);
+MATCHER_P(AsUint32Eq, value, "") {
+    return *reinterpret_cast<const uint32_t*>(arg) == value;
 }
 
+using MockClientReadHandle = client::MockMemoryTransferService::MockReadHandle;
+using MockClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle;
+using MockServerReadHandle = server::MockMemoryTransferService::MockReadHandle;
+using MockServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle;
+using MockClientHandles = std::tuple<MockClientReadHandle*, MockClientWriteHandle*>;
+using MockServerHandles = std::tuple<MockServerReadHandle*, MockServerWriteHandle*>;
+
 // WireMemoryTransferServiceTests test the MemoryTransferService with buffer mapping.
 // They test the basic success and error cases for buffer mapping, and they test
 // mocked failures of each fallible MemoryTransferService method that an embedder
 // could implement.
 // The test harness defines multiple helpers for expecting operations on Read/Write handles
-// and for mocking failures. The helpers are designed such that for a given run of a test,
-// a Serialization expection has a corresponding Deserialization expectation for which the
-// serialized data must match.
+// and for mocking failures.
 // There are tests which check for Success for every mapping operation which mock an entire
 // mapping operation from map to unmap, and add all MemoryTransferService expectations. Tests
 // which check for errors perform the same mapping operations but insert mocked failures for
 // various mapping or MemoryTransferService operations.
-class WireMemoryTransferServiceTests : public WireTest {
-  public:
-    WireMemoryTransferServiceTests() {}
-    ~WireMemoryTransferServiceTests() override = default;
-
-    client::MemoryTransferService* GetClientMemoryTransferService() override {
-        return &clientMemoryTransferService;
-    }
-
-    server::MemoryTransferService* GetServerMemoryTransferService() override {
-        return &serverMemoryTransferService;
-    }
-
-    void SetUp() override {
-        WireTest::SetUp();
-
-        mockBufferMapCallback = std::make_unique<StrictMock<MockBufferMapCallback>>();
-
-        // TODO(enga): Make this thread-safe.
-        mBufferContent++;
-        mMappedBufferContent = 0;
-        mUpdatedBufferContent++;
-        mSerializeCreateInfo++;
-        mReadHandleSerializeDataInfo++;
-        mWriteHandleSerializeDataInfo++;
-    }
-
-    void TearDown() override {
-        WireTest::TearDown();
-
-        // Delete mock so that expectations are checked
-        mockBufferMapCallback = nullptr;
-    }
-
-    void FlushClient(bool success = true) {
-        WireTest::FlushClient(success);
-        Mock::VerifyAndClearExpectations(&serverMemoryTransferService);
-    }
-
-    void FlushServer(bool success = true) {
-        WireTest::FlushServer(success);
-
-        Mock::VerifyAndClearExpectations(&mockBufferMapCallback);
-        Mock::VerifyAndClearExpectations(&clientMemoryTransferService);
-    }
-
+class WireMemoryTransferServiceTestBase : public WireTest,
+                                          public testing::WithParamInterface<MapModeParam> {
   protected:
-    using ClientReadHandle = client::MockMemoryTransferService::MockReadHandle;
-    using ServerReadHandle = server::MockMemoryTransferService::MockReadHandle;
-    using ClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle;
-    using ServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle;
+    client::MemoryTransferService* GetClientMemoryTransferService() override { return &mClientMTS; }
+    server::MemoryTransferService* GetServerMemoryTransferService() override { return &mServerMTS; }
 
-    std::pair<WGPUBuffer, WGPUBuffer> CreateBuffer(WGPUBufferUsage usage = WGPUBufferUsage_None) {
-        WGPUBufferDescriptor descriptor = {};
+    wgpu::BufferUsage GetUsage() {
+        switch (GetParam().mMapMode) {
+            case wgpu::MapMode::Read:
+                return wgpu::BufferUsage::MapRead;
+            case wgpu::MapMode::Write:
+                return wgpu::BufferUsage::MapWrite;
+            default:
+                DAWN_UNREACHABLE();
+        }
+    }
+
+    std::pair<WGPUBuffer, wgpu::Buffer> CreateBuffer() {
+        wgpu::BufferUsage usage = GetUsage();
+        bool mappedAtCreation = GetParam().mMappedAtCreation;
+
+        wgpu::BufferDescriptor descriptor = {};
         descriptor.size = kBufferSize;
+        descriptor.mappedAtCreation = mappedAtCreation;
         descriptor.usage = usage;
 
         WGPUBuffer apiBuffer = api.GetNewBuffer();
-        WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-
-        EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
-            .WillOnce(Return(apiBuffer))
-            .RetiresOnSaturation();
-
-        return std::make_pair(apiBuffer, buffer);
-    }
-
-    std::pair<WGPUBuffer, WGPUBuffer> CreateBufferMapped(
-        WGPUBufferUsage usage = WGPUBufferUsage_None) {
-        WGPUBufferDescriptor descriptor = {};
-        descriptor.size = sizeof(mBufferContent);
-        descriptor.mappedAtCreation = true;
-        descriptor.usage = usage;
-
-        WGPUBuffer apiBuffer = api.GetNewBuffer();
-
-        WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-
+        wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
         EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-        EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, sizeof(mBufferContent)))
-            .WillOnce(Return(&mMappedBufferContent));
 
         return std::make_pair(apiBuffer, buffer);
     }
 
-    ClientReadHandle* ExpectReadHandleCreation() {
-        // Create the handle first so we can use it in later expectations.
-        ClientReadHandle* handle = clientMemoryTransferService.NewReadHandle();
+    std::tuple<WGPUBuffer, wgpu::Buffer, MockClientHandles, MockServerHandles> CreateValidBuffer() {
+        WGPUBuffer apiBuffer;
+        wgpu::Buffer buffer;
 
-        EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent)))
-            .WillOnce(InvokeWithoutArgs([=] { return handle; }));
+        // The client should create and serialize the appropriate handles on buffer creation.
+        auto clientHandles = ExpectHandleCreation(true);
+        ExpectHandleSerialization(clientHandles);
+        std::tie(apiBuffer, buffer) = CreateBuffer();
 
-        return handle;
+        // When the commands are flushed, the server should appropriately deserialize the handles.
+        auto serverHandles = ExpectHandleDeserialization(true);
+        if (GetParam().mMappedAtCreation) {
+            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mServerBufferContent));
+        }
+        FlushClient();
+
+        return std::make_tuple(apiBuffer, buffer, clientHandles, serverHandles);
     }
 
-    void MockReadHandleCreationFailure() {
-        EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent)))
-            .WillOnce(InvokeWithoutArgs([=] { return nullptr; }));
+    MockClientHandles ExpectHandleCreation(bool success) {
+        wgpu::MapMode mode = GetParam().mMapMode;
+        bool mappedAtCreation = GetParam().mMappedAtCreation;
+        switch (mode) {
+            case wgpu::MapMode::Read: {
+                auto* readHandle = success ? new StrictMock<MockClientReadHandle>() : nullptr;
+                EXPECT_CALL(mClientMTS, CreateReadHandle(kBufferSize)).WillOnce(Return(readHandle));
+                if (!success) {
+                    return std::make_tuple(nullptr, nullptr);
+                }
+                if (mappedAtCreation) {
+                    auto* writeHandle = new StrictMock<MockClientWriteHandle>();
+                    EXPECT_CALL(mClientMTS, CreateWriteHandle(kBufferSize))
+                        .WillOnce(Return(writeHandle));
+                    EXPECT_CALL(*writeHandle, GetData).WillOnce(Return(&mClientBufferContent));
+                    return std::make_tuple(readHandle, writeHandle);
+                }
+                return std::make_tuple(readHandle, nullptr);
+            }
+            case wgpu::MapMode::Write: {
+                auto* writeHandle = success ? new StrictMock<MockClientWriteHandle>() : nullptr;
+                EXPECT_CALL(mClientMTS, CreateWriteHandle(kBufferSize))
+                    .WillOnce(Return(writeHandle));
+                if (!success) {
+                    return std::make_tuple(nullptr, nullptr);
+                }
+                if (mappedAtCreation) {
+                    EXPECT_CALL(*writeHandle, GetData).WillOnce(Return(&mClientBufferContent));
+                }
+                return std::make_tuple(nullptr, writeHandle);
+            }
+            default:
+                DAWN_UNREACHABLE();
+        }
     }
 
-    void ExpectReadHandleSerialization(ClientReadHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreateSize(handle))
-            .WillOnce(InvokeWithoutArgs([&] { return sizeof(mSerializeCreateInfo); }));
-        EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreate(handle, _))
-            .WillOnce(WithArg<1>([&](void* serializePointer) {
-                memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo));
+    void ExpectHandleSerialization(MockClientHandles& clientHandles) {
+        auto* readHandle = std::get<MockClientReadHandle*>(clientHandles);
+        if (readHandle) {
+            EXPECT_CALL(*readHandle, SerializeCreateSize).WillOnce(InvokeWithoutArgs([&] {
                 return sizeof(mSerializeCreateInfo);
             }));
-    }
-
-    ServerReadHandle* ExpectServerReadHandleDeserialize() {
-        // Create the handle first so we can use it in later expectations.
-        ServerReadHandle* handle = serverMemoryTransferService.NewReadHandle();
-
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)),
-                                            sizeof(mSerializeCreateInfo), _))
-            .WillOnce(WithArg<2>([=](server::MemoryTransferService::ReadHandle** readHandle) {
-                *readHandle = handle;
-                return true;
+            EXPECT_CALL(*readHandle, SerializeCreate(_))
+                .WillOnce(WithArg<0>([&](void* serializePointer) {
+                    memcpy(serializePointer, &mSerializeCreateInfo, kDataSize);
+                    return kDataSize;
+                }));
+        }
+        auto* writeHandle = std::get<MockClientWriteHandle*>(clientHandles);
+        if (writeHandle) {
+            EXPECT_CALL(*writeHandle, SerializeCreateSize).WillOnce(InvokeWithoutArgs([&] {
+                return sizeof(mSerializeCreateInfo);
             }));
-
-        return handle;
+            EXPECT_CALL(*writeHandle, SerializeCreate(_))
+                .WillOnce(WithArg<0>([&](void* serializePointer) {
+                    memcpy(serializePointer, &mSerializeCreateInfo, kDataSize);
+                    return kDataSize;
+                }));
+        }
     }
 
-    void MockServerReadHandleDeserializeFailure() {
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)),
-                                            sizeof(mSerializeCreateInfo), _))
-            .WillOnce(InvokeWithoutArgs([&] { return false; }));
+    MockServerHandles ExpectHandleDeserialization(bool success) {
+        wgpu::MapMode mode = GetParam().mMapMode;
+        bool mappedAtCreation = GetParam().mMappedAtCreation;
+        switch (mode) {
+            case wgpu::MapMode::Read: {
+                MockServerWriteHandle* writeHandle = nullptr;
+                if (mappedAtCreation) {
+                    writeHandle = success ? new StrictMock<MockServerWriteHandle>() : nullptr;
+                    EXPECT_CALL(mServerMTS, DeserializeWriteHandle(AsUint32Eq(mSerializeCreateInfo),
+                                                                   kDataSize, _))
+                        .WillOnce(
+                            WithArg<2>([=](server::MemoryTransferService::WriteHandle** handle) {
+                                *handle = writeHandle;
+                                return success;
+                            }));
+                    if (!success) {
+                        return std::make_tuple(nullptr, nullptr);
+                    }
+                }
+
+                auto* readHandle = success ? new StrictMock<MockServerReadHandle>() : nullptr;
+                EXPECT_CALL(mServerMTS,
+                            DeserializeReadHandle(AsUint32Eq(mSerializeCreateInfo), kDataSize, _))
+                    .WillOnce(WithArg<2>([=](server::MemoryTransferService::ReadHandle** handle) {
+                        *handle = readHandle;
+                        return success;
+                    }));
+                return std::make_tuple(readHandle, writeHandle);
+            }
+            case wgpu::MapMode::Write: {
+                auto* writeHandle = success ? new StrictMock<MockServerWriteHandle>() : nullptr;
+                EXPECT_CALL(mServerMTS,
+                            DeserializeWriteHandle(AsUint32Eq(mSerializeCreateInfo), kDataSize, _))
+                    .WillOnce(WithArg<2>([=](server::MemoryTransferService::WriteHandle** handle) {
+                        *handle = writeHandle;
+                        return success;
+                    }));
+                return std::make_tuple(nullptr, writeHandle);
+            }
+            default:
+                DAWN_UNREACHABLE();
+        }
     }
 
-    void ExpectServerReadHandleSerializeDataUpdate(ServerReadHandle* handle) {
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnReadHandleSizeOfSerializeDataUpdate(handle, _, _))
-            .WillOnce(InvokeWithoutArgs([&] { return sizeof(mReadHandleSerializeDataInfo); }));
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnReadHandleSerializeDataUpdate(handle, _, _, _, _))
-            .WillOnce(WithArg<4>([&](void* serializePointer) {
-                memcpy(serializePointer, &mReadHandleSerializeDataInfo,
-                       sizeof(mReadHandleSerializeDataInfo));
-                return sizeof(mReadHandleSerializeDataInfo);
-            }));
-    }
-
-    void ExpectClientReadHandleDeserializeDataUpdate(ClientReadHandle* handle,
-                                                     uint32_t* mappedData) {
-        EXPECT_CALL(
-            clientMemoryTransferService,
-            OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
-                                              sizeof(mReadHandleSerializeDataInfo), _, _))
-            .WillOnce(Return(true));
-    }
-
-    void MockClientReadHandleDeserializeDataUpdateFailure(ClientReadHandle* handle) {
-        EXPECT_CALL(
-            clientMemoryTransferService,
-            OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)),
-                                              sizeof(mReadHandleSerializeDataInfo), _, _))
-            .WillOnce(Return(false));
-    }
-
-    ClientWriteHandle* ExpectWriteHandleCreation(bool mappedAtCreation) {
-        // Create the handle first so we can use it in later expectations.
-        ClientWriteHandle* handle = clientMemoryTransferService.NewWriteHandle();
-
-        EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent)))
-            .WillOnce(InvokeWithoutArgs([=] { return handle; }));
-        if (mappedAtCreation) {
-            EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(handle))
-                .WillOnce(Return(&mBufferContent));
+    void ExpectClientSerializeData(MockClientHandles& clientHandles) {
+        auto* clientHandle = std::get<MockClientWriteHandle*>(clientHandles);
+        if (!clientHandle) {
+            return;
         }
 
-        return handle;
+        EXPECT_CALL(*clientHandle, SizeOfSerializeDataUpdate(_, _)).WillOnce(Return(kDataSize));
+        EXPECT_CALL(*clientHandle, SerializeDataUpdate)
+            .WillOnce(WithArg<0>([&](void* serializePointer) {
+                memcpy(serializePointer, &mClientBufferContent, kBufferSize);
+            }));
     }
+    void ExpectServerSerializeData(MockServerHandles& serverHandles) {
+        auto* serverHandle = std::get<MockServerReadHandle*>(serverHandles);
+        if (!serverHandle) {
+            return;
+        }
 
-    void MockWriteHandleCreationFailure() {
-        EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent)))
-            .WillOnce(InvokeWithoutArgs([=] { return nullptr; }));
-    }
-
-    void ExpectWriteHandleSerialization(ClientWriteHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreateSize(handle))
-            .WillOnce(InvokeWithoutArgs([&] { return sizeof(mSerializeCreateInfo); }));
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreate(handle, _))
-            .WillOnce(WithArg<1>([&](void* serializePointer) {
-                memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo));
-                return sizeof(mSerializeCreateInfo);
+        EXPECT_CALL(*serverHandle, SizeOfSerializeDataUpdate(_, _)).WillOnce(Return(kDataSize));
+        EXPECT_CALL(*serverHandle, SerializeDataUpdate)
+            .WillOnce(WithArg<3>([&](void* serializePointer) {
+                memcpy(serializePointer, &mServerBufferContent, kBufferSize);
+                return kBufferSize;
             }));
     }
 
-    ServerWriteHandle* ExpectServerWriteHandleDeserialization() {
-        // Create the handle first so it can be used in later expectations.
-        ServerWriteHandle* handle = serverMemoryTransferService.NewWriteHandle();
+    void ExpectClientDeserializeData(bool success, MockClientHandles& clientHandles) {
+        auto* clientHandle = std::get<MockClientReadHandle*>(clientHandles);
+        if (!clientHandle) {
+            return;
+        }
 
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)),
-                                             sizeof(mSerializeCreateInfo), _))
-            .WillOnce(WithArg<2>([=](server::MemoryTransferService::WriteHandle** writeHandle) {
-                *writeHandle = handle;
-                return true;
+        EXPECT_CALL(*clientHandle, DeserializeDataUpdate(_, kBufferSize, 0, kBufferSize))
+            .WillOnce(WithArg<0>([&, success](const void* deserializePointer) {
+                if (success) {
+                    // Copy the data manually here.
+                    memcpy(&mClientBufferContent, deserializePointer, kBufferSize);
+                }
+                return success;
             }));
-
-        return handle;
     }
+    void ExpectServerDeserializeData(bool success, MockServerHandles& serverHandles) {
+        auto* serverHandle = std::get<MockServerWriteHandle*>(serverHandles);
+        if (!serverHandle) {
+            return;
+        }
 
-    void MockServerWriteHandleDeserializeFailure() {
-        EXPECT_CALL(serverMemoryTransferService,
-                    OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)),
-                                             sizeof(mSerializeCreateInfo), _))
-            .WillOnce(Return(false));
-    }
-
-    void ExpectClientWriteHandleSerializeDataUpdate(ClientWriteHandle* handle) {
-        EXPECT_CALL(clientMemoryTransferService,
-                    OnWriteHandleSizeOfSerializeDataUpdate(handle, _, _))
-            .WillOnce(InvokeWithoutArgs([&] { return sizeof(mWriteHandleSerializeDataInfo); }));
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeDataUpdate(handle, _, _, _))
-            .WillOnce(WithArg<1>([&](void* serializePointer) {
-                memcpy(serializePointer, &mWriteHandleSerializeDataInfo,
-                       sizeof(mWriteHandleSerializeDataInfo));
-                return sizeof(mWriteHandleSerializeDataInfo);
+        EXPECT_CALL(*serverHandle, DeserializeDataUpdate(_, kBufferSize, 0, kBufferSize))
+            .WillOnce(WithArg<0>([&, success](const void* deserializePointer) {
+                if (success) {
+                    // Copy the data manually here.
+                    memcpy(&mServerBufferContent, deserializePointer, kBufferSize);
+                }
+                return success;
             }));
     }
 
-    void ExpectServerWriteHandleDeserializeDataUpdate(ServerWriteHandle* handle,
-                                                      uint32_t expectedData) {
-        EXPECT_CALL(
-            serverMemoryTransferService,
-            OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
-                                               sizeof(mWriteHandleSerializeDataInfo), _, _))
-            .WillOnce(Return(true));
+    void ExpectClientHandleDestruction(MockClientHandles& clientHandles) {
+        auto* readHandle = std::get<MockClientReadHandle*>(clientHandles);
+        if (readHandle) {
+            EXPECT_CALL(*readHandle, Destroy).Times(1);
+        }
+        auto* writeHandle = std::get<MockClientWriteHandle*>(clientHandles);
+        if (writeHandle) {
+            EXPECT_CALL(*writeHandle, Destroy).Times(1);
+        }
+    }
+    void ExpectServerHandleDestruction(MockServerHandles& serverHandles) {
+        auto* readHandle = std::get<MockServerReadHandle*>(serverHandles);
+        if (readHandle) {
+            EXPECT_CALL(*readHandle, Destroy).Times(1);
+        }
+        auto* writeHandle = std::get<MockServerWriteHandle*>(serverHandles);
+        if (writeHandle) {
+            EXPECT_CALL(*writeHandle, Destroy).Times(1);
+        }
     }
 
-    void MockServerWriteHandleDeserializeDataUpdateFailure(ServerWriteHandle* handle) {
-        EXPECT_CALL(
-            serverMemoryTransferService,
-            OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)),
-                                               sizeof(mWriteHandleSerializeDataInfo), _, _))
-            .WillOnce(Return(false));
+    // Sets expectations for a successful map async call and verifies that the results match for the
+    // MapMode.
+    void ExpectSuccessfulMapAsync(WGPUBuffer apiBuffer,
+                                  wgpu::Buffer buffer,
+                                  MockClientHandles& clientHandles,
+                                  MockServerHandles& serverHandles) {
+        wgpu::MapMode mode = GetParam().mMapMode;
+
+        // Mode independent expectations.
+        EXPECT_CALL(api,
+                    OnBufferMapAsync2(apiBuffer, static_cast<WGPUMapMode>(mode), 0, kBufferSize, _))
+            .WillOnce(InvokeWithoutArgs([&] {
+                api.CallBufferMapAsync2Callback(apiBuffer, WGPUMapAsyncStatus_Success,
+                                                kEmptyOutputStringView);
+            }));
+        EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+
+        switch (mode) {
+            case wgpu::MapMode::Read: {
+                auto* clientHandle = std::get<MockClientReadHandle*>(clientHandles);
+                ASSERT_THAT(clientHandle, NotNull());
+                EXPECT_CALL(*clientHandle, GetData).WillOnce(Return(&mClientBufferContent));
+                EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
+                    .WillOnce(Return(&mServerBufferContent));
+
+                buffer.MapAsync(mode, 0, kBufferSize, wgpu::CallbackMode::AllowSpontaneous,
+                                mMapAsyncCb.Callback());
+
+                // The server should serialize its buffer when the client flushes.
+                ExpectServerSerializeData(serverHandles);
+                FlushClient();
+
+                // The client should deserialize into its buffer when the server flushes.
+                ExpectClientDeserializeData(true, clientHandles);
+                FlushServer();
+
+                // The data between the server and the client should be the same now.
+                EXPECT_EQ(mServerBufferContent, mClientBufferContent);
+                break;
+            }
+            case wgpu::MapMode::Write: {
+                auto* clientHandle = std::get<MockClientWriteHandle*>(clientHandles);
+                ASSERT_THAT(clientHandle, NotNull());
+                EXPECT_CALL(*clientHandle, GetData).WillOnce(Return(&mClientBufferContent));
+                EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+                    .WillOnce(Return(&mClientBufferContent));
+
+                buffer.MapAsync(mode, 0, kBufferSize, wgpu::CallbackMode::AllowSpontaneous,
+                                mMapAsyncCb.Callback());
+                FlushClient();
+                FlushServer();
+                break;
+            }
+            default:
+                DAWN_UNREACHABLE();
+        }
     }
 
-    // Arbitrary values used within tests to check if serialized data is correctly passed
-    // between the client and server. The static data changes between runs of the tests and
-    // test expectations will check that serialized values are passed to the respective
-    // deserialization function.
-    static uint32_t mSerializeCreateInfo;
-    static uint32_t mReadHandleSerializeDataInfo;
-    static uint32_t mWriteHandleSerializeDataInfo;
+    // Note that we pass in the mode explicitly here to allow reuse for mappedAtCreation.
+    void ExpectSuccessfulUnmap(wgpu::MapMode mode,
+                               WGPUBuffer apiBuffer,
+                               wgpu::Buffer buffer,
+                               MockClientHandles& clientHandles,
+                               MockServerHandles& serverHandles) {
+        switch (mode) {
+            case wgpu::MapMode::Read: {
+                // Unmap the buffer.
+                buffer.Unmap();
+                EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+                FlushClient();
+                break;
+            }
+            case wgpu::MapMode::Write: {
+                // The client should serialize its buffer when Unmap is called.
+                ExpectClientSerializeData(clientHandles);
+                buffer.Unmap();
 
-    // Represents the buffer contents for the test.
-    static uint32_t mBufferContent;
+                // The server should deserialize into its buffer when the client flushes.
+                EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+                ExpectServerDeserializeData(true, serverHandles);
+                FlushClient();
 
-    static constexpr size_t kBufferSize = sizeof(mBufferContent);
+                // The data between the server and the client should be the same now.
+                EXPECT_EQ(mServerBufferContent, mClientBufferContent);
+                break;
+            }
+            default:
+                DAWN_UNREACHABLE();
+        }
+    }
 
-    // The client's zero-initialized buffer for writing.
-    uint32_t mMappedBufferContent = 0;
+    void ExpectSuccessfulUnmapAtCreation(WGPUBuffer apiBuffer,
+                                         wgpu::Buffer buffer,
+                                         MockClientHandles& clientHandles,
+                                         MockServerHandles& serverHandles) {
+        ASSERT_TRUE(GetParam().mMappedAtCreation);
 
-    // |mMappedBufferContent| should be set equal to |mUpdatedBufferContent| when the client
-    // performs a write. Test expectations should check that |mBufferContent ==
-    // mUpdatedBufferContent| after all writes are flushed.
-    static uint32_t mUpdatedBufferContent;
+        // Ensure that the contents on the client and server side are different now as a part of the
+        // test.
+        mClientBufferContent = kDataGenerator++;
+        mServerBufferContent = kDataGenerator++;
+        ASSERT_THAT(mClientBufferContent, Ne(mServerBufferContent));
 
-    StrictMock<wire::server::MockMemoryTransferService> serverMemoryTransferService;
-    StrictMock<wire::client::MockMemoryTransferService> clientMemoryTransferService;
+        // If the map mode of the buffer was actually MapRead, we should expect destruction of the
+        // WriteHandles, and setting the ReadHandles instead.
+        bool isRead = GetParam().mMapMode == wgpu::MapMode::Read;
+        if (isRead) {
+            auto* clientWriteHandle = std::get<MockClientWriteHandle*>(clientHandles);
+            ASSERT_THAT(clientWriteHandle, NotNull());
+            EXPECT_CALL(*clientWriteHandle, Destroy).Times(1);
+            auto* clientReadHandle = std::get<MockClientReadHandle*>(clientHandles);
+            ASSERT_THAT(clientReadHandle, NotNull());
+            EXPECT_CALL(*clientReadHandle, GetData).WillOnce(Return(&mClientBufferContent));
+
+            auto* serverHandle = std::get<MockServerWriteHandle*>(serverHandles);
+            ASSERT_THAT(serverHandle, NotNull());
+            EXPECT_CALL(*serverHandle, Destroy).Times(1);
+        }
+
+        ExpectSuccessfulUnmap(wgpu::MapMode::Write, apiBuffer, buffer, clientHandles,
+                              serverHandles);
+
+        // Update the handles accordingly if we are MapRead.
+        if (isRead) {
+            clientHandles =
+                std::make_tuple(std::get<MockClientReadHandle*>(clientHandles), nullptr);
+            serverHandles =
+                std::make_tuple(std::get<MockServerReadHandle*>(serverHandles), nullptr);
+        }
+    }
+
+    // Static atomic that's used to generate different values across tests.
+    static std::atomic<uint32_t> kDataGenerator;
+
+    uint32_t mClientBufferContent = 0;
+    uint32_t mServerBufferContent = 0;
+    static constexpr size_t kBufferSize = sizeof(uint32_t);
+
+    uint32_t mSerializeCreateInfo = kDataGenerator++;
+    static constexpr size_t kDataSize = sizeof(uint32_t);
+
+    StrictMock<MockCppCallback<wgpu::BufferMapCallback2<void>*>> mMapAsyncCb;
+
+    StrictMock<wire::server::MockMemoryTransferService> mServerMTS;
+    StrictMock<wire::client::MockMemoryTransferService> mClientMTS;
 };
+std::atomic<uint32_t> WireMemoryTransferServiceTestBase::kDataGenerator = 1337;
 
-uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337;
-uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349;
-uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242;
-uint32_t WireMemoryTransferServiceTests::mReadHandleSerializeDataInfo = 1394;
-uint32_t WireMemoryTransferServiceTests::mWriteHandleSerializeDataInfo = 1235;
+class WireMemoryTransferServiceBufferHandleTests : public WireMemoryTransferServiceTestBase {};
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    WireMemoryTransferServiceBufferHandleTests,
+    testing::ValuesIn(ParamGenerator<WireMemoryTransferServiceBufferHandleTests::ParamType,
+                                     MapMode,
+                                     MappedAtCreation>({wgpu::MapMode::Read, wgpu::MapMode::Write},
+                                                       {true, false})),
+    &TestParamToString<WireMemoryTransferServiceBufferHandleTests::ParamType>);
 
-// Test successful mapping for reading.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) {
-    WGPUBuffer buffer;
+// Test handle(s) destroy behavior.
+TEST_P(WireMemoryTransferServiceBufferHandleTests, Destroy) {
     WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
 
-    // The client should create and serialize a ReadHandle on creation.
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
+    // The client handles are destroyed on buffer.Destroy().
+    ExpectClientHandleDestruction(clientHandles);
+    buffer.Destroy();
 
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-
-    // The server should deserialize the read handle from the client and then serialize
-    // an initialization message.
-    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-
+    // The server handles are destroyed when the destroy command is flushed.
+    ExpectServerHandleDestruction(serverHandles);
+    EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
     FlushClient();
 
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
+    // Releasing the buffer should be reflected on the server when flushed.
+    buffer = nullptr;
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
+    FlushClient();
+}
 
-    // The handle serialize data update on mapAsync cmd
-    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
+// Test handle(s) creation failure.
+TEST_P(WireMemoryTransferServiceBufferHandleTests, CreationFailure) {
+    ExpectHandleCreation(false);
 
-    // Mock a successful callback
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
-        .WillOnce(Return(&mBufferContent));
-    EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mBufferContent));
+    wgpu::BufferDescriptor descriptor = {};
+    descriptor.size = kBufferSize;
+    descriptor.usage = GetUsage();
 
+    WGPUBuffer apiErrorBuffer = api.GetNewBuffer();
+    wgpu::Buffer errorBuffer = device.CreateBuffer(&descriptor);
+    EXPECT_CALL(api, DeviceCreateErrorBuffer(apiDevice, _)).WillOnce(Return(apiErrorBuffer));
     FlushClient();
 
-    // The client receives a successful callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
+    // Releasing the buffer should be reflected on the server when flushed.
+    errorBuffer = nullptr;
+    EXPECT_CALL(api, BufferRelease(apiErrorBuffer)).Times(1);
+    FlushClient();
+}
 
-    // The client should receive the handle data update message from the server.
-    ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
+// Test handle(s) deserialization (only the handles across the wire, not the data) failure.
+TEST_P(WireMemoryTransferServiceBufferHandleTests, DeserializationFailure) {
+    WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
 
+    // The client should create and serialize the appropriate handles on buffer creation.
+    auto clientHandles = ExpectHandleCreation(true);
+    ExpectHandleSerialization(clientHandles);
+    std::tie(apiBuffer, buffer) = CreateBuffer();
+
+    // When the commands are flushed, mock that the server fails to deserialize the handle.
+    ExpectHandleDeserialization(false);
+    FlushClient(false);
+
+    // The client handles are destroyed when the buffer is released.
+    ExpectClientHandleDestruction(clientHandles);
+    buffer = nullptr;
+
+    // Releasing the buffer should be reflected on the server when flushed.
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
+    FlushClient();
+}
+
+class WireMemoryTransferServiceBufferMapAsyncTests : public WireMemoryTransferServiceTestBase {};
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    WireMemoryTransferServiceBufferMapAsyncTests,
+    testing::ValuesIn(ParamGenerator<WireMemoryTransferServiceBufferMapAsyncTests::ParamType,
+                                     MapMode,
+                                     MappedAtCreation>({wgpu::MapMode::Read, wgpu::MapMode::Write},
+                                                       {true, false})),
+    &TestParamToString<WireMemoryTransferServiceBufferMapAsyncTests::ParamType>);
+
+// Test successful mapping.
+TEST_P(WireMemoryTransferServiceBufferMapAsyncTests, Success) {
+    WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
+
+    // If we were mappedAtCreation, successfully handle that initial unmapping now.
+    if (GetParam().mMappedAtCreation) {
+        ExpectSuccessfulUnmapAtCreation(apiBuffer, buffer, clientHandles, serverHandles);
+    }
+
+    // Ensure that the contents on the client and server side are different now as a part of the
+    // test.
+    mClientBufferContent = kDataGenerator++;
+    mServerBufferContent = kDataGenerator++;
+    ASSERT_THAT(mClientBufferContent, Ne(mServerBufferContent));
+
+    ExpectSuccessfulMapAsync(apiBuffer, buffer, clientHandles, serverHandles);
+    ExpectSuccessfulUnmap(GetParam().mMapMode, apiBuffer, buffer, clientHandles, serverHandles);
+
+    // The client handles are destroyed when the buffer is released.
+    ExpectClientHandleDestruction(clientHandles);
+    buffer = nullptr;
+
+    // The server handles are destroyed when the release command is flushed.
+    ExpectServerHandleDestruction(serverHandles);
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
+    FlushClient();
+}
+
+// Test unsuccessful mapping with error.
+TEST_P(WireMemoryTransferServiceBufferMapAsyncTests, Error) {
+    wgpu::MapMode mode = GetParam().mMapMode;
+
+    WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
+
+    // If we were mappedAtCreation, successfully handle that initial unmapping now.
+    if (GetParam().mMappedAtCreation) {
+        ExpectSuccessfulUnmapAtCreation(apiBuffer, buffer, clientHandles, serverHandles);
+    }
+
+    buffer.MapAsync(mode, 0, kBufferSize, wgpu::CallbackMode::AllowSpontaneous,
+                    mMapAsyncCb.Callback());
+
+    // Make the server respond to the callback with an error.
+    EXPECT_CALL(api,
+                OnBufferMapAsync2(apiBuffer, static_cast<WGPUMapMode>(mode), 0, kBufferSize, _))
+        .WillOnce(InvokeWithoutArgs([&] {
+            api.CallBufferMapAsync2Callback(apiBuffer, WGPUMapAsyncStatus_Error,
+                                            ToOutputStringView("Validation error"));
+        }));
+    FlushClient();
+
+    // The callback should happen when the server flushes the response.
+    EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Error, SizedString("Validation error")))
+        .Times(1);
     FlushServer();
 
-    wgpuBufferUnmap(buffer);
+    // Unmap the buffer.
+    buffer.Unmap();
     EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-
     FlushClient();
 
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
+    // The client handles are destroyed when the buffer is released.
+    ExpectClientHandleDestruction(clientHandles);
+    buffer = nullptr;
 
+    // The server handles are destroyed when the release command is flushed.
+    ExpectServerHandleDestruction(serverHandles);
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
     FlushClient();
 }
 
-// Test ReadHandle destroy behavior
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroy) {
-    WGPUBuffer buffer;
+// Test DeserializeDataUpdate (actual data) failure w.r.t MapAsync.
+TEST_P(WireMemoryTransferServiceBufferMapAsyncTests, DeserializeDataUpdateFailure) {
+    wgpu::MapMode mode = GetParam().mMapMode;
+
     WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
 
-    // The client should create and serialize a ReadHandle on creation.
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
+    // If we were mappedAtCreation, successfully handle that initial unmapping now.
+    if (GetParam().mMappedAtCreation) {
+        ExpectSuccessfulUnmapAtCreation(apiBuffer, buffer, clientHandles, serverHandles);
+    }
 
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-
-    // The server should deserialize the read handle from the client and then serialize
-    // an initialization message.
-    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-
-    FlushClient();
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-    wgpuBufferDestroy(buffer);
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test unsuccessful mapping for reading.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    // The client should create and serialize a ReadHandle on creation.
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
-
-    // The server should deserialize the ReadHandle from the client.
-    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // Mock a failed callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
+    // Set mode independent expectations for the map async call now.
+    EXPECT_CALL(api,
+                OnBufferMapAsync2(apiBuffer, static_cast<WGPUMapMode>(mode), 0, kBufferSize, _))
         .WillOnce(InvokeWithoutArgs([&] {
-            api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_ValidationError);
+            api.CallBufferMapAsync2Callback(apiBuffer, WGPUMapAsyncStatus_Success,
+                                            kEmptyOutputStringView);
         }));
 
-    FlushClient();
+    switch (mode) {
+        case wgpu::MapMode::Read: {
+            EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Unknown, _)).Times(1);
+            EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mServerBufferContent));
 
-    // The client receives an error callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_ValidationError, _)).Times(1);
+            buffer.MapAsync(mode, 0, kBufferSize, wgpu::CallbackMode::AllowSpontaneous,
+                            mMapAsyncCb.Callback());
 
-    FlushServer();
+            // The server should serialize its buffer when the client flushes.
+            ExpectServerSerializeData(serverHandles);
+            FlushClient();
 
-    wgpuBufferUnmap(buffer);
+            // Mock that the client fails to deserialize into its buffer when the server flushes.
+            ExpectClientDeserializeData(false, clientHandles);
+            FlushServer(false);
+            break;
+        }
+        case wgpu::MapMode::Write: {
+            EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+            EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
+                .WillOnce(Return(&mClientBufferContent));
+            auto* clientHandle = std::get<MockClientWriteHandle*>(clientHandles);
+            ASSERT_THAT(clientHandle, NotNull());
+            EXPECT_CALL(*clientHandle, GetData).WillOnce(Return(&mClientBufferContent));
 
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
+            buffer.MapAsync(mode, 0, kBufferSize, wgpu::CallbackMode::AllowSpontaneous,
+                            mMapAsyncCb.Callback());
+            FlushClient();
+            FlushServer();
 
-    FlushClient();
+            // The client should serialize its buffer when Unmap is called.
+            ExpectClientSerializeData(clientHandles);
+            buffer.Unmap();
 
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
+            // Mock that the server fails to deserialize into its buffer when the client flushes.
+            ExpectServerDeserializeData(false, serverHandles);
+            FlushClient(false);
+            break;
+        }
+        default:
+            DAWN_UNREACHABLE();
+    }
 
+    // The client handles are destroyed when the buffer is released.
+    ExpectClientHandleDestruction(clientHandles);
+    buffer = nullptr;
+
+    // The server handles are destroyed when the release command is flushed.
+    ExpectServerHandleDestruction(serverHandles);
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
     FlushClient();
 }
 
-// Test ReadHandle creation failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) {
-    // Mock a ReadHandle creation failure
-    MockReadHandleCreationFailure();
-
-    WGPUBufferDescriptor descriptor = {};
-    descriptor.size = kBufferSize;
-    descriptor.usage = WGPUBufferUsage_MapRead;
-
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-    wgpuBufferRelease(buffer);
-}
-
-// Test MapRead DeserializeReadHandle failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) {
-    WGPUBuffer buffer;
+// Test mapping and then destroying the buffer before unmapping on the client side.
+TEST_P(WireMemoryTransferServiceBufferMapAsyncTests, DestroyBeforeUnmap) {
     WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
 
-    // The client should create and serialize a ReadHandle on mapping for reading..
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
+    // If we were mappedAtCreation, successfully handle that initial unmapping now.
+    if (GetParam().mMappedAtCreation) {
+        ExpectSuccessfulUnmapAtCreation(apiBuffer, buffer, clientHandles, serverHandles);
+    }
 
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-
-    // Mock a Deserialization failure.
-    MockServerReadHandleDeserializeFailure();
-
-    FlushClient(false);
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test read handle DeserializeDataUpdate failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeDataUpdateFailure) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    // The client should create and serialize a ReadHandle on mapping for reading.
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
-
-    // The server should deserialize the read handle from the client and then serialize
-    // an initialization message.
-    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // The handle serialize data update on mapAsync cmd
-    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
-
-    // Mock a successful callback
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mBufferContent));
-
-    FlushClient();
-
-    // The client should receive the handle data update message from the server.
-    // Mock a deserialization failure.
-    MockClientReadHandleDeserializeDataUpdateFailure(clientHandle);
-
-    // Failed deserialization is a fatal failure and the client synchronously receives a
-    // DEVICE_LOST callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Unknown, _)).Times(1);
-
-    FlushServer(false);
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test mapping for reading destroying the buffer before unmapping on the client side.
-TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    // The client should create and serialize a ReadHandle on mapping for reading..
-    ClientReadHandle* clientHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientHandle);
-
-    // The server should deserialize the read handle from the client and then serialize
-    // an initialization message.
-    ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead);
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // The handle serialize data update on mapAsync cmd
-    ExpectServerReadHandleSerializeDataUpdate(serverHandle);
-
-    // Mock a successful callback
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle))
-        .WillOnce(Return(&mBufferContent));
-    EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mBufferContent));
-
-    FlushClient();
-
-    // The client receives a successful callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
-
-    // The client should receive the handle data update message from the server.
-    ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent);
-
-    FlushServer();
+    ExpectSuccessfulMapAsync(apiBuffer, buffer, clientHandles, serverHandles);
 
     // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
     // immediately, both in the client and server side.
     {
-        EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
-        wgpuBufferDestroy(buffer);
+        // Destroying the buffer should immediately destroy the client handles.
+        ExpectClientHandleDestruction(clientHandles);
+        buffer.Destroy();
 
-        EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
+        // Flushing the client should destroy the server handles.
+        ExpectServerHandleDestruction(serverHandles);
         EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
         FlushClient();
 
-        // The handle is already destroyed so unmap only results in a server unmap call.
-        wgpuBufferUnmap(buffer);
-
+        // The handle(s) are already destroyed so unmap only results in a server unmap call.
+        buffer.Unmap();
         EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
         FlushClient();
     }
 
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
+    // The handle(s) are already destroyed so release only results in a server release call.
+    buffer = nullptr;
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
     FlushClient();
 }
 
-// Test successful mapping for writing.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
-    WGPUBuffer buffer;
+class WireMemoryTransferServiceBufferMappedAtCreationTests
+    : public WireMemoryTransferServiceTestBase {};
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    WireMemoryTransferServiceBufferMappedAtCreationTests,
+    testing::ValuesIn(
+        ParamGenerator<WireMemoryTransferServiceBufferMappedAtCreationTests::ParamType,
+                       MapMode,
+                       MappedAtCreation>({wgpu::MapMode::Read, wgpu::MapMode::Write}, {true})),
+    &TestParamToString<WireMemoryTransferServiceBufferMappedAtCreationTests::ParamType>);
+
+// Test DeserializeDataUpdate (actual data) failure w.r.t for the initial Unmap.
+TEST_P(WireMemoryTransferServiceBufferMappedAtCreationTests, DeserializeDataUpdateFailure) {
     WGPUBuffer apiBuffer;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
 
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
+    // The client should serialize its buffer when Unmap is called. Additionally, if we are in read
+    // mode, the client side WriteHandle used for mappedAtCreation should be destroyed now and it
+    // should reset to use the ReadHandle.
+    ExpectClientSerializeData(clientHandles);
+    if (GetParam().mMapMode == wgpu::MapMode::Read) {
+        auto* clientWriteHandle = std::get<MockClientWriteHandle*>(clientHandles);
+        ASSERT_THAT(clientWriteHandle, NotNull());
+        EXPECT_CALL(*clientWriteHandle, Destroy).Times(1);
+        auto* clientReadHandle = std::get<MockClientReadHandle*>(clientHandles);
+        ASSERT_THAT(clientReadHandle, NotNull());
+        EXPECT_CALL(*clientReadHandle, GetData).WillOnce(Return(&mClientBufferContent));
+        clientHandles = std::make_tuple(std::get<MockClientReadHandle*>(clientHandles), nullptr);
+    }
+    buffer.Unmap();
 
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // Mock a successful callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
-        .WillOnce(Return(&mBufferContent));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mMappedBufferContent));
-
-    FlushClient();
-
-    // The client receives a successful callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
-
-    FlushServer();
-
-    // The client writes to the handle contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // The client will then serialize data update and destroy the handle on Unmap()
-    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
-
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message.
-    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
-
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test WriteHandle destroy behavior
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroy) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    FlushClient();
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    wgpuBufferDestroy(buffer);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test unsuccessful MapWrite.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    // The client should create and serialize a WriteHandle on buffer creation with MapWrite
-    // usage.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // Mock an error callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs([&] {
-            api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_ValidationError);
-        }));
-
-    FlushClient();
-
-    // The client receives an error callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_ValidationError, _)).Times(1);
-
-    FlushServer();
-
-    wgpuBufferUnmap(buffer);
-
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test WriteHandle creation failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) {
-    // Mock a WriteHandle creation failure
-    MockWriteHandleCreationFailure();
-
-    WGPUBufferDescriptor descriptor = {};
-    descriptor.size = kBufferSize;
-    descriptor.usage = WGPUBufferUsage_MapWrite;
-
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-    wgpuBufferRelease(buffer);
-}
-
-// Test MapWrite DeserializeWriteHandle failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    // The client should create and serialize a WriteHandle on buffer creation with MapWrite
-    // usage.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // Mock a deserialization failure.
-    MockServerWriteHandleDeserializeFailure();
-
+    // Mock that the server fails to deserialize into its buffer when the client flushes.
+    ExpectServerDeserializeData(false, serverHandles);
     FlushClient(false);
 
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
+    // The client handles are destroyed when the buffer is released.
+    ExpectClientHandleDestruction(clientHandles);
+    buffer = nullptr;
 
+    // The server handles are destroyed when the release command is flushed.
+    ExpectServerHandleDestruction(serverHandles);
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
     FlushClient();
 }
 
-// Test MapWrite DeserializeDataUpdate failure.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeDataUpdateFailure) {
-    WGPUBuffer buffer;
+// Test mapping and then destroying the buffer before unmapping on the client side.
+TEST_P(WireMemoryTransferServiceBufferMappedAtCreationTests, DestroyBeforeUnmap) {
     WGPUBuffer apiBuffer;
-
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // Mock a successful callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
-        .WillOnce(Return(&mBufferContent));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mMappedBufferContent));
-
-    FlushClient();
-
-    // The client receives a success callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
-
-    FlushServer();
-
-    // The client writes to the handle contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // The client will then serialize data update
-    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
-
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message. Mock a deserialization failure.
-    MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
-
-    FlushClient(false);
-
-    // The handle is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test MapWrite destroying the buffer before unmapping on the client side.
-TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) {
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    FlushClient();
-
-    wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr);
-
-    // Mock a successful callback.
-    EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _))
-        .WillOnce(InvokeWithoutArgs(
-            [&] { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); }));
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle))
-        .WillOnce(Return(&mBufferContent));
-    EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize))
-        .WillOnce(Return(&mMappedBufferContent));
-
-    FlushClient();
-
-    // The client receives a successful callback.
-    EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
-
-    FlushServer();
-
-    // The client writes to the handle contents.
-    mMappedBufferContent = mUpdatedBufferContent;
+    wgpu::Buffer buffer;
+    MockClientHandles clientHandles;
+    MockServerHandles serverHandles;
+    std::tie(apiBuffer, buffer, clientHandles, serverHandles) = CreateValidBuffer();
 
     // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
     // immediately, both in the client and server side.
     {
-        // The handle is destroyed once the buffer is destroyed.
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
+        // Destroying the buffer should immediately destroy the client handles.
+        ExpectClientHandleDestruction(clientHandles);
+        buffer.Destroy();
 
-        wgpuBufferDestroy(buffer);
-
-        EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
+        // Flushing the client should destroy the server handles.
+        ExpectServerHandleDestruction(serverHandles);
         EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
         FlushClient();
 
-        // The handle is already destroyed so unmap only results in a server unmap call.
-        wgpuBufferUnmap(buffer);
-
+        // The handle(s) are already destroyed so unmap only results in a server unmap call.
+        buffer.Unmap();
         EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
         FlushClient();
     }
 
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test successful buffer creation with mappedAtCreation = true.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationSuccess) {
-    // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped();
-    FlushClient();
-
-    // Update the mapped contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
-    // destroy it.
-    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message.
-    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
-
-    // After the handle is updated it can be destroyed.
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test buffer creation with mappedAtCreation WriteHandle creation failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationWriteHandleCreationFailure) {
-    // Mock a WriteHandle creation failure
-    MockWriteHandleCreationFailure();
-
-    WGPUBufferDescriptor descriptor = {};
-    descriptor.size = sizeof(mBufferContent);
-    descriptor.mappedAtCreation = true;
-
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-    EXPECT_EQ(nullptr, buffer);
-}
-
-// Test buffer creation with mappedAtCreation DeserializeWriteHandle failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeWriteHandleFailure) {
-    // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    // The server should then deserialize the WriteHandle from the client.
-    MockServerWriteHandleDeserializeFailure();
-
-    WGPUBufferDescriptor descriptor = {};
-    descriptor.size = sizeof(mBufferContent);
-    descriptor.mappedAtCreation = true;
-
-    WGPUBuffer apiBuffer = api.GetNewBuffer();
-
-    WGPUBuffer buffer = wgpuDeviceCreateBuffer(cDevice, &descriptor);
-
-    EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer));
-    // Now bufferGetMappedRange won't be called if deserialize writeHandle fails
-
-    FlushClient(false);
-
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test buffer creation with mappedAtCreation = true DeserializeDataUpdate failure.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeDataUpdateFailure) {
-    // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped();
-    FlushClient();
-
-    // Update the mapped contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
-    // destroy it.
-    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message. Mock a deserialization failure.
-    MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle);
-
-    FlushClient(false);
-
-    // Failed BufferUpdateMappedData cmd will early return so BufferUnmap is not processed.
-    // The server side writeHandle is destructed at buffer destruction.
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test mappedAtCreation=true destroying the buffer before unmapping on the client side.
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDestroyBeforeUnmap) {
-    // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
-    ExpectWriteHandleSerialization(clientHandle);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped();
-    FlushClient();
-
-    // Update the mapped contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping
-    // immediately, both in the client and server side.
-    {
-        EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-        wgpuBufferDestroy(buffer);
-
-        EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-        EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1);
-        FlushClient();
-
-        // The handle is already destroyed so unmap only results in a server unmap call.
-        wgpuBufferUnmap(buffer);
-
-        EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-        FlushClient();
-    }
-
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test a buffer with mappedAtCreation and MapRead usage destroy WriteHandle on unmap and switch
-// data pointer to ReadHandle
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapReadSuccess) {
-    // The client should create and serialize a ReadHandle and a WriteHandle on
-    // createBufferMapped.
-    ClientReadHandle* clientReadHandle = ExpectReadHandleCreation();
-    ExpectReadHandleSerialization(clientReadHandle);
-    ClientWriteHandle* clientWriteHandle = ExpectWriteHandleCreation(true);
-    ExpectWriteHandleSerialization(clientWriteHandle);
-
-    // The server should then deserialize a ReadHandle and a WriteHandle from the client.
-    ServerReadHandle* serverReadHandle = ExpectServerReadHandleDeserialize();
-    ServerWriteHandle* serverWriteHandle = ExpectServerWriteHandleDeserialization();
-
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapRead);
-    FlushClient();
-
-    // Update the mapped contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // When the client Unmaps the buffer, it will serialize data update writes to the handle and
-    // destroy it.
-    ExpectClientWriteHandleSerializeDataUpdate(clientWriteHandle);
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientWriteHandle)).Times(1);
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientReadHandle))
-        .WillOnce(Return(&mBufferContent));
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message.
-    ExpectServerWriteHandleDeserializeDataUpdate(serverWriteHandle, mUpdatedBufferContent);
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverWriteHandle)).Times(1);
-    FlushClient();
-
-    // The ReadHandle will be destroyed on buffer destroy.
-    EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientReadHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverReadHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
-    FlushClient();
-}
-
-// Test WriteHandle preserves after unmap for a buffer with mappedAtCreation and MapWrite usage
-TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapWriteSuccess) {
-    // The client should create and serialize a WriteHandle on createBufferMapped.
-    ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true);
-
-    ExpectWriteHandleSerialization(clientHandle);
-
-    // The server should then deserialize the WriteHandle from the client.
-    ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
-
-    WGPUBuffer buffer;
-    WGPUBuffer apiBuffer;
-    std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapWrite);
-    FlushClient();
-
-    // Update the mapped contents.
-    mMappedBufferContent = mUpdatedBufferContent;
-
-    // When the client Unmaps the buffer, it will serialize data update writes to the handle.
-    ExpectClientWriteHandleSerializeDataUpdate(clientHandle);
-
-    wgpuBufferUnmap(buffer);
-
-    // The server deserializes the data update message.
-    ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent);
-    EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
-
-    FlushClient();
-
-    // The writeHandle is preserved after unmap and is destroyed once the buffer is destroyed.
-    EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
-    EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    wgpuBufferRelease(buffer);
-
+    // The handle(s) are already destroyed so release only results in a server release call.
+    buffer = nullptr;
+    EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1);
     FlushClient();
 }
 
diff --git a/src/dawn/wire/client/ClientMemoryTransferService_mock.cpp b/src/dawn/wire/client/ClientMemoryTransferService_mock.cpp
index e70bfcc..abf1c43 100644
--- a/src/dawn/wire/client/ClientMemoryTransferService_mock.cpp
+++ b/src/dawn/wire/client/ClientMemoryTransferService_mock.cpp
@@ -32,82 +32,12 @@
 
 namespace dawn::wire::client {
 
-MockMemoryTransferService::MockReadHandle::MockReadHandle(MockMemoryTransferService* service)
-    : ReadHandle(), mService(service) {}
-
 MockMemoryTransferService::MockReadHandle::~MockReadHandle() {
-    mService->OnReadHandleDestroy(this);
+    Destroy();
 }
 
-size_t MockMemoryTransferService::MockReadHandle::SerializeCreateSize() {
-    return mService->OnReadHandleSerializeCreateSize(this);
-}
-
-void MockMemoryTransferService::MockReadHandle::SerializeCreate(void* serializePointer) {
-    mService->OnReadHandleSerializeCreate(this, serializePointer);
-}
-
-const void* MockMemoryTransferService::MockReadHandle::GetData() {
-    return mService->OnReadHandleGetData(this);
-}
-
-bool MockMemoryTransferService::MockReadHandle::DeserializeDataUpdate(
-    const void* deserializePointer,
-    size_t deserializeSize,
-    size_t offset,
-    size_t size) {
-    DAWN_ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-    return mService->OnReadHandleDeserializeDataUpdate(
-        this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset, size);
-}
-
-MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
-    : WriteHandle(), mService(service) {}
-
 MockMemoryTransferService::MockWriteHandle::~MockWriteHandle() {
-    mService->OnWriteHandleDestroy(this);
-}
-
-size_t MockMemoryTransferService::MockWriteHandle::SerializeCreateSize() {
-    return mService->OnWriteHandleSerializeCreateSize(this);
-}
-
-void MockMemoryTransferService::MockWriteHandle::SerializeCreate(void* serializePointer) {
-    mService->OnWriteHandleSerializeCreate(this, serializePointer);
-}
-
-void* MockMemoryTransferService::MockWriteHandle::GetData() {
-    return mService->OnWriteHandleGetData(this);
-}
-
-size_t MockMemoryTransferService::MockWriteHandle::SizeOfSerializeDataUpdate(size_t offset,
-                                                                             size_t size) {
-    return mService->OnWriteHandleSizeOfSerializeDataUpdate(this, offset, size);
-}
-
-void MockMemoryTransferService::MockWriteHandle::SerializeDataUpdate(void* serializePointer,
-                                                                     size_t offset,
-                                                                     size_t size) {
-    mService->OnWriteHandleSerializeDataUpdate(this, serializePointer, offset, size);
-}
-
-MockMemoryTransferService::MockMemoryTransferService() = default;
-MockMemoryTransferService::~MockMemoryTransferService() = default;
-
-MockMemoryTransferService::ReadHandle* MockMemoryTransferService::CreateReadHandle(size_t size) {
-    return OnCreateReadHandle(size);
-}
-
-MockMemoryTransferService::WriteHandle* MockMemoryTransferService::CreateWriteHandle(size_t size) {
-    return OnCreateWriteHandle(size);
-}
-
-MockMemoryTransferService::MockReadHandle* MockMemoryTransferService::NewReadHandle() {
-    return new MockReadHandle(this);
-}
-
-MockMemoryTransferService::MockWriteHandle* MockMemoryTransferService::NewWriteHandle() {
-    return new MockWriteHandle(this);
+    Destroy();
 }
 
 }  // namespace dawn::wire::client
diff --git a/src/dawn/wire/client/ClientMemoryTransferService_mock.h b/src/dawn/wire/client/ClientMemoryTransferService_mock.h
index ad5a389..5256dc7 100644
--- a/src/dawn/wire/client/ClientMemoryTransferService_mock.h
+++ b/src/dawn/wire/client/ClientMemoryTransferService_mock.h
@@ -40,72 +40,29 @@
   public:
     class MockReadHandle : public ReadHandle {
       public:
-        explicit MockReadHandle(MockMemoryTransferService* service);
         ~MockReadHandle() override;
+        MOCK_METHOD(void, Destroy, ());
 
-        size_t SerializeCreateSize() override;
-        void SerializeCreate(void* serializePointer) override;
-        const void* GetData() override;
-        bool DeserializeDataUpdate(const void* deserializePointer,
-                                   size_t deserializeSize,
-                                   size_t offset,
-                                   size_t size) override;
-
-      private:
-        raw_ptr<MockMemoryTransferService> mService;
+        MOCK_METHOD(size_t, SerializeCreateSize, (), (override));
+        MOCK_METHOD(void, SerializeCreate, (void*), (override));
+        MOCK_METHOD(const void*, GetData, (), (override));
+        MOCK_METHOD(bool, DeserializeDataUpdate, (const void*, size_t, size_t, size_t), (override));
     };
 
     class MockWriteHandle : public WriteHandle {
       public:
-        explicit MockWriteHandle(MockMemoryTransferService* service);
         ~MockWriteHandle() override;
+        MOCK_METHOD(void, Destroy, ());
 
-        size_t SerializeCreateSize() override;
-        void SerializeCreate(void* serializePointer) override;
-        void* GetData() override;
-        size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
-        void SerializeDataUpdate(void* serializePointer, size_t offset, size_t size) override;
-
-      private:
-        raw_ptr<MockMemoryTransferService> mService;
+        MOCK_METHOD(size_t, SerializeCreateSize, (), (override));
+        MOCK_METHOD(void, SerializeCreate, (void*), (override));
+        MOCK_METHOD(void*, GetData, (), (override));
+        MOCK_METHOD(size_t, SizeOfSerializeDataUpdate, (size_t, size_t), (override));
+        MOCK_METHOD(void, SerializeDataUpdate, (void*, size_t, size_t), (override));
     };
 
-    MockMemoryTransferService();
-    ~MockMemoryTransferService() override;
-
-    ReadHandle* CreateReadHandle(size_t) override;
-    WriteHandle* CreateWriteHandle(size_t) override;
-
-    MockReadHandle* NewReadHandle();
-    MockWriteHandle* NewWriteHandle();
-
-    MOCK_METHOD(ReadHandle*, OnCreateReadHandle, (size_t));
-    MOCK_METHOD(WriteHandle*, OnCreateWriteHandle, (size_t));
-
-    MOCK_METHOD(size_t, OnReadHandleSerializeCreateSize, (const ReadHandle*));
-    MOCK_METHOD(void, OnReadHandleSerializeCreate, (const ReadHandle*, void* serializePointer));
-    MOCK_METHOD((const void*), OnReadHandleGetData, (const ReadHandle*));
-    MOCK_METHOD(bool,
-                OnReadHandleDeserializeDataUpdate,
-                (const ReadHandle*,
-                 const uint32_t* deserializePointer,
-                 size_t deserializeSize,
-                 size_t offset,
-                 size_t size));
-    MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle*));
-
-    MOCK_METHOD(size_t, OnWriteHandleSerializeCreateSize, (const void* WriteHandle));
-    MOCK_METHOD(void,
-                OnWriteHandleSerializeCreate,
-                (const void* WriteHandle, void* serializePointer));
-    MOCK_METHOD((void*), OnWriteHandleGetData, (const void* WriteHandle));
-    MOCK_METHOD(size_t,
-                OnWriteHandleSizeOfSerializeDataUpdate,
-                (const void* WriteHandle, size_t offset, size_t size));
-    MOCK_METHOD(size_t,
-                OnWriteHandleSerializeDataUpdate,
-                (const void* WriteHandle, void* serializePointer, size_t offset, size_t size));
-    MOCK_METHOD(void, OnWriteHandleDestroy, (const void* WriteHandle));
+    MOCK_METHOD(ReadHandle*, CreateReadHandle, (size_t), (override));
+    MOCK_METHOD(WriteHandle*, CreateWriteHandle, (size_t), (override));
 };
 
 }  // namespace dawn::wire::client
diff --git a/src/dawn/wire/server/ServerMemoryTransferService_mock.cpp b/src/dawn/wire/server/ServerMemoryTransferService_mock.cpp
index 6472ca7..800ea32 100644
--- a/src/dawn/wire/server/ServerMemoryTransferService_mock.cpp
+++ b/src/dawn/wire/server/ServerMemoryTransferService_mock.cpp
@@ -31,71 +31,12 @@
 
 namespace dawn::wire::server {
 
-MockMemoryTransferService::MockReadHandle::MockReadHandle(MockMemoryTransferService* service)
-    : ReadHandle(), mService(service) {}
-
 MockMemoryTransferService::MockReadHandle::~MockReadHandle() {
-    mService->OnReadHandleDestroy(this);
+    Destroy();
 }
 
-size_t MockMemoryTransferService::MockReadHandle::SizeOfSerializeDataUpdate(size_t offset,
-                                                                            size_t size) {
-    return mService->OnReadHandleSizeOfSerializeDataUpdate(this, offset, size);
-}
-
-void MockMemoryTransferService::MockReadHandle::SerializeDataUpdate(const void* data,
-                                                                    size_t offset,
-                                                                    size_t size,
-                                                                    void* serializePointer) {
-    mService->OnReadHandleSerializeDataUpdate(this, data, offset, size, serializePointer);
-}
-
-MockMemoryTransferService::MockWriteHandle::MockWriteHandle(MockMemoryTransferService* service)
-    : WriteHandle(), mService(service) {}
-
 MockMemoryTransferService::MockWriteHandle::~MockWriteHandle() {
-    mService->OnWriteHandleDestroy(this);
-}
-
-const uint32_t* MockMemoryTransferService::MockWriteHandle::GetData() const {
-    return reinterpret_cast<const uint32_t*>(mTargetData);
-}
-
-bool MockMemoryTransferService::MockWriteHandle::DeserializeDataUpdate(
-    const void* deserializePointer,
-    size_t deserializeSize,
-    size_t offset,
-    size_t size) {
-    DAWN_ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-    return mService->OnWriteHandleDeserializeDataUpdate(
-        this, reinterpret_cast<const uint32_t*>(deserializePointer), deserializeSize, offset, size);
-}
-
-MockMemoryTransferService::MockMemoryTransferService() = default;
-MockMemoryTransferService::~MockMemoryTransferService() = default;
-
-bool MockMemoryTransferService::DeserializeReadHandle(const void* deserializePointer,
-                                                      size_t deserializeSize,
-                                                      ReadHandle** readHandle) {
-    DAWN_ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-    return OnDeserializeReadHandle(reinterpret_cast<const uint32_t*>(deserializePointer),
-                                   deserializeSize, readHandle);
-}
-
-bool MockMemoryTransferService::DeserializeWriteHandle(const void* deserializePointer,
-                                                       size_t deserializeSize,
-                                                       WriteHandle** writeHandle) {
-    DAWN_ASSERT(deserializeSize % sizeof(uint32_t) == 0);
-    return OnDeserializeWriteHandle(reinterpret_cast<const uint32_t*>(deserializePointer),
-                                    deserializeSize, writeHandle);
-}
-
-MockMemoryTransferService::MockReadHandle* MockMemoryTransferService::NewReadHandle() {
-    return new MockReadHandle(this);
-}
-
-MockMemoryTransferService::MockWriteHandle* MockMemoryTransferService::NewWriteHandle() {
-    return new MockWriteHandle(this);
+    Destroy();
 }
 
 }  // namespace dawn::wire::server
diff --git a/src/dawn/wire/server/ServerMemoryTransferService_mock.h b/src/dawn/wire/server/ServerMemoryTransferService_mock.h
index e96b64b..ae2a8d3 100644
--- a/src/dawn/wire/server/ServerMemoryTransferService_mock.h
+++ b/src/dawn/wire/server/ServerMemoryTransferService_mock.h
@@ -40,81 +40,23 @@
   public:
     class MockReadHandle : public ReadHandle {
       public:
-        explicit MockReadHandle(MockMemoryTransferService* service);
         ~MockReadHandle() override;
+        MOCK_METHOD(void, Destroy, ());
 
-        size_t SizeOfSerializeDataUpdate(size_t offset, size_t size) override;
-        void SerializeDataUpdate(const void* data,
-                                 size_t offset,
-                                 size_t size,
-                                 void* serializePointer) override;
-
-      private:
-        raw_ptr<MockMemoryTransferService> mService;
+        MOCK_METHOD(size_t, SizeOfSerializeDataUpdate, (size_t, size_t), (override));
+        MOCK_METHOD(void, SerializeDataUpdate, (const void*, size_t, size_t, void*), (override));
     };
 
     class MockWriteHandle : public WriteHandle {
       public:
-        explicit MockWriteHandle(MockMemoryTransferService* service);
         ~MockWriteHandle() override;
+        MOCK_METHOD(void, Destroy, ());
 
-        bool DeserializeDataUpdate(const void* deserializePointer,
-                                   size_t deserializeSize,
-                                   size_t offset,
-                                   size_t size) override;
-
-        const uint32_t* GetData() const;
-
-      private:
-        raw_ptr<MockMemoryTransferService> mService;
+        MOCK_METHOD(bool, DeserializeDataUpdate, (const void*, size_t, size_t, size_t), (override));
     };
 
-    MockMemoryTransferService();
-    ~MockMemoryTransferService() override;
-
-    bool DeserializeReadHandle(const void* deserializePointer,
-                               size_t deserializeSize,
-                               ReadHandle** readHandle) override;
-
-    bool DeserializeWriteHandle(const void* deserializePointer,
-                                size_t deserializeSize,
-                                WriteHandle** writeHandle) override;
-
-    MockReadHandle* NewReadHandle();
-    MockWriteHandle* NewWriteHandle();
-
-    MOCK_METHOD(bool,
-                OnDeserializeReadHandle,
-                (const uint32_t* deserializePointer,
-                 size_t deserializeSize,
-                 ReadHandle** readHandle));
-
-    MOCK_METHOD(bool,
-                OnDeserializeWriteHandle,
-                (const uint32_t* deserializePointer,
-                 size_t deserializeSize,
-                 WriteHandle** writeHandle));
-
-    MOCK_METHOD(size_t,
-                OnReadHandleSizeOfSerializeDataUpdate,
-                (const ReadHandle* readHandle, size_t offset, size_t size));
-    MOCK_METHOD(void,
-                OnReadHandleSerializeDataUpdate,
-                (const ReadHandle* readHandle,
-                 const void* data,
-                 size_t offset,
-                 size_t size,
-                 void* serializePointer));
-    MOCK_METHOD(void, OnReadHandleDestroy, (const ReadHandle* readHandle));
-
-    MOCK_METHOD(bool,
-                OnWriteHandleDeserializeDataUpdate,
-                (const WriteHandle* writeHandle,
-                 const uint32_t* deserializePointer,
-                 size_t deserializeSize,
-                 size_t offset,
-                 size_t size));
-    MOCK_METHOD(void, OnWriteHandleDestroy, (const WriteHandle* writeHandle));
+    MOCK_METHOD(bool, DeserializeReadHandle, (const void*, size_t, ReadHandle**), (override));
+    MOCK_METHOD(bool, DeserializeWriteHandle, (const void*, size_t, WriteHandle**), (override));
 };
 
 }  // namespace dawn::wire::server