| // 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; |
| }; |