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