| // Copyright 2021 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_SYS_COMPONENT_CPP_TESTING_SCOPED_CHILD_H_ |
| #define LIB_SYS_COMPONENT_CPP_TESTING_SCOPED_CHILD_H_ |
| |
| #include <fuchsia/component/cpp/fidl.h> |
| #include <fuchsia/component/decl/cpp/fidl.h> |
| #include <lib/async/dispatcher.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <lib/sys/cpp/service_directory.h> |
| #include <zircon/status.h> |
| #include <zircon/types.h> |
| |
| #include <memory> |
| |
| namespace component_testing { |
| |
| // A scoped instance of a dynamically created child component. This class |
| // will automatically destroy the child component once it goes out of scope. |
| class ScopedChild final { |
| public: |
| // Create a dynamic child component using the fuchsia.component.Realm API. |
| // |realm_proxy| must be bound to a connection to the fuchsia.component.Realm protocol. |
| // |collection| is the name of the collection to create the child under. This |
| // field must refer to a name in the current component's manifest file. |
| // |name| is the name to assign to the child. |
| // |url| is the component component URL of the child component. |
| static ScopedChild New(fuchsia::component::RealmSyncPtr realm_proxy, std::string collection, |
| std::string name, std::string url); |
| |
| // Same as above with a randomly generated `name`. |
| static ScopedChild New(fuchsia::component::RealmSyncPtr realm_proxy, std::string collection, |
| std::string url); |
| |
| // Create a dynamic child component using the fuchsia.component.Realm API. |
| // |collection| is the name of the collection to create the child under. This |
| // field must refer to a name in the current component's manifest file. |
| // |name| is the name to assign to the child. |
| // |url| is the component component URL of the child component. |
| // |svc| is used to make a connection to the protocol. If it's not provided, |
| // then the namespace entry will be used. |
| static ScopedChild New(std::string collection, std::string name, std::string url, |
| std::shared_ptr<sys::ServiceDirectory> svc = nullptr); |
| |
| // Same as above with a randomly generated `name`. |
| static ScopedChild New(std::string collection, std::string url, |
| std::shared_ptr<sys::ServiceDirectory> svc = nullptr); |
| |
| ~ScopedChild(); |
| |
| ScopedChild(ScopedChild&&) noexcept; |
| ScopedChild& operator=(ScopedChild&&) noexcept; |
| |
| ScopedChild(const ScopedChild&) = delete; |
| ScopedChild& operator=(const ScopedChild&) = delete; |
| |
| // When the destructor of this object is invoked, call |
| // fuchsia.component/Realm.DestroyChild asynchronously. This will make the |
| // operation non-blocking, which is useful if a test has a slow running |
| // teardown or if destruction *must* be async for any other reason. |
| // |dispatcher| must be non-null, or |async_get_default_dispatcher| must be |
| // configured to return a non-null value |
| // |dispatcher| must outlive the lifetime of this object. |
| void MakeTeardownAsync(async_dispatcher_t* dispatcher = nullptr); |
| |
| // Connect to an interface in the exposed directory of the child component. |
| // |
| // The discovery name of the interface is inferred from the C++ type of the |
| // interface. Callers can supply an interface name explicitly to override |
| // the default name. |
| // |
| // This overload for |ConnectAtExposedDir| panics if the connection operation |
| // doesn't return ZX_OK. Callers that wish to receive that status should use |
| // one of the other overloads that returns a |zx_status_t|. |
| // |
| // # Example |
| // |
| // ``` |
| // auto echo = instance.Connect<test::placeholders::Echo>(); |
| // ``` |
| template <typename Interface> |
| fidl::InterfacePtr<Interface> Connect( |
| const std::string& interface_name = Interface::Name_) const { |
| fidl::InterfacePtr<Interface> result; |
| zx_status_t status = Connect(interface_name, result.NewRequest().TakeChannel()); |
| ZX_ASSERT_MSG(status == ZX_OK, "Connect to protocol %s on the exposed dir of %s failed: %s", |
| interface_name.c_str(), child_ref_.name.c_str(), zx_status_get_string(status)); |
| return std::move(result); |
| } |
| |
| // SynchronousInterfacePtr method overload of |ConnectAtExposedDir|. See |
| // method above for more details. |
| template <typename Interface> |
| fidl::SynchronousInterfacePtr<Interface> ConnectSync( |
| const std::string& interface_name = Interface::Name_) const { |
| fidl::SynchronousInterfacePtr<Interface> result; |
| zx_status_t status = Connect(interface_name, result.NewRequest().TakeChannel()); |
| ZX_ASSERT_MSG(status == ZX_OK, "Connect to protocol %s on the exposed dir of %s failed", |
| interface_name.c_str(), child_ref_.name.c_str()); |
| return std::move(result); |
| } |
| |
| // Connect to exposed directory of the child component. |
| template <typename Interface> |
| zx_status_t Connect(fidl::InterfaceRequest<Interface> request) const { |
| return exposed_dir_.Connect<Interface>(std::move(request)); |
| } |
| |
| // Connect to an interface in the exposed directory using the supplied |
| // channel. |
| zx_status_t Connect(const std::string& interface_name, zx::channel request) const; |
| |
| // Get the child name of this instance. |
| std::string GetChildName() const; |
| |
| // Clone the exposed directory. |
| fidl::InterfaceHandle<fuchsia::io::Directory> CloneExposedDir() const { |
| return exposed_dir_.CloneChannel(); |
| } |
| |
| private: |
| ScopedChild(std::shared_ptr<sys::ServiceDirectory> svc, |
| fuchsia::component::decl::ChildRef child_ref, sys::ServiceDirectory exposed_dir); |
| |
| std::shared_ptr<sys::ServiceDirectory> svc_ = nullptr; |
| fuchsia::component::decl::ChildRef child_ref_; |
| sys::ServiceDirectory exposed_dir_; |
| async_dispatcher_t* dispatcher_ = nullptr; |
| bool has_moved_ = false; |
| }; |
| |
| } // namespace component_testing |
| |
| // Until all clients of the API have been migrated, keep the legacy namespace. |
| // TODO(fxbug.dev/90794): Remove this. |
| namespace sys { |
| namespace testing { |
| using component_testing::ScopedChild; |
| } // namespace testing |
| } // namespace sys |
| |
| #endif // LIB_SYS_COMPONENT_CPP_TESTING_SCOPED_CHILD_H_ |