blob: 0696e82ffb972f697a87ad5db7624ad7a9ad4a15 [file] [log] [blame]
// Copyright 2020 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/driver_runner.h"
#include <fuchsia/driver/framework/cpp/fidl_test_base.h>
#include <fuchsia/io/cpp/fidl_test_base.h>
#include <fuchsia/sys2/cpp/fidl_test_base.h>
#include <lib/async-loop/cpp/loop.h>
#include <lib/async-loop/default.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fit/defer.h>
#include <lib/gtest/test_loop_fixture.h>
#include <lib/inspect/cpp/reader.h>
#include <lib/inspect/testing/cpp/inspect.h>
#include <lib/sys/cpp/testing/component_context_provider.h>
#include <gtest/gtest.h>
#include "src/devices/bin/driver_manager/fake_driver_index.h"
namespace fdata = fuchsia_data;
namespace fdf = fuchsia::driver::framework;
namespace fio = fuchsia::io;
namespace frunner = fuchsia_component_runner;
namespace fsys = fuchsia::sys2;
using namespace testing;
using namespace inspect::testing;
class fake_context : public fit::context {
public:
fit::executor* executor() const override {
EXPECT_TRUE(false);
return nullptr;
}
fit::suspended_task suspend_task() override {
EXPECT_TRUE(false);
return fit::suspended_task();
}
};
class UnbindWatcher : public fidl::WireAsyncEventHandler<frunner::ComponentController> {
public:
UnbindWatcher(size_t index, std::vector<size_t>& indices) : index_(index), indices_(indices) {}
void Unbound(fidl::UnbindInfo) override { indices_.emplace_back(index_); }
private:
const size_t index_;
std::vector<size_t>& indices_;
};
class TestRealm : public fsys::testing::Realm_TestBase {
public:
using BindChildHandler =
fit::function<void(fsys::ChildRef child, fidl::InterfaceRequest<fio::Directory> exposed_dir)>;
using CreateChildHandler =
fit::function<void(fsys::CollectionRef collection, fsys::ChildDecl decl)>;
using DestroyChildHandler = fit::function<void(fsys::ChildRef child)>;
void SetBindChildHandler(BindChildHandler bind_child_handler) {
bind_child_handler_ = std::move(bind_child_handler);
}
void SetCreateChildHandler(CreateChildHandler create_child_handler) {
create_child_handler_ = std::move(create_child_handler);
}
void SetDestroyChildHandler(DestroyChildHandler destroy_child_handler) {
destroy_child_handler_ = std::move(destroy_child_handler);
}
private:
void BindChild(fsys::ChildRef child, fidl::InterfaceRequest<fio::Directory> exposed_dir,
BindChildCallback callback) override {
bind_child_handler_(std::move(child), std::move(exposed_dir));
callback(fsys::Realm_BindChild_Result(fit::ok()));
}
void CreateChild(fsys::CollectionRef collection, fsys::ChildDecl decl,
CreateChildCallback callback) override {
create_child_handler_(std::move(collection), std::move(decl));
callback(fsys::Realm_CreateChild_Result(fit::ok()));
}
void DestroyChild(fsys::ChildRef child, DestroyChildCallback callback) override {
destroy_child_handler_(std::move(child));
callback(fsys::Realm_DestroyChild_Result(fit::ok()));
}
void NotImplemented_(const std::string& name) override {
printf("Not implemented: Realm::%s\n", name.data());
}
BindChildHandler bind_child_handler_;
CreateChildHandler create_child_handler_;
DestroyChildHandler destroy_child_handler_ = [](auto) {};
};
class TestDirectory : public fio::testing::Directory_TestBase {
public:
using OpenHandler =
fit::function<void(std::string path, fidl::InterfaceRequest<fio::Node> object)>;
void SetOpenHandler(OpenHandler open_handler) { open_handler_ = std::move(open_handler); }
private:
void Clone(uint32_t flags, fidl::InterfaceRequest<fio::Node> object) override {
EXPECT_EQ(ZX_FS_FLAG_CLONE_SAME_RIGHTS, flags);
}
void Open(uint32_t flags, uint32_t mode, std::string path,
fidl::InterfaceRequest<fio::Node> object) override {
open_handler_(std::move(path), std::move(object));
}
void NotImplemented_(const std::string& name) override {
printf("Not implemented: Directory::%s\n", name.data());
}
OpenHandler open_handler_;
};
class TestDriverHost : public fdf::testing::DriverHost_TestBase {
public:
using StartHandler = fit::function<void(fdf::DriverStartArgs start_args,
fidl::InterfaceRequest<fdf::Driver> driver)>;
void SetStartHandler(StartHandler start_handler) { start_handler_ = std::move(start_handler); }
private:
void Start(fdf::DriverStartArgs start_args, fidl::InterfaceRequest<fdf::Driver> driver) override {
start_handler_(std::move(start_args), std::move(driver));
}
void NotImplemented_(const std::string& name) override {
printf("Not implemented: DriverHost::%s\n", name.data());
}
StartHandler start_handler_;
};
class TestTransaction : public fidl::Transaction {
public:
TestTransaction(bool close) : close_(close) {}
private:
std::unique_ptr<Transaction> TakeOwnership() override {
EXPECT_TRUE(false);
return nullptr;
}
zx_status_t Reply(fidl::OutgoingMessage* message) override {
EXPECT_TRUE(false);
return ZX_OK;
}
void Close(zx_status_t epitaph) override { EXPECT_TRUE(close_); }
bool close_;
};
struct Driver {
std::string url;
std::string binary;
bool colocate = false;
bool close = false;
};
class DriverRunnerTest : public gtest::TestLoopFixture {
public:
void SetUp() override {
TestLoopFixture::SetUp();
fidl::InterfaceRequestHandler<fsys::Realm> handler = [this](auto request) {
EXPECT_EQ(ZX_OK, realm_binding_.Bind(std::move(request), loop_.dispatcher()));
};
ASSERT_EQ(ZX_OK, provider_.context()->outgoing()->AddPublicService(std::move(handler)));
}
protected:
inspect::Inspector* inspector() { return &inspector_; }
async::Loop& loop() { return loop_; }
TestRealm& realm() { return realm_; }
TestDirectory& driver_host_dir() { return driver_host_dir_; }
TestDriverHost& driver_host() { return driver_host_; }
fidl::Binding<fio::Directory>& driver_dir_binding() { return driver_dir_binding_; }
zx::channel ConnectToRealm() {
fsys::RealmPtr realm;
provider_.ConnectToPublicService(realm.NewRequest(loop_.dispatcher()));
return realm.Unbind().TakeChannel();
}
FakeDriverIndex CreateDriverIndex() {
return FakeDriverIndex(loop().dispatcher(),
[](auto args) -> zx::status<FakeDriverIndex::MatchResult> {
std::string_view name(args.name().data(), args.name().size());
if (name == "second") {
return zx::ok(FakeDriverIndex::MatchResult{
.url = "fuchsia-boot:///#meta/second-driver.cm",
.matched_args = std::move(args),
});
} else {
return zx::error(ZX_ERR_NOT_FOUND);
}
});
}
void StartDriverHost(std::string coll, std::string name) {
realm().SetCreateChildHandler(
[coll, name](fsys::CollectionRef collection, fsys::ChildDecl decl) {
EXPECT_EQ(coll, collection.name);
EXPECT_EQ(name, decl.name());
EXPECT_EQ("fuchsia-boot:///#meta/driver_host2.cm", decl.url());
});
realm().SetBindChildHandler([this, coll, name](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(coll, child.collection.value_or(""));
EXPECT_EQ(name, child.name);
EXPECT_EQ(ZX_OK, driver_host_dir_binding_.Bind(std::move(exposed_dir), loop_.dispatcher()));
});
driver_host_dir().SetOpenHandler([this](std::string path, auto object) {
EXPECT_EQ(fdf::DriverHost::Name_, path);
EXPECT_EQ(ZX_OK, driver_host_binding_.Bind(object.TakeChannel(), loop_.dispatcher()));
});
}
zx::channel StartDriver(DriverRunner* driver_runner, Driver driver) {
fidl::FidlAllocator allocator;
fidl::VectorView<fdata::wire::DictionaryEntry> program_entries(allocator, 2);
program_entries[0].key.Set(allocator, "binary");
program_entries[0].value.set_str(allocator, allocator, driver.binary);
program_entries[1].key.Set(allocator, "colocate");
program_entries[1].value.set_str(allocator, allocator, driver.colocate ? "true" : "false");
fdata::wire::Dictionary program(allocator);
program.set_entries(allocator, std::move(program_entries));
auto outgoing_endpoints = fidl::CreateEndpoints<fuchsia_io::Directory>();
EXPECT_EQ(ZX_OK, outgoing_endpoints.status_value());
frunner::wire::ComponentStartInfo start_info(allocator);
start_info.set_resolved_url(allocator, allocator, driver.url)
.set_program(allocator, std::move(program))
.set_ns(allocator)
.set_outgoing_dir(allocator, std::move(outgoing_endpoints->server));
zx::channel controller_client_end, controller_server_end;
EXPECT_EQ(ZX_OK, zx::channel::create(0, &controller_client_end, &controller_server_end));
TestTransaction transaction(driver.close);
{
fidl::WireInterface<frunner::ComponentRunner>::StartCompleter::Sync completer(&transaction);
static_cast<fidl::WireInterface<frunner::ComponentRunner>*>(driver_runner)
->Start(std::move(start_info), std::move(controller_server_end), completer);
}
loop().RunUntilIdle();
return controller_client_end;
}
zx::status<zx::channel> StartRootDriver(std::string url, DriverRunner* driver_runner) {
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {
EXPECT_EQ("boot-drivers", collection.name);
EXPECT_EQ("root", decl.name());
EXPECT_EQ("fuchsia-boot:///#meta/root-driver.cm", decl.url());
});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ("boot-drivers", child.collection);
EXPECT_EQ("root", child.name);
EXPECT_EQ(ZX_OK, driver_dir_binding_.Bind(std::move(exposed_dir), loop_.dispatcher()));
});
auto start = driver_runner->StartRootDriver(std::move(url));
if (start.is_error()) {
return start.take_error();
}
loop().RunUntilIdle();
StartDriverHost("driver_hosts", "driver_host-0");
zx::channel controller =
StartDriver(driver_runner, {
.url = "fuchsia-boot:///#meta/root-driver.cm",
.binary = "driver/root-driver.so",
});
return zx::ok(std::move(controller));
}
void Unbind() {
driver_host_binding_.Unbind();
loop().RunUntilIdle();
}
void Emplace(fidl::InterfaceRequest<fdf::Driver> request) {
requests_.emplace_back(std::move(request));
}
inspect::Hierarchy Inspect(DriverRunner* driver_runner) {
fake_context context;
auto inspector = driver_runner->Inspect()(context).take_value();
return inspect::ReadFromInspector(inspector)(context).take_value();
}
private:
TestRealm realm_;
TestDirectory driver_host_dir_;
TestDirectory driver_dir_;
TestDriverHost driver_host_;
fidl::Binding<fsys::Realm> realm_binding_{&realm_};
fidl::Binding<fio::Directory> driver_host_dir_binding_{&driver_host_dir_};
fidl::Binding<fio::Directory> driver_dir_binding_{&driver_dir_};
fidl::Binding<fdf::DriverHost> driver_host_binding_{&driver_host_};
std::vector<fidl::InterfaceRequest<fdf::Driver>> requests_;
inspect::Inspector inspector_;
async::Loop loop_{&kAsyncLoopConfigNoAttachToCurrentThread};
sys::testing::ComponentContextProvider provider_{loop_.dispatcher()};
};
// Start the root driver.
TEST_F(DriverRunnerTest, StartRootDriver) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
}
// Start the root driver, and add a child node owned by the root driver.
TEST_F(DriverRunnerTest, StartRootDriver_AddOwnedChild) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
fdf::NodePtr second_node;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()),
second_node.NewRequest(loop().dispatcher()),
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
}
// Start the root driver, add a child node, then remove it.
TEST_F(DriverRunnerTest, StartRootDriver_RemoveOwnedChild) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodeControllerPtr node_controller;
fdf::NodePtr root_node, second_node;
driver_host().SetStartHandler([this, &node_controller, &root_node, &second_node](
fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()),
second_node.NewRequest(loop().dispatcher()),
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
node_controller->Remove();
loop().RunUntilIdle();
EXPECT_FALSE(second_node.is_bound());
EXPECT_TRUE(root_node.is_bound());
}
// Start the root driver, and add a child node with an invalid name.
TEST_F(DriverRunnerTest, StartRootDriver_AddOwnedChild_InvalidName) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node, invalid_node;
driver_host().SetStartHandler([this, &root_node, &invalid_node](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second.invalid");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()),
invalid_node.NewRequest(loop().dispatcher()),
[](auto result) { EXPECT_TRUE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
loop().RunUntilIdle();
EXPECT_FALSE(invalid_node.is_bound());
EXPECT_TRUE(root_node.is_bound());
}
// Start the root driver, and add two child nodes with duplicate names.
TEST_F(DriverRunnerTest, StartRootDriver_AddOwnedChild_DuplicateNames) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node, second_node, invalid_node;
driver_host().SetStartHandler([this, &root_node, &second_node, &invalid_node](
fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()),
second_node.NewRequest(loop().dispatcher()),
[](auto result) { EXPECT_FALSE(result.is_err()); });
args.set_name("second");
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()),
invalid_node.NewRequest(loop().dispatcher()),
[](auto result) { EXPECT_TRUE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
loop().RunUntilIdle();
EXPECT_FALSE(invalid_node.is_bound());
EXPECT_TRUE(second_node.is_bound());
EXPECT_TRUE(root_node.is_bound());
}
// Start the root driver, and add a child node with duplicate symbols. The child
// node is unowned, so if we did not have duplicate symbols, the second driver
// would bind to it.
TEST_F(DriverRunnerTest, StartRootDriver_AddUnownedChild_DuplicateSymbols) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodeControllerPtr node_controller;
driver_host().SetStartHandler([this, &node_controller](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("sym").set_address(0xfeed)));
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("sym").set_address(0xf00d)));
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_TRUE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
loop().RunUntilIdle();
ASSERT_FALSE(node_controller.is_bound());
}
// Start the root driver, and add a child node that has a symbol without an
// address.
TEST_F(DriverRunnerTest, StartRootDriver_AddUnownedChild_SymbolMissingAddress) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodeControllerPtr node_controller;
driver_host().SetStartHandler([this, &node_controller](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_symbols()->emplace_back(std::move(fdf::NodeSymbol().set_name("sym")));
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_TRUE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
loop().RunUntilIdle();
ASSERT_FALSE(node_controller.is_bound());
}
// Start the root driver, and add a child node that has a symbol without a name.
TEST_F(DriverRunnerTest, StartRootDriver_AddUnownedChild_SymbolMissingName) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodeControllerPtr node_controller;
driver_host().SetStartHandler([this, &node_controller](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_symbols()->emplace_back(std::move(fdf::NodeSymbol().set_address(0xfeed)));
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_TRUE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
loop().RunUntilIdle();
ASSERT_FALSE(node_controller.is_bound());
}
// Start the root driver, and then start a second driver in a new driver host.
TEST_F(DriverRunnerTest, StartSecondDriver_NewDriverHost) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {
EXPECT_EQ("boot-drivers", collection.name);
EXPECT_EQ("root.second", decl.name());
EXPECT_EQ("fuchsia-boot:///#meta/second-driver.cm", decl.url());
});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ("boot-drivers", child.collection);
EXPECT_EQ("root.second", child.name);
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_offers()->emplace_back("fuchsia.package.Protocol");
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("sym").set_address(0xfeed)));
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& offers = start_args.offers();
EXPECT_EQ(1u, offers.size());
EXPECT_EQ("fuchsia.package.Protocol", offers[0]);
EXPECT_TRUE(start_args.symbols().empty());
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/second-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
EXPECT_TRUE(start_args.exposed_dir().is_valid());
});
StartDriverHost("driver_hosts", "driver_host-1");
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
});
}
// Start the root driver, and then start a second driver in the same driver
// host.
TEST_F(DriverRunnerTest, StartSecondDriver_SameDriverHost) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {
EXPECT_EQ("boot-drivers", collection.name);
EXPECT_EQ("root.second", decl.name());
EXPECT_EQ("fuchsia-boot:///#meta/second-driver.cm", decl.url());
});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ("boot-drivers", child.collection);
EXPECT_EQ("root.second", child.name);
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_offers()->emplace_back("fuchsia.package.Protocol");
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("sym").set_address(0xfeed)));
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& offers = start_args.offers();
EXPECT_EQ(1u, offers.size());
EXPECT_EQ("fuchsia.package.Protocol", offers[0]);
auto& symbols = start_args.symbols();
EXPECT_EQ(1u, symbols.size());
EXPECT_EQ("sym", symbols[0].name());
EXPECT_EQ(0xfeedu, symbols[0].address());
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/second-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("true", entries[1].value->str());
EXPECT_TRUE(start_args.exposed_dir().is_valid());
});
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
.colocate = true,
});
}
// Start the root driver, and then start a second driver that we match based on
// node properties.
TEST_F(DriverRunnerTest, StartSecondDriver_UseProperties) {
FakeDriverIndex driver_index(
loop().dispatcher(), [](auto args) -> zx::status<FakeDriverIndex::MatchResult> {
if (args.has_properties() && args.properties()[0].key() == 0x1985 &&
args.properties()[0].value() == 0x2301) {
return zx::ok(FakeDriverIndex::MatchResult{
.url = "fuchsia-boot:///#meta/second-driver.cm",
.matched_args = std::move(args),
});
} else {
return zx::error(ZX_ERR_NOT_FOUND);
}
});
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {
EXPECT_EQ("boot-drivers", collection.name);
EXPECT_EQ("root.second", decl.name());
EXPECT_EQ("fuchsia-boot:///#meta/second-driver.cm", decl.url());
});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ("boot-drivers", child.collection);
EXPECT_EQ("root.second", child.name);
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_properties()->emplace_back(
std::move(fdf::NodeProperty().set_key(0x1985).set_value(0x2301)));
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/second-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("true", entries[1].value->str());
EXPECT_TRUE(start_args.exposed_dir().is_valid());
});
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
.colocate = true,
});
}
// Start the root driver, and then add a child node that does not bind to a
// second driver.
TEST_F(DriverRunnerTest, StartSecondDriver_UnknownNode) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
auto& entries = start_args.program().entries();
EXPECT_EQ(2u, entries.size());
EXPECT_EQ("binary", entries[0].key);
EXPECT_EQ("driver/root-driver.so", entries[0].value->str());
EXPECT_EQ("colocate", entries[1].key);
EXPECT_EQ("false", entries[1].value->str());
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("unknown-node");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
StartDriver(&driver_runner, {.close = true});
}
// Start the second driver, and then unbind its associated node.
TEST_F(DriverRunnerTest, StartSecondDriver_UnbindSecondNode) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node;
driver_host().SetStartHandler([this, &root_node](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
fdf::NodePtr second_node;
driver_host().SetStartHandler([this, &second_node](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
EXPECT_EQ(ZX_OK, second_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
});
StartDriverHost("driver_hosts", "driver_host-1");
zx::channel second_driver =
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
});
// Unbinding the second node stops the driver bound to it.
second_node.Unbind();
realm().SetDestroyChildHandler([](fsys::ChildRef child) {
EXPECT_EQ("root.second", child.name);
EXPECT_EQ("boot-drivers", child.collection);
});
loop().RunUntilIdle();
zx_signals_t signals = 0;
ASSERT_EQ(ZX_OK, second_driver.wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &signals));
ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED);
// On destruction, we unbind the root node, which stops the root driver.
realm().SetDestroyChildHandler([](fsys::ChildRef child) {
EXPECT_EQ("root", child.name);
EXPECT_EQ("boot-drivers", child.collection);
});
}
// Start the second driver, and then close the associated Driver protocol
// channel.
TEST_F(DriverRunnerTest, StartSecondDriver_CloseSecondDriver) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node;
driver_host().SetStartHandler([this, &root_node](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
fdf::NodePtr second_node;
fidl::InterfaceRequest<fdf::Driver> second_request;
driver_host().SetStartHandler([this, &second_node, &second_request](
fdf::DriverStartArgs start_args, auto request) {
second_request = std::move(request);
EXPECT_EQ(ZX_OK, second_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
});
StartDriverHost("driver_hosts", "driver_host-1");
zx::channel second_driver =
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
});
// Closing the Driver protocol channel of the second driver causes the driver
// to be stopped.
second_request.TakeChannel();
loop().RunUntilIdle();
zx_signals_t signals = 0;
ASSERT_EQ(ZX_OK, second_driver.wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &signals));
ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED);
}
// Start a chain of drivers, and then unbind the second driver's node.
TEST_F(DriverRunnerTest, StartDriverChain_UnbindSecondNode) {
FakeDriverIndex driver_index(loop().dispatcher(),
[](auto args) -> zx::status<FakeDriverIndex::MatchResult> {
std::string name(args.name().data(), args.name().size());
return zx::ok(FakeDriverIndex::MatchResult{
.url = "fuchsia-boot:///#meta/" + name + "-driver.cm",
.matched_args = std::move(args),
});
});
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node;
driver_host().SetStartHandler([this, &root_node](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("node-0");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
constexpr size_t kMaxNodes = 10;
std::vector<fdf::NodePtr> nodes;
std::vector<zx::channel> drivers;
for (size_t i = 1; i <= kMaxNodes; i++) {
driver_host().SetStartHandler([this, &nodes, i](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
auto& node = nodes.emplace_back();
EXPECT_EQ(ZX_OK, node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
// Only add a node that a driver will be bound to.
if (i != kMaxNodes) {
fdf::NodeAddArgs args;
args.set_name("node-" + std::to_string(i));
fdf::NodeControllerPtr node_controller;
node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
}
});
StartDriverHost("driver_hosts", "driver_host-" + std::to_string(i));
drivers.emplace_back(
StartDriver(&driver_runner,
{
.url = "fuchsia-boot:///#meta/node-" + std::to_string(i - 1) + "-driver.cm",
.binary = "driver/driver.so",
}));
}
// Unbinding the second node stops all drivers bound in the sub-tree, in a
// depth-first order.
std::vector<size_t> indices;
std::vector<fidl::Client<frunner::ComponentController>> clients;
for (auto& driver : drivers) {
clients.emplace_back(std::move(driver), loop().dispatcher(),
std::make_shared<UnbindWatcher>(clients.size() + 1, indices));
}
nodes.front().Unbind();
loop().RunUntilIdle();
EXPECT_THAT(indices, ElementsAre(10, 9, 8, 7, 6, 5, 4, 3, 2, 1));
}
// Start the second driver, and then unbind the root node.
TEST_F(DriverRunnerTest, StartSecondDriver_UnbindRootNode) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node;
driver_host().SetStartHandler([this, &root_node](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
fdf::NodePtr second_node;
driver_host().SetStartHandler([this, &second_node](fdf::DriverStartArgs start_args,
auto request) {
Emplace(std::move(request));
EXPECT_EQ(ZX_OK, second_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
});
StartDriverHost("driver_hosts", "driver_host-1");
zx::channel second_driver =
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
});
// Unbinding the root node stops all drivers.
std::vector<size_t> indices;
fidl::Client<frunner::ComponentController> root_client(
std::move(root_driver.value()), loop().dispatcher(),
std::make_shared<UnbindWatcher>(0, indices));
fidl::Client<frunner::ComponentController> second_client(
std::move(second_driver), loop().dispatcher(), std::make_shared<UnbindWatcher>(1, indices));
root_node.Unbind();
loop().RunUntilIdle();
EXPECT_THAT(indices, ElementsAre(1, 0));
}
// Start the second driver, and then stop the root driver.
TEST_F(DriverRunnerTest, StartSecondDriver_StopRootDriver) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
fdf::NodePtr root_node;
driver_host().SetStartHandler([this, &root_node](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
auto root_driver = StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner);
ASSERT_TRUE(root_driver.is_ok());
zx::unowned_channel unowned_root_driver(root_driver.value());
driver_host().SetStartHandler(
[this](fdf::DriverStartArgs start_args, auto request) { Emplace(std::move(request)); });
StartDriverHost("driver_hosts", "driver_host-1");
zx::channel second_driver =
StartDriver(&driver_runner, {
.url = "fuchsia-boot:///#meta/second-driver.cm",
.binary = "driver/second-driver.so",
});
// Stopping the root driver stops all drivers.
std::vector<size_t> indices;
fidl::Client<frunner::ComponentController> root_client(
std::move(root_driver.value()), loop().dispatcher(),
std::make_shared<UnbindWatcher>(0, indices));
fidl::Client<frunner::ComponentController> second_client(
std::move(second_driver), loop().dispatcher(), std::make_shared<UnbindWatcher>(1, indices));
root_client->Stop();
loop().RunUntilIdle();
EXPECT_THAT(indices, ElementsAre(1, 0));
}
// Start a driver and inspect the driver runner.
TEST_F(DriverRunnerTest, StartAndInspect) {
auto driver_index = CreateDriverIndex();
auto driver_index_client = driver_index.Connect();
ASSERT_EQ(driver_index_client.status_value(), ZX_OK);
DriverRunner driver_runner(ConnectToRealm(), std::move(driver_index_client.value()), inspector(),
loop().dispatcher());
auto defer = fit::defer([this] { Unbind(); });
driver_host().SetStartHandler([this](fdf::DriverStartArgs start_args, auto request) {
Emplace(std::move(request));
realm().SetCreateChildHandler([](fsys::CollectionRef collection, fsys::ChildDecl decl) {});
realm().SetBindChildHandler([this](fsys::ChildRef child, auto exposed_dir) {
EXPECT_EQ(ZX_OK, driver_dir_binding().Bind(std::move(exposed_dir), loop().dispatcher()));
});
fdf::NodePtr root_node;
EXPECT_EQ(ZX_OK, root_node.Bind(std::move(*start_args.mutable_node()), loop().dispatcher()));
fdf::NodeAddArgs args;
args.set_name("second");
args.mutable_offers()->emplace_back("fuchsia.package.ProtocolA");
args.mutable_offers()->emplace_back("fuchsia.package.ProtocolB");
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("symbol-A").set_address(0x2301)));
args.mutable_symbols()->emplace_back(
std::move(fdf::NodeSymbol().set_name("symbol-B").set_address(0x1985)));
fdf::NodeControllerPtr node_controller;
root_node->AddChild(std::move(args), node_controller.NewRequest(loop().dispatcher()), {},
[](auto result) { EXPECT_FALSE(result.is_err()); });
});
ASSERT_TRUE(StartRootDriver("fuchsia-boot:///#meta/root-driver.cm", &driver_runner).is_ok());
EXPECT_THAT(
Inspect(&driver_runner),
AllOf(NodeMatches(NameMatches("root")),
ChildrenMatch(UnorderedElementsAre(AllOf(
NodeMatches(NameMatches("root")),
ChildrenMatch(UnorderedElementsAre(AllOf(NodeMatches(AllOf(
NameMatches("second"),
PropertyList(UnorderedElementsAre(
StringIs("offers", "fuchsia.package.ProtocolA, fuchsia.package.ProtocolB"),
StringIs("symbols", "symbol-A, symbol-B")))))))))))));
}