Realm builder

The realm builder library exists to facilitate integration testing of components by allowing for the run-time construction of realms and mocked components specific to individual test cases.

If a test wishes to launch a child component, then realm builder is likely a good fit for assisting the test.

If a test does not benefit from having either realms tailor made to each test case or realms containing mocked components unique to each test case, then the test can likely be made simpler to implement, understand, and maintain by using static component manifests. If a test does call for either (or both) of these things, then realm builder is a good fit for assisting the test.

The realm builder library is available in multiple languages, and the exact semantics and abilities available in each language may vary. This document uses Rust in its example code, but the concepts shown exist in all versions of the library. For a comprehensive list of features and which languages they are supported in, see the feature matrix at the end of this document.

Set up realm builder

Add the realm builder CML shard as an include in the test's manifest:

{
    include: [
        "//src/lib/fuchsia-component-test/meta/fuchsia_component_test.shard.cml",
    ],
    ...
}

This shard declares a component collection called fuchsia_component_test_collection where the constructed realms run, and offers a set of default capabilities to those realms:

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/meta/fuchsia_component_test.shard.cml" region_tag="collection_offers" adjust_indentation="auto" %}

Construct the component topology

Create a new RealmBuilder instance for each test case in your test. This creates a unique, isolated, child realm that ensures that the side-effects of one test case do not affect the others.

Use the RealmBuilder instance to add child components to the realm with the add_component() function. Each child component requires the following:

  1. Component name: Unique identifier for the component inside the realm. For static components, this maps to the name attribute of an instance listed in the children section of the component manifest.
  2. Component source: Defines how the component is created when the realm is built. For static components, this should be a ComponentSource::url with a valid component URL. This maps to the url attribute of an instance listed in the children section of a component manifest.

The example below adds two static child components to the created realm:

  • Component a loads from fuchsia-pkg://fuchsia.com/foo#meta/foo.cm
  • Component b loads from fuchsia-pkg://fuchsia.com/bar#meta/bar.cm
{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="add_a_and_b_example" adjust_indentation="auto" %}

This creates the following component topology:

   <root>
  /      \
 a        b

Note: Realm builder interprets component sources defined using a relative URL to be contained in the same package as the test controller.

Adding a mock component

Mock components allow tests to supply a local function that behaves as a dedicated component. Realm builder implements the protocols that enables the component framework to treat the local function as a component and handle incoming FIDL connections. The local function can hold state specific to the test case where it is used, allowing each constructed realm to have a mock for its specific use case.

The following example serves the fidl.examples.routing.echo.Echo protocol from a mock component that handles protocol requests from a local echo_server_mock() function:

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="mock_component_example" adjust_indentation="auto" %}

The echo_server_mock() creates a new ServiceFs to handle incoming FIDL connections. When the echo_string() protocol function is called, the mock sends a message to the test controller.

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="echo_server_mock" adjust_indentation="auto" %}

Add capability routes

By default there are no capability routes in the created realm. To route capabilities to components using RealmBuilder, call the add_route() function with the appropriate CapabilityRoute.

The following example adds a CapabilityRoute to offer component b the fidl.examples.routing.echo.Echo protocol from component a.

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="route_from_a_to_b_example" adjust_indentation="auto" %}

Exposing realm capabilities

To route capabilities provided from inside the created realm to the test controller, set the target of the CapabilityRoute using RouteEndpoint::above_root(). The created realm will automatically expose the capability to its parent. This allows the RealmBuilder instance to access the exposed capability.

The following example exposes a fidl.examples.routing.echo.EchoClientStats protocol to the parent test component:

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="route_to_above_root_example" adjust_indentation="auto" %}

Offering external capabilities

To route capabilities from the test controller to components inside the created realm, set the source of the CapabilityRoute using RouteEndpoint::above_root(). Consider the following example to make the fuchsia.logger.LogSink protocol from the parent's realm available to components a and b:

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="route_logsink_example" adjust_indentation="auto" %}

The fuchsia.logger.LogSink protocol is offered by default to the created realm through the realm builder shard. To route a capability that isn't in the realm builder shard, offer it directly:

{
    include: [
        "//src/lib/fuchsia-component-test/meta/fuchsia_component_test.shard.cml",
    ],
    children: [
        {
            name: "some-child",
            url: "...",
        },
    ],
    offer: [
        {
            protocol: "fuchsia.example.Foo",
            from: "#some-child",
            to: [ "#fuchsia_component_test_collection" ],
        },
    ],
    ...
}

Creating the realm

After you have added all the components and routes needed for the test case, use realm.create() to create the realm and make its components ready to execute.

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="create_realm" adjust_indentation="auto" %}

Note: The constructed realm instance is immutable. You cannot change components or routes after calling create().

Use the realm_instance returned by create() to perform additional tasks. Any eager components in the realm will immediately execute, and any capabilities routed using above_root are now accessible by the test.

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="connect_to_protocol" adjust_indentation="auto" %}

Destroying the realm

When the test no longer needs the realm, it can be destroyed by destroying the realm instance returned by create():

// As per rust semantics, this also happens when `realm_instance` goes out of
// scope.
drop(realm_instance);

This action instructs Component Manager to destroy the realm and all its children.

Advanced configuration

Modifying generated manifests

For cases where the capability routing features supported by add_route() are not sufficient, you can manually adjust the manifest declarations. Realm builder supports this for the following component types:

  • Mock components created by realm builder.
  • URL components contained in the same package as the test controller.

After constructing the realm:

  1. Use the get_decl() function of the constructed realm to obtain a specific child's manifest.
  2. Modify the appropriate manifest attributes.
  3. Substitute the updated manifest for the component by calling the set_decl() function.

See the following example:

{% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="src/lib/fuchsia-component-test/tests/src/lib.rs" region_tag="mutate_generated_manifest_example" adjust_indentation="auto" %}

When adding routes for modified components, add them directly to the constructed realm where you obtained the manifest instead of using the builder instance. This ensures the routes are properly validated against the modified component when the realm is created.

Determining a moniker

The moniker for a RealmBuilder child component looks like the following:

fuchsia_component_test_collection:{{ '<var>' }}child-name{{ '</var>' }}/{{ '<var>' }}component-name{{ '</var>' }}

The moniker consists of the following elements:

  • child-name: Obtained by calling the child_name() function of the constructed realm.
  • component-name: The “Component name” parameter provided to add_component() when constructing the realm.

Troubleshooting

Invalid capability routes

The add_route() function cannot validate if a capability is properly offered to the created realm from the test controller.

If you attempt to route capabilities with a source of above_root without a corresponding offer, requests to open the capability will not resolve and you will see error messages similar to the following:

[86842.196][klog][E] [component_manager] ERROR: Failed to route protocol `fidl.examples.routing.echo.Echo` with target component `/core:0/test_manager:0/tests:auto-10238282593681900609:4/test_wrapper:0/test_root:0/fuchsia_component_test_
[86842.197][klog][I] collection:auto-4046836611955440668:16/echo-client:0`: An `offer from parent` declaration was found at `/core:0/test_manager:0/tests:auto-10238282593681900609:4/test_wrapper:0/test_root:0/fuchsia_component_test_colle
[86842.197][klog][I] ction:auto-4046836611955440668:16` for `fidl.examples.routing.echo.Echo`, but no matching `offer` declaration was found in the parent

For more information on how to properly offer capabilities from the test controller, see offering external capabilities.

Language feature matrix

Rust
Legacy componentsN
Mock componentsY
Strong capability routesY
Weak capability routesN
Custom environmentsN
Setting subdirectoriesN