Split wire tests into multiple test files

Bug: dawn:104
Change-Id: I0dc46dabc92c98df13dbf596dea63aeaaea85d3d
Reviewed-on: https://dawn-review.googlesource.com/c/4562
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index d7581f8..933c915 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -939,7 +939,6 @@
     "src/tests/unittests/SerialMapTests.cpp",
     "src/tests/unittests/SerialQueueTests.cpp",
     "src/tests/unittests/ToBackendTests.cpp",
-    "src/tests/unittests/WireTests.cpp",
     "src/tests/unittests/validation/BindGroupValidationTests.cpp",
     "src/tests/unittests/validation/BufferValidationTests.cpp",
     "src/tests/unittests/validation/CommandBufferValidationTests.cpp",
@@ -958,6 +957,14 @@
     "src/tests/unittests/validation/ValidationTest.cpp",
     "src/tests/unittests/validation/ValidationTest.h",
     "src/tests/unittests/validation/VertexBufferValidationTests.cpp",
+    "src/tests/unittests/wire/WireArgumentTests.cpp",
+    "src/tests/unittests/wire/WireBasicTests.cpp",
+    "src/tests/unittests/wire/WireBufferMappingTests.cpp",
+    "src/tests/unittests/wire/WireCallbackTests.cpp",
+    "src/tests/unittests/wire/WireFenceTests.cpp",
+    "src/tests/unittests/wire/WireOptionalTests.cpp",
+    "src/tests/unittests/wire/WireTest.cpp",
+    "src/tests/unittests/wire/WireTest.h",
   ]
 
   if (dawn_enable_d3d12) {
diff --git a/src/tests/unittests/WireTests.cpp b/src/tests/unittests/WireTests.cpp
deleted file mode 100644
index 27af06f..0000000
--- a/src/tests/unittests/WireTests.cpp
+++ /dev/null
@@ -1,1659 +0,0 @@
-// Copyright 2017 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 "gtest/gtest.h"
-#include "mock/mock_dawn.h"
-
-#include "common/Assert.h"
-#include "common/Constants.h"
-#include "dawn_wire/WireClient.h"
-#include "dawn_wire/WireServer.h"
-#include "utils/TerribleCommandBuffer.h"
-
-#include <memory>
-
-using namespace testing;
-
-// Definition of a "Lambda predicate matcher" for GMock to allow checking deep structures
-// are passed correctly by the wire.
-
-// Helper templates to extract the argument type of a lambda.
-template<typename T>
-struct MatcherMethodArgument;
-
-template<typename Lambda, typename Arg>
-struct MatcherMethodArgument<bool (Lambda::*)(Arg) const> {
-    using Type = Arg;
-};
-
-template<typename Lambda>
-using MatcherLambdaArgument = typename MatcherMethodArgument<decltype(&Lambda::operator())>::Type;
-
-// The matcher itself, unfortunately it isn't able to return detailed information like other
-// matchers do.
-template <typename Lambda, typename Arg>
-class LambdaMatcherImpl : public MatcherInterface<Arg> {
-  public:
-    explicit LambdaMatcherImpl(Lambda lambda) : mLambda(lambda) {}
-
-    void DescribeTo(std::ostream* os) const override {
-        *os << "with a custom matcher";
-    }
-
-    bool MatchAndExplain(Arg value, MatchResultListener* listener) const override {
-        if (!mLambda(value)) {
-            *listener << "which doesn't satisfy the custom predicate";
-            return false;
-        }
-        return true;
-    }
-
-  private:
-    Lambda mLambda;
-};
-
-// Use the MatchesLambda as follows:
-//
-//   EXPECT_CALL(foo, Bar(MatchesLambda([](ArgType arg) -> bool {
-//       return CheckPredicateOnArg(arg);
-//   })));
-template <typename Lambda>
-inline Matcher<MatcherLambdaArgument<Lambda>> MatchesLambda(Lambda lambda) {
-    return MakeMatcher(new LambdaMatcherImpl<Lambda, MatcherLambdaArgument<Lambda>>(lambda));
-}
-
-// Mock classes to add expectations on the wire calling callbacks
-class MockDeviceErrorCallback {
-    public:
-        MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata));
-};
-
-static std::unique_ptr<MockDeviceErrorCallback> mockDeviceErrorCallback;
-static void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) {
-    mockDeviceErrorCallback->Call(message, userdata);
-}
-
-class MockBuilderErrorCallback {
-    public:
-        MOCK_METHOD4(Call, void(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2));
-};
-
-static std::unique_ptr<MockBuilderErrorCallback> mockBuilderErrorCallback;
-static void ToMockBuilderErrorCallback(dawnBuilderErrorStatus status, const char* message, dawnCallbackUserdata userdata1, dawnCallbackUserdata userdata2) {
-    mockBuilderErrorCallback->Call(status, message, userdata1, userdata2);
-}
-
-class MockBufferMapReadCallback {
-    public:
-        MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, const uint32_t* ptr, dawnCallbackUserdata userdata));
-};
-
-static std::unique_ptr<MockBufferMapReadCallback> mockBufferMapReadCallback;
-static void ToMockBufferMapReadCallback(dawnBufferMapAsyncStatus status, const void* ptr, dawnCallbackUserdata userdata) {
-    // Assume the data is uint32_t to make writing matchers easier
-    mockBufferMapReadCallback->Call(status, static_cast<const uint32_t*>(ptr), userdata);
-}
-
-class MockBufferMapWriteCallback {
-    public:
-        MOCK_METHOD3(Call, void(dawnBufferMapAsyncStatus status, uint32_t* ptr, dawnCallbackUserdata userdata));
-};
-
-static std::unique_ptr<MockBufferMapWriteCallback> mockBufferMapWriteCallback;
-uint32_t* lastMapWritePointer = nullptr;
-static void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status, void* ptr, dawnCallbackUserdata userdata) {
-    // Assume the data is uint32_t to make writing matchers easier
-    lastMapWritePointer = static_cast<uint32_t*>(ptr);
-    mockBufferMapWriteCallback->Call(status, lastMapWritePointer, userdata);
-}
-
-class MockFenceOnCompletionCallback {
-  public:
-    MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata));
-};
-
-static std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
-static void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status,
-                                            dawnCallbackUserdata userdata) {
-    mockFenceOnCompletionCallback->Call(status, userdata);
-}
-
-class WireTestsBase : public Test {
-    protected:
-        WireTestsBase(bool ignoreSetCallbackCalls)
-            : mIgnoreSetCallbackCalls(ignoreSetCallbackCalls) {
-        }
-
-        void SetUp() override {
-            mockDeviceErrorCallback = std::make_unique<MockDeviceErrorCallback>();
-            mockBuilderErrorCallback = std::make_unique<MockBuilderErrorCallback>();
-            mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
-            mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
-            mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
-
-            dawnProcTable mockProcs;
-            dawnDevice mockDevice;
-            api.GetProcTableAndDevice(&mockProcs, &mockDevice);
-
-            // This SetCallback call cannot be ignored because it is done as soon as we start the server
-            EXPECT_CALL(api, OnDeviceSetErrorCallback(_, _, _)).Times(Exactly(1));
-            if (mIgnoreSetCallbackCalls) {
-                EXPECT_CALL(api, OnBuilderSetErrorCallback(_, _, _, _)).Times(AnyNumber());
-            }
-            EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber());
-
-            mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
-            mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>(mWireServer.get());
-
-            mWireServer.reset(new dawn_wire::WireServer(mockDevice, mockProcs, mS2cBuf.get()));
-            mC2sBuf->SetHandler(mWireServer.get());
-
-            mWireClient.reset(new dawn_wire::WireClient(mC2sBuf.get()));
-            dawnProcTable clientProcs = mWireClient->GetProcs();
-            device = mWireClient->GetDevice();
-            dawnSetProcs(&clientProcs);
-            mS2cBuf->SetHandler(mWireClient.get());
-
-            apiDevice = mockDevice;
-        }
-
-        void TearDown() override {
-            dawnSetProcs(nullptr);
-
-            // Reset client before mocks are deleted.
-            // Incomplete callbacks will be called on deletion, so the mocks cannot be null.
-            mWireClient = nullptr;
-
-            // Delete mocks so that expectations are checked
-            mockDeviceErrorCallback = nullptr;
-            mockBuilderErrorCallback = nullptr;
-            mockBufferMapReadCallback = nullptr;
-            mockBufferMapWriteCallback = nullptr;
-            mockFenceOnCompletionCallback = nullptr;
-        }
-
-        void FlushClient() {
-            ASSERT_TRUE(mC2sBuf->Flush());
-        }
-
-        void FlushServer() {
-            ASSERT_TRUE(mS2cBuf->Flush());
-        }
-
-        MockProcTable api;
-        dawnDevice apiDevice;
-        dawnDevice device;
-
-    private:
-        bool mIgnoreSetCallbackCalls = false;
-
-        std::unique_ptr<dawn_wire::WireServer> mWireServer;
-        std::unique_ptr<dawn_wire::WireClient> mWireClient;
-        std::unique_ptr<utils::TerribleCommandBuffer> mS2cBuf;
-        std::unique_ptr<utils::TerribleCommandBuffer> mC2sBuf;
-};
-
-class WireTests : public WireTestsBase {
-    public:
-        WireTests() : WireTestsBase(true) {
-        }
-};
-
-// One call gets forwarded correctly.
-TEST_F(WireTests, CallForwarded) {
-    dawnDeviceCreateCommandBufferBuilder(device);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-    FlushClient();
-}
-
-// Test that calling methods on a new object works as expected.
-TEST_F(WireTests, CreateThenCall) {
-    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
-    dawnCommandBufferBuilderGetResult(builder);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-
-    dawnCommandBuffer apiCmdBuf = api.GetNewCommandBuffer();
-    EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder))
-        .WillOnce(Return(apiCmdBuf));
-
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-    EXPECT_CALL(api, CommandBufferRelease(apiCmdBuf));
-    FlushClient();
-}
-
-// Test that client reference/release do not call the backend API.
-TEST_F(WireTests, RefCountKeptInClient) {
-    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
-
-    dawnCommandBufferBuilderReference(builder);
-    dawnCommandBufferBuilderRelease(builder);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-
-    FlushClient();
-}
-
-// Test that client reference/release do not call the backend API.
-TEST_F(WireTests, ReleaseCalledOnRefCount0) {
-    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
-
-    dawnCommandBufferBuilderRelease(builder);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-
-    FlushClient();
-}
-
-// Test that the wire is able to send numerical values
-TEST_F(WireTests, ValueArgument) {
-    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
-    dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder);
-    dawnComputePassEncoderDispatch(pass, 1, 2, 3);
-
-    dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiBuilder));
-
-    dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder();
-    EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder))
-        .WillOnce(Return(apiPass));
-
-    EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3))
-        .Times(1);
-
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder));
-    EXPECT_CALL(api, ComputePassEncoderRelease(apiPass));
-    FlushClient();
-}
-
-// Test that the wire is able to send arrays of numerical values
-static constexpr uint32_t testPushConstantValues[4] = {
-    0,
-    42,
-    0xDEADBEEFu,
-    0xFFFFFFFFu
-};
-
-bool CheckPushConstantValues(const uint32_t* values) {
-    for (int i = 0; i < 4; ++i) {
-        if (values[i] != testPushConstantValues[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-TEST_F(WireTests, ValueArrayArgument) {
-    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
-    dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder);
-    dawnComputePassEncoderSetPushConstants(pass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, testPushConstantValues);
-
-    dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiBuilder));
-
-    dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder();
-    EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder))
-        .WillOnce(Return(apiPass));
-
-    EXPECT_CALL(api, ComputePassEncoderSetPushConstants(apiPass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4, ResultOf(CheckPushConstantValues, Eq(true))));
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder));
-    EXPECT_CALL(api, ComputePassEncoderRelease(apiPass));
-
-    FlushClient();
-}
-
-// Test that the wire is able to send C strings
-TEST_F(WireTests, CStringArgument) {
-    // Create shader module
-    dawnShaderModuleDescriptor vertexDescriptor;
-    vertexDescriptor.nextInChain = nullptr;
-    vertexDescriptor.codeSize = 0;
-    dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor);
-    dawnShaderModule apiVsModule = api.GetNewShaderModule();
-    EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _))
-        .WillOnce(Return(apiVsModule));
-
-    // Create the blend state descriptor
-    dawnBlendDescriptor blendDescriptor;
-    blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
-    blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
-    blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
-    dawnBlendStateDescriptor blendStateDescriptor;
-    blendStateDescriptor.nextInChain = nullptr;
-    blendStateDescriptor.alphaBlend = blendDescriptor;
-    blendStateDescriptor.colorBlend = blendDescriptor;
-    blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
-
-    // Create the input state
-    dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
-    dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder();
-    EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice))
-        .WillOnce(Return(apiInputStateBuilder));
-
-    dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder);
-    dawnInputState apiInputState = api.GetNewInputState();
-    EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder))
-        .WillOnce(Return(apiInputState));
-
-    // Create the depth-stencil state
-    dawnStencilStateFaceDescriptor stencilFace;
-    stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS;
-    stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP;
-    stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP;
-    stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP;
-
-    dawnDepthStencilStateDescriptor depthStencilState;
-    depthStencilState.nextInChain = nullptr;
-    depthStencilState.depthWriteEnabled = false;
-    depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS;
-    depthStencilState.stencilBack = stencilFace;
-    depthStencilState.stencilFront = stencilFace;
-    depthStencilState.stencilReadMask = 0xff;
-    depthStencilState.stencilWriteMask = 0xff;
-
-    // Create the pipeline layout
-    dawnPipelineLayoutDescriptor layoutDescriptor;
-    layoutDescriptor.nextInChain = nullptr;
-    layoutDescriptor.numBindGroupLayouts = 0;
-    layoutDescriptor.bindGroupLayouts = nullptr;
-    dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor);
-    dawnPipelineLayout apiLayout = api.GetNewPipelineLayout();
-    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _))
-        .WillOnce(Return(apiLayout));
-
-    // Create pipeline
-    dawnRenderPipelineDescriptor pipelineDescriptor;
-    pipelineDescriptor.nextInChain = nullptr;
-
-    dawnPipelineStageDescriptor vertexStage;
-    vertexStage.nextInChain = nullptr;
-    vertexStage.module = vsModule;
-    vertexStage.entryPoint = "main";
-    pipelineDescriptor.vertexStage = &vertexStage;
-
-    dawnPipelineStageDescriptor fragmentStage;
-    fragmentStage.nextInChain = nullptr;
-    fragmentStage.module = vsModule;
-    fragmentStage.entryPoint = "main";
-    pipelineDescriptor.fragmentStage = &fragmentStage;
-
-    dawnAttachmentsStateDescriptor attachmentsState;
-    attachmentsState.nextInChain = nullptr;
-    attachmentsState.numColorAttachments = 1;
-    dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM};
-    dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment};
-    attachmentsState.colorAttachments = colorAttachmentPtr;
-    attachmentsState.hasDepthStencilAttachment = false;
-    // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid
-    // data because we don't have optional substructures yet.
-    attachmentsState.depthStencilAttachment = &colorAttachment;
-    pipelineDescriptor.attachmentsState = &attachmentsState;
-
-    pipelineDescriptor.numBlendStates = 1;
-    pipelineDescriptor.blendStates = &blendStateDescriptor;
-
-    pipelineDescriptor.sampleCount = 1;
-    pipelineDescriptor.layout = layout;
-    pipelineDescriptor.inputState = inputState;
-    pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32;
-    pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
-    pipelineDescriptor.depthStencilState = &depthStencilState;
-
-    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
-	EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
-        return desc->vertexStage->entryPoint == std::string("main");
-    })))
-        .WillOnce(Return(nullptr));
-        EXPECT_CALL(api, ShaderModuleRelease(apiVsModule));
-        EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder));
-        EXPECT_CALL(api, InputStateRelease(apiInputState));
-        EXPECT_CALL(api, PipelineLayoutRelease(apiLayout));
-
-        FlushClient();
-}
-
-// Test that the wire is able to send optional pointers to structures
-TEST_F(WireTests, OptionalStructPointer) {
-    // Create shader module
-    dawnShaderModuleDescriptor vertexDescriptor;
-    vertexDescriptor.nextInChain = nullptr;
-    vertexDescriptor.codeSize = 0;
-    dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor);
-    dawnShaderModule apiVsModule = api.GetNewShaderModule();
-    EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _))
-        .WillOnce(Return(apiVsModule));
-
-    // Create the blend state descriptor
-    dawnBlendDescriptor blendDescriptor;
-    blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
-    blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
-    blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
-    dawnBlendStateDescriptor blendStateDescriptor;
-    blendStateDescriptor.nextInChain = nullptr;
-    blendStateDescriptor.alphaBlend = blendDescriptor;
-    blendStateDescriptor.colorBlend = blendDescriptor;
-    blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
-
-    // Create the input state
-    dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
-    dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder();
-    EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice))
-        .WillOnce(Return(apiInputStateBuilder));
-
-    dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder);
-    dawnInputState apiInputState = api.GetNewInputState();
-    EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder))
-        .WillOnce(Return(apiInputState));
-
-    // Create the depth-stencil state
-    dawnStencilStateFaceDescriptor stencilFace;
-    stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS;
-    stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP;
-    stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP;
-    stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP;
-
-    dawnDepthStencilStateDescriptor depthStencilState;
-    depthStencilState.nextInChain = nullptr;
-    depthStencilState.depthWriteEnabled = false;
-    depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS;
-    depthStencilState.stencilBack = stencilFace;
-    depthStencilState.stencilFront = stencilFace;
-    depthStencilState.stencilReadMask = 0xff;
-    depthStencilState.stencilWriteMask = 0xff;
-
-    // Create the pipeline layout
-    dawnPipelineLayoutDescriptor layoutDescriptor;
-    layoutDescriptor.nextInChain = nullptr;
-    layoutDescriptor.numBindGroupLayouts = 0;
-    layoutDescriptor.bindGroupLayouts = nullptr;
-    dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor);
-    dawnPipelineLayout apiLayout = api.GetNewPipelineLayout();
-    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _))
-        .WillOnce(Return(apiLayout));
-
-    // Create pipeline
-    dawnRenderPipelineDescriptor pipelineDescriptor;
-    pipelineDescriptor.nextInChain = nullptr;
-
-    dawnPipelineStageDescriptor vertexStage;
-    vertexStage.nextInChain = nullptr;
-    vertexStage.module = vsModule;
-    vertexStage.entryPoint = "main";
-    pipelineDescriptor.vertexStage = &vertexStage;
-
-    dawnPipelineStageDescriptor fragmentStage;
-    fragmentStage.nextInChain = nullptr;
-    fragmentStage.module = vsModule;
-    fragmentStage.entryPoint = "main";
-    pipelineDescriptor.fragmentStage = &fragmentStage;
-
-    dawnAttachmentsStateDescriptor attachmentsState;
-    attachmentsState.nextInChain = nullptr;
-    attachmentsState.numColorAttachments = 1;
-    dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM};
-    dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment};
-    attachmentsState.colorAttachments = colorAttachmentPtr;
-    attachmentsState.hasDepthStencilAttachment = false;
-    // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid
-    // data because we don't have optional substructures yet.
-    attachmentsState.depthStencilAttachment = &colorAttachment;
-    pipelineDescriptor.attachmentsState = &attachmentsState;
-
-    pipelineDescriptor.numBlendStates = 1;
-    pipelineDescriptor.blendStates = &blendStateDescriptor;
-
-    pipelineDescriptor.sampleCount = 1;
-    pipelineDescriptor.layout = layout;
-    pipelineDescriptor.inputState = inputState;
-    pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32;
-    pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
-
-    // First case: depthStencilState is not null.
-    pipelineDescriptor.depthStencilState = &depthStencilState;
-    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
-	EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
-        return desc->depthStencilState != nullptr &&
-            desc->depthStencilState->nextInChain == nullptr &&
-            desc->depthStencilState->depthWriteEnabled == false &&
-            desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS &&
-            desc->depthStencilState->stencilBack.compare == DAWN_COMPARE_FUNCTION_ALWAYS &&
-            desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilBack.depthFailOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilFront.compare == DAWN_COMPARE_FUNCTION_ALWAYS &&
-            desc->depthStencilState->stencilFront.failOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilFront.depthFailOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilFront.passOp == DAWN_STENCIL_OPERATION_KEEP &&
-            desc->depthStencilState->stencilReadMask == 0xff &&
-            desc->depthStencilState->stencilWriteMask == 0xff;
-    })))
-        .WillOnce(Return(nullptr));
-
-    FlushClient();
-
-    // Second case: depthStencilState is null.
-    pipelineDescriptor.depthStencilState = nullptr;
-    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
-	EXPECT_CALL(api, DeviceCreateRenderPipeline(apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
-        return desc->depthStencilState == nullptr;
-    })))
-        .WillOnce(Return(nullptr));
-
-    EXPECT_CALL(api, ShaderModuleRelease(apiVsModule));
-    EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder));
-    EXPECT_CALL(api, InputStateRelease(apiInputState));
-    EXPECT_CALL(api, PipelineLayoutRelease(apiLayout));
-
-    FlushClient();
-}
-
-// Test that the wire is able to send objects as value arguments
-TEST_F(WireTests, ObjectAsValueArgument) {
-    // Create a RenderPassDescriptor
-    dawnRenderPassDescriptorBuilder renderPassBuilder = dawnDeviceCreateRenderPassDescriptorBuilder(device);
-    dawnRenderPassDescriptor renderPass = dawnRenderPassDescriptorBuilderGetResult(renderPassBuilder);
-
-    dawnRenderPassDescriptorBuilder apiRenderPassBuilder = api.GetNewRenderPassDescriptorBuilder();
-    EXPECT_CALL(api, DeviceCreateRenderPassDescriptorBuilder(apiDevice))
-        .WillOnce(Return(apiRenderPassBuilder));
-    dawnRenderPassDescriptor apiRenderPass = api.GetNewRenderPassDescriptor();
-    EXPECT_CALL(api, RenderPassDescriptorBuilderGetResult(apiRenderPassBuilder))
-        .WillOnce(Return(apiRenderPass));
-
-    // Create command buffer builder, setting render pass descriptor
-    dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
-    dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, renderPass);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-
-    EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, apiRenderPass))
-        .Times(1);
-
-    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-    EXPECT_CALL(api, RenderPassDescriptorBuilderRelease(apiRenderPassBuilder));
-    EXPECT_CALL(api, RenderPassDescriptorRelease(apiRenderPass));
-    FlushClient();
-}
-
-// Test that the wire is able to send array of objects
-TEST_F(WireTests, ObjectsAsPointerArgument) {
-    dawnCommandBuffer cmdBufs[2];
-    dawnCommandBuffer apiCmdBufs[2];
-
-    // Create two command buffers we need to use a GMock sequence otherwise the order of the
-    // CreateCommandBufferBuilder might be swapped since they are equivalent in term of matchers
-    Sequence s;
-    for (int i = 0; i < 2; ++i) {
-        dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
-        cmdBufs[i] = dawnCommandBufferBuilderGetResult(cmdBufBuilder);
-
-        dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-        EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-            .InSequence(s)
-            .WillOnce(Return(apiCmdBufBuilder));
-
-        apiCmdBufs[i] = api.GetNewCommandBuffer();
-        EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder))
-            .WillOnce(Return(apiCmdBufs[i]));
-        EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
-        EXPECT_CALL(api, CommandBufferRelease(apiCmdBufs[i]));
-    }
-
-    // Create queue
-    dawnQueue queue = dawnDeviceCreateQueue(device);
-    dawnQueue apiQueue = api.GetNewQueue();
-    EXPECT_CALL(api, DeviceCreateQueue(apiDevice))
-        .WillOnce(Return(apiQueue));
-
-    // Submit command buffer and check we got a call with both API-side command buffers
-    dawnQueueSubmit(queue, 2, cmdBufs);
-
-    EXPECT_CALL(api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const dawnCommandBuffer* cmdBufs) -> bool {
-        return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1];
-    })));
-
-    EXPECT_CALL(api, QueueRelease(apiQueue));
-    FlushClient();
-}
-
-// Test that the wire is able to send structures that contain pure values (non-objects)
-TEST_F(WireTests, StructureOfValuesArgument) {
-    dawnSamplerDescriptor descriptor;
-    descriptor.nextInChain = nullptr;
-    descriptor.magFilter = DAWN_FILTER_MODE_LINEAR;
-    descriptor.minFilter = DAWN_FILTER_MODE_NEAREST;
-    descriptor.mipmapFilter = DAWN_FILTER_MODE_LINEAR;
-    descriptor.addressModeU = DAWN_ADDRESS_MODE_CLAMP_TO_EDGE;
-    descriptor.addressModeV = DAWN_ADDRESS_MODE_REPEAT;
-    descriptor.addressModeW = DAWN_ADDRESS_MODE_MIRRORED_REPEAT;
-    descriptor.lodMinClamp = kLodMin;
-    descriptor.lodMaxClamp = kLodMax;
-    descriptor.compareFunction = DAWN_COMPARE_FUNCTION_NEVER;
-    descriptor.borderColor = DAWN_BORDER_COLOR_TRANSPARENT_BLACK;
-
-    dawnDeviceCreateSampler(device, &descriptor);
-    EXPECT_CALL(api, DeviceCreateSampler(apiDevice, MatchesLambda([](const dawnSamplerDescriptor* desc) -> bool {
-        return desc->nextInChain == nullptr &&
-            desc->magFilter == DAWN_FILTER_MODE_LINEAR &&
-            desc->minFilter == DAWN_FILTER_MODE_NEAREST &&
-            desc->mipmapFilter == DAWN_FILTER_MODE_LINEAR &&
-            desc->addressModeU == DAWN_ADDRESS_MODE_CLAMP_TO_EDGE &&
-            desc->addressModeV == DAWN_ADDRESS_MODE_REPEAT &&
-            desc->addressModeW == DAWN_ADDRESS_MODE_MIRRORED_REPEAT &&
-            desc->compareFunction == DAWN_COMPARE_FUNCTION_NEVER &&
-            desc->borderColor == DAWN_BORDER_COLOR_TRANSPARENT_BLACK &&
-            desc->lodMinClamp == kLodMin &&
-            desc->lodMaxClamp == kLodMax;
-    })))
-        .WillOnce(Return(nullptr));
-
-    FlushClient();
-}
-
-// Test that the wire is able to send structures that contain objects
-TEST_F(WireTests, StructureOfObjectArrayArgument) {
-    dawnBindGroupLayoutDescriptor bglDescriptor;
-    bglDescriptor.numBindings = 0;
-    bglDescriptor.bindings = nullptr;
-
-    dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor);
-    dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
-    EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
-
-    dawnPipelineLayoutDescriptor descriptor;
-    descriptor.nextInChain = nullptr;
-    descriptor.numBindGroupLayouts = 1;
-    descriptor.bindGroupLayouts = &bgl;
-
-    dawnDeviceCreatePipelineLayout(device, &descriptor);
-    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, MatchesLambda([apiBgl](const dawnPipelineLayoutDescriptor* desc) -> bool {
-        return desc->nextInChain == nullptr &&
-            desc->numBindGroupLayouts == 1 &&
-            desc->bindGroupLayouts[0] == apiBgl;
-    })))
-        .WillOnce(Return(nullptr));
-
-    EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl));
-    FlushClient();
-}
-
-// Test that the wire is able to send structures that contain objects
-TEST_F(WireTests, StructureOfStructureArrayArgument) {
-    static constexpr int NUM_BINDINGS = 3;
-    dawnBindGroupLayoutBinding bindings[NUM_BINDINGS]{
-        {0, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLER},
-        {1, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLED_TEXTURE},
-        {2,
-         static_cast<dawnShaderStageBit>(DAWN_SHADER_STAGE_BIT_VERTEX |
-                                        DAWN_SHADER_STAGE_BIT_FRAGMENT),
-         DAWN_BINDING_TYPE_UNIFORM_BUFFER},
-    };
-    dawnBindGroupLayoutDescriptor bglDescriptor;
-    bglDescriptor.numBindings = NUM_BINDINGS;
-    bglDescriptor.bindings = bindings;
-
-    dawnDeviceCreateBindGroupLayout(device, &bglDescriptor);
-    dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
-    EXPECT_CALL(
-        api,
-        DeviceCreateBindGroupLayout(
-            apiDevice, MatchesLambda([bindings](const dawnBindGroupLayoutDescriptor* desc) -> bool {
-                for (int i = 0; i < NUM_BINDINGS; ++i) {
-                    const auto& a = desc->bindings[i];
-                    const auto& b = bindings[i];
-                    if (a.binding != b.binding || a.visibility != b.visibility ||
-                        a.type != b.type) {
-                        return false;
-                    }
-                }
-                return desc->nextInChain == nullptr && desc->numBindings == 3;
-            })))
-        .WillOnce(Return(apiBgl));
-
-    EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl));
-    FlushClient();
-}
-
-// Test passing nullptr instead of objects - object as value version
-TEST_F(WireTests, OptionalObjectValue) {
-    dawnBindGroupLayoutDescriptor bglDesc;
-    bglDesc.nextInChain = nullptr;
-    bglDesc.numBindings = 0;
-    dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDesc);
-
-    dawnBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout();
-    EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _))
-        .WillOnce(Return(apiBindGroupLayout));
-
-    // The `sampler`, `textureView` and `buffer` members of a binding are optional.
-    dawnBindGroupBinding binding;
-    binding.binding = 0;
-    binding.sampler = nullptr;
-    binding.textureView = nullptr;
-    binding.buffer = nullptr;
-
-    dawnBindGroupDescriptor bgDesc;
-    bgDesc.nextInChain = nullptr;
-    bgDesc.layout = bgl;
-    bgDesc.numBindings = 1;
-    bgDesc.bindings = &binding;
-
-    dawnDeviceCreateBindGroup(device, &bgDesc);
-    EXPECT_CALL(api, DeviceCreateBindGroup(apiDevice, MatchesLambda([](const dawnBindGroupDescriptor* desc) -> bool {
-        return desc->nextInChain == nullptr &&
-            desc->numBindings == 1 &&
-            desc->bindings[0].binding == 0 &&
-            desc->bindings[0].sampler == nullptr &&
-            desc->bindings[0].buffer == nullptr &&
-            desc->bindings[0].textureView == nullptr;
-    })))
-        .WillOnce(Return(nullptr));
-
-    EXPECT_CALL(api, BindGroupLayoutRelease(apiBindGroupLayout));
-    FlushClient();
-}
-
-// Test passing nullptr instead of objects - array of objects version
-TEST_F(WireTests, DISABLED_NullptrInArray) {
-    dawnBindGroupLayout nullBGL = nullptr;
-
-    dawnPipelineLayoutDescriptor descriptor;
-    descriptor.nextInChain = nullptr;
-    descriptor.numBindGroupLayouts = 1;
-    descriptor.bindGroupLayouts = &nullBGL;
-
-    dawnDeviceCreatePipelineLayout(device, &descriptor);
-    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, MatchesLambda([](const dawnPipelineLayoutDescriptor* desc) -> bool {
-        return desc->nextInChain == nullptr &&
-            desc->numBindGroupLayouts == 1 &&
-            desc->bindGroupLayouts[0] == nullptr;
-    })))
-        .WillOnce(Return(nullptr));
-
-    FlushClient();
-}
-
-// Test that the server doesn't forward calls to error objects or with error objects
-// Also test that when GetResult is called on an error builder, the error callback is fired
-// TODO(cwallez@chromium.org): This test is disabled because the introduction of encoders breaks
-// the assumptions of the "builder error" handling that a builder is self-contained. We need to
-// revisit this once the new error handling is in place.
-TEST_F(WireTests, DISABLED_CallsSkippedAfterBuilderError) {
-    dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
-    dawnCommandBufferBuilderSetErrorCallback(cmdBufBuilder, ToMockBuilderErrorCallback, 1, 2);
-
-    dawnRenderPassEncoder pass = dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, nullptr);
-
-    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4);
-    dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder); // Hey look an error!
-
-    // These calls will be skipped because of the error
-    dawnBufferSetSubData(buffer, 0, 0, nullptr);
-    dawnRenderPassEncoderSetIndexBuffer(pass, buffer, 0);
-    dawnRenderPassEncoderEndPass(pass);
-    dawnCommandBufferBuilderGetResult(cmdBufBuilder);
-
-    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
-        .WillOnce(Return(apiCmdBufBuilder));
-
-    dawnRenderPassEncoder apiPass = api.GetNewRenderPassEncoder();
-    EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, _))
-        .WillOnce(Return(apiPass));
-
-    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
-        .WillOnce(Return(apiBufferBuilder));
-
-    // Hey look an error!
-    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
-        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
-            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, "Error");
-            return nullptr;
-        }));
-
-    EXPECT_CALL(api, BufferSetSubData(_, _, _, _)).Times(0);
-    EXPECT_CALL(api, RenderPassEncoderSetIndexBuffer(_, _, _)).Times(0);
-    EXPECT_CALL(api, CommandBufferBuilderGetResult(_)).Times(0);
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 1, 2)).Times(1);
-    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 3, 4)).Times(1);
-
-    FlushServer();
-}
-
-// Test that we get a success builder error status when no error happens
-TEST_F(WireTests, SuccessCallbackOnBuilderSuccess) {
-    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2);
-    dawnBufferBuilderGetResult(bufferBuilder);
-
-    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
-        .WillOnce(Return(apiBufferBuilder));
-
-    dawnBuffer apiBuffer = api.GetNewBuffer();
-    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
-        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
-            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "I like cheese");
-            return apiBuffer;
-        }));
-
-    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    FlushClient();
-
-    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, _ , 1 ,2));
-
-    FlushServer();
-}
-
-// Test that the client calls the builder callback with unknown when it HAS to fire the callback but can't
-// know the status yet.
-TEST_F(WireTests, UnknownBuilderErrorStatusCallback) {
-    // The builder is destroyed before the object is built
-    {
-        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2);
-
-        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 1 ,2)).Times(1);
-
-        dawnBufferBuilderRelease(bufferBuilder);
-    }
-
-    // If the builder has been consumed, it doesn't fire the callback with unknown
-    {
-        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4);
-        dawnBufferBuilderGetResult(bufferBuilder);
-
-        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 3, 4)).Times(0);
-
-        dawnBufferBuilderRelease(bufferBuilder);
-    }
-
-    // If the builder has been consumed, and the object is destroyed before the result comes from the server,
-    // then the callback is fired with unknown
-    {
-        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 5, 6);
-        dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder);
-
-        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _ , 5, 6)).Times(1);
-
-        dawnBufferRelease(buffer);
-    }
-}
-
-// Test that a builder success status doesn't get forwarded to the device
-TEST_F(WireTests, SuccessCallbackNotForwardedToDevice) {
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, 0);
-
-    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-    dawnBufferBuilderGetResult(bufferBuilder);
-
-    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
-        .WillOnce(Return(apiBufferBuilder));
-
-    dawnBuffer apiBuffer = api.GetNewBuffer();
-    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
-        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
-            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "I like cheese");
-            return apiBuffer;
-        }));
-
-    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    FlushClient();
-    FlushServer();
-}
-
-// Test that a builder error status gets forwarded to the device
-TEST_F(WireTests, ErrorCallbackForwardedToDevice) {
-    uint64_t userdata = 30495;
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
-
-    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-    dawnBufferBuilderGetResult(bufferBuilder);
-
-    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
-        .WillOnce(Return(apiBufferBuilder));
-
-    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
-        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
-            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR, "Error :(");
-            return nullptr;
-        }));
-
-    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
-    FlushClient();
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
-
-    FlushServer();
-}
-
-class WireSetCallbackTests : public WireTestsBase {
-    public:
-        WireSetCallbackTests() : WireTestsBase(false) {
-        }
-};
-
-// Test the return wire for device error callbacks
-TEST_F(WireSetCallbackTests, DeviceErrorCallback) {
-    uint64_t userdata = 3049785;
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
-
-    // Setting the error callback should stay on the client side and do nothing
-    FlushClient();
-
-    // Calling the callback on the server side will result in the callback being called on the client side
-    api.CallDeviceErrorCallback(apiDevice, "Some error message");
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), userdata))
-        .Times(1);
-
-    FlushServer();
-}
-
-// Test the return wire for device error callbacks
-TEST_F(WireSetCallbackTests, BuilderErrorCallback) {
-    uint64_t userdata1 = 982734;
-    uint64_t userdata2 = 982734239028;
-
-    // Create the buffer builder, the callback is set immediately on the server side
-    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
-
-    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
-    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
-        .WillOnce(Return(apiBufferBuilder));
-
-    EXPECT_CALL(api, OnBuilderSetErrorCallback(apiBufferBuilder, _, _, _))
-        .Times(1);
-
-    FlushClient();
-
-    // Setting the callback on the client side doesn't do anything on the server side
-    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, userdata1, userdata2);
-    FlushClient();
-
-    // Create an object so that it is a valid case to call the error callback
-    dawnBufferBuilderGetResult(bufferBuilder);
-
-    dawnBuffer apiBuffer = api.GetNewBuffer();
-    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
-        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
-            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS, "Success!");
-            return apiBuffer;
-        }));
-
-    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
-    EXPECT_CALL(api, BufferRelease(apiBuffer));
-    FlushClient();
-
-    // The error callback gets called on the client side
-    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, StrEq("Success!"), userdata1, userdata2))
-        .Times(1);
-
-    FlushServer();
-}
-
-class WireBufferMappingTests : public WireTestsBase {
-    public:
-        WireBufferMappingTests() : WireTestsBase(true) {
-        }
-
-        void SetUp() override {
-            WireTestsBase::SetUp();
-
-            {
-                dawnBufferDescriptor descriptor;
-                descriptor.nextInChain = nullptr;
-
-                apiBuffer = api.GetNewBuffer();
-                buffer = dawnDeviceCreateBuffer(device, &descriptor);
-
-                EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
-                    .WillOnce(Return(apiBuffer))
-                    .RetiresOnSaturation();
-                EXPECT_CALL(api, BufferRelease(apiBuffer));
-                FlushClient();
-            }
-            {
-                dawnBufferDescriptor descriptor;
-                descriptor.nextInChain = nullptr;
-
-                errorBuffer = dawnDeviceCreateBuffer(device, &descriptor);
-
-                EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
-                    .WillOnce(Return(nullptr))
-                    .RetiresOnSaturation();
-                FlushClient();
-            }
-        }
-
-    protected:
-        // A successfully created buffer
-        dawnBuffer buffer;
-        dawnBuffer apiBuffer;
-
-        // An buffer that wasn't created on the server side
-        dawnBuffer errorBuffer;
-};
-
-// MapRead-specific tests
-
-// Check mapping for reading a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
-    dawnCallbackUserdata userdata = 8653;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
-        .Times(1);
-
-    FlushServer();
-
-    dawnBufferUnmap(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(WireBufferMappingTests, ErrorWhileMappingForRead) {
-    dawnCallbackUserdata userdata = 8654;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-}
-
-// Check mapping for reading a buffer that didn't get created on the server side
-TEST_F(WireBufferMappingTests, MappingForReadErrorBuffer) {
-    dawnCallbackUserdata userdata = 8655;
-    dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-
-    dawnBufferUnmap(errorBuffer);
-
-    FlushClient();
-}
-
-// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
-    dawnCallbackUserdata userdata = 8656;
-    dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
-        .Times(1);
-
-    dawnBufferRelease(errorBuffer);
-}
-
-// Check the map read callback is called with UNKNOWN when the map request would have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
-    dawnCallbackUserdata userdata = 8657;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    // Oh no! We are calling Unmap too early!
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
-        .Times(1);
-    dawnBufferUnmap(buffer);
-
-    // The callback shouldn't get called, even when the request succeeded on the server side
-    FlushServer();
-}
-
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
-    // Successful map
-    dawnCallbackUserdata userdata = 34098;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }))
-        .RetiresOnSaturation();
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
-        .Times(1);
-
-    FlushServer();
-
-    // Map failure while the buffer is already mapped
-    userdata ++;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-}
-
-// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
-    dawnCallbackUserdata userdata = 2039;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            dawnBufferUnmap(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(WireBufferMappingTests, DestroyInsideMapReadCallback) {
-    dawnCallbackUserdata userdata = 2039;
-    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapReadCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            dawnBufferRelease(buffer);
-        }));
-
-    FlushServer();
-
-    FlushClient();
-}
-
-// MapWrite-specific tests
-
-// Check mapping for writing a succesfully created buffer
-TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
-    dawnCallbackUserdata userdata = 8653;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    uint32_t serverBufferContent = 31337;
-    uint32_t updatedContent = 4242;
-    uint32_t zero = 0;
-
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &serverBufferContent);
-        }));
-
-    FlushClient();
-
-    // The map write callback always gets a buffer full of zeroes.
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
-        .Times(1);
-
-    FlushServer();
-
-    // Write something to the mapped pointer
-    *lastMapWritePointer = updatedContent;
-
-    dawnBufferUnmap(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(WireBufferMappingTests, ErrorWhileMappingForWrite) {
-    dawnCallbackUserdata userdata = 8654;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-}
-
-// Check mapping for writing a buffer that didn't get created on the server side
-TEST_F(WireBufferMappingTests, MappingForWriteErrorBuffer) {
-    dawnCallbackUserdata userdata = 8655;
-    dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-
-    dawnBufferUnmap(errorBuffer);
-
-    FlushClient();
-}
-
-// Check that the map write callback is called with UNKNOWN when the buffer is destroyed before the request is finished
-TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
-    dawnCallbackUserdata userdata = 8656;
-    dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
-        .Times(1);
-
-    dawnBufferRelease(errorBuffer);
-}
-
-// Check the map read callback is called with UNKNOWN when the map request would have worked, but Unmap was called
-TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
-    dawnCallbackUserdata userdata = 8657;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    // Oh no! We are calling Unmap too early!
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
-        .Times(1);
-    dawnBufferUnmap(buffer);
-
-    // The callback shouldn't get called, even when the request succeeded on the server side
-    FlushServer();
-}
-
-// Check that an error map read callback gets nullptr while a buffer is already mapped
-TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
-    // Successful map
-    dawnCallbackUserdata userdata = 34098;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    uint32_t zero = 0;
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }))
-        .RetiresOnSaturation();
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
-        .Times(1);
-
-    FlushServer();
-
-    // Map failure while the buffer is already mapped
-    userdata ++;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
-        .Times(1);
-
-    FlushServer();
-}
-
-// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
-TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
-    dawnCallbackUserdata userdata = 2039;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    uint32_t zero = 0;
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            dawnBufferUnmap(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(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
-    dawnCallbackUserdata userdata = 2039;
-    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
-
-    uint32_t bufferContent = 31337;
-    uint32_t zero = 0;
-    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, &bufferContent);
-        }));
-
-    FlushClient();
-
-    EXPECT_CALL(*mockBufferMapWriteCallback, Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
-        .WillOnce(InvokeWithoutArgs([&]() {
-            dawnBufferRelease(buffer);
-        }));
-
-    FlushServer();
-
-    FlushClient();
-}
-
-class WireFenceTests : public WireTestsBase {
-  public:
-    WireFenceTests() : WireTestsBase(true) {
-    }
-
-    void SetUp() override {
-        WireTestsBase::SetUp();
-
-        {
-            dawnFenceDescriptor descriptor;
-            descriptor.initialValue = 1;
-            descriptor.nextInChain = nullptr;
-
-            apiFence = api.GetNewFence();
-            fence = dawnDeviceCreateFence(device, &descriptor);
-
-            EXPECT_CALL(api, DeviceCreateFence(apiDevice, _)).WillOnce(Return(apiFence));
-            EXPECT_CALL(api, FenceRelease(apiFence));
-            FlushClient();
-        }
-        {
-            queue = dawnDeviceCreateQueue(device);
-            apiQueue = api.GetNewQueue();
-            EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
-            EXPECT_CALL(api, QueueRelease(apiQueue));
-            FlushClient();
-        }
-    }
-
-  protected:
-    void DoQueueSignal(uint64_t signalValue) {
-        dawnQueueSignal(queue, fence, signalValue);
-        EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1);
-
-        // This callback is generated to update the completedValue of the fence
-        // on the client
-        EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _))
-            .WillOnce(InvokeWithoutArgs([&]() {
-                api.CallFenceOnCompletionCallback(apiFence, DAWN_FENCE_COMPLETION_STATUS_SUCCESS);
-            }));
-    }
-
-    // A successfully created fence
-    dawnFence fence;
-    dawnFence apiFence;
-
-    dawnQueue queue;
-    dawnQueue apiQueue;
-};
-
-// Check that signaling a fence succeeds
-TEST_F(WireFenceTests, QueueSignalSuccess) {
-    DoQueueSignal(2u);
-    DoQueueSignal(3u);
-    FlushClient();
-    FlushServer();
-}
-
-// Without any flushes, it is valid to signal a value greater than the current
-// signaled value
-TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) {
-    dawnCallbackUserdata userdata = 9157;
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
-
-    dawnQueueSignal(queue, fence, 2u);
-    dawnQueueSignal(queue, fence, 4u);
-    dawnQueueSignal(queue, fence, 5u);
-}
-
-// Without any flushes, errors should be generated when signaling a value less
-// than or equal to the current signaled value
-TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) {
-    dawnCallbackUserdata userdata = 3157;
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
-    dawnQueueSignal(queue, fence, 0u);  // Error
-    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
-    dawnQueueSignal(queue, fence, 1u);  // Error
-    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
-    dawnQueueSignal(queue, fence, 4u);  // Success
-    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
-
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
-    dawnQueueSignal(queue, fence, 3u);  // Error
-    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
-}
-
-// Check that callbacks are immediately called if the fence is already finished
-TEST_F(WireFenceTests, OnCompletionImmediate) {
-    // Can call on value < (initial) signaled value happens immediately
-    {
-        dawnCallbackUserdata userdata = 9847;
-        EXPECT_CALL(*mockFenceOnCompletionCallback,
-                    Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
-            .Times(1);
-        dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, userdata);
-    }
-
-    // Can call on value == (initial) signaled value happens immediately
-    {
-        dawnCallbackUserdata userdata = 4347;
-        EXPECT_CALL(*mockFenceOnCompletionCallback,
-                    Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
-            .Times(1);
-        dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, userdata);
-    }
-}
-
-// Check that all passed client completion callbacks are called
-TEST_F(WireFenceTests, OnCompletionMultiple) {
-    DoQueueSignal(3u);
-    DoQueueSignal(6u);
-
-    dawnCallbackUserdata userdata0 = 2134;
-    dawnCallbackUserdata userdata1 = 7134;
-    dawnCallbackUserdata userdata2 = 3144;
-    dawnCallbackUserdata userdata3 = 1130;
-
-    // Add callbacks in a non-monotonic order. They should still be called
-    // in order of increasing fence value.
-    // Add multiple callbacks for the same value.
-    dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, userdata0);
-    dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata1);
-    dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, userdata2);
-    dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata3);
-
-    Sequence s1, s2;
-    EXPECT_CALL(*mockFenceOnCompletionCallback,
-                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1))
-        .Times(1)
-        .InSequence(s1);
-    EXPECT_CALL(*mockFenceOnCompletionCallback,
-                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3))
-        .Times(1)
-        .InSequence(s2);
-    EXPECT_CALL(*mockFenceOnCompletionCallback,
-                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2))
-        .Times(1)
-        .InSequence(s1, s2);
-    EXPECT_CALL(*mockFenceOnCompletionCallback,
-                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0))
-        .Times(1)
-        .InSequence(s1, s2);
-
-    FlushClient();
-    FlushServer();
-}
-
-// Without any flushes, it is valid to wait on a value less than or equal to
-// the last signaled value
-TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) {
-    dawnQueueSignal(queue, fence, 4u);
-    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0);
-    dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0);
-    dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0);
-}
-
-// Without any flushes, errors should be generated when waiting on a value greater
-// than the last signaled value
-TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) {
-    dawnCallbackUserdata userdata1 = 3817;
-    dawnCallbackUserdata userdata2 = 3857;
-    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata2);
-
-    EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata1))
-        .Times(1);
-    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata2)).Times(1);
-
-    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata1);
-}
-
-// Check that the fence completed value is initialized
-TEST_F(WireFenceTests, GetCompletedValueInitialization) {
-    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
-}
-
-// Check that the fence completed value updates after signaling the fence
-TEST_F(WireFenceTests, GetCompletedValueUpdate) {
-    DoQueueSignal(3u);
-    FlushClient();
-    FlushServer();
-
-    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u);
-}
-
-// Check that the fence completed value does not update without a flush
-TEST_F(WireFenceTests, GetCompletedValueNoUpdate) {
-    dawnQueueSignal(queue, fence, 3u);
-    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
-}
-
-// Check that the callback is called with UNKNOWN when the fence is destroyed
-// before the completed value is updated
-TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) {
-    dawnCallbackUserdata userdata = 8616;
-    dawnQueueSignal(queue, fence, 3u);
-    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata);
-    EXPECT_CALL(*mockFenceOnCompletionCallback,
-                Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata))
-        .Times(1);
-
-    dawnFenceRelease(fence);
-}
diff --git a/src/tests/unittests/wire/WireArgumentTests.cpp b/src/tests/unittests/wire/WireArgumentTests.cpp
new file mode 100644
index 0000000..613c0c1
--- /dev/null
+++ b/src/tests/unittests/wire/WireArgumentTests.cpp
@@ -0,0 +1,382 @@
+// 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 "common/Constants.h"
+
+using namespace testing;
+using namespace dawn_wire;
+
+class WireArgumentTests : public WireTest {
+  public:
+    WireArgumentTests() : WireTest(true) {
+    }
+    ~WireArgumentTests() override = default;
+};
+
+// Test that the wire is able to send numerical values
+TEST_F(WireArgumentTests, ValueArgument) {
+    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
+    dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder);
+    dawnComputePassEncoderDispatch(pass, 1, 2, 3);
+
+    dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)).WillOnce(Return(apiBuilder));
+
+    dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder();
+    EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)).WillOnce(Return(apiPass));
+
+    EXPECT_CALL(api, ComputePassEncoderDispatch(apiPass, 1, 2, 3)).Times(1);
+
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder));
+    EXPECT_CALL(api, ComputePassEncoderRelease(apiPass));
+    FlushClient();
+}
+
+// Test that the wire is able to send arrays of numerical values
+static constexpr uint32_t testPushConstantValues[4] = {0, 42, 0xDEADBEEFu, 0xFFFFFFFFu};
+
+bool CheckPushConstantValues(const uint32_t* values) {
+    for (int i = 0; i < 4; ++i) {
+        if (values[i] != testPushConstantValues[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+TEST_F(WireArgumentTests, ValueArrayArgument) {
+    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
+    dawnComputePassEncoder pass = dawnCommandBufferBuilderBeginComputePass(builder);
+    dawnComputePassEncoderSetPushConstants(pass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4,
+                                           testPushConstantValues);
+
+    dawnCommandBufferBuilder apiBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice)).WillOnce(Return(apiBuilder));
+
+    dawnComputePassEncoder apiPass = api.GetNewComputePassEncoder();
+    EXPECT_CALL(api, CommandBufferBuilderBeginComputePass(apiBuilder)).WillOnce(Return(apiPass));
+
+    EXPECT_CALL(api,
+                ComputePassEncoderSetPushConstants(apiPass, DAWN_SHADER_STAGE_BIT_VERTEX, 0, 4,
+                                                   ResultOf(CheckPushConstantValues, Eq(true))));
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiBuilder));
+    EXPECT_CALL(api, ComputePassEncoderRelease(apiPass));
+
+    FlushClient();
+}
+
+// Test that the wire is able to send C strings
+TEST_F(WireArgumentTests, CStringArgument) {
+    // Create shader module
+    dawnShaderModuleDescriptor vertexDescriptor;
+    vertexDescriptor.nextInChain = nullptr;
+    vertexDescriptor.codeSize = 0;
+    dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor);
+    dawnShaderModule apiVsModule = api.GetNewShaderModule();
+    EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
+
+    // Create the blend state descriptor
+    dawnBlendDescriptor blendDescriptor;
+    blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
+    blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
+    blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
+    dawnBlendStateDescriptor blendStateDescriptor;
+    blendStateDescriptor.nextInChain = nullptr;
+    blendStateDescriptor.alphaBlend = blendDescriptor;
+    blendStateDescriptor.colorBlend = blendDescriptor;
+    blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
+
+    // Create the input state
+    dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
+    dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder();
+    EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice))
+        .WillOnce(Return(apiInputStateBuilder));
+
+    dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder);
+    dawnInputState apiInputState = api.GetNewInputState();
+    EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder))
+        .WillOnce(Return(apiInputState));
+
+    // Create the depth-stencil state
+    dawnStencilStateFaceDescriptor stencilFace;
+    stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS;
+    stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP;
+    stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP;
+    stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP;
+
+    dawnDepthStencilStateDescriptor depthStencilState;
+    depthStencilState.nextInChain = nullptr;
+    depthStencilState.depthWriteEnabled = false;
+    depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS;
+    depthStencilState.stencilBack = stencilFace;
+    depthStencilState.stencilFront = stencilFace;
+    depthStencilState.stencilReadMask = 0xff;
+    depthStencilState.stencilWriteMask = 0xff;
+
+    // Create the pipeline layout
+    dawnPipelineLayoutDescriptor layoutDescriptor;
+    layoutDescriptor.nextInChain = nullptr;
+    layoutDescriptor.numBindGroupLayouts = 0;
+    layoutDescriptor.bindGroupLayouts = nullptr;
+    dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor);
+    dawnPipelineLayout apiLayout = api.GetNewPipelineLayout();
+    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout));
+
+    // Create pipeline
+    dawnRenderPipelineDescriptor pipelineDescriptor;
+    pipelineDescriptor.nextInChain = nullptr;
+
+    dawnPipelineStageDescriptor vertexStage;
+    vertexStage.nextInChain = nullptr;
+    vertexStage.module = vsModule;
+    vertexStage.entryPoint = "main";
+    pipelineDescriptor.vertexStage = &vertexStage;
+
+    dawnPipelineStageDescriptor fragmentStage;
+    fragmentStage.nextInChain = nullptr;
+    fragmentStage.module = vsModule;
+    fragmentStage.entryPoint = "main";
+    pipelineDescriptor.fragmentStage = &fragmentStage;
+
+    dawnAttachmentsStateDescriptor attachmentsState;
+    attachmentsState.nextInChain = nullptr;
+    attachmentsState.numColorAttachments = 1;
+    dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM};
+    dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment};
+    attachmentsState.colorAttachments = colorAttachmentPtr;
+    attachmentsState.hasDepthStencilAttachment = false;
+    // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid
+    // data because we don't have optional substructures yet.
+    attachmentsState.depthStencilAttachment = &colorAttachment;
+    pipelineDescriptor.attachmentsState = &attachmentsState;
+
+    pipelineDescriptor.numBlendStates = 1;
+    pipelineDescriptor.blendStates = &blendStateDescriptor;
+
+    pipelineDescriptor.sampleCount = 1;
+    pipelineDescriptor.layout = layout;
+    pipelineDescriptor.inputState = inputState;
+    pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32;
+    pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+    pipelineDescriptor.depthStencilState = &depthStencilState;
+
+    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
+    EXPECT_CALL(api,
+                DeviceCreateRenderPipeline(
+                    apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
+                        return desc->vertexStage->entryPoint == std::string("main");
+                    })))
+        .WillOnce(Return(nullptr));
+    EXPECT_CALL(api, ShaderModuleRelease(apiVsModule));
+    EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder));
+    EXPECT_CALL(api, InputStateRelease(apiInputState));
+    EXPECT_CALL(api, PipelineLayoutRelease(apiLayout));
+
+    FlushClient();
+}
+
+// Test that the wire is able to send objects as value arguments
+TEST_F(WireArgumentTests, ObjectAsValueArgument) {
+    // Create a RenderPassDescriptor
+    dawnRenderPassDescriptorBuilder renderPassBuilder =
+        dawnDeviceCreateRenderPassDescriptorBuilder(device);
+    dawnRenderPassDescriptor renderPass =
+        dawnRenderPassDescriptorBuilderGetResult(renderPassBuilder);
+
+    dawnRenderPassDescriptorBuilder apiRenderPassBuilder = api.GetNewRenderPassDescriptorBuilder();
+    EXPECT_CALL(api, DeviceCreateRenderPassDescriptorBuilder(apiDevice))
+        .WillOnce(Return(apiRenderPassBuilder));
+    dawnRenderPassDescriptor apiRenderPass = api.GetNewRenderPassDescriptor();
+    EXPECT_CALL(api, RenderPassDescriptorBuilderGetResult(apiRenderPassBuilder))
+        .WillOnce(Return(apiRenderPass));
+
+    // Create command buffer builder, setting render pass descriptor
+    dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
+    dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, renderPass);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+
+    EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, apiRenderPass)).Times(1);
+
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+    EXPECT_CALL(api, RenderPassDescriptorBuilderRelease(apiRenderPassBuilder));
+    EXPECT_CALL(api, RenderPassDescriptorRelease(apiRenderPass));
+    FlushClient();
+}
+
+// Test that the wire is able to send array of objects
+TEST_F(WireArgumentTests, ObjectsAsPointerArgument) {
+    dawnCommandBuffer cmdBufs[2];
+    dawnCommandBuffer apiCmdBufs[2];
+
+    // Create two command buffers we need to use a GMock sequence otherwise the order of the
+    // CreateCommandBufferBuilder might be swapped since they are equivalent in term of matchers
+    Sequence s;
+    for (int i = 0; i < 2; ++i) {
+        dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
+        cmdBufs[i] = dawnCommandBufferBuilderGetResult(cmdBufBuilder);
+
+        dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+        EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+            .InSequence(s)
+            .WillOnce(Return(apiCmdBufBuilder));
+
+        apiCmdBufs[i] = api.GetNewCommandBuffer();
+        EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder))
+            .WillOnce(Return(apiCmdBufs[i]));
+        EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+        EXPECT_CALL(api, CommandBufferRelease(apiCmdBufs[i]));
+    }
+
+    // Create queue
+    dawnQueue queue = dawnDeviceCreateQueue(device);
+    dawnQueue apiQueue = api.GetNewQueue();
+    EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
+
+    // Submit command buffer and check we got a call with both API-side command buffers
+    dawnQueueSubmit(queue, 2, cmdBufs);
+
+    EXPECT_CALL(
+        api, QueueSubmit(apiQueue, 2, MatchesLambda([=](const dawnCommandBuffer* cmdBufs) -> bool {
+                             return cmdBufs[0] == apiCmdBufs[0] && cmdBufs[1] == apiCmdBufs[1];
+                         })));
+
+    EXPECT_CALL(api, QueueRelease(apiQueue));
+    FlushClient();
+}
+
+// Test that the wire is able to send structures that contain pure values (non-objects)
+TEST_F(WireArgumentTests, StructureOfValuesArgument) {
+    dawnSamplerDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.magFilter = DAWN_FILTER_MODE_LINEAR;
+    descriptor.minFilter = DAWN_FILTER_MODE_NEAREST;
+    descriptor.mipmapFilter = DAWN_FILTER_MODE_LINEAR;
+    descriptor.addressModeU = DAWN_ADDRESS_MODE_CLAMP_TO_EDGE;
+    descriptor.addressModeV = DAWN_ADDRESS_MODE_REPEAT;
+    descriptor.addressModeW = DAWN_ADDRESS_MODE_MIRRORED_REPEAT;
+    descriptor.lodMinClamp = kLodMin;
+    descriptor.lodMaxClamp = kLodMax;
+    descriptor.compareFunction = DAWN_COMPARE_FUNCTION_NEVER;
+    descriptor.borderColor = DAWN_BORDER_COLOR_TRANSPARENT_BLACK;
+
+    dawnDeviceCreateSampler(device, &descriptor);
+    EXPECT_CALL(api, DeviceCreateSampler(
+                         apiDevice, MatchesLambda([](const dawnSamplerDescriptor* desc) -> bool {
+                             return desc->nextInChain == nullptr &&
+                                    desc->magFilter == DAWN_FILTER_MODE_LINEAR &&
+                                    desc->minFilter == DAWN_FILTER_MODE_NEAREST &&
+                                    desc->mipmapFilter == DAWN_FILTER_MODE_LINEAR &&
+                                    desc->addressModeU == DAWN_ADDRESS_MODE_CLAMP_TO_EDGE &&
+                                    desc->addressModeV == DAWN_ADDRESS_MODE_REPEAT &&
+                                    desc->addressModeW == DAWN_ADDRESS_MODE_MIRRORED_REPEAT &&
+                                    desc->compareFunction == DAWN_COMPARE_FUNCTION_NEVER &&
+                                    desc->borderColor == DAWN_BORDER_COLOR_TRANSPARENT_BLACK &&
+                                    desc->lodMinClamp == kLodMin && desc->lodMaxClamp == kLodMax;
+                         })))
+        .WillOnce(Return(nullptr));
+
+    FlushClient();
+}
+
+// Test that the wire is able to send structures that contain objects
+TEST_F(WireArgumentTests, StructureOfObjectArrayArgument) {
+    dawnBindGroupLayoutDescriptor bglDescriptor;
+    bglDescriptor.numBindings = 0;
+    bglDescriptor.bindings = nullptr;
+
+    dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDescriptor);
+    dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
+    EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _)).WillOnce(Return(apiBgl));
+
+    dawnPipelineLayoutDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.numBindGroupLayouts = 1;
+    descriptor.bindGroupLayouts = &bgl;
+
+    dawnDeviceCreatePipelineLayout(device, &descriptor);
+    EXPECT_CALL(api, DeviceCreatePipelineLayout(
+                         apiDevice,
+                         MatchesLambda([apiBgl](const dawnPipelineLayoutDescriptor* desc) -> bool {
+                             return desc->nextInChain == nullptr &&
+                                    desc->numBindGroupLayouts == 1 &&
+                                    desc->bindGroupLayouts[0] == apiBgl;
+                         })))
+        .WillOnce(Return(nullptr));
+
+    EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl));
+    FlushClient();
+}
+
+// Test that the wire is able to send structures that contain objects
+TEST_F(WireArgumentTests, StructureOfStructureArrayArgument) {
+    static constexpr int NUM_BINDINGS = 3;
+    dawnBindGroupLayoutBinding bindings[NUM_BINDINGS]{
+        {0, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLER},
+        {1, DAWN_SHADER_STAGE_BIT_VERTEX, DAWN_BINDING_TYPE_SAMPLED_TEXTURE},
+        {2,
+         static_cast<dawnShaderStageBit>(DAWN_SHADER_STAGE_BIT_VERTEX |
+                                         DAWN_SHADER_STAGE_BIT_FRAGMENT),
+         DAWN_BINDING_TYPE_UNIFORM_BUFFER},
+    };
+    dawnBindGroupLayoutDescriptor bglDescriptor;
+    bglDescriptor.numBindings = NUM_BINDINGS;
+    bglDescriptor.bindings = bindings;
+
+    dawnDeviceCreateBindGroupLayout(device, &bglDescriptor);
+    dawnBindGroupLayout apiBgl = api.GetNewBindGroupLayout();
+    EXPECT_CALL(
+        api,
+        DeviceCreateBindGroupLayout(
+            apiDevice, MatchesLambda([bindings](const dawnBindGroupLayoutDescriptor* desc) -> bool {
+                for (int i = 0; i < NUM_BINDINGS; ++i) {
+                    const auto& a = desc->bindings[i];
+                    const auto& b = bindings[i];
+                    if (a.binding != b.binding || a.visibility != b.visibility ||
+                        a.type != b.type) {
+                        return false;
+                    }
+                }
+                return desc->nextInChain == nullptr && desc->numBindings == 3;
+            })))
+        .WillOnce(Return(apiBgl));
+
+    EXPECT_CALL(api, BindGroupLayoutRelease(apiBgl));
+    FlushClient();
+}
+
+// Test passing nullptr instead of objects - array of objects version
+TEST_F(WireArgumentTests, DISABLED_NullptrInArray) {
+    dawnBindGroupLayout nullBGL = nullptr;
+
+    dawnPipelineLayoutDescriptor descriptor;
+    descriptor.nextInChain = nullptr;
+    descriptor.numBindGroupLayouts = 1;
+    descriptor.bindGroupLayouts = &nullBGL;
+
+    dawnDeviceCreatePipelineLayout(device, &descriptor);
+    EXPECT_CALL(api,
+                DeviceCreatePipelineLayout(
+                    apiDevice, MatchesLambda([](const dawnPipelineLayoutDescriptor* desc) -> bool {
+                        return desc->nextInChain == nullptr && desc->numBindGroupLayouts == 1 &&
+                               desc->bindGroupLayouts[0] == nullptr;
+                    })))
+        .WillOnce(Return(nullptr));
+
+    FlushClient();
+}
diff --git a/src/tests/unittests/wire/WireBasicTests.cpp b/src/tests/unittests/wire/WireBasicTests.cpp
new file mode 100644
index 0000000..bb214b3
--- /dev/null
+++ b/src/tests/unittests/wire/WireBasicTests.cpp
@@ -0,0 +1,84 @@
+// 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"
+
+using namespace testing;
+using namespace dawn_wire;
+
+class WireBasicTests : public WireTest {
+  public:
+    WireBasicTests() : WireTest(true) {
+    }
+    ~WireBasicTests() override = default;
+};
+
+// One call gets forwarded correctly.
+TEST_F(WireBasicTests, CallForwarded) {
+    dawnDeviceCreateCommandBufferBuilder(device);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+    FlushClient();
+}
+
+// Test that calling methods on a new object works as expected.
+TEST_F(WireBasicTests, CreateThenCall) {
+    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
+    dawnCommandBufferBuilderGetResult(builder);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+
+    dawnCommandBuffer apiCmdBuf = api.GetNewCommandBuffer();
+    EXPECT_CALL(api, CommandBufferBuilderGetResult(apiCmdBufBuilder)).WillOnce(Return(apiCmdBuf));
+
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+    EXPECT_CALL(api, CommandBufferRelease(apiCmdBuf));
+    FlushClient();
+}
+
+// Test that client reference/release do not call the backend API.
+TEST_F(WireBasicTests, RefCountKeptInClient) {
+    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
+
+    dawnCommandBufferBuilderReference(builder);
+    dawnCommandBufferBuilderRelease(builder);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+
+    FlushClient();
+}
+
+// Test that client reference/release do not call the backend API.
+TEST_F(WireBasicTests, ReleaseCalledOnRefCount0) {
+    dawnCommandBufferBuilder builder = dawnDeviceCreateCommandBufferBuilder(device);
+
+    dawnCommandBufferBuilderRelease(builder);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+
+    EXPECT_CALL(api, CommandBufferBuilderRelease(apiCmdBufBuilder));
+
+    FlushClient();
+}
diff --git a/src/tests/unittests/wire/WireBufferMappingTests.cpp b/src/tests/unittests/wire/WireBufferMappingTests.cpp
new file mode 100644
index 0000000..d4dcf02
--- /dev/null
+++ b/src/tests/unittests/wire/WireBufferMappingTests.cpp
@@ -0,0 +1,513 @@
+// 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"
+
+using namespace testing;
+using namespace dawn_wire;
+
+namespace {
+
+    // Mock classes to add expectations on the wire calling callbacks
+    class MockBufferMapReadCallback {
+      public:
+        MOCK_METHOD3(Call,
+                     void(dawnBufferMapAsyncStatus status,
+                          const uint32_t* ptr,
+                          dawnCallbackUserdata userdata));
+    };
+
+    std::unique_ptr<MockBufferMapReadCallback> mockBufferMapReadCallback;
+    void ToMockBufferMapReadCallback(dawnBufferMapAsyncStatus status,
+                                     const void* ptr,
+                                     dawnCallbackUserdata userdata) {
+        // Assume the data is uint32_t to make writing matchers easier
+        mockBufferMapReadCallback->Call(status, static_cast<const uint32_t*>(ptr), userdata);
+    }
+
+    class MockBufferMapWriteCallback {
+      public:
+        MOCK_METHOD3(Call,
+                     void(dawnBufferMapAsyncStatus status,
+                          uint32_t* ptr,
+                          dawnCallbackUserdata userdata));
+    };
+
+    std::unique_ptr<MockBufferMapWriteCallback> mockBufferMapWriteCallback;
+    uint32_t* lastMapWritePointer = nullptr;
+    void ToMockBufferMapWriteCallback(dawnBufferMapAsyncStatus status,
+                                      void* ptr,
+                                      dawnCallbackUserdata userdata) {
+        // Assume the data is uint32_t to make writing matchers easier
+        lastMapWritePointer = static_cast<uint32_t*>(ptr);
+        mockBufferMapWriteCallback->Call(status, lastMapWritePointer, userdata);
+    }
+
+}  // anonymous namespace
+
+class WireBufferMappingTests : public WireTest {
+  public:
+    WireBufferMappingTests() : WireTest(true) {
+    }
+    ~WireBufferMappingTests() override = default;
+
+    void SetUp() override {
+        WireTest::SetUp();
+
+        mockBufferMapReadCallback = std::make_unique<MockBufferMapReadCallback>();
+        mockBufferMapWriteCallback = std::make_unique<MockBufferMapWriteCallback>();
+
+        {
+            dawnBufferDescriptor descriptor;
+            descriptor.nextInChain = nullptr;
+
+            apiBuffer = api.GetNewBuffer();
+            buffer = dawnDeviceCreateBuffer(device, &descriptor);
+
+            EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
+                .WillOnce(Return(apiBuffer))
+                .RetiresOnSaturation();
+            EXPECT_CALL(api, BufferRelease(apiBuffer));
+            FlushClient();
+        }
+        {
+            dawnBufferDescriptor descriptor;
+            descriptor.nextInChain = nullptr;
+
+            errorBuffer = dawnDeviceCreateBuffer(device, &descriptor);
+
+            EXPECT_CALL(api, DeviceCreateBuffer(apiDevice, _))
+                .WillOnce(Return(nullptr))
+                .RetiresOnSaturation();
+            FlushClient();
+        }
+    }
+
+    void TearDown() override {
+        WireTest::TearDown();
+
+        // Delete mocks so that expectations are checked
+        mockBufferMapReadCallback = nullptr;
+        mockBufferMapWriteCallback = nullptr;
+    }
+
+  protected:
+    // A successfully created buffer
+    dawnBuffer buffer;
+    dawnBuffer apiBuffer;
+
+    // An buffer that wasn't created on the server side
+    dawnBuffer errorBuffer;
+};
+
+// MapRead-specific tests
+
+// Check mapping for reading a succesfully created buffer
+TEST_F(WireBufferMappingTests, MappingForReadSuccessBuffer) {
+    dawnCallbackUserdata userdata = 8653;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                    &bufferContent);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
+        .Times(1);
+
+    FlushServer();
+
+    dawnBufferUnmap(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(WireBufferMappingTests, ErrorWhileMappingForRead) {
+    dawnCallbackUserdata userdata = 8654;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Check mapping for reading a buffer that didn't get created on the server side
+TEST_F(WireBufferMappingTests, MappingForReadErrorBuffer) {
+    dawnCallbackUserdata userdata = 8655;
+    dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback,
+                           userdata);
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+
+    dawnBufferUnmap(errorBuffer);
+
+    FlushClient();
+}
+
+// Check that the map read callback is called with UNKNOWN when the buffer is destroyed before the
+// request is finished
+TEST_F(WireBufferMappingTests, DestroyBeforeReadRequestEnd) {
+    dawnCallbackUserdata userdata = 8656;
+    dawnBufferMapReadAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback,
+                           userdata);
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
+        .Times(1);
+
+    dawnBufferRelease(errorBuffer);
+}
+
+// Check the map read callback is called with UNKNOWN when the map request would have worked, but
+// Unmap was called
+TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForRead) {
+    dawnCallbackUserdata userdata = 8657;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                    &bufferContent);
+        }));
+
+    FlushClient();
+
+    // Oh no! We are calling Unmap too early!
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
+        .Times(1);
+    dawnBufferUnmap(buffer);
+
+    // The callback shouldn't get called, even when the request succeeded on the server side
+    FlushServer();
+}
+
+// Check that an error map read callback gets nullptr while a buffer is already mapped
+TEST_F(WireBufferMappingTests, MappingForReadingErrorWhileAlreadyMappedGetsNullptr) {
+    // Successful map
+    dawnCallbackUserdata userdata = 34098;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                    &bufferContent);
+        }))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
+        .Times(1);
+
+    FlushServer();
+
+    // Map failure while the buffer is already mapped
+    userdata++;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Test that the MapReadCallback isn't fired twice when unmap() is called inside the callback
+TEST_F(WireBufferMappingTests, UnmapInsideMapReadCallback) {
+    dawnCallbackUserdata userdata = 2039;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                    &bufferContent);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
+        .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(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(WireBufferMappingTests, DestroyInsideMapReadCallback) {
+    dawnCallbackUserdata userdata = 2039;
+    dawnBufferMapReadAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapReadCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapReadAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapReadCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                    &bufferContent);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapReadCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(bufferContent)), userdata))
+        .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); }));
+
+    FlushServer();
+
+    FlushClient();
+}
+
+// MapWrite-specific tests
+
+// Check mapping for writing a succesfully created buffer
+TEST_F(WireBufferMappingTests, MappingForWriteSuccessBuffer) {
+    dawnCallbackUserdata userdata = 8653;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t serverBufferContent = 31337;
+    uint32_t updatedContent = 4242;
+    uint32_t zero = 0;
+
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &serverBufferContent);
+        }));
+
+    FlushClient();
+
+    // The map write callback always gets a buffer full of zeroes.
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
+        .Times(1);
+
+    FlushServer();
+
+    // Write something to the mapped pointer
+    *lastMapWritePointer = updatedContent;
+
+    dawnBufferUnmap(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(WireBufferMappingTests, ErrorWhileMappingForWrite) {
+    dawnCallbackUserdata userdata = 8654;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Check mapping for writing a buffer that didn't get created on the server side
+TEST_F(WireBufferMappingTests, MappingForWriteErrorBuffer) {
+    dawnCallbackUserdata userdata = 8655;
+    dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback,
+                            userdata);
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+
+    dawnBufferUnmap(errorBuffer);
+
+    FlushClient();
+}
+
+// Check that the map write callback is called with UNKNOWN when the buffer is destroyed before the
+// request is finished
+TEST_F(WireBufferMappingTests, DestroyBeforeWriteRequestEnd) {
+    dawnCallbackUserdata userdata = 8656;
+    dawnBufferMapWriteAsync(errorBuffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback,
+                            userdata);
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
+        .Times(1);
+
+    dawnBufferRelease(errorBuffer);
+}
+
+// Check the map read callback is called with UNKNOWN when the map request would have worked, but
+// Unmap was called
+TEST_F(WireBufferMappingTests, UnmapCalledTooEarlyForWrite) {
+    dawnCallbackUserdata userdata = 8657;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &bufferContent);
+        }));
+
+    FlushClient();
+
+    // Oh no! We are calling Unmap too early!
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_UNKNOWN, nullptr, userdata))
+        .Times(1);
+    dawnBufferUnmap(buffer);
+
+    // The callback shouldn't get called, even when the request succeeded on the server side
+    FlushServer();
+}
+
+// Check that an error map read callback gets nullptr while a buffer is already mapped
+TEST_F(WireBufferMappingTests, MappingForWritingErrorWhileAlreadyMappedGetsNullptr) {
+    // Successful map
+    dawnCallbackUserdata userdata = 34098;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    uint32_t zero = 0;
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &bufferContent);
+        }))
+        .RetiresOnSaturation();
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
+        .Times(1);
+
+    FlushServer();
+
+    // Map failure while the buffer is already mapped
+    userdata++;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_ERROR, nullptr, userdata))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Test that the MapWriteCallback isn't fired twice when unmap() is called inside the callback
+TEST_F(WireBufferMappingTests, UnmapInsideMapWriteCallback) {
+    dawnCallbackUserdata userdata = 2039;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    uint32_t zero = 0;
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &bufferContent);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
+        .WillOnce(InvokeWithoutArgs([&]() { dawnBufferUnmap(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(WireBufferMappingTests, DestroyInsideMapWriteCallback) {
+    dawnCallbackUserdata userdata = 2039;
+    dawnBufferMapWriteAsync(buffer, 40, sizeof(uint32_t), ToMockBufferMapWriteCallback, userdata);
+
+    uint32_t bufferContent = 31337;
+    uint32_t zero = 0;
+    EXPECT_CALL(api, OnBufferMapWriteAsyncCallback(apiBuffer, 40, sizeof(uint32_t), _, _))
+        .WillOnce(InvokeWithoutArgs([&]() {
+            api.CallMapWriteCallback(apiBuffer, DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS,
+                                     &bufferContent);
+        }));
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBufferMapWriteCallback,
+                Call(DAWN_BUFFER_MAP_ASYNC_STATUS_SUCCESS, Pointee(Eq(zero)), userdata))
+        .WillOnce(InvokeWithoutArgs([&]() { dawnBufferRelease(buffer); }));
+
+    FlushServer();
+
+    FlushClient();
+}
diff --git a/src/tests/unittests/wire/WireCallbackTests.cpp b/src/tests/unittests/wire/WireCallbackTests.cpp
new file mode 100644
index 0000000..64b68ac
--- /dev/null
+++ b/src/tests/unittests/wire/WireCallbackTests.cpp
@@ -0,0 +1,305 @@
+// 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"
+
+using namespace testing;
+using namespace dawn_wire;
+
+namespace {
+
+    // Mock classes to add expectations on the wire calling callbacks
+    class MockDeviceErrorCallback {
+      public:
+        MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata));
+    };
+
+    std::unique_ptr<MockDeviceErrorCallback> mockDeviceErrorCallback;
+    void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) {
+        mockDeviceErrorCallback->Call(message, userdata);
+    }
+
+    class MockBuilderErrorCallback {
+      public:
+        MOCK_METHOD4(Call,
+                     void(dawnBuilderErrorStatus status,
+                          const char* message,
+                          dawnCallbackUserdata userdata1,
+                          dawnCallbackUserdata userdata2));
+    };
+
+    std::unique_ptr<MockBuilderErrorCallback> mockBuilderErrorCallback;
+    void ToMockBuilderErrorCallback(dawnBuilderErrorStatus status,
+                                    const char* message,
+                                    dawnCallbackUserdata userdata1,
+                                    dawnCallbackUserdata userdata2) {
+        mockBuilderErrorCallback->Call(status, message, userdata1, userdata2);
+    }
+
+}  // anonymous namespace
+
+class WireCallbackTests : public WireTest {
+  public:
+    WireCallbackTests() : WireTest(true) {
+    }
+    ~WireCallbackTests() override = default;
+
+    void SetUp() override {
+        WireTest::SetUp();
+
+        mockDeviceErrorCallback = std::make_unique<MockDeviceErrorCallback>();
+        mockBuilderErrorCallback = std::make_unique<MockBuilderErrorCallback>();
+    }
+
+    void TearDown() override {
+        WireTest::TearDown();
+
+        // Delete mocks so that expectations are checked
+        mockDeviceErrorCallback = nullptr;
+        mockBuilderErrorCallback = nullptr;
+    }
+};
+
+// Test that we get a success builder error status when no error happens
+TEST_F(WireCallbackTests, SuccessCallbackOnBuilderSuccess) {
+    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2);
+    dawnBufferBuilderGetResult(bufferBuilder);
+
+    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
+        .WillOnce(Return(apiBufferBuilder));
+
+    dawnBuffer apiBuffer = api.GetNewBuffer();
+    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
+        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
+            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS,
+                                         "I like cheese");
+            return apiBuffer;
+        }));
+
+    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
+    EXPECT_CALL(api, BufferRelease(apiBuffer));
+    FlushClient();
+
+    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, _, 1, 2));
+
+    FlushServer();
+}
+
+// Test that the client calls the builder callback with unknown when it HAS to fire the callback but
+// can't know the status yet.
+TEST_F(WireCallbackTests, UnknownBuilderErrorStatusCallback) {
+    // The builder is destroyed before the object is built
+    {
+        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 1, 2);
+
+        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 1, 2))
+            .Times(1);
+
+        dawnBufferBuilderRelease(bufferBuilder);
+    }
+
+    // If the builder has been consumed, it doesn't fire the callback with unknown
+    {
+        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4);
+        dawnBufferBuilderGetResult(bufferBuilder);
+
+        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 3, 4))
+            .Times(0);
+
+        dawnBufferBuilderRelease(bufferBuilder);
+    }
+
+    // If the builder has been consumed, and the object is destroyed before the result comes from
+    // the server, then the callback is fired with unknown
+    {
+        dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+        dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 5, 6);
+        dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder);
+
+        EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_UNKNOWN, _, 5, 6))
+            .Times(1);
+
+        dawnBufferRelease(buffer);
+    }
+}
+
+// Test that a builder success status doesn't get forwarded to the device
+TEST_F(WireCallbackTests, SuccessCallbackNotForwardedToDevice) {
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, 0);
+
+    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+    dawnBufferBuilderGetResult(bufferBuilder);
+
+    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
+        .WillOnce(Return(apiBufferBuilder));
+
+    dawnBuffer apiBuffer = api.GetNewBuffer();
+    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
+        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
+            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS,
+                                         "I like cheese");
+            return apiBuffer;
+        }));
+
+    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
+    EXPECT_CALL(api, BufferRelease(apiBuffer));
+    FlushClient();
+    FlushServer();
+}
+
+// Test that a builder error status gets forwarded to the device
+TEST_F(WireCallbackTests, ErrorCallbackForwardedToDevice) {
+    uint64_t userdata = 30495;
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
+
+    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+    dawnBufferBuilderGetResult(bufferBuilder);
+
+    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
+        .WillOnce(Return(apiBufferBuilder));
+
+    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
+        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
+            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR,
+                                         "Error :(");
+            return nullptr;
+        }));
+
+    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
+    FlushClient();
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
+
+    FlushServer();
+}
+
+// Test the return wire for device error callbacks
+TEST_F(WireCallbackTests, DeviceErrorCallback) {
+    uint64_t userdata = 3049785;
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
+
+    // Setting the error callback should stay on the client side and do nothing
+    FlushClient();
+
+    // Calling the callback on the server side will result in the callback being called on the
+    // client side
+    api.CallDeviceErrorCallback(apiDevice, "Some error message");
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(StrEq("Some error message"), userdata)).Times(1);
+
+    FlushServer();
+}
+
+// Test the return wire for device error callbacks
+TEST_F(WireCallbackTests, BuilderErrorCallback) {
+    uint64_t userdata1 = 982734;
+    uint64_t userdata2 = 982734239028;
+
+    // Create the buffer builder, the callback is set immediately on the server side
+    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+
+    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
+        .WillOnce(Return(apiBufferBuilder));
+
+    EXPECT_CALL(api, OnBuilderSetErrorCallback(apiBufferBuilder, _, _, _)).Times(1);
+
+    FlushClient();
+
+    // Setting the callback on the client side doesn't do anything on the server side
+    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, userdata1,
+                                      userdata2);
+    FlushClient();
+
+    // Create an object so that it is a valid case to call the error callback
+    dawnBufferBuilderGetResult(bufferBuilder);
+
+    dawnBuffer apiBuffer = api.GetNewBuffer();
+    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
+        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
+            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_SUCCESS,
+                                         "Success!");
+            return apiBuffer;
+        }));
+
+    EXPECT_CALL(api, BufferBuilderRelease(apiBufferBuilder));
+    EXPECT_CALL(api, BufferRelease(apiBuffer));
+    FlushClient();
+
+    // The error callback gets called on the client side
+    EXPECT_CALL(*mockBuilderErrorCallback,
+                Call(DAWN_BUILDER_ERROR_STATUS_SUCCESS, StrEq("Success!"), userdata1, userdata2))
+        .Times(1);
+
+    FlushServer();
+}
+
+// Test that the server doesn't forward calls to error objects or with error objects
+// Also test that when GetResult is called on an error builder, the error callback is fired
+// TODO(cwallez@chromium.org): This test is disabled because the introduction of encoders breaks
+// the assumptions of the "builder error" handling that a builder is self-contained. We need to
+// revisit this once the new error handling is in place.
+TEST_F(WireCallbackTests, DISABLED_CallsSkippedAfterBuilderError) {
+    dawnCommandBufferBuilder cmdBufBuilder = dawnDeviceCreateCommandBufferBuilder(device);
+    dawnCommandBufferBuilderSetErrorCallback(cmdBufBuilder, ToMockBuilderErrorCallback, 1, 2);
+
+    dawnRenderPassEncoder pass = dawnCommandBufferBuilderBeginRenderPass(cmdBufBuilder, nullptr);
+
+    dawnBufferBuilder bufferBuilder = dawnDeviceCreateBufferBuilderForTesting(device);
+    dawnBufferBuilderSetErrorCallback(bufferBuilder, ToMockBuilderErrorCallback, 3, 4);
+    dawnBuffer buffer = dawnBufferBuilderGetResult(bufferBuilder);  // Hey look an error!
+
+    // These calls will be skipped because of the error
+    dawnBufferSetSubData(buffer, 0, 0, nullptr);
+    dawnRenderPassEncoderSetIndexBuffer(pass, buffer, 0);
+    dawnRenderPassEncoderEndPass(pass);
+    dawnCommandBufferBuilderGetResult(cmdBufBuilder);
+
+    dawnCommandBufferBuilder apiCmdBufBuilder = api.GetNewCommandBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateCommandBufferBuilder(apiDevice))
+        .WillOnce(Return(apiCmdBufBuilder));
+
+    dawnRenderPassEncoder apiPass = api.GetNewRenderPassEncoder();
+    EXPECT_CALL(api, CommandBufferBuilderBeginRenderPass(apiCmdBufBuilder, _))
+        .WillOnce(Return(apiPass));
+
+    dawnBufferBuilder apiBufferBuilder = api.GetNewBufferBuilder();
+    EXPECT_CALL(api, DeviceCreateBufferBuilderForTesting(apiDevice))
+        .WillOnce(Return(apiBufferBuilder));
+
+    // Hey look an error!
+    EXPECT_CALL(api, BufferBuilderGetResult(apiBufferBuilder))
+        .WillOnce(InvokeWithoutArgs([&]() -> dawnBuffer {
+            api.CallBuilderErrorCallback(apiBufferBuilder, DAWN_BUILDER_ERROR_STATUS_ERROR,
+                                         "Error");
+            return nullptr;
+        }));
+
+    EXPECT_CALL(api, BufferSetSubData(_, _, _, _)).Times(0);
+    EXPECT_CALL(api, RenderPassEncoderSetIndexBuffer(_, _, _)).Times(0);
+    EXPECT_CALL(api, CommandBufferBuilderGetResult(_)).Times(0);
+
+    FlushClient();
+
+    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 1, 2)).Times(1);
+    EXPECT_CALL(*mockBuilderErrorCallback, Call(DAWN_BUILDER_ERROR_STATUS_ERROR, _, 3, 4)).Times(1);
+
+    FlushServer();
+}
diff --git a/src/tests/unittests/wire/WireFenceTests.cpp b/src/tests/unittests/wire/WireFenceTests.cpp
new file mode 100644
index 0000000..5c8fb88
--- /dev/null
+++ b/src/tests/unittests/wire/WireFenceTests.cpp
@@ -0,0 +1,266 @@
+// 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"
+
+using namespace testing;
+using namespace dawn_wire;
+
+namespace {
+
+    // Mock classes to add expectations on the wire calling callbacks
+    class MockDeviceErrorCallback {
+      public:
+        MOCK_METHOD2(Call, void(const char* message, dawnCallbackUserdata userdata));
+    };
+
+    std::unique_ptr<MockDeviceErrorCallback> mockDeviceErrorCallback;
+    void ToMockDeviceErrorCallback(const char* message, dawnCallbackUserdata userdata) {
+        mockDeviceErrorCallback->Call(message, userdata);
+    }
+
+    class MockFenceOnCompletionCallback {
+      public:
+        MOCK_METHOD2(Call, void(dawnFenceCompletionStatus status, dawnCallbackUserdata userdata));
+    };
+
+    std::unique_ptr<MockFenceOnCompletionCallback> mockFenceOnCompletionCallback;
+    void ToMockFenceOnCompletionCallback(dawnFenceCompletionStatus status,
+                                         dawnCallbackUserdata userdata) {
+        mockFenceOnCompletionCallback->Call(status, userdata);
+    }
+
+}  // anonymous namespace
+
+class WireFenceTests : public WireTest {
+  public:
+    WireFenceTests() : WireTest(true) {
+    }
+    ~WireFenceTests() override = default;
+
+    void SetUp() override {
+        WireTest::SetUp();
+
+        mockDeviceErrorCallback = std::make_unique<MockDeviceErrorCallback>();
+        mockFenceOnCompletionCallback = std::make_unique<MockFenceOnCompletionCallback>();
+
+        {
+            dawnFenceDescriptor descriptor;
+            descriptor.initialValue = 1;
+            descriptor.nextInChain = nullptr;
+
+            apiFence = api.GetNewFence();
+            fence = dawnDeviceCreateFence(device, &descriptor);
+
+            EXPECT_CALL(api, DeviceCreateFence(apiDevice, _)).WillOnce(Return(apiFence));
+            EXPECT_CALL(api, FenceRelease(apiFence));
+            FlushClient();
+        }
+        {
+            queue = dawnDeviceCreateQueue(device);
+            apiQueue = api.GetNewQueue();
+            EXPECT_CALL(api, DeviceCreateQueue(apiDevice)).WillOnce(Return(apiQueue));
+            EXPECT_CALL(api, QueueRelease(apiQueue));
+            FlushClient();
+        }
+    }
+
+    void TearDown() override {
+        WireTest::TearDown();
+
+        // Delete mocks so that expectations are checked
+        mockDeviceErrorCallback = nullptr;
+        mockFenceOnCompletionCallback = nullptr;
+    }
+
+  protected:
+    void DoQueueSignal(uint64_t signalValue) {
+        dawnQueueSignal(queue, fence, signalValue);
+        EXPECT_CALL(api, QueueSignal(apiQueue, apiFence, signalValue)).Times(1);
+
+        // This callback is generated to update the completedValue of the fence
+        // on the client
+        EXPECT_CALL(api, OnFenceOnCompletionCallback(apiFence, signalValue, _, _))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                api.CallFenceOnCompletionCallback(apiFence, DAWN_FENCE_COMPLETION_STATUS_SUCCESS);
+            }));
+    }
+
+    // A successfully created fence
+    dawnFence fence;
+    dawnFence apiFence;
+
+    dawnQueue queue;
+    dawnQueue apiQueue;
+};
+
+// Check that signaling a fence succeeds
+TEST_F(WireFenceTests, QueueSignalSuccess) {
+    DoQueueSignal(2u);
+    DoQueueSignal(3u);
+    FlushClient();
+    FlushServer();
+}
+
+// Without any flushes, it is valid to signal a value greater than the current
+// signaled value
+TEST_F(WireFenceTests, QueueSignalSynchronousValidationSuccess) {
+    dawnCallbackUserdata userdata = 9157;
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
+
+    dawnQueueSignal(queue, fence, 2u);
+    dawnQueueSignal(queue, fence, 4u);
+    dawnQueueSignal(queue, fence, 5u);
+}
+
+// Without any flushes, errors should be generated when signaling a value less
+// than or equal to the current signaled value
+TEST_F(WireFenceTests, QueueSignalSynchronousValidationError) {
+    dawnCallbackUserdata userdata = 3157;
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata);
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
+    dawnQueueSignal(queue, fence, 0u);  // Error
+    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
+    dawnQueueSignal(queue, fence, 1u);  // Error
+    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(0);
+    dawnQueueSignal(queue, fence, 4u);  // Success
+    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
+
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata)).Times(1);
+    dawnQueueSignal(queue, fence, 3u);  // Error
+    EXPECT_TRUE(Mock::VerifyAndClear(mockDeviceErrorCallback.get()));
+}
+
+// Check that callbacks are immediately called if the fence is already finished
+TEST_F(WireFenceTests, OnCompletionImmediate) {
+    // Can call on value < (initial) signaled value happens immediately
+    {
+        dawnCallbackUserdata userdata = 9847;
+        EXPECT_CALL(*mockFenceOnCompletionCallback,
+                    Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
+            .Times(1);
+        dawnFenceOnCompletion(fence, 0, ToMockFenceOnCompletionCallback, userdata);
+    }
+
+    // Can call on value == (initial) signaled value happens immediately
+    {
+        dawnCallbackUserdata userdata = 4347;
+        EXPECT_CALL(*mockFenceOnCompletionCallback,
+                    Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata))
+            .Times(1);
+        dawnFenceOnCompletion(fence, 1, ToMockFenceOnCompletionCallback, userdata);
+    }
+}
+
+// Check that all passed client completion callbacks are called
+TEST_F(WireFenceTests, OnCompletionMultiple) {
+    DoQueueSignal(3u);
+    DoQueueSignal(6u);
+
+    dawnCallbackUserdata userdata0 = 2134;
+    dawnCallbackUserdata userdata1 = 7134;
+    dawnCallbackUserdata userdata2 = 3144;
+    dawnCallbackUserdata userdata3 = 1130;
+
+    // Add callbacks in a non-monotonic order. They should still be called
+    // in order of increasing fence value.
+    // Add multiple callbacks for the same value.
+    dawnFenceOnCompletion(fence, 6, ToMockFenceOnCompletionCallback, userdata0);
+    dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata1);
+    dawnFenceOnCompletion(fence, 3, ToMockFenceOnCompletionCallback, userdata2);
+    dawnFenceOnCompletion(fence, 2, ToMockFenceOnCompletionCallback, userdata3);
+
+    Sequence s1, s2;
+    EXPECT_CALL(*mockFenceOnCompletionCallback,
+                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata1))
+        .Times(1)
+        .InSequence(s1);
+    EXPECT_CALL(*mockFenceOnCompletionCallback,
+                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata3))
+        .Times(1)
+        .InSequence(s2);
+    EXPECT_CALL(*mockFenceOnCompletionCallback,
+                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata2))
+        .Times(1)
+        .InSequence(s1, s2);
+    EXPECT_CALL(*mockFenceOnCompletionCallback,
+                Call(DAWN_FENCE_COMPLETION_STATUS_SUCCESS, userdata0))
+        .Times(1)
+        .InSequence(s1, s2);
+
+    FlushClient();
+    FlushServer();
+}
+
+// Without any flushes, it is valid to wait on a value less than or equal to
+// the last signaled value
+TEST_F(WireFenceTests, OnCompletionSynchronousValidationSuccess) {
+    dawnQueueSignal(queue, fence, 4u);
+    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, 0);
+    dawnFenceOnCompletion(fence, 3u, ToMockFenceOnCompletionCallback, 0);
+    dawnFenceOnCompletion(fence, 4u, ToMockFenceOnCompletionCallback, 0);
+}
+
+// Without any flushes, errors should be generated when waiting on a value greater
+// than the last signaled value
+TEST_F(WireFenceTests, OnCompletionSynchronousValidationError) {
+    dawnCallbackUserdata userdata1 = 3817;
+    dawnCallbackUserdata userdata2 = 3857;
+    dawnDeviceSetErrorCallback(device, ToMockDeviceErrorCallback, userdata2);
+
+    EXPECT_CALL(*mockFenceOnCompletionCallback, Call(DAWN_FENCE_COMPLETION_STATUS_ERROR, userdata1))
+        .Times(1);
+    EXPECT_CALL(*mockDeviceErrorCallback, Call(_, userdata2)).Times(1);
+
+    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata1);
+}
+
+// Check that the fence completed value is initialized
+TEST_F(WireFenceTests, GetCompletedValueInitialization) {
+    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
+}
+
+// Check that the fence completed value updates after signaling the fence
+TEST_F(WireFenceTests, GetCompletedValueUpdate) {
+    DoQueueSignal(3u);
+    FlushClient();
+    FlushServer();
+
+    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 3u);
+}
+
+// Check that the fence completed value does not update without a flush
+TEST_F(WireFenceTests, GetCompletedValueNoUpdate) {
+    dawnQueueSignal(queue, fence, 3u);
+    EXPECT_EQ(dawnFenceGetCompletedValue(fence), 1u);
+}
+
+// Check that the callback is called with UNKNOWN when the fence is destroyed
+// before the completed value is updated
+TEST_F(WireFenceTests, DestroyBeforeOnCompletionEnd) {
+    dawnCallbackUserdata userdata = 8616;
+    dawnQueueSignal(queue, fence, 3u);
+    dawnFenceOnCompletion(fence, 2u, ToMockFenceOnCompletionCallback, userdata);
+    EXPECT_CALL(*mockFenceOnCompletionCallback,
+                Call(DAWN_FENCE_COMPLETION_STATUS_UNKNOWN, userdata))
+        .Times(1);
+
+    dawnFenceRelease(fence);
+}
diff --git a/src/tests/unittests/wire/WireOptionalTests.cpp b/src/tests/unittests/wire/WireOptionalTests.cpp
new file mode 100644
index 0000000..9243b00
--- /dev/null
+++ b/src/tests/unittests/wire/WireOptionalTests.cpp
@@ -0,0 +1,208 @@
+// 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"
+
+using namespace testing;
+using namespace dawn_wire;
+
+class WireOptionalTests : public WireTest {
+  public:
+    WireOptionalTests() : WireTest(true) {
+    }
+    ~WireOptionalTests() override = default;
+};
+
+// Test passing nullptr instead of objects - object as value version
+TEST_F(WireOptionalTests, OptionalObjectValue) {
+    dawnBindGroupLayoutDescriptor bglDesc;
+    bglDesc.nextInChain = nullptr;
+    bglDesc.numBindings = 0;
+    dawnBindGroupLayout bgl = dawnDeviceCreateBindGroupLayout(device, &bglDesc);
+
+    dawnBindGroupLayout apiBindGroupLayout = api.GetNewBindGroupLayout();
+    EXPECT_CALL(api, DeviceCreateBindGroupLayout(apiDevice, _))
+        .WillOnce(Return(apiBindGroupLayout));
+
+    // The `sampler`, `textureView` and `buffer` members of a binding are optional.
+    dawnBindGroupBinding binding;
+    binding.binding = 0;
+    binding.sampler = nullptr;
+    binding.textureView = nullptr;
+    binding.buffer = nullptr;
+
+    dawnBindGroupDescriptor bgDesc;
+    bgDesc.nextInChain = nullptr;
+    bgDesc.layout = bgl;
+    bgDesc.numBindings = 1;
+    bgDesc.bindings = &binding;
+
+    dawnDeviceCreateBindGroup(device, &bgDesc);
+    EXPECT_CALL(api, DeviceCreateBindGroup(
+                         apiDevice, MatchesLambda([](const dawnBindGroupDescriptor* desc) -> bool {
+                             return desc->nextInChain == nullptr && desc->numBindings == 1 &&
+                                    desc->bindings[0].binding == 0 &&
+                                    desc->bindings[0].sampler == nullptr &&
+                                    desc->bindings[0].buffer == nullptr &&
+                                    desc->bindings[0].textureView == nullptr;
+                         })))
+        .WillOnce(Return(nullptr));
+
+    EXPECT_CALL(api, BindGroupLayoutRelease(apiBindGroupLayout));
+    FlushClient();
+}
+
+// Test that the wire is able to send optional pointers to structures
+TEST_F(WireOptionalTests, OptionalStructPointer) {
+    // Create shader module
+    dawnShaderModuleDescriptor vertexDescriptor;
+    vertexDescriptor.nextInChain = nullptr;
+    vertexDescriptor.codeSize = 0;
+    dawnShaderModule vsModule = dawnDeviceCreateShaderModule(device, &vertexDescriptor);
+    dawnShaderModule apiVsModule = api.GetNewShaderModule();
+    EXPECT_CALL(api, DeviceCreateShaderModule(apiDevice, _)).WillOnce(Return(apiVsModule));
+
+    // Create the blend state descriptor
+    dawnBlendDescriptor blendDescriptor;
+    blendDescriptor.operation = DAWN_BLEND_OPERATION_ADD;
+    blendDescriptor.srcFactor = DAWN_BLEND_FACTOR_ONE;
+    blendDescriptor.dstFactor = DAWN_BLEND_FACTOR_ONE;
+    dawnBlendStateDescriptor blendStateDescriptor;
+    blendStateDescriptor.nextInChain = nullptr;
+    blendStateDescriptor.alphaBlend = blendDescriptor;
+    blendStateDescriptor.colorBlend = blendDescriptor;
+    blendStateDescriptor.colorWriteMask = DAWN_COLOR_WRITE_MASK_ALL;
+
+    // Create the input state
+    dawnInputStateBuilder inputStateBuilder = dawnDeviceCreateInputStateBuilder(device);
+    dawnInputStateBuilder apiInputStateBuilder = api.GetNewInputStateBuilder();
+    EXPECT_CALL(api, DeviceCreateInputStateBuilder(apiDevice))
+        .WillOnce(Return(apiInputStateBuilder));
+
+    dawnInputState inputState = dawnInputStateBuilderGetResult(inputStateBuilder);
+    dawnInputState apiInputState = api.GetNewInputState();
+    EXPECT_CALL(api, InputStateBuilderGetResult(apiInputStateBuilder))
+        .WillOnce(Return(apiInputState));
+
+    // Create the depth-stencil state
+    dawnStencilStateFaceDescriptor stencilFace;
+    stencilFace.compare = DAWN_COMPARE_FUNCTION_ALWAYS;
+    stencilFace.failOp = DAWN_STENCIL_OPERATION_KEEP;
+    stencilFace.depthFailOp = DAWN_STENCIL_OPERATION_KEEP;
+    stencilFace.passOp = DAWN_STENCIL_OPERATION_KEEP;
+
+    dawnDepthStencilStateDescriptor depthStencilState;
+    depthStencilState.nextInChain = nullptr;
+    depthStencilState.depthWriteEnabled = false;
+    depthStencilState.depthCompare = DAWN_COMPARE_FUNCTION_ALWAYS;
+    depthStencilState.stencilBack = stencilFace;
+    depthStencilState.stencilFront = stencilFace;
+    depthStencilState.stencilReadMask = 0xff;
+    depthStencilState.stencilWriteMask = 0xff;
+
+    // Create the pipeline layout
+    dawnPipelineLayoutDescriptor layoutDescriptor;
+    layoutDescriptor.nextInChain = nullptr;
+    layoutDescriptor.numBindGroupLayouts = 0;
+    layoutDescriptor.bindGroupLayouts = nullptr;
+    dawnPipelineLayout layout = dawnDeviceCreatePipelineLayout(device, &layoutDescriptor);
+    dawnPipelineLayout apiLayout = api.GetNewPipelineLayout();
+    EXPECT_CALL(api, DeviceCreatePipelineLayout(apiDevice, _)).WillOnce(Return(apiLayout));
+
+    // Create pipeline
+    dawnRenderPipelineDescriptor pipelineDescriptor;
+    pipelineDescriptor.nextInChain = nullptr;
+
+    dawnPipelineStageDescriptor vertexStage;
+    vertexStage.nextInChain = nullptr;
+    vertexStage.module = vsModule;
+    vertexStage.entryPoint = "main";
+    pipelineDescriptor.vertexStage = &vertexStage;
+
+    dawnPipelineStageDescriptor fragmentStage;
+    fragmentStage.nextInChain = nullptr;
+    fragmentStage.module = vsModule;
+    fragmentStage.entryPoint = "main";
+    pipelineDescriptor.fragmentStage = &fragmentStage;
+
+    dawnAttachmentsStateDescriptor attachmentsState;
+    attachmentsState.nextInChain = nullptr;
+    attachmentsState.numColorAttachments = 1;
+    dawnAttachmentDescriptor colorAttachment = {nullptr, DAWN_TEXTURE_FORMAT_R8_G8_B8_A8_UNORM};
+    dawnAttachmentDescriptor* colorAttachmentPtr[] = {&colorAttachment};
+    attachmentsState.colorAttachments = colorAttachmentPtr;
+    attachmentsState.hasDepthStencilAttachment = false;
+    // Even with hasDepthStencilAttachment = false, depthStencilAttachment must point to valid
+    // data because we don't have optional substructures yet.
+    attachmentsState.depthStencilAttachment = &colorAttachment;
+    pipelineDescriptor.attachmentsState = &attachmentsState;
+
+    pipelineDescriptor.numBlendStates = 1;
+    pipelineDescriptor.blendStates = &blendStateDescriptor;
+
+    pipelineDescriptor.sampleCount = 1;
+    pipelineDescriptor.layout = layout;
+    pipelineDescriptor.inputState = inputState;
+    pipelineDescriptor.indexFormat = DAWN_INDEX_FORMAT_UINT32;
+    pipelineDescriptor.primitiveTopology = DAWN_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+    // First case: depthStencilState is not null.
+    pipelineDescriptor.depthStencilState = &depthStencilState;
+    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
+    EXPECT_CALL(
+        api,
+        DeviceCreateRenderPipeline(
+            apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
+                return desc->depthStencilState != nullptr &&
+                       desc->depthStencilState->nextInChain == nullptr &&
+                       desc->depthStencilState->depthWriteEnabled == false &&
+                       desc->depthStencilState->depthCompare == DAWN_COMPARE_FUNCTION_ALWAYS &&
+                       desc->depthStencilState->stencilBack.compare ==
+                           DAWN_COMPARE_FUNCTION_ALWAYS &&
+                       desc->depthStencilState->stencilBack.failOp == DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilBack.depthFailOp ==
+                           DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilBack.passOp == DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilFront.compare ==
+                           DAWN_COMPARE_FUNCTION_ALWAYS &&
+                       desc->depthStencilState->stencilFront.failOp ==
+                           DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilFront.depthFailOp ==
+                           DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilFront.passOp ==
+                           DAWN_STENCIL_OPERATION_KEEP &&
+                       desc->depthStencilState->stencilReadMask == 0xff &&
+                       desc->depthStencilState->stencilWriteMask == 0xff;
+            })))
+        .WillOnce(Return(nullptr));
+
+    FlushClient();
+
+    // Second case: depthStencilState is null.
+    pipelineDescriptor.depthStencilState = nullptr;
+    dawnDeviceCreateRenderPipeline(device, &pipelineDescriptor);
+    EXPECT_CALL(api,
+                DeviceCreateRenderPipeline(
+                    apiDevice, MatchesLambda([](const dawnRenderPipelineDescriptor* desc) -> bool {
+                        return desc->depthStencilState == nullptr;
+                    })))
+        .WillOnce(Return(nullptr));
+
+    EXPECT_CALL(api, ShaderModuleRelease(apiVsModule));
+    EXPECT_CALL(api, InputStateBuilderRelease(apiInputStateBuilder));
+    EXPECT_CALL(api, InputStateRelease(apiInputState));
+    EXPECT_CALL(api, PipelineLayoutRelease(apiLayout));
+
+    FlushClient();
+}
diff --git a/src/tests/unittests/wire/WireTest.cpp b/src/tests/unittests/wire/WireTest.cpp
new file mode 100644
index 0000000..cfc5cf5
--- /dev/null
+++ b/src/tests/unittests/wire/WireTest.cpp
@@ -0,0 +1,74 @@
+// 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"
+#include "dawn_wire/WireServer.h"
+#include "utils/TerribleCommandBuffer.h"
+
+using namespace testing;
+using namespace dawn_wire;
+
+WireTest::WireTest(bool ignoreSetCallbackCalls) : mIgnoreSetCallbackCalls(ignoreSetCallbackCalls) {
+}
+
+WireTest::~WireTest() {
+}
+
+void WireTest::SetUp() {
+    dawnProcTable mockProcs;
+    dawnDevice mockDevice;
+    api.GetProcTableAndDevice(&mockProcs, &mockDevice);
+
+    // This SetCallback call cannot be ignored because it is done as soon as we start the server
+    EXPECT_CALL(api, OnDeviceSetErrorCallback(_, _, _)).Times(Exactly(1));
+    if (mIgnoreSetCallbackCalls) {
+        EXPECT_CALL(api, OnBuilderSetErrorCallback(_, _, _, _)).Times(AnyNumber());
+    }
+    EXPECT_CALL(api, DeviceTick(_)).Times(AnyNumber());
+
+    mS2cBuf = std::make_unique<utils::TerribleCommandBuffer>();
+    mC2sBuf = std::make_unique<utils::TerribleCommandBuffer>(mWireServer.get());
+
+    mWireServer.reset(new WireServer(mockDevice, mockProcs, mS2cBuf.get()));
+    mC2sBuf->SetHandler(mWireServer.get());
+
+    mWireClient.reset(new WireClient(mC2sBuf.get()));
+    mS2cBuf->SetHandler(mWireClient.get());
+
+    device = mWireClient->GetDevice();
+    dawnProcTable clientProcs = mWireClient->GetProcs();
+    dawnSetProcs(&clientProcs);
+
+    apiDevice = mockDevice;
+}
+
+void WireTest::TearDown() {
+    dawnSetProcs(nullptr);
+
+    // Derived classes should call the base TearDown() first. The client must
+    // be reset before any mocks are deleted.
+    // Incomplete client callbacks will be called on deletion, so the mocks
+    // cannot be null.
+    mWireClient = nullptr;
+}
+
+void WireTest::FlushClient() {
+    ASSERT_TRUE(mC2sBuf->Flush());
+}
+
+void WireTest::FlushServer() {
+    ASSERT_TRUE(mS2cBuf->Flush());
+}
diff --git a/src/tests/unittests/wire/WireTest.h b/src/tests/unittests/wire/WireTest.h
new file mode 100644
index 0000000..295de0a
--- /dev/null
+++ b/src/tests/unittests/wire/WireTest.h
@@ -0,0 +1,99 @@
+// 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 "gtest/gtest.h"
+#include "mock/mock_dawn.h"
+
+#include <memory>
+
+// Definition of a "Lambda predicate matcher" for GMock to allow checking deep structures
+// are passed correctly by the wire.
+
+// Helper templates to extract the argument type of a lambda.
+template <typename T>
+struct MatcherMethodArgument;
+
+template <typename Lambda, typename Arg>
+struct MatcherMethodArgument<bool (Lambda::*)(Arg) const> {
+    using Type = Arg;
+};
+
+template <typename Lambda>
+using MatcherLambdaArgument = typename MatcherMethodArgument<decltype(&Lambda::operator())>::Type;
+
+// The matcher itself, unfortunately it isn't able to return detailed information like other
+// matchers do.
+template <typename Lambda, typename Arg>
+class LambdaMatcherImpl : public testing::MatcherInterface<Arg> {
+  public:
+    explicit LambdaMatcherImpl(Lambda lambda) : mLambda(lambda) {
+    }
+
+    void DescribeTo(std::ostream* os) const override {
+        *os << "with a custom matcher";
+    }
+
+    bool MatchAndExplain(Arg value, testing::MatchResultListener* listener) const override {
+        if (!mLambda(value)) {
+            *listener << "which doesn't satisfy the custom predicate";
+            return false;
+        }
+        return true;
+    }
+
+  private:
+    Lambda mLambda;
+};
+
+// Use the MatchesLambda as follows:
+//
+//   EXPECT_CALL(foo, Bar(MatchesLambda([](ArgType arg) -> bool {
+//       return CheckPredicateOnArg(arg);
+//   })));
+template <typename Lambda>
+inline testing::Matcher<MatcherLambdaArgument<Lambda>> MatchesLambda(Lambda lambda) {
+    return MakeMatcher(new LambdaMatcherImpl<Lambda, MatcherLambdaArgument<Lambda>>(lambda));
+}
+
+namespace dawn_wire {
+    class WireClient;
+    class WireServer;
+}  // namespace dawn_wire
+
+namespace utils {
+    class TerribleCommandBuffer;
+}
+
+class WireTest : public testing::Test {
+  protected:
+    WireTest(bool ignoreSetCallbackCalls);
+    ~WireTest() override;
+
+    void SetUp() override;
+    void TearDown() override;
+    void FlushClient();
+    void FlushServer();
+
+    MockProcTable api;
+    dawnDevice apiDevice;
+    dawnDevice device;
+
+  private:
+    bool mIgnoreSetCallbackCalls = false;
+
+    std::unique_ptr<dawn_wire::WireServer> mWireServer;
+    std::unique_ptr<dawn_wire::WireClient> mWireClient;
+    std::unique_ptr<utils::TerribleCommandBuffer> mS2cBuf;
+    std::unique_ptr<utils::TerribleCommandBuffer> mC2sBuf;
+};