blob: 1dcecfa4bb34eb8110a9b32d7c049172531bf034 [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.
@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;
};