| // Copyright 2024 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 "src/devices/bin/driver_manager/tests/driver_runner_test_fixture.h" |
| |
| #include <fidl/fuchsia.component.decl/cpp/test_base.h> |
| #include <fidl/fuchsia.component/cpp/test_base.h> |
| #include <fidl/fuchsia.driver.framework/cpp/test_base.h> |
| #include <fidl/fuchsia.driver.host/cpp/test_base.h> |
| #include <fidl/fuchsia.io/cpp/test_base.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fit/defer.h> |
| #include <lib/inspect/cpp/reader.h> |
| #include <lib/inspect/testing/cpp/inspect.h> |
| |
| #include <bind/fuchsia/platform/cpp/bind.h> |
| |
| #include "src/devices/bin/driver_manager/testing/fake_driver_index.h" |
| #include "src/storage/lib/vfs/cpp/synchronous_vfs.h" |
| |
| namespace driver_runner { |
| |
| namespace fdata = fuchsia_data; |
| namespace fdfw = fuchsia_driver_framework; |
| namespace fdh = fuchsia_driver_host; |
| namespace fio = fuchsia_io; |
| namespace fprocess = fuchsia_process; |
| namespace frunner = fuchsia_component_runner; |
| namespace fcomponent = fuchsia_component; |
| namespace fdecl = fuchsia_component_decl; |
| |
| void CheckNode(const inspect::Hierarchy& hierarchy, const NodeChecker& checker) { |
| auto node = hierarchy.GetByPath(checker.node_name); |
| ASSERT_NE(nullptr, node); |
| |
| if (node->children().size() != checker.child_names.size()) { |
| printf("Mismatched children\n"); |
| for (size_t i = 0; i < node->children().size(); i++) { |
| printf("Child %ld : %s\n", i, node->children()[i].name().c_str()); |
| } |
| ASSERT_EQ(node->children().size(), checker.child_names.size()); |
| } |
| |
| for (auto& child : checker.child_names) { |
| auto ptr = node->GetByPath({child}); |
| if (!ptr) { |
| printf("Failed to find child %s\n", child.c_str()); |
| } |
| ASSERT_NE(nullptr, ptr); |
| } |
| |
| for (auto& property : checker.str_properties) { |
| auto prop = node->node().get_property<inspect::StringPropertyValue>(property.first); |
| if (!prop) { |
| printf("Failed to find property %s\n", property.first.c_str()); |
| } |
| ASSERT_EQ(property.second, prop->value()); |
| } |
| } |
| |
| zx::result<fidl::ClientEnd<fuchsia_ldsvc::Loader>> LoaderFactory() { |
| auto endpoints = fidl::CreateEndpoints<fuchsia_ldsvc::Loader>(); |
| if (endpoints.is_error()) { |
| return endpoints.take_error(); |
| } |
| return zx::ok(std::move(endpoints->client)); |
| } |
| |
| fdecl::ChildRef CreateChildRef(std::string name, std::string collection) { |
| return fdecl::ChildRef({.name = std::move(name), .collection = std::move(collection)}); |
| } |
| |
| class FakeContext : public fpromise::context { |
| public: |
| fpromise::executor* executor() const override { |
| EXPECT_TRUE(false); |
| return nullptr; |
| } |
| |
| fpromise::suspended_task suspend_task() override { |
| EXPECT_TRUE(false); |
| return fpromise::suspended_task(); |
| } |
| }; |
| |
| fidl::AnyTeardownObserver TeardownWatcher(size_t index, std::vector<size_t>& indices) { |
| return fidl::ObserveTeardown([&indices = indices, index] { indices.emplace_back(index); }); |
| } |
| |
| void TestRealm::AssertDestroyedChildren(const std::vector<fdecl::ChildRef>& expected) { |
| auto destroyed_children = destroyed_children_; |
| for (const auto& child : expected) { |
| auto it = std::find_if(destroyed_children.begin(), destroyed_children.end(), |
| [&child](const fdecl::ChildRef& other) { |
| return child.name() == other.name() && |
| child.collection() == other.collection(); |
| }); |
| ASSERT_NE(it, destroyed_children.end()); |
| destroyed_children.erase(it); |
| } |
| ASSERT_EQ(destroyed_children.size(), 0ul); |
| } |
| void TestRealm::CreateChild(CreateChildRequest& request, CreateChildCompleter::Sync& completer) { |
| handles_ = std::move(request.args().numbered_handles()); |
| auto offers = request.args().dynamic_offers(); |
| create_child_handler_( |
| std::move(request.collection()), std::move(request.decl()), |
| offers.has_value() ? std::move(offers.value()) : std::vector<fdecl::Offer>{}); |
| completer.Reply(fidl::Response<fuchsia_component::Realm::CreateChild>(fit::ok())); |
| } |
| void TestRealm::DestroyChild(DestroyChildRequest& request, DestroyChildCompleter::Sync& completer) { |
| destroyed_children_.push_back(std::move(request.child())); |
| completer.Reply(fidl::Response<fuchsia_component::Realm::DestroyChild>(fit::ok())); |
| } |
| void TestRealm::OpenExposedDir(OpenExposedDirRequest& request, |
| OpenExposedDirCompleter::Sync& completer) { |
| open_exposed_dir_handler_(std::move(request.child()), std::move(request.exposed_dir())); |
| completer.Reply(fidl::Response<fuchsia_component::Realm::OpenExposedDir>(fit::ok())); |
| } |
| class TestTransaction : public fidl::Transaction { |
| public: |
| explicit TestTransaction(bool close) : close_(close) {} |
| |
| private: |
| std::unique_ptr<Transaction> TakeOwnership() override { |
| return std::make_unique<TestTransaction>(close_); |
| } |
| |
| zx_status_t Reply(fidl::OutgoingMessage* message, fidl::WriteOptions write_options) override { |
| EXPECT_TRUE(false); |
| return ZX_OK; |
| } |
| |
| void Close(zx_status_t epitaph) override { |
| EXPECT_TRUE(close_) << "epitaph: " << zx_status_get_string(epitaph); |
| } |
| |
| bool close_; |
| }; |
| |
| fidl::ClientEnd<fuchsia_component::Realm> DriverRunnerTest::ConnectToRealm() { |
| zx::result realm_endpoints = fidl::CreateEndpoints<fcomponent::Realm>(); |
| ZX_ASSERT(ZX_OK == realm_endpoints.status_value()); |
| realm_binding_.emplace(dispatcher(), std::move(realm_endpoints->server), &realm_, |
| fidl::kIgnoreBindingClosure); |
| return std::move(realm_endpoints->client); |
| } |
| FakeDriverIndex DriverRunnerTest::CreateDriverIndex() { |
| return FakeDriverIndex(dispatcher(), [](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| if (args.name().get() == "second") { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| } |
| |
| if (args.name().get() == "dev-group-0") { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .spec = fdfw::CompositeParent({ |
| .composite = fdfw::CompositeInfo{{ |
| .spec = fdfw::CompositeNodeSpec{{ |
| .name = "test-group", |
| .parents = std::vector<fdfw::ParentSpec>(2), |
| }}, |
| .matched_driver = fdfw::CompositeDriverMatch{{ |
| .composite_driver = fdfw::CompositeDriverInfo{{ |
| .composite_name = "test-composite", |
| .driver_info = fdfw::DriverInfo{{ |
| .url = "fuchsia-boot:///#meta/composite-driver.cm", |
| .colocate = true, |
| .package_type = fdfw::DriverPackageType::kBoot, |
| }}, |
| }}, |
| .parent_names = {{"node-0", "node-1"}}, |
| .primary_parent_index = 1, |
| }}, |
| }}, |
| .index = 0, |
| })}); |
| } |
| |
| if (args.name().get() == "dev-group-1") { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .spec = fdfw::CompositeParent({ |
| .composite = fdfw::CompositeInfo{{ |
| .spec = fdfw::CompositeNodeSpec{{ |
| .name = "test-group", |
| .parents = std::vector<fdfw::ParentSpec>(2), |
| }}, |
| .matched_driver = fdfw::CompositeDriverMatch{{ |
| .composite_driver = fdfw::CompositeDriverInfo{{ |
| .composite_name = "test-composite", |
| .driver_info = fdfw::DriverInfo{{ |
| .url = "fuchsia-boot:///#meta/composite-driver.cm", |
| .colocate = true, |
| .package_type = fdfw::DriverPackageType::kBoot, |
| }}, |
| }}, |
| .parent_names = {{"node-0", "node-1"}}, |
| .primary_parent_index = 1, |
| }}, |
| }}, |
| .index = 1, |
| })}); |
| } |
| |
| return zx::error(ZX_ERR_NOT_FOUND); |
| }); |
| } |
| void DriverRunnerTest::SetupDriverRunner(FakeDriverIndex driver_index) { |
| driver_index_.emplace(std::move(driver_index)); |
| driver_runner_.emplace(ConnectToRealm(), driver_index_->Connect(), inspect(), &LoaderFactory, |
| dispatcher(), false); |
| SetupDevfs(); |
| } |
| void DriverRunnerTest::SetupDriverRunner() { SetupDriverRunner(CreateDriverIndex()); } |
| void DriverRunnerTest::PrepareRealmForDriverComponentStart(const std::string& name, |
| const std::string& url) { |
| realm().SetCreateChildHandler( |
| [name, url](fdecl::CollectionRef collection, fdecl::Child decl, auto offers) { |
| EXPECT_EQ("boot-drivers", collection.name()); |
| EXPECT_EQ(name, decl.name().value()); |
| EXPECT_EQ(url, decl.url().value()); |
| }); |
| } |
| void DriverRunnerTest::PrepareRealmForSecondDriverComponentStart() { |
| PrepareRealmForDriverComponentStart("dev.second", second_driver_url); |
| } |
| void DriverRunnerTest::PrepareRealmForStartDriverHost(bool use_next_vdso) { |
| constexpr std::string_view kDriverHostName = "driver-host-"; |
| std::string coll = "driver-hosts"; |
| realm().SetCreateChildHandler( |
| [coll, kDriverHostName, use_next_vdso](fdecl::CollectionRef collection, fdecl::Child decl, |
| auto offers) { |
| EXPECT_EQ(coll, collection.name()); |
| EXPECT_EQ(kDriverHostName, decl.name().value().substr(0, kDriverHostName.size())); |
| if (use_next_vdso) { |
| EXPECT_EQ("fuchsia-boot:///driver_host#meta/driver_host_next.cm", decl.url()); |
| } else { |
| EXPECT_EQ("fuchsia-boot:///driver_host#meta/driver_host.cm", decl.url()); |
| } |
| }); |
| realm().SetOpenExposedDirHandler( |
| [this, coll, kDriverHostName](fdecl::ChildRef child, auto exposed_dir) { |
| EXPECT_EQ(coll, child.collection().value_or("")); |
| EXPECT_EQ(kDriverHostName, child.name().substr(0, kDriverHostName.size())); |
| driver_host_dir_.Bind(std::move(exposed_dir)); |
| }); |
| driver_host_dir_.SetOpenHandler([this](const std::string& path, auto object) { |
| EXPECT_EQ(fidl::DiscoverableProtocolName<fdh::DriverHost>, path); |
| driver_host_binding_.emplace(dispatcher(), |
| fidl::ServerEnd<fdh::DriverHost>(object.TakeChannel()), |
| &driver_host_, fidl::kIgnoreBindingClosure); |
| }); |
| } |
| void DriverRunnerTest::StopDriverComponent( |
| fidl::ClientEnd<frunner::ComponentController> component) { |
| fidl::WireClient client(std::move(component), dispatcher()); |
| auto stop_result = client->Stop(); |
| ASSERT_EQ(ZX_OK, stop_result.status()); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| } |
| DriverRunnerTest::StartDriverResult DriverRunnerTest::StartDriver( |
| Driver driver, std::optional<StartDriverHandler> start_handler) { |
| std::unique_ptr<TestDriver> started_driver; |
| driver_host().SetStartHandler( |
| [&started_driver, dispatcher = dispatcher(), start_handler = std::move(start_handler)]( |
| fdfw::DriverStartArgs start_args, fidl::ServerEnd<fdh::Driver> driver) mutable { |
| started_driver = std::make_unique<TestDriver>( |
| dispatcher, std::move(start_args.node().value()), std::move(driver)); |
| start_args.node().reset(); |
| if (start_handler.has_value()) { |
| start_handler.value()(started_driver.get(), std::move(start_args)); |
| } |
| }); |
| |
| if (!driver.colocate) { |
| PrepareRealmForStartDriverHost(driver.use_next_vdso); |
| } |
| |
| fidl::Arena arena; |
| |
| fidl::VectorView<fdata::wire::DictionaryEntry> program_entries(arena, 4); |
| program_entries[0].key.Set(arena, "binary"); |
| program_entries[0].value = fdata::wire::DictionaryValue::WithStr(arena, driver.binary); |
| |
| program_entries[1].key.Set(arena, "colocate"); |
| program_entries[1].value = |
| fdata::wire::DictionaryValue::WithStr(arena, driver.colocate ? "true" : "false"); |
| |
| program_entries[2].key.Set(arena, "host_restart_on_crash"); |
| program_entries[2].value = |
| fdata::wire::DictionaryValue::WithStr(arena, driver.host_restart_on_crash ? "true" : "false"); |
| |
| program_entries[3].key.Set(arena, "use_next_vdso"); |
| program_entries[3].value = |
| fdata::wire::DictionaryValue::WithStr(arena, driver.use_next_vdso ? "true" : "false"); |
| |
| auto program_builder = fdata::wire::Dictionary::Builder(arena); |
| program_builder.entries(program_entries); |
| |
| auto outgoing_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>(); |
| EXPECT_EQ(ZX_OK, outgoing_endpoints.status_value()); |
| |
| auto start_info_builder = frunner::wire::ComponentStartInfo::Builder(arena); |
| start_info_builder.resolved_url(driver.url) |
| .program(program_builder.Build()) |
| .outgoing_dir(std::move(outgoing_endpoints->server)) |
| .ns({}) |
| .numbered_handles(realm().TakeHandles(arena)); |
| |
| auto controller_endpoints = fidl::CreateEndpoints<frunner::ComponentController>(); |
| EXPECT_EQ(ZX_OK, controller_endpoints.status_value()); |
| TestTransaction transaction(driver.close); |
| { |
| fidl::WireServer<frunner::ComponentRunner>::StartCompleter::Sync completer(&transaction); |
| fidl::WireRequest<frunner::ComponentRunner::Start> request{ |
| start_info_builder.Build(), std::move(controller_endpoints->server)}; |
| static_cast<fidl::WireServer<frunner::ComponentRunner>&>(driver_runner().runner_for_tests()) |
| .Start(&request, completer); |
| } |
| RunLoopUntilIdle(); |
| return {std::move(started_driver), std::move(controller_endpoints->client)}; |
| } |
| zx::result<DriverRunnerTest::StartDriverResult> DriverRunnerTest::StartRootDriver() { |
| realm().SetCreateChildHandler( |
| [](fdecl::CollectionRef collection, fdecl::Child decl, auto offers) { |
| EXPECT_EQ("boot-drivers", collection.name()); |
| EXPECT_EQ("dev", decl.name()); |
| EXPECT_EQ(root_driver_url, decl.url()); |
| }); |
| auto start = driver_runner().StartRootDriver(root_driver_url); |
| if (start.is_error()) { |
| return start.take_error(); |
| } |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| StartDriverHandler start_handler = [](TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| ValidateProgram(start_args.program(), root_driver_binary, "false", "false", "false"); |
| }; |
| return zx::ok(StartDriver( |
| { |
| .url = root_driver_url, |
| .binary = root_driver_binary, |
| }, |
| std::move(start_handler))); |
| } |
| void DriverRunnerTest::Unbind() { |
| if (driver_host_binding_.has_value()) { |
| driver_host_binding_.reset(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| } |
| } |
| void DriverRunnerTest::ValidateProgram(std::optional<::fuchsia_data::Dictionary>& program, |
| std::string_view binary, std::string_view colocate, |
| std::string_view host_restart_on_crash, |
| std::string_view use_next_vdso) { |
| ZX_ASSERT(program.has_value()); |
| auto& entries_opt = program.value().entries(); |
| ZX_ASSERT(entries_opt.has_value()); |
| auto& entries = entries_opt.value(); |
| EXPECT_EQ(4u, entries.size()); |
| EXPECT_EQ("binary", entries[0].key()); |
| EXPECT_EQ(std::string(binary), entries[0].value()->str().value()); |
| EXPECT_EQ("colocate", entries[1].key()); |
| EXPECT_EQ(std::string(colocate), entries[1].value()->str().value()); |
| EXPECT_EQ("host_restart_on_crash", entries[2].key()); |
| EXPECT_EQ(std::string(host_restart_on_crash), entries[2].value()->str().value()); |
| EXPECT_EQ("use_next_vdso", entries[3].key()); |
| EXPECT_EQ(std::string(use_next_vdso), entries[3].value()->str().value()); |
| } |
| void DriverRunnerTest::AssertNodeBound(const std::shared_ptr<CreatedChild>& child) { |
| auto& node = child->node; |
| ASSERT_TRUE(node.has_value() && node.value().is_valid()); |
| } |
| void DriverRunnerTest::AssertNodeNotBound(const std::shared_ptr<CreatedChild>& child) { |
| auto& node = child->node; |
| ASSERT_FALSE(node.has_value() && node.value().is_valid()); |
| } |
| void DriverRunnerTest::AssertNodeControllerBound(const std::shared_ptr<CreatedChild>& child) { |
| auto& controller = child->node_controller; |
| ASSERT_TRUE(controller.has_value() && controller.value().is_valid()); |
| } |
| void DriverRunnerTest::AssertNodeControllerNotBound(const std::shared_ptr<CreatedChild>& child) { |
| auto& controller = child->node_controller; |
| ASSERT_FALSE(controller.has_value() && controller.value().is_valid()); |
| } |
| inspect::Hierarchy DriverRunnerTest::Inspect() { |
| FakeContext context; |
| auto inspector = driver_runner().Inspect()(context).take_value(); |
| return inspect::ReadFromInspector(inspector)(context).take_value(); |
| } |
| void DriverRunnerTest::SetupDevfs() { driver_runner().root_node()->SetupDevfsForRootNode(devfs_); } |
| DriverRunnerTest::StartDriverResult DriverRunnerTest::StartSecondDriver(bool colocate, |
| bool host_restart_on_crash, |
| bool use_next_vdso) { |
| StartDriverHandler start_handler = [colocate, host_restart_on_crash, use_next_vdso]( |
| TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| if (!colocate) { |
| EXPECT_FALSE(start_args.symbols().has_value()); |
| } |
| |
| ValidateProgram(start_args.program(), second_driver_binary, colocate ? "true" : "false", |
| host_restart_on_crash ? "true" : "false", use_next_vdso ? "true" : "false"); |
| }; |
| return StartDriver( |
| { |
| .url = second_driver_url, |
| .binary = second_driver_binary, |
| .colocate = colocate, |
| .host_restart_on_crash = host_restart_on_crash, |
| .use_next_vdso = use_next_vdso, |
| }, |
| std::move(start_handler)); |
| } |
| void TestDirectory::Bind(fidl::ServerEnd<fio::Directory> request) { |
| bindings_.AddBinding(dispatcher_, std::move(request), this, fidl::kIgnoreBindingClosure); |
| } |
| void TestDirectory::Clone(CloneRequest& request, CloneCompleter::Sync& completer) { |
| EXPECT_EQ(fio::OpenFlags::kCloneSameRights, request.flags()); |
| fidl::ServerEnd<fio::Directory> dir(request.object().TakeChannel()); |
| Bind(std::move(dir)); |
| } |
| void TestDirectory::Open(OpenRequest& request, OpenCompleter::Sync& completer) { |
| open_handler_(request.path(), std::move(request.object())); |
| } |
| void TestDriver::Stop(StopCompleter::Sync& completer) { |
| stop_handler_(); |
| if (!dont_close_binding_in_stop_) { |
| driver_binding_.Close(ZX_OK); |
| } |
| } |
| std::shared_ptr<CreatedChild> TestDriver::AddChild(std::string_view child_name, bool owned, |
| bool expect_error, |
| const std::string& class_name) { |
| fidl::Arena arena; |
| auto devfs = fuchsia_driver_framework::wire::DevfsAddArgs::Builder(arena) |
| .connector_supports(fuchsia_device_fs::ConnectionType::kController) |
| .class_name(class_name) |
| .Build(); |
| auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) |
| .name(arena, child_name) |
| .devfs_args(devfs) |
| .Build(); |
| return AddChild(fidl::ToNatural(args), owned, expect_error); |
| } |
| std::shared_ptr<CreatedChild> TestDriver::AddChild(fdfw::NodeAddArgs child_args, bool owned, |
| bool expect_error, |
| fit::function<void()> on_bind) { |
| auto controller_endpoints = fidl::CreateEndpoints<fdfw::NodeController>(); |
| ZX_ASSERT(ZX_OK == controller_endpoints.status_value()); |
| |
| auto child_node_endpoints = fidl::CreateEndpoints<fdfw::Node>(); |
| ZX_ASSERT(ZX_OK == child_node_endpoints.status_value()); |
| |
| fidl::ServerEnd<fdfw::Node> child_node_server = {}; |
| if (owned) { |
| child_node_server = std::move(child_node_endpoints->server); |
| } |
| |
| node_ |
| ->AddChild({std::move(child_args), std::move(controller_endpoints->server), |
| std::move(child_node_server)}) |
| .Then([expect_error](fidl::Result<fdfw::Node::AddChild> result) { |
| if (expect_error) { |
| EXPECT_TRUE(result.is_error()); |
| } else { |
| EXPECT_TRUE(result.is_ok()); |
| } |
| }); |
| |
| class NodeEventHandler : public fidl::AsyncEventHandler<fdfw::Node> { |
| public: |
| explicit NodeEventHandler(std::shared_ptr<CreatedChild> child) : child_(std::move(child)) {} |
| void on_fidl_error(::fidl::UnbindInfo error) override { |
| child_->node.reset(); |
| delete this; |
| } |
| void handle_unknown_event(fidl::UnknownEventMetadata<fdfw::Node> metadata) override {} |
| |
| private: |
| std::shared_ptr<CreatedChild> child_; |
| }; |
| |
| class ControllerEventHandler : public fidl::AsyncEventHandler<fdfw::NodeController> { |
| public: |
| explicit ControllerEventHandler(std::shared_ptr<CreatedChild> child, |
| fit::function<void()> on_bind) |
| : child_(std::move(child)), on_bind_(std::move(on_bind)) {} |
| void OnBind() override { on_bind_(); } |
| void on_fidl_error(::fidl::UnbindInfo error) override { |
| child_->node_controller.reset(); |
| delete this; |
| } |
| void handle_unknown_event(fidl::UnknownEventMetadata<fdfw::NodeController> metadata) override {} |
| |
| private: |
| std::shared_ptr<CreatedChild> child_; |
| fit::function<void()> on_bind_; |
| }; |
| |
| std::shared_ptr<CreatedChild> child = std::make_shared<CreatedChild>(); |
| child->node_controller.emplace(std::move(controller_endpoints->client), dispatcher_, |
| new ControllerEventHandler(child, std::move(on_bind))); |
| if (owned) { |
| child->node.emplace(std::move(child_node_endpoints->client), dispatcher_, |
| new NodeEventHandler(child)); |
| } |
| |
| return child; |
| } |
| fidl::VectorView<fprocess::wire::HandleInfo> TestRealm::TakeHandles(fidl::AnyArena& arena) { |
| if (handles_.has_value()) { |
| return fidl::ToWire(arena, std::move(handles_)); |
| } |
| |
| return fidl::VectorView<fprocess::wire::HandleInfo>(arena, 0); |
| } |
| fidl::WireClient<fuchsia_device::Controller> DriverRunnerTest::ConnectToDeviceController( |
| std::string_view child_name) { |
| fs::SynchronousVfs vfs(dispatcher()); |
| zx::result dev_res = devfs().Connect(vfs); |
| EXPECT_EQ(dev_res.status_value(), ZX_OK); |
| fidl::WireClient<fuchsia_io::Directory> dev{std::move(*dev_res), dispatcher()}; |
| zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>(); |
| EXPECT_EQ(controller_endpoints.status_value(), ZX_OK); |
| |
| auto device_controller_path = std::string(child_name) + "/device_controller"; |
| EXPECT_EQ(dev->Open(fuchsia_io::OpenFlags::kNotDirectory, {}, |
| fidl::StringView::FromExternal(device_controller_path), |
| fidl::ServerEnd<fuchsia_io::Node>(controller_endpoints->server.TakeChannel())) |
| .status(), |
| ZX_OK); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| return fidl::WireClient<fuchsia_device::Controller>{std::move(controller_endpoints->client), |
| dispatcher()}; |
| } |
| |
| } // namespace driver_runner |