// 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/WireClient.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 class WireBufferMappingTests : public WireTest { public: WireBufferMappingTests() { } ~WireBufferMappingTests() override = default; void SetUp() override { WireTest::SetUp(); mockBufferMapCallback = std::make_unique>(); apiBuffer = api.GetNewBuffer(); } void TearDown() override { WireTest::TearDown(); // Delete mock so that expectations are checked mockBufferMapCallback = nullptr; } void FlushClient() { WireTest::FlushClient(); Mock::VerifyAndClearExpectations(&mockBufferMapCallback); } void FlushServer() { WireTest::FlushServer(); Mock::VerifyAndClearExpectations(&mockBufferMapCallback); } void SetupBuffer(WGPUBufferUsageFlags usage) { WGPUBufferDescriptor descriptor = {}; descriptor.size = kBufferSize; descriptor.usage = usage; buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)) .WillOnce(Return(apiBuffer)) .RetiresOnSaturation(); FlushClient(); } protected: static constexpr uint64_t kBufferSize = sizeof(uint32_t); // A successfully created buffer WGPUBuffer buffer; WGPUBuffer apiBuffer; }; // Tests specific to mapping for reading class WireBufferMappingReadTests : public WireBufferMappingTests { public: WireBufferMappingReadTests() { } ~WireBufferMappingReadTests() override = default; void SetUp() override { WireBufferMappingTests::SetUp(); SetupBuffer(WGPUBufferUsage_MapRead); } }; // Check mapping for reading a succesfully created buffer TEST_F(WireBufferMappingReadTests, MappingForReadSuccessBuffer) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); EXPECT_EQ(bufferContent, *static_cast(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize))); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Check that things work correctly when a validation error happens when mapping the buffer for // reading TEST_F(WireBufferMappingReadTests, ErrorWhileMappingForRead) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); EXPECT_EQ(nullptr, wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize)); } // Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the // request is finished TEST_F(WireBufferMappingReadTests, DestroyBeforeReadRequestEnd) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Return success uint32_t bufferContent = 0; 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(&bufferContent)); // Destroy before the client gets the success, so the callback is called with // DestroyedBeforeCallback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _)) .Times(1); wgpuBufferRelease(buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); FlushServer(); } // Check the map read callback is called with "UnmappedBeforeCallback" when the map request would // have worked, but Unmap was called TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForRead) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); // Oh no! We are calling Unmap too early! However the callback gets fired only after we get // an answer from the server. wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)); FlushClient(); // The callback shouldn't get called with success, even when the request succeeded on the // server side EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _)) .Times(1); FlushServer(); } // Check that even if Unmap() was called early client-side, we correctly surface server-side // validation errors. TEST_F(WireBufferMappingReadTests, UnmapCalledTooEarlyForReadButServerSideError) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); // Oh no! We are calling Unmap too early! However the callback gets fired only after we get // an answer from the server that the mapAsync call was an error. wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)); FlushClient(); // The callback should be called with the server-side error and not the UnmappedBeforeCallback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)) .Times(1); FlushServer(); } // Check the map read callback is called with "DestroyedBeforeCallback" when the map request would // have worked, but Destroy was called TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForRead) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); // Oh no! We are calling Unmap too early! However the callback gets fired only after we get // an answer from the server. wgpuBufferDestroy(buffer); EXPECT_CALL(api, BufferDestroy(apiBuffer)); FlushClient(); // The callback shouldn't get called with success, even when the request succeeded on the // server side EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _)) .Times(1); FlushServer(); } // Check that even if Destroy() was called early client-side, we correctly surface server-side // validation errors. TEST_F(WireBufferMappingReadTests, DestroyCalledTooEarlyForReadButServerSideError) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); // Oh no! We are calling Destroy too early! However the callback gets fired only after we get // an answer from the server that the mapAsync call was an error. wgpuBufferDestroy(buffer); EXPECT_CALL(api, BufferDestroy(apiBuffer)); FlushClient(); // The callback should be called with the server-side error and not the DestroyedBeforCallback.. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)) .Times(1); FlushServer(); } // Check that an error map read while a buffer is already mapped won't changed the result of get // mapped range TEST_F(WireBufferMappingReadTests, MappingForReadingErrorWhileAlreadyMappedUnchangeMapData) { // Successful map wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); // Map failure while the buffer is already mapped wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Read, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); EXPECT_EQ(bufferContent, *static_cast(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize))); } // Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback TEST_F(WireBufferMappingReadTests, UnmapInsideMapReadCallback) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferUnmap(buffer); })); FlushServer(); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Test that the MapReadCallback isn't fired twice the buffer external refcount reaches 0 in the // callback TEST_F(WireBufferMappingReadTests, DestroyInsideMapReadCallback) { wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; 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(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferRelease(buffer); })); FlushServer(); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); } // Tests specific to mapping for writing class WireBufferMappingWriteTests : public WireBufferMappingTests { public: WireBufferMappingWriteTests() { } ~WireBufferMappingWriteTests() override = default; void SetUp() override { WireBufferMappingTests::SetUp(); SetupBuffer(WGPUBufferUsage_MapWrite); } }; // Check mapping for writing a succesfully created buffer TEST_F(WireBufferMappingWriteTests, MappingForWriteSuccessBuffer) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t serverBufferContent = 31337; uint32_t updatedContent = 4242; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&serverBufferContent)); FlushClient(); // The map write callback always gets a buffer full of zeroes. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); uint32_t* lastMapWritePointer = static_cast(wgpuBufferGetMappedRange(buffer, 0, kBufferSize)); ASSERT_EQ(0u, *lastMapWritePointer); // Write something to the mapped pointer *lastMapWritePointer = updatedContent; wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); // After the buffer is unmapped, the content of the buffer is updated on the server ASSERT_EQ(serverBufferContent, updatedContent); } // Check that things work correctly when a validation error happens when mapping the buffer for // writing TEST_F(WireBufferMappingWriteTests, ErrorWhileMappingForWrite) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); EXPECT_EQ(nullptr, wgpuBufferGetMappedRange(buffer, 0, kBufferSize)); } // Check that the map write callback is called with "DestroyedBeforeCallback" when the buffer is // destroyed before the request is finished TEST_F(WireBufferMappingWriteTests, DestroyBeforeWriteRequestEnd) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); // Return success uint32_t bufferContent = 31337; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&bufferContent)); // Destroy before the client gets the success, so the callback is called with // DestroyedBeforeCallback. EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, _)) .Times(1); wgpuBufferRelease(buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); FlushServer(); } // Check the map write callback is called with "UnmappedBeforeCallback" when the map request would // have worked, but Unmap was called TEST_F(WireBufferMappingWriteTests, UnmapCalledTooEarlyForWrite) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&bufferContent)); FlushClient(); // Oh no! We are calling Unmap too early! EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_UnmappedBeforeCallback, _)) .Times(1); wgpuBufferUnmap(buffer); // The callback shouldn't get called, even when the request succeeded on the server side FlushServer(); } // Check that an error map write while a buffer is already mapped TEST_F(WireBufferMappingWriteTests, MappingForWritingErrorWhileAlreadyMapped) { // Successful map wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); // Map failure while the buffer is already mapped wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); EXPECT_NE(nullptr, static_cast(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize))); } // Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback TEST_F(WireBufferMappingWriteTests, UnmapInsideMapWriteCallback) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferUnmap(buffer); })); FlushServer(); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Test that the MapWriteCallback isn't fired twice the buffer external refcount reaches 0 in the // callback TEST_F(WireBufferMappingWriteTests, DestroyInsideMapWriteCallback) { wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); uint32_t bufferContent = 31337; EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&bufferContent)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)) .WillOnce(InvokeWithoutArgs([&]() { wgpuBufferRelease(buffer); })); FlushServer(); EXPECT_CALL(api, BufferRelease(apiBuffer)); FlushClient(); } // Test successful buffer creation with mappedAtCreation=true TEST_F(WireBufferMappingTests, MappedAtCreationSuccess) { WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; descriptor.mappedAtCreation = true; WGPUBuffer apiBuffer = api.GetNewBuffer(); uint32_t apiBufferData = 1234; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData)); FlushClient(); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Test that releasing a buffer mapped at creation does not call Unmap TEST_F(WireBufferMappingTests, MappedAtCreationReleaseBeforeUnmap) { WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; descriptor.mappedAtCreation = true; WGPUBuffer apiBuffer = api.GetNewBuffer(); uint32_t apiBufferData = 1234; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData)); FlushClient(); wgpuBufferRelease(buffer); EXPECT_CALL(api, BufferRelease(apiBuffer)).Times(1); FlushClient(); } // Test that it is valid to map a buffer after it is mapped at creation and unmapped TEST_F(WireBufferMappingTests, MappedAtCreationThenMapSuccess) { WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; descriptor.usage = WGPUMapMode_Write; descriptor.mappedAtCreation = true; WGPUBuffer apiBuffer = api.GetNewBuffer(); uint32_t apiBufferData = 1234; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData)); FlushClient(); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)) .WillOnce(Return(&apiBufferData)); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Success, _)).Times(1); FlushServer(); } // Test that it is invalid to map a buffer after mappedAtCreation but before Unmap TEST_F(WireBufferMappingTests, MappedAtCreationThenMapFailure) { WGPUBufferDescriptor descriptor = {}; descriptor.size = 4; descriptor.mappedAtCreation = true; WGPUBuffer apiBuffer = api.GetNewBuffer(); uint32_t apiBufferData = 1234; WGPUBuffer buffer = wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _)).WillOnce(Return(apiBuffer)); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, 4)).WillOnce(Return(&apiBufferData)); FlushClient(); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, nullptr); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs( [&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error); })); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_Error, _)).Times(1); FlushServer(); EXPECT_NE(nullptr, static_cast(wgpuBufferGetConstMappedRange(buffer, 0, kBufferSize))); wgpuBufferUnmap(buffer); EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1); FlushClient(); } // Check that trying to create a buffer of size MAX_SIZE_T is an error handling in the client and // never gets to the server-side. TEST_F(WireBufferMappingTests, MaxSizeMappableBufferOOMDirectly) { size_t kOOMSize = std::numeric_limits::max(); WGPUBuffer apiBuffer = api.GetNewBuffer(); // Check for CreateBufferMapped. { WGPUBufferDescriptor descriptor = {}; descriptor.usage = WGPUBufferUsage_CopySrc; descriptor.size = kOOMSize; descriptor.mappedAtCreation = true; wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_OutOfMemory, _)); EXPECT_CALL(api, DeviceCreateErrorBuffer(apiDevice)).WillOnce(Return(apiBuffer)); FlushClient(); } // Check for MapRead usage. { WGPUBufferDescriptor descriptor = {}; descriptor.usage = WGPUBufferUsage_MapRead; descriptor.size = kOOMSize; wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_OutOfMemory, _)); EXPECT_CALL(api, DeviceCreateErrorBuffer(apiDevice)).WillOnce(Return(apiBuffer)); FlushClient(); } // Check for MapWrite usage. { WGPUBufferDescriptor descriptor = {}; descriptor.usage = WGPUBufferUsage_MapWrite; descriptor.size = kOOMSize; wgpuDeviceCreateBuffer(device, &descriptor); EXPECT_CALL(api, DeviceInjectError(apiDevice, WGPUErrorType_OutOfMemory, _)); EXPECT_CALL(api, DeviceCreateErrorBuffer(apiDevice)).WillOnce(Return(apiBuffer)); FlushClient(); } } // Test that registering a callback then wire disconnect calls the callback with // DeviceLost. TEST_F(WireBufferMappingTests, MapThenDisconnect) { SetupBuffer(WGPUMapMode_Write); wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallback, this); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)).Times(1); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1); GetWireClient()->Disconnect(); } // Test that registering a callback after wire disconnect calls the callback with // DeviceLost. TEST_F(WireBufferMappingTests, MapAfterDisconnect) { SetupBuffer(WGPUMapMode_Read); GetWireClient()->Disconnect(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)).Times(1); wgpuBufferMapAsync(buffer, WGPUMapMode_Read, 0, kBufferSize, ToMockBufferMapCallback, this); } // Hack to pass in test context into user callback struct TestData { WireBufferMappingTests* pTest; WGPUBuffer* pTestBuffer; size_t numRequests; }; static void ToMockBufferMapCallbackWithNewRequests(WGPUBufferMapAsyncStatus status, void* userdata) { TestData* testData = reinterpret_cast(userdata); // Mimic the user callback is sending new requests ASSERT_NE(testData, nullptr); ASSERT_NE(testData->pTest, nullptr); ASSERT_NE(testData->pTestBuffer, nullptr); mockBufferMapCallback->Call(status, testData->pTest); // Send the requests a number of times for (size_t i = 0; i < testData->numRequests; i++) { wgpuBufferMapAsync(*(testData->pTestBuffer), WGPUMapMode_Write, 0, sizeof(uint32_t), ToMockBufferMapCallback, testData->pTest); } } // Test that requests inside user callbacks before disconnect are called TEST_F(WireBufferMappingTests, MapInsideCallbackBeforeDisconnect) { SetupBuffer(WGPUMapMode_Write); TestData testData = {this, &buffer, 10}; wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallbackWithNewRequests, &testData); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)).Times(1); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DeviceLost, this)) .Times(1 + testData.numRequests); GetWireClient()->Disconnect(); } // Test that requests inside user callbacks before object destruction are called TEST_F(WireBufferMappingWriteTests, MapInsideCallbackBeforeDestruction) { TestData testData = {this, &buffer, 10}; wgpuBufferMapAsync(buffer, WGPUMapMode_Write, 0, kBufferSize, ToMockBufferMapCallbackWithNewRequests, &testData); EXPECT_CALL(api, OnBufferMapAsync(apiBuffer, WGPUMapMode_Write, 0, kBufferSize, _, _)) .WillOnce(InvokeWithoutArgs([&]() { api.CallBufferMapAsyncCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success); })); EXPECT_CALL(api, BufferGetMappedRange(apiBuffer, 0, kBufferSize)).Times(1); FlushClient(); EXPECT_CALL(*mockBufferMapCallback, Call(WGPUBufferMapAsyncStatus_DestroyedBeforeCallback, this)) .Times(1 + testData.numRequests); wgpuBufferRelease(buffer); }