blob: 1d7d8286867b36fb8aef40e0af4fa6c5f7b4d537 [file] [log] [blame]
// 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 classes to add expectations on the wire calling callbacks
class MockBufferMapReadCallback {
public:
MOCK_METHOD(void,
Call,
(WGPUBufferMapAsyncStatus status,
const uint32_t* ptr,
uint64_t dataLength,
void* userdata));
};
std::unique_ptr<StrictMock<MockBufferMapReadCallback>> mockBufferMapReadCallback;
void ToMockBufferMapReadCallback(WGPUBufferMapAsyncStatus status,
const void* ptr,
uint64_t dataLength,
void* userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapReadCallback->Call(status, static_cast<const uint32_t*>(ptr), dataLength,
userdata);
}
class MockBufferMapWriteCallback {
public:
MOCK_METHOD(
void,
Call,
(WGPUBufferMapAsyncStatus status, uint32_t* ptr, uint64_t dataLength, void* userdata));
};
std::unique_ptr<StrictMock<MockBufferMapWriteCallback>> mockBufferMapWriteCallback;
void ToMockBufferMapWriteCallback(WGPUBufferMapAsyncStatus status,
void* ptr,
uint64_t dataLength,
void* userdata) {
// Assume the data is uint32_t to make writing matchers easier
mockBufferMapWriteCallback->Call(status, static_cast<uint32_t*>(ptr), dataLength, userdata);
}
class MockBufferCreateMappedCallback {
public:
MOCK_METHOD(void,
Call,
(WGPUBufferMapAsyncStatus status,
WGPUBuffer buffer,
uint32_t* ptr,
uint64_t dataLength,
void* 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();
mockBufferMapReadCallback = std::make_unique<StrictMock<MockBufferMapReadCallback>>();
mockBufferMapWriteCallback = std::make_unique<StrictMock<MockBufferMapWriteCallback>>();
// TODO(enga): Make this thread-safe.
mBufferContent++;
mMappedBufferContent = 0;
mUpdatedBufferContent++;
mSerializeCreateInfo++;
mSerializeInitialDataInfo++;
mSerializeFlushInfo++;
}
void TearDown() override {
WireTest::TearDown();
// Delete mocks so that expectations are checked
mockBufferMapReadCallback = nullptr;
mockBufferMapWriteCallback = nullptr;
}
void FlushClient(bool success = true) {
WireTest::FlushClient(success);
Mock::VerifyAndClearExpectations(&serverMemoryTransferService);
}
void FlushServer(bool success = true) {
WireTest::FlushServer(success);
Mock::VerifyAndClearExpectations(&mockBufferMapReadCallback);
Mock::VerifyAndClearExpectations(&mockBufferMapWriteCallback);
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<WGPUBuffer, WGPUBuffer> CreateBuffer() {
WGPUBufferDescriptor descriptor = {};
descriptor.size = sizeof(mBufferContent);
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<WGPUCreateBufferMappedResult, WGPUCreateBufferMappedResult> CreateBufferMapped() {
WGPUBufferDescriptor descriptor = {};
descriptor.size = sizeof(mBufferContent);
WGPUBuffer apiBuffer = api.GetNewBuffer();
WGPUCreateBufferMappedResult apiResult;
apiResult.buffer = apiBuffer;
apiResult.data = reinterpret_cast<uint8_t*>(&mMappedBufferContent);
apiResult.dataLength = sizeof(mMappedBufferContent);
WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor);
EXPECT_CALL(api, DeviceCreateBufferMapped(apiDevice, _))
.WillOnce(Return(apiResult))
.RetiresOnSaturation();
return std::make_pair(apiResult, result);
}
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 ExpectServerReadHandleInitialize(ServerReadHandle* handle) {
EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialDataSize(handle, _, _))
.WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeInitialDataInfo); }));
EXPECT_CALL(serverMemoryTransferService, OnReadHandleSerializeInitialData(handle, _, _, _))
.WillOnce(WithArg<3>([&](void* serializePointer) {
memcpy(serializePointer, &mSerializeInitialDataInfo,
sizeof(mSerializeInitialDataInfo));
return sizeof(mSerializeInitialDataInfo);
}));
}
void ExpectClientReadHandleDeserializeInitialize(ClientReadHandle* handle,
uint32_t* mappedData) {
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
handle, Pointee(Eq(mSerializeInitialDataInfo)),
sizeof(mSerializeInitialDataInfo), _, _))
.WillOnce(WithArgs<3, 4>([=](const void** data, size_t* dataLength) {
*data = mappedData;
*dataLength = sizeof(*mappedData);
return true;
}));
}
void MockClientReadHandleDeserializeInitializeFailure(ClientReadHandle* handle) {
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDeserializeInitialData(
handle, Pointee(Eq(mSerializeInitialDataInfo)),
sizeof(mSerializeInitialDataInfo), _, _))
.WillOnce(InvokeWithoutArgs([&]() { return false; }));
}
ClientWriteHandle* ExpectWriteHandleCreation() {
// 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;
}));
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(InvokeWithoutArgs([&]() {
return false;
}));
}
void ExpectClientWriteHandleOpen(ClientWriteHandle* handle, uint32_t* mappedData) {
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
.WillOnce(InvokeWithoutArgs([=]() {
return std::make_pair(mappedData, sizeof(*mappedData));
}));
}
void MockClientWriteHandleOpenFailure(ClientWriteHandle* handle) {
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleOpen(handle))
.WillOnce(InvokeWithoutArgs(
[&]() { return std::make_pair(nullptr, 0); }));
}
void ExpectClientWriteHandleSerializeFlush(ClientWriteHandle* handle) {
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlushSize(handle))
.WillOnce(InvokeWithoutArgs([&]() { return sizeof(mSerializeFlushInfo); }));
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleSerializeFlush(handle, _))
.WillOnce(WithArg<1>([&](void* serializePointer) {
memcpy(serializePointer, &mSerializeFlushInfo, sizeof(mSerializeFlushInfo));
return sizeof(mSerializeFlushInfo);
}));
}
void ExpectServerWriteHandleDeserializeFlush(ServerWriteHandle* handle, uint32_t expectedData) {
EXPECT_CALL(serverMemoryTransferService,
OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
sizeof(mSerializeFlushInfo)))
.WillOnce(InvokeWithoutArgs([=]() {
// The handle data should be updated.
EXPECT_EQ(*handle->GetData(), expectedData);
return true;
}));
}
void MockServerWriteHandleDeserializeFlushFailure(ServerWriteHandle* handle) {
EXPECT_CALL(serverMemoryTransferService,
OnWriteHandleDeserializeFlush(handle, Pointee(Eq(mSerializeFlushInfo)),
sizeof(mSerializeFlushInfo)))
.WillOnce(InvokeWithoutArgs([&]() { 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 mSerializeInitialDataInfo;
static uint32_t mSerializeFlushInfo;
// Represents the buffer contents for the test.
static uint32_t 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<dawn_wire::server::MockMemoryTransferService> serverMemoryTransferService;
testing::StrictMock<dawn_wire::client::MockMemoryTransferService> clientMemoryTransferService;
};
uint32_t WireMemoryTransferServiceTests::mBufferContent = 1337;
uint32_t WireMemoryTransferServiceTests::mUpdatedBufferContent = 2349;
uint32_t WireMemoryTransferServiceTests::mSerializeCreateInfo = 4242;
uint32_t WireMemoryTransferServiceTests::mSerializeInitialDataInfo = 1394;
uint32_t WireMemoryTransferServiceTests::mSerializeFlushInfo = 1235;
// Test successful MapRead.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadSuccess) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a ReadHandle on mapReadAsync.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
// The server should deserialize the MapRead handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
ExpectServerReadHandleInitialize(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapReadCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success, &mBufferContent,
sizeof(mBufferContent));
}));
FlushClient();
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, &mBufferContent, sizeof(mBufferContent), _))
.Times(1);
// The client should receive the handle initialization message from the server.
ExpectClientReadHandleDeserializeInitialize(clientHandle, &mBufferContent);
FlushServer();
// The handle is destroyed once the buffer is unmapped.
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
}
// Test unsuccessful MapRead.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadError) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a ReadHandle on mapReadAsync.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
// The server should deserialize the ReadHandle from the client.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
// Mock a failed callback.
EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapReadCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error, nullptr, 0);
}));
// Since the mapping failed, the handle is immediately destroyed.
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
FlushClient();
// The client receives an error callback.
EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
.Times(1);
// The client receives the map failure and destroys the handle.
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
FlushServer();
wgpuBufferUnmap(buffer);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
}
// Test MapRead ReadHandle creation failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadHandleCreationFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// Mock a ReadHandle creation failure
MockReadHandleCreationFailure();
// Failed creation of a ReadHandle is a fatal failure and the client synchronously receives a
// DEVICE_LOST callback.
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
}
// Test MapRead DeserializeReadHandle failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeReadHandleFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a ReadHandle on mapReadAsync.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
// Mock a Deserialization failure.
MockServerReadHandleDeserializeFailure();
FlushClient(false);
// The server received a fatal failure and the client callback was never returned.
// It is called when the wire is destructed.
EXPECT_CALL(*mockBufferMapReadCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _))
.Times(1);
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
}
// Test MapRead DeserializeInitialData failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDeserializeInitialDataFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a ReadHandle on mapReadAsync.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
// The server should deserialize the MapRead handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
ExpectServerReadHandleInitialize(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapReadCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success, &mBufferContent,
sizeof(mBufferContent));
}));
FlushClient();
// The client should receive the handle initialization message from the server.
// Mock a deserialization failure.
MockClientReadHandleDeserializeInitializeFailure(clientHandle);
// Failed deserialization is a fatal failure and the client synchronously receives a
// DEVICE_LOST callback.
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1);
// The handle will be destroyed since deserializing failed.
EXPECT_CALL(clientMemoryTransferService, OnReadHandleDestroy(clientHandle)).Times(1);
FlushServer(false);
EXPECT_CALL(serverMemoryTransferService, OnReadHandleDestroy(serverHandle)).Times(1);
}
// Test MapRead destroying the buffer before unmapping on the client side.
TEST_F(WireMemoryTransferServiceTests, BufferMapReadDestroyBeforeUnmap) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a ReadHandle on mapReadAsync.
ClientReadHandle* clientHandle = ExpectReadHandleCreation();
ExpectReadHandleSerialization(clientHandle);
wgpuBufferMapReadAsync(buffer, ToMockBufferMapReadCallback, nullptr);
// The server should deserialize the MapRead handle from the client and then serialize
// an initialization message.
ServerReadHandle* serverHandle = ExpectServerReadHandleDeserialize();
ExpectServerReadHandleInitialize(serverHandle);
// Mock a successful callback
EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapReadCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success, &mBufferContent,
sizeof(mBufferContent));
}));
FlushClient();
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapReadCallback,
Call(WGPUBufferMapAsyncStatus_Success, &mBufferContent, sizeof(mBufferContent), _))
.Times(1);
// The client should receive the handle initialization message from the server.
ExpectClientReadHandleDeserializeInitialize(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 MapWrite.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteSuccess) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success,
&mMappedBufferContent, sizeof(mMappedBufferContent));
}));
FlushClient();
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent,
sizeof(mMappedBufferContent), _))
.Times(1);
// Since the mapping succeeds, the client opens the WriteHandle.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
FlushServer();
// The client writes to the handle contents.
mMappedBufferContent = mUpdatedBufferContent;
// The client will then flush and destroy the handle on Unmap()
ExpectClientWriteHandleSerializeFlush(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
// The server deserializes the Flush message.
ExpectServerWriteHandleDeserializeFlush(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 unsuccessful MapWrite.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteError) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a WriteHandle on mapWriteAsync.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
// Mock an error callback.
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Error, nullptr, 0);
}));
// Since the mapping fails, the handle is immediately destroyed because it won't be written.
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
FlushClient();
// The client receives an error callback.
EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Error, nullptr, 0, _))
.Times(1);
// Client receives the map failure and destroys the handle.
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
FlushServer();
wgpuBufferUnmap(buffer);
EXPECT_CALL(api, BufferUnmap(apiBuffer)).Times(1);
FlushClient();
}
// Test MapRead WriteHandle creation failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleCreationFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// Mock a WriteHandle creation failure
MockWriteHandleCreationFailure();
// Failed creation of a WriteHandle is a fatal failure and the client synchronously receives a
// DEVICE_LOST callback.
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
}
// Test MapWrite DeserializeWriteHandle failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeWriteHandleFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
// The client should create and serialize a WriteHandle on mapWriteAsync.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// Mock a deserialization failure.
MockServerWriteHandleDeserializeFailure();
FlushClient(false);
// The server hit a fatal failure and never returned the callback. The client callback is
// called when the wire is destructed.
EXPECT_CALL(*mockBufferMapWriteCallback, Call(WGPUBufferMapAsyncStatus_Unknown, nullptr, 0, _))
.Times(1);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
}
// Test MapWrite handle Open failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteHandleOpenFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success,
&mMappedBufferContent, sizeof(mMappedBufferContent));
}));
FlushClient();
// Since the mapping succeeds, the client opens the WriteHandle.
// Mock a failure.
MockClientWriteHandleOpenFailure(clientHandle);
// Failing to open a handle is a fatal failure and the client receives a DEVICE_LOST callback.
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_DeviceLost, nullptr, 0, _))
.Times(1);
// Since opening the handle fails, it gets destroyed immediately.
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
FlushServer(false);
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
}
// Test MapWrite DeserializeFlush failure.
TEST_F(WireMemoryTransferServiceTests, BufferMapWriteDeserializeFlushFailure) {
WGPUBuffer buffer;
WGPUBuffer apiBuffer;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success,
&mMappedBufferContent, sizeof(mMappedBufferContent));
}));
FlushClient();
// The client receives a success callback.
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent,
sizeof(mMappedBufferContent), _))
.Times(1);
// Since the mapping succeeds, the client opens the WriteHandle.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
FlushServer();
// The client writes to the handle contents.
mMappedBufferContent = mUpdatedBufferContent;
// The client will then flush and destroy the handle on Unmap()
ExpectClientWriteHandleSerializeFlush(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(buffer);
// The server deserializes the Flush message. Mock a deserialization failure.
MockServerWriteHandleDeserializeFlushFailure(serverHandle);
FlushClient(false);
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;
std::tie(apiBuffer, buffer) = CreateBuffer();
FlushClient();
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
ExpectWriteHandleSerialization(clientHandle);
wgpuBufferMapWriteAsync(buffer, ToMockBufferMapWriteCallback, nullptr);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
// Mock a successful callback.
EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, _, _))
.WillOnce(InvokeWithoutArgs([&]() {
api.CallMapWriteCallback(apiBuffer, WGPUBufferMapAsyncStatus_Success,
&mMappedBufferContent, sizeof(mMappedBufferContent));
}));
FlushClient();
// The client receives a successful callback.
EXPECT_CALL(*mockBufferMapWriteCallback,
Call(WGPUBufferMapAsyncStatus_Success, &mMappedBufferContent,
sizeof(mMappedBufferContent), _))
.Times(1);
// Since the mapping succeeds, the client opens the WriteHandle.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
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.
{
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 CreateBufferMapped.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedSuccess) {
// The client should create and serialize a WriteHandle on createBufferMapped.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
// Staging data is immediately available so the handle is Opened.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
WGPUCreateBufferMappedResult result;
WGPUCreateBufferMappedResult apiResult;
std::tie(apiResult, result) = CreateBufferMapped();
FlushClient();
// Update the mapped contents.
mMappedBufferContent = mUpdatedBufferContent;
// When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
ExpectClientWriteHandleSerializeFlush(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(result.buffer);
// The server deserializes the Flush message.
ExpectServerWriteHandleDeserializeFlush(serverHandle, mUpdatedBufferContent);
// After the handle is updated it can be destroyed.
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
EXPECT_CALL(api, BufferUnmap(apiResult.buffer)).Times(1);
FlushClient();
}
// Test CreateBufferMapped WriteHandle creation failure.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedWriteHandleCreationFailure) {
// Mock a WriteHandle creation failure
MockWriteHandleCreationFailure();
WGPUBufferDescriptor descriptor = {};
descriptor.size = sizeof(mBufferContent);
WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor);
// TODO(enga): Check that the client generated a context lost.
EXPECT_EQ(result.data, nullptr);
EXPECT_EQ(result.dataLength, 0u);
}
// Test CreateBufferMapped DeserializeWriteHandle failure.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDeserializeWriteHandleFailure) {
// The client should create and serialize a WriteHandle on createBufferMapped.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
// Staging data is immediately available so the handle is Opened.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
MockServerWriteHandleDeserializeFailure();
WGPUCreateBufferMappedResult result;
WGPUCreateBufferMappedResult apiResult;
std::tie(apiResult, result) = CreateBufferMapped();
FlushClient(false);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
}
// Test CreateBufferMapped handle Open failure.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedHandleOpenFailure) {
// The client should create a WriteHandle on createBufferMapped.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
// Staging data is immediately available so the handle is Opened.
// Mock a failure.
MockClientWriteHandleOpenFailure(clientHandle);
// Since synchronous opening of the handle failed, it is destroyed immediately.
// Note: The handle is not serialized because sychronously opening it failed.
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
WGPUBufferDescriptor descriptor = {};
descriptor.size = sizeof(mBufferContent);
WGPUCreateBufferMappedResult result = wgpuDeviceCreateBufferMapped(device, &descriptor);
// TODO(enga): Check that the client generated a context lost.
EXPECT_EQ(result.data, nullptr);
EXPECT_EQ(result.dataLength, 0u);
}
// Test CreateBufferMapped DeserializeFlush failure.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDeserializeFlushFailure) {
// The client should create and serialize a WriteHandle on createBufferMapped.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
// Staging data is immediately available so the handle is Opened.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
WGPUCreateBufferMappedResult result;
WGPUCreateBufferMappedResult apiResult;
std::tie(apiResult, result) = CreateBufferMapped();
FlushClient();
// Update the mapped contents.
mMappedBufferContent = mUpdatedBufferContent;
// When the client Unmaps the buffer, it will flush writes to the handle and destroy it.
ExpectClientWriteHandleSerializeFlush(clientHandle);
EXPECT_CALL(clientMemoryTransferService, OnWriteHandleDestroy(clientHandle)).Times(1);
wgpuBufferUnmap(result.buffer);
// The server deserializes the Flush message. Mock a deserialization failure.
MockServerWriteHandleDeserializeFlushFailure(serverHandle);
FlushClient(false);
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
}
// Test CreateBufferMapped destroying the buffer before unmapping on the client side.
TEST_F(WireMemoryTransferServiceTests, CreateBufferMappedDestroyBeforeUnmap) {
// The client should create and serialize a WriteHandle on createBufferMapped.
ClientWriteHandle* clientHandle = ExpectWriteHandleCreation();
// Staging data is immediately available so the handle is Opened.
ExpectClientWriteHandleOpen(clientHandle, &mMappedBufferContent);
ExpectWriteHandleSerialization(clientHandle);
// The server should then deserialize the WriteHandle from the client.
ServerWriteHandle* serverHandle = ExpectServerWriteHandleDeserialization();
WGPUCreateBufferMappedResult result;
WGPUCreateBufferMappedResult apiResult;
std::tie(apiResult, result) = 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(result.buffer);
EXPECT_CALL(serverMemoryTransferService, OnWriteHandleDestroy(serverHandle)).Times(1);
EXPECT_CALL(api, BufferDestroy(apiResult.buffer)).Times(1);
FlushClient();
// The handle is already destroyed so unmap only results in a server unmap call.
wgpuBufferUnmap(result.buffer);
EXPECT_CALL(api, BufferUnmap(apiResult.buffer)).Times(1);
FlushClient();
}
}