Test requestAdapter and requestDevice on the wire

Bug: dawn:689
Change-Id: I032cfcba755be241126dfa8447a38625d7183334
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/71523
Reviewed-by: Loko Kung <lokokung@google.com>
Commit-Queue: Austin Eng <enga@chromium.org>
diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn
index db919f4..4eb64a3 100644
--- a/src/tests/BUILD.gn
+++ b/src/tests/BUILD.gn
@@ -272,6 +272,7 @@
     "unittests/validation/VertexStateValidationTests.cpp",
     "unittests/validation/VideoViewsValidationTests.cpp",
     "unittests/validation/WriteBufferTests.cpp",
+    "unittests/wire/WireAdapterTests.cpp",
     "unittests/wire/WireArgumentTests.cpp",
     "unittests/wire/WireBasicTests.cpp",
     "unittests/wire/WireBufferMappingTests.cpp",
@@ -284,6 +285,7 @@
     "unittests/wire/WireInjectInstanceTests.cpp",
     "unittests/wire/WireInjectSwapChainTests.cpp",
     "unittests/wire/WireInjectTextureTests.cpp",
+    "unittests/wire/WireInstanceTests.cpp",
     "unittests/wire/WireMemoryTransferServiceTests.cpp",
     "unittests/wire/WireOptionalTests.cpp",
     "unittests/wire/WireQueueTests.cpp",
diff --git a/src/tests/unittests/wire/WireAdapterTests.cpp b/src/tests/unittests/wire/WireAdapterTests.cpp
new file mode 100644
index 0000000..48b0a7f
--- /dev/null
+++ b/src/tests/unittests/wire/WireAdapterTests.cpp
@@ -0,0 +1,330 @@
+// Copyright 2021 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/MockCallback.h"
+#include "tests/unittests/wire/WireTest.h"
+
+#include "dawn_wire/WireClient.h"
+#include "dawn_wire/WireServer.h"
+
+#include <webgpu/webgpu_cpp.h>
+#include <unordered_set>
+#include <vector>
+
+namespace {
+
+    using namespace testing;
+    using namespace dawn_wire;
+
+    class WireAdapterTests : public WireTest {
+      protected:
+        // Bootstrap the tests and create a fake adapter.
+        void SetUp() override {
+            WireTest::SetUp();
+
+            auto reservation = GetWireClient()->ReserveInstance();
+            instance = wgpu::Instance::Acquire(reservation.instance);
+
+            WGPUInstance apiInstance = api.GetNewInstance();
+            EXPECT_CALL(api, InstanceReference(apiInstance));
+            EXPECT_TRUE(GetWireServer()->InjectInstance(apiInstance, reservation.id,
+                                                        reservation.generation));
+
+            wgpu::RequestAdapterOptions options = {};
+            MockCallback<WGPURequestAdapterCallback> cb;
+            auto* userdata = cb.MakeUserdata(this);
+            instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+            // Expect the server to receive the message. Then, mock a fake reply.
+            apiAdapter = api.GetNewAdapter();
+            EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
+                .WillOnce(InvokeWithoutArgs([&]() {
+                    EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
+                        .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
+                            *properties = {};
+                            properties->name = "";
+                            properties->driverDescription = "";
+                        })));
+
+                    EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull()))
+                        .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+                            *limits = {};
+                            return true;
+                        })));
+
+                    EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
+                        .WillOnce(Return(0))
+                        .WillOnce(Return(0));
+                    api.CallInstanceRequestAdapterCallback(
+                        apiInstance, WGPURequestAdapterStatus_Success, apiAdapter, nullptr);
+                }));
+            FlushClient();
+
+            // Expect the callback in the client.
+            WGPUAdapter cAdapter;
+            EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, this))
+                .WillOnce(SaveArg<1>(&cAdapter));
+            FlushServer();
+
+            EXPECT_NE(cAdapter, nullptr);
+            adapter = wgpu::Adapter::Acquire(cAdapter);
+        }
+
+        void TearDown() override {
+            adapter = nullptr;
+            instance = nullptr;
+            WireTest::TearDown();
+        }
+
+        WGPUAdapter apiAdapter;
+        wgpu::Instance instance;
+        wgpu::Adapter adapter;
+    };
+
+    // Test that the DeviceDescriptor is passed from the client to the server.
+    TEST_F(WireAdapterTests, RequestDevicePassesDescriptor) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        // Test an empty descriptor
+        {
+            wgpu::DeviceDescriptor desc = {};
+            adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+            EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+                .WillOnce(WithArg<1>(Invoke([](const WGPUDeviceDescriptor* apiDesc) {
+                    EXPECT_EQ(apiDesc->label, nullptr);
+                    EXPECT_EQ(apiDesc->requiredFeaturesCount, 0u);
+                    EXPECT_EQ(apiDesc->requiredLimits, nullptr);
+                })));
+            FlushClient();
+        }
+
+        // Test a non-empty descriptor
+        {
+            wgpu::RequiredLimits limits = {};
+            limits.limits.maxStorageTexturesPerShaderStage = 5;
+
+            std::vector<wgpu::FeatureName> features = {wgpu::FeatureName::TextureCompressionETC2,
+                                                       wgpu::FeatureName::TextureCompressionASTC};
+
+            wgpu::DeviceDescriptor desc = {};
+            desc.label = "hello device";
+            desc.requiredLimits = &limits;
+            desc.requiredFeaturesCount = features.size();
+            desc.requiredFeatures = features.data();
+
+            adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+            EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](const WGPUDeviceDescriptor* apiDesc) {
+                    EXPECT_STREQ(apiDesc->label, desc.label);
+
+                    ASSERT_EQ(apiDesc->requiredFeaturesCount, features.size());
+                    for (uint32_t i = 0; i < features.size(); ++i) {
+                        EXPECT_EQ(apiDesc->requiredFeatures[i],
+                                  static_cast<WGPUFeatureName>(features[i]));
+                    }
+
+                    ASSERT_NE(apiDesc->requiredLimits, nullptr);
+                    EXPECT_EQ(apiDesc->requiredLimits->nextInChain, nullptr);
+                    EXPECT_EQ(apiDesc->requiredLimits->limits.maxStorageTexturesPerShaderStage,
+                              limits.limits.maxStorageTexturesPerShaderStage);
+                })));
+            FlushClient();
+        }
+
+        // Delete the adapter now, or it'll call the mock callback after it's deleted.
+        adapter = nullptr;
+    }
+
+    // Test that RequestDevice forwards the device information to the client.
+    TEST_F(WireAdapterTests, RequestDeviceSuccess) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        wgpu::SupportedLimits fakeLimits = {};
+        fakeLimits.limits.maxTextureDimension1D = 433;
+        fakeLimits.limits.maxVertexAttributes = 1243;
+
+        std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+            wgpu::FeatureName::Depth32FloatStencil8,
+            wgpu::FeatureName::TextureCompressionBC,
+        };
+
+        wgpu::DeviceDescriptor desc = {};
+        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+        // Expect the server to receive the message. Then, mock a fake reply.
+        WGPUDevice apiDevice = api.GetNewDevice();
+        EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                // Set on device creation to forward callbacks to the client.
+                EXPECT_CALL(api,
+                            OnDeviceSetUncapturedErrorCallback(apiDevice, NotNull(), NotNull()))
+                    .Times(1);
+                EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, NotNull(), NotNull()))
+                    .Times(1);
+                EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, NotNull(), NotNull()))
+                    .Times(1);
+
+                EXPECT_CALL(api, DeviceGetLimits(apiDevice, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+                        *reinterpret_cast<wgpu::SupportedLimits*>(limits) = fakeLimits;
+                        return true;
+                    })));
+
+                EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
+                    .WillOnce(Return(fakeFeatures.size()));
+
+                EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+                        for (wgpu::FeatureName feature : fakeFeatures) {
+                            *(features++) = static_cast<WGPUFeatureName>(feature);
+                        }
+                        return fakeFeatures.size();
+                    })));
+
+                api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
+                                                     apiDevice, nullptr);
+            }));
+        FlushClient();
+
+        // Expect the callback in the client and all the device information to match.
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Success, NotNull(), nullptr, this))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUDevice cDevice) {
+                wgpu::Device device = wgpu::Device::Acquire(cDevice);
+
+                wgpu::SupportedLimits limits;
+                EXPECT_TRUE(device.GetLimits(&limits));
+                EXPECT_EQ(limits.limits.maxTextureDimension1D,
+                          fakeLimits.limits.maxTextureDimension1D);
+                EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
+
+                std::vector<wgpu::FeatureName> features;
+                features.resize(device.EnumerateFeatures(nullptr));
+                ASSERT_EQ(features.size(), fakeFeatures.size());
+                EXPECT_EQ(device.EnumerateFeatures(&features[0]), features.size());
+
+                std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
+                for (wgpu::FeatureName feature : features) {
+                    EXPECT_EQ(featureSet.erase(feature), 1u);
+                }
+            })));
+        FlushServer();
+
+        // Cleared when the device is destroyed.
+        EXPECT_CALL(api, OnDeviceSetUncapturedErrorCallback(apiDevice, nullptr, nullptr)).Times(1);
+        EXPECT_CALL(api, OnDeviceSetLoggingCallback(apiDevice, nullptr, nullptr)).Times(1);
+        EXPECT_CALL(api, OnDeviceSetDeviceLostCallback(apiDevice, nullptr, nullptr)).Times(1);
+    }
+
+    // Test that features requested that the implementation supports, but not the
+    // wire reject the callback.
+    TEST_F(WireAdapterTests, RequestFeatureUnsupportedByWire) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+            // Some value that is not a valid feature
+            static_cast<wgpu::FeatureName>(-2),
+            wgpu::FeatureName::TextureCompressionASTC,
+        };
+
+        wgpu::DeviceDescriptor desc = {};
+        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+        // Expect the server to receive the message. Then, mock a fake reply.
+        // The reply contains features that the device implementation supports, but the
+        // wire does not.
+        WGPUDevice apiDevice = api.GetNewDevice();
+        EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, nullptr))
+                    .WillOnce(Return(fakeFeatures.size()));
+
+                EXPECT_CALL(api, DeviceEnumerateFeatures(apiDevice, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+                        for (wgpu::FeatureName feature : fakeFeatures) {
+                            *(features++) = static_cast<WGPUFeatureName>(feature);
+                        }
+                        return fakeFeatures.size();
+                    })));
+
+                // The device was actually created, but the wire didn't support its features.
+                // Expect it to be released.
+                EXPECT_CALL(api, DeviceRelease(apiDevice));
+
+                // Fake successful creation. The client still receives a failure due to
+                // unsupported features.
+                api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Success,
+                                                     apiDevice, nullptr);
+            }));
+        FlushClient();
+
+        // Expect an error callback since the feature is not supported.
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Error, nullptr, NotNull(), this)).Times(1);
+        FlushServer();
+    }
+
+    // Test that RequestDevice errors forward to the client.
+    TEST_F(WireAdapterTests, RequestDeviceError) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        wgpu::DeviceDescriptor desc = {};
+        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+        // Expect the server to receive the message. Then, mock an error.
+        EXPECT_CALL(api, OnAdapterRequestDevice(apiAdapter, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                api.CallAdapterRequestDeviceCallback(apiAdapter, WGPURequestDeviceStatus_Error,
+                                                     nullptr, "Request device failed");
+            }));
+        FlushClient();
+
+        // Expect the callback in the client.
+        EXPECT_CALL(
+            cb, Call(WGPURequestDeviceStatus_Error, nullptr, StrEq("Request device failed"), this))
+            .Times(1);
+        FlushServer();
+    }
+
+    // Test that RequestDevice receives unknown status if the adapter is deleted
+    // before the callback happens.
+    TEST_F(WireAdapterTests, RequestDeviceAdapterDestroyedBeforeCallback) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        wgpu::DeviceDescriptor desc = {};
+        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+        adapter = nullptr;
+    }
+
+    // Test that RequestDevice receives unknown status if the wire is disconnected
+    // before the callback happens.
+    TEST_F(WireAdapterTests, RequestDeviceWireDisconnectedBeforeCallback) {
+        MockCallback<WGPURequestDeviceCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        wgpu::DeviceDescriptor desc = {};
+        adapter.RequestDevice(&desc, cb.Callback(), userdata);
+
+        EXPECT_CALL(cb, Call(WGPURequestDeviceStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+        GetWireClient()->Disconnect();
+    }
+
+}  // anonymous namespace
\ No newline at end of file
diff --git a/src/tests/unittests/wire/WireInstanceTests.cpp b/src/tests/unittests/wire/WireInstanceTests.cpp
new file mode 100644
index 0000000..6ef0a4c
--- /dev/null
+++ b/src/tests/unittests/wire/WireInstanceTests.cpp
@@ -0,0 +1,286 @@
+// Copyright 2021 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/MockCallback.h"
+#include "tests/unittests/wire/WireTest.h"
+
+#include "dawn_wire/WireClient.h"
+#include "dawn_wire/WireServer.h"
+
+#include <webgpu/webgpu_cpp.h>
+#include <unordered_set>
+#include <vector>
+
+namespace {
+
+    using namespace testing;
+    using namespace dawn_wire;
+
+    class WireInstanceBasicTest : public WireTest {};
+    class WireInstanceTests : public WireTest {
+      protected:
+        void SetUp() override {
+            WireTest::SetUp();
+
+            auto reservation = GetWireClient()->ReserveInstance();
+            instance = wgpu::Instance::Acquire(reservation.instance);
+
+            apiInstance = api.GetNewInstance();
+            EXPECT_CALL(api, InstanceReference(apiInstance));
+            EXPECT_TRUE(GetWireServer()->InjectInstance(apiInstance, reservation.id,
+                                                        reservation.generation));
+        }
+
+        void TearDown() override {
+            instance = nullptr;
+            WireTest::TearDown();
+        }
+
+        wgpu::Instance instance;
+        WGPUInstance apiInstance;
+    };
+
+    // Test that an Instance can be reserved and injected into the wire.
+    TEST_F(WireInstanceBasicTest, ReserveAndInject) {
+        auto reservation = GetWireClient()->ReserveInstance();
+        wgpu::Instance instance = wgpu::Instance::Acquire(reservation.instance);
+
+        WGPUInstance apiInstance = api.GetNewInstance();
+        EXPECT_CALL(api, InstanceReference(apiInstance));
+        EXPECT_TRUE(
+            GetWireServer()->InjectInstance(apiInstance, reservation.id, reservation.generation));
+
+        instance = nullptr;
+
+        EXPECT_CALL(api, InstanceRelease(apiInstance));
+        FlushClient();
+    }
+
+    // Test that RequestAdapterOptions are passed from the client to the server.
+    TEST_F(WireInstanceTests, RequestAdapterPassesOptions) {
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+
+        for (wgpu::PowerPreference powerPreference :
+             {wgpu::PowerPreference::LowPower, wgpu::PowerPreference::HighPerformance}) {
+            wgpu::RequestAdapterOptions options = {};
+            options.powerPreference = powerPreference;
+
+            instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+            EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
+                .WillOnce(WithArg<1>(Invoke([&](const WGPURequestAdapterOptions* apiOptions) {
+                    EXPECT_EQ(apiOptions->powerPreference,
+                              static_cast<WGPUPowerPreference>(options.powerPreference));
+                    EXPECT_EQ(apiOptions->forceFallbackAdapter, options.forceFallbackAdapter);
+                })));
+            FlushClient();
+        }
+
+        // Delete the instance now, or it'll call the mock callback after it's deleted.
+        instance = nullptr;
+    }
+
+    // Test that RequestAdapter forwards the adapter information to the client.
+    TEST_F(WireInstanceTests, RequestAdapterSuccess) {
+        wgpu::RequestAdapterOptions options = {};
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+        instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+        wgpu::AdapterProperties fakeProperties = {};
+        fakeProperties.vendorID = 0x134;
+        fakeProperties.deviceID = 0x918;
+        fakeProperties.name = "fake adapter";
+        fakeProperties.driverDescription = "hello world";
+        fakeProperties.backendType = wgpu::BackendType::D3D12;
+        fakeProperties.adapterType = wgpu::AdapterType::IntegratedGPU;
+
+        wgpu::SupportedLimits fakeLimits = {};
+        fakeLimits.limits.maxTextureDimension1D = 433;
+        fakeLimits.limits.maxVertexAttributes = 1243;
+
+        std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+            wgpu::FeatureName::Depth32FloatStencil8,
+            wgpu::FeatureName::TextureCompressionBC,
+        };
+
+        // Expect the server to receive the message. Then, mock a fake reply.
+        WGPUAdapter apiAdapter = api.GetNewAdapter();
+        EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
+                    .WillOnce(SetArgPointee<1>(
+                        *reinterpret_cast<WGPUAdapterProperties*>(&fakeProperties)));
+
+                EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+                        *reinterpret_cast<wgpu::SupportedLimits*>(limits) = fakeLimits;
+                        return true;
+                    })));
+
+                EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
+                    .WillOnce(Return(fakeFeatures.size()));
+
+                EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+                        for (wgpu::FeatureName feature : fakeFeatures) {
+                            *(features++) = static_cast<WGPUFeatureName>(feature);
+                        }
+                        return fakeFeatures.size();
+                    })));
+                api.CallInstanceRequestAdapterCallback(
+                    apiInstance, WGPURequestAdapterStatus_Success, apiAdapter, nullptr);
+            }));
+        FlushClient();
+
+        // Expect the callback in the client and all the adapter information to match.
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, this))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter cAdapter) {
+                wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+
+                wgpu::AdapterProperties properties;
+                adapter.GetProperties(&properties);
+                EXPECT_EQ(properties.vendorID, fakeProperties.vendorID);
+                EXPECT_EQ(properties.deviceID, fakeProperties.deviceID);
+                EXPECT_STREQ(properties.name, fakeProperties.name);
+                EXPECT_STREQ(properties.driverDescription, fakeProperties.driverDescription);
+                EXPECT_EQ(properties.backendType, fakeProperties.backendType);
+                EXPECT_EQ(properties.adapterType, fakeProperties.adapterType);
+
+                wgpu::SupportedLimits limits;
+                EXPECT_TRUE(adapter.GetLimits(&limits));
+                EXPECT_EQ(limits.limits.maxTextureDimension1D,
+                          fakeLimits.limits.maxTextureDimension1D);
+                EXPECT_EQ(limits.limits.maxVertexAttributes, fakeLimits.limits.maxVertexAttributes);
+
+                std::vector<wgpu::FeatureName> features;
+                features.resize(adapter.EnumerateFeatures(nullptr));
+                ASSERT_EQ(features.size(), fakeFeatures.size());
+                EXPECT_EQ(adapter.EnumerateFeatures(&features[0]), features.size());
+
+                std::unordered_set<wgpu::FeatureName> featureSet(fakeFeatures);
+                for (wgpu::FeatureName feature : features) {
+                    EXPECT_EQ(featureSet.erase(feature), 1u);
+                }
+            })));
+        FlushServer();
+    }
+
+    // Test that features returned by the implementation that aren't supported
+    // in the wire are not exposed.
+    TEST_F(WireInstanceTests, RequestAdapterWireLacksFeatureSupport) {
+        wgpu::RequestAdapterOptions options = {};
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+        instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+        std::initializer_list<wgpu::FeatureName> fakeFeatures = {
+            wgpu::FeatureName::Depth24UnormStencil8,
+            // Some value that is not a valid feature
+            static_cast<wgpu::FeatureName>(-2),
+        };
+
+        // Expect the server to receive the message. Then, mock a fake reply.
+        WGPUAdapter apiAdapter = api.GetNewAdapter();
+        EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                EXPECT_CALL(api, AdapterGetProperties(apiAdapter, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUAdapterProperties* properties) {
+                        *properties = {};
+                        properties->name = "";
+                        properties->driverDescription = "";
+                    })));
+
+                EXPECT_CALL(api, AdapterGetLimits(apiAdapter, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUSupportedLimits* limits) {
+                        *limits = {};
+                        return true;
+                    })));
+
+                EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, nullptr))
+                    .WillOnce(Return(fakeFeatures.size()));
+
+                EXPECT_CALL(api, AdapterEnumerateFeatures(apiAdapter, NotNull()))
+                    .WillOnce(WithArg<1>(Invoke([&](WGPUFeatureName* features) {
+                        for (wgpu::FeatureName feature : fakeFeatures) {
+                            *(features++) = static_cast<WGPUFeatureName>(feature);
+                        }
+                        return fakeFeatures.size();
+                    })));
+                api.CallInstanceRequestAdapterCallback(
+                    apiInstance, WGPURequestAdapterStatus_Success, apiAdapter, nullptr);
+            }));
+        FlushClient();
+
+        // Expect the callback in the client and all the adapter information to match.
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Success, NotNull(), nullptr, this))
+            .WillOnce(WithArg<1>(Invoke([&](WGPUAdapter cAdapter) {
+                wgpu::Adapter adapter = wgpu::Adapter::Acquire(cAdapter);
+
+                wgpu::FeatureName feature;
+                ASSERT_EQ(adapter.EnumerateFeatures(nullptr), 1u);
+                adapter.EnumerateFeatures(&feature);
+
+                EXPECT_EQ(feature, wgpu::FeatureName::Depth24UnormStencil8);
+            })));
+        FlushServer();
+    }
+
+    // Test that RequestAdapter errors forward to the client.
+    TEST_F(WireInstanceTests, RequestAdapterError) {
+        wgpu::RequestAdapterOptions options = {};
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+        instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+        // Expect the server to receive the message. Then, mock an error.
+        EXPECT_CALL(api, OnInstanceRequestAdapter(apiInstance, NotNull(), NotNull(), NotNull()))
+            .WillOnce(InvokeWithoutArgs([&]() {
+                api.CallInstanceRequestAdapterCallback(apiInstance, WGPURequestAdapterStatus_Error,
+                                                       nullptr, "Some error");
+            }));
+        FlushClient();
+
+        // Expect the callback in the client.
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Error, nullptr, StrEq("Some error"), this))
+            .Times(1);
+        FlushServer();
+    }
+
+    // Test that RequestAdapter receives unknown status if the instance is deleted
+    // before the callback happens.
+    TEST_F(WireInstanceTests, RequestAdapterInstanceDestroyedBeforeCallback) {
+        wgpu::RequestAdapterOptions options = {};
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+        instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+        instance = nullptr;
+    }
+
+    // Test that RequestAdapter receives unknown status if the wire is disconnected
+    // before the callback happens.
+    TEST_F(WireInstanceTests, RequestAdapterWireDisconnectBeforeCallback) {
+        wgpu::RequestAdapterOptions options = {};
+        MockCallback<WGPURequestAdapterCallback> cb;
+        auto* userdata = cb.MakeUserdata(this);
+        instance.RequestAdapter(&options, cb.Callback(), userdata);
+
+        EXPECT_CALL(cb, Call(WGPURequestAdapterStatus_Unknown, nullptr, NotNull(), this)).Times(1);
+        GetWireClient()->Disconnect();
+    }
+
+}  // anonymous namespace
\ No newline at end of file