| // Copyright 2023 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. | |
| #ifndef LIB_DRIVER_TESTING_CPP_TEST_NODE_H_ | |
| #define LIB_DRIVER_TESTING_CPP_TEST_NODE_H_ | |
| #include <fidl/fuchsia.driver.framework/cpp/fidl.h> | |
| #include <zircon/availability.h> | |
| namespace fdf_testing { | |
| // This class serves as the FIDL servers for the fuchsia_driver_framework::Node and | |
| // fuchsia_driver_framework::NodeController to a driver under test. | |
| // | |
| // In a regular driver environment, this is provided by the driver framework's driver manager. | |
| // The Node FIDL is how drivers communicate with the driver framework to add child nodes into the | |
| // driver topology. | |
| // | |
| // The TestNode is part of the unit test's environment that is given to the driver under test using | |
| // it's start args. Therefore this class is where the test acquires the start args to give to the | |
| // driver, using CreateStartArgsAndServe. The result of this contains three values: | |
| // - The actual start args for the driver to be given to the driver's Start. | |
| // - A server end of the incoming directory the driver will use. This must be passed into the | |
| // |fdf_testing::TestEnvironment::Initialize| function. | |
| // - A client end to the outgoing directory of the driver. This can be used by the test to | |
| // talk to FIDLs provided by the driver under test. | |
| // | |
| // # Thread safety | |
| // | |
| // This class is thread-unsafe. Instances must be managed and used from a synchronized dispatcher. | |
| // See | |
| // https://fuchsia.dev/fuchsia-src/development/languages/c-cpp/thread-safe-async#synchronized-dispatcher | |
| // | |
| // If the dispatcher used for it is the foreground dispatcher, the TestNode does not need to be | |
| // wrapped in a DispatcherBound. | |
| // | |
| // If the dispatcher is a background dispatcher, the suggestion is to wrap this inside of an | |
| // |async_patterns::TestDispatcherBound|. | |
| class TestNode final : public fidl::WireServer<fuchsia_driver_framework::NodeController>, | |
| public fidl::WireServer<fuchsia_driver_framework::Node> { | |
| public: | |
| // This can be used to access child nodes that a driver has created. | |
| using ChildrenMap = std::unordered_map<std::string, TestNode>; | |
| // Used to see the result of using NodeController::RequestBind. | |
| struct BindData { | |
| bool force_rebind; | |
| std::string driver_url_suffix; | |
| }; | |
| // This is the bundle returned from |CreateStartArgsAndServe|. | |
| // As described above, start_args goes to the driver, incoming_directory_server goes to the | |
| // environment, and outgoing_directory_client may be used by the test. | |
| struct CreateStartArgsResult { | |
| fuchsia_driver_framework::DriverStartArgs start_args; | |
| fidl::ServerEnd<fuchsia_io::Directory> incoming_directory_server; | |
| fidl::ClientEnd<fuchsia_io::Directory> outgoing_directory_client; | |
| }; | |
| // If no dispatcher is provided, this will try to use the current fdf_dispatcher. If there | |
| // is also no fdf_dispatcher, it will try to use the thread's default async_dispatcher. | |
| explicit TestNode(std::string name, async_dispatcher_t* dispatcher = nullptr); | |
| ~TestNode() override; | |
| // Gets the children created by the driver on this node. | |
| ChildrenMap& children() { return children_; } | |
| // Gets the name of the node. | |
| const std::string& name() const { return name_; } | |
| // Create a channel pair, serve the server end, and return the client end. | |
| // This method is thread-unsafe. Must be called from the same context as the dispatcher. | |
| zx::result<fidl::ClientEnd<fuchsia_driver_framework::Node>> CreateNodeChannel(); | |
| // Serve the given server end. | |
| // This method is thread-unsafe. Must be called from the same context as the dispatcher. | |
| zx::result<> Serve(fidl::ServerEnd<fuchsia_driver_framework::Node> server_end); | |
| // Creates the start args for a driver, and serve the fdf::Node in it. | |
| zx::result<CreateStartArgsResult> CreateStartArgsAndServe(); | |
| // Connects to the devfs device this node is serving. | |
| zx::result<zx::channel> ConnectToDevice(); | |
| // Whether this node has started serving the fdf::Node, this happens when |Serve| | |
| // or |CreateNodeChannel| has been called. | |
| bool HasNode() { | |
| std::lock_guard guard(checker_); | |
| return node_binding_.has_value(); | |
| } | |
| // Get the node properties that this node was created with. Can be used to validate that a driver | |
| // is creating valid child nodes. | |
| std::vector<fuchsia_driver_framework::NodeProperty2> GetProperties() const { | |
| std::lock_guard guard(checker_); | |
| return properties_; | |
| } | |
| // Gets the bind data that were stored as part of NodeController::RequestBind calls. | |
| std::vector<BindData> GetBindData() const { | |
| std::lock_guard guard(checker_); | |
| return bind_data_; | |
| } | |
| // Get the dispatcher this Node object lives and serves FIDLs on. | |
| async_dispatcher_t* dispatcher() const { return dispatcher_; } | |
| private: | |
| void AddChild(AddChildRequestView request, AddChildCompleter::Sync& completer) override; | |
| #if FUCHSIA_API_LEVEL_AT_LEAST(HEAD) | |
| void ProvideResource( | |
| fidl::WireServer<fuchsia_driver_framework::Node>::ProvideResourceRequestView request, | |
| fidl::WireServer<fuchsia_driver_framework::Node>::ProvideResourceCompleter::Sync& completer) | |
| override; | |
| #endif | |
| void Remove(RemoveCompleter::Sync& completer) override { RemoveFromParent(); } | |
| void RequestBind(RequestBindRequestView request, RequestBindCompleter::Sync& completer) override; | |
| #if FUCHSIA_API_LEVEL_AT_LEAST(29) | |
| void WaitForDriver(WaitForDriverCompleter::Sync& completer) override; | |
| #endif | |
| void handle_unknown_method( | |
| fidl::UnknownMethodMetadata<fuchsia_driver_framework::NodeController> metadata, | |
| fidl::UnknownMethodCompleter::Sync& completer) override; | |
| void handle_unknown_method(fidl::UnknownMethodMetadata<fuchsia_driver_framework::Node> metadata, | |
| fidl::UnknownMethodCompleter::Sync& completer) override; | |
| void SetParent(TestNode* parent, | |
| fidl::ServerEnd<fuchsia_driver_framework::NodeController> controller); | |
| void SetProperties(std::vector<fuchsia_driver_framework::NodeProperty2> properties); | |
| void RemoveFromParent(); | |
| void RemoveChild(const std::string& name); | |
| void set_devfs_connector_client(fidl::ClientEnd<fuchsia_device_fs::Connector> client) { | |
| std::lock_guard guard(checker_); | |
| devfs_connector_client_.Bind(std::move(client), dispatcher_); | |
| } | |
| std::optional<fidl::ServerBinding<fuchsia_driver_framework::Node>> node_binding_ | |
| __TA_GUARDED(checker_); | |
| std::optional<fidl::ServerBinding<fuchsia_driver_framework::NodeController>> controller_binding_ | |
| __TA_GUARDED(checker_); | |
| async_dispatcher_t* dispatcher_; | |
| std::vector<fuchsia_driver_framework::NodeProperty2> properties_ __TA_GUARDED(checker_); | |
| std::vector<BindData> bind_data_ __TA_GUARDED(checker_); | |
| std::string name_ __TA_GUARDED(checker_); | |
| std::optional<std::reference_wrapper<TestNode>> parent_ __TA_GUARDED(checker_); | |
| ChildrenMap children_ __TA_GUARDED(checker_); | |
| fidl::WireClient<fuchsia_device_fs::Connector> devfs_connector_client_ __TA_GUARDED(checker_); | |
| async::synchronization_checker checker_; | |
| }; | |
| } // namespace fdf_testing | |
| #endif // LIB_DRIVER_TESTING_CPP_TEST_NODE_H_ |