| // 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 <fidl/fuchsia.component.decl/cpp/test_base.h> |
| #include <fidl/fuchsia.component/cpp/test_base.h> |
| #include <fidl/fuchsia.driver.framework/cpp/test_base.h> |
| #include <fidl/fuchsia.driver.host/cpp/test_base.h> |
| #include <fidl/fuchsia.io/cpp/test_base.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/driver/component/cpp/node_add_args.h> |
| #include <lib/fit/defer.h> |
| #include <lib/inspect/cpp/reader.h> |
| #include <lib/inspect/testing/cpp/inspect.h> |
| |
| #include <bind/fuchsia/platform/cpp/bind.h> |
| |
| #include "gmock/gmock.h" |
| #include "src/devices/bin/driver_manager/composite/composite_node_spec.h" |
| #include "src/devices/bin/driver_manager/node.h" |
| #include "src/devices/bin/driver_manager/testing/fake_driver_index.h" |
| #include "src/devices/bin/driver_manager/tests/driver_runner_test_fixture.h" |
| |
| namespace driver_runner { |
| |
| namespace frunner = fuchsia_component_runner; |
| |
| using driver_manager::Collection; |
| using driver_manager::Node; |
| using testing::ElementsAre; |
| |
| // This is a parameterized variant of |DriverRunnerTestBase|. This enables each test case being run |
| // twice, once with the dynamic linker and once not (the legacy path). |
| class DriverRunnerTest : public DriverRunnerTestBase, public ::testing::WithParamInterface<bool> { |
| public: |
| void SetUp() override { use_dynamic_linker_ = GetParam(); } |
| |
| void SetupDriverRunner(FakeDriverIndex fake_driver_index) { |
| if (use_dynamic_linker_) { |
| auto driver_host_runner = |
| std::make_unique<driver_manager::DriverHostRunner>(dispatcher(), ConnectToRealm()); |
| DriverRunnerTestBase::SetupDriverRunnerWithDynamicLinker( |
| dispatcher(), std::move(driver_host_runner), std::move(fake_driver_index)); |
| } else { |
| DriverRunnerTestBase::SetupDriverRunner(std::move(fake_driver_index)); |
| } |
| } |
| |
| void SetupDriverRunner() { SetupDriverRunner(CreateDriverIndex()); } |
| |
| zx::result<StartDriverResult> StartRootDriver() { |
| if (use_dynamic_linker_) { |
| return DriverRunnerTestBase::StartRootDriverDynamicLinking(); |
| } else { |
| return DriverRunnerTestBase::StartRootDriver(); |
| } |
| } |
| |
| StartDriverResult StartSecondDriver(std::string_view moniker, bool colocate = false, |
| bool host_restart_on_crash = false, |
| bool use_next_vdso = false) { |
| return DriverRunnerTestBase::StartSecondDriver(moniker, colocate, host_restart_on_crash, |
| use_next_vdso, use_dynamic_linker()); |
| } |
| |
| // If |use_dynamic_linker| is not provided, it will be generated from the test configuration. |
| void ValidateProgram(std::optional<::fuchsia_data::Dictionary>& program, std::string_view binary, |
| std::string_view colocate, std::string_view host_restart_on_crash, |
| std::string_view use_next_vdso, |
| std::optional<std::string_view> use_dynamic_linker = std::nullopt) { |
| std::string use_dynamic_linker_str = use_dynamic_linker_ ? "true" : "false"; |
| if (use_dynamic_linker.has_value()) { |
| use_dynamic_linker_str = use_dynamic_linker.value(); |
| } |
| return DriverRunnerTestBase::ValidateProgram(program, binary, colocate, host_restart_on_crash, |
| use_next_vdso, use_dynamic_linker_str); |
| } |
| |
| bool use_dynamic_linker() const { return use_dynamic_linker_; } |
| |
| private: |
| bool use_dynamic_linker_ = false; |
| }; |
| |
| // Start the root driver. |
| TEST_P(DriverRunnerTest, StartRootDriver) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver. Make sure that the driver is stopped before the Component is exited. |
| TEST_P(DriverRunnerTest, StartRootDriver_DriverStopBeforeComponentExit) { |
| SetupDriverRunner(); |
| |
| std::vector<size_t> event_order; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| ServeStopListener(std::move(root_driver->controller), TeardownWatcher(1, event_order)); |
| |
| root_driver->driver->SetStopHandler([&event_order]() { event_order.push_back(0); }); |
| root_driver->driver->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| // Make sure the driver was stopped before we told the component framework the driver was stopped. |
| EXPECT_THAT(event_order, ElementsAre(0, 1)); |
| } |
| |
| // Start the root driver, and add a child node owned by the root driver. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddOwnedChild) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| root_driver->driver->AddChild("second", true, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, add a child node, then remove it. |
| TEST_P(DriverRunnerTest, StartRootDriver_RemoveOwnedChild) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> created_child = |
| root_driver->driver->AddChild("second", true, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| AssertNodeBound(created_child); |
| AssertNodeControllerBound(created_child); |
| |
| EXPECT_TRUE(created_child->node_controller.value()->Remove().is_ok()); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| AssertNodeNotBound(created_child); |
| ASSERT_NE(nullptr, root_driver->driver.get()); |
| EXPECT_TRUE(root_driver->driver->node().is_valid()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add two child nodes with duplicate names. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddOwnedChild_DuplicateNames) { |
| SetupDriverRunner(); |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", true, false); |
| std::shared_ptr<CreatedChild> invalid_child = root_driver->driver->AddChild("second", true, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| AssertNodeNotBound(invalid_child); |
| AssertNodeBound(child); |
| |
| ASSERT_NE(nullptr, root_driver->driver.get()); |
| EXPECT_TRUE(root_driver->driver->node().is_valid()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node with an offer that is missing a |
| // source. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddUnownedChild_OfferMissingSource) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .target_name = std::make_optional<std::string>("fuchsia.package.Renamed"), |
| }))), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild(std::move(args), false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| AssertNodeControllerNotBound(child); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node with one offer that has a source |
| // and another that has a target. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddUnownedChild_OfferHasRef) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .source = fdecl::Ref::WithSelf(fdecl::SelfRef()), |
| .source_name = "fuchsia.package.Protocol", |
| }))), |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .source_name = "fuchsia.package.Protocol", |
| .target = fdecl::Ref::WithSelf(fdecl::SelfRef()), |
| }))), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild(std::move(args), false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| AssertNodeControllerNotBound(child); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // 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_P(DriverRunnerTest, StartRootDriver_AddUnownedChild_DuplicateSymbols) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "sym", |
| .address = 0xf00d, |
| }), |
| fdfw::NodeSymbol({ |
| .name = "sym", |
| .address = 0xf00d, |
| }), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild(std::move(args), false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| AssertNodeControllerNotBound(child); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node that has a symbol without an |
| // address. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddUnownedChild_SymbolMissingAddress) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = std::make_optional<std::string>("sym"), |
| }), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild(std::move(args), false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| AssertNodeControllerNotBound(child); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node that has a symbol without a name. |
| TEST_P(DriverRunnerTest, StartRootDriver_AddUnownedChild_SymbolMissingName) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = std::nullopt, |
| .address = 0xfeed, |
| }), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild(std::move(args), false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| AssertNodeControllerNotBound(child); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then start a second driver in a new driver host. |
| TEST_P(DriverRunnerTest, StartSecondDriver_NewDriverHost) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| realm().SetCreateChildHandler( |
| [](fdecl::CollectionRef collection, fdecl::Child decl, std::vector<fdecl::Offer> offers) { |
| EXPECT_EQ("boot-drivers", collection.name()); |
| EXPECT_EQ("dev.second", decl.name()); |
| EXPECT_EQ(second_driver_url, decl.url()); |
| |
| EXPECT_EQ(1u, offers.size()); |
| ASSERT_TRUE(offers[0].Which() == fdecl::Offer::Tag::kService); |
| auto& service = offers[0].service().value(); |
| |
| ASSERT_TRUE(service.source().has_value()); |
| ASSERT_TRUE(service.source().value().Which() == fdecl::Ref::Tag::kChild); |
| auto& source_ref = service.source().value().child().value(); |
| EXPECT_EQ("root", source_ref.name()); |
| EXPECT_EQ("boot-drivers", source_ref.collection().value_or("missing")); |
| |
| ASSERT_TRUE(service.source_name().has_value()); |
| EXPECT_EQ("fuchsia.package.Protocol", service.source_name().value()); |
| }); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "sym", |
| .address = 0xfeed, |
| }), |
| }, |
| }, |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .source_name = "fuchsia.package.Protocol", |
| .source_instance_filter = std::vector<std::string>{"default"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("default", "instance-1"), |
| }, |
| }))), |
| }, |
| }, |
| }); |
| |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| bool did_bind = false; |
| child->node_controller.value()->WaitForDriver().Then( |
| [&did_bind](fidl::Result<fuchsia_driver_framework::NodeController::WaitForDriver>& result) { |
| if (result.is_ok() && result.value().driver_started_node_token().has_value()) { |
| did_bind = true; |
| return; |
| } |
| |
| ZX_ASSERT_MSG(false, " WaitForDriver failed: %s.", |
| result.error_value().FormatDescription().c_str()); |
| }); |
| |
| auto [driver, controller] = StartSecondDriver("root.second"); |
| EXPECT_TRUE(did_bind); |
| ServeStopListener(std::move(controller)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then start a second driver in the same driver |
| // host. |
| TEST_P(DriverRunnerTest, StartSecondDriver_SameDriverHost) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "sym", |
| .address = 0xfeed, |
| }), |
| }, |
| }, |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .source_name = "fuchsia.package.Protocol", |
| .source_instance_filter = std::vector<std::string>{"default"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("default", "instance-1"), |
| }, |
| }))), |
| }, |
| }, |
| }); |
| |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| bool did_bind = false; |
| child->node_controller.value()->WaitForDriver().Then( |
| [&did_bind](fidl::Result<fuchsia_driver_framework::NodeController::WaitForDriver>& result) { |
| if (result.is_ok() && result.value().driver_started_node_token().has_value()) { |
| did_bind = true; |
| return; |
| } |
| |
| ZX_ASSERT_MSG(false, " WaitForDriver failed: %s.", |
| result.error_value().FormatDescription().c_str()); |
| }); |
| |
| auto second_driver_config = kDefaultSecondDriverPkgConfig; |
| std::string binary = std::string(second_driver_config.main_module.open_path); |
| StartDriverHandler start_handler = [this, binary](TestDriver* driver, |
| fdfw::DriverStartArgs start_args) { |
| auto& symbols = start_args.symbols().value(); |
| EXPECT_EQ(1u, symbols.size()); |
| EXPECT_EQ("sym", symbols[0].name().value()); |
| EXPECT_EQ(0xfeedu, symbols[0].address()); |
| ValidateProgram(start_args.program(), binary, "true", "false", "false"); |
| }; |
| auto [driver, controller] = StartDriverWithConfig("dev.second", |
| { |
| .url = second_driver_url, |
| .binary = binary, |
| .colocate = true, |
| .use_dynamic_linker = use_dynamic_linker(), |
| }, |
| std::move(start_handler), second_driver_config); |
| ServeStopListener(std::move(controller)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| EXPECT_TRUE(did_bind); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then start a second driver that we match based on |
| // node properties. |
| TEST_P(DriverRunnerTest, StartSecondDriver_UseProperties) { |
| FakeDriverIndex driver_index( |
| dispatcher(), [](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| if (args.has_properties() && args.properties()[0].key.get() == "second_node_prop" && |
| args.properties()[0].value.is_int_value() && |
| args.properties()[0].value.int_value() == 0x2301) { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| } else { |
| return zx::error(ZX_ERR_NOT_FOUND); |
| } |
| }); |
| SetupDriverRunner(std::move(driver_index)); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .properties = |
| { |
| { |
| fdf::MakeProperty("second_node_prop", 0x2301u), |
| }, |
| }, |
| }); |
| |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver, controller] = StartSecondDriver("dev.second", true); |
| ServeStopListener(std::move(controller)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| TEST_P(DriverRunnerTest, CheckOnBindNode) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .properties = |
| { |
| { |
| fdf::MakeProperty("second_node_prop", 0x2301u), |
| }, |
| }, |
| }); |
| |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| std::optional<zx::event> node_token; |
| child->node_controller.value()->WaitForDriver().Then( |
| [&node_token](fidl::Result<fuchsia_driver_framework::NodeController::WaitForDriver>& result) { |
| if (result.is_ok() && result.value().driver_started_node_token().has_value()) { |
| node_token = std::move(result.value().driver_started_node_token().value()); |
| return; |
| } |
| |
| ZX_ASSERT_MSG(false, " WaitForDriver failed: %s.", |
| result.error_value().FormatDescription().c_str()); |
| }); |
| |
| auto [driver, controller] = StartSecondDriver("dev.second", true); |
| ServeStopListener(std::move(controller)); |
| |
| ASSERT_TRUE(node_token.has_value()); |
| zx_info_handle_basic_t info; |
| ASSERT_EQ(node_token->get_info(ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr), |
| ZX_OK); |
| |
| ASSERT_TRUE(driver->node_token().has_value()); |
| zx_info_handle_basic_t info2; |
| ASSERT_EQ( |
| driver->node_token()->get_info(ZX_INFO_HANDLE_BASIC, &info2, sizeof(info2), nullptr, nullptr), |
| ZX_OK); |
| |
| ASSERT_EQ(info.koid, info2.koid); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Disable the second driver and try to create a Node that would bind to it. The node should fail |
| // to match a driver. |
| TEST_P(DriverRunnerTest, SecondNodeWithDisabledSecondDriver) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| // Disable the second-driver url, and restart with rematching of the requested. |
| driver_index().disable_driver_url(second_driver_url); |
| |
| fidl::Arena arena; |
| auto devfs = fuchsia_driver_framework::wire::DevfsAddArgs::Builder(arena) |
| .connector_supports(fuchsia_device_fs::ConnectionType::kController) |
| .class_name("driver_runner_test") |
| .Build(); |
| auto args = fuchsia_driver_framework::wire::NodeAddArgs::Builder(arena) |
| .name(arena, "second") |
| .devfs_args(devfs) |
| .Build(); |
| auto child = root_driver->driver->AddChild(fidl::ToNatural(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| EXPECT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| bool bind_failed = false; |
| child->node_controller.value()->WaitForDriver().Then( |
| [&bind_failed]( |
| fidl::Result<fuchsia_driver_framework::NodeController::WaitForDriver>& result) { |
| if (result.is_ok() && result.value().match_error().has_value() && |
| result.value().match_error().value() == ZX_ERR_NOT_FOUND) { |
| bind_failed = true; |
| return; |
| } |
| |
| ZX_ASSERT_MSG(false, " WaitForDriver did not get expected error"); |
| }); |
| |
| // Mark the boot-up as complete so the error gets emitted out. |
| driver_runner().BootupDoneForTesting(); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(bind_failed); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| // Only the root node was destroyed since the second node never created a component. |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then disable and rematch it which should make it available for |
| // matching. Undisable the driver and then restart with rematch, which should get the node again. |
| TEST_P(DriverRunnerTest, StartSecondDriver_DisableAndRematch_UndisableAndRestart) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener = ServeStopListener(std::move(controller)); |
| |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Disable the second-driver url, and restart with rematching of the requested. |
| driver_index().disable_driver_url(second_driver_url); |
| zx::result count = driver_runner().RestartNodesColocatedWithDriverUrl( |
| second_driver_url, fuchsia_driver_development::RestartRematchFlags::kRequested); |
| EXPECT_EQ(1u, count.value()); |
| |
| // Our driver should get closed. |
| while (!stop_listener.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| // Since we disabled the driver url, the rematch should have not gotten a match, and therefore |
| // the node should haver become orphaned. |
| EXPECT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Undisable the driver, and try binding all available nodes. This should cause it to get |
| // started again. |
| driver_index().un_disable_driver_url(second_driver_url); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_runner().TryBindAllAvailable(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver_2, controller_2] = StartSecondDriver("dev.second"); |
| ServeStopListener(std::move(controller_2)); |
| |
| // This list should be empty now that it got bound again. |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| // The node was destroyed twice. |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers"), |
| CreateChildRef("dev.second", "boot-drivers"), |
| CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the second driver with host_restart_on_crash enabled, and then kill the driver host, and |
| // observe the node start the driver again in another host. Done by both a node client drop, and a |
| // driver host server binding close. |
| TEST_P(DriverRunnerTest, StartSecondDriverHostRestartOnCrash) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver_1, controller_1] = StartSecondDriver("dev.second", false, true); |
| StopListener& stop_listener_1 = ServeStopListener(std::move(controller_1)); |
| |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Stop the driver host binding. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_1->CloseBinding(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_TRUE(stop_listener_1.is_stopped()); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_2, controller_2] = StartSecondDriver("dev.second", false, true); |
| StopListener& stop_listener_2 = ServeStopListener(std::move(controller_2)); |
| |
| // Drop the node client binding. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_2->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_TRUE(stop_listener_2.is_stopped()); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_3, controller_3] = StartSecondDriver("dev.second", false, true); |
| StopListener& stop_listener_3 = ServeStopListener(std::move(controller_3)); |
| |
| // Now try to drop the node and close the binding at the same time. They should not break each |
| // other. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_3->CloseBinding(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| driver_3->DropNode(); |
| EXPECT_FALSE(RunLoopUntilIdle()); |
| EXPECT_TRUE(stop_listener_3.is_stopped()); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_4, controller_4] = StartSecondDriver("dev.second", false, true); |
| StopListener& stop_listener_4 = ServeStopListener(std::move(controller_4)); |
| |
| // Again try to drop the node and close the binding at the same time but in opposite order. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_4->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| driver_4->CloseBinding(); |
| EXPECT_FALSE(RunLoopUntilIdle()); |
| EXPECT_TRUE(stop_listener_4.is_stopped()); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_5, controller_5] = StartSecondDriver("dev.second", false, true); |
| StopListener& stop_listener_5 = ServeStopListener(std::move(controller_5)); |
| |
| // Finally don't RunLoopUntilIdle in between the two. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_5->CloseBinding(); |
| driver_5->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_TRUE(stop_listener_5.is_stopped()); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_6, controller_6] = StartSecondDriver("dev.second", false, true); |
| ServeStopListener(std::move(controller_6)); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers"), |
| CreateChildRef("dev.second", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers"), |
| CreateChildRef("dev.second", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers"), |
| CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the second driver with use_next_vdso enabled, |
| TEST_P(DriverRunnerTest, StartSecondDriver_UseNextVdso) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver_1, controller_1] = StartSecondDriver("dev.second", true, false, true); |
| ServeStopListener(std::move(controller_1)); |
| |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // The root driver adds a node that only binds after a RequestBind() call. |
| TEST_P(DriverRunnerTest, BindThroughRequest) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("child", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| driver_index().set_match_callback([](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| }); |
| |
| PrepareRealmForDriverComponentStart("dev.child", second_driver_url); |
| AssertNodeControllerBound(child); |
| child->node_controller.value() |
| ->RequestBind(fdfw::NodeControllerRequestBindRequest()) |
| .Then([](fidl::Result<fdfw::NodeController::RequestBind>& result) { |
| if (result.is_error()) { |
| fdf_log::error("RequestBind error: {}", result.error_value().FormatDescription()); |
| } |
| EXPECT_TRUE(result.is_ok()); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| auto [driver, controller] = StartSecondDriver("dev.child"); |
| ServeStopListener(std::move(controller)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.child", "boot-drivers")}); |
| } |
| |
| // The root driver adds a node that only binds after a RequestBind() call. Then Restarts through |
| // RequestBind() with force_rebind, once without a url suffix, and another with the url suffix. |
| TEST_P(DriverRunnerTest, BindAndRestartThroughRequest) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("child", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| driver_index().set_match_callback([](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| if (args.has_driver_url_suffix()) { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = "fuchsia-boot:///#meta/third-driver.cm", |
| }); |
| } |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| }); |
| |
| // Prepare realm for the second-driver CreateChild. |
| PrepareRealmForDriverComponentStart("dev.child", second_driver_url); |
| |
| // Bind the child node to the second-driver driver. |
| auto bind_request = fdfw::NodeControllerRequestBindRequest(); |
| child->node_controller.value() |
| ->RequestBind(fdfw::NodeControllerRequestBindRequest()) |
| .Then([](fidl::Result<fdfw::NodeController::RequestBind>& result) { |
| if (result.is_error()) { |
| fdf_log::error("RequestBind error: {}", result.error_value().FormatDescription()); |
| } |
| EXPECT_TRUE(result.is_ok()); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Get the second-driver running. |
| auto [driver_1, controller_1] = StartSecondDriver("dev.child"); |
| ServeStopListener(std::move(controller_1)); |
| |
| // Prepare realm for the second-driver CreateChild again. |
| PrepareRealmForDriverComponentStart("dev.child", second_driver_url); |
| |
| // Request rebind of the second-driver to the node. |
| child->node_controller.value() |
| ->RequestBind(fdfw::NodeControllerRequestBindRequest({ |
| .force_rebind = true, |
| })) |
| .Then([](fidl::Result<fdfw::NodeController::RequestBind>& result) { |
| if (result.is_error()) { |
| fdf_log::error("RequestBind error: {}", result.error_value().FormatDescription()); |
| } |
| EXPECT_TRUE(result.is_ok()); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Get the second-driver running again. |
| auto [driver_2, controller_2] = StartSecondDriver("dev.child"); |
| ServeStopListener(std::move(controller_2)); |
| |
| // Prepare realm for the third-driver CreateChild. |
| PrepareRealmForDriverComponentStart("dev.child", "fuchsia-boot:///#meta/third-driver.cm"); |
| |
| // Request rebind of the node with the third-driver. |
| child->node_controller.value() |
| ->RequestBind(fdfw::NodeControllerRequestBindRequest({ |
| .force_rebind = true, |
| .driver_url_suffix = "third", |
| })) |
| .Then([](fidl::Result<fdfw::NodeController::RequestBind>& result) { |
| if (result.is_error()) { |
| fdf_log::error("RequestBind error: {}", result.error_value().FormatDescription()); |
| } |
| EXPECT_TRUE(result.is_ok()); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Get the third-driver running. |
| auto third_driver_config = kDefaultThirdDriverPkgConfig; |
| std::string binary = std::string(third_driver_config.main_module.open_path); |
| StartDriverHandler start_handler = [&](TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| EXPECT_FALSE(start_args.symbols().has_value()); |
| ValidateProgram(start_args.program(), binary, "false", "false", "false"); |
| }; |
| auto third_driver = StartDriverWithConfig("dev.child", |
| { |
| .url = "fuchsia-boot:///#meta/third-driver.cm", |
| .binary = binary, |
| .use_dynamic_linker = use_dynamic_linker(), |
| }, |
| std::move(start_handler), third_driver_config); |
| ServeStopListener(std::move(third_driver.controller)); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({ |
| CreateChildRef("root", "boot-drivers"), |
| CreateChildRef("dev.child", "boot-drivers"), |
| CreateChildRef("dev.child", "boot-drivers"), |
| CreateChildRef("dev.child", "boot-drivers"), |
| }); |
| } |
| |
| // Start the root driver, and then add a child node that does not bind to a |
| // second driver. |
| TEST_P(DriverRunnerTest, StartSecondDriver_UnknownNode) { |
| SetupDriverRunner(); |
| |
| // Unknown driver request in this test requires enabling the introspector's GetMoniker. |
| EnableIntrospector(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("unknown-node", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| StartDriver("root", {.close = true, .use_dynamic_linker = use_dynamic_linker()}); |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then add a child node that only binds to a base driver. |
| TEST_P(DriverRunnerTest, StartSecondDriver_BindOrphanToBaseDriver) { |
| bool base_drivers_loaded = false; |
| FakeDriverIndex fake_driver_index( |
| dispatcher(), [&base_drivers_loaded](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| if (base_drivers_loaded) { |
| if (args.name().get() == "second") { |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| } |
| } |
| return zx::error(ZX_ERR_NOT_FOUND); |
| }); |
| SetupDriverRunner(std::move(fake_driver_index)); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .properties = |
| { |
| { |
| fdfw::NodeProperty({ |
| .key = fdfw::NodePropertyKey::WithStringValue("driver.prop-one"), |
| .value = fdfw::NodePropertyValue::WithStringValue("value"), |
| }), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Make sure the node we added was orphaned. |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Set the handlers for the new driver. |
| PrepareRealmForSecondDriverComponentStart(); |
| |
| // Tell driver index to return the second driver, and wait for base drivers to load. |
| base_drivers_loaded = true; |
| driver_runner().TryBindAllAvailable(); |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| driver_index().InvokeWatchDriverResponse(); |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| // See that we don't have an orphan anymore. |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then unbind its associated node. |
| TEST_P(DriverRunnerTest, StartSecondDriver_UnbindSecondNode) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener = ServeStopListener(std::move(controller)); |
| |
| // Unbinding the second node stops the driver bound to it. |
| driver->DropNode(); |
| while (!stop_listener.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then close the associated Driver protocol |
| // channel. |
| TEST_P(DriverRunnerTest, StartSecondDriver_CloseSecondDriver) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener = ServeStopListener(std::move(controller)); |
| |
| // Closing the Driver protocol channel of the second driver causes the driver |
| // to be stopped. |
| driver->CloseBinding(); |
| while (!stop_listener.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start a chain of drivers, and then unbind the second driver's node. |
| TEST_P(DriverRunnerTest, StartDriverChain_UnbindSecondNode) { |
| FakeDriverIndex driver_index(dispatcher(), |
| [](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| std::string name(args.name().get()); |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = "fuchsia-boot:///#meta/" + name + "-driver.cm", |
| }); |
| }); |
| SetupDriverRunner(std::move(driver_index)); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| constexpr size_t kMaxNodes = 10; |
| |
| // The drivers vector will start with the root. |
| std::vector<StartDriverResult> drivers; |
| drivers.reserve(kMaxNodes + 1); |
| drivers.emplace_back(std::move(root_driver.value())); |
| |
| std::vector<std::shared_ptr<CreatedChild>> children; |
| children.reserve(kMaxNodes); |
| |
| std::string component_moniker = "dev"; |
| for (size_t i = 0; i < kMaxNodes; i++) { |
| auto child_name = "node-" + std::to_string(i); |
| component_moniker += "." + child_name; |
| PrepareRealmForDriverComponentStart(component_moniker, |
| "fuchsia-boot:///#meta/" + child_name + "-driver.cm"); |
| children.emplace_back(drivers.back().driver->AddChild(child_name, false, false)); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto driver_config = kDefaultDriverPkgConfig; |
| std::string binary = std::string(driver_config.main_module.open_path); |
| StartDriverHandler start_handler = [this, binary](TestDriver* driver, |
| fdfw::DriverStartArgs start_args) { |
| EXPECT_FALSE(start_args.symbols().has_value()); |
| ValidateProgram(start_args.program(), binary, "false", "false", "false"); |
| }; |
| drivers.emplace_back(StartDriverWithConfig( |
| component_moniker, |
| { |
| .url = "fuchsia-boot:///#meta/node-" + std::to_string(i) + "-driver.cm", |
| .binary = binary, |
| .use_dynamic_linker = use_dynamic_linker(), |
| }, |
| std::move(start_handler), driver_config)); |
| } |
| |
| // Unbinding the second node stops all drivers bound in the sub-tree, in a |
| // depth-first order. |
| std::vector<size_t> indices; |
| size_t listeners = 0; |
| |
| // Start at 1 since 0 is the root driver. |
| for (size_t i = 1; i < drivers.size(); i++) { |
| ServeStopListener(std::move(drivers[i].controller), TeardownWatcher(listeners + 1, indices)); |
| listeners++; |
| } |
| |
| drivers[1].driver->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_THAT(indices, ElementsAre(10, 9, 8, 7, 6, 5, 4, 3, 2, 1)); |
| |
| StopDriverComponent(std::move(drivers[0].controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.node-0", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4.node-5", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4.node-5.node-6", "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4.node-5.node-6.node-7", |
| "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4.node-5.node-6.node-7.node-8", |
| "boot-drivers"), |
| CreateChildRef("dev.node-0.node-1.node-2.node-3.node-4.node-5.node-6.node-7.node-8.node-9", |
| "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then unbind the root node. |
| TEST_P(DriverRunnerTest, StartSecondDriver_UnbindRootNode) { |
| SetupDriverRunner(); |
| |
| std::vector<size_t> indices; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| StopListener& stop_listener_root = |
| ServeStopListener(std::move(root_driver->controller), TeardownWatcher(0, indices)); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener_second = |
| ServeStopListener(std::move(controller), TeardownWatcher(1, indices)); |
| |
| // Unbinding the root node stops all drivers. |
| root_driver->driver->DropNode(); |
| |
| while (!stop_listener_second.is_stopped() || !stop_listener_root.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| } |
| |
| // Start the second driver, and then Stop the root node. |
| TEST_P(DriverRunnerTest, StartSecondDriver_StopRootNode) { |
| SetupDriverRunner(); |
| |
| // These represent the order that Driver::Stop is called |
| std::vector<size_t> driver_stop_indices; |
| // These represent the order that the component stop happens. |
| std::vector<size_t> indices; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| StopListener& stop_listener_root = |
| ServeStopListener(std::move(root_driver->controller), TeardownWatcher(0, indices)); |
| |
| root_driver->driver->SetStopHandler( |
| [&driver_stop_indices]() { driver_stop_indices.push_back(0); }); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener_second = |
| ServeStopListener(std::move(controller), TeardownWatcher(1, indices)); |
| |
| driver->SetStopHandler([&driver_stop_indices]() { driver_stop_indices.push_back(1); }); |
| |
| // Simulate the Component Framework calling Stop on the root driver. |
| [[maybe_unused]] auto result = stop_listener_root.client()->Stop(); |
| |
| while (!stop_listener_second.is_stopped() || !stop_listener_root.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| // Check that the driver components were shut down in order. |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| // Check that Driver::Stop was called in order. |
| EXPECT_THAT(driver_stop_indices, ElementsAre(1, 0)); |
| } |
| |
| // Start the second driver, stop the root driver, and block while waiting on the |
| // second driver to shut down. |
| TEST_P(DriverRunnerTest, StartSecondDriver_BlockOnSecondDriver) { |
| SetupDriverRunner(); |
| |
| std::vector<size_t> indices; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| StopListener& stop_listener_root = |
| ServeStopListener(std::move(root_driver->controller), TeardownWatcher(0, indices)); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("second", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| auto [driver, controller] = StartSecondDriver("dev.second"); |
| StopListener& stop_listener_second = |
| ServeStopListener(std::move(controller), TeardownWatcher(1, indices)); |
| |
| // When the second driver gets asked to stop, don't drop the binding, |
| // which means DriverRunner will wait for the binding to drop. |
| driver->SetDontCloseBindingInStop(); |
| |
| // Stopping the root driver stops all drivers, but is blocked waiting on the |
| // second driver to stop. |
| [[maybe_unused]] auto result = stop_listener_root.client()->Stop(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| // Nothing has shut down yet, since we are waiting. |
| EXPECT_THAT(indices, ElementsAre()); |
| |
| // Attempt to add a child node to a removed node. |
| driver->AddChild("should_fail", false, true); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Unbind the second node, indicating the second driver has stopped, thereby |
| // continuing the stop sequence. |
| driver->CloseBinding(); |
| |
| while (!stop_listener_second.is_stopped() || !stop_listener_root.is_stopped()) { |
| RunLoopUntilIdle(); |
| } |
| |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| } |
| |
| TEST_P(DriverRunnerTest, CreateAndBindCompositeNodeSpec) { |
| SetupDriverRunner(); |
| |
| // Add a match for the composite node spec that we are creating. |
| std::string name("test-group"); |
| |
| const fuchsia_driver_framework::CompositeNodeSpec fidl_spec( |
| {.name = name, |
| .parents2 = std::vector<fuchsia_driver_framework::ParentSpec2>{ |
| fuchsia_driver_framework::ParentSpec2({ |
| .bind_rules = std::vector<fuchsia_driver_framework::BindRule2>(), |
| .properties = std::vector<fuchsia_driver_framework::NodeProperty2>(), |
| }), |
| fuchsia_driver_framework::ParentSpec2({ |
| .bind_rules = std::vector<fuchsia_driver_framework::BindRule2>(), |
| .properties = std::vector<fuchsia_driver_framework::NodeProperty2>(), |
| })}}); |
| |
| auto spec = std::make_unique<driver_manager::CompositeNodeSpec>( |
| driver_manager::CompositeNodeSpecCreateInfo{ |
| .name = name, |
| .parents = fidl_spec.parents2().value(), |
| }, |
| dispatcher(), &driver_runner()); |
| fidl::Arena<> arena; |
| |
| driver_runner().composite_node_spec_manager().AddSpec( |
| fidl::ToWire(arena, fidl_spec), std::move(spec), |
| [](fit::result<fuchsia_driver_framework::CompositeNodeSpecError> result) { |
| ASSERT_TRUE(result.is_ok()); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_EQ( |
| 2u, driver_runner().composite_node_spec_manager().specs().at(name)->GetParentNodes().size()); |
| |
| ASSERT_FALSE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->GetParentNodes().at(0)); |
| |
| ASSERT_FALSE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->GetParentNodes().at(1)); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child_0 = |
| root_driver->driver->AddChild("dev-group-0", false, false); |
| std::shared_ptr<CreatedChild> child_1 = |
| root_driver->driver->AddChild("dev-group-1", false, false); |
| |
| PrepareRealmForDriverComponentStart("dev.dev-group-1.test-group", |
| "fuchsia-boot:///#meta/composite-driver.cm"); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_TRUE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->GetParentNodes().at(0)); |
| |
| ASSERT_TRUE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->GetParentNodes().at(1)); |
| |
| auto composite_driver_config = kDefaultCompositeDriverPkgConfig; |
| std::string binary = std::string(composite_driver_config.main_module.open_path); |
| StartDriverHandler start_handler = [this, binary](TestDriver* driver, |
| fdfw::DriverStartArgs start_args) { |
| ValidateProgram(start_args.program(), binary, "true", "false", "false"); |
| }; |
| auto composite_driver = |
| StartDriverWithConfig("dev.dev-group-1.test-group", |
| { |
| .url = "fuchsia-boot:///#meta/composite-driver.cm", |
| .binary = binary, |
| .colocate = true, |
| .use_dynamic_linker = use_dynamic_linker(), |
| }, |
| std::move(start_handler), composite_driver_config); |
| ServeStopListener(std::move(composite_driver.controller)); |
| |
| auto hierarchy = Inspect(); |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, { |
| .node_name = {"node_topology"}, |
| .child_names = {"dev"}, |
| })); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, {.node_name = {"node_topology", "dev"}, |
| .child_names = {"dev-group-0", "dev-group-1"}, |
| .str_properties = { |
| {"driver", root_driver_url}, |
| }})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode( |
| hierarchy, |
| {.node_name = {"node_topology", "dev", "dev-group-0"}, .child_names = {"test-group"}})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode( |
| hierarchy, |
| {.node_name = {"node_topology", "dev", "dev-group-1"}, .child_names = {"test-group"}})); |
| |
| ASSERT_NO_FATAL_FAILURE( |
| CheckNode(hierarchy, {.node_name = {"node_topology", "dev", "dev-group-0", "test-group"}, |
| .str_properties = { |
| {"driver", "fuchsia-boot:///#meta/composite-driver.cm"}, |
| }})); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("root", "boot-drivers"), |
| CreateChildRef("dev.dev-group-1.test-group", "boot-drivers")}); |
| } |
| |
| // Start a driver and inspect the driver runner. |
| TEST_P(DriverRunnerTest, StartAndInspect) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| fdfw::NodeAddArgs args( |
| { |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "symbol-A", |
| .address = 0x2301, |
| }), |
| fdfw::NodeSymbol({ |
| .name = "symbol-B", |
| .address = 0x1985, |
| }), |
| }, |
| }, |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService(fdecl::OfferService({ |
| .source_name = "fuchsia.package.ProtocolA", |
| .source_instance_filter = std::vector<std::string>{"default"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("default", "instance-1"), |
| }, |
| }))), |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithService( |
| fdecl::OfferService( |
| { |
| .source_name = "fuchsia.package.ProtocolB", |
| .source_instance_filter = std::vector<std::string>{"default"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("default", "instance-1"), |
| }, |
| }))), |
| }, |
| }, |
| }); |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto hierarchy = Inspect(); |
| ASSERT_EQ("root", hierarchy.node().name()); |
| ASSERT_EQ(2ul, hierarchy.children().size()); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, { |
| .node_name = {"node_topology"}, |
| .child_names = {"dev"}, |
| })); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, {.node_name = {"node_topology", "dev"}, |
| .child_names = {"second"}, |
| .str_properties = { |
| {"driver", root_driver_url}, |
| }})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode( |
| hierarchy, {.node_name = {"node_topology", "dev", "second"}, |
| .child_names = {}, |
| .str_properties = {{"driver", "unbound"}}, |
| .array_str_properties = { |
| {"offers", {"fuchsia.package.ProtocolA", "fuchsia.package.ProtocolB"}}, |
| {"symbols", {"symbol-A", "symbol-B"}}, |
| }})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, { |
| .node_name = {"orphan_nodes"}, |
| })); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| TEST_P(DriverRunnerTest, TestTearDownNodeTreeWithManyChildren) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::vector<std::shared_ptr<CreatedChild>> children; |
| for (size_t i = 0; i < 100; i++) { |
| children.emplace_back(root_driver->driver->AddChild("child" + std::to_string(i), false, false)); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| } |
| |
| Unbind(); |
| } |
| |
| TEST_P(DriverRunnerTest, TestBindResultTracker) { |
| bool callback_called = false; |
| bool* callback_called_ptr = &callback_called; |
| |
| auto callback = [callback_called_ptr]( |
| fidl::VectorView<fuchsia_driver_development::wire::NodeBindingInfo> results) { |
| ASSERT_EQ(std::string_view("node_name"), results[0].node_name().get()); |
| ASSERT_EQ(std::string_view("driver_url"), results[0].driver_url().get()); |
| ASSERT_EQ(1ul, results.size()); |
| *callback_called_ptr = true; |
| }; |
| |
| driver_manager::BindResultTracker tracker(3, std::move(callback)); |
| ASSERT_EQ(false, callback_called); |
| tracker.ReportNoBind(); |
| ASSERT_EQ(false, callback_called); |
| tracker.ReportSuccessfulBind(std::string_view("node_name"), "driver_url"); |
| ASSERT_EQ(false, callback_called); |
| tracker.ReportNoBind(); |
| ASSERT_EQ(true, callback_called); |
| |
| callback_called = false; |
| auto callback_two = |
| [callback_called_ptr]( |
| fidl::VectorView<fuchsia_driver_development::wire::NodeBindingInfo> results) { |
| ASSERT_EQ(0ul, results.size()); |
| *callback_called_ptr = true; |
| }; |
| |
| driver_manager::BindResultTracker tracker_two(3, std::move(callback_two)); |
| ASSERT_EQ(false, callback_called); |
| tracker_two.ReportNoBind(); |
| ASSERT_EQ(false, callback_called); |
| tracker_two.ReportNoBind(); |
| ASSERT_EQ(false, callback_called); |
| tracker_two.ReportNoBind(); |
| ASSERT_EQ(true, callback_called); |
| |
| callback_called = false; |
| auto callback_three = |
| [callback_called_ptr]( |
| fidl::VectorView<fuchsia_driver_development::wire::NodeBindingInfo> results) { |
| ASSERT_EQ(std::string_view("node_name"), results[0].node_name().get()); |
| ASSERT_EQ(std::string_view("test_spec"), |
| results[0].composite_parents()[0].composite().spec().name().get()); |
| ASSERT_EQ(std::string_view("test_spec_2"), |
| results[0].composite_parents()[1].composite().spec().name().get()); |
| ASSERT_EQ(1ul, results.size()); |
| *callback_called_ptr = true; |
| }; |
| |
| driver_manager::BindResultTracker tracker_three(3, std::move(callback_three)); |
| ASSERT_EQ(false, callback_called); |
| tracker_three.ReportNoBind(); |
| ASSERT_EQ(false, callback_called); |
| tracker_three.ReportNoBind(); |
| ASSERT_EQ(false, callback_called); |
| |
| { |
| tracker_three.ReportSuccessfulBind(std::string_view("node_name"), |
| std::vector{ |
| fdfw::CompositeParent{{ |
| .composite = fdfw::CompositeInfo{{ |
| .spec = fdfw::CompositeNodeSpec{{ |
| .name = "test_spec", |
| }}, |
| }}, |
| }}, |
| fdfw::CompositeParent{{ |
| .composite = fdfw::CompositeInfo{{ |
| .spec = fdfw::CompositeNodeSpec{{ |
| .name = "test_spec_2", |
| }}, |
| }}, |
| }}, |
| }); |
| } |
| |
| ASSERT_EQ(true, callback_called); |
| } |
| |
| // Start the root driver, add a child node, and verify that the child node's device controller is |
| // reachable. |
| TEST_P(DriverRunnerTest, ConnectToDeviceController) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| const char* kChildName = "node-1"; |
| std::shared_ptr<CreatedChild> created_child = |
| root_driver->driver->AddChild(kChildName, true, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto device_controller = ConnectToDeviceController(kChildName); |
| |
| // Call one of the device controller's method in order to verify that the controller works. |
| device_controller->GetTopologicalPath().Then( |
| [](fidl::WireUnownedResult<fuchsia_device::Controller::GetTopologicalPath>& reply) { |
| ASSERT_EQ(reply.status(), ZX_OK); |
| ASSERT_TRUE(reply->is_ok()); |
| ASSERT_EQ(reply.value()->path.get(), "/dev/node-1"); |
| }); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| } |
| |
| // Start the root driver, add a child node, and verify that calling the child's device controller's |
| // `ConnectToController` FIDL method works. |
| TEST_P(DriverRunnerTest, ConnectToControllerFidlMethod) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| const char* kChildName = "node-1"; |
| std::shared_ptr<CreatedChild> created_child = |
| root_driver->driver->AddChild(kChildName, true, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto device_controller_1 = ConnectToDeviceController(kChildName); |
| |
| auto controller_endpoints = fidl::Endpoints<fuchsia_device::Controller>::Create(); |
| fidl::OneWayStatus result = |
| device_controller_1->ConnectToController(std::move(controller_endpoints.server)); |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(result.status(), ZX_OK); |
| |
| fidl::WireClient<fuchsia_device::Controller> device_controller_2{ |
| std::move(controller_endpoints.client), dispatcher()}; |
| |
| // Verify that the two device controllers connect to the same device server. |
| // This is done by verifying the topological paths returned by the device controllers are the |
| // same. |
| std::string topological_path_1; |
| device_controller_1->GetTopologicalPath().Then( |
| [&](fidl::WireUnownedResult<fuchsia_device::Controller::GetTopologicalPath>& reply) { |
| ASSERT_EQ(reply.status(), ZX_OK); |
| ASSERT_TRUE(reply->is_ok()); |
| topological_path_1 = reply.value()->path.get(); |
| }); |
| |
| std::string topological_path_2; |
| device_controller_2->GetTopologicalPath().Then( |
| [&](fidl::WireUnownedResult<fuchsia_device::Controller::GetTopologicalPath>& reply) { |
| ASSERT_EQ(reply.status(), ZX_OK); |
| ASSERT_TRUE(reply->is_ok()); |
| topological_path_2 = reply.value()->path.get(); |
| }); |
| |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(topological_path_1, topological_path_2); |
| } |
| |
| // Verify that device controller's Bind FIDL method works. |
| TEST_P(DriverRunnerTest, DeviceControllerBind) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| std::shared_ptr<CreatedChild> child = root_driver->driver->AddChild("child", false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| driver_index().set_match_callback([](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| EXPECT_EQ(args.driver_url_suffix().get(), second_driver_url); |
| return zx::ok(FakeDriverIndex::MatchResult{ |
| .url = second_driver_url, |
| }); |
| }); |
| |
| PrepareRealmForDriverComponentStart("dev.child", second_driver_url); |
| AssertNodeControllerBound(child); |
| |
| // Bind the driver. |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| auto device_controller = ConnectToDeviceController("child"); |
| device_controller->Bind(fidl::StringView::FromExternal(second_driver_url)) |
| .Then([](fidl::WireUnownedResult<fuchsia_device::Controller::Bind>& reply) { |
| ASSERT_EQ(reply.status(), ZX_OK); |
| }); |
| ASSERT_TRUE(RunLoopUntilIdle()); |
| |
| // Verify the driver was bound. |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| auto [driver, controller] = StartSecondDriver("dev.child"); |
| ServeStopListener(std::move(controller)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("root", "boot-drivers"), CreateChildRef("dev.child", "boot-drivers")}); |
| } |
| |
| TEST(CompositeServiceOfferTest, WorkingOffer) { |
| const std::string_view kServiceName = "fuchsia.service"; |
| |
| auto offer = driver_manager::NodeOffer{ |
| .source_collection = driver_manager::Collection::kBoot, |
| .transport = driver_manager::OfferTransport::ZirconTransport, |
| .service_name = std::string(kServiceName), |
| .source_instance_filter = std::vector<std::string>{"default", "instance-2"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("instance-1", "default"), |
| fdecl::NameMapping("instance-1", "instance-2"), |
| }, |
| }; |
| auto new_offer = driver_manager::CreateCompositeOffer(offer, "parent_node", false); |
| ASSERT_EQ(2ul, new_offer.renamed_instances.size()); |
| // Check that the default instance got renamed. |
| ASSERT_EQ(std::string("instance-1"), new_offer.renamed_instances[0].source_name()); |
| ASSERT_EQ(std::string("parent_node"), new_offer.renamed_instances[0].target_name()); |
| |
| // Check that a non-default instance stayed the same. |
| ASSERT_EQ(std::string("instance-1"), new_offer.renamed_instances[1].source_name()); |
| ASSERT_EQ(std::string("instance-2"), new_offer.renamed_instances[1].target_name()); |
| |
| ASSERT_EQ(2ul, new_offer.source_instance_filter.size()); |
| // Check that the default filter got renamed. |
| ASSERT_EQ(std::string("parent_node"), new_offer.source_instance_filter[0]); |
| |
| // Check that a non-default filter stayed the same. |
| ASSERT_EQ(std::string("instance-2"), new_offer.source_instance_filter[1]); |
| } |
| |
| TEST(CompositeServiceOfferTest, WorkingOfferPrimary) { |
| const std::string_view kServiceName = "fuchsia.service"; |
| |
| auto offer = driver_manager::NodeOffer{ |
| .source_collection = driver_manager::Collection::kBoot, |
| .transport = driver_manager::OfferTransport::ZirconTransport, |
| .service_name = std::string(kServiceName), |
| .source_instance_filter = std::vector<std::string>{"default", "instance-2"}, |
| .renamed_instances = |
| std::vector<fdecl::NameMapping>{ |
| fdecl::NameMapping("instance-1", "default"), |
| fdecl::NameMapping("instance-1", "instance-2"), |
| }, |
| }; |
| auto new_offer = driver_manager::CreateCompositeOffer(offer, "parent_node", true); |
| |
| ASSERT_EQ(3ul, new_offer.renamed_instances.size()); |
| // Check that the default instance stayed the same (because we're primary). |
| ASSERT_EQ(std::string("instance-1"), new_offer.renamed_instances[0].source_name()); |
| ASSERT_EQ(std::string("default"), new_offer.renamed_instances[0].target_name()); |
| |
| // Check that the default instance got renamed. |
| ASSERT_EQ(std::string("instance-1"), new_offer.renamed_instances[1].source_name()); |
| ASSERT_EQ(std::string("parent_node"), new_offer.renamed_instances[1].target_name()); |
| |
| // Check that a non-default instance stayed the same. |
| ASSERT_EQ(std::string("instance-1"), new_offer.renamed_instances[2].source_name()); |
| ASSERT_EQ(std::string("instance-2"), new_offer.renamed_instances[2].target_name()); |
| |
| ASSERT_EQ(3ul, new_offer.source_instance_filter.size()); |
| // Check that the default filter stayed the same (because we're primary). |
| EXPECT_EQ(std::string("default"), new_offer.source_instance_filter[0]); |
| |
| // Check that the default filter got renamed. |
| EXPECT_EQ(std::string("parent_node"), new_offer.source_instance_filter[1]); |
| |
| // Check that a non-default filter stayed the same. |
| EXPECT_EQ(std::string("instance-2"), new_offer.source_instance_filter[2]); |
| } |
| |
| TEST(NodeTest, ToCollection) { |
| async::Loop loop{&kAsyncLoopConfigNeverAttachToThread}; |
| constexpr char kGrandparentName[] = "grandparent"; |
| std::shared_ptr<Node> grandparent = |
| std::make_shared<Node>(kGrandparentName, std::weak_ptr<Node>{}, nullptr, loop.dispatcher()); |
| |
| constexpr char kParentName[] = "parent"; |
| std::shared_ptr<Node> parent = |
| std::make_shared<Node>(kParentName, grandparent, nullptr, loop.dispatcher()); |
| |
| constexpr char kChild1Name[] = "child1"; |
| std::shared_ptr<Node> child1 = |
| std::make_shared<Node>(kChild1Name, parent, nullptr, loop.dispatcher()); |
| |
| constexpr char kChild2Name[] = "child2"; |
| std::shared_ptr<Node> child2 = std::make_shared<Node>( |
| kChild2Name, std::vector{std::weak_ptr{parent}, std::weak_ptr{child1}}, |
| std::vector<std::string>{"parent", "child1"}, nullptr, loop.dispatcher(), 0); |
| |
| // Test parentless |
| EXPECT_EQ(ToCollection(*grandparent, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*grandparent, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*grandparent, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*grandparent, fdfw::DriverPackageType::kUniverse), |
| Collection::kFullPackage); |
| |
| // // Test single parent with grandparent collection set to none |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| // Test single parent with parent collection set to none |
| grandparent->set_collection(Collection::kBoot); |
| parent->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kPackage); |
| parent->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kFullPackage); |
| parent->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBoot), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kBase), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child1, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| // Test multi parent |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kNone); |
| child1->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kBoot); |
| child1->set_collection(Collection::kNone); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kNone); |
| parent->set_collection(Collection::kNone); |
| child1->set_collection(Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kFullPackage); |
| parent->set_collection(Collection::kFullPackage); |
| child1->set_collection(Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| // Test multi parent with one parent collection set to none |
| grandparent->set_collection(Collection::kBoot); |
| parent->set_collection(Collection::kNone); |
| child1->set_collection(Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kPackage); |
| parent->set_collection(Collection::kNone); |
| child1->set_collection(Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| |
| grandparent->set_collection(Collection::kFullPackage); |
| parent->set_collection(Collection::kNone); |
| child1->set_collection(Collection::kBoot); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBoot), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kBase), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kCached), Collection::kFullPackage); |
| EXPECT_EQ(ToCollection(*child2, fdfw::DriverPackageType::kUniverse), Collection::kFullPackage); |
| } |
| |
| // The tests are parameterized on whether to use the dynamic linker or not. |
| INSTANTIATE_TEST_SUITE_P(/* no prefix */, DriverRunnerTest, testing::Values(true, false), |
| [](const testing::TestParamInfo<bool>& info) { |
| if (info.param) { |
| return "DynamicLinker"; |
| } |
| return "Legacy"; |
| }); |
| |
| } // namespace driver_runner |