blob: 08aa834d1570060821dbb91bb5eb7c636b49647f [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.
library fuchsia.component.test;
using fuchsia.component;
using fuchsia.component.decl;
using fuchsia.component.runner;
using fuchsia.component.types;
using fuchsia.io;
using fuchsia.io2;
using fuchsia.url;
type RealmBuilderError = strict enum : uint32 {
/// An attempt was made to interact with a component declaration for a
/// component which is located behind a child declaration, and is thus not
/// directly available to RealmBuilder.
NODE_BEHIND_CHILD_DECL = 0;
/// An attempt was made to retrieve a child of a component which does not
/// exist.
NO_SUCH_CHILD = 1;
/// The root component cannot have a URL-based source.
ROOT_CANNOT_BE_SET_TO_URL = 2;
/// The root component cannot be marked as eager.
ROOT_CANNOT_BE_EAGER = 3;
/// RealmBuilder failed to parse the FIDL from one of the arguments.
BAD_FIDL = 4;
/// A required field was missing in the input.
MISSING_FIELD = 5;
/// The set of targets for a route is not allowed to be empty.
ROUTE_TARGETS_EMPTY = 6;
/// The source for the route does not exist.
MISSING_ROUTE_SOURCE = 7;
/// A target for the route does not exist.
MISSING_ROUTE_TARGET = 8;
/// A target for the route is equal to the source, and it is impossible to
/// route a component's capability to itself.
ROUTE_SOURCE_AND_TARGET_MATCH = 9;
/// A component manifest has failed validation.
VALIDATION_ERROR = 10;
/// The requested route is impossible because the capability type cannot be
/// exposed.
UNABLE_TO_EXPOSE = 11;
/// The source of the route is invalid because currently "above root" is the
/// only supported source for storage capabilities.
STORAGE_SOURCE_INVALID = 12;
/// There is no component in this realm with the given moniker.
MONIKER_NOT_FOUND = 13;
/// The package directory has already been set for this connection.
PKG_DIR_ALREADY_SET = 14;
/// Unable to load component from package, the package dir is not set.
PKG_DIR_NOT_SET = 15;
/// Failed to load component from package due to IO error.
PKG_DIR_IO_ERROR = 16;
/// Failed to load the component decl.
FAILED_TO_LOAD_COMPONENT_DECL = 17;
/// An invalid capability was requested from the "debug" route endpoint.
///
/// Only protocols may be requested from the "debug" route endpoint in
/// RealmBuilder.
INVALID_CAPABILITY_FROM_DEBUG = 18;
};
const MAX_MOCK_ID_LENGTH uint32 = 1000;
const MAX_LEN_ROUTE_ENDPOINTS uint32 = 100;
/// The handles a mock component uses to consume capabilities from and provide
/// capabilities to the framework.
type MockComponentStartInfo = resource table {
1: ns vector<fuchsia.component.runner.ComponentNamespaceEntry>:fuchsia.component.runner.MAX_NAMESPACE_COUNT;
2: outgoing_dir server_end:fuchsia.io.Directory;
};
/// A component to be added to the realm, which is either a component
/// declaration that RealmBuilder should provide or an external URL that should
/// be referenced in a child decl.
type Component = flexible union {
1: decl fuchsia.component.decl.Component;
2: url string:fuchsia.component.types.MAX_URL_LENGTH;
3: legacy_url string:fuchsia.component.types.MAX_URL_LENGTH;
};
/// A capability route, denoting where a capability comes from and where it goes
/// to.
type CapabilityRoute = table {
/// The capability to route
1: capability Capability;
/// Where the capability comes from
2: source RouteEndpoint;
/// Where the capability goes to
3: targets vector<RouteEndpoint>:MAX_LEN_ROUTE_ENDPOINTS;
/// If true any components loaded in from the package will be modified as
/// needed to make the route valid. If false the package-local components
/// will not be modified.
4: force_route bool;
};
/// A capability to be routed
type Capability = flexible union {
1: protocol ProtocolCapability;
2: directory DirectoryCapability;
3: storage StorageCapability;
4: service ServiceCapability;
};
/// A service capability
type ServiceCapability = table {
1: name string:fuchsia.component.MAX_NAME_LENGTH;
};
/// A protocol capability
type ProtocolCapability = table {
1: name string:fuchsia.component.MAX_NAME_LENGTH;
};
/// A directory capability
type DirectoryCapability = table {
1: name string:fuchsia.component.MAX_NAME_LENGTH;
2: path string:fuchsia.component.MAX_PATH_LENGTH;
3: rights fuchsia.io2.Rights;
};
/// A storage capability
type StorageCapability = table {
1: name string:fuchsia.component.MAX_NAME_LENGTH;
2: path string:fuchsia.component.MAX_PATH_LENGTH;
};
/// The endpoint of a capability route, describing either the provider or
/// consumer of a capability.
type RouteEndpoint = flexible union {
1: component string:fuchsia.component.MAX_PATH_LENGTH;
2: above_root AboveRoot;
3: debug Debug;
};
/// The capability route's endpoint exists above the constructed realm, and is
/// offered to the realm's collection or will be accessed by the parent using
/// `fuchsia.component#Realm`.
type AboveRoot = struct {};
/// The capability route's endpoint exists in the environment and is offered as
/// "debug".
type Debug = struct {};
/// This stateful protocol can be used to construct a new component realm at
/// runtime. This new realm is built iteratively by calling the methods on this
/// protocol to add new components and capability routes between them. Due to
/// the stateful nature of this protocol, one realm may be constructed per
/// connection.
///
/// Once the realm details are successfully processed, `Commit` should be called
/// to produce a URL which can be used to create the component.
@discoverable
protocol RealmBuilder {
/// Initializes this connection to the framework intermediary. This function
/// should be called at the stat of every new RealmBuilder
/// connection, with the `pkg_dir_handle` argument providing a connection
/// to the test's package with rx* permissions. Components that are added
/// with a relative URL are loaded from this directory handle. If this call
/// is not made, any call to SetComponent with a relative URL will return an
/// error.
Init(resource struct {
pkg_dir_handle client_end:fuchsia.io.Directory;
}) -> (struct {}) error RealmBuilderError;
/// Sets the component to the provided component source. If the source is
/// a `Component::decl` then a new node is added to the internal tree
/// structure maintained for this connection. If the source is a
/// `Component::url` then a new ChildDecl is added to the parent of the
/// moniker. If any parents for the component do not exist then they are
/// added. If a different component already exists under this moniker,
/// then it is replaced.
SetComponent(struct {
moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
component Component;
}) -> (struct {}) error RealmBuilderError;
/// Sets the component to be a mock component. A new Component decl is
/// generated for the component, and assigned a new mock id. This id is
/// returned by this function, and when the mock component should start
/// running a new OnMockRunRequest is issued with this same id.
SetMockComponent(struct {
moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
}) -> (struct {
mock_id string:MAX_MOCK_ID_LENGTH;
}) error RealmBuilderError;
/// Returns the current value of a component decl in the realm being
/// constructed. Note that this cannot retrieve decls through external
/// URLs, so for example if `SetComponent` is called with `Component::url`
/// and then `GetComponentDecl` is called with the same moniker, an error
/// will be returned.
GetComponentDecl(struct {
moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
}) -> (struct {
component_decl fuchsia.component.decl.Component;
}) error RealmBuilderError;
/// Adds a capability route to the realm being constructed, adding any
/// necessary offers, exposes, uses, and capability declarations to any
/// component involved in the route. Note that components added with
/// `Component::url` can not be modified, and they are presumed to already
/// have the declarations needed for the route to be valid. If an error is
/// returned some of the components in the route may have been updated while
/// others were not.
RouteCapability(struct {
route CapabilityRoute;
}) -> (struct {}) error RealmBuilderError;
/// Marks the component and any ancestors of it as eager, ensuring that the
/// component is started immediately once the realm is bound to.
MarkAsEager(struct {
moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
}) -> (struct {}) error RealmBuilderError;
/// Returns true if the component exists in this realm.
Contains(struct {
moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
}) -> (struct {
exists bool;
});
/// Assembles the realm being constructed and returns the URL for the root
/// component in the realm, which may then be used to create a new component
/// in any collection where fuchsia-test-component is properly set up.
Commit() -> (struct {
root_component_url string:fuchsia.component.types.MAX_URL_LENGTH;
}) error RealmBuilderError;
/// The component framework is requesting that a mock component start
/// running
-> OnMockRunRequest(resource struct {
mock_id string:MAX_MOCK_ID_LENGTH;
start_info MockComponentStartInfo;
});
/// The component framework is requesting that a mock component stop
/// running
-> OnMockStopRequest(struct {
mock_id string:MAX_MOCK_ID_LENGTH;
});
};
/// This protocol can be used to instruct the Realm Builder Server to begin
/// creating a new realm.
@discoverable
protocol RealmBuilderFactory {
/// Creates a new RealmBuilder. The client end of `realm_server_end` can be
/// used to mutate the realm that is being constructed, by doing things such
/// as adding new children to the realm or adding capability routes between
/// them. The client end of `builder_server_end` is used to finalize the
/// realm, after which point it can be launched in a collection.
Create(resource struct {
pkg_dir_handle client_end:fuchsia.io.Directory;
realm_server_end server_end:Realm;
builder_server_end server_end:Builder;
}) -> ();
};
/// Errors that may be returned by the `Realm` and `Builder` protocols.
///
/// Will be renamed to `RealmBuilderError` once the other definition under this
/// name earlier in this file is removed.
type RealmBuilderError2 = strict enum : uint32 {
/// Child cannot be added to the realm, as there is already a child in the
/// realm with that name.
CHILD_ALREADY_EXISTS = 0;
/// A legacy component URL was given to `AddChild`, or a modern component
/// url was given to `AddLegacyChild`.
INVALID_MANIFEST_EXTENSION = 1;
/// A component declaration failed validation.
INVALID_COMPONENT_DECL = 2;
/// The referenced child does not exist.
NO_SUCH_CHILD = 3;
/// The component declaration for the referenced child cannot be viewed nor
/// manipulated by RealmBuilder, because the child was added to the realm
/// using an URL that was neither a relative nor a legacy URL.
CHILD_DECL_NOT_VISIBLE = 4;
/// The source does not exist.
NO_SUCH_SOURCE = 5;
/// A target does not exist.
NO_SUCH_TARGET = 6;
/// The `capabilities` field is empty.
CAPABILITIES_EMPTY = 7;
/// The `targets` field is empty.
TARGETS_EMPTY = 8;
/// The `from` value is equal to one of the elements in `to`.
SOURCE_AND_TARGET_MATCH = 9;
/// The test package does not contain the component declaration referenced
/// by a relative URL.
DECL_NOT_FOUND = 10;
/// Encountered an I/O error when attempting to read a component declaration
/// referenced by a relative URL from the test package.
DECL_READ_ERROR = 11;
/// The `Build` function has been called multiple times on this channel.
BUILD_ALREADY_CALLED = 12;
/// A capability is invalid. This may occur if a required field is empty or
/// if an unsupported type is received.
CAPABILITY_INVALID = 13;
/// The handle the client provided for the child realm is not usable
INVALID_CHILD_REALM_HANDLE = 14;
/// `ReplaceComponentDecl` was called on a legacy or local component with a
/// program declaration that did not match the one from the old component
/// declaration. This could render a legacy or local component
/// non-functional, and is disallowed.
IMMUTABLE_PROGRAM = 15;
};
protocol Builder {
/// Assembles the realm being constructed and returns the URL for the root
/// component in the realm, which may then be used to create a new component
/// in any collection where fuchsia-test-component is properly set up. Once
/// this is called, any Realm channels for the realm will no longer be
/// usable. The `runner` argument must be provided if the `AddLocalChild`
/// function has been used in this realm, as this runner channel will be
/// used to inform the client when to start and stop running any local
/// component implementations.
///
/// Errors:
/// - `INVALID_COMPONENT_DECL`: A component declaration failed validaiton.
/// - `BUILD_ALREADY_CALLED`: The `Build` function has been called multiple
/// times on this channel.
Build(resource struct {
runner client_end:fuchsia.component.runner.ComponentRunner;
}) -> (struct {
root_component_url string:fuchsia.component.types.MAX_URL_LENGTH;
}) error RealmBuilderError2;
};
/// A capability that can be routed around a realm using `AddRoute`.
///
/// Will be renamed to `Capability` once the other definition under this name
/// earlier in this file is removed.
type Capability2 = flexible union {
1: protocol Protocol;
2: directory Directory;
};
/// A protocol capability
type Protocol = table {
1: name fuchsia.component.name;
2: as fuchsia.component.name; // optional
3: type fuchsia.component.decl.DependencyType; // optional
};
/// A directory capability
type Directory = table {
1: name fuchsia.component.name;
2: as fuchsia.component.name; // optional
3: type fuchsia.component.decl.DependencyType; // optional
4: rights fuchsia.io2.Rights; // optional
5: subdir string:fuchsia.component.MAX_PATH_LENGTH; // optional
};
/// Properties that may be set on a child when it is added to a realm
type ChildOptions = table {
1: startup fuchsia.component.decl.StartupMode;
2: environment fuchsia.component.name;
3: on_terminate fuchsia.component.decl.OnTerminate;
};
protocol Realm {
/// Adds a component to the realm.
///
/// Errors:
/// - `CHILD_ALREADY_EXISTS`: this realm already contains a child with the
/// given name.
/// - `INVALID_MANIFEST_EXTENSION`: `url` ends with `.cmx`, and thus should
/// be used with `AddLegacyChild` instead of `AddChild`.
/// - `DECL_NOT_FOUND`: The test package does not contain the component
/// declaration referenced by a relative URL.
/// - `DECL_READ_ERROR`: Encountered an I/O error when attempting to read a
/// component declaration referenced by a relative URL from the test
/// package.
AddChild(struct {
/// The name of the child that is being added.
name fuchsia.component.name;
/// The component's URL.
url fuchsia.url.Url;
/// Additional properties for the child.
options ChildOptions;
}) -> (struct {}) error RealmBuilderError2;
/// Adds a [legacy
/// component](https://fuchsia.dev/fuchsia-src/concepts/components/v1) to
/// the realm. When the component is launched, RealmBuilder will reach out
/// to appmgr to assist with launching the component, and the component will
/// be able to utilize all of the features of the legacy component
/// framework. Note that _only_ protocol capabilities may be routed to this
/// component. Capabilities of any other type (such as a directory) are
/// unsupported for legacy components launched by RealmBuilder, and this
/// legacy component should instead use the CMX features to access things
/// such as storage.
///
/// Errors:
/// - `CHILD_ALREADY_EXISTS`: this realm already contains a child with the
/// given name.
/// - `INVALID_MANIFEST_EXTENSION`: `url` does not end with `.cmx`, and thus
/// should be used with `AddChild` instead of `AddLegacyChild`.
AddLegacyChild(struct {
/// The name of the child that is being added.
name fuchsia.component.name;
/// The component's legacy URL (commonly ends with `.cmx`).
legacy_url fuchsia.url.Url;
/// Additional properties for the child.
options ChildOptions;
}) -> (struct {}) error RealmBuilderError2;
/// Adds a component to this realm whose declaration is set to `decl`. When
/// launched, the component will share the test package as its package
/// directory, and may access any resources from it.
///
/// Errors:
/// - `CHILD_ALREADY_EXISTS`: this realm already contains a child with the
/// given name.
/// - `INVALID_COMPONENT_DECL`: `decl` failed validation.
AddChildFromDecl(struct {
/// The name of the child that is being added.
name fuchsia.component.name;
/// The component's declaration.
decl fuchsia.component.decl.Component;
/// Additional properties for the child.
options ChildOptions;
}) -> (struct {}) error RealmBuilderError2;
/// Adds a component to the realm whose implementation will be provided by
/// the client. When this component should be started, the runner channel
/// passed into `Build` will receive a start request for a component whose
/// `ProgramDecl` contains the relative moniker from the root of the
/// constructed realm for the child that is to be run under the `program`
/// key `LOCAL_COMPONENT_NAME`.
///
/// Errors:
/// - `CHILD_ALREADY_EXISTS`: this realm already contains a child with the
/// given name.
AddLocalChild(struct {
/// The name of the child that is being added.
name fuchsia.component.name;
/// Additional properties for the child.
options ChildOptions;
}) -> (struct {}) error RealmBuilderError2;
/// Adds a child realm which can be built with the client end of
/// `child_realm`.
///
/// Errors:
/// - `CHILD_ALREADY_EXISTS`: this realm already contains a child with the
/// given name.
AddChildRealm(resource struct {
/// The name of the child realm that is being added.
name fuchsia.component.name;
/// Additional properties for the child.
options ChildOptions;
/// The server end of the `Realm` channel that will be used to build the
/// sub-realm.
child_realm server_end:Realm;
}) -> (struct {}) error RealmBuilderError2;
/// Returns the component decl for the given component. `name` must refer to
/// a component that is one of the following:
///
/// - A component with a local implementation
/// - A legacy component
/// - A component added with a relative URL
/// - An automatically generated realm (ex: the root)
///
/// Errors:
/// - `NO_SUCH_CHILD`: This realm does not contain a child with the given
/// name.
/// - `CHILD_DECL_NOT_VISIBLE`: The component decl cannot be fetched for
/// the referenced child, because the child was added to the realm using
/// an absolute (not-relative) and modern (not legacy) URL.
GetComponentDecl(struct {
/// The name of the component whose declaration is being retrieved.
name fuchsia.component.name;
}) -> (struct {
component_decl fuchsia.component.decl.Component;
}) error RealmBuilderError2;
/// Replaces the component decl for the given component. `name` must
/// refer to a component that is one of the following:
///
/// - A component with a local implementation
/// - A legacy component
/// - A component added with a relative URL
/// - An automatically generated realm (ex: the root)
///
///
/// Errors:
/// - `NO_SUCH_CHILD`: This realm does not contain a child with the given
/// name.
/// - `CHILD_ALREADY_EXISTS`: The component whose decl is being replaced has
/// had a child added to it through realm builder with the same name as an
/// element in `component_decl.children`.
/// - `CHILD_DECL_NOT_VISIBLE`: The component decl cannot be manipulated for
/// the referenced child, because the child was added to the realm using
/// an absolute (not relative) and modern (not legacy) URL.
ReplaceComponentDecl(struct {
/// The name of the component whose declaration is being replaced.
name fuchsia.component.name;
/// The new component declaration for `name`.
component_decl fuchsia.component.decl.Component;
}) -> (struct {}) error RealmBuilderError2;
/// Mutates component manifests in the realm such that every component in
/// `to` will have a valid capability route for each item in `capabilities`
/// provided by `from`.
///
/// Errors:
/// - `NO_SUCH_SOURCE`: `from` references a non-existent child.
/// - `NO_SUCH_TARGET`: `to` references a non-existent child.
/// - `CAPABILITIES_EMPTY`: `capabilities` is empty.
/// - `TARGETS_EMPTY`: `to` is empty.
/// - `SOURCE_AND_TARGET_MATCH`: `from` is equal to one of the elements in
/// `to`.
/// - `INVALID_COMPONENT_DECL`: The requested route caused one of the
/// involved manifests to fail validation.
AddRoute(struct {
/// The capabilities that are to be routed.
capabilities vector<Capability2>:MAX;
/// The location where the elements of `capabilities` are available.
from fuchsia.component.decl.Ref;
/// The locations that should be able to access `capabilities`.
to vector<fuchsia.component.decl.Ref>:MAX;
}) -> (struct {}) error RealmBuilderError2;
};