| // 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"))))))))))))); |
| } |