| // 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. |
| @available(added=7) |
| library fuchsia.component.test; |
| |
| using fuchsia.component; |
| using fuchsia.component.config; |
| using fuchsia.component.decl; |
| using fuchsia.component.runner; |
| using fuchsia.component.types; |
| using fuchsia.data; |
| using fuchsia.mem; |
| using fuchsia.io; |
| using fuchsia.url; |
| |
| // The Realm Builder Server will include a local component's name in its |
| // program dictionary under this string. Clients should use this value when |
| // extracting a local component's name. |
| const LOCAL_COMPONENT_NAME_KEY string = "LOCAL_COMPONENT_NAME"; |
| |
| /// 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; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// Identical to `Create`, but instead of the realm being empty by default |
| /// it contains the contents of the manifest located at `relative_url` |
| /// within `pkg_dir_handle`. |
| CreateFromRelativeUrl(resource struct { |
| pkg_dir_handle client_end:fuchsia.io.Directory; |
| relative_url fuchsia.url.Url; |
| realm_server_end server_end:Realm; |
| builder_server_end server_end:Builder; |
| }) -> (struct {}) error RealmBuilderError; |
| }; |
| |
| /// 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 RealmBuilderError = 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; |
| |
| /// The URL provided to `RealmBuilderFactory.CreateFromRelativeURL` is not a |
| /// relative URL. |
| URL_IS_NOT_RELATIVE = 16; |
| |
| /// The handle the client provided for the test's pkg directory is not |
| /// usable. |
| INVALID_PKG_DIR_HANDLE = 17; |
| |
| /// The component does not have a config schema defined. Attempting to |
| /// set a config value is not allowed. |
| NO_CONFIG_SCHEMA = 18; |
| |
| /// The component's config schema does not have a field with that name. |
| NO_SUCH_CONFIG_FIELD = 19; |
| |
| /// A config value is invalid. This may mean a type mismatch or an issue |
| /// with constraints like string/vector length. |
| CONFIG_VALUE_INVALID = 20; |
| }; |
| |
| 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 RealmBuilderError; |
| }; |
| |
| /// 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 Capability = flexible union { |
| 1: protocol Protocol; |
| 2: directory Directory; |
| 3: storage Storage; |
| 4: service Service; |
| 5: event Event; |
| }; |
| |
| /// A protocol capability |
| type Protocol = table { |
| /// The name of the capability. This is usually the name of the FIDL |
| /// protocol, e.g. `fuchsia.logger.LogSink`. If path is not set, the |
| /// protocol will be installed in a target component's namespace at |
| // `/svc/{name}`. |
| 1: name fuchsia.component.name; |
| |
| /// A rename of the capability, which can be set when routing to another |
| // component. This field is optional. |
| 2: as fuchsia.component.name; |
| |
| /// For information on this type, see |
| /// https://fuchsia.dev/go/components/declaration#DependencyType. |
| /// This field is optional and defaults to `STRONG`. |
| 3: type fuchsia.component.decl.DependencyType; |
| |
| /// Override the path in which the protocol is installed. Instead of |
| /// `/svc/{name}`, this value will be used. Path should begin with a |
| /// leading slash and omit a trailing slash, e.g. |
| /// `/foo/fuchsia.logger.LogSink`. This field is optional. |
| 4: path string:fuchsia.component.MAX_PATH_LENGTH; |
| }; |
| |
| /// A directory capability. |
| type Directory = table { |
| /// The name of the capability. This is not the path of the directory. |
| /// Instead it is a name used for routing. |
| 1: name fuchsia.component.name; |
| |
| /// A rename of the capability, which can be set when routing to another |
| // component. This field is optional. |
| 2: as fuchsia.component.name; |
| |
| /// For information on this type, see |
| /// https://fuchsia.dev/go/components/declaration#DependencyType. |
| /// This field is optional and defaults to `STRONG`. |
| 3: type fuchsia.component.decl.DependencyType; |
| |
| /// The subdirectory of this directory to offer instead of the root. For |
| /// example, if you set `bar/baz` as the subdir of `foo`, then `bar/baz` |
| /// will be the root of the target's `foo`. This field is optional. |
| 4: subdir string:fuchsia.component.MAX_PATH_LENGTH; |
| |
| /// The maximum rights that can be set by a component using this directory. |
| /// This field is required if it is being routed to a local component, |
| /// otherwise, it is optional. |
| 5: rights fuchsia.io.Rights; |
| |
| /// The path in which to install the directory. The path should have a |
| /// leading slash but no trailing slash, e.g. `/config/data`. This field |
| /// is required. |
| 6: path string:fuchsia.component.MAX_PATH_LENGTH; |
| }; |
| |
| /// A storage capability |
| type Storage = table { |
| /// The name of the capability. This is not the path of the directory. |
| /// Instead it is a name used for routing. |
| 1: name fuchsia.component.name; |
| |
| /// A rename of the capability, which can be set when routing to another |
| // component. This field is optional. |
| 2: as fuchsia.component.name; |
| |
| /// The path in which to install the directory. The path should have a |
| /// leading slash but no trailing slash, e.g. `/config/data`. This field |
| /// is required. |
| 3: path fuchsia.component.name; |
| }; |
| |
| /// A service capability |
| type Service = table { |
| /// The name of the capability. This is usually the name of the FIDL |
| /// service, e.g. `fuchsia.echo.EchoService`. If path is not set, the |
| /// service will be installed in a target component's namespace at |
| // `/svc/{name}`. |
| 1: name fuchsia.component.name; |
| |
| /// A rename of the capability, which can be set when routing to another |
| // component. This field is optional. |
| 2: as fuchsia.component.name; |
| |
| /// Override the path in which the service is installed. Instead of |
| /// `/svc/{name}`, this value will be used. Path should begin with a |
| /// leading slash and omit a trailing slash, e.g. |
| /// `/foo/fuchsia.echo.EchoService`. This field is optional. |
| 3: path string:fuchsia.component.MAX_PATH_LENGTH; |
| }; |
| |
| /// An event capability |
| type Event = table { |
| /// The name of the capability. |
| 1: name fuchsia.component.name; |
| |
| /// A rename of the capability, which can be set when routing to another |
| // component. This field is optional. |
| 2: as fuchsia.component.name; |
| |
| /// A filter to apply on the event. |
| 3: filter fuchsia.data.Dictionary; |
| }; |
| |
| /// Properties that may be set on a child when it is added to a realm. |
| type ChildOptions = table { |
| /// For information on this type, see |
| /// https://fuchsia.dev/go/components/declaration#StartupMode. |
| /// Defaults to `LAZY`. |
| 1: startup fuchsia.component.decl.StartupMode; |
| |
| /// Specify a custom environment for the child to run under. |
| 2: environment fuchsia.component.name; |
| |
| /// For information on this type, see |
| /// https://fuchsia.dev/go/components/declaration#OnTerminate. |
| /// Defaults to `NONE`. |
| 3: on_terminate fuchsia.component.decl.OnTerminate; |
| }; |
| |
| /// Maximum number of entries allowed in one call of `Realm.ReadOnlyDirectory`. |
| const MAX_DIRECTORY_ENTRIES uint32 = 1024; |
| |
| /// The contents of a directory that should be provided by the realm builder |
| /// server. |
| type DirectoryContents = resource struct { |
| entries vector<DirectoryEntry>:MAX_DIRECTORY_ENTRIES; |
| }; |
| |
| /// An entry in a directory. |
| type DirectoryEntry = resource struct { |
| /// The path to the file. Valid examples include `foo.txt` and |
| /// `foo/bar.json`. |
| file_path fuchsia.component.name; |
| |
| /// The contents of the file. |
| file_contents fuchsia.mem.Buffer; |
| }; |
| |
| 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| AddChild(struct { |
| /// The name of the child that is being added. |
| name fuchsia.component.child_name; |
| |
| /// The component's URL. |
| url fuchsia.url.Url; |
| |
| /// Additional properties for the child. |
| options ChildOptions; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// 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`. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| AddLegacyChild(struct { |
| /// The name of the child that is being added. |
| name fuchsia.component.child_name; |
| |
| /// The component's legacy URL (commonly ends with `.cmx`). |
| legacy_url fuchsia.url.Url; |
| |
| /// Additional properties for the child. |
| options ChildOptions; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| AddChildFromDecl(struct { |
| /// The name of the child that is being added. |
| name fuchsia.component.child_name; |
| |
| /// The component's declaration. |
| decl fuchsia.component.decl.Component; |
| |
| /// Additional properties for the child. |
| options ChildOptions; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| AddLocalChild(struct { |
| /// The name of the child that is being added. |
| name fuchsia.component.child_name; |
| |
| /// Additional properties for the child. |
| options ChildOptions; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| 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 RealmBuilderError; |
| |
| /// 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| GetComponentDecl(struct { |
| /// The name of the component whose declaration is being retrieved. |
| name fuchsia.component.child_name; |
| }) -> (struct { |
| component_decl fuchsia.component.decl.Component; |
| }) error RealmBuilderError; |
| |
| /// 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. |
| /// - `INVALID_COMPONENT_DECL`: `component_decl` failed validation. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| ReplaceComponentDecl(struct { |
| /// The name of the component whose declaration is being replaced. |
| name fuchsia.component.child_name; |
| |
| /// The new component declaration for `name`. |
| component_decl fuchsia.component.decl.Component; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// Returns the component decl for this realm. |
| /// |
| /// Errors: |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| GetRealmDecl() -> (struct { |
| component_decl fuchsia.component.decl.Component; |
| }) error RealmBuilderError; |
| |
| /// Replaces the component decl for this realm. |
| /// |
| /// Errors: |
| /// - `INVALID_COMPONENT_DECL`: `component_decl` failed validation. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| ReplaceRealmDecl(struct { |
| /// The new component declaration for this realm. |
| component_decl fuchsia.component.decl.Component; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// 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. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| AddRoute(struct { |
| /// The capabilities that are to be routed. |
| capabilities vector<Capability>: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 RealmBuilderError; |
| |
| /// 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_contents`. |
| /// |
| /// Errors: |
| /// - `NO_SUCH_TARGET`: `offer-to` references a non-existent child. |
| /// - `BUILD_ALREADY_CALLED`: The `Builder.Build` function has been called |
| /// for this realm, and thus this `Realm` channel can no longer be used. |
| ReadOnlyDirectory(resource struct { |
| /// The name of the directory capability. |
| name fuchsia.component.name; |
| |
| /// The target that this directory will be offered to. |
| to vector<fuchsia.component.decl.Ref>:MAX; |
| |
| /// The contents of the directory. |
| directory_contents DirectoryContents; |
| }) -> (struct {}) error RealmBuilderError; |
| |
| /// Replaces the configuration value for a field specified by `key`. |
| /// The component specified should have a config schema with this field. |
| /// The value must conform to all constraints as defined by the schema. |
| /// |
| /// Errors: |
| /// - `NO_CONFIG_SCHEMA`: component does not have a config schema |
| /// - `NO_SUCH_CONFIG_FIELD`: `key` could not be found in component's config schema |
| /// - `CONFIG_VALUE_INVALID`: `value` does not meet config schema constraints |
| ReplaceConfigValue(struct { |
| /// The name of the component whose config value is being replaced. |
| name fuchsia.component.name; |
| |
| /// The key of the config field whose value is being replaced. |
| key fuchsia.component.decl.ConfigKey; |
| |
| /// The config value being replaced. |
| value fuchsia.component.config.ValueSpec; |
| }) -> (struct {}) error RealmBuilderError; |
| }; |