| // 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/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_node_spec_v2.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; |
| |
| // Start the root driver. |
| TEST_F(DriverRunnerTest, StartRootDriver) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver. Make sure that the driver is stopped before the Component is exited. |
| TEST_F(DriverRunnerTest, StartRootDriver_DriverStopBeforeComponentExit) { |
| SetupDriverRunner(); |
| |
| std::vector<size_t> event_order; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| fidl::WireSharedClient<frunner::ComponentController> root_client( |
| std::move(root_driver->controller), dispatcher(), 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_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, add a child node, then remove it. |
| TEST_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add two child nodes with duplicate names. |
| TEST_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node with an offer that is missing a |
| // source. |
| TEST_F(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::WithProtocol(fdecl::OfferProtocol({ |
| .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("dev", "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_F(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::WithProtocol(fdecl::OfferProtocol({ |
| .source = fdecl::Ref::WithSelf(fdecl::SelfRef()), |
| .source_name = "fuchsia.package.Protocol", |
| }))), |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithProtocol(fdecl::OfferProtocol({ |
| .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("dev", "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_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node that has a symbol without an |
| // address. |
| TEST_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and add a child node that has a symbol without a name. |
| TEST_F(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("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then start a second driver in a new driver host. |
| TEST_F(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::kProtocol); |
| auto& protocol = offers[0].protocol().value(); |
| |
| ASSERT_TRUE(protocol.source().has_value()); |
| ASSERT_TRUE(protocol.source().value().Which() == fdecl::Ref::Tag::kChild); |
| auto& source_ref = protocol.source().value().child().value(); |
| EXPECT_EQ("dev", source_ref.name()); |
| EXPECT_EQ("boot-drivers", source_ref.collection().value_or("missing")); |
| |
| ASSERT_TRUE(protocol.source_name().has_value()); |
| EXPECT_EQ("fuchsia.package.Protocol", protocol.source_name().value()); |
| |
| ASSERT_TRUE(protocol.target_name().has_value()); |
| EXPECT_EQ("fuchsia.package.Renamed", protocol.target_name()); |
| }); |
| |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "sym", |
| .address = 0xfeed, |
| }), |
| }, |
| }, |
| .offers2 = |
| { |
| { |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.Protocol", |
| .target_name = "fuchsia.package.Renamed", |
| }))), |
| }, |
| }, |
| }); |
| |
| bool did_bind = false; |
| auto on_bind = [&did_bind]() { did_bind = true; }; |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false, std::move(on_bind)); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_TRUE(did_bind); |
| |
| auto [driver, controller] = StartSecondDriver(); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then start a second driver in the same driver |
| // host. |
| TEST_F(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::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.Protocol", |
| .target_name = "fuchsia.package.Renamed", |
| }))), |
| }, |
| }, |
| }); |
| |
| bool did_bind = false; |
| auto on_bind = [&did_bind]() { did_bind = true; }; |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false, std::move(on_bind)); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_TRUE(did_bind); |
| |
| StartDriverHandler start_handler = [](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(), second_driver_binary, "true", "false", "false"); |
| }; |
| auto [driver, controller] = StartDriver( |
| { |
| .url = second_driver_url, |
| .binary = second_driver_binary, |
| .colocate = true, |
| }, |
| std::move(start_handler)); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "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_F(DriverRunnerTest, StartSecondDriver_UseProperties) { |
| FakeDriverIndex driver_index( |
| dispatcher(), [](auto args) -> zx::result<FakeDriverIndex::MatchResult> { |
| if (args.has_properties() && args.properties()[0].key.is_int_value() && |
| args.properties()[0].key.int_value() == 0x1985 && |
| args.properties()[0].value.is_int_value() && |
| args.properties()[0].value.int_value() == 0x2301 |
| |
| && args.properties()[1].key.is_string_value() && |
| args.properties()[1].key.string_value().get() == |
| bind_fuchsia_platform::DRIVER_FRAMEWORK_VERSION && |
| args.properties()[1].value.is_int_value() && args.properties()[1].value.int_value() == 2 |
| |
| ) { |
| 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 = |
| { |
| { |
| fdfw::NodeProperty({ |
| .key = fdfw::NodePropertyKey::WithIntValue(0x1985), |
| .value = fdfw::NodePropertyValue::WithIntValue(0x2301), |
| }), |
| }, |
| }, |
| }); |
| |
| std::shared_ptr<CreatedChild> child = |
| root_driver->driver->AddChild(std::move(args), false, false); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| auto [driver, controller] = StartSecondDriver(true); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.second", "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_F(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(); |
| |
| 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. |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| zx_signals_t signals = 0; |
| ASSERT_EQ(ZX_OK, |
| controller.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // 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(); |
| |
| // 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("dev", "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_F(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(false, true); |
| |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Stop the driver host binding. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_1->CloseBinding(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| zx_signals_t signals = 0; |
| ASSERT_EQ(ZX_OK, controller_1.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), |
| &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_2, controller_2] = StartSecondDriver(false, true); |
| |
| // Drop the node client binding. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_2->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| signals = 0; |
| ASSERT_EQ(ZX_OK, controller_2.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), |
| &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_3, controller_3] = StartSecondDriver(false, true); |
| |
| // 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()); |
| |
| signals = 0; |
| ASSERT_EQ(ZX_OK, controller_3.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), |
| &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_4, controller_4] = StartSecondDriver(false, true); |
| |
| // 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()); |
| |
| signals = 0; |
| ASSERT_EQ(ZX_OK, controller_4.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), |
| &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_5, controller_5] = StartSecondDriver(false, true); |
| |
| // Finally don't RunLoopUntilIdle in between the two. |
| PrepareRealmForSecondDriverComponentStart(); |
| driver_5->CloseBinding(); |
| driver_5->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| signals = 0; |
| ASSERT_EQ(ZX_OK, controller_5.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), |
| &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| // The driver host and driver should be started again by the node. |
| auto [driver_6, controller_6] = StartSecondDriver(false, true); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "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_F(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(true, false, true); |
| |
| EXPECT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // The root driver adds a node that only binds after a RequestBind() call. |
| TEST_F(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([](auto result) {}); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| auto [driver, controller] = StartSecondDriver(); |
| |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "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_F(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([](auto result) {}); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| ASSERT_EQ(0u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| // Get the second-driver running. |
| auto [driver_1, controller_1] = StartSecondDriver(); |
| |
| // 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([](auto result) {}); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Get the second-driver running again. |
| auto [driver_2, controller_2] = StartSecondDriver(); |
| |
| // 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([](auto result) {}); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| // Get the third-driver running. |
| StartDriverHandler start_handler = [&](TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| EXPECT_FALSE(start_args.symbols().has_value()); |
| ValidateProgram(start_args.program(), "driver/third-driver.so", "false", "false", "false"); |
| }; |
| auto third_driver = StartDriver( |
| { |
| .url = "fuchsia-boot:///#meta/third-driver.cm", |
| .binary = "driver/third-driver.so", |
| }, |
| std::move(start_handler)); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({ |
| CreateChildRef("dev", "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_F(DriverRunnerTest, StartSecondDriver_UnknownNode) { |
| SetupDriverRunner(); |
| |
| 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({.close = true}); |
| ASSERT_EQ(1u, driver_runner().bind_manager().NumOrphanedNodes()); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("dev", "boot-drivers")}); |
| } |
| |
| // Start the root driver, and then add a child node that only binds to a base driver. |
| TEST_F(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().ScheduleWatchForDriverLoad(); |
| 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("dev", "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then unbind its associated node. |
| TEST_F(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(); |
| |
| // Unbinding the second node stops the driver bound to it. |
| driver->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| zx_signals_t signals = 0; |
| ASSERT_EQ(ZX_OK, |
| controller.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start the second driver, and then close the associated Driver protocol |
| // channel. |
| TEST_F(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(); |
| |
| // Closing the Driver protocol channel of the second driver causes the driver |
| // to be stopped. |
| driver->CloseBinding(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| zx_signals_t signals = 0; |
| ASSERT_EQ(ZX_OK, |
| controller.channel().wait_one(ZX_CHANNEL_PEER_CLOSED, zx::time::infinite(), &signals)); |
| ASSERT_TRUE(signals & ZX_CHANNEL_PEER_CLOSED); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.second", "boot-drivers")}); |
| } |
| |
| // Start a chain of drivers, and then unbind the second driver's node. |
| TEST_F(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()); |
| |
| StartDriverHandler start_handler = [](TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| EXPECT_FALSE(start_args.symbols().has_value()); |
| ValidateProgram(start_args.program(), "driver/driver.so", "false", "false", "false"); |
| }; |
| drivers.emplace_back(StartDriver( |
| { |
| .url = "fuchsia-boot:///#meta/node-" + std::to_string(i) + "-driver.cm", |
| .binary = "driver/driver.so", |
| }, |
| std::move(start_handler))); |
| } |
| |
| // 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::WireSharedClient<frunner::ComponentController>> clients; |
| |
| // Start at 1 since 0 is the root driver. |
| for (size_t i = 1; i < drivers.size(); i++) { |
| clients.emplace_back(std::move(drivers[i].controller), dispatcher(), |
| TeardownWatcher(clients.size() + 1, indices)); |
| } |
| |
| 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("dev", "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_F(DriverRunnerTest, StartSecondDriver_UnbindRootNode) { |
| 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(); |
| |
| // Unbinding the root node stops all drivers. |
| std::vector<size_t> indices; |
| fidl::WireSharedClient<frunner::ComponentController> root_client( |
| std::move(root_driver->controller), dispatcher(), TeardownWatcher(0, indices)); |
| fidl::WireSharedClient<frunner::ComponentController> second_client( |
| std::move(controller), dispatcher(), TeardownWatcher(1, indices)); |
| root_driver->driver->DropNode(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| } |
| |
| // Start the second driver, and then Stop the root node. |
| TEST_F(DriverRunnerTest, StartSecondDriver_StopRootNode) { |
| SetupDriverRunner(); |
| |
| // These represent the order that Driver::Stop is called |
| std::vector<size_t> driver_stop_indices; |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| 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(); |
| |
| driver->SetStopHandler([&driver_stop_indices]() { driver_stop_indices.push_back(1); }); |
| |
| std::vector<size_t> indices; |
| fidl::WireSharedClient<frunner::ComponentController> root_client( |
| std::move(root_driver->controller), dispatcher(), TeardownWatcher(0, indices)); |
| fidl::WireSharedClient<frunner::ComponentController> second_client( |
| std::move(controller), dispatcher(), TeardownWatcher(1, indices)); |
| |
| // Simulate the Component Framework calling Stop on the root driver. |
| [[maybe_unused]] auto result = root_client->Stop(); |
| |
| EXPECT_TRUE(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, and then stop the root driver. |
| TEST_F(DriverRunnerTest, StartSecondDriver_StopRootDriver) { |
| 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(); |
| |
| // Stopping the root driver stops all drivers. |
| std::vector<size_t> indices; |
| fidl::WireSharedClient<frunner::ComponentController> root_client( |
| std::move(root_driver->controller), dispatcher(), TeardownWatcher(0, indices)); |
| fidl::WireSharedClient<frunner::ComponentController> second_client( |
| std::move(controller), dispatcher(), TeardownWatcher(1, indices)); |
| [[maybe_unused]] auto result = root_client->Stop(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| } |
| |
| // Start the second driver, stop the root driver, and block while waiting on the |
| // second driver to shut down. |
| TEST_F(DriverRunnerTest, StartSecondDriver_BlockOnSecondDriver) { |
| 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(); |
| |
| // 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. |
| std::vector<size_t> indices; |
| fidl::WireSharedClient<frunner::ComponentController> root_client( |
| std::move(root_driver->controller), dispatcher(), TeardownWatcher(0, indices)); |
| fidl::WireSharedClient<frunner::ComponentController> second_client( |
| std::move(controller), dispatcher(), TeardownWatcher(1, indices)); |
| [[maybe_unused]] auto result = 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(); |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| EXPECT_THAT(indices, ElementsAre(1, 0)); |
| } |
| |
| TEST_F(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, |
| .parents = std::vector<fuchsia_driver_framework::ParentSpec>{ |
| fuchsia_driver_framework::ParentSpec({ |
| .bind_rules = std::vector<fuchsia_driver_framework::BindRule>(), |
| .properties = std::vector<fuchsia_driver_framework::NodeProperty>(), |
| }), |
| fuchsia_driver_framework::ParentSpec({ |
| .bind_rules = std::vector<fuchsia_driver_framework::BindRule>(), |
| .properties = std::vector<fuchsia_driver_framework::NodeProperty>(), |
| })}}); |
| |
| auto spec = std::make_unique<driver_manager::CompositeNodeSpecV2>( |
| driver_manager::CompositeNodeSpecCreateInfo{ |
| .name = name, |
| .size = 2, |
| }, |
| dispatcher(), &driver_runner()); |
| fidl::Arena<> arena; |
| auto added = driver_runner().composite_node_spec_manager().AddSpec(fidl::ToWire(arena, fidl_spec), |
| std::move(spec)); |
| ASSERT_TRUE(added.is_ok()); |
| |
| EXPECT_TRUE(RunLoopUntilIdle()); |
| |
| ASSERT_EQ(2u, |
| driver_runner().composite_node_spec_manager().specs().at(name)->parent_specs().size()); |
| |
| ASSERT_FALSE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->parent_specs().at(0)); |
| |
| ASSERT_FALSE( |
| driver_runner().composite_node_spec_manager().specs().at(name)->parent_specs().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)->parent_specs().at(0)); |
| |
| ASSERT_TRUE(driver_runner().composite_node_spec_manager().specs().at(name)->parent_specs().at(1)); |
| |
| StartDriverHandler start_handler = [](TestDriver* driver, fdfw::DriverStartArgs start_args) { |
| ValidateProgram(start_args.program(), "driver/composite-driver.so", "true", "false", "false"); |
| }; |
| auto composite_driver = StartDriver( |
| { |
| .url = "fuchsia-boot:///#meta/composite-driver.cm", |
| .binary = "driver/composite-driver.so", |
| .colocate = true, |
| }, |
| std::move(start_handler)); |
| |
| 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("dev", "boot-drivers"), |
| CreateChildRef("dev.dev-group-1.test-group", "boot-drivers")}); |
| } |
| |
| TEST_F(DriverRunnerTest, StartAndInspectLegacyOffers) { |
| SetupDriverRunner(); |
| |
| auto root_driver = StartRootDriver(); |
| ASSERT_EQ(ZX_OK, root_driver.status_value()); |
| |
| PrepareRealmForSecondDriverComponentStart(); |
| fdfw::NodeAddArgs args({ |
| .name = "second", |
| .offers = |
| { |
| { |
| |
| fuchsia_component_decl::Offer::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.ProtocolA", |
| .target_name = "fuchsia.package.RenamedA", |
| })), |
| |
| fuchsia_component_decl::Offer::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.ProtocolB", |
| .target_name = "fuchsia.package.RenamedB", |
| })), |
| }, |
| }, |
| .symbols = |
| { |
| { |
| fdfw::NodeSymbol({ |
| .name = "symbol-A", |
| .address = 0x2301, |
| }), |
| fdfw::NodeSymbol({ |
| .name = "symbol-B", |
| .address = 0x1985, |
| }), |
| }, |
| }, |
| }); |
| 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 = { |
| {"offers", "fuchsia.package.RenamedA, fuchsia.package.RenamedB"}, |
| {"symbols", "symbol-A, symbol-B"}, |
| {"driver", "unbound"}, |
| }})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, { |
| .node_name = {"orphan_nodes"}, |
| })); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("dev", "boot-drivers")}); |
| } |
| |
| // Start a driver and inspect the driver runner. |
| TEST_F(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::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.ProtocolA", |
| .target_name = "fuchsia.package.RenamedA", |
| }))), |
| fuchsia_driver_framework::Offer::WithZirconTransport( |
| fuchsia_component_decl::Offer::WithProtocol(fdecl::OfferProtocol({ |
| .source_name = "fuchsia.package.ProtocolB", |
| .target_name = "fuchsia.package.RenamedB", |
| }))), |
| }, |
| }, |
| }); |
| 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 = { |
| {"offers", "fuchsia.package.RenamedA, fuchsia.package.RenamedB"}, |
| {"symbols", "symbol-A, symbol-B"}, |
| {"driver", "unbound"}, |
| }})); |
| |
| ASSERT_NO_FATAL_FAILURE(CheckNode(hierarchy, { |
| .node_name = {"orphan_nodes"}, |
| })); |
| |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren({CreateChildRef("dev", "boot-drivers")}); |
| } |
| |
| TEST_F(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_F(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.count()); |
| *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.count()); |
| *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.count()); |
| *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_F(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_F(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); |
| |
| zx::result controller_endpoints = fidl::CreateEndpoints<fuchsia_device::Controller>(); |
| ASSERT_EQ(controller_endpoints.status_value(), ZX_OK); |
| 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_F(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(); |
| driver->CloseBinding(); |
| driver->DropNode(); |
| StopDriverComponent(std::move(root_driver->controller)); |
| realm().AssertDestroyedChildren( |
| {CreateChildRef("dev", "boot-drivers"), CreateChildRef("dev.child", "boot-drivers")}); |
| } |
| |
| TEST(CompositeServiceOfferTest, WorkingOffer) { |
| const std::string_view kServiceName = "fuchsia.service"; |
| fidl::Arena<> arena; |
| auto service = fdecl::wire::OfferService::Builder(arena); |
| service.source_name(arena, kServiceName); |
| service.target_name(arena, kServiceName); |
| |
| fidl::VectorView<fdecl::wire::NameMapping> mappings(arena, 2); |
| mappings[0].source_name = fidl::StringView(arena, "instance-1"); |
| mappings[0].target_name = fidl::StringView(arena, "default"); |
| |
| mappings[1].source_name = fidl::StringView(arena, "instance-1"); |
| mappings[1].target_name = fidl::StringView(arena, "instance-2"); |
| service.renamed_instances(mappings); |
| |
| fidl::VectorView<fidl::StringView> filters(arena, 2); |
| filters[0] = fidl::StringView(arena, "default"); |
| filters[1] = fidl::StringView(arena, "instance-2"); |
| service.source_instance_filter(filters); |
| |
| auto offer = fdecl::wire::Offer::WithService(arena, service.Build()); |
| auto new_offer = driver_manager::CreateCompositeServiceOffer(arena, offer, "parent_node", false); |
| ASSERT_TRUE(new_offer); |
| |
| ASSERT_EQ(2ul, new_offer->service().renamed_instances().count()); |
| // Check that the default instance got renamed. |
| ASSERT_EQ(std::string("instance-1"), |
| std::string(new_offer->service().renamed_instances()[0].source_name.get())); |
| ASSERT_EQ(std::string("parent_node"), |
| std::string(new_offer->service().renamed_instances()[0].target_name.get())); |
| |
| // Check that a non-default instance stayed the same. |
| ASSERT_EQ(std::string("instance-1"), |
| std::string(new_offer->service().renamed_instances()[1].source_name.get())); |
| ASSERT_EQ(std::string("instance-2"), |
| std::string(new_offer->service().renamed_instances()[1].target_name.get())); |
| |
| ASSERT_EQ(2ul, new_offer->service().source_instance_filter().count()); |
| // Check that the default filter got renamed. |
| ASSERT_EQ(std::string("parent_node"), |
| std::string(new_offer->service().source_instance_filter()[0].get())); |
| |
| // Check that a non-default filter stayed the same. |
| ASSERT_EQ(std::string("instance-2"), |
| std::string(new_offer->service().source_instance_filter()[1].get())); |
| } |
| |
| TEST(CompositeServiceOfferTest, WorkingOfferPrimary) { |
| const std::string_view kServiceName = "fuchsia.service"; |
| fidl::Arena<> arena; |
| auto service = fdecl::wire::OfferService::Builder(arena); |
| service.source_name(arena, kServiceName); |
| service.target_name(arena, kServiceName); |
| |
| fidl::VectorView<fdecl::wire::NameMapping> mappings(arena, 2); |
| mappings[0].source_name = fidl::StringView(arena, "instance-1"); |
| mappings[0].target_name = fidl::StringView(arena, "default"); |
| |
| mappings[1].source_name = fidl::StringView(arena, "instance-1"); |
| mappings[1].target_name = fidl::StringView(arena, "instance-2"); |
| service.renamed_instances(mappings); |
| |
| fidl::VectorView<fidl::StringView> filters(arena, 2); |
| filters[0] = fidl::StringView(arena, "default"); |
| filters[1] = fidl::StringView(arena, "instance-2"); |
| service.source_instance_filter(filters); |
| |
| auto offer = fdecl::wire::Offer::WithService(arena, service.Build()); |
| auto new_offer = driver_manager::CreateCompositeServiceOffer(arena, offer, "parent_node", true); |
| ASSERT_TRUE(new_offer); |
| |
| ASSERT_EQ(3ul, new_offer->service().renamed_instances().count()); |
| // Check that the default instance stayed the same (because we're primary). |
| ASSERT_EQ(std::string("instance-1"), |
| std::string(new_offer->service().renamed_instances()[0].source_name.get())); |
| ASSERT_EQ(std::string("default"), |
| std::string(new_offer->service().renamed_instances()[0].target_name.get())); |
| |
| // Check that the default instance got renamed. |
| ASSERT_EQ(std::string("instance-1"), |
| std::string(new_offer->service().renamed_instances()[1].source_name.get())); |
| ASSERT_EQ(std::string("parent_node"), |
| std::string(new_offer->service().renamed_instances()[1].target_name.get())); |
| |
| // Check that a non-default instance stayed the same. |
| ASSERT_EQ(std::string("instance-1"), |
| std::string(new_offer->service().renamed_instances()[2].source_name.get())); |
| ASSERT_EQ(std::string("instance-2"), |
| std::string(new_offer->service().renamed_instances()[2].target_name.get())); |
| |
| ASSERT_EQ(3ul, new_offer->service().source_instance_filter().count()); |
| // Check that the default filter stayed the same (because we're primary). |
| EXPECT_EQ(std::string("default"), |
| std::string(new_offer->service().source_instance_filter()[0].get())); |
| |
| // Check that the default filter got renamed. |
| EXPECT_EQ(std::string("parent_node"), |
| std::string(new_offer->service().source_instance_filter()[1].get())); |
| |
| // Check that a non-default filter stayed the same. |
| EXPECT_EQ(std::string("instance-2"), |
| std::string(new_offer->service().source_instance_filter()[2].get())); |
| } |
| |
| TEST(NodeTest, ToCollection) { |
| async::Loop loop{&kAsyncLoopConfigNeverAttachToThread}; |
| InspectManager inspect(loop.dispatcher()); |
| constexpr uint32_t kProtocolId = 0; |
| |
| constexpr char kGrandparentName[] = "grandparent"; |
| std::shared_ptr<Node> grandparent = std::make_shared<Node>( |
| kGrandparentName, std::vector<std::weak_ptr<Node>>{}, nullptr, loop.dispatcher(), |
| inspect.CreateDevice(kGrandparentName, zx::vmo{}, kProtocolId)); |
| |
| constexpr char kParentName[] = "parent"; |
| std::shared_ptr<Node> parent = std::make_shared<Node>( |
| kParentName, std::vector<std::weak_ptr<Node>>{grandparent}, nullptr, loop.dispatcher(), |
| inspect.CreateDevice(kParentName, zx::vmo{}, kProtocolId)); |
| |
| constexpr char kChild1Name[] = "child1"; |
| std::shared_ptr<Node> child1 = std::make_shared<Node>( |
| kChild1Name, std::vector<std::weak_ptr<Node>>{parent}, nullptr, loop.dispatcher(), |
| inspect.CreateDevice(kChild1Name, zx::vmo{}, kProtocolId)); |
| |
| constexpr char kChild2Name[] = "child2"; |
| std::shared_ptr<Node> child2 = std::make_shared<Node>( |
| kChild2Name, std::vector<std::weak_ptr<Node>>{parent, child1}, nullptr, loop.dispatcher(), |
| inspect.CreateDevice(kChild2Name, zx::vmo{}, kProtocolId), 0, |
| driver_manager::NodeType::kComposite); |
| |
| // 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); |
| } |
| |
| } // namespace driver_runner |