// Copyright 2019 The Dawn Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "tests/unittests/wire/WireTest.h" #include "dawn_wire/client/ClientMemoryTransferService_mock.h" #include "dawn_wire/server/ServerMemoryTransferService_mock.h" using namespace testing; using namespace dawn_wire; namespace { // Mock class to add expectations on the wire calling callbacks class MockBufferMapCallback { public: MOCK_METHOD(void, Call, (WGPUBufferMapAsyncStatus status, void* userdata)); }; std::unique_ptr> mockBufferMapCallback; void ToMockBufferMapCallback(WGPUBufferMapAsyncStatus status, void* userdata) { mockBufferMapCallback->Call(status, userdata); } } // anonymous namespace // WireMemoryTransferServiceTests test the MemoryTransferService with buffer mapping. // They test the basic success and error cases for buffer mapping, and they test // mocked failures of each fallible MemoryTransferService method that an embedder // could implement. // The test harness defines multiple helpers for expecting operations on Read/Write handles // and for mocking failures. The helpers are designed such that for a given run of a test, // a Serialization expection has a corresponding Deserialization expectation for which the // serialized data must match. // There are tests which check for Success for every mapping operation which mock an entire mapping // operation from map to unmap, and add all MemoryTransferService expectations. // Tests which check for errors perform the same mapping operations but insert mocked failures for // various mapping or MemoryTransferService operations. class WireMemoryTransferServiceTests : public WireTest { public: WireMemoryTransferServiceTests() { } ~WireMemoryTransferServiceTests() override = default; client::MemoryTransferService* GetClientMemoryTransferService() override { return &clientMemoryTransferService; } server::MemoryTransferService* GetServerMemoryTransferService() override { return &serverMemoryTransferService; } void SetUp() override { WireTest::SetUp(); mockBufferMapCallback = std::make_unique>(); // TODO(enga): Make this thread-safe. mBufferContent++; mMappedBufferContent = 0; mUpdatedBufferContent++; mSerializeCreateInfo++; mReadHandleSerializeDataInfo++; mWriteHandleSerializeDataInfo++; } void TearDown() override { WireTest::TearDown(); // Delete mock so that expectations are checked mockBufferMapCallback = nullptr; } void FlushClient(bool success = true) { WireTest::FlushClient(success); Mock::VerifyAndClearExpectations(&serverMemoryTransferService); } void FlushServer(bool success = true) { WireTest::FlushServer(success); Mock::VerifyAndClearExpectations(&mockBufferMapCallback); Mock::VerifyAndClearExpectations(&clientMemoryTransferService); } protected: using ClientReadHandle = client::MockMemoryTransferService::MockReadHandle; using ServerReadHandle = server::MockMemoryTransferService::MockReadHandle; using ClientWriteHandle = client::MockMemoryTransferService::MockWriteHandle; using ServerWriteHandle = server::MockMemoryTransferService::MockWriteHandle; std::pair CreateBuffer(WGPUBufferUsage usage = WGPUBufferUsage_None) { WGPUBufferDescriptor descriptor = {}; descriptor.size = kBufferSize; descriptor.usage = usage; WGPUBuffer apiBuffer = api.GetNewBuffer(); WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) .WillOnce(Return(apiBuffer)) .RetiresOnSaturation(); return std::make_pair(apiBuffer, buffer); } std::pair CreateBufferMapped( WGPUBufferUsage usage = WGPUBufferUsage_None) { WGPUBufferDescriptor descriptor = {}; descriptor.size = sizeof(mBufferContent); descriptor.mappedAtCreation = true; descriptor.usage = usage; WGPUBuffer apiBuffer = api.GetNewBuffer(); WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, sizeof(mBufferContent))) .WillOnce(Return(&mMappedBufferContent)); return std::make_pair(apiBuffer, buffer); } ClientReadHandle* ExpectReadHandleCreation() { // Create the handle first so we can use it in later expectations. ClientReadHandle* handle = clientMemoryTransferService.NewReadHandle(); EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent))) .WillOnce(InvokeWithoutArgs([=]() { return handle; })); return handle; } void MockReadHandleCreationFailure() { EXPECT_CALL(clientMemoryTransferService, OnCreateReadHandle(sizeof(mBufferContent))) .WillOnce(InvokeWithoutArgs([=]() { return nullptr; })); } void ExpectReadHandleSerialization(ClientReadHandle* handle) { EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreateSize(handle)) .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeCreateInfo); })); EXPECT_CALL(clientMemoryTransferService, OnReadHandleSerializeCreate(handle, _)) .WillOnce(WithArg<1>([&](void* serializePointer) { memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo)); return sizeof(mSerializeCreateInfo); })); } ServerReadHandle* ExpectServerReadHandleDeserialize() { // Create the handle first so we can use it in later expectations. ServerReadHandle* handle = serverMemoryTransferService.NewReadHandle(); EXPECT_CALL(serverMemoryTransferService, OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)), sizeof(mSerializeCreateInfo), _)) .WillOnce(WithArg<2>([=](server::MemoryTransferService::ReadHandle** readHandle) { *readHandle = handle; return true; })); return handle; } void MockServerReadHandleDeserializeFailure() { EXPECT_CALL(serverMemoryTransferService, OnDeserializeReadHandle(Pointee(Eq(mSerializeCreateInfo)), sizeof(mSerializeCreateInfo), _)) .WillOnce(InvokeWithoutArgs([&]() { return false; })); } void ExpectServerReadHandleSerializeDataUpdate(ServerReadHandle* handle) { EXPECT_CALL(serverMemoryTransferService, OnReadHandleSizeOfSerializeDataUpdate(handle, _, _)) .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mReadHandleSerializeDataInfo); })); EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeDataUpdate(handle, _, _, _, _)) .WillOnce(WithArg<4>([&](void* serializePointer) { memcpy(serializePointer, &mReadHandleSerializeDataInfo, sizeof(mReadHandleSerializeDataInfo)); return sizeof(mReadHandleSerializeDataInfo); })); } void ExpectClientReadHandleDeserializeDataUpdate(ClientReadHandle* handle, uint32_t* mappedData) { EXPECT_CALL( clientMemoryTransferService, OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)), sizeof(mReadHandleSerializeDataInfo), _, _)) .WillOnce(Return(true)); } void MockClientReadHandleDeserializeDataUpdateFailure(ClientReadHandle* handle) { EXPECT_CALL( clientMemoryTransferService, OnReadHandleDeserializeDataUpdate(handle, Pointee(Eq(mReadHandleSerializeDataInfo)), sizeof(mReadHandleSerializeDataInfo), _, _)) .WillOnce(Return(false)); } ClientWriteHandle* ExpectWriteHandleCreation(bool mappedAtCreation) { // Create the handle first so we can use it in later expectations. ClientWriteHandle* handle = clientMemoryTransferService.NewWriteHandle(); EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent))) .WillOnce(InvokeWithoutArgs([=]() { return handle; })); if (mappedAtCreation) { EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(handle)) .WillOnce(Return(&mBufferContent)); } return handle; } void MockWriteHandleCreationFailure() { EXPECT_CALL(clientMemoryTransferService, OnCreateWriteHandle(sizeof(mBufferContent))) .WillOnce(InvokeWithoutArgs([=]() { return nullptr; })); } void ExpectWriteHandleSerialization(ClientWriteHandle* handle) { EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreateSize(handle)) .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeCreateInfo); })); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeCreate(handle, _)) .WillOnce(WithArg<1>([&](void* serializePointer) { memcpy(serializePointer, &mSerializeCreateInfo, sizeof(mSerializeCreateInfo)); return sizeof(mSerializeCreateInfo); })); } ServerWriteHandle* ExpectServerWriteHandleDeserialization() { // Create the handle first so it can be used in later expectations. ServerWriteHandle* handle = serverMemoryTransferService.NewWriteHandle(); EXPECT_CALL(serverMemoryTransferService, OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)), sizeof(mSerializeCreateInfo), _)) .WillOnce(WithArg<2>([=](server::MemoryTransferService::WriteHandle** writeHandle) { *writeHandle = handle; return true; })); return handle; } void MockServerWriteHandleDeserializeFailure() { EXPECT_CALL(serverMemoryTransferService, OnDeserializeWriteHandle(Pointee(Eq(mSerializeCreateInfo)), sizeof(mSerializeCreateInfo), _)) .WillOnce(Return(false)); } void ExpectClientWriteHandleSerializeDataUpdate(ClientWriteHandle* handle) { EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSizeOfSerializeDataUpdate(handle, _, _)) .WillOnce(InvokeWithoutArgs([&]() { return sizeof(mWriteHandleSerializeDataInfo); })); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeDataUpdate(handle, _, _, _)) .WillOnce(WithArg<1>([&](void* serializePointer) { memcpy(serializePointer, &mWriteHandleSerializeDataInfo, sizeof(mWriteHandleSerializeDataInfo)); return sizeof(mWriteHandleSerializeDataInfo); })); } void ExpectServerWriteHandleDeserializeDataUpdate(ServerWriteHandle* handle, uint32_t expectedData) { EXPECT_CALL( serverMemoryTransferService, OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)), sizeof(mWriteHandleSerializeDataInfo), _, _)) .WillOnce(Return(true)); } void MockServerWriteHandleDeserializeDataUpdateFailure(ServerWriteHandle* handle) { EXPECT_CALL( serverMemoryTransferService, OnWriteHandleDeserializeDataUpdate(handle, Pointee(Eq(mWriteHandleSerializeDataInfo)), sizeof(mWriteHandleSerializeDataInfo), _, _)) .WillOnce(Return(false)); } // Arbitrary values used within tests to check if serialized data is correctly passed // between the client and server. The static data changes between runs of the tests and // test expectations will check that serialized values are passed to the respective // deserialization function. static uint32_t mSerializeCreateInfo; static uint32_t mReadHandleSerializeDataInfo; static uint32_t mWriteHandleSerializeDataInfo; // Represents the buffer contents for the test. static uint32_t mBufferContent; static constexpr size_t kBufferSize = sizeof(mBufferContent); // The client's zero-initialized buffer for writing. uint32_t mMappedBufferContent = 0; // |mMappedBufferContent| should be set equal to |mUpdatedBufferContent| when the client // performs a write. Test expectations should check that |mBufferContent == // mUpdatedBufferContent| after all writes are flushed. static uint32_t mUpdatedBufferContent; testing::StrictMock serverMemoryTransferService; testing::StrictMock clientMemoryTransferService; }; uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337; uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349; uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242; uint32_t WireMemoryTransferServiceTests::mReadHandleSerializeDataInfo = 1394; uint32_t WireMemoryTransferServiceTests::mWriteHandleSerializeDataInfo = 1235; // Test successful mapping for reading. TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on creation. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); // The server should deserialize the read handle from the client and then serialize // an initialization message. ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // The handle serialize data update on mapAsync cmd ExpectServerReadHandleSerializeDataUpdate(serverHandle); // Mock a successful callback EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle)) .WillOnce(Return(&mBufferContent)); EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mBufferContent)); FlushClient(); // The client receives a successful callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); // The client should receive the handle data update message from the server. ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent); FlushServer(); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); } // Test ReadHandle destroy behavior TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroy) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on creation. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); // The server should deserialize the read handle from the client and then serialize // an initialization message. ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); wgpuBufferDestroy(buffer); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); FlushClient(); } // Test unsuccessful mapping for reading. TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on creation. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); // The server should deserialize the ReadHandle from the client. ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Mock a failed callback. EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); // The client receives an error callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); } // Test ReadHandle creation failure. TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) { // Mock a ReadHandle creation failure MockReadHandleCreationFailure(); WGPUBufferDescriptor descriptor = {}; descriptor.size = kBufferSize; descriptor.usage = WGPUBufferUsage_MapRead; wgpuDeviceCreateBuffer(device, &descriptor); } // Test MapRead DeserializeReadHandle failure. TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on mapping for reading.. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); // Mock a Deserialization failure. MockServerReadHandleDeserializeFailure(); FlushClient(false); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); } // Test read handle DeserializeDataUpdate failure. TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeDataUpdateFailure) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on mapping for reading. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); // The server should deserialize the read handle from the client and then serialize // an initialization message. ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // The handle serialize data update on mapAsync cmd ExpectServerReadHandleSerializeDataUpdate(serverHandle); // Mock a successful callback EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mBufferContent)); FlushClient(); // The client should receive the handle data update message from the server. // Mock a deserialization failure. MockClientReadHandleDeserializeDataUpdateFailure(clientHandle); // Failed deserialization is a fatal failure and the client synchronously receives a // DEVICE_LOST callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, _)).Times(1); FlushServer(false); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); } // Test mapping for reading destroying the buffer before unmapping on the client side. TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a ReadHandle on mapping for reading.. ClientReadHandle* clientHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientHandle); // The server should deserialize the read handle from the client and then serialize // an initialization message. ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize(); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapRead); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // The handle serialize data update on mapAsync cmd ExpectServerReadHandleSerializeDataUpdate(serverHandle); // Mock a successful callback EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientHandle)) .WillOnce(Return(&mBufferContent)); EXPECT_CALL(api, BufferGetConstMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mBufferContent)); FlushClient(); // The client receives a successful callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); // The client should receive the handle data update message from the server. ExpectClientReadHandleDeserializeDataUpdate(clientHandle, &mBufferContent); FlushServer(); // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping // immediately, both in the client and server side. { EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1); wgpuBufferDestroy(buffer); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); FlushClient(); // The handle is already destroyed so unmap only results in a server unmap call. wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } } // Test successful mapping for writing. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) { WGPUBuffer buffer; WGPUBuffer apiBuffer; ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Mock a successful callback. EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle)) .WillOnce(Return(&mBufferContent)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mMappedBufferContent)); FlushClient(); // The client receives a successful callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); // The client writes to the handle contents. mMappedBufferContent = mUpdatedBufferContent; // The client will then serialize data update and destroy the handle on Unmap() ExpectClientWriteHandleSerializeDataUpdate(clientHandle); wgpuBufferUnmap(buffer); // The server deserializes the data update message. ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); } // Test WriteHandle destroy behavior TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroy) { WGPUBuffer buffer; WGPUBuffer apiBuffer; ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); wgpuBufferDestroy(buffer); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); FlushClient(); } // Test unsuccessful MapWrite. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Mock an error callback. EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); // The client receives an error callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); } // Test WriteHandle creation failure. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) { // Mock a WriteHandle creation failure MockWriteHandleCreationFailure(); WGPUBufferDescriptor descriptor = {}; descriptor.size = kBufferSize; descriptor.usage = WGPUBufferUsage_MapWrite; wgpuDeviceCreateBuffer(device, &descriptor); } // Test MapWrite DeserializeWriteHandle failure. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) { WGPUBuffer buffer; WGPUBuffer apiBuffer; // The client should create and serialize a WriteHandle on buffer creation with MapWrite usage. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // Mock a deserialization failure. MockServerWriteHandleDeserializeFailure(); FlushClient(false); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); } // Test MapWrite DeserializeDataUpdate failure. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeDataUpdateFailure) { WGPUBuffer buffer; WGPUBuffer apiBuffer; ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Mock a successful callback. EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle)) .WillOnce(Return(&mBufferContent)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mMappedBufferContent)); FlushClient(); // The client receives a success callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); // The client writes to the handle contents. mMappedBufferContent = mUpdatedBufferContent; // The client will then serialize data update ExpectClientWriteHandleSerializeDataUpdate(clientHandle); wgpuBufferUnmap(buffer); // The server deserializes the data update message. Mock a deserialization failure. MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle); FlushClient(false); // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); } // Test MapWrite destroying the buffer before unmapping on the client side. TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDestroyBeforeUnmap) { WGPUBuffer buffer; WGPUBuffer apiBuffer; ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(false); ExpectWriteHandleSerialization(clientHandle); std::tie(apiBuffer, buffer) = CreateBuffer(WGPUBufferUsage_MapWrite); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Mock a successful callback. EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleGetData(clientHandle)) .WillOnce(Return(&mBufferContent)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&mMappedBufferContent)); FlushClient(); // The client receives a successful callback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); // The client writes to the handle contents. mMappedBufferContent = mUpdatedBufferContent; // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping // immediately, both in the client and server side. { // The handle is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); wgpuBufferDestroy(buffer); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); FlushClient(); // The handle is already destroyed so unmap only results in a server unmap call. wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } } // Test successful buffer creation with mappedAtCreation = true. TEST_F(WireMemoryTransferServiceTests, MappedAtCreationSuccess) { // The client should create and serialize a WriteHandle on createBufferMapped. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientHandle); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); WGPUBuffer buffer; WGPUBuffer apiBuffer; std::tie(apiBuffer, buffer) = CreateBufferMapped(); FlushClient(); // Update the mapped contents. mMappedBufferContent = mUpdatedBufferContent; // When the client Unmaps the buffer, it will serialize data update writes to the handle and // destroy it. ExpectClientWriteHandleSerializeDataUpdate(clientHandle); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); wgpuBufferUnmap(buffer); // The server deserializes the data update message. ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent); // After the handle is updated it can be destroyed. EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Test buffer creation with mappedAtCreation WriteHandle creation failure. TEST_F(WireMemoryTransferServiceTests, MappedAtCreationWriteHandleCreationFailure) { // Mock a WriteHandle creation failure MockWriteHandleCreationFailure(); WGPUBufferDescriptor descriptor = {}; descriptor.size = sizeof(mBufferContent); descriptor.mappedAtCreation = true; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, sizeof(mBufferContent))); } // Test buffer creation with mappedAtCreation DeserializeWriteHandle failure. TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeWriteHandleFailure) { // The client should create and serialize a WriteHandle on createBufferMapped. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientHandle); // The server should then deserialize the WriteHandle from the client. MockServerWriteHandleDeserializeFailure(); WGPUBufferDescriptor descriptor = {}; descriptor.size = sizeof(mBufferContent); descriptor.mappedAtCreation = true; WGPUBuffer apiBuffer = api.GetNewBuffer(); wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); // Now bufferGetMappedRange won't be called if deserialize writeHandle fails FlushClient(false); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); } // Test buffer creation with mappedAtCreation = true DeserializeDataUpdate failure. TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDeserializeDataUpdateFailure) { // The client should create and serialize a WriteHandle on createBufferMapped. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientHandle); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); WGPUBuffer buffer; WGPUBuffer apiBuffer; std::tie(apiBuffer, buffer) = CreateBufferMapped(); FlushClient(); // Update the mapped contents. mMappedBufferContent = mUpdatedBufferContent; // When the client Unmaps the buffer, it will serialize data update writes to the handle and // destroy it. ExpectClientWriteHandleSerializeDataUpdate(clientHandle); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); wgpuBufferUnmap(buffer); // The server deserializes the data update message. Mock a deserialization failure. MockServerWriteHandleDeserializeDataUpdateFailure(serverHandle); FlushClient(false); // Failed BufferUpdateMappedData cmd will early return so BufferUnmap is not processed. // The server side writeHandle is destructed at buffer destruction. EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); } // Test mappedAtCreation=true destroying the buffer before unmapping on the client side. TEST_F(WireMemoryTransferServiceTests, MappedAtCreationDestroyBeforeUnmap) { // The client should create and serialize a WriteHandle on createBufferMapped. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientHandle); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); WGPUBuffer buffer; WGPUBuffer apiBuffer; std::tie(apiBuffer, buffer) = CreateBufferMapped(); FlushClient(); // Update the mapped contents. mMappedBufferContent = mUpdatedBufferContent; // THIS IS THE TEST: destroy the buffer before unmapping and check it destroyed the mapping // immediately, both in the client and server side. { EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); wgpuBufferDestroy(buffer); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); EXPECT_CALL(api, BufferDestroy(apiBuffer)).Times(1); FlushClient(); // The handle is already destroyed so unmap only results in a server unmap call. wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } } // Test a buffer with mappedAtCreation and MapRead usage destroy WriteHandle on unmap and switch // data pointer to ReadHandle TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapReadSuccess) { // The client should create and serialize a ReadHandle and a WriteHandle on createBufferMapped. ClientReadHandle* clientReadHandle = ExpectReadHandleCreation(); ExpectReadHandleSerialization(clientReadHandle); ClientWriteHandle* clientWriteHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientWriteHandle); // The server should then deserialize a ReadHandle and a WriteHandle from the client. ServerReadHandle* serverReadHandle = ExpectServerReadHandleDeserialize(); ServerWriteHandle* serverWriteHandle = ExpectServerWriteHandleDeserialization(); WGPUBuffer buffer; WGPUBuffer apiBuffer; std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapRead); FlushClient(); // Update the mapped contents. mMappedBufferContent = mUpdatedBufferContent; // When the client Unmaps the buffer, it will serialize data update writes to the handle and // destroy it. ExpectClientWriteHandleSerializeDataUpdate(clientWriteHandle); EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientWriteHandle)).Times(1); EXPECT_CALL(clientMemoryTransferService, OnReadHandleGetData(clientReadHandle)) .WillOnce(Return(&mBufferContent)); wgpuBufferUnmap(buffer); // The server deserializes the data update message. ExpectServerWriteHandleDeserializeDataUpdate(serverWriteHandle, mUpdatedBufferContent); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverWriteHandle)).Times(1); FlushClient(); // The ReadHandle will be destoryed on buffer destroy. EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientReadHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverReadHandle)).Times(1); } // Test WriteHandle preserves after unmap for a buffer with mappedAtCreation and MapWrite usage TEST_F(WireMemoryTransferServiceTests, MappedAtCreationAndMapWriteSuccess) { // The client should create and serialize a WriteHandle on createBufferMapped. ClientWriteHandle* clientHandle = ExpectWriteHandleCreation(true); ExpectWriteHandleSerialization(clientHandle); // The server should then deserialize the WriteHandle from the client. ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization(); WGPUBuffer buffer; WGPUBuffer apiBuffer; std::tie(apiBuffer, buffer) = CreateBufferMapped(WGPUBufferUsage_MapWrite); FlushClient(); // Update the mapped contents. mMappedBufferContent = mUpdatedBufferContent; // When the client Unmaps the buffer, it will serialize data update writes to the handle. ExpectClientWriteHandleSerializeDataUpdate(clientHandle); wgpuBufferUnmap(buffer); // The server deserializes the data update message. ExpectServerWriteHandleDeserializeDataUpdate(serverHandle, mUpdatedBufferContent); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // The writeHandle is preserved after unmap and is destroyed once the buffer is destroyed. EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1); EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1); }