[wgpu-header] Clean up for 2nd userdata for mapAsync and workDone.

- Updates relevant usages to use the new C++ helpers.
- Updates Null backend to more realistically simulate queue work.
- Note that these are done together and not in separate changes
  because they have some inter-dependency.

Bug: 42241461
Change-Id: I3866972355cf3ae2b3f82a1c41d47dbbc5308535
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/188445
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Loko Kung <lokokung@google.com>
diff --git a/src/dawn/native/Buffer.cpp b/src/dawn/native/Buffer.cpp
index 9cdffb0..554c5d8 100644
--- a/src/dawn/native/Buffer.cpp
+++ b/src/dawn/native/Buffer.cpp
@@ -669,6 +669,10 @@
                              size_t size,
                              WGPUBufferMapCallback callback,
                              void* userdata) {
+    GetInstance()->EmitDeprecationWarning(
+        "Old MapAsync APIs are deprecated. If using C please pass a CallbackInfo "
+        "struct that has two userdatas. Otherwise, if using C++, please use templated helpers.");
+
     // Check for an existing pending map first because it just
     // rejects the callback and doesn't produce a validation error.
     if (mState == BufferState::PendingMap) {
@@ -721,6 +725,10 @@
                                 size_t offset,
                                 size_t size,
                                 const BufferMapCallbackInfo& callbackInfo) {
+    GetInstance()->EmitDeprecationWarning(
+        "Old MapAsync APIs are deprecated. If using C please pass a CallbackInfo "
+        "struct that has two userdatas. Otherwise, if using C++, please use templated helpers.");
+
     // TODO(crbug.com/dawn/2052): Once we always return a future, change this to log to the instance
     // (note, not raise a validation error to the device) and return the null future.
     DAWN_ASSERT(callbackInfo.nextInChain == nullptr);
diff --git a/src/dawn/native/Queue.cpp b/src/dawn/native/Queue.cpp
index 9c5e924..eb6a732 100644
--- a/src/dawn/native/Queue.cpp
+++ b/src/dawn/native/Queue.cpp
@@ -257,6 +257,10 @@
 }
 
 void QueueBase::APIOnSubmittedWorkDone(WGPUQueueWorkDoneCallback callback, void* userdata) {
+    GetInstance()->EmitDeprecationWarning(
+        "Old OnSubmittedWorkDone APIs are deprecated. If using C please pass a CallbackInfo "
+        "struct that has two userdatas. Otherwise, if using C++, please use templated helpers.");
+
     // The error status depends on the type of error so we let the validation function choose it
     wgpu::QueueWorkDoneStatus status;
     if (GetDevice()->ConsumedError(ValidateOnSubmittedWorkDone(&status))) {
@@ -279,6 +283,10 @@
 }
 
 Future QueueBase::APIOnSubmittedWorkDoneF(const QueueWorkDoneCallbackInfo& callbackInfo) {
+    GetInstance()->EmitDeprecationWarning(
+        "Old OnSubmittedWorkDone APIs are deprecated. If using C please pass a CallbackInfo "
+        "struct that has two userdatas. Otherwise, if using C++, please use templated helpers.");
+
     return APIOnSubmittedWorkDone2(
         {ToAPI(callbackInfo.nextInChain), ToAPI(callbackInfo.mode),
          [](WGPUQueueWorkDoneStatus status, void* callback, void* userdata) {
diff --git a/src/dawn/native/null/DeviceNull.cpp b/src/dawn/native/null/DeviceNull.cpp
index b63c5c8..c0720268 100644
--- a/src/dawn/native/null/DeviceNull.cpp
+++ b/src/dawn/native/null/DeviceNull.cpp
@@ -325,7 +325,6 @@
     mPendingOperations.clear();
 
     DAWN_TRY(GetQueue()->CheckPassedSerials());
-    GetQueue()->IncrementLastSubmittedCommandSerial();
 
     return {};
 }
@@ -385,6 +384,7 @@
 }
 
 MaybeError Buffer::MapAsyncImpl(wgpu::MapMode mode, size_t offset, size_t size) {
+    GetDevice()->GetQueue()->IncrementLastSubmittedCommandSerial();
     return {};
 }
 
@@ -426,6 +426,7 @@
     Device* device = ToBackend(GetDevice());
 
     DAWN_TRY(device->SubmitPendingOperations());
+    IncrementLastSubmittedCommandSerial();
 
     return {};
 }
diff --git a/src/dawn/tests/DawnTest.cpp b/src/dawn/tests/DawnTest.cpp
index e6fdcbd..78a2d90 100644
--- a/src/dawn/tests/DawnTest.cpp
+++ b/src/dawn/tests/DawnTest.cpp
@@ -48,6 +48,7 @@
 #include "dawn/native/Device.h"
 #include "dawn/native/Instance.h"
 #include "dawn/native/dawn_platform.h"
+#include "dawn/tests/MockCallback.h"
 #include "dawn/tests/PartitionAllocSupport.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
 #include "dawn/utils/PlatformDebugLogger.h"
@@ -69,11 +70,7 @@
 
 using testing::_;
 using testing::AtMost;
-
-struct MapReadUserdata {
-    raw_ptr<DawnTestBase> test;
-    size_t slot;
-};
+using testing::MockCppCallback;
 
 DawnTestEnvironment* gTestEnv = nullptr;
 DawnTestBase* gCurrentTest = nullptr;
@@ -809,15 +806,6 @@
     WaitForAllOperations();
     instance = nullptr;
 
-    // Since the native instance is a global, we can't rely on it's destruction to clean up all
-    // callbacks. Instead, for each test, we make sure to clear all events. Note that we use a
-    // do-while loop here because we need to flush the wire via WaitABit before we start processing
-    // the native events to ensure that any remaining client side commands, i.e. destructions, are
-    // flushed to the server.
-    do {
-        WaitABit();
-    } while (dawn::native::InstanceProcessEvents(gTestEnv->GetInstance()->Get()));
-
     // D3D11 and D3D12's GPU-based validation will accumulate objects over time if the backend
     // device is not destroyed and recreated, so we reset it here.
     if ((IsD3D11() || IsD3D12()) && IsBackendValidationEnabled()) {
@@ -1648,6 +1636,35 @@
     return EXPECT_TEXTURE_EQ(colorData.data(), colorTexture, {0, 0}, {width, height});
 }
 
+void DawnTestBase::MapAsyncAndWait(const wgpu::Buffer& buffer,
+                                   wgpu::MapMode mapMode,
+                                   uint64_t offset,
+                                   uint64_t size) {
+    DAWN_ASSERT(mapMode == wgpu::MapMode::Read || mapMode == wgpu::MapMode::Write);
+
+    if (!UsesWire()) {
+        // We use a new mock callback here so that the validation on the call happens as soon as the
+        // scope of this call ends.
+        MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)> mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+
+        wgpu::FutureWaitInfo waitInfo = {buffer.MapAsync(
+            mapMode, offset, size, wgpu::CallbackMode::WaitAnyOnly, mockCb.Callback())};
+        ASSERT_EQ(instance.WaitAny(1, &waitInfo, UINT64_MAX), wgpu::WaitStatus::Success);
+    } else {
+        bool done = false;
+        buffer.MapAsync(mapMode, offset, size, wgpu::CallbackMode::AllowProcessEvents,
+                        [&done](wgpu::MapAsyncStatus status, const char*) {
+                            ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                            done = true;
+                        });
+
+        while (!done) {
+            WaitABit();
+        }
+    }
+}
+
 void DawnTestBase::WaitABit(wgpu::Instance targetInstance) {
     if (targetInstance == nullptr) {
         targetInstance = instance;
@@ -1670,22 +1687,6 @@
 }
 
 void DawnTestBase::WaitForAllOperations() {
-    // TODO: crbug.com/42241461 - This block should be removed once we have migrated all tests to
-    // use the new entry points.
-    if (device != nullptr) {
-        // Callback might be invoked on another thread that calls the same WaitABit() method, not
-        // necessarily the current thread. So we need to use atomic here.
-        std::atomic<bool> done(false);
-        device.GetQueue().OnSubmittedWorkDone(
-            [](WGPUQueueWorkDoneStatus, void* userdata) {
-                *static_cast<std::atomic<bool>*>(userdata) = true;
-            },
-            &done);
-        while (!done.load()) {
-            WaitABit();
-        }
-    }
-
     do {
         FlushWire();
         if (UsesWire() && instance != nullptr) {
@@ -1727,12 +1728,24 @@
     mNumPendingMapOperations = mReadbackSlots.size();
 
     // Map all readback slots
-    for (size_t i = 0; i < mReadbackSlots.size(); ++i) {
-        MapReadUserdata* userdata = new MapReadUserdata{this, i};
+    for (size_t slotIndex = 0; slotIndex < mReadbackSlots.size(); ++slotIndex) {
+        auto& slot = mReadbackSlots[slotIndex];
 
-        const ReadbackSlot& slot = mReadbackSlots[i];
-        slot.buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize, SlotMapCallback,
-                             userdata);
+        slot.buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                             wgpu::CallbackMode::AllowProcessEvents,
+                             [this, &slot](wgpu::MapAsyncStatus status, const char*) {
+                                 DAWN_ASSERT(status == wgpu::MapAsyncStatus::Success);
+                                 Mutex::AutoLock lg(&mMutex);
+
+                                 if (status == wgpu::MapAsyncStatus::Success) {
+                                     slot.mappedData = slot.buffer.GetConstMappedRange();
+                                     DAWN_ASSERT(slot.mappedData != nullptr);
+                                 } else {
+                                     slot.mappedData = nullptr;
+                                 }
+
+                                 mNumPendingMapOperations.fetch_sub(1, std::memory_order_release);
+                             });
     }
 
     // Busy wait until all map operations are done.
@@ -1741,26 +1754,6 @@
     }
 }
 
-// static
-void DawnTestBase::SlotMapCallback(WGPUBufferMapAsyncStatus status, void* userdata_) {
-    DAWN_ASSERT(status == WGPUBufferMapAsyncStatus_Success ||
-                status == WGPUBufferMapAsyncStatus_DeviceLost);
-    std::unique_ptr<MapReadUserdata> userdata(static_cast<MapReadUserdata*>(userdata_));
-    DawnTestBase* test = userdata->test;
-
-    Mutex::AutoLock lg(&test->mMutex);
-
-    ReadbackSlot* slot = &test->mReadbackSlots[userdata->slot];
-    if (status == WGPUBufferMapAsyncStatus_Success) {
-        slot->mappedData = slot->buffer.GetConstMappedRange();
-        DAWN_ASSERT(slot->mappedData != nullptr);
-    } else {
-        slot->mappedData = nullptr;
-    }
-
-    test->mNumPendingMapOperations.fetch_sub(1, std::memory_order_release);
-}
-
 void DawnTestBase::ResolveExpectations() {
     for (const auto& expectation : mDeferredExpectations) {
         EXPECT_TRUE(mReadbackSlots[expectation.readbackSlot].mappedData != nullptr);
diff --git a/src/dawn/tests/DawnTest.h b/src/dawn/tests/DawnTest.h
index 7688f95..208d0c4 100644
--- a/src/dawn/tests/DawnTest.h
+++ b/src/dawn/tests/DawnTest.h
@@ -586,6 +586,11 @@
                                                     mipLevel, {}, &expectedStencil);
     }
 
+    void MapAsyncAndWait(const wgpu::Buffer& buffer,
+                         wgpu::MapMode mapMode,
+                         uint64_t offset,
+                         uint64_t size);
+
     void WaitABit(wgpu::Instance = nullptr);
     void FlushWire();
     void WaitForAllOperations();
@@ -659,7 +664,6 @@
 
     // Maps all the buffers and fill ReadbackSlot::mappedData
     void MapSlotsSynchronously();
-    static void SlotMapCallback(WGPUBufferMapAsyncStatus status, void* userdata);
     std::atomic<size_t> mNumPendingMapOperations = 0;
 
     // Reserve space where the data for an expectation can be copied
diff --git a/src/dawn/tests/end2end/BufferHostMappedPointerTests.cpp b/src/dawn/tests/end2end/BufferHostMappedPointerTests.cpp
index 682a30d..5580b7f 100644
--- a/src/dawn/tests/end2end/BufferHostMappedPointerTests.cpp
+++ b/src/dawn/tests/end2end/BufferHostMappedPointerTests.cpp
@@ -263,9 +263,10 @@
     ASSERT_DEVICE_ERROR(buffer.Unmap());
 
     // Invalid to map a persistently host mapped buffer.
-    ASSERT_DEVICE_ERROR_MSG(
-        buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize, nullptr, nullptr),
-        testing::HasSubstr("cannot be mapped"));
+    ASSERT_DEVICE_ERROR_MSG(buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize,
+                                            wgpu::CallbackMode::AllowSpontaneous,
+                                            [](wgpu::MapAsyncStatus, const char*) {}),
+                            testing::HasSubstr("cannot be mapped"));
 
     // Still invalid to GetMappedRange() or Unmap.
     ASSERT_EQ(buffer.GetMappedRange(), nullptr);
diff --git a/src/dawn/tests/end2end/BufferTests.cpp b/src/dawn/tests/end2end/BufferTests.cpp
index e6a1d78..6eca2cd 100644
--- a/src/dawn/tests/end2end/BufferTests.cpp
+++ b/src/dawn/tests/end2end/BufferTests.cpp
@@ -34,6 +34,7 @@
 #include <vector>
 
 #include "dawn/tests/DawnTest.h"
+#include "dawn/tests/MockCallback.h"
 #include "dawn/utils/ComboRenderPipelineDescriptor.h"
 #include "dawn/utils/WGPUHelpers.h"
 #include "partition_alloc/pointers/raw_ptr.h"
@@ -41,6 +42,11 @@
 namespace dawn {
 namespace {
 
+using testing::_;
+using testing::MockCppCallback;
+
+using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
+
 using FutureCallbackMode = std::optional<wgpu::CallbackMode>;
 DAWN_TEST_PARAM_STRUCT(BufferMappingTestParams, FutureCallbackMode);
 
@@ -60,26 +66,26 @@
                          size_t size,
                          wgpu::BufferMapCallback cb = nullptr,
                          void* ud = nullptr) {
-        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);
-            }
-        };
-
         // Legacy MapAsync
         if (!GetParam().mFutureCallbackMode) {
-            buffer.MapAsync(mode, offset, size, callback, &userdata);
+            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);
+                }
+            };
+
+            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(mode, offset, size, callback, &userdata));
             while (!userdata.done) {
                 // Flush wire and call instance process events.
                 WaitABit();
@@ -87,19 +93,27 @@
             return;
         }
 
-        wgpu::Future future = buffer.MapAsync(
-            mode, offset, size, {nullptr, *GetParam().mFutureCallbackMode, callback, &userdata});
+        bool done = false;
+        wgpu::Future future =
+            buffer.MapAsync(mode, offset, size, *GetParam().mFutureCallbackMode,
+                            [cb, ud, &done](wgpu::MapAsyncStatus status, const char*) {
+                                done = true;
+                                ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
+                                if (cb) {
+                                    cb(WGPUBufferMapAsyncStatus_Success, ud);
+                                }
+                            });
         switch (*GetParam().mFutureCallbackMode) {
             case wgpu::CallbackMode::WaitAnyOnly: {
                 wgpu::FutureWaitInfo waitInfo = {future};
                 GetInstance().WaitAny(1, &waitInfo, UINT64_MAX);
                 ASSERT_TRUE(waitInfo.completed);
-                ASSERT_TRUE(userdata.done);
+                ASSERT_TRUE(done);
                 break;
             }
             case wgpu::CallbackMode::AllowProcessEvents:
             case wgpu::CallbackMode::AllowSpontaneous:
-                while (!userdata.done) {
+                while (!done) {
                     // Flush wire and call instance process events.
                     WaitABit();
                 }
@@ -448,13 +462,13 @@
     if (!GetParam().mFutureCallbackMode) {
         // Map all the buffers.
         for (uint32_t i = 0; i < kBuffers; ++i) {
-            buffers[i].MapAsync(
+            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);
+                &mapCompletedCount));
         }
 
         // Wait for all mappings to complete
@@ -464,14 +478,12 @@
     } 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,
-                                    {nullptr, *GetParam().mFutureCallbackMode,
-                                     [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                                         ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                                         (*static_cast<uint32_t*>(userdata))++;
-                                     },
-                                     &mapCompletedCount});
+            futures[i] = buffers[i].MapAsync(
+                wgpu::MapMode::Write, 0, descriptor.size, *GetParam().mFutureCallbackMode,
+                [&mapCompletedCount](wgpu::MapAsyncStatus status, const char*) {
+                    ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
+                    mapCompletedCount++;
+                });
         }
 
         switch (*GetParam().mFutureCallbackMode) {
@@ -521,78 +533,78 @@
     wgpu::Buffer buffer = CreateMapReadBuffer(sizeof(data));
     queue.WriteBuffer(buffer, 0, data, sizeof(data));
 
-    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;
-    };
-
     // 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;
+        };
+
         // Map the buffer but do not wait on the result yet.
-        buffer.MapAsync(wgpu::MapMode::Read, 8, 4, cb1, &done1);
+        EXPECT_DEPRECATION_WARNING(buffer.MapAsync(wgpu::MapMode::Read, 8, 4, cb1, &done1));
 
         // 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(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2Mapped, &done2));
+            ASSERT_DEVICE_ERROR(EXPECT_DEPRECATION_WARNING(
+                buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2Mapped, &done2)));
         } else {
-            buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2, &done2);
+            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(wgpu::MapMode::Read, 0, 4, cb2, &done2));
         }
 
         while (!done1 || !done2) {
             WaitABit();
         }
     } else {
+        MockMapAsyncCallback cb1;
+        MockMapAsyncCallback cb2;
+        EXPECT_CALL(cb1, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        EXPECT_CALL(cb2, Call(wgpu::MapAsyncStatus::Error, _)).Times(1);
+
         // Map the buffer but do not wait on the result yet.
-        wgpu::Future f1 = buffer.MapAsync(wgpu::MapMode::Read, 8, 4,
-                                          {nullptr, *GetParam().mFutureCallbackMode, cb1, &done1});
+        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::Future f2;
-        if (buffer.GetMapState() == wgpu::BufferMapState::Mapped) {
-            ASSERT_DEVICE_ERROR(f2 = buffer.MapAsync(
-                                    wgpu::MapMode::Read, 0, 4,
-                                    {nullptr, *GetParam().mFutureCallbackMode, cb2Mapped, &done2}));
+        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,
-                                 {nullptr, *GetParam().mFutureCallbackMode, cb2, &done2});
+            f2 = {buffer.MapAsync(wgpu::MapMode::Read, 0, 4, *GetParam().mFutureCallbackMode,
+                                  cb2.Callback())};
         }
 
         switch (*GetParam().mFutureCallbackMode) {
             case wgpu::CallbackMode::WaitAnyOnly: {
-                wgpu::FutureWaitInfo waitInfo[] = {{f1}, {f2}};
-                GetInstance().WaitAny(2, waitInfo, UINT64_MAX);
-
-                if (!waitInfo[0].completed) {
-                    GetInstance().WaitAny(1, &waitInfo[0], UINT64_MAX);
-                } else if (!waitInfo[1].completed) {
-                    GetInstance().WaitAny(1, &waitInfo[1], UINT64_MAX);
-                }
+                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:
-                while (!done1 || !done2) {
-                    WaitABit();
-                }
+                WaitForAllOperations();
                 break;
         }
     }
@@ -708,11 +720,19 @@
                             wgpu::BufferMapCallback callback,
                             void* userdata) {
         if (!GetParam().mFutureCallbackMode) {
-            buffer.MapAsync(mapMode, offset, size, callback, userdata);
+            EXPECT_DEPRECATION_WARNING(buffer.MapAsync(mapMode, offset, size, callback, userdata));
             return {0};
         } else {
-            return buffer.MapAsync(mapMode, offset, size,
-                                   {nullptr, *GetParam().mFutureCallbackMode, callback, userdata});
+            return buffer.MapAsync(mapMode, offset, size, *GetParam().mFutureCallbackMode,
+                                   [callback, userdata](wgpu::MapAsyncStatus status, const char*) {
+                                       // 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);
+                                   });
         }
     }
 
@@ -720,11 +740,14 @@
                                        wgpu::QueueWorkDoneCallback callback,
                                        void* userdata) {
         if (!GetParam().mFutureCallbackMode) {
-            queueObj.OnSubmittedWorkDone(callback, userdata);
+            EXPECT_DEPRECATION_WARNING(queueObj.OnSubmittedWorkDone(callback, userdata));
             return {0};
         } else {
             return queueObj.OnSubmittedWorkDone(
-                {nullptr, *GetParam().mFutureCallbackMode, callback, userdata});
+                *GetParam().mFutureCallbackMode,
+                [callback, userdata](wgpu::QueueWorkDoneStatus status) {
+                    callback(static_cast<WGPUQueueWorkDoneStatus>(status), userdata);
+                });
         }
     }
 
@@ -925,7 +948,11 @@
 
     const void* MapAsyncAndWait(const wgpu::Buffer& buffer, wgpu::MapMode mode, size_t size) {
         bool done = false;
-        buffer.MapAsync(mode, 0, size, MapCallback, &done);
+        buffer.MapAsync(mode, 0, size, wgpu::CallbackMode::AllowProcessEvents,
+                        [&done](wgpu::MapAsyncStatus status, const char*) {
+                            EXPECT_EQ(wgpu::MapAsyncStatus::Success, status);
+                            done = true;
+                        });
 
         while (!done) {
             WaitABit();
@@ -1053,13 +1080,11 @@
     EXPECT_BUFFER_U32_EQ(myData, buffer, 0);
 
     bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Write, 0, 4,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                    [&done](wgpu::MapAsyncStatus status, const char*) {
+                        EXPECT_EQ(wgpu::MapAsyncStatus::Success, status);
+                        done = true;
+                    });
 
     while (!done) {
         WaitABit();
@@ -1076,13 +1101,11 @@
 
     ASSERT_DEVICE_ERROR([&] {
         bool done = false;
-        buffer.MapAsync(
-            wgpu::MapMode::Write, 0, 4,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_ValidationError, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
+        buffer.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        [&done](wgpu::MapAsyncStatus status, const char*) {
+                            EXPECT_EQ(wgpu::MapAsyncStatus::Error, status);
+                            done = true;
+                        });
 
         while (!done) {
             WaitABit();
@@ -1287,13 +1310,12 @@
         ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&descriptor));
 
         bool done = false;
-        ASSERT_DEVICE_ERROR(buffer.MapAsync(
-            wgpu::MapMode::Write, 0, 4,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                EXPECT_EQ(status, WGPUBufferMapAsyncStatus_ValidationError);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done));
+        ASSERT_DEVICE_ERROR(buffer.MapAsync(wgpu::MapMode::Write, 0, 4,
+                                            wgpu::CallbackMode::AllowProcessEvents,
+                                            [&done](wgpu::MapAsyncStatus status, const char*) {
+                                                EXPECT_EQ(wgpu::MapAsyncStatus::Error, status);
+                                                done = true;
+                                            }));
 
         while (!done) {
             WaitABit();
diff --git a/src/dawn/tests/end2end/BufferZeroInitTests.cpp b/src/dawn/tests/end2end/BufferZeroInitTests.cpp
index a552b9f..9a9fd2d 100644
--- a/src/dawn/tests/end2end/BufferZeroInitTests.cpp
+++ b/src/dawn/tests/end2end/BufferZeroInitTests.cpp
@@ -78,26 +78,6 @@
         return device.CreateBuffer(&descriptor);
     }
 
-    void MapAsyncAndWait(wgpu::Buffer buffer,
-                         wgpu::MapMode mapMode,
-                         uint64_t offset,
-                         uint64_t size) {
-        DAWN_ASSERT(mapMode == wgpu::MapMode::Read || mapMode == wgpu::MapMode::Write);
-
-        bool done = false;
-        buffer.MapAsync(
-            mapMode, offset, size,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
-
-        while (!done) {
-            WaitABit();
-        }
-    }
-
     wgpu::Texture CreateAndInitializeTexture(const wgpu::Extent3D& size,
                                              wgpu::TextureFormat format,
                                              wgpu::Color color = {0.f, 0.f, 0.f, 0.f}) {
diff --git a/src/dawn/tests/end2end/CopyTests.cpp b/src/dawn/tests/end2end/CopyTests.cpp
index eabf0e5..b5e4258 100644
--- a/src/dawn/tests/end2end/CopyTests.cpp
+++ b/src/dawn/tests/end2end/CopyTests.cpp
@@ -3094,12 +3094,11 @@
 
     // Ensure the underlying ID3D12Resource of bigBuffer is deleted.
     bool submittedWorkDone = false;
-    queue.OnSubmittedWorkDone(
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &submittedWorkDone);
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              [&submittedWorkDone](wgpu::QueueWorkDoneStatus status) {
+                                  EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                                  submittedWorkDone = true;
+                              });
     while (!submittedWorkDone) {
         WaitABit();
     }
@@ -3133,13 +3132,12 @@
         bufferDescriptor.usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
         wgpu::Buffer uploadBuffer = device.CreateBuffer(&bufferDescriptor);
         bool done = false;
-        uploadBuffer.MapAsync(
-            wgpu::MapMode::Write, 0, static_cast<uint32_t>(expectedData.size()),
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
+        uploadBuffer.MapAsync(wgpu::MapMode::Write, 0, static_cast<uint32_t>(expectedData.size()),
+                              wgpu::CallbackMode::AllowProcessEvents,
+                              [&done](wgpu::MapAsyncStatus status, const char*) {
+                                  ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
+                                  done = true;
+                              });
         while (!done) {
             WaitABit();
         }
@@ -3339,13 +3337,12 @@
 
         // Check the data in readback buffer
         bool done = false;
-        readbackBuffer.MapAsync(
-            wgpu::MapMode::Read, 0, kBufferSize,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
+        readbackBuffer.MapAsync(wgpu::MapMode::Read, 0, kBufferSize,
+                                wgpu::CallbackMode::AllowProcessEvents,
+                                [&done](wgpu::MapAsyncStatus status, const char*) {
+                                    ASSERT_EQ(wgpu::MapAsyncStatus::Success, status);
+                                    done = true;
+                                });
         while (!done) {
             WaitABit();
         }
@@ -3365,12 +3362,11 @@
 
     void EnsureSubmittedWorkDone() {
         bool submittedWorkDone = false;
-        queue.OnSubmittedWorkDone(
-            [](WGPUQueueWorkDoneStatus status, void* userdata) {
-                EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &submittedWorkDone);
+        queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                                  [&submittedWorkDone](wgpu::QueueWorkDoneStatus status) {
+                                      EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                                      submittedWorkDone = true;
+                                  });
         while (!submittedWorkDone) {
             WaitABit();
         }
diff --git a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
index 7eb9960..ca4f1ef 100644
--- a/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
+++ b/src/dawn/tests/end2end/DepthStencilCopyTests.cpp
@@ -106,24 +106,6 @@
 
 class DepthStencilCopyTests : public DawnTestWithParams<DepthStencilCopyTestParams> {
   protected:
-    void MapAsyncAndWait(const wgpu::Buffer& buffer,
-                         wgpu::MapMode mode,
-                         size_t offset,
-                         size_t size) {
-        bool done = false;
-        buffer.MapAsync(
-            mode, offset, size,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
-
-        while (!done) {
-            WaitABit();
-        }
-    }
-
     void SetUp() override {
         DawnTestWithParams<DepthStencilCopyTestParams>::SetUp();
 
diff --git a/src/dawn/tests/end2end/DestroyTests.cpp b/src/dawn/tests/end2end/DestroyTests.cpp
index 4c79683..d86c651 100644
--- a/src/dawn/tests/end2end/DestroyTests.cpp
+++ b/src/dawn/tests/end2end/DestroyTests.cpp
@@ -220,19 +220,10 @@
     DestroyDevice();
 
     wgpu::Queue queue = device.GetQueue();
-    queue.OnSubmittedWorkDone(
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            // TODO(crbug.com/dawn/2021): Wire and native differ slightly for now. Unify once we
-            // decide on the correct result. In theory maybe we want to pretend that things succeed
-            // when the device is lost.
-            DestroyTest* test = static_cast<DestroyTest*>(userdata);
-            if (test->UsesWire()) {
-                EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            } else {
-                EXPECT_EQ(status, WGPUQueueWorkDoneStatus_DeviceLost);
-            }
-        },
-        this);
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              [](wgpu::QueueWorkDoneStatus status) {
+                                  EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                              });
 }
 
 DAWN_INSTANTIATE_TEST(DestroyTest,
diff --git a/src/dawn/tests/end2end/DeviceInitializationTests.cpp b/src/dawn/tests/end2end/DeviceInitializationTests.cpp
index fb1f443..a0e12e2 100644
--- a/src/dawn/tests/end2end/DeviceInitializationTests.cpp
+++ b/src/dawn/tests/end2end/DeviceInitializationTests.cpp
@@ -43,42 +43,39 @@
 
     void TearDown() override { dawnProcSetProcs(nullptr); }
 
-    // Test that the device can still be used by testing a buffer copy.
+    // Test that the device can still be used by creating an async pipeline. Note that this test
+    // would be better if we did something like a buffer copy instead, but that can only be done
+    // once wgpu::CallbackMode::AllowSpontaneous is completely implemented.
+    // TODO(crbug.com/42241003): Update this test do a buffer copy instead.
     void ExpectDeviceUsable(wgpu::Device device) {
-        wgpu::Buffer src =
-            utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::CopySrc, {1, 2, 3, 4});
+        device.PushErrorScope(wgpu::ErrorFilter::Validation);
 
-        wgpu::Buffer dst = utils::CreateBufferFromData<uint32_t>(
-            device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead, {0, 0, 0, 0});
+        wgpu::ComputePipelineDescriptor desc;
+        desc.compute.module = utils::CreateShaderModule(device, R"(
+            @compute @workgroup_size(1) fn main() {}
+        )");
 
-        wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
-        encoder.CopyBufferToBuffer(src, 0, dst, 0, 4 * sizeof(uint32_t));
+        std::atomic<uint8_t> callbacks = 0;
+        device.CreateComputePipelineAsync(
+            &desc, wgpu::CallbackMode::AllowSpontaneous,
+            [&callbacks](wgpu::CreatePipelineAsyncStatus status, wgpu::ComputePipeline pipeline,
+                         const char*) {
+                EXPECT_EQ(status, wgpu::CreatePipelineAsyncStatus::Success);
+                EXPECT_NE(pipeline, nullptr);
+                callbacks++;
+            });
 
-        wgpu::CommandBuffer commands = encoder.Finish();
-        device.GetQueue().Submit(1, &commands);
+        device.PopErrorScope(
+            wgpu::CallbackMode::AllowSpontaneous,
+            [&callbacks](wgpu::PopErrorScopeStatus status, wgpu::ErrorType type, const char*) {
+                EXPECT_EQ(status, wgpu::PopErrorScopeStatus::Success);
+                EXPECT_EQ(type, wgpu::ErrorType::NoError);
+                callbacks++;
+            });
 
-        bool done = false;
-        dst.MapAsync(
-            wgpu::MapMode::Read, 0, 4 * sizeof(uint32_t),
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
-
-        // Note: we can't actually test this if Tick moves over to
-        // wgpuInstanceProcessEvents. We can still test that object creation works
-        // without crashing.
-        while (!done) {
-            device.Tick();
+        while (callbacks != 2) {
             utils::USleep(100);
         }
-
-        const uint32_t* mapping = static_cast<const uint32_t*>(dst.GetConstMappedRange());
-        EXPECT_EQ(mapping[0], 1u);
-        EXPECT_EQ(mapping[1], 2u);
-        EXPECT_EQ(mapping[2], 3u);
-        EXPECT_EQ(mapping[3], 4u);
     }
 };
 
diff --git a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
index a1e0338..dbc194f 100644
--- a/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLifetimeTests.cpp
@@ -28,11 +28,20 @@
 #include <utility>
 
 #include "dawn/tests/DawnTest.h"
+#include "dawn/tests/MockCallback.h"
 #include "dawn/utils/WGPUHelpers.h"
 
 namespace dawn {
 namespace {
 
+using testing::_;
+using testing::HasSubstr;
+using testing::Invoke;
+using testing::MockCppCallback;
+using testing::Return;
+
+using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
+
 class DeviceLifetimeTests : public DawnTest {};
 
 // Test that the device can be dropped before its queue.
@@ -50,17 +59,10 @@
     queue.Submit(1, &commandBuffer);
 
     // Ask for an onSubmittedWorkDone callback and drop the device.
-    queue.OnSubmittedWorkDone(
-        [](WGPUQueueWorkDoneStatus status, void*) {
-            // There is a bug in DeviceBase::Destroy(). If all submitted work is done when
-            // OnSubmittedWorkDone() is being called, the callback will be resolved with
-            // DeviceLost, otherwise the callback will be resolved with Success.
-            // TODO(dawn:1640): fix DeviceBase::Destroy() to always reslove the callback
-            // with success.
-            EXPECT_TRUE(status == WGPUQueueWorkDoneStatus_Success ||
-                        status == WGPUQueueWorkDoneStatus_DeviceLost);
-        },
-        nullptr);
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              [](wgpu::QueueWorkDoneStatus status) {
+                                  EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                              });
 
     device = nullptr;
 }
@@ -72,28 +74,14 @@
     wgpu::CommandBuffer commandBuffer = encoder.Finish();
     queue.Submit(1, &commandBuffer);
 
-    struct Userdata {
-        wgpu::Device device;
-        bool done;
-    };
     // Ask for an onSubmittedWorkDone callback and drop the device inside the callback.
-    Userdata data = Userdata{std::move(device), false};
-    queue.OnSubmittedWorkDone(
-        [](WGPUQueueWorkDoneStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUQueueWorkDoneStatus_Success);
-            static_cast<Userdata*>(userdata)->device = nullptr;
-            static_cast<Userdata*>(userdata)->done = true;
-        },
-        &data);
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              [this](wgpu::QueueWorkDoneStatus status) {
+                                  EXPECT_EQ(status, wgpu::QueueWorkDoneStatus::Success);
+                                  this->device = nullptr;
+                              });
 
-    while (!data.done) {
-        // WaitABit no longer can call tick since we've moved the device from the fixture into the
-        // userdata.
-        if (data.device) {
-            data.device.Tick();
-        }
-        WaitABit();
-    }
+    WaitForAllOperations();
 }
 
 // Test that the device can be dropped while a popErrorScope callback is in flight.
@@ -116,7 +104,7 @@
     }
 }
 
-// Test that the device can be dropped inside an onSubmittedWorkDone callback.
+// Test that the device can be dropped inside an popErrorScope callback.
 TEST_P(DeviceLifetimeTests, DroppedInsidePopErrorScope) {
     struct Userdata {
         wgpu::Device device;
@@ -159,14 +147,14 @@
     desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
     wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void*) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_DestroyedBeforeCallback);
-        },
-        nullptr);
+    MockMapAsyncCallback cb;
+    EXPECT_CALL(cb, Call(wgpu::MapAsyncStatus::Aborted, _)).Times(1);
+
+    buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                    wgpu::CallbackMode::AllowProcessEvents, cb.Callback());
 
     device = nullptr;
+    WaitForAllOperations();
 }
 
 // Test that the device can be dropped before a mapped buffer created from it.
@@ -176,18 +164,7 @@
     desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
     wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-    bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
-
-    while (!done) {
-        WaitABit();
-    }
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, wgpu::kWholeMapSize);
 
     device = nullptr;
 }
@@ -213,18 +190,12 @@
 
     device = nullptr;
 
-    bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_DeviceLost);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
+    MockMapAsyncCallback cb;
+    EXPECT_CALL(cb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("lost"))).Times(1);
 
-    while (!done) {
-        WaitABit();
-    }
+    buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                    wgpu::CallbackMode::AllowProcessEvents, cb.Callback());
+    WaitForAllOperations();
 }
 
 // Test that the device can be dropped before a buffer created from it, then mapping the buffer
@@ -237,35 +208,23 @@
 
     device = nullptr;
 
-    struct UserData {
-        wgpu::Buffer buffer;
-        bool done = false;
-    };
-
-    UserData userData;
-    userData.buffer = buffer;
-
     // First mapping.
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdataPtr) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_DeviceLost);
-            auto userdata = static_cast<UserData*>(userdataPtr);
+    buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [&buffer](wgpu::MapAsyncStatus status, const char* message) {
+                        EXPECT_EQ(status, wgpu::MapAsyncStatus::Error);
+                        EXPECT_THAT(message, HasSubstr("lost"));
 
-            // Second mapping.
-            userdata->buffer.MapAsync(
-                wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-                [](WGPUBufferMapAsyncStatus status, void* userdataPtr) {
-                    EXPECT_EQ(status, WGPUBufferMapAsyncStatus_DeviceLost);
-                    *static_cast<bool*>(userdataPtr) = true;
-                },
-                &userdata->done);
-        },
-        &userData);
+                        // Second mapping
+                        buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                                        wgpu::CallbackMode::AllowProcessEvents,
+                                        [](wgpu::MapAsyncStatus status, const char* message) {
+                                            EXPECT_EQ(status, wgpu::MapAsyncStatus::Error);
+                                            EXPECT_THAT(message, HasSubstr("lost"));
+                                        });
+                    });
 
-    while (!userData.done) {
-        WaitABit();
-    }
+    WaitForAllOperations();
 }
 
 // Test that the device can be dropped inside a buffer map callback.
@@ -275,40 +234,22 @@
     desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::CopyDst;
     wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-    struct Userdata {
-        wgpu::Device device;
-        wgpu::Buffer buffer;
-        bool wire;
-        bool done;
-    };
+    buffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [this, buffer](wgpu::MapAsyncStatus status, const char*) {
+                        EXPECT_EQ(status, wgpu::MapAsyncStatus::Success);
+                        device = nullptr;
 
-    // Ask for a mapAsync callback and drop the device inside the callback.
-    Userdata data = Userdata{std::move(device), buffer, UsesWire(), false};
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-            auto* data = static_cast<Userdata*>(userdata);
-            data->device = nullptr;
-            data->done = true;
+                        // Mapped data should be null since the buffer is implicitly destroyed.
+                        // TODO(crbug.com/dawn/1424): On the wire client, we don't track device
+                        // child objects so the mapped data is still available when the device is
+                        // destroyed.
+                        if (!UsesWire()) {
+                            EXPECT_EQ(buffer.GetConstMappedRange(), nullptr);
+                        }
+                    });
 
-            // Mapped data should be null since the buffer is implicitly destroyed.
-            // TODO(crbug.com/dawn/1424): On the wire client, we don't track device child objects so
-            // the mapped data is still available when the device is destroyed.
-            if (!data->wire) {
-                EXPECT_EQ(data->buffer.GetConstMappedRange(), nullptr);
-            }
-        },
-        &data);
-
-    while (!data.done) {
-        // WaitABit no longer can call tick since we've moved the device from the fixture into the
-        // userdata.
-        if (data.device) {
-            data.device.Tick();
-        }
-        WaitABit();
-    }
+    WaitForAllOperations();
 
     // Mapped data should be null since the buffer is implicitly destroyed.
     // TODO(crbug.com/dawn/1424): On the wire client, we don't track device child objects so the
diff --git a/src/dawn/tests/end2end/DeviceLostTests.cpp b/src/dawn/tests/end2end/DeviceLostTests.cpp
index 9c58a0e..d6cfd24 100644
--- a/src/dawn/tests/end2end/DeviceLostTests.cpp
+++ b/src/dawn/tests/end2end/DeviceLostTests.cpp
@@ -41,17 +41,11 @@
 
 using testing::_;
 using testing::Exactly;
-using testing::MockCallback;
+using testing::HasSubstr;
+using testing::MockCppCallback;
 
-class MockQueueWorkDoneCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
-};
-
-static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
-static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
-    mockQueueWorkDoneCallback->Call(status, userdata);
-}
+using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
+using MockQueueWorkDoneCallback = MockCppCallback<void (*)(wgpu::QueueWorkDoneStatus)>;
 
 static const int fakeUserData = 0;
 
@@ -60,12 +54,10 @@
     void SetUp() override {
         DawnTest::SetUp();
         DAWN_TEST_UNSUPPORTED_IF(UsesWire());
-        mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
     }
 
     void TearDown() override {
         instance.ProcessEvents();  // Flush all callbacks.
-        mockQueueWorkDoneCallback = nullptr;
         DawnTest::TearDown();
     }
 
@@ -74,28 +66,13 @@
         EXPECT_EQ(&fakeUserData, userdata);
     }
 
-    void MapAsyncAndWait(const wgpu::Buffer& buffer,
-                         wgpu::MapMode mode,
-                         size_t offset,
-                         size_t size) {
-        bool done = false;
-        buffer.MapAsync(
-            mode, offset, size,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
-
-        while (!done) {
-            WaitABit();
-        }
-    }
-
     template <typename T>
     void ExpectObjectIsError(const T& object) {
         EXPECT_TRUE(dawn::native::CheckIsErrorForTesting(object.Get()));
     }
+
+    MockQueueWorkDoneCallback mWorkDoneCb;
+    MockMapAsyncCallback mMapAsyncCb;
 };
 
 // Test that DeviceLostCallback is invoked when LostForTestimg is called
@@ -259,10 +236,13 @@
     wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
 
     LoseDeviceForTesting();
-    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
+
+    EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("is lost"))).Times(1);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                    mMapAsyncCb.Callback());
 }
 
-// Test that BufferMapAsync for writing calls back with device lost status when device lost after
+// Test that BufferMapAsync for writing calls back with success when device lost after
 // mapping
 TEST_P(DeviceLostTest, BufferMapAsyncBeforeLossFailsForWriting) {
     wgpu::BufferDescriptor bufferDescriptor;
@@ -270,9 +250,12 @@
     bufferDescriptor.usage = wgpu::BufferUsage::MapWrite;
     wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
 
-    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
+    EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                    mMapAsyncCb.Callback());
 
     LoseDeviceForTesting();
+    WaitForAllOperations();
 }
 
 // Test that buffer.Unmap after device is lost
@@ -307,10 +290,13 @@
     wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
 
     LoseDeviceForTesting();
-    buffer.MapAsync(wgpu::MapMode::Read, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
+
+    EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("is lost"))).Times(1);
+    buffer.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                    mMapAsyncCb.Callback());
 }
 
-// Test that BufferMapAsync for reading calls back with device lost status when device lost after
+// Test that BufferMapAsync for reading calls back with success when device lost after
 // mapping
 TEST_P(DeviceLostTest, BufferMapAsyncBeforeLossFailsForReading) {
     wgpu::BufferDescriptor bufferDescriptor;
@@ -319,9 +305,12 @@
 
     wgpu::Buffer buffer = device.CreateBuffer(&bufferDescriptor);
 
-    buffer.MapAsync(wgpu::MapMode::Read, 0, 4, MapFailCallback, const_cast<int*>(&fakeUserData));
+    EXPECT_CALL(mMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buffer.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                    mMapAsyncCb.Callback());
 
     LoseDeviceForTesting();
+    WaitForAllOperations();
 }
 
 // Test that WriteBuffer after device is lost
@@ -412,22 +401,20 @@
     ExpectObjectIsError(encoder.Finish());
 }
 
-// Test that QueueOnSubmittedWorkDone after device is lost.
+// Test QueueOnSubmittedWorkDone after device is lost.
 TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneAfterDeviceLost) {
     LoseDeviceForTesting();
 
-    // callback should have device lost status
-    EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, nullptr))
-        .Times(1);
-    queue.OnSubmittedWorkDone(ToMockQueueWorkDone, nullptr);
+    // Callback should have success status
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success));
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, mWorkDoneCb.Callback());
 }
 
-// Test that QueueOnSubmittedWorkDone when the device is lost after calling OnSubmittedWorkDone
+// Test QueueOnSubmittedWorkDone when the device is lost after calling OnSubmittedWorkDone
 TEST_P(DeviceLostTest, QueueOnSubmittedWorkDoneBeforeLossFails) {
-    // callback should have device lost status
-    EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_DeviceLost, nullptr))
-        .Times(1);
-    queue.OnSubmittedWorkDone(ToMockQueueWorkDone, nullptr);
+    // Callback should have success status
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success));
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents, mWorkDoneCb.Callback());
 
     LoseDeviceForTesting();
 }
diff --git a/src/dawn/tests/end2end/EventTests.cpp b/src/dawn/tests/end2end/EventTests.cpp
index a84f5fc..28e215b 100644
--- a/src/dawn/tests/end2end/EventTests.cpp
+++ b/src/dawn/tests/end2end/EventTests.cpp
@@ -195,24 +195,12 @@
         }
     }
 
-    wgpu::Future OnSubmittedWorkDone(WGPUQueueWorkDoneStatus expectedStatus) {
-        struct Userdata {
-            EventCompletionTests* self;
-            WGPUQueueWorkDoneStatus expectedStatus;
-        };
-        Userdata* userdata = new Userdata{this, expectedStatus};
-
-        return testQueue.OnSubmittedWorkDone({
-            nullptr,
-            GetCallbackMode(),
-            [](WGPUQueueWorkDoneStatus status, void* userdata) {
-                Userdata* u = reinterpret_cast<Userdata*>(userdata);
-                u->self->mCallbacksCompletedCount++;
-                ASSERT_EQ(status, u->expectedStatus);
-                delete u;
-            },
-            userdata,
-        });
+    wgpu::Future OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus expectedStatus) {
+        return testQueue.OnSubmittedWorkDone(
+            GetCallbackMode(), [this, expectedStatus](wgpu::QueueWorkDoneStatus status) {
+                mCallbacksCompletedCount++;
+                ASSERT_EQ(status, expectedStatus);
+            });
     }
 
     void TestWaitAll(bool loopOnlyOnce = false) {
@@ -345,14 +333,14 @@
 // WorkDone event after submitting some trivial work.
 TEST_P(EventCompletionTests, WorkDoneSimple) {
     TrivialSubmit();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
 // WorkDone event before device loss, wait afterward.
 TEST_P(EventCompletionTests, WorkDoneAcrossDeviceLoss) {
     TrivialSubmit();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
@@ -360,33 +348,33 @@
 TEST_P(EventCompletionTests, WorkDoneAfterDeviceLoss) {
     TrivialSubmit();
     LoseTestDevice();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
 // WorkDone event twice after submitting some trivial work.
 TEST_P(EventCompletionTests, WorkDoneTwice) {
     TrivialSubmit();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
 // WorkDone event without ever having submitted any work.
 TEST_P(EventCompletionTests, WorkDoneNoWork) {
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
 // WorkDone event after all work has completed already.
 TEST_P(EventCompletionTests, WorkDoneAlreadyCompleted) {
     TrivialSubmit();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
-    TrackForTest(OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success));
+    TrackForTest(OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success));
     TestWaitAll();
 }
 
@@ -396,9 +384,9 @@
     DAWN_TEST_UNSUPPORTED_IF(GetCallbackMode() != wgpu::CallbackMode::WaitAnyOnly);
 
     TrivialSubmit();
-    wgpu::Future f1 = OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success);
+    wgpu::Future f1 = OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success);
     TrivialSubmit();
-    wgpu::Future f2 = OnSubmittedWorkDone(WGPUQueueWorkDoneStatus_Success);
+    wgpu::Future f2 = OnSubmittedWorkDone(wgpu::QueueWorkDoneStatus::Success);
 
     // When using WaitAny, normally callback ordering guarantees would guarantee f1 completes before
     // f2. But if we wait on f2 first, then f2 is allowed to complete first because f1 still hasn't
@@ -409,8 +397,8 @@
     TestWaitAll(/*loopOnlyOnce=*/true);
 }
 
-constexpr WGPUQueueWorkDoneStatus kStatusUninitialized =
-    static_cast<WGPUQueueWorkDoneStatus>(INT32_MAX);
+constexpr wgpu::QueueWorkDoneStatus kStatusUninitialized =
+    static_cast<wgpu::QueueWorkDoneStatus>(INT32_MAX);
 
 TEST_P(EventCompletionTests, WorkDoneDropInstanceBeforeEvent) {
     // TODO(crbug.com/dawn/1987): Wire does not implement instance destruction correctly yet.
@@ -419,22 +407,18 @@
     UseSecondInstance();
     testInstance = nullptr;  // Drop the last external ref to the instance.
 
-    WGPUQueueWorkDoneStatus status = kStatusUninitialized;
-    testQueue.OnSubmittedWorkDone({nullptr, GetCallbackMode(),
-                                   [](WGPUQueueWorkDoneStatus status, void* userdata) {
-                                       *reinterpret_cast<WGPUQueueWorkDoneStatus*>(userdata) =
-                                           status;
-                                   },
-                                   &status});
+    wgpu::QueueWorkDoneStatus status = kStatusUninitialized;
+    testQueue.OnSubmittedWorkDone(GetCallbackMode(),
+                                  [&status](wgpu::QueueWorkDoneStatus result) { status = result; });
 
     if (IsSpontaneous()) {
         // TODO(crbug.com/dawn/2059): Once Spontaneous is implemented, this should no longer expect
         // the callback to be cleaned up immediately (and should expect it to happen on a future
         // Tick).
-        ASSERT_THAT(status, AnyOf(Eq(WGPUQueueWorkDoneStatus_Success),
-                                  Eq(WGPUQueueWorkDoneStatus_InstanceDropped)));
+        ASSERT_THAT(status, AnyOf(Eq(wgpu::QueueWorkDoneStatus::Success),
+                                  Eq(wgpu::QueueWorkDoneStatus::InstanceDropped)));
     } else {
-        ASSERT_EQ(status, WGPUQueueWorkDoneStatus_InstanceDropped);
+        ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::InstanceDropped);
     }
 }
 
@@ -444,13 +428,9 @@
 
     UseSecondInstance();
 
-    WGPUQueueWorkDoneStatus status = kStatusUninitialized;
-    testQueue.OnSubmittedWorkDone({nullptr, GetCallbackMode(),
-                                   [](WGPUQueueWorkDoneStatus status, void* userdata) {
-                                       *reinterpret_cast<WGPUQueueWorkDoneStatus*>(userdata) =
-                                           status;
-                                   },
-                                   &status});
+    wgpu::QueueWorkDoneStatus status = kStatusUninitialized;
+    testQueue.OnSubmittedWorkDone(GetCallbackMode(),
+                                  [&status](wgpu::QueueWorkDoneStatus result) { status = result; });
 
     if (IsSpontaneous()) {
         testInstance = nullptr;  // Drop the last external ref to the instance.
@@ -458,12 +438,12 @@
         // TODO(crbug.com/dawn/2059): Once Spontaneous is implemented, this should no longer expect
         // the callback to be cleaned up immediately (and should expect it to happen on a future
         // Tick).
-        ASSERT_THAT(status, AnyOf(Eq(WGPUQueueWorkDoneStatus_Success),
-                                  Eq(WGPUQueueWorkDoneStatus_InstanceDropped)));
+        ASSERT_THAT(status, AnyOf(Eq(wgpu::QueueWorkDoneStatus::Success),
+                                  Eq(wgpu::QueueWorkDoneStatus::InstanceDropped)));
     } else {
         ASSERT_EQ(status, kStatusUninitialized);
         testInstance = nullptr;  // Drop the last external ref to the instance.
-        ASSERT_EQ(status, WGPUQueueWorkDoneStatus_InstanceDropped);
+        ASSERT_EQ(status, wgpu::QueueWorkDoneStatus::InstanceDropped);
     }
 }
 
@@ -527,8 +507,7 @@
 
     for (uint64_t timeout : {uint64_t(1), uint64_t(0), UINT64_MAX}) {
         wgpu::FutureWaitInfo info{device2.GetQueue().OnSubmittedWorkDone(
-            {nullptr, wgpu::CallbackMode::WaitAnyOnly, [](WGPUQueueWorkDoneStatus, void*) {},
-             nullptr})};
+            wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus) {})};
         wgpu::WaitStatus status = instance2.WaitAny(1, &info, timeout);
         if (timeout == 0) {
             ASSERT_TRUE(status == wgpu::WaitStatus::Success ||
@@ -563,9 +542,8 @@
         for (size_t count : {kTimedWaitAnyMaxCountDefault, kTimedWaitAnyMaxCountDefault + 1}) {
             std::vector<wgpu::FutureWaitInfo> infos;
             for (size_t i = 0; i < count; ++i) {
-                infos.push_back(
-                    {queue2.OnSubmittedWorkDone({nullptr, wgpu::CallbackMode::WaitAnyOnly,
-                                                 [](WGPUQueueWorkDoneStatus, void*) {}, nullptr})});
+                infos.push_back({queue2.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
+                                                            [](wgpu::QueueWorkDoneStatus) {})});
             }
             wgpu::WaitStatus status = instance2.WaitAny(infos.size(), infos.data(), timeout);
             if (timeout == 0) {
@@ -609,10 +587,10 @@
 
     for (uint64_t timeout : {uint64_t(0), uint64_t(1)}) {
         std::vector<wgpu::FutureWaitInfo> infos{{
-            {queue2.OnSubmittedWorkDone({nullptr, wgpu::CallbackMode::WaitAnyOnly,
-                                         [](WGPUQueueWorkDoneStatus, void*) {}, nullptr})},
-            {queue3.OnSubmittedWorkDone({nullptr, wgpu::CallbackMode::WaitAnyOnly,
-                                         [](WGPUQueueWorkDoneStatus, void*) {}, nullptr})},
+            {queue2.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
+                                        [](wgpu::QueueWorkDoneStatus) {})},
+            {queue3.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
+                                        [](wgpu::QueueWorkDoneStatus) {})},
         }};
         wgpu::WaitStatus status = instance2.WaitAny(infos.size(), infos.data(), timeout);
         if (timeout == 0) {
@@ -642,8 +620,8 @@
 // events call we were crashing.
 TEST_P(FutureTests, MixedSourcePolling) {
     // OnSubmittedWorkDone is implemented via a queue serial.
-    device.GetQueue().OnSubmittedWorkDone({nullptr, wgpu::CallbackMode::AllowProcessEvents,
-                                           [](WGPUQueueWorkDoneStatus, void*) {}, nullptr});
+    device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                                          [](wgpu::QueueWorkDoneStatus) {});
 
     // PopErrorScope is implemented via a signal.
     device.PushErrorScope(wgpu::ErrorFilter::Validation);
diff --git a/src/dawn/tests/end2end/MultithreadTests.cpp b/src/dawn/tests/end2end/MultithreadTests.cpp
index 6a2fbea..b6d22dd 100644
--- a/src/dawn/tests/end2end/MultithreadTests.cpp
+++ b/src/dawn/tests/end2end/MultithreadTests.cpp
@@ -202,25 +202,18 @@
     constexpr uint32_t kSize = static_cast<uint32_t>(kDataSize * sizeof(uint32_t));
 
     utils::RunInParallel(10, [=, &myData = std::as_const(myData)](uint32_t) {
-        wgpu::Buffer buffer;
-        std::atomic<bool> mapCompleted(false);
-
         // Create buffer and request mapping.
-        buffer = CreateBuffer(kSize, wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc);
+        wgpu::Buffer buffer =
+            CreateBuffer(kSize, wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc);
 
-        buffer.MapAsync(
-            wgpu::MapMode::Write, 0, kSize,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                EXPECT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                (*static_cast<std::atomic<bool>*>(userdata)) = true;
-            },
-            &mapCompleted);
+        wgpu::FutureWaitInfo waitInfo = {
+            buffer.MapAsync(wgpu::MapMode::Write, 0, kSize, wgpu::CallbackMode::AllowProcessEvents,
+                            [](wgpu::MapAsyncStatus status, const char*) {
+                                ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                            })};
 
         // Wait for the mapping to complete
-        while (!mapCompleted.load()) {
-            device.Tick();
-            FlushWire();
-        }
+        ASSERT_EQ(instance.WaitAny(1, &waitInfo, UINT64_MAX), wgpu::WaitStatus::Success);
 
         // Buffer is mapped, write into it and unmap .
         memcpy(buffer.GetMappedRange(0, kSize), myData.data(), kSize);
diff --git a/src/dawn/tests/end2end/NonzeroBufferCreationTests.cpp b/src/dawn/tests/end2end/NonzeroBufferCreationTests.cpp
index b562c37..264adb1 100644
--- a/src/dawn/tests/end2end/NonzeroBufferCreationTests.cpp
+++ b/src/dawn/tests/end2end/NonzeroBufferCreationTests.cpp
@@ -33,23 +33,7 @@
 namespace dawn {
 namespace {
 
-class NonzeroBufferCreationTests : public DawnTest {
-  public:
-    void MapReadAsyncAndWait(wgpu::Buffer buffer, uint64_t offset, uint64_t size) {
-        bool done = false;
-        buffer.MapAsync(
-            wgpu::MapMode::Read, offset, size,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
-
-        while (!done) {
-            WaitABit();
-        }
-    }
-};
+class NonzeroBufferCreationTests : public DawnTest {};
 
 // Verify that each byte of the buffer has all been initialized to 1 with the toggle enabled when it
 // is created with CopyDst usage.
@@ -111,7 +95,7 @@
         EXPECT_EQ(0, memcmp(mappedData, expectedData.data(), kSize));
         buffer.Unmap();
 
-        MapReadAsyncAndWait(buffer, 0, kSize);
+        MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kSize);
         mappedData = static_cast<const uint8_t*>(buffer.GetConstMappedRange());
         EXPECT_EQ(0, memcmp(mappedData, expectedData.data(), kSize));
         buffer.Unmap();
diff --git a/src/dawn/tests/end2end/QueueTimelineTests.cpp b/src/dawn/tests/end2end/QueueTimelineTests.cpp
index 787bcce..d89c30f 100644
--- a/src/dawn/tests/end2end/QueueTimelineTests.cpp
+++ b/src/dawn/tests/end2end/QueueTimelineTests.cpp
@@ -28,61 +28,30 @@
 #include <memory>
 
 #include "dawn/tests/DawnTest.h"
+#include "dawn/tests/MockCallback.h"
 #include "gmock/gmock.h"
 
 namespace dawn {
 namespace {
 
+using testing::_;
 using testing::InSequence;
+using testing::MockCppCallback;
 
-class MockMapCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
-};
-
-static std::unique_ptr<MockMapCallback> mockMapCallback;
-static void ToMockMapCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
-    EXPECT_EQ(status, WGPUBufferMapAsyncStatus_Success);
-    mockMapCallback->Call(status, userdata);
-}
-
-class MockQueueWorkDoneCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
-};
-
-static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
-static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
-    mockQueueWorkDoneCallback->Call(status, userdata);
-}
-
-static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback1;
-static void ToMockQueueWorkDone1(WGPUQueueWorkDoneStatus status, void* userdata) {
-    mockQueueWorkDoneCallback1->Call(status, userdata);
-}
+using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
+using MockQueueWorkDoneCallback = MockCppCallback<void (*)(wgpu::QueueWorkDoneStatus)>;
 
 class QueueTimelineTests : public DawnTest {
   protected:
     void SetUp() override {
         DawnTest::SetUp();
 
-        mockMapCallback = std::make_unique<MockMapCallback>();
-        mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
-        mockQueueWorkDoneCallback1 = std::make_unique<MockQueueWorkDoneCallback>();
-
         wgpu::BufferDescriptor descriptor;
         descriptor.size = 4;
         descriptor.usage = wgpu::BufferUsage::MapRead;
         mMapReadBuffer = device.CreateBuffer(&descriptor);
     }
 
-    void TearDown() override {
-        mockMapCallback = nullptr;
-        mockQueueWorkDoneCallback = nullptr;
-        mockQueueWorkDoneCallback1 = nullptr;
-        DawnTest::TearDown();
-    }
-
     wgpu::Buffer mMapReadBuffer;
 };
 
@@ -90,13 +59,17 @@
 // when queue.OnSubmittedWorkDone is called after mMapReadBuffer.MapAsync. The callback order should
 // happen in the order the functions are called.
 TEST_P(QueueTimelineTests, MapRead_OnWorkDone) {
+    MockMapAsyncCallback mockMapAsyncCb;
+    MockQueueWorkDoneCallback mockQueueWorkDoneCb;
+
     InSequence sequence;
-    EXPECT_CALL(*mockMapCallback, Call(WGPUBufferMapAsyncStatus_Success, this)).Times(1);
-    EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
+    EXPECT_CALL(mockMapAsyncCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    EXPECT_CALL(mockQueueWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
 
-    mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize, ToMockMapCallback, this);
-
-    queue.OnSubmittedWorkDone(ToMockQueueWorkDone, this);
+    mMapReadBuffer.MapAsync(wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
+                            wgpu::CallbackMode::AllowProcessEvents, mockMapAsyncCb.Callback());
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              mockQueueWorkDoneCb.Callback());
 
     WaitForAllOperations();
     mMapReadBuffer.Unmap();
@@ -104,12 +77,17 @@
 
 // Test that the OnSubmittedWorkDone callbacks should happen in the order the functions are called.
 TEST_P(QueueTimelineTests, OnWorkDone_OnWorkDone) {
-    InSequence sequence;
-    EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
-    EXPECT_CALL(*mockQueueWorkDoneCallback1, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
+    MockQueueWorkDoneCallback mockQueueWorkDoneCb1;
+    MockQueueWorkDoneCallback mockQueueWorkDoneCb2;
 
-    queue.OnSubmittedWorkDone(ToMockQueueWorkDone, this);
-    queue.OnSubmittedWorkDone(ToMockQueueWorkDone1, this);
+    InSequence sequence;
+    EXPECT_CALL(mockQueueWorkDoneCb1, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+    EXPECT_CALL(mockQueueWorkDoneCb2, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              mockQueueWorkDoneCb1.Callback());
+    queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                              mockQueueWorkDoneCb2.Callback());
 
     WaitForAllOperations();
 }
diff --git a/src/dawn/tests/perf_tests/DawnPerfTest.cpp b/src/dawn/tests/perf_tests/DawnPerfTest.cpp
index c5178ed..5e93bfe 100644
--- a/src/dawn/tests/perf_tests/DawnPerfTest.cpp
+++ b/src/dawn/tests/perf_tests/DawnPerfTest.cpp
@@ -265,11 +265,8 @@
 
         submittedIterations++;
         mTest->queue.OnSubmittedWorkDone(
-            [](WGPUQueueWorkDoneStatus, void* userdata) {
-                uint64_t* counter = static_cast<uint64_t*>(userdata);
-                (*counter)++;
-            },
-            &finishedIterations);
+            wgpu::CallbackMode::AllowProcessEvents,
+            [&finishedIterations](wgpu::QueueWorkDoneStatus) { finishedIterations++; });
 
         if (mRunning) {
             ++mNumStepsPerformed;
diff --git a/src/dawn/tests/perf_tests/DawnPerfTest.h b/src/dawn/tests/perf_tests/DawnPerfTest.h
index 2ad3379..063963c 100644
--- a/src/dawn/tests/perf_tests/DawnPerfTest.h
+++ b/src/dawn/tests/perf_tests/DawnPerfTest.h
@@ -185,12 +185,9 @@
 
     void ComputeGPUElapsedTime() {
         bool done = false;
-        mReadbackBuffer.MapAsync(
-            wgpu::MapMode::Read, 0, sizeof(uint64_t) * kTimestampQueryCount,
-            [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                *static_cast<bool*>(userdata) = true;
-            },
-            &done);
+        mReadbackBuffer.MapAsync(wgpu::MapMode::Read, 0, sizeof(uint64_t) * kTimestampQueryCount,
+                                 wgpu::CallbackMode::AllowProcessEvents,
+                                 [&done](wgpu::MapAsyncStatus, const char*) { done = true; });
         while (!done) {
             DawnTestWithParams<Params>::WaitABit();
         }
diff --git a/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp b/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
index 659af9d..401da1e 100644
--- a/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
+++ b/src/dawn/tests/perf_tests/UniformBufferUpdatePerf.cpp
@@ -310,17 +310,13 @@
 
         // Return the staging buffer once it's done with the last usage and re-mapped.
         if (GetParam().uploadMethod == UploadMethod::MultipleStagingBuffer) {
-            CallbackData* callbackData = new CallbackData({this, stagingBuffer});
-            stagingBuffer.MapAsync(
-                wgpu::MapMode::Write, 0, GetBufferSize(),
-                [](WGPUBufferMapAsyncStatus status, void* userdata) {
-                    CallbackData* data = static_cast<CallbackData*>(userdata);
-                    if (status == WGPUBufferMapAsyncStatus::WGPUBufferMapAsyncStatus_Success) {
-                        data->self->ReturnStagingBuffer(data->buffer);
-                    }
-                    delete data;
-                },
-                callbackData);
+            stagingBuffer.MapAsync(wgpu::MapMode::Write, 0, GetBufferSize(),
+                                   wgpu::CallbackMode::AllowProcessEvents,
+                                   [this, stagingBuffer](wgpu::MapAsyncStatus status, const char*) {
+                                       if (status == wgpu::MapAsyncStatus::Success) {
+                                           this->ReturnStagingBuffer(stagingBuffer);
+                                       }
+                                   });
         }
 
         switch (GetParam().uniformBuffer) {
@@ -330,16 +326,12 @@
                 break;
             case UniformBuffer::Multiple:
                 // Return the uniform buffer once it's done with the last submit.
-                CallbackData* callbackData = new CallbackData({this, uniformBuffer});
-                queue.OnSubmittedWorkDone(
-                    [](WGPUQueueWorkDoneStatus status, void* userdata) {
-                        CallbackData* data = static_cast<CallbackData*>(userdata);
-                        if (status == WGPUQueueWorkDoneStatus::WGPUQueueWorkDoneStatus_Success) {
-                            data->self->ReturnUniformBuffer(data->buffer);
-                        }
-                        delete data;
-                    },
-                    callbackData);
+                queue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                                          [this, uniformBuffer](wgpu::QueueWorkDoneStatus status) {
+                                              if (status == wgpu::QueueWorkDoneStatus::Success) {
+                                                  this->ReturnUniformBuffer(uniformBuffer);
+                                              }
+                                          });
                 break;
         }
 
diff --git a/src/dawn/tests/unittests/native/DestroyObjectTests.cpp b/src/dawn/tests/unittests/native/DestroyObjectTests.cpp
index 3e35b3d..361a613 100644
--- a/src/dawn/tests/unittests/native/DestroyObjectTests.cpp
+++ b/src/dawn/tests/unittests/native/DestroyObjectTests.cpp
@@ -62,12 +62,15 @@
 using ::testing::ByMove;
 using ::testing::InSequence;
 using ::testing::Mock;
-using testing::MockCallback;
+using testing::MockCppCallback;
 using ::testing::NiceMock;
 using ::testing::Return;
 using ::testing::StrictMock;
 using ::testing::Test;
 
+using MockMapAsyncCallback =
+    StrictMock<MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>>;
+
 static constexpr std::string_view kComputeShader = R"(
         @compute @workgroup_size(1) fn main() {}
     )";
@@ -235,7 +238,7 @@
     desc.size = 16;
     desc.usage = wgpu::BufferUsage::MapRead;
 
-    StrictMock<MockCallback<wgpu::BufferMapCallback>> cb;
+    MockMapAsyncCallback cb;
     EXPECT_CALL(cb, Call).Times(1);
     Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
     {
@@ -247,8 +250,9 @@
     {
         EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
         wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 16, cb.Callback(), cb.MakeUserdata(this));
-        device.Tick();
+        buffer.MapAsync(wgpu::MapMode::Read, 0, 16, wgpu::CallbackMode::AllowProcessEvents,
+                        cb.Callback());
+        ProcessEvents();
 
         EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
         buffer.Destroy();
@@ -263,7 +267,7 @@
     desc.size = 16;
     desc.usage = wgpu::BufferUsage::MapRead;
 
-    StrictMock<MockCallback<wgpu::BufferMapCallback>> cb;
+    MockMapAsyncCallback cb;
     EXPECT_CALL(cb, Call).Times(1);
     Ref<BufferMock> bufferMock = AcquireRef(new BufferMock(mDeviceMock, &desc));
     {
@@ -277,8 +281,9 @@
 
         EXPECT_CALL(*mDeviceMock, CreateBufferImpl).WillOnce(Return(ByMove(std::move(bufferMock))));
         wgpu::Buffer buffer = device.CreateBuffer(ToCppAPI(&desc));
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 16, cb.Callback(), cb.MakeUserdata(this));
-        device.Tick();
+        buffer.MapAsync(wgpu::MapMode::Read, 0, 16, wgpu::CallbackMode::AllowProcessEvents,
+                        cb.Callback());
+        ProcessEvents();
 
         EXPECT_TRUE(FromAPI(buffer.Get())->IsAlive());
     }
diff --git a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
index d1b520f..32f6562 100644
--- a/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/BufferValidationTests.cpp
@@ -30,21 +30,19 @@
 #include <vector>
 
 #include "dawn/common/Platform.h"
+#include "dawn/tests/MockCallback.h"
 #include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "gmock/gmock.h"
 
 using testing::_;
-using testing::InvokeWithoutArgs;
+using testing::HasSubstr;
+using testing::Invoke;
+using testing::MockCppCallback;
+using testing::TestParamInfo;
+using testing::Values;
+using testing::WithParamInterface;
 
-class MockBufferMapAsyncCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata));
-};
-
-static std::unique_ptr<MockBufferMapAsyncCallback> mockBufferMapAsyncCallback;
-static void ToMockBufferMapAsyncCallback(WGPUBufferMapAsyncStatus status, void* userdata) {
-    mockBufferMapAsyncCallback->Call(status, userdata);
-}
+using MockMapAsyncCallback = MockCppCallback<void (*)(wgpu::MapAsyncStatus, const char*)>;
 
 class BufferValidationTest : public ValidationTest {
   protected:
@@ -73,29 +71,12 @@
         return device.CreateBuffer(&descriptor);
     }
 
-    void AssertMapAsyncError(wgpu::Buffer buffer, wgpu::MapMode mode, size_t offset, size_t size) {
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_ValidationError, _))
-            .Times(1);
-
-        ASSERT_DEVICE_ERROR(
-            buffer.MapAsync(mode, offset, size, ToMockBufferMapAsyncCallback, nullptr));
-    }
-
     wgpu::Queue queue;
 
     void SetUp() override {
         ValidationTest::SetUp();
-
-        mockBufferMapAsyncCallback = std::make_unique<MockBufferMapAsyncCallback>();
         queue = device.GetQueue();
     }
-
-    void TearDown() override {
-        // Delete mocks so that expectations are checked
-        mockBufferMapAsyncCallback = nullptr;
-
-        ValidationTest::TearDown();
-    }
 };
 
 // Test case where creation should succeed
@@ -184,469 +165,396 @@
     }
 }
 
-// Test the success case for mapping buffer for reading
-TEST_F(BufferValidationTest, MapAsync_ReadSuccess) {
-    wgpu::Buffer buf = CreateMapReadBuffer(4);
+class BufferMappingValidationTest : public BufferValidationTest,
+                                    public WithParamInterface<wgpu::MapMode> {
+  protected:
+    wgpu::Buffer CreateBuffer(uint64_t size) {
+        switch (GetParam()) {
+            case wgpu::MapMode::Read:
+                return CreateMapReadBuffer(size);
+            case wgpu::MapMode::Write:
+                return CreateMapWriteBuffer(size);
+            default:
+                DAWN_UNREACHABLE();
+        }
+        DAWN_UNREACHABLE();
+    }
 
-    buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+    wgpu::Buffer CreateMappedAtCreationBuffer(uint64_t size) {
+        switch (GetParam()) {
+            case wgpu::MapMode::Read:
+                return BufferMappedAtCreation(size, wgpu::BufferUsage::MapRead);
+            case wgpu::MapMode::Write:
+                return BufferMappedAtCreation(size, wgpu::BufferUsage::MapWrite);
+            default:
+                DAWN_UNREACHABLE();
+        }
+        DAWN_UNREACHABLE();
+    }
 
-    EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
+    void AssertMapAsyncError(wgpu::Buffer buffer,
+                             wgpu::MapMode mode,
+                             size_t offset,
+                             size_t size,
+                             bool deviceError = true) {
+        // We use a new mock callback here so that the validation on the call happens as soon as the
+        // scope of this call ends. This is possible since we are using Spontaneous mode.
+        MockMapAsyncCallback mockCb;
+
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, _)).Times(1);
+        if (deviceError) {
+            ASSERT_DEVICE_ERROR(buffer.MapAsync(
+                mode, offset, size, wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback()));
+        } else {
+            buffer.MapAsync(mode, offset, size, wgpu::CallbackMode::AllowSpontaneous,
+                            mockCb.Callback());
+        }
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+                         BufferMappingValidationTest,
+                         testing::Values(wgpu::MapMode::Read, wgpu::MapMode::Write),
+                         [](const TestParamInfo<BufferMappingValidationTest::ParamType>& info) {
+                             switch (info.param) {
+                                 case wgpu::MapMode::Read:
+                                     return "Read";
+                                 case wgpu::MapMode::Write:
+                                     return "Write";
+                                 default:
+                                     DAWN_UNREACHABLE();
+                             }
+                             DAWN_UNREACHABLE();
+                         });
+
+// Test the success case for mapping buffer.
+TEST_P(BufferMappingValidationTest, MapAsync_Success) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
     WaitForAllOperations();
-
-    buf.Unmap();
-}
-
-// Test the success case for mapping buffer for writing
-TEST_F(BufferValidationTest, MapAsync_WriteSuccess) {
-    wgpu::Buffer buf = CreateMapWriteBuffer(4);
-
-    buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-
-    EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
-    WaitForAllOperations();
-
-    buf.Unmap();
+    buffer.Unmap();
 }
 
 // Test map async with a buffer that's an error
-TEST_F(BufferValidationTest, MapAsync_ErrorBuffer) {
+TEST_P(BufferMappingValidationTest, MapAsync_ErrorBuffer) {
     wgpu::BufferDescriptor desc;
     desc.size = 4;
     desc.usage = wgpu::BufferUsage::MapRead | wgpu::BufferUsage::MapWrite;
     wgpu::Buffer buffer;
     ASSERT_DEVICE_ERROR(buffer = device.CreateBuffer(&desc));
 
-    AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
-    AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
+    AssertMapAsyncError(buffer, GetParam(), 0, 4);
 }
 
 // Test map async with an invalid offset and size alignment.
-TEST_F(BufferValidationTest, MapAsync_OffsetSizeAlignment) {
+TEST_P(BufferMappingValidationTest, MapAsync_OffsetSizeAlignment) {
     // Control case, offset aligned to 8 and size to 4 is valid
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Read, 8, 4, nullptr, nullptr);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Write, 8, 4, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(12);
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+        WaitForAllOperations();
     }
 
     // Error case, offset aligned to 4 is an error.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(12);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 4, 4);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 4, 4);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        AssertMapAsyncError(buffer, GetParam(), 4, 4);
     }
 
     // Error case, size aligned to 2 is an error.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(8);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 6);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 6);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        AssertMapAsyncError(buffer, GetParam(), 0, 6);
     }
 }
 
 // Test map async with an invalid offset and size OOB checks
-TEST_F(BufferValidationTest, MapAsync_OffsetSizeOOB) {
+TEST_P(BufferMappingValidationTest, MapAsync_OffsetSizeOOB) {
     // Valid case: full buffer is ok.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 8, nullptr, nullptr);
-    }
+        wgpu::Buffer buffer = CreateBuffer(8);
 
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+        WaitForAllOperations();
+    }
     // Valid case: range in the middle of the buffer is ok.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(16);
-        buffer.MapAsync(wgpu::MapMode::Read, 8, 4, nullptr, nullptr);
-    }
+        wgpu::Buffer buffer = CreateBuffer(16);
 
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+        WaitForAllOperations();
+    }
     // Valid case: empty range at the end of the buffer is ok.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Read, 8, 0, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 8, 0, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+        WaitForAllOperations();
     }
 
     // Error case, offset is larger than the buffer size (even if size is 0).
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(12);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 16, 0);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        AssertMapAsyncError(buffer, GetParam(), 16, 0);
     }
-
     // Error case, offset + size is larger than the buffer
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(12);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8, 8);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        AssertMapAsyncError(buffer, GetParam(), 8, 8);
     }
-
     // Error case, offset + size is larger than the buffer, overflow case.
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(12);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 8,
-                            std::numeric_limits<size_t>::max() & ~size_t(7));
+        wgpu::Buffer buffer = CreateBuffer(12);
+        AssertMapAsyncError(buffer, GetParam(), 8, std::numeric_limits<size_t>::max() & ~size_t(7));
     }
 }
 
 // Test map async with a buffer that has the wrong usage
-TEST_F(BufferValidationTest, MapAsync_WrongUsage) {
+TEST_P(BufferMappingValidationTest, MapAsync_WrongUsage) {
     {
         wgpu::BufferDescriptor desc;
         desc.usage = wgpu::BufferUsage::Vertex;
         desc.size = 4;
         wgpu::Buffer buffer = device.CreateBuffer(&desc);
 
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
+        AssertMapAsyncError(buffer, GetParam(), 0, 4);
     }
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(4);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
+        wgpu::Buffer buffer =
+            GetParam() == wgpu::MapMode::Read ? CreateMapWriteBuffer(4) : CreateMapReadBuffer(4);
+        AssertMapAsyncError(buffer, GetParam(), 0, 4);
     }
 }
 
 // Test map async with a wrong mode
-TEST_F(BufferValidationTest, MapAsync_WrongMode) {
+TEST_P(BufferMappingValidationTest, MapAsync_WrongMode) {
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
+        wgpu::Buffer buffer = CreateBuffer(4);
         AssertMapAsyncError(buffer, wgpu::MapMode::None, 0, 4);
     }
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
+        wgpu::Buffer buffer = CreateBuffer(4);
         AssertMapAsyncError(buffer, wgpu::MapMode::Read | wgpu::MapMode::Write, 0, 4);
     }
 }
 
 // Test map async with a buffer that's already mapped
-TEST_F(BufferValidationTest, MapAsync_AlreadyMapped) {
+TEST_P(BufferMappingValidationTest, MapAsync_AlreadyMapped) {
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(4);
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
+
+        AssertMapAsyncError(buffer, GetParam(), 0, 4);
     }
     {
-        wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapRead);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(4);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
-        WaitForAllOperations();
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
-    }
-    {
-        wgpu::Buffer buffer = BufferMappedAtCreation(4, wgpu::BufferUsage::MapWrite);
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
+        wgpu::Buffer buffer = CreateMappedAtCreationBuffer(4);
+        AssertMapAsyncError(buffer, GetParam(), 0, 4);
     }
 }
 
 // Test MapAsync() immediately causes a pending map error
-TEST_F(BufferValidationTest, MapAsync_PendingMap) {
-    // Read + overlapping range
+TEST_P(BufferMappingValidationTest, MapAsync_PendingMap) {
+    // Note that in the wire, we currently don't generate a validation error while in native we do.
+    // If eventually we add a way to inject errors on the wire, we may be able to make this behavior
+    // more aligned.
+    bool validationError = !UsesWire();
+
+    // Overlapping range
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
+        wgpu::Buffer buffer = CreateBuffer(4);
+
         // The first map async call should succeed while the second one should fail
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this);
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_MappingAlreadyPending, this + 1))
-            .Times(1);
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
-            .Times(1);
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+
+        AssertMapAsyncError(buffer, GetParam(), 0, 4, validationError);
         WaitForAllOperations();
     }
 
-    // Read + non-overlapping range
+    // Non-overlapping range
     {
-        wgpu::Buffer buffer = CreateMapReadBuffer(16);
-        // The first map async call should succeed while the second one should fail
-        buffer.MapAsync(wgpu::MapMode::Read, 0, 8, ToMockBufferMapAsyncCallback, this);
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_MappingAlreadyPending, this + 1))
-            .Times(1);
-        buffer.MapAsync(wgpu::MapMode::Read, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
-            .Times(1);
-        WaitForAllOperations();
-    }
+        wgpu::Buffer buffer = CreateBuffer(16);
 
-    // Write + overlapping range
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(4);
         // The first map async call should succeed while the second one should fail
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this);
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_MappingAlreadyPending, this + 1))
-            .Times(1);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
-            .Times(1);
-        WaitForAllOperations();
-    }
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
 
-    // Write + non-overlapping range
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(16);
-        // The first map async call should succeed while the second one should fail
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, ToMockBufferMapAsyncCallback, this);
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_MappingAlreadyPending, this + 1))
-            .Times(1);
-        buffer.MapAsync(wgpu::MapMode::Write, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this))
-            .Times(1);
+        AssertMapAsyncError(buffer, GetParam(), 8, 8, validationError);
         WaitForAllOperations();
     }
 }
 
 // Test map async with a buffer that's destroyed
-TEST_F(BufferValidationTest, MapAsync_Destroy) {
-    {
-        wgpu::Buffer buffer = CreateMapReadBuffer(4);
-        buffer.Destroy();
-        AssertMapAsyncError(buffer, wgpu::MapMode::Read, 0, 4);
-    }
-    {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(4);
-        buffer.Destroy();
-        AssertMapAsyncError(buffer, wgpu::MapMode::Write, 0, 4);
-    }
+TEST_P(BufferMappingValidationTest, MapAsync_Destroy) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+    buffer.Destroy();
+    AssertMapAsyncError(buffer, GetParam(), 0, 4);
 }
 
 // Test map async but unmapping before the result is ready.
-TEST_F(BufferValidationTest, MapAsync_UnmapBeforeResult) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+TEST_P(BufferMappingValidationTest, MapAsync_UnmapBeforeResult) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
-            .Times(1);
-        buf.Unmap();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("unmapped"))).Times(1);
 
-        // The callback shouldn't be called again.
-        WaitForAllOperations();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
-            .Times(1);
-        buf.Unmap();
-
-        // The callback shouldn't be called again.
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer.Unmap();
+    WaitForAllOperations();
 }
 
 // When a MapAsync is cancelled with Unmap it might still be in flight, test doing a new request
 // works as expected and we don't get the cancelled request's data.
-TEST_F(BufferValidationTest, MapAsync_UnmapBeforeResultAndMapAgain) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(16);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 8, ToMockBufferMapAsyncCallback, this + 0);
+TEST_P(BufferMappingValidationTest, MapAsync_UnmapBeforeResultAndMapAgain) {
+    wgpu::Buffer buffer = CreateBuffer(16);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, this + 0))
-            .Times(1);
-        buf.Unmap();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("unmapped"))).Times(1);
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
 
-        buf.MapAsync(wgpu::MapMode::Read, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1))
-            .Times(1);
-        WaitForAllOperations();
+    buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer.Unmap();
+    buffer.MapAsync(GetParam(), 8, 8, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
 
-        // Check that only the second MapAsync had an effect
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange(0));
-        ASSERT_NE(nullptr, buf.GetConstMappedRange(8));
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(16);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 8, ToMockBufferMapAsyncCallback, this + 0);
+    WaitForAllOperations();
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, this + 0))
-            .Times(1);
-        buf.Unmap();
-
-        buf.MapAsync(wgpu::MapMode::Write, 8, 8, ToMockBufferMapAsyncCallback, this + 1);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, this + 1))
-            .Times(1);
-        WaitForAllOperations();
-
-        // Check that only the second MapAsync had an effect
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange(0));
-        ASSERT_NE(nullptr, buf.GetConstMappedRange(8));
-    }
+    // Check that only the second MapAsync had an effect
+    ASSERT_EQ(nullptr, buffer.GetConstMappedRange(0));
+    ASSERT_NE(nullptr, buffer.GetConstMappedRange(8));
 }
 
 // Test map async but destroying before the result is ready.
-TEST_F(BufferValidationTest, MapAsync_DestroyBeforeResult) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+TEST_P(BufferMappingValidationTest, MapAsync_DestroyBeforeResult) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-            .Times(1);
-        buf.Destroy();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, HasSubstr("destroyed"))).Times(1);
 
-        // The callback shouldn't be called again.
-        WaitForAllOperations();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-            .Times(1);
-        buf.Destroy();
-
-        // The callback shouldn't be called again.
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer.Destroy();
+    WaitForAllOperations();
 }
 
 // Test that the MapCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(BufferValidationTest, MapAsync_UnmapCalledInCallback) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+TEST_P(BufferMappingValidationTest, MapAsync_UnmapCalledInCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .WillOnce(InvokeWithoutArgs([&] { buf.Unmap(); }));
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
+        buffer.Unmap();
+    }));
 
-        WaitForAllOperations();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .WillOnce(InvokeWithoutArgs([&] { buf.Unmap(); }));
-
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
 }
 
 // Test that the MapCallback isn't fired twice when destroy() is called inside the callback
-TEST_F(BufferValidationTest, MapAsync_DestroyCalledInCallback) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
+TEST_P(BufferMappingValidationTest, MapAsync_DestroyCalledInCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .WillOnce(InvokeWithoutArgs([&] { buf.Destroy(); }));
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
+        buffer.Destroy();
+    }));
 
-        WaitForAllOperations();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .WillOnce(InvokeWithoutArgs([&] { buf.Destroy(); }));
-
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
 }
 
 // Test MapAsync call in MapAsync success callback
-// This test is disabled now because there seems to be a reeantrancy bug in the
-// FlushWire call. See https://dawn-review.googlesource.com/c/dawn/+/116220 for the details.
-TEST_F(BufferValidationTest, DISABLED_MapAsync_MapAsyncInMapAsyncSuccessCallback) {
-    // Test MapAsync call in MapAsync validation success callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+TEST_P(BufferMappingValidationTest, MapAsync_RetryInSuccessCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .WillOnce(InvokeWithoutArgs([&] {
-                EXPECT_CALL(*mockBufferMapAsyncCallback,
-                            Call(WGPUBufferMapAsyncStatus_ValidationError, _));
-                // Should cause validation error because of already mapped buffer
-                ASSERT_DEVICE_ERROR(
-                    buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
-            }));
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).WillOnce(Invoke([&] {
+        // MapAsync call on destroyed buffer should be invalid
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("already mapped")))
+            .Times(1);
+        ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous,
+                                            mockCb.Callback()));
+    }));
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        WaitForAllOperations();
-        // we need another wire flush to make the MapAsync in the callback to the server
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
 }
 
-// Test MapAsync call in MapAsync rejection callback
-TEST_F(BufferValidationTest, MapAsync_MapAsyncInMapAsyncRejectionCallback) {
-    // Test MapAsync call in MapAsync validation error callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+// Test MapAsync call in MapAsync validation error callback
+TEST_P(BufferMappingValidationTest, MapAsync_RetryInErrorCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_ValidationError, _))
-            .WillOnce(InvokeWithoutArgs([&] {
-                // Retry with valid parameter and it should succeed
-                EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _));
-                buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-            }));
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, _)).WillOnce(Invoke([&] {
+        // Retry with valid parameter and it should succeed
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+    }));
 
-        // Write map mode on read buffer is invalid and it should reject with validation error
-        ASSERT_DEVICE_ERROR(
-            buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
+    // Wrong map mode on buffer is invalid and it should reject with validation error
+    ASSERT_DEVICE_ERROR(buffer.MapAsync(
+        GetParam() == wgpu::MapMode::Read ? wgpu::MapMode::Write : wgpu::MapMode::Read, 0, 4,
+        wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback()));
+    WaitForAllOperations();
+}
 
-        WaitForAllOperations();
-        // we need another wire flush to make the MapAsync in the callback to the server
-        WaitForAllOperations();
-    }
+// Test MapAsync call in MapAsync unmapped callback
+TEST_P(BufferMappingValidationTest, MapAsync_RetryInUnmappedCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-    // Test MapAsync call in MapAsync Unmapped before callback callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).WillOnce(Invoke([&] {
+        // MapAsync call on unmapped buffer should be valid
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
+    }));
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
-            .WillOnce(InvokeWithoutArgs([&] {
-                // MapAsync call on unmapped buffer should be valid
-                EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _));
-                buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-            }));
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer.Unmap();
+    WaitForAllOperations();
+}
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        buf.Unmap();
-        WaitForAllOperations();
-        WaitForAllOperations();
-    }
+// Test MapAsync call in MapAsync destroyed callback
+TEST_P(BufferMappingValidationTest, MapAsync_RetryInDestroyedCallback) {
+    wgpu::Buffer buffer = CreateBuffer(4);
 
-    // Test MapAsync call in MapAsync Destroyed before callback callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).WillOnce(Invoke([&] {
+        // MapAsync call on destroyed buffer should be invalid
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("destroyed"))).Times(1);
+        ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous,
+                                            mockCb.Callback()));
+    }));
 
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-            .WillOnce(InvokeWithoutArgs([&] {
-                // MapAsync call on destroyed buffer should be invalid
-                EXPECT_CALL(*mockBufferMapAsyncCallback,
-                            Call(WGPUBufferMapAsyncStatus_ValidationError, _));
-                ASSERT_DEVICE_ERROR(
-                    buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr));
-            }));
-
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        buf.Destroy();
-        WaitForAllOperations();
-        WaitForAllOperations();
-    }
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    buffer.Destroy();
+    WaitForAllOperations();
 }
 
 // Test the success case for mappedAtCreation
@@ -676,22 +584,16 @@
 }
 
 // Test that it is valid to Destroy an unmapped buffer
-TEST_F(BufferValidationTest, DestroyUnmappedBuffer) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.Destroy();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.Destroy();
-    }
+TEST_P(BufferMappingValidationTest, DestroyUnmappedBuffer) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+    buffer.Destroy();
 }
 
 // Test that it is valid to Destroy a destroyed buffer
-TEST_F(BufferValidationTest, DestroyDestroyedBuffer) {
-    wgpu::Buffer buf = CreateMapWriteBuffer(4);
-    buf.Destroy();
-    buf.Destroy();
+TEST_P(BufferMappingValidationTest, DestroyDestroyedBuffer) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+    buffer.Destroy();
+    buffer.Destroy();
 }
 
 // Test that it is valid to Unmap an error buffer
@@ -706,32 +608,24 @@
 }
 
 // Test that it is valid to Unmap a destroyed buffer
-TEST_F(BufferValidationTest, UnmapDestroyedBuffer) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        buf.Destroy();
-        buf.Unmap();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.Destroy();
-        buf.Unmap();
-    }
+TEST_P(BufferMappingValidationTest, UnmapDestroyedBuffer) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+    buffer.Destroy();
+    buffer.Unmap();
 }
 
 // Test that unmap then mapping a destroyed buffer is an error.
 // Regression test for crbug.com/1388920.
-TEST_F(BufferValidationTest, MapDestroyedBufferAfterUnmap) {
+TEST_P(BufferMappingValidationTest, MapDestroyedBufferAfterUnmap) {
     wgpu::Buffer buffer = CreateMapReadBuffer(4);
     buffer.Destroy();
     buffer.Unmap();
 
-    ASSERT_DEVICE_ERROR(buffer.MapAsync(
-        wgpu::MapMode::Read, 0, wgpu::kWholeMapSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            EXPECT_EQ(WGPUBufferMapAsyncStatus_ValidationError, status);
-        },
-        nullptr));
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Error, HasSubstr("destroyed"))).Times(1);
+    ASSERT_DEVICE_ERROR(buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize,
+                                        wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback()));
+
     WaitForAllOperations();
 }
 
@@ -767,7 +661,10 @@
         wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
         wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
 
-        bufA.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        bufA.MapAsync(wgpu::MapMode::Write, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                      mockCb.Callback());
 
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
@@ -779,7 +676,10 @@
         wgpu::Buffer bufA = device.CreateBuffer(&descriptorA);
         wgpu::Buffer bufB = device.CreateBuffer(&descriptorB);
 
-        bufB.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        bufB.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                      mockCb.Callback());
 
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
         encoder.CopyBufferToBuffer(bufA, 0, bufB, 0, 4);
@@ -844,31 +744,25 @@
 }
 
 // Test that it is valid to call Unmap on a buffer that is not mapped
-TEST_F(BufferValidationTest, UnmapUnmappedBuffer) {
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        // Buffer starts unmapped. Unmap shouldn't fail.
-        buf.Unmap();
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
-        buf.Unmap();
-        // Unmapping a second time shouldn't fail.
-        buf.Unmap();
-    }
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        // Buffer starts unmapped. Unmap shouldn't fail.
-        buf.Unmap();
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
-        buf.Unmap();
-        // Unmapping a second time shouldn't fail.
-        buf.Unmap();
-    }
+TEST_P(BufferMappingValidationTest, UnmapUnmappedBuffer) {
+    wgpu::Buffer buffer = CreateBuffer(4);
+
+    // Buffer starts unmapped. Unmap shouldn't fail.
+    buffer.Unmap();
+
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call).Times(1);
+    buffer.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowSpontaneous, mockCb.Callback());
+    buffer.Unmap();
+
+    // Unmapping a second time shouldn't fail.
+    buffer.Unmap();
 }
 
 // Test that it is invalid to call GetMappedRange on an unmapped buffer.
 TEST_F(BufferValidationTest, GetMappedRange_OnUnmappedBuffer) {
-    // Unmapped at creation case.
     {
+        // Unmapped at creation case.
         wgpu::BufferDescriptor desc;
         desc.size = 4;
         desc.usage = wgpu::BufferUsage::CopySrc;
@@ -886,33 +780,21 @@
         ASSERT_EQ(nullptr, buf.GetMappedRange());
         ASSERT_EQ(nullptr, buf.GetConstMappedRange());
     }
+}
 
-    // Unmapped after MapAsync read case.
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+// Test that it is invalid to call GetMappedRange on an unmapped buffer.
+TEST_P(BufferMappingValidationTest, GetMappedRange_OnUnmappedBuffer) {
+    // Unmapped after valid mapping.
+    wgpu::Buffer buf = CreateBuffer(4);
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        WaitForAllOperations();
-        buf.Unmap();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
+    buf.Unmap();
 
-        ASSERT_EQ(nullptr, buf.GetMappedRange());
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange());
-    }
-
-    // Unmapped after MapAsync write case.
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        WaitForAllOperations();
-        buf.Unmap();
-
-        ASSERT_EQ(nullptr, buf.GetMappedRange());
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange());
-    }
+    ASSERT_EQ(nullptr, buf.GetMappedRange());
+    ASSERT_EQ(nullptr, buf.GetConstMappedRange());
 }
 
 // Test that it is invalid to call GetMappedRange on a destroyed buffer.
@@ -937,41 +819,31 @@
         ASSERT_EQ(nullptr, buf.GetMappedRange());
         ASSERT_EQ(nullptr, buf.GetConstMappedRange());
     }
+}
 
-    // Destroyed after MapAsync read case.
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+// Test that it is invalid to call GetMappedRange on a destroyed buffer.
+TEST_P(BufferMappingValidationTest, GetMappedRange_OnDestroyedBuffer) {
+    // Destroyed after MapAsync case.
+    wgpu::Buffer buf = CreateBuffer(4);
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        WaitForAllOperations();
-        buf.Destroy();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
+    buf.Destroy();
 
-        ASSERT_EQ(nullptr, buf.GetMappedRange());
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange());
-    }
-
-    // Destroyed after MapAsync write case.
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        WaitForAllOperations();
-        buf.Destroy();
-
-        ASSERT_EQ(nullptr, buf.GetMappedRange());
-        ASSERT_EQ(nullptr, buf.GetConstMappedRange());
-    }
+    ASSERT_EQ(nullptr, buf.GetMappedRange());
+    ASSERT_EQ(nullptr, buf.GetConstMappedRange());
 }
 
 // Test that it is invalid to call GetMappedRange on a buffer after MapAsync for reading
 TEST_F(BufferValidationTest, GetMappedRange_NonConstOnMappedForReading) {
     wgpu::Buffer buf = CreateMapReadBuffer(4);
 
-    buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-    EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1);
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buf.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                 mockCb.Callback());
     WaitForAllOperations();
 
     ASSERT_EQ(nullptr, buf.GetMappedRange());
@@ -985,25 +857,20 @@
         ASSERT_NE(buffer.GetConstMappedRange(), nullptr);
         ASSERT_EQ(buffer.GetConstMappedRange(), buffer.GetMappedRange());
     }
+}
 
-    // GetMappedRange after MapAsync for reading case.
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
+// Test valid cases to call GetMappedRange on a buffer.
+TEST_P(BufferMappingValidationTest, GetMappedRange_ValidBufferStateCases) {
+    // GetMappedRange after MapAsync case.
+    wgpu::Buffer buf = CreateBuffer(4);
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
-        WaitForAllOperations();
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+    buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+    WaitForAllOperations();
 
-        ASSERT_NE(buf.GetConstMappedRange(), nullptr);
-    }
-
-    // GetMappedRange after MapAsync for writing case.
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr);
-        WaitForAllOperations();
-
-        ASSERT_NE(buf.GetConstMappedRange(), nullptr);
+    ASSERT_NE(buf.GetConstMappedRange(), nullptr);
+    if (GetParam() == wgpu::MapMode::Write) {
         ASSERT_EQ(buf.GetConstMappedRange(), buf.GetMappedRange());
     }
 }
@@ -1057,102 +924,116 @@
 }
 
 // Test validation of the GetMappedRange parameters
-TEST_F(BufferValidationTest, GetMappedRange_OffsetSizeOOB) {
+TEST_P(BufferMappingValidationTest, GetMappedRange_OffsetSizeOOB) {
+    MockMapAsyncCallback mockCb;
+    EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(11);
+
     // Valid case: full range is ok
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr);
+        EXPECT_NE(buffer.GetConstMappedRange(0, 8), nullptr);
     }
 
     // Valid case: full range is ok with defaulted MapAsync size
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_NE(buffer.GetMappedRange(0, 8), nullptr);
+        EXPECT_NE(buffer.GetConstMappedRange(0, 8), nullptr);
     }
 
     // Valid case: full range is ok with defaulted MapAsync size and defaulted GetMappedRangeSize
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, wgpu::kWholeMapSize, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        buffer.MapAsync(GetParam(), 0, wgpu::kWholeMapSize, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_NE(buffer.GetMappedRange(0, wgpu::kWholeMapSize), nullptr);
+        EXPECT_NE(buffer.GetConstMappedRange(0, wgpu::kWholeMapSize), nullptr);
     }
 
     // Valid case: empty range at the end is ok
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_NE(buffer.GetMappedRange(8, 0), nullptr);
+        EXPECT_NE(buffer.GetConstMappedRange(8, 0), nullptr);
     }
 
     // Valid case: range in the middle is ok.
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(16);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 16, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(16);
+        buffer.MapAsync(GetParam(), 0, 16, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_NE(buffer.GetMappedRange(8, 4), nullptr);
+        EXPECT_NE(buffer.GetConstMappedRange(8, 4), nullptr);
     }
 
     // Error case: offset is larger than the mapped range (even with size = 0)
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(8);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(8);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_EQ(buffer.GetMappedRange(9, 0), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(16, 0), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(9, 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(16, 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
     }
 
     // Error case: offset is larger than the buffer size (even with size = 0)
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(16);
-        buffer.MapAsync(wgpu::MapMode::Write, 8, 8, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(16);
+        buffer.MapAsync(GetParam(), 8, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_EQ(buffer.GetMappedRange(16, 4), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(24, 0), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(16, 4), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(24, 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(std::numeric_limits<size_t>::max(), 0), nullptr);
     }
 
     // Error case: offset + size is larger than the mapped range
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        buffer.MapAsync(GetParam(), 0, 12, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_EQ(buffer.GetMappedRange(8, 5), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(8, 8), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(8, 5), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(8, 8), nullptr);
     }
 
     // Error case: offset + size is larger than the mapped range, overflow case
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 12, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        buffer.MapAsync(GetParam(), 0, 12, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
         // set size to (max - 1) to avoid being equal to kWholeMapSize
-        EXPECT_EQ(buffer.GetMappedRange(8, std::numeric_limits<size_t>::max() - 1), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(8, std::numeric_limits<size_t>::max() - 1), nullptr);
     }
 
     // Error case: size is larger than the mapped range when using default kWholeMapSize
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Write, 0, 8, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        buffer.MapAsync(GetParam(), 0, 8, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_EQ(buffer.GetMappedRange(0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(0), nullptr);
     }
 
     // Error case: offset is before the start of the range (even with size = 0)
     {
-        wgpu::Buffer buffer = CreateMapWriteBuffer(12);
-        buffer.MapAsync(wgpu::MapMode::Write, 8, 4, nullptr, nullptr);
+        wgpu::Buffer buffer = CreateBuffer(12);
+        buffer.MapAsync(GetParam(), 8, 4, wgpu::CallbackMode::AllowProcessEvents,
+                        mockCb.Callback());
         WaitForAllOperations();
-        EXPECT_EQ(buffer.GetMappedRange(7, 4), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(0, 4), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(0, 12), nullptr);
-        EXPECT_EQ(buffer.GetMappedRange(0, 0), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(7, 4), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(0, 4), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(0, 12), nullptr);
+        EXPECT_EQ(buffer.GetConstMappedRange(0, 0), nullptr);
     }
 }
 
@@ -1229,122 +1110,6 @@
 
 // Test that GetMapState() shows expected buffer map state
 TEST_F(BufferValidationTest, GetMapState) {
-    // MapRead + MapAsync + Unmap
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
-        buf.Unmap();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapRead + MapAsync + Unmap before the callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        buf.Unmap();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapRead + MapAsync + Destroy
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
-        buf.Destroy();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapRead + MapAsync + Destroy before the callback
-    {
-        wgpu::Buffer buf = CreateMapReadBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        buf.Destroy();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapWrite + MapAsync + Unmap
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
-        buf.Unmap();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapWrite + MapAsync + Unmap before the callback
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        buf.Unmap();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapWrite + MapAsync + Destroy
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback, Call(WGPUBufferMapAsyncStatus_Success, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
-        buf.Destroy();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
-    // MapWrite + MapAsync + Destroy before the callback
-    {
-        wgpu::Buffer buf = CreateMapWriteBuffer(4);
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        EXPECT_CALL(*mockBufferMapAsyncCallback,
-                    Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _))
-            .Times(1);
-        buf.MapAsync(wgpu::MapMode::Write, 0, 4, ToMockBufferMapAsyncCallback, nullptr);
-        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
-        buf.Destroy();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-        WaitForAllOperations();
-        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
-    }
-
     // MappedAtCreation + Unmap
     {
         wgpu::Buffer buf = BufferMappedAtCreation(4, wgpu::BufferUsage::CopySrc);
@@ -1362,6 +1127,77 @@
     }
 }
 
+// Test that GetMapState() shows expected buffer map state
+TEST_P(BufferMappingValidationTest, GetMapState) {
+    // MapAsync + Unmap
+    {
+        wgpu::Buffer buf = CreateBuffer(4);
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
+
+        WaitForAllOperations();
+        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
+
+        buf.Unmap();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+    }
+
+    // MapAsync + Unmap before the callback
+    {
+        wgpu::Buffer buf = CreateBuffer(4);
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).Times(1);
+        buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
+
+        buf.Unmap();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        WaitForAllOperations();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+    }
+
+    // MapAsync + Destroy
+    {
+        wgpu::Buffer buf = CreateBuffer(4);
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Success, _)).Times(1);
+        buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
+
+        WaitForAllOperations();
+        EXPECT_EQ(wgpu::BufferMapState::Mapped, buf.GetMapState());
+
+        buf.Destroy();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+    }
+
+    // MapAsync + Destroy before the callback
+    {
+        wgpu::Buffer buf = CreateBuffer(4);
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        MockMapAsyncCallback mockCb;
+        EXPECT_CALL(mockCb, Call(wgpu::MapAsyncStatus::Aborted, _)).Times(1);
+        buf.MapAsync(GetParam(), 0, 4, wgpu::CallbackMode::AllowProcessEvents, mockCb.Callback());
+        EXPECT_EQ(wgpu::BufferMapState::Pending, buf.GetMapState());
+
+        buf.Destroy();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+
+        WaitForAllOperations();
+        EXPECT_EQ(wgpu::BufferMapState::Unmapped, buf.GetMapState());
+    }
+}
+
 class BufferMapExtendedUsagesValidationTest : public BufferValidationTest {
   protected:
     void SetUp() override {
diff --git a/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp b/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
index 610fe22..c68d4da 100644
--- a/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueueOnSubmittedWorkDoneValidationTests.cpp
@@ -27,36 +27,28 @@
 
 #include <memory>
 
+#include "dawn/tests/MockCallback.h"
 #include "dawn/tests/unittests/validation/ValidationTest.h"
 #include "gmock/gmock.h"
 
-class MockQueueWorkDoneCallback {
-  public:
-    MOCK_METHOD(void, Call, (WGPUQueueWorkDoneStatus status, void* userdata));
-};
+namespace dawn {
+namespace {
 
-static std::unique_ptr<MockQueueWorkDoneCallback> mockQueueWorkDoneCallback;
-static void ToMockQueueWorkDone(WGPUQueueWorkDoneStatus status, void* userdata) {
-    mockQueueWorkDoneCallback->Call(status, userdata);
-}
+using testing::MockCppCallback;
 
 class QueueOnSubmittedWorkDoneValidationTests : public ValidationTest {
   protected:
-    void SetUp() override {
-        ValidationTest::SetUp();
-        mockQueueWorkDoneCallback = std::make_unique<MockQueueWorkDoneCallback>();
-    }
-
-    void TearDown() override {
-        mockQueueWorkDoneCallback = nullptr;
-        ValidationTest::TearDown();
-    }
+    MockCppCallback<void (*)(wgpu::QueueWorkDoneStatus)> mWorkDoneCb;
 };
 
 // Test that OnSubmittedWorkDone can be called as soon as the queue is created.
 TEST_F(QueueOnSubmittedWorkDoneValidationTests, CallBeforeSubmits) {
-    EXPECT_CALL(*mockQueueWorkDoneCallback, Call(WGPUQueueWorkDoneStatus_Success, this)).Times(1);
-    device.GetQueue().OnSubmittedWorkDone(ToMockQueueWorkDone, this);
+    EXPECT_CALL(mWorkDoneCb, Call(wgpu::QueueWorkDoneStatus::Success)).Times(1);
+    device.GetQueue().OnSubmittedWorkDone(wgpu::CallbackMode::AllowProcessEvents,
+                                          mWorkDoneCb.Callback());
 
     WaitForAllOperations();
 }
+
+}  // anonymous namespace
+}  // namespace dawn
diff --git a/src/dawn/tests/unittests/validation/QueueSubmitValidationTests.cpp b/src/dawn/tests/unittests/validation/QueueSubmitValidationTests.cpp
index a4ffce5..9a34660 100644
--- a/src/dawn/tests/unittests/validation/QueueSubmitValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueueSubmitValidationTests.cpp
@@ -68,12 +68,13 @@
     }
 
     // Map the buffer, submitting when the buffer is mapped should fail
-    buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, nullptr, nullptr);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, wgpu::CallbackMode::AllowProcessEvents,
+                    [](wgpu::MapAsyncStatus, const char*) {});
 
     // Try submitting before the callback is fired.
     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
 
-    WaitForAllOperations(device);
+    WaitForAllOperations();
 
     {
         wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
@@ -132,7 +133,8 @@
     wgpu::Queue queue = device.GetQueue();
 
     // Map the source buffer to force a failure
-    buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, nullptr, nullptr);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, kBufferSize, wgpu::CallbackMode::AllowProcessEvents,
+                    [](wgpu::MapAsyncStatus, const char*) {});
 
     // Submitting a command buffer with a mapped buffer should fail
     ASSERT_DEVICE_ERROR(queue.Submit(1, &commands));
@@ -152,23 +154,14 @@
     descriptor.usage = wgpu::BufferUsage::MapWrite;
     wgpu::Buffer buffer = device.CreateBuffer(&descriptor);
 
-    struct CallbackData {
-        wgpu::Device device;
-        wgpu::Buffer buffer;
-    } callbackData = {device, buffer};
+    buffer.MapAsync(wgpu::MapMode::Write, 0, descriptor.size,
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [buffer, queue = device.GetQueue()](wgpu::MapAsyncStatus, const char*) {
+                        buffer.Unmap();
+                        queue.Submit(0, nullptr);
+                    });
 
-    const auto callback = [](WGPUBufferMapAsyncStatus status, void* userdata) {
-        CallbackData* data = reinterpret_cast<CallbackData*>(userdata);
-
-        data->buffer.Unmap();
-
-        wgpu::Queue queue = data->device.GetQueue();
-        queue.Submit(0, nullptr);
-    };
-
-    buffer.MapAsync(wgpu::MapMode::Write, 0, descriptor.size, callback, &callbackData);
-
-    WaitForAllOperations(device);
+    WaitForAllOperations();
 }
 
 // Test that submitting in a render pipeline creation callback doesn't cause re-entrance
@@ -195,7 +188,7 @@
             device.GetQueue().Submit(0, nullptr);
         });
 
-    WaitForAllOperations(device);
+    WaitForAllOperations();
 }
 
 // Test that submitting in a compute pipeline creation callback doesn't cause re-entrance
@@ -213,7 +206,7 @@
             device.GetQueue().Submit(0, nullptr);
         });
 
-    WaitForAllOperations(device);
+    WaitForAllOperations();
 }
 
 // Test that buffers in unused compute pass bindgroups are still checked for in
diff --git a/src/dawn/tests/unittests/validation/QueueWriteBufferValidationTests.cpp b/src/dawn/tests/unittests/validation/QueueWriteBufferValidationTests.cpp
index d0a12ad..0a04eaf 100644
--- a/src/dawn/tests/unittests/validation/QueueWriteBufferValidationTests.cpp
+++ b/src/dawn/tests/unittests/validation/QueueWriteBufferValidationTests.cpp
@@ -133,7 +133,8 @@
         descriptor.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
         wgpu::Buffer buf = device.CreateBuffer(&descriptor);
 
-        buf.MapAsync(wgpu::MapMode::Read, 0, 4, nullptr, nullptr);
+        buf.MapAsync(wgpu::MapMode::Read, 0, 4, wgpu::CallbackMode::AllowProcessEvents,
+                     [](wgpu::MapAsyncStatus, const char*) {});
         uint32_t value = 0;
         ASSERT_DEVICE_ERROR(queue.WriteBuffer(buf, 0, &value, sizeof(value)));
     }
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.cpp b/src/dawn/tests/unittests/validation/ValidationTest.cpp
index 2472044..7786b01 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.cpp
+++ b/src/dawn/tests/unittests/validation/ValidationTest.cpp
@@ -266,24 +266,6 @@
     } while (dawn::native::InstanceProcessEvents(mDawnInstance->Get()) || !mWireHelper->IsIdle());
 }
 
-void ValidationTest::WaitForAllOperations(const wgpu::Device& waitDevice) {
-    bool done = false;
-    waitDevice.GetQueue().OnSubmittedWorkDone(
-        [](WGPUQueueWorkDoneStatus, void* userdata) { *static_cast<bool*>(userdata) = true; },
-        &done);
-
-    // Force the currently submitted operations to completed.
-    while (!done) {
-        instance.ProcessEvents();
-        FlushWire();
-    }
-
-    // TODO(cwallez@chromium.org): It's not clear why we need this additional tick. Investigate it
-    // once WebGPU has defined the ordering of callbacks firing.
-    waitDevice.Tick();
-    FlushWire();
-}
-
 const dawn::native::ToggleInfo* ValidationTest::GetToggleInfo(const char* name) const {
     return mDawnInstance->GetToggleInfo(name);
 }
diff --git a/src/dawn/tests/unittests/validation/ValidationTest.h b/src/dawn/tests/unittests/validation/ValidationTest.h
index 3485585..a5e5e20 100644
--- a/src/dawn/tests/unittests/validation/ValidationTest.h
+++ b/src/dawn/tests/unittests/validation/ValidationTest.h
@@ -144,11 +144,7 @@
     bool UsesWire() const;
 
     void FlushWire();
-
-    // TODO: crbug.com/42241461 - Remove overload that passes a device once we update tests to use
-    // new entry points.
     void WaitForAllOperations();
-    void WaitForAllOperations(const wgpu::Device& device);
 
     // Helper functions to create objects to test validation.
 
diff --git a/src/dawn/tests/white_box/D3D12ResidencyTests.cpp b/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
index a6a09f0..ebef69c 100644
--- a/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
+++ b/src/dawn/tests/white_box/D3D12ResidencyTests.cpp
@@ -229,13 +229,12 @@
 
     // Calling MapAsync for reading should make the buffer resident.
     bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Read, 0, sizeof(uint32_t),
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
+    buffer.MapAsync(wgpu::MapMode::Read, 0, sizeof(uint32_t),
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [&done](wgpu::MapAsyncStatus status, const char*) {
+                        ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                        done = true;
+                    });
     EXPECT_TRUE(CheckIfBufferIsResident(buffer));
 
     while (!done) {
@@ -275,13 +274,12 @@
 
     // Calling MapAsync for writing should make the buffer resident.
     bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Write, 0, sizeof(uint32_t),
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, sizeof(uint32_t),
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [&done](wgpu::MapAsyncStatus status, const char*) {
+                        ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                        done = true;
+                    });
     EXPECT_TRUE(CheckIfBufferIsResident(buffer));
 
     while (!done) {
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp b/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp
index b580837..a7d9a9e 100644
--- a/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests.cpp
@@ -49,23 +49,6 @@
     return features;
 }
 
-void SharedBufferMemoryTests::MapAsyncAndWait(const wgpu::Buffer& buffer,
-                                              wgpu::MapMode mode,
-                                              uint32_t bufferSize) {
-    bool done = false;
-    buffer.MapAsync(
-        mode, 0, bufferSize,
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
-
-    while (!done) {
-        WaitABit();
-    }
-}
-
 wgpu::Texture Create2DTexture(wgpu::Device device,
                               uint32_t width,
                               uint32_t height,
@@ -231,13 +214,12 @@
     memory.BeginAccess(buffer, &desc);
 
     bool done = false;
-    buffer.MapAsync(
-        wgpu::MapMode::Write, 0, sizeof(uint32_t),
-        [](WGPUBufferMapAsyncStatus status, void* userdata) {
-            ASSERT_EQ(WGPUBufferMapAsyncStatus_Success, status);
-            *static_cast<bool*>(userdata) = true;
-        },
-        &done);
+    buffer.MapAsync(wgpu::MapMode::Write, 0, sizeof(uint32_t),
+                    wgpu::CallbackMode::AllowProcessEvents,
+                    [&done](wgpu::MapAsyncStatus status, const char*) {
+                        ASSERT_EQ(status, wgpu::MapAsyncStatus::Success);
+                        done = true;
+                    });
 
     // Calling EndAccess should generate an error even if the buffer has not completed being mapped.
     wgpu::SharedBufferMemoryEndAccessState state;
@@ -279,7 +261,11 @@
     wgpu::Buffer sharedBuffer = memory.CreateBuffer();
 
     // Mapping a buffer without calling BeginAccess should cause an error.
-    ASSERT_DEVICE_ERROR(sharedBuffer.MapAsync(wgpu::MapMode::Write, 0, 4, nullptr, nullptr));
+    ASSERT_DEVICE_ERROR(sharedBuffer.MapAsync(wgpu::MapMode::Write, 0, 4,
+                                              wgpu::CallbackMode::AllowProcessEvents,
+                                              [](wgpu::MapAsyncStatus status, const char*) {
+                                                  ASSERT_EQ(status, wgpu::MapAsyncStatus::Error);
+                                              }));
 }
 
 // Ensure multiple buffers created from a SharedBufferMemory cannot be accessed simultaneously.
@@ -362,7 +348,7 @@
     beginAccessDesc.initialized = false;
     memory.BeginAccess(buffer, &beginAccessDesc);
 
-    MapAsyncAndWait(buffer, wgpu::MapMode::Write, kBufferSize);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, kBufferSize);
 
     uint32_t* mappedData = static_cast<uint32_t*>(buffer.GetMappedRange(0, kBufferSize));
     memcpy(mappedData, &kBufferData, kBufferSize);
@@ -441,7 +427,7 @@
     memory.BeginAccess(buffer, &beginAccessDesc);
     EXPECT_BUFFER_U32_EQ(kBufferData, buffer, 0);
 
-    MapAsyncAndWait(buffer, wgpu::MapMode::Write, kBufferSize);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Write, 0, kBufferSize);
 
     uint32_t* mappedData = static_cast<uint32_t*>(buffer.GetMappedRange(0, kBufferSize));
     memcpy(mappedData, &kBufferData2, kBufferSize);
@@ -462,7 +448,7 @@
     beginAccessDesc.initialized = true;
     memory.BeginAccess(buffer, &beginAccessDesc);
 
-    MapAsyncAndWait(buffer, wgpu::MapMode::Read, kBufferSize);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kBufferSize);
 
     const uint32_t* mappedData =
         static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize));
@@ -478,7 +464,7 @@
     wgpu::CommandBuffer commandBuffer = encoder.Finish();
     queue.Submit(1, &commandBuffer);
 
-    MapAsyncAndWait(buffer, wgpu::MapMode::Read, kBufferSize);
+    MapAsyncAndWait(buffer, wgpu::MapMode::Read, 0, kBufferSize);
 
     mappedData = static_cast<const uint32_t*>(buffer.GetConstMappedRange(0, kBufferSize));
     ASSERT_EQ(*mappedData, kBufferData2);
diff --git a/src/dawn/tests/white_box/SharedBufferMemoryTests.h b/src/dawn/tests/white_box/SharedBufferMemoryTests.h
index d7e3b14..9f631a3 100644
--- a/src/dawn/tests/white_box/SharedBufferMemoryTests.h
+++ b/src/dawn/tests/white_box/SharedBufferMemoryTests.h
@@ -64,9 +64,6 @@
   public:
     void SetUp() override;
     std::vector<wgpu::FeatureName> GetRequiredFeatures() override;
-
-  protected:
-    void MapAsyncAndWait(const wgpu::Buffer& buffer, wgpu::MapMode mode, uint32_t bufferSize);
 };
 }  // namespace dawn