blob: dc045be952addbe973108eaf79f6e280407babab [file] [log] [blame]
// 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_REALM_BUILDER_H_
#define LIB_SYS_COMPONENT_CPP_TESTING_REALM_BUILDER_H_
#include <fuchsia/component/config/cpp/fidl.h>
#include <fuchsia/component/cpp/fidl.h>
#include <fuchsia/component/decl/cpp/fidl.h>
#include <fuchsia/component/runner/cpp/fidl.h>
#include <fuchsia/component/test/cpp/fidl.h>
#include <fuchsia/io/cpp/fidl.h>
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/sys/component/cpp/testing/internal/local_component_runner.h>
#include <lib/sys/component/cpp/testing/realm_builder_types.h>
#include <lib/sys/component/cpp/testing/scoped_child.h>
#include <lib/sys/cpp/service_directory.h>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
namespace component_testing {
// Default child options provided to all components.
const ChildOptions kDefaultChildOptions{.startup_mode = StartupMode::LAZY, .environment = ""};
// Root of a constructed Realm. This object can not be instantiated directly.
// Instead, it can only be constructed with the Realm::Builder/Build().
class RealmRoot final {
public:
RealmRoot(RealmRoot&& other) = default;
RealmRoot& operator=(RealmRoot&& other) = default;
RealmRoot(RealmRoot& other) = delete;
RealmRoot& operator=(RealmRoot& other) = delete;
// Connect to an interface in the exposed directory of the root 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 |Connect| 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 = realm.Connect<test::placeholders::Echo>();
// ```
template <typename Interface>
fidl::InterfacePtr<Interface> Connect(
const std::string& interface_name = Interface::Name_) const {
return root_.Connect<Interface>(interface_name);
}
// SynchronousInterfacePtr method overload of |Connect|. See
// method above for more details.
template <typename Interface>
fidl::SynchronousInterfacePtr<Interface> ConnectSync(
const std::string& interface_name = Interface::Name_) const {
return root_.ConnectSync<Interface>(interface_name);
}
// Connect to exposed directory of the root component.
template <typename Interface>
zx_status_t Connect(fidl::InterfaceRequest<Interface> request) const {
return root_.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;
// Return a handle to the exposed directory of the root component.
fidl::InterfaceHandle<fuchsia::io::Directory> CloneRoot() const {
return root_.CloneExposedDir();
}
// Get the child name of the root component.
std::string GetChildName() const;
private:
// Friend classes are needed because the constructor is private.
friend class Realm;
friend class RealmBuilder;
explicit RealmRoot(std::unique_ptr<internal::LocalComponentRunner> local_component_runner,
ScopedChild root);
std::unique_ptr<internal::LocalComponentRunner> local_component_runner_;
ScopedChild root_;
};
// A `Realm` describes a component instance together with its children.
// Clients can use this class to build a realm from scratch,
// programmatically adding children and routes.
//
// Clients may also use this class to recursively build sub-realms by calling
// `AddChildRealm`.
// For more information about RealmBuilder, see the following link.
// https://fuchsia.dev/fuchsia-src/development/testing/components/realm_builder
// For examples on how to use this library, see the integration tests
// found at //sdk/cpp/tests/realm_builder_test.cc
class Realm final {
public:
Realm(Realm&&) = default;
Realm& operator=(Realm&&) = default;
Realm(const Realm&) = delete;
Realm operator=(const Realm&) = delete;
// Add a v2 component (.cm) to this Realm.
// Names must be unique. Duplicate names will result in a panic.
Realm& AddChild(const std::string& child_name, const std::string& url,
ChildOptions options = kDefaultChildOptions);
// Add a v1 component (.cmx) to this Realm.
// Names must be unique. Duplicate names will result in a panic.
Realm& AddLegacyChild(const std::string& child_name, const std::string& url,
ChildOptions options = kDefaultChildOptions);
// Add a component implemented by a C++ class. The class should inherit from
// the `Local` class. The pointer must not be nullptr. If it is, this
// method will panic. It is expected the pointer will be valid for the lifetime
// of the constructed RealmRoot below.
// Names must be unique. Duplicate names will result in a panic.
Realm& AddLocalChild(const std::string& child_name, LocalComponent* local_impl,
ChildOptions options = kDefaultChildOptions);
// Create a sub realm as child of this Realm instance. The constructed
// Realm is returned.
Realm AddChildRealm(const std::string& child_name, ChildOptions options = kDefaultChildOptions);
// Route a capability from one child to another.
Realm& AddRoute(Route route);
/// Offers a directory capability to a component in this realm. The
/// directory will be read-only (i.e. have `r*` rights), and will have the
/// contents described in `directory`.
Realm& RouteReadOnlyDirectory(const std::string& name, std::vector<Ref> to,
DirectoryContents directory);
/// Replaces the value of a given configuration field
Realm& ReplaceConfigValue(const std::string& name, const std::string& key, ConfigValue value);
/// Updates the Component decl of the given child.
void ReplaceComponentDecl(const std::string& child_name,
fuchsia::component::decl::Component decl);
/// Updates the Component decl of this Realm.
void ReplaceRealmDecl(fuchsia::component::decl::Component decl);
/// Fetches the Component decl of the given child.
fuchsia::component::decl::Component GetComponentDecl(const std::string& child_name);
/// Fetches the Component decl of this Realm.
fuchsia::component::decl::Component GetRealmDecl();
friend class RealmBuilder;
private:
explicit Realm(fuchsia::component::test::RealmSyncPtr realm_proxy,
std::shared_ptr<internal::LocalComponentRunner::Builder> runner_builder,
std::vector<std::string> scope = {});
std::string GetResolvedName(const std::string& child_name);
fuchsia::component::test::RealmSyncPtr realm_proxy_;
std::shared_ptr<internal::LocalComponentRunner::Builder> runner_builder_;
std::vector<std::string> scope_;
};
// Use this Builder class to construct a Realm object.
class RealmBuilder final {
public:
// Factory method to create a new Realm::Builder object.
// |svc| must outlive the RealmBuilder object and created Realm object.
// If it's nullptr, then the current process' "/svc" namespace entry is used.
static RealmBuilder Create(std::shared_ptr<sys::ServiceDirectory> svc = nullptr);
// Same as above but the Realm will contain the contents in the manifest of
// the component referenced by the |relative_url|.
static RealmBuilder CreateFromRelativeUrl(std::string_view relative_url,
std::shared_ptr<sys::ServiceDirectory> svc = nullptr);
RealmBuilder(RealmBuilder&&) = default;
RealmBuilder& operator=(RealmBuilder&&) = default;
RealmBuilder(const RealmBuilder&) = delete;
RealmBuilder& operator=(const RealmBuilder&) = delete;
// Add a v2 component (.cm) to the root realm being constructed.
// See |Realm.AddChild| for more details.
RealmBuilder& AddChild(const std::string& child_name, const std::string& url,
ChildOptions options = kDefaultChildOptions);
// Add a v1 component (.cmx) to the root realm being constructed.
// See |Realm.AddLegacyChild| for more details.
RealmBuilder& AddLegacyChild(const std::string& child_name, const std::string& url,
ChildOptions options = kDefaultChildOptions);
// Add a component implemented by a C++ class.
// See |Realm.AddLocalChild| for more details.
RealmBuilder& AddLocalChild(const std::string& child_name, LocalComponent* local_impl,
ChildOptions options = kDefaultChildOptions);
// Create a sub realm as child of the root realm. The constructed
// Realm is returned.
// See |Realm.AddChildRealm| for more details.
Realm AddChildRealm(const std::string& child_name, ChildOptions options = kDefaultChildOptions);
// Route a capability for the root realm being constructed.
// See |Realm.AddRoute| for more details.
RealmBuilder& AddRoute(Route route);
/// Offers a directory capability to a component for the root realm.
// See |Realm.RouteReadOnlyDirectory| for more details.
RealmBuilder& RouteReadOnlyDirectory(const std::string& name, std::vector<Ref> to,
DirectoryContents directory);
/// Replaces the value of a given configuration field for the root realm.
RealmBuilder& ReplaceConfigValue(const std::string& name, const std::string& key,
ConfigValue value);
/// Updates the Component decl of the given child of the root realm.
void ReplaceComponentDecl(const std::string& child_name,
fuchsia::component::decl::Component decl);
/// Updates the Component decl of this root realm.
void ReplaceRealmDecl(fuchsia::component::decl::Component decl);
/// Fetches the Component decl of the given child of the root realm.
fuchsia::component::decl::Component GetComponentDecl(const std::string& child_name);
/// Fetches the Component decl of this root realm.
fuchsia::component::decl::Component GetRealmDecl();
// Build the realm root prepared by the associated builder methods, e.g. |AddComponent|.
// |dispatcher| must be non-null, or |async_get_default_dispatcher| must be
// configured to return a non-null value
// This function can only be called once per Realm::Builder instance.
// Multiple invocations will result in a panic.
// |dispatcher| must outlive the lifetime of the constructed |RealmRoot|.
RealmRoot Build(async_dispatcher* dispatcher = nullptr);
// A reference to the root `Realm` object.
Realm& root();
private:
RealmBuilder(std::shared_ptr<sys::ServiceDirectory> svc,
fuchsia::component::test::BuilderSyncPtr builder_proxy,
fuchsia::component::test::RealmSyncPtr test_realm_proxy);
static RealmBuilder CreateImpl(cpp17::optional<std::string_view> relative_url = cpp17::nullopt,
std::shared_ptr<sys::ServiceDirectory> svc = nullptr);
bool realm_commited_ = false;
std::shared_ptr<sys::ServiceDirectory> svc_;
fuchsia::component::test::BuilderSyncPtr builder_proxy_;
std::shared_ptr<internal::LocalComponentRunner::Builder> runner_builder_;
Realm root_;
};
} // namespace component_testing
#endif // LIB_SYS_COMPONENT_CPP_TESTING_REALM_BUILDER_H_