blob: 4bd216c01cae90996d4fc73c0b8eb2cc4ece13e6 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fidl/fuchsia.driver.test.logger/cpp/wire.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/ddk/binding.h>
#include <lib/ddk/driver.h>
#include <lib/fdio/directory.h>
#include <lib/fidl/coding.h>
#include <lib/fidl/cpp/message.h>
#include <string.h>
#include <threads.h>
#include <zircon/fidl.h>
#include <vector>
#include <fbl/algorithm.h>
#include <fbl/vector.h>
#include <zxtest/zxtest.h>
#include "coordinator_test_utils.h"
#include "devfs.h"
#include "driver_host.h"
#include "fdio.h"
#include "src/devices/bin/driver_manager/fake_driver_index.h"
constexpr char kDriverPath[] = "/pkg/driver/mock-device.so";
static CoordinatorConfig NullConfig() { return DefaultConfig(nullptr, nullptr, nullptr); }
namespace {
class FidlTransaction : public fidl::Transaction {
public:
FidlTransaction(FidlTransaction&&) = default;
explicit FidlTransaction(zx_txid_t transaction_id, zx::unowned_channel channel)
: txid_(transaction_id), channel_(channel) {}
std::unique_ptr<fidl::Transaction> TakeOwnership() override {
return std::make_unique<FidlTransaction>(std::move(*this));
}
zx_status_t Reply(fidl::OutgoingMessage* message, fidl::WriteOptions write_options) override {
ZX_ASSERT(txid_ != 0);
message->set_txid(txid_);
txid_ = 0;
message->Write(channel_, std::move(write_options));
return message->status();
}
void Close(zx_status_t epitaph) override { ZX_ASSERT(false); }
void InternalError(fidl::UnbindInfo info, fidl::ErrorOrigin origin) override {
detected_error_ = info;
}
~FidlTransaction() override = default;
const std::optional<fidl::UnbindInfo>& detected_error() const { return detected_error_; }
private:
zx_txid_t txid_;
zx::unowned_channel channel_;
std::optional<fidl::UnbindInfo> detected_error_;
};
class FakeDevice : public fidl::WireServer<fuchsia_device_manager::DeviceController> {
public:
FakeDevice(fidl::ServerEnd<fuchsia_driver_test_logger::Logger> test_output,
const fidl::StringView expected_driver = {})
: test_output_(std::move(test_output)), expected_driver_(expected_driver) {}
void BindDriver(BindDriverRequestView request, BindDriverCompleter::Sync& completer) override {
if (expected_driver_.empty() || expected_driver_.get() == request->driver_path.get()) {
bind_called_ = true;
completer.Reply(ZX_OK, test_output_.TakeChannel());
} else {
completer.Reply(ZX_ERR_INTERNAL, zx::channel{});
}
}
void ConnectProxy(ConnectProxyRequestView request,
ConnectProxyCompleter::Sync& _completer) override {}
void Init(InitRequestView request, InitCompleter::Sync& completer) override {}
void Suspend(SuspendRequestView request, SuspendCompleter::Sync& completer) override {}
void Resume(ResumeRequestView request, ResumeCompleter::Sync& completer) override {}
void Unbind(UnbindRequestView request, UnbindCompleter::Sync& completer) override {}
void CompleteRemoval(CompleteRemovalRequestView request,
CompleteRemovalCompleter::Sync& completer) override {}
void Open(OpenRequestView request, OpenCompleter::Sync& _completer) override {}
bool bind_called() { return bind_called_; }
private:
fidl::ServerEnd<fuchsia_driver_test_logger::Logger> test_output_;
const fidl::StringView expected_driver_;
bool bind_called_ = false;
};
// Reads a BindDriver request from remote, checks that it is for the expected
// driver, and then sends a ZX_OK response.
void CheckBindDriverReceived(
const fidl::ServerEnd<fuchsia_device_manager::DeviceController>& controller,
const fidl::StringView expected_driver) {
uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
zx_handle_t handles[ZX_CHANNEL_MAX_MSG_HANDLES];
fidl_channel_handle_metadata_t handle_metadata[ZX_CHANNEL_MAX_MSG_HANDLES];
fidl::IncomingMessage msg = fidl::MessageRead(
controller.channel(), fidl::ChannelMessageStorageView{
.bytes = fidl::BufferSpan(bytes, std::size(bytes)),
.handles = handles,
.handle_metadata = handle_metadata,
.handle_capacity = ZX_CHANNEL_MAX_MSG_HANDLES,
});
ASSERT_TRUE(msg.ok());
auto* header = msg.header();
FidlTransaction txn(header->txid, zx::unowned(controller.channel()));
FakeDevice fake(fidl::ServerEnd<fuchsia_driver_test_logger::Logger>(), expected_driver);
fidl::WireDispatch(
static_cast<fidl::WireServer<fuchsia_device_manager::DeviceController>*>(&fake),
std::move(msg), &txn);
ASSERT_FALSE(txn.detected_error());
ASSERT_TRUE(fake.bind_called());
}
} // namespace
TEST(MiscTestCase, InitCoreDevices) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
coordinator.InitCoreDevices(kSystemDriverPath);
}
TEST(MiscTestCase, DumpState) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
coordinator.InitCoreDevices(kSystemDriverPath);
constexpr int32_t kBufSize = 256;
char buf[kBufSize + 1] = {0};
zx::vmo vmo;
ASSERT_OK(zx::vmo::create(kBufSize, 0, &vmo));
VmoWriter writer(std::move(vmo));
coordinator.debug_dump()->DumpState(&writer);
ASSERT_EQ(writer.written(), writer.available());
ASSERT_LT(writer.written(), kBufSize);
ASSERT_GT(writer.written(), 0);
ASSERT_OK(writer.vmo().read(buf, 0, writer.written()));
ASSERT_NE(nullptr, strstr(buf, "[root]"));
}
TEST(MiscTestCase, LoadDriver) {
bool found_driver = false;
auto callback = [&found_driver](Driver* drv, const char* version) {
delete drv;
found_driver = true;
};
load_driver(nullptr, kDriverPath, callback);
ASSERT_TRUE(found_driver);
}
TEST(MiscTestCase, LoadDisabledDriver) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
ASSERT_OK(loop.StartThread("mock-boot-args"));
mock_boot_arguments::Server boot_args{{{"driver.mock_device.disable", "true"}}};
fidl::WireSyncClient<fuchsia_boot::Arguments> client;
boot_args.CreateClient(loop.dispatcher(), &client);
bool found_driver = false;
auto callback = [&found_driver](Driver* drv, const char* version) {
delete drv;
found_driver = true;
};
load_driver(&client, kDriverPath, callback);
ASSERT_FALSE(found_driver);
}
TEST(MiscTestCase, BindDrivers) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
coordinator.InitCoreDevices(kSystemDriverPath);
coordinator.set_running(true);
Driver* driver;
auto callback = [&coordinator, &driver](Driver* drv, const char* version) {
driver = drv;
return coordinator.DriverAdded(drv, version);
};
load_driver(nullptr, kDriverPath, callback);
loop.RunUntilIdle();
ASSERT_EQ(1, coordinator.drivers().size_slow());
ASSERT_EQ(driver, &coordinator.drivers().front());
}
TEST(MiscTestCase, BindDevices) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
ASSERT_NO_FATAL_FAILURE(InitializeCoordinator(&coordinator));
// Add the device.
auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>();
ASSERT_OK(controller_endpoints.status_value());
auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>();
ASSERT_OK(coordinator_endpoints.status_value());
fbl::RefPtr<Device> device;
auto status = coordinator.device_manager()->AddDevice(
coordinator.sys_device(), std::move(controller_endpoints->client),
std::move(coordinator_endpoints->server), nullptr /* props_data */, 0 /* props_count */,
nullptr /* str_props_data */, 0 /* str_props_count */, "mock-device", ZX_PROTOCOL_TEST,
{} /* driver_path */, {} /* args */, false /* skip_autobind */, false /* has_init */,
true /* always_init */, zx::vmo() /*inspect*/, zx::channel() /* client_remote */,
fidl::ClientEnd<fio::Directory>() /* outgoing_dir */, &device);
ASSERT_OK(status);
ASSERT_EQ(1, coordinator.device_manager()->devices().size_slow());
// Add the driver.
load_driver(nullptr, kDriverPath, fit::bind_member(&coordinator, &Coordinator::DriverAdded));
loop.RunUntilIdle();
ASSERT_FALSE(coordinator.drivers().is_empty());
// The device has no driver_host, so the init task should automatically complete.
ASSERT_TRUE(device->is_visible());
ASSERT_EQ(Device::State::kActive, device->state());
// Bind the device to a fake driver_host.
fbl::RefPtr<Device> dev = fbl::RefPtr(&coordinator.device_manager()->devices().front());
auto host = fbl::MakeRefCounted<DriverHost>(
&coordinator, fidl::ClientEnd<fuchsia_device_manager::DriverHostController>(),
fidl::ClientEnd<fuchsia_io::Directory>(), zx::process{});
dev->set_host(std::move(host));
status = coordinator.bind_driver_manager()->BindDevice(dev, kDriverPath, true /* new device */);
ASSERT_OK(status);
// Check the BindDriver request.
ASSERT_NO_FATAL_FAILURE(
CheckBindDriverReceived(controller_endpoints->server, fidl::StringView(kDriverPath)));
loop.RunUntilIdle();
// Reset the fake driver_host connection.
dev->set_host(nullptr);
coordinator_endpoints->client.reset();
controller_endpoints->server.reset();
loop.RunUntilIdle();
}
void CompareStrProperty(const fuchsia_device_manager::wire::DeviceStrProperty expected,
const StrProperty actual) {
ASSERT_STREQ(expected.key.get(), actual.key);
if (expected.value.is_int_value()) {
auto* value = std::get_if<StrPropValueType::Integer>(&actual.value);
ASSERT_TRUE(value);
ASSERT_EQ(expected.value.int_value(), *value);
} else if (expected.value.is_str_value()) {
auto* value = std::get_if<StrPropValueType::String>(&actual.value);
ASSERT_TRUE(value);
ASSERT_STREQ(expected.value.str_value(), *value);
} else if (expected.value.is_bool_value()) {
auto* value = std::get_if<StrPropValueType::Bool>(&actual.value);
ASSERT_TRUE(value);
ASSERT_EQ(expected.value.bool_value(), *value);
} else if (expected.value.is_enum_value()) {
auto* value = std::get_if<StrPropValueType::Enum>(&actual.value);
ASSERT_TRUE(value);
ASSERT_STREQ(expected.value.enum_value(), *value);
}
}
// Adds a device with the given properties to the device coordinator, then checks that the
// coordinator contains the device, and that its properties are correct.
void AddDeviceWithProperties(const fuchsia_device_manager::wire::DeviceProperty* props_data,
size_t props_count,
const fuchsia_device_manager::wire::DeviceStrProperty* str_props_data,
size_t str_props_count) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
ASSERT_NO_FATAL_FAILURE(InitializeCoordinator(&coordinator));
auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>();
ASSERT_OK(controller_endpoints.status_value());
auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>();
ASSERT_OK(coordinator_endpoints.status_value());
fbl::RefPtr<Device> device;
auto status = coordinator.device_manager()->AddDevice(
coordinator.sys_device(), std::move(controller_endpoints->client),
std::move(coordinator_endpoints->server), props_data, props_count, str_props_data,
str_props_count, "mock-device", ZX_PROTOCOL_TEST, {} /* driver_path */, {} /* args */,
false /* skip_autobind */, false /* has_init */, true /* always_init */,
zx::vmo() /*inspect*/, zx::channel() /* client_remote */,
fidl::ClientEnd<fio::Directory>() /* outgoing_dir */, &device);
ASSERT_OK(status);
// Check that the device has been added to the coordinator, with the correct properties.
ASSERT_EQ(1, coordinator.device_manager()->devices().size_slow());
const Device& dev = coordinator.device_manager()->devices().front();
ASSERT_EQ(dev.props().size(), props_count);
for (size_t i = 0; i < props_count; i++) {
ASSERT_EQ(dev.props()[i].id, props_data[i].id);
ASSERT_EQ(dev.props()[i].reserved, props_data[i].reserved);
ASSERT_EQ(dev.props()[i].value, props_data[i].value);
}
ASSERT_EQ(dev.str_props().size(), str_props_count);
for (size_t i = 0; i < str_props_count; i++) {
CompareStrProperty(str_props_data[i], dev.str_props()[i]);
}
controller_endpoints->server.reset();
coordinator_endpoints->client.reset();
loop.RunUntilIdle();
}
TEST(MiscTestCase, DeviceProperties) {
// No properties.
AddDeviceWithProperties(nullptr, 0, nullptr, 0);
// Multiple device properties. No string properties.
fuchsia_device_manager::wire::DeviceProperty props[] = {
fuchsia_device_manager::wire::DeviceProperty{1, 0, 1},
fuchsia_device_manager::wire::DeviceProperty{2, 0, 1},
};
AddDeviceWithProperties(props, std::size(props), nullptr, 0);
uint32_t int_val = 1000;
auto str_val = fidl::StringView::FromExternal("timberdoodle");
// Multiple device string properties. No device properties.
fuchsia_device_manager::wire::DeviceStrProperty str_props[] = {
fuchsia_device_manager::wire::DeviceStrProperty{
"snipe", fuchsia_device_manager::wire::PropertyValue::WithStrValue(
fidl::ObjectView<fidl::StringView>::FromExternal(&str_val))},
fuchsia_device_manager::wire::DeviceStrProperty{
"sandpiper", fuchsia_device_manager::wire::PropertyValue::WithIntValue(int_val)},
};
AddDeviceWithProperties(nullptr, 0, str_props, std::size(str_props));
// Multiple device properties and device string properties.
AddDeviceWithProperties(props, std::size(props), str_props, std::size(str_props));
}
TEST(MiscTestCase, InvalidStringProperties) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
InspectManager inspect_manager(loop.dispatcher());
Coordinator coordinator(NullConfig(), &inspect_manager, loop.dispatcher(), loop.dispatcher());
ASSERT_NO_FATAL_FAILURE(InitializeCoordinator(&coordinator));
auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>();
ASSERT_OK(controller_endpoints.status_value());
auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>();
ASSERT_OK(coordinator_endpoints.status_value());
// Create an invalid string with invalid UTF-8 characters.
const char kInvalidStr[] = {static_cast<char>(0xC0), 0};
auto str_val = fidl::StringView::FromExternal("ovenbird");
fuchsia_device_manager::wire::DeviceStrProperty str_props[] = {
fuchsia_device_manager::wire::DeviceStrProperty{
fidl::StringView::FromExternal(kInvalidStr, std::size(kInvalidStr)),
fuchsia_device_manager::wire::PropertyValue::WithStrValue(
fidl::ObjectView<fidl::StringView>::FromExternal(&str_val))},
};
fbl::RefPtr<Device> device;
auto status = coordinator.device_manager()->AddDevice(
coordinator.sys_device(), std::move(controller_endpoints->client),
std::move(coordinator_endpoints->server), nullptr /* props */, 0 /* props_count */, str_props,
std::size(str_props), "mock-device", ZX_PROTOCOL_TEST, {} /* driver_path */, {} /* args */,
false /* skip_autobind */, false /* has_init */, true /* always_init */,
zx::vmo() /*inspect*/, zx::channel() /* client_remote */,
fidl::ClientEnd<fio::Directory>() /* outgoing_dir */, &device);
ASSERT_EQ(ZX_ERR_INVALID_ARGS, status);
}
TEST(MiscTestCase, DeviceAlreadyBoundFromDriverIndex) {
constexpr const char kFakeDriverUrl[] = "#driver/mock-device.so";
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
async::Loop index_loop(&kAsyncLoopConfigNeverAttachToThread);
ASSERT_OK(index_loop.StartThread("test-thread"));
InspectManager inspect_manager(loop.dispatcher());
FakeDriverIndex fake(index_loop.dispatcher(),
[&](auto args) -> zx::status<FakeDriverIndex::MatchResult> {
return zx::ok(FakeDriverIndex::MatchResult{
.url = kFakeDriverUrl,
});
});
auto config = NullConfig();
config.driver_index = fidl::WireSharedClient<fdi::DriverIndex>(std::move(fake.Connect().value()),
loop.dispatcher());
Coordinator coordinator(std::move(config), &inspect_manager, loop.dispatcher(),
loop.dispatcher());
ASSERT_NO_FATAL_FAILURE(InitializeCoordinator(&coordinator));
// Add the device.
auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>();
ASSERT_OK(controller_endpoints.status_value());
auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>();
ASSERT_OK(coordinator_endpoints.status_value());
fbl::RefPtr<Device> device;
auto status = coordinator.device_manager()->AddDevice(
coordinator.sys_device(), std::move(controller_endpoints->client),
std::move(coordinator_endpoints->server), nullptr /* props_data */, 0 /* props_count */,
nullptr /* str_props_data */, 0 /* str_props_count */, "mock-device", ZX_PROTOCOL_TEST,
{} /* driver_path */, {} /* args */, true /* skip_autobind */, false /* has_init */,
true /* always_init */, zx::vmo() /*inspect*/, zx::channel() /* client_remote */,
fidl::ClientEnd<fio::Directory>() /*outgoing_dir*/, &device);
ASSERT_OK(status);
ASSERT_EQ(1, coordinator.device_manager()->devices().size_slow());
// Bind the device to a fake driver_host.
fbl::RefPtr<Device> dev = fbl::RefPtr(&coordinator.device_manager()->devices().front());
auto host = fbl::MakeRefCounted<DriverHost>(
&coordinator, fidl::ClientEnd<fuchsia_device_manager::DriverHostController>(),
fidl::ClientEnd<fuchsia_io::Directory>(), zx::process{});
dev->set_host(std::move(host));
status =
coordinator.bind_driver_manager()->BindDevice(dev, kFakeDriverUrl, true /* new device */);
ASSERT_OK(status);
loop.RunUntilIdle();
status =
coordinator.bind_driver_manager()->BindDevice(dev, kFakeDriverUrl, true /* new device */);
ASSERT_STATUS(status, ZX_ERR_ALREADY_BOUND);
loop.RunUntilIdle();
// Reset the fake driver_host connection.
dev->set_host(nullptr);
coordinator_endpoints->client.reset();
controller_endpoints->server.reset();
loop.RunUntilIdle();
}
TEST(MiscTestCase, AddDeviceGroup) {
async::Loop loop(&kAsyncLoopConfigNoAttachToCurrentThread);
async::Loop index_loop(&kAsyncLoopConfigNeverAttachToThread);
ASSERT_OK(index_loop.StartThread("test-thread"));
InspectManager inspect_manager(loop.dispatcher());
FakeDriverIndex fake_driver_index(index_loop.dispatcher(),
[&](auto args) -> zx::status<FakeDriverIndex::MatchResult> {
return zx::error(ZX_ERR_NOT_FOUND);
});
auto config = NullConfig();
config.driver_index = fidl::WireSharedClient<fdi::DriverIndex>(
std::move(fake_driver_index.Connect().value()), loop.dispatcher());
Coordinator coordinator(std::move(config), &inspect_manager, loop.dispatcher(),
loop.dispatcher());
ASSERT_NO_FATAL_FAILURE(InitializeCoordinator(&coordinator));
auto controller_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::DeviceController>();
ASSERT_OK(controller_endpoints.status_value());
auto coordinator_endpoints = fidl::CreateEndpoints<fuchsia_device_manager::Coordinator>();
ASSERT_OK(coordinator_endpoints.status_value());
// Add the device.
fbl::RefPtr<Device> device;
auto status = coordinator.device_manager()->AddDevice(
coordinator.sys_device(), std::move(controller_endpoints->client),
std::move(coordinator_endpoints->server), nullptr /* props_data */, 0 /* props_count */,
nullptr /* str_props_data */, 0 /* str_props_count */, "mock-device", ZX_PROTOCOL_TEST,
{} /* driver_path */, {} /* args */, true /* skip_autobind */, false /* has_init */,
true /* always_init */, zx::vmo() /*inspect*/, zx::channel() /* client_remote */,
fidl::ClientEnd<fio::Directory>() /*outgoing_dir*/, &device);
ASSERT_OK(status);
ASSERT_EQ(1, coordinator.device_manager()->devices().size_slow());
fidl::Arena allocator;
fidl::VectorView<fuchsia_device_manager::wire::DeviceStrProperty> str_props(allocator, 2);
str_props[0] = fuchsia_device_manager::wire::DeviceStrProperty{
fidl::StringView(allocator, "scoter"),
fuchsia_device_manager::wire::PropertyValue::WithStrValue(
allocator, fidl::StringView(allocator, "bufflehead"))};
str_props[1] = fuchsia_device_manager::wire::DeviceStrProperty{
fidl::StringView(allocator, "merganser"),
fuchsia_device_manager::wire::PropertyValue::WithIntValue(1000)};
fidl::VectorView<fuchsia_device_manager::wire::DeviceProperty> props(allocator, 2);
props[0] = fuchsia_device_manager::wire::DeviceProperty{1, 0, 1};
props[1] = fuchsia_device_manager::wire::DeviceProperty{2, 0, 1};
fidl::VectorView<fdf::wire::DeviceGroupProperty> node_properties(allocator, 2);
auto prop_vals_1 = fidl::VectorView<fdf::wire::NodePropertyValue>(allocator, 1);
prop_vals_1[0] = fdf::wire::NodePropertyValue::WithBoolValue(false);
node_properties[0] = fdf::wire::DeviceGroupProperty{
.key = fdf::wire::NodePropertyKey::WithIntValue(100),
.condition = fdf::wire::Condition::kAccept,
.values = prop_vals_1,
};
auto prop_vals_2 = fidl::VectorView<fdf::wire::NodePropertyValue>(allocator, 1);
prop_vals_2[0] = fdf::wire::NodePropertyValue::WithIntValue(20);
node_properties[1] = fdf::wire::DeviceGroupProperty{
.key = fdf::wire::NodePropertyKey::WithIntValue(5),
.condition = fdf::wire::Condition::kAccept,
.values = prop_vals_2,
};
fidl::VectorView<fdf::wire::DeviceGroupNode> fragments(allocator, 1);
fragments[0] = fdf::wire::DeviceGroupNode{
.name = fidl::StringView(allocator, "mallard"),
.properties = node_properties,
};
fidl::VectorView<fuchsia_device_manager::wire::DeviceMetadata> metadata(allocator, 0);
fuchsia_device_manager::wire::DeviceGroupDescriptor group_desc =
fuchsia_device_manager::wire::DeviceGroupDescriptor{
.props = props,
.str_props = str_props,
.fragments = fragments,
.spawn_colocated = false,
.metadata = metadata,
};
ASSERT_OK(coordinator.AddDeviceGroup(device, "group", group_desc));
loop.RunUntilIdle();
ZX_ASSERT(
coordinator.bind_driver_manager()->device_groups().count("/dev/sys/mock-device/group") != 0);
controller_endpoints->server.reset();
coordinator_endpoints->client.reset();
loop.RunUntilIdle();
}
int main(int argc, char** argv) { return RUN_ALL_TESTS(argc, argv); }